-
Notifications
You must be signed in to change notification settings - Fork 825
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feature(trace): adds named tracer factory #420
Conversation
Co-Authored-By: Mayur Kale <[email protected]>
Co-Authored-By: Mayur Kale <[email protected]>
…try-node into bg/add_tracer_factory_1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My biggest question is that there are 3 methods of getting a tracer factory:
- new them directly
- call static
.instance
method - call exported
getTracerFactory
function
It is not clear to me which is the best or recommended way to get a tracer factory. If the answer is not 1, then should we disallow it by making the constructor private?
@@ -17,14 +17,14 @@ if (EXPORTER.toLowerCase().startsWith('z')) { | |||
exporter = new JaegerExporter(options); | |||
} | |||
|
|||
const tracer = new BasicTracer(); | |||
// Initialize the OpenTelemetry APIs to use the BasicTracer bindings | |||
opentelemetry.initGlobalTracerFactory(new BasicTracerFactory()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this use BasicTracerFactory.instance()
instead of directly calling new
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO this (initialization of global tracer factory and BasicTracerFactory
) seems a little burdensome for the end users. I was thinking something like this:
// In case of BasicTracer
const opentelemetry = require('@opentelemetry/tracing');
opentelemetry.getTracerFactory().getTracer(); // should return the default BasicTracer.
opentelemetry.getTracerFactory().getTracer('mongodb'); // with name
opentelemetry.getTracerFactory().getTracer('redis', '1.0.0'); // with name and version
opentelemetry.getTracerFactory().addSpanProcessor(...);
// In case of NodeTracer
const opentelemetry = require('@opentelemetry/node');
opentelemetry.getTracerFactory().getTracer(); // should return the default NodeTracer.
// In case of WebTracer
const opentelemetry = require('@opentelemetry/web');
opentelemetry.getTracerFactory().getTracer(); // should return the default WebTracer.
The getTracerFactory
function is responsible for returning the corresponding TracerFactory
and we should create (and assign) the TracerFactory
instance statically once you load the library (maybe in index.ts
). WDYT?
const { SimpleSpanProcessor } = require('@opentelemetry/tracing'); | ||
const { JaegerExporter } = require('@opentelemetry/exporter-jaeger'); | ||
const { ZipkinExporter } = require('@opentelemetry/exporter-zipkin'); | ||
const EXPORTER = process.env.EXPORTER || ''; | ||
|
||
function setupTracerAndExporters(service) { | ||
const tracer = new NodeTracer({ | ||
const factory = new NodeTracerFactory({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
instance?
private _spanProcessors: SpanProcessor[] = []; | ||
private _config?: BasicTracerConfig; | ||
|
||
constructor(config?: BasicTracerConfig) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
constructor could be private, forcing users to use BasicTracerFactory.instance
which would prevent creation of multiple NodeTracerFactories. Is there a use case where we would want to allow the creation of multiple or should it always be a singleton?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just commented below but check this out: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/api-tracing.md#runtimes-with-multiple-deploymentsapplications
There definitely is a case for multiple factories.
From #434
|
Co-Authored-By: dyladan <[email protected]>
I'm having second thoughts on the singleton approach for the factory. There is a use case for having multiple factories if your runtime contains different applications and needs to be provided different trace factory configurations. The specification mentions delegating to a different provider for this use case but honestly that's just another layer of abstraction, it would be simpler imo to just have users do |
imho we should be deviating from the spec as little as possible. If you believe the spec should be changed that is a separate issue. Maybe it could be written in a more language/implementation Maybe the spec could, instead of specifying a static tracer factory with a delegate, say something along the lines of "support multiple tracer factory configurations for runtimes with multiple applications" edit: I assume by "static" you are referring to the singleton pattern? |
Yeah meant singleton, still drinking my morning coffee :) I don't think we'd be deviating much if at all from the specification as it doesn't call for a singleton. It does mention having a global tracer factory registry, but that is what |
Yea I'm just looking over it now. It looks like it allows for the creation of multiple tracer factories, and the delegate uses the wording "may" which to me just suggests an implementation rather than requiring it. |
FWIW I think it's worth remembering that a primary goal of the spec is, to quote,
I'm not quite sure what the most 'javascript-y' way to accomplish the named tracer factory is in this library, but I'd hew towards doing what feels natural for this language. |
…try-node into bg/add_tracer_factory_1
@mayurkale22 @dyladan @OlivierAlbertini I've updated this to not use the singleton and did a bit of refactoring, ptal! |
import { BasicTracer } from './BasicTracer'; | ||
import { SpanProcessor } from './SpanProcessor'; | ||
|
||
export abstract class AbstractBasicTracerFactory |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is the abstract factory specific to basic tracer? Why not just AbstractTracerFactory
and _newTracer
would return types.Tracer
?
BasicTracerFactory
and NodeTracerFactory
would inherit from it as NodeTracer
and BasicTracer
both implement types.Tracer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's due to this line specifically, since span processors is not an API level concept. https://github.com/open-telemetry/opentelemetry-js/pull/420/files#diff-7de0ceee2be4a0e5c9c8ba090fcc1ce4R28
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But the base class they inherit from should be more general. BasicTracer
and WebTracer
are both just specializations of types.Tracer
. The abstract base class is just an abstract tracer factory which all other tracer factories inherit from. Those more specific tracer factories would then have more specific typings.
class AbstractTracerFactory {
protected abstract _newTracer(): types.Tracer;
}
class BasicTracerFactory {
protected _newTracer(): BasicTracer {
return new BasicTracer();
}
}
class WebTracerFactory {
protected _newTracer(): WebTracer {
return new WebTracer();
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool, updated the abstract factory to be more general.
return tracer; | ||
} | ||
|
||
protected abstract _newTracer(): BasicTracer; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this BasicTracer
and not types.Tracer
?
export class NodeTracerFactory implements types.TracerFactory { | ||
private readonly _tracers: Map<string, NodeTracer> = new Map(); | ||
private _spanProcessors: SpanProcessor[] = []; | ||
export class NodeTracerFactory extends AbstractBasicTracerFactory { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems weird to me the NodeTracerFactory
extends AbstractBasicTracerFactory
when NodeTracer
and BasicTracer
are different
BasicTracerConfig, | ||
} from '@opentelemetry/tracing'; | ||
|
||
export class WebTracerFactory extends AbstractBasicTracerFactory { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here, I think it should just inherit from a more general AbstractTracerFactory
this._config = config; | ||
} | ||
|
||
protected _newTracer(): BasicTracer { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this BasicTracer
return type not WebTracer
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have added few minor comments, typos or just upvoted for the existing issue, there are some places which are missing jsdoc too. I think the example for examples/tracer-web
is not updated.
this._tracerFactory = tracerFactory || null; | ||
this._fallbackTracerFactory = | ||
fallbackTracerFactory || new NoopTracerFactory(); | ||
this._currentTracerFactory = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we have here this.start();
instead?
@@ -15,3 +15,4 @@ | |||
*/ | |||
|
|||
export * from './NodeTracer'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think we should remove this now? As NodeTracerFactory
is responsible for tracer creation and all.
* @param name identifies the instrumentation library | ||
* @param [version] is the semantic version of the library. | ||
*/ | ||
getTracer(name: string, version?: string): types.Tracer { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should return type T
} | ||
} | ||
|
||
getTracer(name: string = '', version?: string): BasicTracer { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason to override the default getTracer
implementation?
* addSpanProcessor adds a {@link SpanProcessor} to the factory, applying it | ||
* to all new and existing tracers. | ||
*/ | ||
addSpanProcessor(spanProcessor: SpanProcessor): void { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this not on AbstractTracerFactory
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
API vs SDK. Again, addSpanProcessor is a sdk concept and doesn't exist at the API level. If the AbstractTracerFactory is going to be in core and reusable by other implementations, it's should be at the level of the API.
/** | ||
* WebTracerFactory produces named tracers. | ||
*/ | ||
export class WebTracerFactory extends BasicTracerFactory { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does this inherit BasicTracerFactory
rather than AbstractTracerFactory<WebTracer>>
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see the reason why we would reimplement getTracer to do the addSpanProcessor loops, the only thing that's different is _newTracer. That being said I don't really have a strong opinion here, can go ahead and make the change
import { NodeTracerConfig } from './config'; | ||
import { BasicTracerFactory } from '@opentelemetry/tracing'; | ||
|
||
export class NodeTracerFactory extends BasicTracerFactory { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does this inherit BasicTracerFactory
rather than AbstractTracerFactory<NodeTracer>>
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see the reason why we would reimplement getTracer to do the addSpanProcessor loops, the only thing that's different is _newTracer. That being said I don't really have a strong opinion here, can go ahead and make the change
import { NoopTracer } from './NoopTracer'; | ||
|
||
export class NoopTracerFactory implements types.TracerFactory { | ||
private readonly _tracer: types.Tracer; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not omit constructor entirely and do:
private readonly _tracer = new NoopTracer();
Closing because it is not longer relevant |
Which problem is this PR solving?
Implements named tracers for Implement Named Tracers #403
TODO: update examples after merging in changes from feat(trace): named tracer factory prototype 2 #434
Short description of the changes
This PR adds in the TracerFactory type and updates the core's global tracer to use a tracerfactory instead of a tracer. Also modifies the TracerDelegate to be for a factory. Adds in the respective implementations for each tracer implementation.
Should name be a mandatory field in TS def? According to the specification an empty or null string can be passed as the name.