autotel 4.0.0 → 4.1.0
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 +26 -1
- package/dist/auto.cjs +2 -2
- package/dist/auto.js +1 -1
- package/dist/correlation-id.cjs +1 -1
- package/dist/correlation-id.js +1 -1
- package/dist/decorators.cjs +1 -1
- package/dist/decorators.js +1 -1
- package/dist/{event-Dlqr4ZNL.cjs → event-BhHREDJk.cjs} +3 -3
- package/dist/{event-Dlqr4ZNL.cjs.map → event-BhHREDJk.cjs.map} +1 -1
- package/dist/{event-_58ryBjh.js → event-ByBTV9M2.js} +3 -3
- package/dist/{event-_58ryBjh.js.map → event-ByBTV9M2.js.map} +1 -1
- package/dist/event.cjs +1 -1
- package/dist/event.js +1 -1
- package/dist/{functional-BGkT8J-h.js → functional-DtI0u4vx.js} +19 -19
- package/dist/functional-DtI0u4vx.js.map +1 -0
- package/dist/{functional-C4CzoVrX.cjs → functional-zpzNLhky.cjs} +4 -4
- package/dist/{functional-C4CzoVrX.cjs.map → functional-zpzNLhky.cjs.map} +1 -1
- package/dist/functional.cjs +1 -1
- package/dist/functional.js +1 -1
- package/dist/http.cjs +1 -1
- package/dist/http.js +1 -1
- package/dist/index.cjs +5 -5
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +5 -5
- package/dist/{init-DJQOdVlN.d.ts → init-B7u-DjxM.d.ts} +57 -2
- package/dist/init-B7u-DjxM.d.ts.map +1 -0
- package/dist/{init-DvapOXCc.cjs → init-BX7AmFRl.cjs} +40 -21
- package/dist/init-BX7AmFRl.cjs.map +1 -0
- package/dist/{init-Ch6t7MNI.js → init-D-jnNMix.js} +39 -20
- package/dist/init-D-jnNMix.js.map +1 -0
- package/dist/{init-CNp-ee80.d.cts → init-DSrRmVnz.d.cts} +57 -2
- package/dist/init-DSrRmVnz.d.cts.map +1 -0
- package/dist/instrumentation.cjs +1 -1
- package/dist/instrumentation.js +1 -1
- package/dist/logger-D3Ej3DII.js +446 -0
- package/dist/logger-D3Ej3DII.js.map +1 -0
- package/dist/logger-thMPLpOG.cjs +487 -0
- package/dist/logger-thMPLpOG.cjs.map +1 -0
- package/dist/logger.cjs +8 -236
- package/dist/logger.js +2 -204
- package/dist/messaging.cjs +1 -1
- package/dist/messaging.js +1 -1
- package/dist/semantic-helpers.cjs +1 -1
- package/dist/semantic-helpers.js +1 -1
- package/dist/{track-3HY4NGV-.cjs → track-D59FfpL0.cjs} +2 -2
- package/dist/{track-3HY4NGV-.cjs.map → track-D59FfpL0.cjs.map} +1 -1
- package/dist/{track-nsKVy-pj.js → track-wc0HafS_.js} +6 -6
- package/dist/track-wc0HafS_.js.map +1 -0
- package/dist/webhook.cjs +1 -1
- package/dist/webhook.js +1 -1
- package/dist/workflow-distributed.cjs +1 -1
- package/dist/workflow-distributed.js +1 -1
- package/dist/workflow.cjs +1 -1
- package/dist/workflow.js +1 -1
- package/dist/{yaml-config-B3dQ82GR.cjs → yaml-config-Ck2uB0Dp.cjs} +2 -1
- package/dist/yaml-config-Ck2uB0Dp.cjs.map +1 -0
- package/dist/yaml-config.cjs +1 -1
- package/dist/yaml-config.d.cts +7 -1
- package/dist/yaml-config.d.cts.map +1 -1
- package/dist/yaml-config.d.ts +7 -1
- package/dist/yaml-config.d.ts.map +1 -1
- package/dist/yaml-config.js +1 -0
- package/dist/yaml-config.js.map +1 -1
- package/package.json +1 -1
- package/skills/autotel-core/SKILL.md +2 -0
- package/skills/autotel-instrumentation/SKILL.md +25 -0
- package/skills/debug-missing-spans/SKILL.md +3 -1
- package/skills/migrate-to-autotel/SKILL.md +24 -23
- package/skills/review-otel-patterns/SKILL.md +5 -4
- package/src/init.customization.test.ts +71 -0
- package/src/init.ts +167 -40
- package/src/yaml-config.test.ts +36 -0
- package/src/yaml-config.ts +10 -1
- package/dist/functional-BGkT8J-h.js.map +0 -1
- package/dist/init-CNp-ee80.d.cts.map +0 -1
- package/dist/init-Ch6t7MNI.js.map +0 -1
- package/dist/init-DJQOdVlN.d.ts.map +0 -1
- package/dist/init-DvapOXCc.cjs.map +0 -1
- package/dist/logger.cjs.map +0 -1
- package/dist/logger.js.map +0 -1
- package/dist/track-nsKVy-pj.js.map +0 -1
- package/dist/yaml-config-B3dQ82GR.cjs.map +0 -1
|
@@ -74,7 +74,7 @@ export function register() {
|
|
|
74
74
|
if (process.env.NEXT_RUNTIME === 'nodejs') {
|
|
75
75
|
init({
|
|
76
76
|
service: 'my-app',
|
|
77
|
-
|
|
77
|
+
endpoint: process.env.OTLP_ENDPOINT!,
|
|
78
78
|
sampling: { rates: { server: 25, client: 5 } },
|
|
79
79
|
attributeRedactor: 'default',
|
|
80
80
|
});
|
|
@@ -201,7 +201,7 @@ export const handler = withLambda(async (event) => {
|
|
|
201
201
|
```typescript
|
|
202
202
|
import { init, trace } from 'autotel';
|
|
203
203
|
|
|
204
|
-
init({ service: 'my-worker',
|
|
204
|
+
init({ service: 'my-worker', endpoint: process.env.OTLP_ENDPOINT! });
|
|
205
205
|
|
|
206
206
|
const processJob = trace(async (job: Job) => {
|
|
207
207
|
// span auto-named after the function
|
|
@@ -219,7 +219,8 @@ All options work with `init()`, framework adapters, and `wrapModule` / `defineWo
|
|
|
219
219
|
| Option | Type | Default | Description |
|
|
220
220
|
| --------------------------------------- | --------------------------------------------------------------- | ----------------- | --------------------------------------------------------------- |
|
|
221
221
|
| `service` / `service.name` | `string` | `'app'` | Service name in `service.name` resource attribute |
|
|
222
|
-
| `
|
|
222
|
+
| `endpoint` | `string` | — | Single OTLP destination shorthand |
|
|
223
|
+
| `destinations` | `Array<{ endpoint, headers?, protocol?, signals? }>` | — | Declarative OTLP fan-out to multiple backends |
|
|
223
224
|
| `spanProcessors` | `SpanProcessor[]` | — | Use **instead of** `exporter` for full control |
|
|
224
225
|
| `sampling.rates` | `{ server?: number, client?: number, internal?: number }` | `100%` | Head sampling per span kind (0–100%) |
|
|
225
226
|
| `sampling.tail` | `TailSampleFn` | — | Keep traces matching predicate (e.g. errors, slow) |
|
|
@@ -291,7 +292,7 @@ Switch backends with **no code changes** — autotel speaks OTLP HTTP/JSON and H
|
|
|
291
292
|
| New Relic | `https://otlp.nr-data.net/v1/traces` | `{ 'api-key': '<key>' }` |
|
|
292
293
|
| Local Jaeger / Tempo / Collector | `http://localhost:4318/v1/traces` | — |
|
|
293
294
|
|
|
294
|
-
Use `init({
|
|
295
|
+
Use `init({ endpoint, headers })` for one backend. For multiple OTLP backends, prefer `init({ destinations: [...] })`. Drop to `composeSpanProcessors([batchA, batchB])` only when you need custom processor-level control.
|
|
295
296
|
|
|
296
297
|
---
|
|
297
298
|
|
|
@@ -325,6 +325,77 @@ describe('init() customization', () => {
|
|
|
325
325
|
});
|
|
326
326
|
});
|
|
327
327
|
|
|
328
|
+
it('supports declarative multi-destination OTLP fan-out', async () => {
|
|
329
|
+
const {
|
|
330
|
+
init,
|
|
331
|
+
traceExporterOptions,
|
|
332
|
+
metricExporterOptions,
|
|
333
|
+
logExporterOptions,
|
|
334
|
+
metricReaderOptions,
|
|
335
|
+
} = await loadInitWithMocks();
|
|
336
|
+
|
|
337
|
+
init({
|
|
338
|
+
service: 'fanout-app',
|
|
339
|
+
logs: true,
|
|
340
|
+
destinations: [
|
|
341
|
+
{
|
|
342
|
+
endpoint: 'https://otlp-gateway.grafana.net/otlp',
|
|
343
|
+
headers: { Authorization: 'Basic grafana' },
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
endpoint: 'https://api.honeycomb.io',
|
|
347
|
+
headers: { 'x-honeycomb-team': 'hny' },
|
|
348
|
+
signals: ['traces'],
|
|
349
|
+
},
|
|
350
|
+
],
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
expect(traceExporterOptions).toHaveLength(2);
|
|
354
|
+
expect(traceExporterOptions[0]).toMatchObject({
|
|
355
|
+
url: 'https://otlp-gateway.grafana.net/otlp/v1/traces',
|
|
356
|
+
headers: { Authorization: 'Basic grafana' },
|
|
357
|
+
});
|
|
358
|
+
expect(traceExporterOptions[1]).toMatchObject({
|
|
359
|
+
url: 'https://api.honeycomb.io/v1/traces',
|
|
360
|
+
headers: { 'x-honeycomb-team': 'hny' },
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
expect(metricExporterOptions).toHaveLength(1);
|
|
364
|
+
expect(metricExporterOptions[0]).toMatchObject({
|
|
365
|
+
url: 'https://otlp-gateway.grafana.net/otlp/v1/metrics',
|
|
366
|
+
});
|
|
367
|
+
expect(metricReaderOptions).toHaveLength(1);
|
|
368
|
+
|
|
369
|
+
expect(logExporterOptions).toHaveLength(1);
|
|
370
|
+
expect(logExporterOptions[0]).toMatchObject({
|
|
371
|
+
url: 'https://otlp-gateway.grafana.net/otlp/v1/logs',
|
|
372
|
+
});
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
it('lets destinations inherit top-level protocol and headers', async () => {
|
|
376
|
+
const { init, traceExporterOptions, metricExporterOptions } =
|
|
377
|
+
await loadInitWithMocks();
|
|
378
|
+
|
|
379
|
+
init({
|
|
380
|
+
service: 'fanout-inherited',
|
|
381
|
+
protocol: 'http',
|
|
382
|
+
headers: 'Authorization=Bearer shared',
|
|
383
|
+
destinations: [
|
|
384
|
+
{ endpoint: 'https://grafana.example.com/otlp' },
|
|
385
|
+
{ endpoint: 'https://honeycomb.example.com' },
|
|
386
|
+
],
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
expect(traceExporterOptions).toHaveLength(2);
|
|
390
|
+
expect(traceExporterOptions[0]).toMatchObject({
|
|
391
|
+
headers: { Authorization: 'Bearer shared' },
|
|
392
|
+
});
|
|
393
|
+
expect(traceExporterOptions[1]).toMatchObject({
|
|
394
|
+
headers: { Authorization: 'Bearer shared' },
|
|
395
|
+
});
|
|
396
|
+
expect(metricExporterOptions).toHaveLength(2);
|
|
397
|
+
});
|
|
398
|
+
|
|
328
399
|
it('resolves sampling preset shorthand to a sampler instance', async () => {
|
|
329
400
|
const { init, getDefaultSampler } = await loadInitWithMocks();
|
|
330
401
|
|
package/src/init.ts
CHANGED
|
@@ -126,6 +126,33 @@ type OTLPExporterConfig = {
|
|
|
126
126
|
concurrencyLimit?: number;
|
|
127
127
|
};
|
|
128
128
|
|
|
129
|
+
export type OtlpSignal = 'traces' | 'metrics' | 'logs';
|
|
130
|
+
|
|
131
|
+
export interface OtlpDestinationConfig {
|
|
132
|
+
/**
|
|
133
|
+
* Base OTLP endpoint for this destination.
|
|
134
|
+
* HTTP destinations may omit `/v1/{signal}`; autotel appends it automatically.
|
|
135
|
+
* gRPC destinations should point at the collector host:port.
|
|
136
|
+
*/
|
|
137
|
+
endpoint: string;
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Headers for this destination. Falls back to top-level `headers`.
|
|
141
|
+
*/
|
|
142
|
+
headers?: Record<string, string> | string;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Protocol for this destination. Falls back to top-level `protocol`.
|
|
146
|
+
*/
|
|
147
|
+
protocol?: 'http' | 'grpc';
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Signals to send to this destination.
|
|
151
|
+
* Defaults to all signals supported by the current init() config.
|
|
152
|
+
*/
|
|
153
|
+
signals?: OtlpSignal[];
|
|
154
|
+
}
|
|
155
|
+
|
|
129
156
|
// Lazy-load gRPC exporters (optional peer dependencies)
|
|
130
157
|
let OTLPTraceExporterGRPC:
|
|
131
158
|
| (new (config: OTLPExporterConfig) => SpanExporter)
|
|
@@ -415,11 +442,45 @@ export interface AutotelConfig {
|
|
|
415
442
|
|
|
416
443
|
/**
|
|
417
444
|
* OTLP endpoint for traces/metrics/logs
|
|
445
|
+
* Single-destination shorthand. For multi-backend OTLP fan-out, use
|
|
446
|
+
* `destinations` instead.
|
|
418
447
|
* Only used if you don't provide custom exporters/processors
|
|
419
448
|
* @default process.env.OTLP_ENDPOINT || 'http://localhost:4318'
|
|
420
449
|
*/
|
|
421
450
|
endpoint?: string;
|
|
422
451
|
|
|
452
|
+
/**
|
|
453
|
+
* Declarative OTLP multi-destination config.
|
|
454
|
+
* Each destination can override endpoint, headers, protocol, and signals.
|
|
455
|
+
*
|
|
456
|
+
* This is the high-level alternative to wiring `spanExporters`,
|
|
457
|
+
* `spanProcessors`, `metricReaders`, and `logRecordProcessors` manually when
|
|
458
|
+
* you want to fan telemetry out to multiple OTLP backends.
|
|
459
|
+
*
|
|
460
|
+
* When provided, `destinations` takes precedence over the single `endpoint`
|
|
461
|
+
* shorthand for built-in OTLP exporters/readers/processors.
|
|
462
|
+
*
|
|
463
|
+
* @example Grafana + Honeycomb for traces, Grafana only for metrics/logs
|
|
464
|
+
* ```typescript
|
|
465
|
+
* init({
|
|
466
|
+
* service: 'my-app',
|
|
467
|
+
* logs: true,
|
|
468
|
+
* destinations: [
|
|
469
|
+
* {
|
|
470
|
+
* endpoint: 'https://otlp-gateway-prod-eu-west-2.grafana.net/otlp',
|
|
471
|
+
* headers: { Authorization: 'Basic ...' },
|
|
472
|
+
* },
|
|
473
|
+
* {
|
|
474
|
+
* endpoint: 'https://api.honeycomb.io',
|
|
475
|
+
* headers: { 'x-honeycomb-team': '...' },
|
|
476
|
+
* signals: ['traces'],
|
|
477
|
+
* },
|
|
478
|
+
* ],
|
|
479
|
+
* })
|
|
480
|
+
* ```
|
|
481
|
+
*/
|
|
482
|
+
destinations?: OtlpDestinationConfig[];
|
|
483
|
+
|
|
423
484
|
/**
|
|
424
485
|
* Custom span processors for traces (supports multiple processors)
|
|
425
486
|
* Allows you to use any backend: Jaeger, Zipkin, Datadog, New Relic, etc.
|
|
@@ -1406,6 +1467,45 @@ function normalizeOtlpHeaders(
|
|
|
1406
1467
|
return parsed;
|
|
1407
1468
|
}
|
|
1408
1469
|
|
|
1470
|
+
type ResolvedOtlpDestination = {
|
|
1471
|
+
endpoint: string;
|
|
1472
|
+
protocol: 'http' | 'grpc';
|
|
1473
|
+
headers?: Record<string, string>;
|
|
1474
|
+
signals?: Set<OtlpSignal>;
|
|
1475
|
+
};
|
|
1476
|
+
|
|
1477
|
+
function resolveOtlpDestinations(
|
|
1478
|
+
config: AutotelConfig,
|
|
1479
|
+
fallbackEndpoint?: string,
|
|
1480
|
+
): ResolvedOtlpDestination[] {
|
|
1481
|
+
const rawDestinations =
|
|
1482
|
+
config.destinations !== undefined
|
|
1483
|
+
? config.destinations
|
|
1484
|
+
: fallbackEndpoint
|
|
1485
|
+
? [
|
|
1486
|
+
{
|
|
1487
|
+
endpoint: fallbackEndpoint,
|
|
1488
|
+
headers: config.headers,
|
|
1489
|
+
protocol: config.protocol,
|
|
1490
|
+
},
|
|
1491
|
+
]
|
|
1492
|
+
: [];
|
|
1493
|
+
|
|
1494
|
+
return rawDestinations.map((destination) => ({
|
|
1495
|
+
endpoint: destination.endpoint,
|
|
1496
|
+
protocol: resolveProtocol(destination.protocol ?? config.protocol),
|
|
1497
|
+
headers: normalizeOtlpHeaders(destination.headers ?? config.headers),
|
|
1498
|
+
signals: destination.signals ? new Set(destination.signals) : undefined,
|
|
1499
|
+
}));
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
function destinationSupportsSignal(
|
|
1503
|
+
destination: ResolvedOtlpDestination,
|
|
1504
|
+
signal: OtlpSignal,
|
|
1505
|
+
): boolean {
|
|
1506
|
+
return destination.signals ? destination.signals.has(signal) : true;
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1409
1509
|
/**
|
|
1410
1510
|
* Initialize autotel - Write Once, Observe Everywhere
|
|
1411
1511
|
*
|
|
@@ -1533,7 +1633,6 @@ export function init(cfg: AutotelConfig): void {
|
|
|
1533
1633
|
// Initialize OpenTelemetry
|
|
1534
1634
|
// Only use endpoint if explicitly configured (no default fallback)
|
|
1535
1635
|
let endpoint = mergedConfig.endpoint ?? devtoolsConfig.endpoint;
|
|
1536
|
-
const otlpHeaders = normalizeOtlpHeaders(mergedConfig.headers);
|
|
1537
1636
|
const version = mergedConfig.version || detectVersion();
|
|
1538
1637
|
const environment =
|
|
1539
1638
|
mergedConfig.environment || process.env.NODE_ENV || 'development';
|
|
@@ -1600,8 +1699,7 @@ export function init(cfg: AutotelConfig): void {
|
|
|
1600
1699
|
);
|
|
1601
1700
|
}
|
|
1602
1701
|
|
|
1603
|
-
|
|
1604
|
-
const protocol = resolveProtocol(mergedConfig.protocol);
|
|
1702
|
+
const otlpDestinations = resolveOtlpDestinations(mergedConfig, endpoint);
|
|
1605
1703
|
|
|
1606
1704
|
// Backward-compatible singular aliases. Plural forms take precedence when both are provided.
|
|
1607
1705
|
const configuredSpanProcessors =
|
|
@@ -1643,16 +1741,23 @@ export function init(cfg: AutotelConfig): void {
|
|
|
1643
1741
|
new TailSamplingSpanProcessor(new BatchSpanProcessor(exporter)),
|
|
1644
1742
|
);
|
|
1645
1743
|
}
|
|
1646
|
-
} else
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1744
|
+
} else {
|
|
1745
|
+
for (const destination of otlpDestinations) {
|
|
1746
|
+
if (!destinationSupportsSignal(destination, 'traces')) continue;
|
|
1747
|
+
|
|
1748
|
+
const traceExporter = createTraceExporter(destination.protocol, {
|
|
1749
|
+
url: formatEndpointUrl(
|
|
1750
|
+
destination.endpoint,
|
|
1751
|
+
'traces',
|
|
1752
|
+
destination.protocol,
|
|
1753
|
+
),
|
|
1754
|
+
headers: destination.headers,
|
|
1755
|
+
});
|
|
1756
|
+
|
|
1757
|
+
spanProcessors.push(
|
|
1758
|
+
new TailSamplingSpanProcessor(new BatchSpanProcessor(traceExporter)),
|
|
1759
|
+
);
|
|
1760
|
+
}
|
|
1656
1761
|
}
|
|
1657
1762
|
// If no endpoint and no custom processors/exporters, array remains empty
|
|
1658
1763
|
// SDK will still work but won't export traces
|
|
@@ -1760,18 +1865,25 @@ export function init(cfg: AutotelConfig): void {
|
|
|
1760
1865
|
if (configuredMetricReaders && configuredMetricReaders.length > 0) {
|
|
1761
1866
|
// User provided custom metric readers
|
|
1762
1867
|
metricReaders.push(...configuredMetricReaders);
|
|
1763
|
-
} else if (metricsEnabled
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1868
|
+
} else if (metricsEnabled) {
|
|
1869
|
+
for (const destination of otlpDestinations) {
|
|
1870
|
+
if (!destinationSupportsSignal(destination, 'metrics')) continue;
|
|
1871
|
+
|
|
1872
|
+
const metricExporter = createMetricExporter(destination.protocol, {
|
|
1873
|
+
url: formatEndpointUrl(
|
|
1874
|
+
destination.endpoint,
|
|
1875
|
+
'metrics',
|
|
1876
|
+
destination.protocol,
|
|
1877
|
+
),
|
|
1878
|
+
headers: destination.headers,
|
|
1879
|
+
});
|
|
1880
|
+
|
|
1881
|
+
metricReaders.push(
|
|
1882
|
+
new PeriodicExportingMetricReader({
|
|
1883
|
+
exporter: metricExporter,
|
|
1884
|
+
}),
|
|
1885
|
+
);
|
|
1886
|
+
}
|
|
1775
1887
|
}
|
|
1776
1888
|
|
|
1777
1889
|
let logRecordProcessors: LogRecordProcessor[] | undefined;
|
|
@@ -1782,24 +1894,39 @@ export function init(cfg: AutotelConfig): void {
|
|
|
1782
1894
|
logRecordProcessors = [...configuredLogRecordProcessors];
|
|
1783
1895
|
}
|
|
1784
1896
|
|
|
1785
|
-
// Auto-configure OTLP log
|
|
1786
|
-
if (logsEnabled
|
|
1787
|
-
const
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1897
|
+
// Auto-configure OTLP log exporters when logs are enabled.
|
|
1898
|
+
if (logsEnabled) {
|
|
1899
|
+
for (const destination of otlpDestinations) {
|
|
1900
|
+
if (!destinationSupportsSignal(destination, 'logs')) continue;
|
|
1901
|
+
|
|
1902
|
+
const logExporter = createLogExporter(destination.protocol, {
|
|
1903
|
+
url: formatEndpointUrl(
|
|
1904
|
+
destination.endpoint,
|
|
1905
|
+
'logs',
|
|
1906
|
+
destination.protocol,
|
|
1907
|
+
),
|
|
1908
|
+
headers: destination.headers,
|
|
1909
|
+
});
|
|
1791
1910
|
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1911
|
+
let processor: LogRecordProcessor = new BatchLogRecordProcessor(
|
|
1912
|
+
logExporter,
|
|
1913
|
+
);
|
|
1914
|
+
if (_stringRedactor) {
|
|
1915
|
+
processor = new RedactingLogRecordProcessor(processor, _stringRedactor);
|
|
1916
|
+
}
|
|
1917
|
+
if (!logRecordProcessors) {
|
|
1918
|
+
logRecordProcessors = [];
|
|
1919
|
+
}
|
|
1920
|
+
logRecordProcessors.push(processor);
|
|
1797
1921
|
}
|
|
1798
|
-
|
|
1799
|
-
|
|
1922
|
+
|
|
1923
|
+
if (
|
|
1924
|
+
otlpDestinations.some((destination) =>
|
|
1925
|
+
destinationSupportsSignal(destination, 'logs'),
|
|
1926
|
+
)
|
|
1927
|
+
) {
|
|
1928
|
+
logger.info({}, '[autotel] OTLP log exporter configured');
|
|
1800
1929
|
}
|
|
1801
|
-
logRecordProcessors.push(processor);
|
|
1802
|
-
logger.info({}, '[autotel] OTLP log exporter configured');
|
|
1803
1930
|
}
|
|
1804
1931
|
|
|
1805
1932
|
// PostHog OTLP logs integration
|
package/src/yaml-config.test.ts
CHANGED
|
@@ -70,6 +70,42 @@ exporter:
|
|
|
70
70
|
});
|
|
71
71
|
});
|
|
72
72
|
|
|
73
|
+
it('should parse exporter destinations configuration', () => {
|
|
74
|
+
const yaml = `
|
|
75
|
+
exporter:
|
|
76
|
+
destinations:
|
|
77
|
+
- endpoint: https://otlp-gateway.grafana.net/otlp
|
|
78
|
+
headers:
|
|
79
|
+
Authorization: Basic grafana
|
|
80
|
+
- endpoint: https://api.honeycomb.io
|
|
81
|
+
protocol: grpc
|
|
82
|
+
headers:
|
|
83
|
+
x-honeycomb-team: secret-key
|
|
84
|
+
signals:
|
|
85
|
+
- traces
|
|
86
|
+
`;
|
|
87
|
+
const filePath = path.join(testDir, 'exporter-destinations.yaml');
|
|
88
|
+
writeFileSync(filePath, yaml);
|
|
89
|
+
|
|
90
|
+
const config = loadYamlConfigFromFile(filePath);
|
|
91
|
+
expect(config.destinations).toEqual([
|
|
92
|
+
{
|
|
93
|
+
endpoint: 'https://otlp-gateway.grafana.net/otlp',
|
|
94
|
+
headers: {
|
|
95
|
+
Authorization: 'Basic grafana',
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
endpoint: 'https://api.honeycomb.io',
|
|
100
|
+
protocol: 'grpc',
|
|
101
|
+
headers: {
|
|
102
|
+
'x-honeycomb-team': 'secret-key',
|
|
103
|
+
},
|
|
104
|
+
signals: ['traces'],
|
|
105
|
+
},
|
|
106
|
+
]);
|
|
107
|
+
});
|
|
108
|
+
|
|
73
109
|
it('should parse resource attributes', () => {
|
|
74
110
|
const yaml = `
|
|
75
111
|
resource:
|
package/src/yaml-config.ts
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
|
|
24
24
|
import { readFileSync, existsSync } from 'node:fs';
|
|
25
25
|
import path from 'node:path';
|
|
26
|
-
import type { AutotelConfig } from './init';
|
|
26
|
+
import type { AutotelConfig, OtlpSignal } from './init';
|
|
27
27
|
import {
|
|
28
28
|
AdaptiveSampler,
|
|
29
29
|
AlwaysSampler,
|
|
@@ -61,6 +61,12 @@ export interface YamlConfig {
|
|
|
61
61
|
endpoint?: string;
|
|
62
62
|
protocol?: 'http' | 'grpc';
|
|
63
63
|
headers?: Record<string, string>;
|
|
64
|
+
destinations?: Array<{
|
|
65
|
+
endpoint: string;
|
|
66
|
+
protocol?: 'http' | 'grpc';
|
|
67
|
+
headers?: Record<string, string>;
|
|
68
|
+
signals?: OtlpSignal[];
|
|
69
|
+
}>;
|
|
64
70
|
};
|
|
65
71
|
resource?: Record<string, string | number | boolean>;
|
|
66
72
|
sampling?: {
|
|
@@ -179,6 +185,9 @@ function yamlToAutotelConfig(yaml: YamlConfig): Partial<AutotelConfig> {
|
|
|
179
185
|
if (yaml.exporter?.endpoint) config.endpoint = yaml.exporter.endpoint;
|
|
180
186
|
if (yaml.exporter?.protocol) config.protocol = yaml.exporter.protocol;
|
|
181
187
|
if (yaml.exporter?.headers) config.headers = yaml.exporter.headers;
|
|
188
|
+
if (yaml.exporter?.destinations) {
|
|
189
|
+
config.destinations = yaml.exporter.destinations;
|
|
190
|
+
}
|
|
182
191
|
|
|
183
192
|
// Resource attributes (flattened)
|
|
184
193
|
if (yaml.resource) config.resourceAttributes = yaml.resource;
|