@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.
Files changed (82) hide show
  1. package/.turbo/turbo-build.log +2 -0
  2. package/AGENTS.md +71 -0
  3. package/README.md +164 -0
  4. package/build.mjs +10 -0
  5. package/dist/env.js +44 -0
  6. package/dist/env.js.map +7 -0
  7. package/dist/facade/context.js +14 -0
  8. package/dist/facade/context.js.map +7 -0
  9. package/dist/facade/http.js +18 -0
  10. package/dist/facade/http.js.map +7 -0
  11. package/dist/facade/logger-bridge.js +40 -0
  12. package/dist/facade/logger-bridge.js.map +7 -0
  13. package/dist/facade/meter.js +16 -0
  14. package/dist/facade/meter.js.map +7 -0
  15. package/dist/facade/propagation.js +20 -0
  16. package/dist/facade/propagation.js.map +7 -0
  17. package/dist/facade/redact.js +21 -0
  18. package/dist/facade/redact.js.map +7 -0
  19. package/dist/facade/report-error.js +26 -0
  20. package/dist/facade/report-error.js.map +7 -0
  21. package/dist/facade/serialize.js +35 -0
  22. package/dist/facade/serialize.js.map +7 -0
  23. package/dist/facade/tracer.js +21 -0
  24. package/dist/facade/tracer.js.map +7 -0
  25. package/dist/index.js +21 -0
  26. package/dist/index.js.map +7 -0
  27. package/dist/init.js +98 -0
  28. package/dist/init.js.map +7 -0
  29. package/dist/nextjs-config.js +21 -0
  30. package/dist/nextjs-config.js.map +7 -0
  31. package/dist/nextjs.js +30 -0
  32. package/dist/nextjs.js.map +7 -0
  33. package/dist/provider/console-provider.js +70 -0
  34. package/dist/provider/console-provider.js.map +7 -0
  35. package/dist/provider/noop-provider.js +45 -0
  36. package/dist/provider/noop-provider.js.map +7 -0
  37. package/dist/provider/otlp-provider.js +212 -0
  38. package/dist/provider/otlp-provider.js.map +7 -0
  39. package/dist/provider/registry.js +40 -0
  40. package/dist/provider/registry.js.map +7 -0
  41. package/dist/provider/run-span.js +34 -0
  42. package/dist/provider/run-span.js.map +7 -0
  43. package/dist/types.js +1 -0
  44. package/dist/types.js.map +7 -0
  45. package/jest.config.cjs +27 -0
  46. package/package.json +68 -0
  47. package/src/__tests__/default-unloaded.test.ts +58 -0
  48. package/src/__tests__/env-load-order.test.ts +80 -0
  49. package/src/__tests__/env.test.ts +72 -0
  50. package/src/__tests__/logger-level.test.ts +64 -0
  51. package/src/__tests__/nextjs-shutdown.test.ts +71 -0
  52. package/src/__tests__/nextjs.test.ts +122 -0
  53. package/src/__tests__/otlp-integration.test.ts +264 -0
  54. package/src/__tests__/pg-instrumentation.test.ts +88 -0
  55. package/src/__tests__/pg-pii-config.test.ts +14 -0
  56. package/src/__tests__/redact.test.ts +92 -0
  57. package/src/__tests__/registry.test.ts +77 -0
  58. package/src/__tests__/run-span.test.ts +56 -0
  59. package/src/__tests__/serialize.test.ts +47 -0
  60. package/src/__tests__/telemetry.test.ts +172 -0
  61. package/src/env.ts +71 -0
  62. package/src/facade/context.ts +17 -0
  63. package/src/facade/http.ts +25 -0
  64. package/src/facade/logger-bridge.ts +50 -0
  65. package/src/facade/meter.ts +20 -0
  66. package/src/facade/propagation.ts +31 -0
  67. package/src/facade/redact.ts +54 -0
  68. package/src/facade/report-error.ts +41 -0
  69. package/src/facade/serialize.ts +46 -0
  70. package/src/facade/tracer.ts +25 -0
  71. package/src/index.ts +30 -0
  72. package/src/init.ts +134 -0
  73. package/src/nextjs-config.ts +22 -0
  74. package/src/nextjs.ts +47 -0
  75. package/src/provider/console-provider.ts +87 -0
  76. package/src/provider/noop-provider.ts +53 -0
  77. package/src/provider/otlp-provider.ts +316 -0
  78. package/src/provider/registry.ts +60 -0
  79. package/src/provider/run-span.ts +45 -0
  80. package/src/types.ts +108 -0
  81. package/tsconfig.json +9 -0
  82. 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
@@ -0,0 +1,9 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "extends": "../../tsconfig.base.json",
4
+ "compilerOptions": {
5
+ "noEmit": true
6
+ },
7
+ "include": ["src/**/*"],
8
+ "exclude": ["node_modules", "dist", "**/__tests__/**"]
9
+ }
package/watch.mjs ADDED
@@ -0,0 +1,6 @@
1
+ import { watch } from '../../scripts/watch.mjs'
2
+ import { dirname } from 'node:path'
3
+ import { fileURLToPath } from 'node:url'
4
+
5
+ const __dirname = dirname(fileURLToPath(import.meta.url))
6
+ watch(__dirname)