@adonisjs/otel 1.1.3 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -5
- package/build/configure.d.ts +1 -1
- package/build/index.d.ts +1 -0
- package/build/index.js +1 -0
- package/build/src/debug.d.ts +1 -1
- package/build/src/destination_manager.d.ts +30 -0
- package/build/src/destination_manager.js +134 -0
- package/build/src/destinations.d.ts +22 -0
- package/build/src/destinations.js +28 -0
- package/build/src/errors.d.ts +2 -2
- package/build/src/errors.js +1 -1
- package/build/src/instrumentations/container.d.ts +1 -1
- package/build/src/instrumentations/events.d.ts +1 -1
- package/build/src/otel.js +75 -7
- package/build/src/start.js +10 -1
- package/build/src/types/destinations.d.ts +66 -0
- package/build/src/types/destinations.js +1 -0
- package/build/src/types/index.d.ts +10 -0
- package/package.json +40 -30
package/README.md
CHANGED
|
@@ -27,12 +27,9 @@ In order to ensure that the AdonisJS community is welcoming to all, please revie
|
|
|
27
27
|
AdonisJS OpenTelemetry is open-sourced software licensed under the [MIT license](LICENSE.md).
|
|
28
28
|
|
|
29
29
|
[gh-workflow-image]: https://img.shields.io/github/actions/workflow/status/adonisjs/otel/checks.yml?style=for-the-badge
|
|
30
|
-
[gh-workflow-url]: https://github.com/adonisjs/otel/actions/workflows/checks.yml
|
|
31
|
-
|
|
30
|
+
[gh-workflow-url]: https://github.com/adonisjs/otel/actions/workflows/checks.yml 'Github action'
|
|
32
31
|
[npm-image]: https://img.shields.io/npm/v/@adonisjs/otel/latest.svg?style=for-the-badge&logo=npm
|
|
33
|
-
[npm-url]: https://www.npmjs.com/package/@adonisjs/otel/v/latest
|
|
34
|
-
|
|
32
|
+
[npm-url]: https://www.npmjs.com/package/@adonisjs/otel/v/latest 'npm'
|
|
35
33
|
[typescript-image]: https://img.shields.io/badge/Typescript-294E80.svg?style=for-the-badge&logo=typescript
|
|
36
|
-
|
|
37
34
|
[license-url]: LICENSE.md
|
|
38
35
|
[license-image]: https://img.shields.io/github/license/adonisjs/otel?style=for-the-badge
|
package/build/configure.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import ConfigureCommand from '@adonisjs/core/commands/configure';
|
|
1
|
+
import type ConfigureCommand from '@adonisjs/core/commands/configure';
|
|
2
2
|
export declare function configure(command: ConfigureCommand): Promise<void>;
|
package/build/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { configure } from './configure.js';
|
|
2
2
|
export { defineConfig } from './src/define_config.js';
|
|
3
3
|
export { OtelManager } from './src/otel.js';
|
|
4
|
+
export { destinations } from './src/destinations.js';
|
|
4
5
|
/**
|
|
5
6
|
* Re-export OTLP exporters so users don't need to install those 100 packages
|
|
6
7
|
* from OpenTelemetry just to get the exporters.
|
package/build/index.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
export { configure } from './configure.js';
|
|
10
10
|
export { defineConfig } from './src/define_config.js';
|
|
11
11
|
export { OtelManager } from './src/otel.js';
|
|
12
|
+
export { destinations } from './src/destinations.js';
|
|
12
13
|
/**
|
|
13
14
|
* Re-export OTLP exporters so users don't need to install those 100 packages
|
|
14
15
|
* from OpenTelemetry just to get the exporters.
|
package/build/src/debug.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: import("util").DebugLogger;
|
|
1
|
+
declare const _default: import("node:util").DebugLogger;
|
|
2
2
|
export default _default;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { NodeSDKConfiguration } from '@opentelemetry/sdk-node';
|
|
2
|
+
import type { DestinationMap } from './types/destinations.js';
|
|
3
|
+
/**
|
|
4
|
+
* OTel SDK primitives built from configured destinations.
|
|
5
|
+
*
|
|
6
|
+
* These arrays are merged inside `OtelManager` with user-provided SDK options.
|
|
7
|
+
*/
|
|
8
|
+
export interface DestinationPipelines {
|
|
9
|
+
spanProcessors?: NonNullable<NodeSDKConfiguration['spanProcessors']>;
|
|
10
|
+
metricReaders?: NonNullable<NodeSDKConfiguration['metricReaders']>;
|
|
11
|
+
logRecordProcessors?: NonNullable<NodeSDKConfiguration['logRecordProcessors']>;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Transforms high-level `destinations` config into concrete OTel exporters/processors.
|
|
15
|
+
*/
|
|
16
|
+
export declare class DestinationManager {
|
|
17
|
+
#private;
|
|
18
|
+
readonly destinations: DestinationMap;
|
|
19
|
+
/**
|
|
20
|
+
* @param destinations Destination map from `OtelConfig.destinations`.
|
|
21
|
+
*/
|
|
22
|
+
constructor(destinations: DestinationMap | undefined);
|
|
23
|
+
/**
|
|
24
|
+
* Build span processors, metric readers and log processors for every enabled destination.
|
|
25
|
+
*
|
|
26
|
+
* One destination can receive all signals or only a subset via `signals`.
|
|
27
|
+
* Multiple destinations create multiple exporters, enabling fan-out.
|
|
28
|
+
*/
|
|
29
|
+
buildPipelines(): DestinationPipelines;
|
|
30
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';
|
|
2
|
+
import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http';
|
|
3
|
+
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
|
|
4
|
+
import { BatchLogRecordProcessor } from '@opentelemetry/sdk-logs';
|
|
5
|
+
import { PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics';
|
|
6
|
+
import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';
|
|
7
|
+
import debug from './debug.js';
|
|
8
|
+
/**
|
|
9
|
+
* Transforms high-level `destinations` config into concrete OTel exporters/processors.
|
|
10
|
+
*/
|
|
11
|
+
export class DestinationManager {
|
|
12
|
+
destinations;
|
|
13
|
+
/**
|
|
14
|
+
* @param destinations Destination map from `OtelConfig.destinations`.
|
|
15
|
+
*/
|
|
16
|
+
constructor(destinations) {
|
|
17
|
+
if (!destinations) {
|
|
18
|
+
this.destinations = {};
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
this.destinations = destinations;
|
|
22
|
+
}
|
|
23
|
+
#resolveSignals(destination) {
|
|
24
|
+
if (destination.signals === 'all') {
|
|
25
|
+
return new Set(['traces', 'metrics', 'logs']);
|
|
26
|
+
}
|
|
27
|
+
return new Set(destination.signals);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Resolve the final endpoint for one signal.
|
|
31
|
+
*
|
|
32
|
+
* Priority:
|
|
33
|
+
* 1) `destination.endpoints[signal]`
|
|
34
|
+
* 2) `destination.endpoint` + `/v1/{signal}`
|
|
35
|
+
* 3) `undefined` (OTel exporter applies env/default fallback)
|
|
36
|
+
*/
|
|
37
|
+
#resolveEndpoint(destination, signal) {
|
|
38
|
+
const signalEndpoint = destination.endpoints?.[signal];
|
|
39
|
+
if (signalEndpoint)
|
|
40
|
+
return signalEndpoint;
|
|
41
|
+
if (destination.endpoint) {
|
|
42
|
+
return `${destination.endpoint.replace(/\/$/, '')}/v1/${signal}`;
|
|
43
|
+
}
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Batch processor options shared by trace and logs processors.
|
|
48
|
+
*/
|
|
49
|
+
#resolveBatchProcessorConfig(destination) {
|
|
50
|
+
return {
|
|
51
|
+
...(destination.maxExportBatchSize !== undefined && {
|
|
52
|
+
maxExportBatchSize: destination.maxExportBatchSize,
|
|
53
|
+
}),
|
|
54
|
+
...(destination.scheduledDelayMillis !== undefined && {
|
|
55
|
+
scheduledDelayMillis: destination.scheduledDelayMillis,
|
|
56
|
+
}),
|
|
57
|
+
...(destination.exportTimeoutMillis !== undefined && {
|
|
58
|
+
exportTimeoutMillis: destination.exportTimeoutMillis,
|
|
59
|
+
}),
|
|
60
|
+
...(destination.maxQueueSize !== undefined && {
|
|
61
|
+
maxQueueSize: destination.maxQueueSize,
|
|
62
|
+
}),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Build span processors, metric readers and log processors for every enabled destination.
|
|
67
|
+
*
|
|
68
|
+
* One destination can receive all signals or only a subset via `signals`.
|
|
69
|
+
* Multiple destinations create multiple exporters, enabling fan-out.
|
|
70
|
+
*/
|
|
71
|
+
buildPipelines() {
|
|
72
|
+
const spanProcessors = [];
|
|
73
|
+
const metricReaders = [];
|
|
74
|
+
const logRecordProcessors = [];
|
|
75
|
+
for (const [destinationKey, destination] of Object.entries(this.destinations)) {
|
|
76
|
+
if (!destination.enabled)
|
|
77
|
+
continue;
|
|
78
|
+
const signals = this.#resolveSignals(destination);
|
|
79
|
+
const destinationName = destination.name ?? destinationKey;
|
|
80
|
+
const headers = destination.headers;
|
|
81
|
+
const timeoutMillis = destination.timeoutMillis;
|
|
82
|
+
const concurrencyLimit = destination.concurrencyLimit;
|
|
83
|
+
const compression = destination.compression;
|
|
84
|
+
const batchProcessorConfig = this.#resolveBatchProcessorConfig(destination);
|
|
85
|
+
if (signals.has('traces')) {
|
|
86
|
+
const url = this.#resolveEndpoint(destination, 'traces');
|
|
87
|
+
const exporter = new OTLPTraceExporter({
|
|
88
|
+
...(url && { url }),
|
|
89
|
+
...(headers && { headers }),
|
|
90
|
+
...(timeoutMillis !== undefined && { timeoutMillis }),
|
|
91
|
+
...(concurrencyLimit !== undefined && { concurrencyLimit }),
|
|
92
|
+
...(compression !== undefined && { compression }),
|
|
93
|
+
});
|
|
94
|
+
spanProcessors.push(new BatchSpanProcessor(exporter, batchProcessorConfig));
|
|
95
|
+
}
|
|
96
|
+
if (signals.has('metrics')) {
|
|
97
|
+
const url = this.#resolveEndpoint(destination, 'metrics');
|
|
98
|
+
const exporter = new OTLPMetricExporter({
|
|
99
|
+
...(url && { url }),
|
|
100
|
+
...(headers && { headers }),
|
|
101
|
+
...(timeoutMillis !== undefined && { timeoutMillis }),
|
|
102
|
+
...(concurrencyLimit !== undefined && { concurrencyLimit }),
|
|
103
|
+
...(compression !== undefined && { compression }),
|
|
104
|
+
});
|
|
105
|
+
metricReaders.push(new PeriodicExportingMetricReader({
|
|
106
|
+
exporter,
|
|
107
|
+
...(destination.metricExportIntervalMillis !== undefined && {
|
|
108
|
+
exportIntervalMillis: destination.metricExportIntervalMillis,
|
|
109
|
+
}),
|
|
110
|
+
...(destination.metricExportTimeoutMillis !== undefined && {
|
|
111
|
+
exportTimeoutMillis: destination.metricExportTimeoutMillis,
|
|
112
|
+
}),
|
|
113
|
+
}));
|
|
114
|
+
}
|
|
115
|
+
if (signals.has('logs')) {
|
|
116
|
+
const url = this.#resolveEndpoint(destination, 'logs');
|
|
117
|
+
const exporter = new OTLPLogExporter({
|
|
118
|
+
...(url && { url }),
|
|
119
|
+
...(headers && { headers }),
|
|
120
|
+
...(timeoutMillis !== undefined && { timeoutMillis }),
|
|
121
|
+
...(concurrencyLimit !== undefined && { concurrencyLimit }),
|
|
122
|
+
...(compression !== undefined && { compression }),
|
|
123
|
+
});
|
|
124
|
+
logRecordProcessors.push(new BatchLogRecordProcessor(exporter, batchProcessorConfig));
|
|
125
|
+
}
|
|
126
|
+
debug('configured destination "%s" for signals: %O', destinationName, [...signals]);
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
spanProcessors: spanProcessors.length > 0 ? spanProcessors : undefined,
|
|
130
|
+
metricReaders: metricReaders.length > 0 ? metricReaders : undefined,
|
|
131
|
+
logRecordProcessors: logRecordProcessors.length > 0 ? logRecordProcessors : undefined,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { OtlpDestinationConfig, OtlpDestinationOptions } from './types/destinations.js';
|
|
2
|
+
/**
|
|
3
|
+
* Create an OTLP destination config with sensible defaults.
|
|
4
|
+
*
|
|
5
|
+
* Defaults:
|
|
6
|
+
* - `enabled: true`
|
|
7
|
+
* - `signals: 'all'`
|
|
8
|
+
*/
|
|
9
|
+
export declare function otlp(options: OtlpDestinationOptions): OtlpDestinationConfig;
|
|
10
|
+
/**
|
|
11
|
+
* Helper namespace for destination factories.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* destinations: {
|
|
16
|
+
* lgtm: destinations.otlp({ endpoint: 'http://localhost:4318', signals: 'all' }),
|
|
17
|
+
* }
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export declare const destinations: {
|
|
21
|
+
otlp: typeof otlp;
|
|
22
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create an OTLP destination config with sensible defaults.
|
|
3
|
+
*
|
|
4
|
+
* Defaults:
|
|
5
|
+
* - `enabled: true`
|
|
6
|
+
* - `signals: 'all'`
|
|
7
|
+
*/
|
|
8
|
+
export function otlp(options) {
|
|
9
|
+
return {
|
|
10
|
+
type: 'otlp',
|
|
11
|
+
...options,
|
|
12
|
+
enabled: options.enabled ?? true,
|
|
13
|
+
signals: options.signals ?? 'all',
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Helper namespace for destination factories.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* destinations: {
|
|
22
|
+
* lgtm: destinations.otlp({ endpoint: 'http://localhost:4318', signals: 'all' }),
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export const destinations = {
|
|
27
|
+
otlp,
|
|
28
|
+
};
|
package/build/src/errors.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const E_OTEL_CONFIG: new (args: [configPath: string], options?: ErrorOptions) => import("@poppinss/utils").Exception;
|
|
2
|
-
export declare const E_OTEL_CONFIG_INVALID: new (args: [configPath: string], options?: ErrorOptions) => import("@poppinss/utils").Exception;
|
|
1
|
+
export declare const E_OTEL_CONFIG: new (args: [configPath: string], options?: ErrorOptions) => import("@poppinss/utils/exception").Exception;
|
|
2
|
+
export declare const E_OTEL_CONFIG_INVALID: new (args: [configPath: string], options?: ErrorOptions) => import("@poppinss/utils/exception").Exception;
|
package/build/src/errors.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { createError } from '@poppinss/utils';
|
|
1
|
+
import { createError } from '@poppinss/utils/exception';
|
|
2
2
|
export const E_OTEL_CONFIG = createError('Failed to load OpenTelemetry config at "%s". Make sure the file exists and has no syntax errors.', 'E_OTEL_CONFIG');
|
|
3
3
|
export const E_OTEL_CONFIG_INVALID = createError('OpenTelemetry config at "%s" must export a configuration object.', 'E_OTEL_CONFIG_INVALID');
|
|
@@ -2,7 +2,7 @@ import type { Span } from '@opentelemetry/api';
|
|
|
2
2
|
import type { InstrumentationConfig } from '@opentelemetry/instrumentation';
|
|
3
3
|
import type { ContainerMakeTracingData } from '@adonisjs/core/types/container';
|
|
4
4
|
import { InstrumentationBase } from '@opentelemetry/instrumentation';
|
|
5
|
-
import { TracingChannelSubscribers } from 'node:diagnostics_channel';
|
|
5
|
+
import { type TracingChannelSubscribers } from 'node:diagnostics_channel';
|
|
6
6
|
/**
|
|
7
7
|
* OpenTelemetry instrumentation for AdonisJS IoC Container.
|
|
8
8
|
*
|
|
@@ -2,7 +2,7 @@ import type { Span } from '@opentelemetry/api';
|
|
|
2
2
|
import type { InstrumentationConfig } from '@opentelemetry/instrumentation';
|
|
3
3
|
import type { TracingChannelSubscribers } from 'node:diagnostics_channel';
|
|
4
4
|
import { InstrumentationBase } from '@opentelemetry/instrumentation';
|
|
5
|
-
import { AllowedEventTypes, EventDispatchData } from '@adonisjs/core/types/events';
|
|
5
|
+
import { type AllowedEventTypes, type EventDispatchData } from '@adonisjs/core/types/events';
|
|
6
6
|
/**
|
|
7
7
|
* OpenTelemetry instrumentation for AdonisJS Event Emitter.
|
|
8
8
|
*
|
package/build/src/otel.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { getNodeAutoInstrumentations, } from '@opentelemetry/auto-instrumentations-node';
|
|
2
2
|
import { resourceFromAttributes } from '@opentelemetry/resources';
|
|
3
3
|
import { NodeSDK } from '@opentelemetry/sdk-node';
|
|
4
|
-
import { ConsoleSpanExporter, ParentBasedSampler, SimpleSpanProcessor, TraceIdRatioBasedSampler, } from '@opentelemetry/sdk-trace-base';
|
|
4
|
+
import { BatchSpanProcessor, ConsoleSpanExporter, ParentBasedSampler, SimpleSpanProcessor, TraceIdRatioBasedSampler, } from '@opentelemetry/sdk-trace-base';
|
|
5
5
|
import { ATTR_HTTP_ROUTE, ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION, } from '@opentelemetry/semantic-conventions';
|
|
6
6
|
import { ATTR_DEPLOYMENT_ENVIRONMENT_NAME, ATTR_SERVICE_INSTANCE_ID, } from '@opentelemetry/semantic-conventions/incubating';
|
|
7
7
|
import { HttpContext } from '@adonisjs/core/http';
|
|
8
8
|
import { HttpUrlFilter } from './http_url_filter.js';
|
|
9
|
+
import { DestinationManager } from './destination_manager.js';
|
|
9
10
|
import debug from './debug.js';
|
|
10
11
|
/**
|
|
11
12
|
* OpenTelemetry SDK manager for AdonisJS.
|
|
@@ -205,21 +206,88 @@ export class OtelManager {
|
|
|
205
206
|
processors.push(new SimpleSpanProcessor(new ConsoleSpanExporter()));
|
|
206
207
|
return processors.length > 0 ? processors : undefined;
|
|
207
208
|
}
|
|
209
|
+
/**
|
|
210
|
+
* Build destination pipelines from the `destinations` config.
|
|
211
|
+
*/
|
|
212
|
+
#buildDestinationPipelines() {
|
|
213
|
+
return new DestinationManager(this.#config.destinations).buildPipelines();
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Whether we should preserve legacy trace exporter settings when destinations add traces.
|
|
217
|
+
*/
|
|
218
|
+
#shouldPreserveTraceExporter(destinationSpanProcessors) {
|
|
219
|
+
const hasDestinationTraceProcessors = (destinationSpanProcessors?.length ?? 0) > 0;
|
|
220
|
+
const hasExplicitSpanProcessors = (this.#config.spanProcessors?.length ?? 0) > 0;
|
|
221
|
+
return hasDestinationTraceProcessors && !hasExplicitSpanProcessors;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Merge configured span processors with destination trace processors.
|
|
225
|
+
*/
|
|
226
|
+
#mergeSpanProcessors(destinationSpanProcessors) {
|
|
227
|
+
const spanProcessors = [
|
|
228
|
+
...(this.#buildSpanProcessors() ?? []),
|
|
229
|
+
...(destinationSpanProcessors ?? []),
|
|
230
|
+
];
|
|
231
|
+
/**
|
|
232
|
+
* When destinations add trace span processors, NodeSDK ignores `traceExporter`
|
|
233
|
+
* and deprecated `spanProcessor`. Re-inject them to preserve existing behavior.
|
|
234
|
+
*/
|
|
235
|
+
if (this.#shouldPreserveTraceExporter(destinationSpanProcessors)) {
|
|
236
|
+
if (this.#config.traceExporter) {
|
|
237
|
+
spanProcessors.unshift(new BatchSpanProcessor(this.#config.traceExporter));
|
|
238
|
+
}
|
|
239
|
+
if (this.#config.spanProcessor) {
|
|
240
|
+
spanProcessors.unshift(this.#config.spanProcessor);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return spanProcessors.length > 0 ? spanProcessors : undefined;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Merge configured metric readers with destination metric readers.
|
|
247
|
+
*/
|
|
248
|
+
#mergeMetricReaders(destinationMetricReaders) {
|
|
249
|
+
const metricReaders = [
|
|
250
|
+
...(this.#config.metricReaders ?? []),
|
|
251
|
+
...(destinationMetricReaders ?? []),
|
|
252
|
+
];
|
|
253
|
+
return metricReaders.length > 0 ? metricReaders : undefined;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Merge configured log record processors with destination log processors.
|
|
257
|
+
*/
|
|
258
|
+
#mergeLogRecordProcessors(destinationLogRecordProcessors) {
|
|
259
|
+
const logRecordProcessors = [
|
|
260
|
+
...(this.#config.logRecordProcessors ?? []),
|
|
261
|
+
...(destinationLogRecordProcessors ?? []),
|
|
262
|
+
];
|
|
263
|
+
return logRecordProcessors.length > 0 ? logRecordProcessors : undefined;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Return NodeSDK user config without internal-only keys.
|
|
267
|
+
*/
|
|
268
|
+
#getNodeSdkUserConfig() {
|
|
269
|
+
const sdkConfig = { ...this.#config };
|
|
270
|
+
delete sdkConfig.destinations;
|
|
271
|
+
return sdkConfig;
|
|
272
|
+
}
|
|
208
273
|
/**
|
|
209
274
|
* Create the NodeSDK instance with all configuration
|
|
210
275
|
*/
|
|
211
276
|
#createSdk() {
|
|
212
|
-
const
|
|
213
|
-
const instrumentations = this.#buildInstrumentations();
|
|
277
|
+
const destinationPipelines = this.#buildDestinationPipelines();
|
|
214
278
|
const sampler = this.#buildSampler();
|
|
215
|
-
const spanProcessors = this.#
|
|
279
|
+
const spanProcessors = this.#mergeSpanProcessors(destinationPipelines.spanProcessors);
|
|
280
|
+
const metricReaders = this.#mergeMetricReaders(destinationPipelines.metricReaders);
|
|
281
|
+
const logRecordProcessors = this.#mergeLogRecordProcessors(destinationPipelines.logRecordProcessors);
|
|
216
282
|
return new NodeSDK({
|
|
217
|
-
...this.#
|
|
218
|
-
resource,
|
|
283
|
+
...this.#getNodeSdkUserConfig(),
|
|
284
|
+
resource: this.#buildResource(),
|
|
219
285
|
serviceName: this.serviceName,
|
|
220
|
-
instrumentations,
|
|
286
|
+
instrumentations: this.#buildInstrumentations(),
|
|
221
287
|
...(sampler && { sampler }),
|
|
222
288
|
...(spanProcessors && { spanProcessors }),
|
|
289
|
+
...(metricReaders && { metricReaders }),
|
|
290
|
+
...(logRecordProcessors && { logRecordProcessors }),
|
|
223
291
|
});
|
|
224
292
|
}
|
|
225
293
|
/**
|
package/build/src/start.js
CHANGED
|
@@ -15,12 +15,21 @@
|
|
|
15
15
|
* import '../otel.js'
|
|
16
16
|
* ```
|
|
17
17
|
*/
|
|
18
|
+
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
19
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
20
|
+
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
21
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
return path;
|
|
25
|
+
};
|
|
18
26
|
import { createAddHookMessageChannel } from 'import-in-the-middle';
|
|
19
27
|
import { register } from 'node:module';
|
|
20
28
|
import { join } from 'node:path';
|
|
29
|
+
import { pathToFileURL } from 'node:url';
|
|
21
30
|
import { E_OTEL_CONFIG, E_OTEL_CONFIG_INVALID } from './errors.js';
|
|
22
31
|
async function loadConfig(path) {
|
|
23
|
-
return await import(path)
|
|
32
|
+
return await import(__rewriteRelativeImportExtension(pathToFileURL(path).href))
|
|
24
33
|
.then((mod) => mod.default || mod)
|
|
25
34
|
.catch((error) => {
|
|
26
35
|
throw new E_OTEL_CONFIG([path], { cause: error });
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
|
|
2
|
+
import type { BufferConfig } from '@opentelemetry/sdk-trace-base';
|
|
3
|
+
export type DestinationSignal = 'traces' | 'metrics' | 'logs';
|
|
4
|
+
export type DestinationSignals = 'all' | DestinationSignal[];
|
|
5
|
+
type OtlpExporterConfig = NonNullable<ConstructorParameters<typeof OTLPTraceExporter>[0]>;
|
|
6
|
+
type DestinationBatchConfig = Pick<BufferConfig, 'maxExportBatchSize' | 'scheduledDelayMillis' | 'exportTimeoutMillis' | 'maxQueueSize'>;
|
|
7
|
+
export interface OtlpDestinationOptions extends DestinationBatchConfig {
|
|
8
|
+
/**
|
|
9
|
+
* Optional label used in debug logs.
|
|
10
|
+
*/
|
|
11
|
+
name?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Enable/disable this destination.
|
|
14
|
+
* @default true
|
|
15
|
+
*/
|
|
16
|
+
enabled?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Signals to export to this destination.
|
|
19
|
+
* @default 'all'
|
|
20
|
+
*/
|
|
21
|
+
signals?: DestinationSignals;
|
|
22
|
+
/**
|
|
23
|
+
* OTLP base endpoint. Signal paths are appended automatically:
|
|
24
|
+
* - /v1/traces
|
|
25
|
+
* - /v1/metrics
|
|
26
|
+
* - /v1/logs
|
|
27
|
+
*/
|
|
28
|
+
endpoint?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Per-signal OTLP endpoints. When set, it overrides `endpoint` for that signal.
|
|
31
|
+
*/
|
|
32
|
+
endpoints?: Partial<Record<DestinationSignal, string>>;
|
|
33
|
+
/**
|
|
34
|
+
* Additional headers sent with each OTLP request.
|
|
35
|
+
*/
|
|
36
|
+
headers?: Record<string, string>;
|
|
37
|
+
/**
|
|
38
|
+
* Request timeout in milliseconds.
|
|
39
|
+
*/
|
|
40
|
+
timeoutMillis?: number;
|
|
41
|
+
/**
|
|
42
|
+
* Optional maximum number of pending HTTP requests.
|
|
43
|
+
*/
|
|
44
|
+
concurrencyLimit?: number;
|
|
45
|
+
/**
|
|
46
|
+
* OTLP payload compression algorithm.
|
|
47
|
+
* @default 'none'
|
|
48
|
+
*/
|
|
49
|
+
compression?: OtlpExporterConfig['compression'];
|
|
50
|
+
/**
|
|
51
|
+
* Metrics export interval for this destination.
|
|
52
|
+
*/
|
|
53
|
+
metricExportIntervalMillis?: number;
|
|
54
|
+
/**
|
|
55
|
+
* Metrics export timeout for this destination.
|
|
56
|
+
*/
|
|
57
|
+
metricExportTimeoutMillis?: number;
|
|
58
|
+
}
|
|
59
|
+
export interface OtlpDestinationConfig extends OtlpDestinationOptions {
|
|
60
|
+
type: 'otlp';
|
|
61
|
+
enabled: boolean;
|
|
62
|
+
signals: DestinationSignals;
|
|
63
|
+
}
|
|
64
|
+
export type DestinationConfig = OtlpDestinationConfig;
|
|
65
|
+
export type DestinationMap = Record<string, DestinationConfig>;
|
|
66
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { NodeSDKConfiguration } from '@opentelemetry/sdk-node';
|
|
2
2
|
import type { HttpContext } from '@adonisjs/core/http';
|
|
3
|
+
import type { DestinationMap } from './destinations.js';
|
|
3
4
|
export type { InstrumentationValue, CustomInstrumentationValue, HttpInstrumentationConfig, PinoInstrumentationConfig, InstrumentationsConfig, } from './instrumentations.js';
|
|
4
5
|
export type { SpanOptions } from './decorators.js';
|
|
5
6
|
export { hiddenFields, type HiddenField, type OtelLoggingPresetOptions } from './logging.js';
|
|
7
|
+
export type { DestinationConfig, DestinationMap, DestinationSignal, DestinationSignals, OtlpDestinationConfig, OtlpDestinationOptions, } from './destinations.js';
|
|
6
8
|
import type { InstrumentationsConfig } from './instrumentations.js';
|
|
7
9
|
/**
|
|
8
10
|
* Configuration for @adonisjs/otel
|
|
@@ -83,6 +85,14 @@ export interface OtelConfig extends Partial<Omit<NodeSDKConfiguration, 'resource
|
|
|
83
85
|
* ```
|
|
84
86
|
*/
|
|
85
87
|
instrumentations?: InstrumentationsConfig;
|
|
88
|
+
/**
|
|
89
|
+
* Configure one or many OTLP destinations and fan-out telemetry to each one.
|
|
90
|
+
*
|
|
91
|
+
* Keys are destination identifiers (for example: `lgtm`, `secondary`).
|
|
92
|
+
* Each destination can target all signals (`traces`, `metrics`, `logs`)
|
|
93
|
+
* or only a subset.
|
|
94
|
+
*/
|
|
95
|
+
destinations?: DestinationMap;
|
|
86
96
|
/**
|
|
87
97
|
* Configure automatic user context extraction in the middleware.
|
|
88
98
|
*
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adonisjs/otel",
|
|
3
3
|
"description": "OpenTelemetry integration for AdonisJS with sensible defaults and zero-config setup",
|
|
4
|
-
"version": "1.1
|
|
4
|
+
"version": "1.2.1",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=20.6.0"
|
|
7
7
|
},
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"format": "prettier --write .",
|
|
44
44
|
"quick:test": "node --import=./tsnode.esm.js --enable-source-maps bin/test.ts",
|
|
45
45
|
"pretest": "npm run lint",
|
|
46
|
-
"test": "
|
|
46
|
+
"test": "npm run quick:test",
|
|
47
47
|
"prebuild": "npm run lint && npm run clean",
|
|
48
48
|
"build": "tsc",
|
|
49
49
|
"checks": "npm run lint && npm run typecheck && npm run test",
|
|
@@ -67,46 +67,56 @@
|
|
|
67
67
|
"license": "MIT",
|
|
68
68
|
"dependencies": {
|
|
69
69
|
"@opentelemetry/api": "^1.9.0",
|
|
70
|
-
"@opentelemetry/auto-instrumentations-node": "^0.
|
|
71
|
-
"@opentelemetry/core": "^2.
|
|
72
|
-
"@opentelemetry/exporter-metrics-otlp-grpc": "^0.
|
|
73
|
-
"@opentelemetry/exporter-trace-otlp-grpc": "^0.
|
|
74
|
-
"@opentelemetry/instrumentation": "^0.
|
|
75
|
-
"@opentelemetry/resources": "^2.
|
|
76
|
-
"@opentelemetry/sdk-metrics": "^2.
|
|
77
|
-
"@opentelemetry/sdk-node": "^0.
|
|
78
|
-
"@opentelemetry/sdk-trace-base": "^2.
|
|
79
|
-
"@opentelemetry/semantic-conventions": "^1.
|
|
80
|
-
"@poppinss/utils": "^
|
|
81
|
-
"import-in-the-middle": "^
|
|
70
|
+
"@opentelemetry/auto-instrumentations-node": "^0.70.0",
|
|
71
|
+
"@opentelemetry/core": "^2.5.1",
|
|
72
|
+
"@opentelemetry/exporter-metrics-otlp-grpc": "^0.212.0",
|
|
73
|
+
"@opentelemetry/exporter-trace-otlp-grpc": "^0.212.0",
|
|
74
|
+
"@opentelemetry/instrumentation": "^0.212.0",
|
|
75
|
+
"@opentelemetry/resources": "^2.5.1",
|
|
76
|
+
"@opentelemetry/sdk-metrics": "^2.5.1",
|
|
77
|
+
"@opentelemetry/sdk-node": "^0.212.0",
|
|
78
|
+
"@opentelemetry/sdk-trace-base": "^2.5.1",
|
|
79
|
+
"@opentelemetry/semantic-conventions": "^1.39.0",
|
|
80
|
+
"@poppinss/utils": "^7.0.0",
|
|
81
|
+
"import-in-the-middle": "^3.0.0"
|
|
82
82
|
},
|
|
83
83
|
"devDependencies": {
|
|
84
|
-
"@adonisjs/assembler": "^
|
|
85
|
-
"@adonisjs/auth": "^
|
|
86
|
-
"@adonisjs/core": "^7.0.0
|
|
87
|
-
"@adonisjs/eslint-config": "
|
|
84
|
+
"@adonisjs/assembler": "^8.0.0",
|
|
85
|
+
"@adonisjs/auth": "^10.0.0",
|
|
86
|
+
"@adonisjs/core": "^7.0.0",
|
|
87
|
+
"@adonisjs/eslint-config": "3.0.0",
|
|
88
88
|
"@adonisjs/prettier-config": "^1.4.5",
|
|
89
|
-
"@adonisjs/tsconfig": "^
|
|
90
|
-
"@japa/assert": "^4.
|
|
91
|
-
"@japa/runner": "^
|
|
89
|
+
"@adonisjs/tsconfig": "^2.0.0",
|
|
90
|
+
"@japa/assert": "^4.2.0",
|
|
91
|
+
"@japa/runner": "^5.3.0",
|
|
92
92
|
"@julr/otel-instrumentation-clickhouse": "^1.0.2",
|
|
93
|
-
"@opentelemetry/context-async-hooks": "^2.
|
|
94
|
-
"@release-it/conventional-changelog": "^10.0.
|
|
95
|
-
"@sentry/node": "^10.
|
|
96
|
-
"@swc/core": "^1.15.
|
|
97
|
-
"@types/node": "^
|
|
98
|
-
"c8": "^
|
|
93
|
+
"@opentelemetry/context-async-hooks": "^2.5.1",
|
|
94
|
+
"@release-it/conventional-changelog": "^10.0.5",
|
|
95
|
+
"@sentry/node": "^10.40.0",
|
|
96
|
+
"@swc/core": "^1.15.13",
|
|
97
|
+
"@types/node": "^25.3.0",
|
|
98
|
+
"c8": "^11.0.0",
|
|
99
99
|
"copyfiles": "^2.4.1",
|
|
100
100
|
"del-cli": "^7.0.0",
|
|
101
|
-
"eslint": "^
|
|
102
|
-
"prettier": "^3.
|
|
103
|
-
"release-it": "^19.
|
|
101
|
+
"eslint": "^10.0.2",
|
|
102
|
+
"prettier": "^3.8.1",
|
|
103
|
+
"release-it": "^19.2.4",
|
|
104
104
|
"ts-node-maintained": "^10.9.6",
|
|
105
105
|
"typescript": "^5.9.3"
|
|
106
106
|
},
|
|
107
107
|
"peerDependencies": {
|
|
108
|
+
"@adonisjs/assembler": "^8.0.0",
|
|
109
|
+
"@adonisjs/auth": "^10.0.0",
|
|
108
110
|
"@adonisjs/core": "^6.2.0 || ^7.0.0 || ^7.0.0-next.27"
|
|
109
111
|
},
|
|
112
|
+
"peerDependenciesMeta": {
|
|
113
|
+
"@adonisjs/assembler": {
|
|
114
|
+
"optional": true
|
|
115
|
+
},
|
|
116
|
+
"@adonisjs/auth": {
|
|
117
|
+
"optional": true
|
|
118
|
+
}
|
|
119
|
+
},
|
|
110
120
|
"publishConfig": {
|
|
111
121
|
"provenance": true,
|
|
112
122
|
"access": "public"
|