-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Rewrite to use @opentelemetry/sdk-node
- Loading branch information
1 parent
6a0a5d1
commit 9039aa0
Showing
6 changed files
with
557 additions
and
196 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,166 +1,81 @@ | ||
import { type ContextManager, DiagConsoleLogger, type TextMapPropagator, diag } from '@opentelemetry/api'; | ||
import { getEnvWithoutDefaults } from '@opentelemetry/core'; | ||
import { type InstrumentationOption, registerInstrumentations } from '@opentelemetry/instrumentation'; | ||
import { BatchSpanProcessor, SimpleSpanProcessor, SpanExporter } from '@opentelemetry/sdk-trace-base'; | ||
import { type NodeTracerConfig, NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; | ||
import { | ||
type DetectorSync, | ||
type IResource, | ||
Resource, | ||
type ResourceDetectionConfig, | ||
detectResourcesSync, | ||
processDetector, | ||
} from '@opentelemetry/resources'; | ||
import { processDetectorSync } from '@opentelemetry/resources'; | ||
import { NodeSDK, type NodeSDKConfiguration } from '@opentelemetry/sdk-node'; | ||
import { BatchSpanProcessor, NoopSpanProcessor, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base'; | ||
import { ZipkinExporter } from '@opentelemetry/exporter-zipkin'; | ||
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; | ||
import { | ||
dockerDetector, | ||
k8sDetector, | ||
osDetector, | ||
packageJsonDetector, | ||
} from '@myrotvorets/opentelemetry-resource-detectors'; | ||
|
||
export interface Config { | ||
serviceName: string; | ||
instrumentations?: InstrumentationOption[]; | ||
resource?: IResource; | ||
detectors?: DetectorSync[]; | ||
tracer?: Omit<NodeTracerConfig, 'plugins' | 'resource'>; | ||
traceExporter?: SpanExporter; | ||
contextManager?: ContextManager; | ||
propagator?: TextMapPropagator; | ||
} | ||
export type Config = { serviceName: string } & Omit<Partial<NodeSDKConfiguration>, 'serviceName'>; | ||
|
||
export class OpenTelemetryConfigurator { | ||
private readonly serviceName: string; | ||
private tracerProvider?: NodeTracerProvider; | ||
private readonly nodeTracerConfig: NodeTracerConfig; | ||
private readonly resourceDetectionConfig: ResourceDetectionConfig; | ||
private readonly traceExporter?: SpanExporter; | ||
private readonly instrumentations?: InstrumentationOption[]; | ||
private readonly contextManager?: ContextManager; | ||
private readonly propagator?: TextMapPropagator; | ||
private unloader?: () => void; | ||
private readonly _config: Config; | ||
private readonly _sdk: NodeSDK; | ||
private _started = false; | ||
|
||
public constructor(config: Config) { | ||
this.serviceName = config.serviceName; | ||
|
||
const envWithoutDefaults = getEnvWithoutDefaults(); | ||
|
||
if (envWithoutDefaults.OTEL_LOG_LEVEL) { | ||
diag.setLogger(new DiagConsoleLogger(), { | ||
logLevel: envWithoutDefaults.OTEL_LOG_LEVEL, | ||
}); | ||
this._config = config; | ||
|
||
if (!this._config.traceExporter && !this._config.spanProcessor) { | ||
this._config.traceExporter = process.env.OTEL_EXPORTER_ZIPKIN_ENDPOINT ? new ZipkinExporter() : undefined; | ||
if (this._config.traceExporter) { | ||
/* c8 disable start */ | ||
this._config.spanProcessor = | ||
process.env.NODE_ENV === 'production' | ||
? new BatchSpanProcessor(this._config.traceExporter) | ||
: new SimpleSpanProcessor(this._config.traceExporter); | ||
/* c8 disable end */ | ||
} else { | ||
this._config.spanProcessor = new NoopSpanProcessor(); | ||
} | ||
} | ||
|
||
this.nodeTracerConfig = { | ||
...(config.tracer ?? {}), | ||
resource: config.resource ?? Resource.empty(), | ||
}; | ||
|
||
this.resourceDetectionConfig = { | ||
detectors: config.detectors ?? [ | ||
processDetector, | ||
if (!this._config.resourceDetectors?.length && false !== this._config.autoDetectResources) { | ||
this._config.resourceDetectors = [ | ||
osDetector, | ||
packageJsonDetector, | ||
k8sDetector, | ||
dockerDetector, | ||
], | ||
}; | ||
k8sDetector, | ||
packageJsonDetector, | ||
processDetectorSync, | ||
]; | ||
} | ||
|
||
this.traceExporter = OpenTelemetryConfigurator.getTraceExporter(config.serviceName, config.traceExporter); | ||
this.instrumentations = config.instrumentations; | ||
this.contextManager = config.contextManager; | ||
this.propagator = config.propagator; | ||
this._sdk = new NodeSDK(this._config); | ||
} | ||
|
||
public start(): void { | ||
if (this.tracerProvider) { | ||
return; | ||
} | ||
|
||
this.unloader = registerInstrumentations({ | ||
instrumentations: this.instrumentations, | ||
}); | ||
public start(): boolean { | ||
if (!this._started) { | ||
this._sdk.start(); | ||
|
||
this.detectResources(); | ||
this.tracerProvider = new NodeTracerProvider(this.nodeTracerConfig); | ||
process.once('SIGINT', this.shutdownHandler); | ||
process.once('SIGTERM', this.shutdownHandler); | ||
process.once('SIGQUIT', this.shutdownHandler); | ||
|
||
if (this.traceExporter) { | ||
const SpanProcessor = process.env.NODE_ENV === 'production' ? BatchSpanProcessor : SimpleSpanProcessor; | ||
this.tracerProvider.addSpanProcessor(new SpanProcessor(this.traceExporter)); | ||
this._started = true; | ||
return true; | ||
} | ||
|
||
this.tracerProvider.register({ | ||
contextManager: this.contextManager, | ||
propagator: this.propagator, | ||
}); | ||
|
||
process.once('SIGINT', this.shutdownHandler); | ||
process.once('SIGTERM', this.shutdownHandler); | ||
process.once('SIGQUIT', this.shutdownHandler); | ||
return false; | ||
} | ||
|
||
public async shutdown(): Promise<void> { | ||
const promises: Promise<unknown>[] = []; | ||
if (this.tracerProvider) { | ||
// Trace provider will shut down the span processor | ||
// Span processor will shut down the span exporter | ||
promises.push(this.tracerProvider.shutdown()); | ||
public async shutdown(): Promise<boolean> { | ||
if (this._started) { | ||
this._started = false; | ||
await this._sdk.shutdown(); | ||
return true; | ||
} | ||
|
||
process.off('SIGINT', this.shutdownHandler); | ||
process.off('SIGTERM', this.shutdownHandler); | ||
process.off('SIGQUIT', this.shutdownHandler); | ||
|
||
this.tracerProvider = undefined; | ||
|
||
this.unloader?.(); | ||
this.unloader = undefined; | ||
await Promise.all(promises); | ||
return false; | ||
} | ||
|
||
private readonly shutdownHandler = (): void => { | ||
this.shutdown().catch((e) => console.error(e)); | ||
}; | ||
|
||
private detectResources(): void { | ||
if (this.resourceDetectionConfig.detectors?.length) { | ||
const resource = detectResourcesSync(this.resourceDetectionConfig); | ||
this.nodeTracerConfig.resource = (this.nodeTracerConfig.resource as Resource).merge(resource); | ||
} | ||
|
||
(this.nodeTracerConfig.resource as Resource).merge( | ||
new Resource({ | ||
[SemanticResourceAttributes.SERVICE_NAME]: this.serviceName, | ||
}), | ||
); | ||
} | ||
|
||
private static getTraceExporter( | ||
serviceName: string, | ||
traceExporter: SpanExporter | undefined, | ||
): SpanExporter | undefined { | ||
if (traceExporter) { | ||
return traceExporter; | ||
} | ||
|
||
/* c8 ignore start */ | ||
if (process.env.OTEL_EXPORTER_ZIPKIN_ENDPOINT || process.env.ZIPKIN_ENDPOINT) { | ||
// Environment variables: | ||
// OTEL_EXPORTER_ZIPKIN_ENDPOINT (ZIPKIN_ENDPOINT): Endpoint for Zipkin traces | ||
// OTEL_EXPORTER_ZIPKIN_TIMEOUT: Maximum time the Zipkin exporter will wait for each batch export (10s by default) | ||
return new ZipkinExporter({ | ||
url: process.env.OTEL_EXPORTER_ZIPKIN_ENDPOINT ?? process.env.ZIPKIN_ENDPOINT, | ||
serviceName, | ||
}); | ||
} | ||
/* c8 ignore end */ | ||
|
||
return undefined; | ||
} | ||
|
||
public getTraceProvider(): NodeTracerProvider | undefined { | ||
return this.tracerProvider; | ||
public get config(): Readonly<Config> { | ||
return this._config; | ||
} | ||
} |
Oops, something went wrong.