-
Notifications
You must be signed in to change notification settings - Fork 790
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
chore: refactoring sdk start so that it can be a real sync #1400
Conversation
Codecov Report
@@ Coverage Diff @@
## master #1400 +/- ##
==========================================
+ Coverage 94.02% 94.24% +0.22%
==========================================
Files 104 145 +41
Lines 3448 4330 +882
Branches 720 881 +161
==========================================
+ Hits 3242 4081 +839
- Misses 206 249 +43
|
I don't understand what the advantage is. Having this sometimes sync and sometimes async seems like a confusing decision to explain to customers. |
This benefits SDK consumers who are writing wrappers around NodeSDK and wish to use it in a similar fashion to
Allowing for sync means I can create a SDK instance in another module and export it at the top level without having to mess with callbacks. You can see the difference in these examples - this (https://glitch.com/edit/#!/generated-tender-snarl) uses NodeSDK, and this (https://glitch.com/edit/#!/petite-verbena-playground) uses NodeTracerProvider. |
I would prefer to have it either always sync or always async. Having it sometimes sync is setting users up for confusion and failure. I would prefer to make |
But this doesn't solve the issue.
To achieve this I think we could delegate to start detecting resources in some "global object whatever we call it , resource detector, resource provider, etc.", it can be also attached to opentelemetry something like "opentelemetry.getResource which will be an observable in case the resource will change after some time - this in fact might be a true) |
Discussion moved to #1410 |
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.
This approach looks good to me! It allows SDK start to be synchronous and spares the export pipeline from having to await the resource by ensuring the promise resolves before letting data through.
``` | ||
|
||
## Configuration | ||
|
||
Below is a full list of configuration options which may be passed into the `NodeSDK` constructor; | ||
|
||
### autoDetectResources |
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.
Are we sure we want to remove this option completely? While I think most will want to autodetect resources, I could see wanting to opt-out in some environments.
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.
This looks really good. I definitely prefer to allow the sdk to be sync so this is a big improvement.
Just a minor note that doesn't block this PR that we will still have the load order issue which can cause some resource detectors to prevent wrapping. To be fixed in a follow up PR.
@@ -171,6 +171,14 @@ export class Span implements api.Span, ReadableSpan { | |||
); | |||
} | |||
|
|||
if (this.resource instanceof Promise) { | |||
this.resource.then(resource => { |
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.
Can we race this against a timeout promise so that we can ensure a slow resource detector does not delay or block tracing?
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.
fixed
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.
Where is the fix I don't see it?
@@ -39,6 +39,6 @@ export interface ReadableSpan { | |||
readonly events: TimedEvent[]; | |||
readonly duration: HrTime; | |||
readonly ended: boolean; | |||
readonly resource: Resource; | |||
readonly resource: Resource | Promise<Resource>; |
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.
Does this need to be a Promise? Since we know we will await it in the tracer, I see no reason to complicate the types of the export pipeline
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 couldn't find a better way so that it has to be like this or I can remove Resource | Promise<Resource>;
from almost everywhere but I will have to cast the resource to unknown and then to Resource. This might be a bit hacky as I will have to fake the typescript but that's the only way that works. So either we have Resource | Promise<Resource>;
everywhere or we fake ts and cast it to "Resource" in tests and in sdk
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.
fixed
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 think it should be possible to have TracerProvider
and MeterProvider
resource
property be Promise<Resource>
(no need for the | Resource
overload I think, Span.resource
be Resource | Promise<Resource>
(since both are needed here to allow it to resolve), and have ReadableSpan.resource
just be type Resource
because it is already resolved when it is sent to the export pipeline.
@@ -15,6 +15,7 @@ | |||
*/ | |||
|
|||
import { Link, CanonicalCode, SpanKind } from '@opentelemetry/api'; | |||
import { Resource } from '@opentelemetry/resources'; |
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.
Unused import?
@@ -27,7 +27,7 @@ import { | |||
statusDescriptionTagName, | |||
} from './transform'; | |||
import { OT_REQUEST_HEADER } from './utils'; | |||
import { SERVICE_RESOURCE } from '@opentelemetry/resources'; | |||
import { Resource, SERVICE_RESOURCE } from '@opentelemetry/resources'; |
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.
Unused import?
@@ -34,7 +34,7 @@ export abstract class Metric<T extends BaseBoundInstrument> | |||
private readonly _name: string, | |||
private readonly _options: api.MetricOptions, | |||
private readonly _kind: MetricKind, | |||
readonly resource: Resource, | |||
public resource: Resource, |
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.
This should be Resource | Promise<Resource>
@@ -79,15 +79,22 @@ export abstract class Metric<T extends BaseBoundInstrument> | |||
|
|||
getMetricRecord(): Promise<MetricRecord[]> { |
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.
If this function is async
you can avoid the new Promise
stuff and just await
async getMetricRecord(): Promise<MetricRecord[]> {
this.resource = this.resource instanceof Promise ? await this.resource : this.resource;
return this._instruments.values().map(instrument => ({
descriptor: this._descriptor,
labels: instrument.getLabels(),
aggregator: instrument.getAggregator(),
resource: this.resource,
instrumentationLibrary: this.instrumentationLibrary,
}));
}
@@ -119,8 +120,34 @@ export class NodeSDK { | |||
} | |||
|
|||
/** Detect resource attributes */ | |||
public async detectResources() { | |||
this.addResource(await detectResources()); | |||
private _detectResources(): Promise<Resource> { |
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.
Can make this async and use await since it returns a promise anyway.
@@ -152,7 +180,7 @@ export class NodeSDK { | |||
if (this._meterProviderConfig) { | |||
const meterProvider = new MeterProvider({ | |||
...this._meterProviderConfig, | |||
resource: this._resource, | |||
resource: (this._detectResources() as unknown) as Resource, |
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
@@ -171,6 +171,14 @@ export class Span implements api.Span, ReadableSpan { | |||
); | |||
} | |||
|
|||
if (this.resource instanceof Promise) { | |||
this.resource.then(resource => { |
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.
Where is the fix I don't see it?
@@ -56,6 +56,11 @@ export class Tracer implements api.Tracer { | |||
this._traceParams = localConfig.traceParams; | |||
this._idGenerator = config.idGenerator || new RandomIdGenerator(); | |||
this.resource = _tracerProvider.resource; | |||
if (this.resource instanceof Promise) { |
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.
Not needed here. Only the span needs to wait for the real resource
@@ -39,7 +39,7 @@ export class Tracer implements api.Tracer { | |||
private readonly _sampler: api.Sampler; | |||
private readonly _traceParams: TraceParams; | |||
private readonly _idGenerator: IdGenerator; | |||
readonly resource: Resource; | |||
public resource: Resource; |
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.
public resource: Resource; | |
public resource: Promise<Resource>; |
@@ -41,7 +41,7 @@ export class Span implements api.Span, ReadableSpan { | |||
readonly links: api.Link[] = []; | |||
readonly events: api.TimedEvent[] = []; | |||
readonly startTime: api.HrTime; | |||
readonly resource: Resource; | |||
resource: Resource; |
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.
resource: Resource; | |
resource: Resource | Promise<Resource>; |
See an example of what I'm asking for here: obecny#2 |
I would rather revert it to previous version when there was more casting as it seems like we have different approach to this. I have added the comment on your PR why I think this is wrong |
@dyladan how is the your progress on that ? |
Oops I thought I had opened it already |
Close in favor of #1484? |
closing this in favour of new PR -> #1484 |
…ations-node/register" for an agent-like experience (open-telemetry#1400) * feat(auto-instrumentation-agent): added intrumentation-agent package * feat(auto-instrumentation-agent): added api peer dependency * feat(auto-instrumentation-agent): updated test * feat(auto-instrumentation-agent): updated release please config * feat(auto-instrumentation-agent): updated .release-please-manifest * feat(auto-instrumentation-agent): updated test timeout * feat(auto-instrumentation-agent): removed timeout, added pretest script * feat(auto-instrumentation-agent): moved agent to auto-instrumentations-node * feat(auto-instrumentation-agent): reverted release please config * feat(auto-instrumentation-agent): updated README.md * feat(auto-instrumentation-agent): added env variable for resource detector configuration * feat(auto-instrumentation-agent): removed no-process-exit * feat(auto-instrumentation-agent): removed pretest script * feat(auto-instrumentation-agent): fixed lint issues * feat(auto-instrumentation-agent): updated register test * feat(auto-instrumentation-agent): fixed typo in test * feat(auto-instrumentation-agent): using env instead of export * feat(auto-instrumentation-agent): updated eslint.config.js * feat(auto-instrumentation-agent): updated test * feat(auto-instrumentation-agent): using language specific env var name * feat(auto-instrumentation-agent): updated README.md * feat(auto-instrumentation-agent): updated dependencies * feat(auto-instrumentation-agent): fixed detector test * feat(auto-instrumentation-agent): added comment to test app * feat(auto-instrumentation-agent): small refactor * feat(auto-instrumentation-agent): updated log messages * feat(auto-instrumentation-agent): added missing aws detectors * feat(auto-instrumentation-agent): updated tests * feat(auto-instrumentation-agent): updated getResourceDetectorsFromEnv() * feat(auto-instrumentation-agent): revert unnecessary changes * feat(auto-instrumentation-agent): added reference to supported instrumentations * feat(auto-instrumentation-agent): added reference to env var spec * feat(auto-instrumentation-agent): added reference node documentation * feat(auto-instrumentation-agent): added note for unsupported config * feat(auto-instrumentation-agent): added note to the debug section * feat(auto-instrumentation-agent): moved opentelemetry/api to regular dependency * feat(auto-instrumentation-agent): fixed lint issue * feat(auto-instrumentation-agent): re-added peer dep * feat(auto-instrumentation-agent): re-added api dev dep * feat(auto-instrumentation-agent): fixed lint issue * feat(auto-instrumentation-agent): fixed lint issue * feat(auto-instrumentation-agent): updated README.md * feat(auto-instrumentation-agent): added exporters reference in README.md * feat(auto-instrumentation-agent): updated debug notes * feat(auto-instrumentation-agent): updated installation section to include the api dependency * feat(auto-instrumentation-agent): reordered aws resource detectors * feat(auto-instrumentation-agent): removed process.exit(0) * feat(auto-instrumentation-agent): reordered aws resource detectors * feat(auto-instrumentation-agent): replaced type any with actual types * feat(auto-instrumentation-agent): updated utils test * feat(auto-instrumentation-agent): updated register test * feat(auto-instrumentation-agent): updated README.md * feat(auto-instrumentation-agent): updated shutdown logs * feat(auto-instrumentation-agent): fixed sinon issue
Which problem is this PR solving?
After this PR the code can look like this
start is not async anymore and resources are being propagated correctly