@open-mercato/telemetry 0.6.7-develop.6834.1.e76f4b8cbc
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/.turbo/turbo-build.log +2 -0
- package/AGENTS.md +71 -0
- package/README.md +164 -0
- package/build.mjs +10 -0
- package/dist/env.js +44 -0
- package/dist/env.js.map +7 -0
- package/dist/facade/context.js +14 -0
- package/dist/facade/context.js.map +7 -0
- package/dist/facade/http.js +18 -0
- package/dist/facade/http.js.map +7 -0
- package/dist/facade/logger-bridge.js +40 -0
- package/dist/facade/logger-bridge.js.map +7 -0
- package/dist/facade/meter.js +16 -0
- package/dist/facade/meter.js.map +7 -0
- package/dist/facade/propagation.js +20 -0
- package/dist/facade/propagation.js.map +7 -0
- package/dist/facade/redact.js +21 -0
- package/dist/facade/redact.js.map +7 -0
- package/dist/facade/report-error.js +26 -0
- package/dist/facade/report-error.js.map +7 -0
- package/dist/facade/serialize.js +35 -0
- package/dist/facade/serialize.js.map +7 -0
- package/dist/facade/tracer.js +21 -0
- package/dist/facade/tracer.js.map +7 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +7 -0
- package/dist/init.js +98 -0
- package/dist/init.js.map +7 -0
- package/dist/nextjs-config.js +21 -0
- package/dist/nextjs-config.js.map +7 -0
- package/dist/nextjs.js +30 -0
- package/dist/nextjs.js.map +7 -0
- package/dist/provider/console-provider.js +70 -0
- package/dist/provider/console-provider.js.map +7 -0
- package/dist/provider/noop-provider.js +45 -0
- package/dist/provider/noop-provider.js.map +7 -0
- package/dist/provider/otlp-provider.js +212 -0
- package/dist/provider/otlp-provider.js.map +7 -0
- package/dist/provider/registry.js +40 -0
- package/dist/provider/registry.js.map +7 -0
- package/dist/provider/run-span.js +34 -0
- package/dist/provider/run-span.js.map +7 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +7 -0
- package/jest.config.cjs +27 -0
- package/package.json +68 -0
- package/src/__tests__/default-unloaded.test.ts +58 -0
- package/src/__tests__/env-load-order.test.ts +80 -0
- package/src/__tests__/env.test.ts +72 -0
- package/src/__tests__/logger-level.test.ts +64 -0
- package/src/__tests__/nextjs-shutdown.test.ts +71 -0
- package/src/__tests__/nextjs.test.ts +122 -0
- package/src/__tests__/otlp-integration.test.ts +264 -0
- package/src/__tests__/pg-instrumentation.test.ts +88 -0
- package/src/__tests__/pg-pii-config.test.ts +14 -0
- package/src/__tests__/redact.test.ts +92 -0
- package/src/__tests__/registry.test.ts +77 -0
- package/src/__tests__/run-span.test.ts +56 -0
- package/src/__tests__/serialize.test.ts +47 -0
- package/src/__tests__/telemetry.test.ts +172 -0
- package/src/env.ts +71 -0
- package/src/facade/context.ts +17 -0
- package/src/facade/http.ts +25 -0
- package/src/facade/logger-bridge.ts +50 -0
- package/src/facade/meter.ts +20 -0
- package/src/facade/propagation.ts +31 -0
- package/src/facade/redact.ts +54 -0
- package/src/facade/report-error.ts +41 -0
- package/src/facade/serialize.ts +46 -0
- package/src/facade/tracer.ts +25 -0
- package/src/index.ts +30 -0
- package/src/init.ts +134 -0
- package/src/nextjs-config.ts +22 -0
- package/src/nextjs.ts +47 -0
- package/src/provider/console-provider.ts +87 -0
- package/src/provider/noop-provider.ts +53 -0
- package/src/provider/otlp-provider.ts +316 -0
- package/src/provider/registry.ts +60 -0
- package/src/provider/run-span.ts +45 -0
- package/src/types.ts +108 -0
- package/tsconfig.json +9 -0
- package/watch.mjs +6 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { TelemetryProvider } from '../types'
|
|
2
|
+
import { NoopProvider } from './noop-provider'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The active provider is a PROCESS-WIDE singleton, stored on `globalThis` rather
|
|
6
|
+
* than in a module variable. This is load-bearing: the worker bootstrap
|
|
7
|
+
* esbuild-bundles the generated DI registry into a `.mjs` that can inline a
|
|
8
|
+
* *private copy* of this module, while job handlers are dynamic-imported as
|
|
9
|
+
* source and use the *source copy*. With module-level state, `initTelemetry()`
|
|
10
|
+
* would set the provider on one copy while the handlers read a still-noop other
|
|
11
|
+
* — so worker logs/spans would never reach the backend. Keying the state on a
|
|
12
|
+
* global symbol makes every copy share one provider. Mirrors the framework's own
|
|
13
|
+
* DI-registrar global.
|
|
14
|
+
*/
|
|
15
|
+
const GLOBAL_KEY = Symbol.for('@open-mercato/telemetry.providerRegistry')
|
|
16
|
+
|
|
17
|
+
type ProviderRegistry = {
|
|
18
|
+
active: TelemetryProvider
|
|
19
|
+
registered: Map<string, TelemetryProvider>
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function store(): ProviderRegistry {
|
|
23
|
+
const g = globalThis as unknown as Record<symbol, ProviderRegistry | undefined>
|
|
24
|
+
let reg = g[GLOBAL_KEY]
|
|
25
|
+
if (!reg) {
|
|
26
|
+
reg = { active: new NoopProvider(), registered: new Map() }
|
|
27
|
+
g[GLOBAL_KEY] = reg
|
|
28
|
+
}
|
|
29
|
+
return reg
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Plug a custom backend before `initTelemetry()` (e.g. tests or a bespoke sink). */
|
|
33
|
+
export function registerProvider(provider: TelemetryProvider): void {
|
|
34
|
+
store().registered.set(provider.name, provider)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function getRegisteredProvider(name: string): TelemetryProvider | undefined {
|
|
38
|
+
return store().registered.get(name)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function getActiveProvider(): TelemetryProvider {
|
|
42
|
+
return store().active
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Set the active provider. Called by `initTelemetry()`. */
|
|
46
|
+
export function setActiveProvider(provider: TelemetryProvider): void {
|
|
47
|
+
store().active = provider
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Restore only the active provider while preserving custom registrations. */
|
|
51
|
+
export function clearActiveProvider(): void {
|
|
52
|
+
store().active = new NoopProvider()
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Test-only: restore the default no-op provider. */
|
|
56
|
+
export function resetActiveProvider(): void {
|
|
57
|
+
const reg = store()
|
|
58
|
+
clearActiveProvider()
|
|
59
|
+
reg.registered.clear()
|
|
60
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { Span } from '../types'
|
|
2
|
+
|
|
3
|
+
function isThenable(value: unknown): value is Promise<unknown> {
|
|
4
|
+
return (
|
|
5
|
+
value != null &&
|
|
6
|
+
(typeof value === 'object' || typeof value === 'function') &&
|
|
7
|
+
typeof (value as { then?: unknown }).then === 'function'
|
|
8
|
+
)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Run `fn(span)` and end `span` when it settles — synchronously for sync
|
|
13
|
+
* returns, on resolve/reject for promises. Exceptions (sync throw or rejected
|
|
14
|
+
* promise) are recorded on the span and re-thrown. Shared by the console and
|
|
15
|
+
* OTLP providers so async spans get correct durations.
|
|
16
|
+
*/
|
|
17
|
+
export function runSpan<T>(span: Span, fn: (span: Span) => T): T {
|
|
18
|
+
let result: T
|
|
19
|
+
try {
|
|
20
|
+
result = fn(span)
|
|
21
|
+
} catch (error) {
|
|
22
|
+
span.recordException(error)
|
|
23
|
+
span.setStatus('error')
|
|
24
|
+
span.end()
|
|
25
|
+
throw error
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (isThenable(result)) {
|
|
29
|
+
return result.then(
|
|
30
|
+
(value) => {
|
|
31
|
+
span.end()
|
|
32
|
+
return value
|
|
33
|
+
},
|
|
34
|
+
(error) => {
|
|
35
|
+
span.recordException(error)
|
|
36
|
+
span.setStatus('error')
|
|
37
|
+
span.end()
|
|
38
|
+
throw error
|
|
39
|
+
},
|
|
40
|
+
) as T
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
span.end()
|
|
44
|
+
return result
|
|
45
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public, vendor-neutral telemetry contract.
|
|
3
|
+
*
|
|
4
|
+
* Nothing in this package's facade may import any `@opentelemetry/*` package —
|
|
5
|
+
* only `provider/otlp-provider.ts` does, and it is loaded dynamically so the SDK
|
|
6
|
+
* never resolves when telemetry is off.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** Signal categories a provider may support. Unsupported signals no-op. */
|
|
10
|
+
export type TelemetrySignal = 'traces' | 'metrics' | 'logs' | 'errors'
|
|
11
|
+
|
|
12
|
+
/** Attribute primitive values allowed on spans/logs/metrics (low-cardinality, no PII). */
|
|
13
|
+
export type AttributeValue = string | number | boolean
|
|
14
|
+
export type Attributes = Record<string, AttributeValue | undefined>
|
|
15
|
+
|
|
16
|
+
export type LogLevel = 'debug' | 'info' | 'warn' | 'error'
|
|
17
|
+
|
|
18
|
+
/** A structured log record routed through the active provider. */
|
|
19
|
+
export type LogRecord = {
|
|
20
|
+
level: LogLevel
|
|
21
|
+
message: string
|
|
22
|
+
attributes?: Attributes
|
|
23
|
+
/** Serialized error (stack only, no PII payloads). */
|
|
24
|
+
error?: { name: string; message: string; stack?: string }
|
|
25
|
+
/** Epoch milliseconds; defaults to emit time. */
|
|
26
|
+
time?: number
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** A single metric observation. */
|
|
30
|
+
export type MetricKind = 'counter' | 'histogram' | 'gauge'
|
|
31
|
+
export type MetricPoint = {
|
|
32
|
+
kind: MetricKind
|
|
33
|
+
name: string
|
|
34
|
+
value: number
|
|
35
|
+
/** Low-cardinality labels only (never tenant/org/user IDs — those are span attributes). */
|
|
36
|
+
labels?: Attributes
|
|
37
|
+
/** UCUM unit (e.g. `s`, `By`); set once per metric name at instrument creation. */
|
|
38
|
+
unit?: string
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Options when starting a span. */
|
|
42
|
+
export type SpanKind = 'internal' | 'server' | 'client' | 'producer' | 'consumer'
|
|
43
|
+
export type SpanOptions = {
|
|
44
|
+
kind?: SpanKind
|
|
45
|
+
attributes?: Attributes
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Vendor-neutral handle to an active span. Backed by a real OTEL span under the
|
|
50
|
+
* OTLP provider, or a cheap shim under noop/console.
|
|
51
|
+
*/
|
|
52
|
+
export interface Span {
|
|
53
|
+
setAttribute(key: string, value: AttributeValue): void
|
|
54
|
+
setAttributes(attributes: Attributes): void
|
|
55
|
+
recordException(error: unknown): void
|
|
56
|
+
setStatus(status: 'ok' | 'error', message?: string): void
|
|
57
|
+
end(): void
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** The active trace + span ids, for correlating structured logs with traces. */
|
|
61
|
+
export type TraceContext = { traceId: string; spanId: string }
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* A telemetry backend.
|
|
65
|
+
*
|
|
66
|
+
* Tracing uses a DELEGATION model — the provider OWNS span creation and runs
|
|
67
|
+
* `fn` inside the span's context (`runInSpan`). This is required so manually
|
|
68
|
+
* created spans and OTEL auto-instrumentation spans (pg/http) share one trace;
|
|
69
|
+
* a detached "finished span" sink cannot propagate context. Logs/metrics stay
|
|
70
|
+
* sink-style.
|
|
71
|
+
*/
|
|
72
|
+
export interface TelemetryProvider {
|
|
73
|
+
readonly name: string
|
|
74
|
+
readonly supports: readonly TelemetrySignal[]
|
|
75
|
+
/** Initialize the backend (SDK start, exporters). Idempotent. */
|
|
76
|
+
start(): Promise<void>
|
|
77
|
+
/** Flush and tear down. Idempotent. */
|
|
78
|
+
shutdown(): Promise<void>
|
|
79
|
+
/** Run `fn` inside a new active span. Records exceptions + duration automatically. */
|
|
80
|
+
runInSpan<T>(name: string, options: SpanOptions, fn: (span: Span) => T): T
|
|
81
|
+
/** The currently active span in this async context, if any. */
|
|
82
|
+
activeSpan(): Span | undefined
|
|
83
|
+
/** The active trace/span ids (for log correlation), or undefined if none is active. */
|
|
84
|
+
activeTraceContext(): TraceContext | undefined
|
|
85
|
+
/** Write the active trace context into `carrier` (W3C propagation). */
|
|
86
|
+
inject(carrier: TraceCarrier): void
|
|
87
|
+
/** Extract a parent trace context from `carrier`, then run `fn` in a new active span under it. */
|
|
88
|
+
runInRemoteSpan<T>(carrier: TraceCarrier, name: string, options: SpanOptions, fn: (span: Span) => T): T
|
|
89
|
+
/** Emit a structured log record (no-op if `logs` unsupported). */
|
|
90
|
+
emitLog(record: LogRecord): void
|
|
91
|
+
/** Record a metric observation (no-op if `metrics` unsupported). */
|
|
92
|
+
recordMetric(point: MetricPoint): void
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Carrier for cross-boundary trace context (W3C `traceparent`/`tracestate`).
|
|
97
|
+
* Embedded in queue job payloads / event payloads to link a producer's trace to
|
|
98
|
+
* the consumer's spans (the queue/event bus expose no metadata channel).
|
|
99
|
+
*/
|
|
100
|
+
export type TraceCarrier = Record<string, string>
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Resolved configuration for the active backend. `signoz`, `newrelic`, and the
|
|
104
|
+
* generic `otlp` all select the same OTLP provider — they differ only by the
|
|
105
|
+
* standard `OTEL_EXPORTER_OTLP_ENDPOINT`/`_HEADERS` that point it at a vendor.
|
|
106
|
+
* Modern New Relic ingests OTLP directly, so it needs no bespoke exporter.
|
|
107
|
+
*/
|
|
108
|
+
export type TelemetryBackendName = 'noop' | 'console' | 'signoz' | 'newrelic' | 'otlp'
|
package/tsconfig.json
ADDED