Skip to content
This repository has been archived by the owner on Oct 3, 2023. It is now read-only.

feat(tracer): separate cls from tracer #484

Merged

Conversation

hekike
Copy link
Contributor

@hekike hekike commented Apr 11, 2019

implements: #462

Changes

  • removed the argument notation interface of the tracer.startChildSpan because it brings ambiguity into span creation
  • Separation of CoreTracer and CoreTracerCls

Breaking Changes

  • startChildSpan only accepts options object
  • Tracer become TracerCls
  • Tracer is the basic non CLS version now

Action Items

  • separate tests out to test-tracer.ts and test-tracer-cls.ts (done)
  • Improve non-cls tracer test coverage (done)

Child of child with CLS Tracer

It's worth calling out that the CLS implementation still supports only one level of relationships as the rootSpan in the namespace context remains the same for child span creation. I guess it's expected based on current instrumentations.

A couple of further optimization ideas

@codecov-io
Copy link

codecov-io commented Apr 11, 2019

Codecov Report

Merging #484 into master will decrease coverage by 0.01%.
The diff coverage is n/a.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #484      +/-   ##
==========================================
- Coverage   95.01%   94.99%   -0.02%     
==========================================
  Files         143      145       +2     
  Lines        9299    10222     +923     
  Branches      515      874     +359     
==========================================
+ Hits         8835     9710     +875     
- Misses        464      512      +48
Impacted Files Coverage Δ
src/trace/model/tracer.ts 66.66% <0%> (-19.3%) ⬇️
src/trace/propagation/noop-propagation.ts 40% <0%> (-10%) ⬇️
src/http2.ts 80.64% <0%> (-8.65%) ⬇️
src/common/noop-logger.ts 11.11% <0%> (-5.56%) ⬇️
test/test-instana.ts 85.18% <0%> (-3.28%) ⬇️
test/test-http2.ts 96.81% <0%> (-2.53%) ⬇️
src/stackdriver-monitoring.ts 78.65% <0%> (-1.91%) ⬇️
src/trace/instrumentation/base-plugin.ts 9.52% <0%> (-1.59%) ⬇️
test/test-zpages.ts 99.27% <0%> (-0.36%) ⬇️
src/internal/string-utils.ts 100% <0%> (ø) ⬆️
... and 102 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 2e6709a...749fceb. Read the comment docs.

Copy link
Member

@mayurkale22 mayurkale22 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please update CHANGELOG.md with a breaking change line. How do you plan to expose basic tracer (with no CLS) using tracing instance?

@@ -42,7 +42,7 @@ function startServer (port) {

/** A function which handles requests and send response. */
function handleRequest (request, response) {
const span = tracer.startChildSpan('octutorials.handleRequest');
const span = tracer.startChildSpan({ name: 'octutorials.handleRequest' });
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest to update the examples in separate PR, WDYT?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any update here?

@@ -0,0 +1,142 @@
/**
* Copyright 2018, OpenCensus Authors
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: s/2018/2019

*/

import * as cls from '../../internal/cls';

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional Nit: Remove blank line

packages/opencensus-core/src/trace/model/tracer-cls.ts Outdated Show resolved Hide resolved
packages/opencensus-core/src/trace/model/tracer-cls.ts Outdated Show resolved Hide resolved
parentSpanId = options.spanContext.spanId || '';
traceState = options.spanContext.traceState;
let traceId;
if (options && options.spanContext && options.spanContext.traceId) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we just do options = options || {name: 'span'}; at the start and remove falsiness check on options (multiple places below)?

Copy link
Contributor Author

@hekike hekike Apr 12, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually, we could do just:

function (options = { name: 'span' }) { ... }

/**
* Starts a span.
* @param nameOrOptions Span name string or SpanOptions object.
* @param kind Span kind if not using SpanOptions object.
* @param [options] span options
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above

* @param childOf Span
* @param [name] Span name
* @param [kind] Span kind
* @param [options] Span Options
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above and just keep options param.

/** Get and set the currentRootSpan to tracer instance */
currentRootSpan: Span;


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Remove extra blank line.

@hekike
Copy link
Contributor Author

hekike commented Apr 12, 2019

@mayurkale22 earlier I posted some API ideas as a comment here, I just moved them to the PR description now. Could you please share what do you think about them?

@mayurkale22
Copy link
Member

@mayurkale22 earlier I posted some API ideas as a comment here, I just moved them to the PR description now. Could you please share what do you think about them?

Thanks for the detailed description, the Changes and Action items makes sense to me. In terms of a breaking changes, could you please elaborate more on ambiguity on span creation? My initial impression was to keep default tracer as TracerCls, so this won't create any breaking change for the
current users. Also, if feasible, (one approach) we can add basicTracer to Tracing instance, which users will call to get the basic non CLS version of tracer object.

merge startRootSpan and startChildSpan in the non CLS Tracer (startSpan)

SGTM, But would love to see same for CLS Tracer (easier to maintain and switch around). WDYT?

@hekike
Copy link
Contributor Author

hekike commented Apr 12, 2019

@mayurkale22 thanks for the feedback! About the API ambiguity: The startChildSpan's original syntax is startChildSpan(name?: string, kind?: types.SpanKind), now we need to add a third childOf argument. This is problematic as all the arguments are optional today. If we add it as a first argument, the user needs to pass undefined for tracerCls.startChildSpan(undefined, 'span-name'). If we add it as the last argument you need to pass undefined when you want optional name and kind. We could build variadic arguments but in my understanding, it is against TypeScript philosophy, but maybe I'm missing something. That's why in think the OpenTracing span creation API is a good tradeoff with a required first string argument and optional secondary options: tracer.startSpan('foo', { childOf: rootSpan }).

Only keeping the object options notations looks clear and robust solution to me, due to the TypeScript types it's easy to discover the available option properties.

@mayurkale22
Copy link
Member

Only keeping the object options notations looks clear and robust solution to me, due to the TypeScript types it's easy to discover the available option properties.

Makes sense to me, please update CHANGELOG.md with a breaking change line. Thanks

const child1 =
tracer.startChildSpan('child1', types.SpanKind.UNSPECIFIED);
const child1 = tracer.startChildSpan(
{name: 'child1', kind: types.SpanKind.UNSPECIFIED});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To simplify these tests and the API, can kind just have a default value? Then we wouldn't need to specify it again and again.

import * as types from './types';

/**
* This class represent a tracer with CLS.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explain what CLS is and when you would or would not want to use it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

@hekike
Copy link
Contributor Author

hekike commented Apr 12, 2019

@mayurkale22 @kenashcraft thanks for the review, I addressed the PR comments:

  • non-breaking change CoreTracer and CoreTracerBase separation
  • fixes in docs and typos
  • documenting CLS usage
  • updating CHANGELOG.md with startChildSpan changes
  • cleaning up startRootSpan defaults

import * as types from './types';

/**
* This class represent a tracer.
* This class represents a tracer with Cointunues Local Storage (CLS).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: s/Cointunues/Continuation/

private readonly IS_SAMPLED = 0x1;
/** A sampler used to make sample decisions */
sampler!: samplerTypes.Sampler;
/** A configuration for starting the tracer */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please update this comment to something like -> An object to log information to

rootSpan.start();
return fn(rootSpan);
}
} else {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we remove this else, and update the debug msg: Tracer is inactive, starting new no record root span?

import * as types from './types';

/**
* This class represent a tracer with CLS.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

* This class represent a tracer.
* This class represents a tracer with Cointunues Local Storage (CLS).
*
* CLS helps keep tracking the root span over function calls automatically.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add -> It is capable of storing, propagating and retrieving arbitrary continuation-local data (also called "context").

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

@@ -52,7 +52,7 @@ describe('Tracer', () => {
});
});

/** Should get/set the current RootSpan from tracer instance */
// /** Should get/set the current RootSpan from tracer instance */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this typo or intended stuff?

@hekike
Copy link
Contributor Author

hekike commented Apr 13, 2019

@mayurkale22 I addressed your new round of comments. Thanks!

@mayurkale22
Copy link
Member

Action Items (Will do after initial feedback)
separate tests out to test-tracer.ts and test-tracer-cls.ts
Improve non-cls tracer test coverage

@hekike Thanks for the work! What's your plan on above action items. Also, How do you plan to expose basic tracer (with no CLS) using tracing instance? OR as a user, how do I use basic tracer in my app?

@hekike
Copy link
Contributor Author

hekike commented Apr 15, 2019

@mayurkale22 I finished the open action items: separates CoreTracer and CoreTracerBase tests and added some context specific tests.

@mtwo
Copy link

mtwo commented Apr 29, 2019

@hekike Mayur has been on vacation for the past two weeks, but he's back next Monday. Do you need someone to approve prior to that?

@hekike
Copy link
Contributor Author

hekike commented Apr 29, 2019

It would be helpful if we could move this work forward. I'm planning to open further PRs which would require this work.

Copy link
Contributor

@draffensperger draffensperger left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a reasonable refactor and so LGTM with a few nit-picky comments.

I personally like the breaking change to simplify tracer.startChildSpan, but I'm not sure how impactful that would be to library users, and I wonder if it would be possible to separate the CLS functionality of the tracer without making that change. I would leave it to @mayurkale22 to make a call here.

/** Indicates if the tracer is active */
private activeLocal: boolean;
/** A configuration for starting the tracer */
private config!: configTypes.TracerConfig;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than using a ! would it be possible to assign some reasonable default config?

Copy link
Contributor Author

@hekike hekike May 2, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default config lives in the opencensus-node today. I could move it to opencensus-core and add as a static property to this class. Then use it here as defaults and consume from here in the opencensus-node. My only concern is that this is not a change in this PR and I would prefer to keep the scope focused. But I'm happy to do this as a separate PR.

/** Bit to represent whether trace is sampled or not. */
private readonly IS_SAMPLED = 0x1;
/** A sampler used to make sample decisions */
sampler!: samplerTypes.Sampler;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar comment here on the sampler - can we assign some reasonable default sampler, e.g. one that samples at the default rate?

// if there is a context propagation, keep the decision
if (options && options.spanContext && options.spanContext.options) {
propagatedSample =
((options.spanContext.options & this.IS_SAMPLED) !== 0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: can the outer parentheses around this expression be removed?

((options.spanContext.options & this.IS_SAMPLED) !== 0);
}

let sampleDecision = !!propagatedSample;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this logic be simplified to return !!propagatedSample || this.sampler.shouldSample(traceId)?

@hekike hekike force-pushed the feat/tracer-cls-separation branch from f9c0569 to cebd25e Compare May 2, 2019 15:09
@bogdandrutu bogdandrutu merged commit 92edaa9 into census-instrumentation:master May 3, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants