@geekmidas/telescope 10.0.0-alpha.57 → 10.0.0-alpha.59

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.
@@ -6,6 +6,7 @@ const require_instrumentation_hono = require("./hono.cjs");
6
6
  const require_instrumentation_middleware = require("./middleware.cjs");
7
7
  const require_instrumentation_otel = require("./otel.cjs");
8
8
  const require_instrumentation_setup = require("./setup.cjs");
9
+ exports.InvalidSampleRatio = require_instrumentation_setup.InvalidSampleRatio;
9
10
  exports.NoopSpanProcessor = require_instrumentation_core.NoopSpanProcessor;
10
11
  exports.OTelTelemetry = require_instrumentation_otel.OTelTelemetry;
11
12
  exports.addSpanEvent = require_instrumentation_tracing.addSpanEvent;
@@ -5,5 +5,5 @@ import { HonoTelemetryMiddlewareOptions, getSpanFromContext, getTraceContextFrom
5
5
  import { EndpointAttributes, HttpRequestAttributes, HttpResponseAttributes, HttpSpanAttributes, UserAttributes, createChildSpan, createHttpServerSpan, endHttpSpan, extractTraceContext, getConstructsTracer, injectTraceContext, isTracingEnabled, toOtelAttributes, withChildSpan, withHttpSpan } from "./http.cjs";
6
6
  import { TelemetryMiddlewareOptions, getContextFromEvent, getSpanFromEvent, telemetryMiddleware, withEventContext } from "./middleware.cjs";
7
7
  import { OTelTelemetry, OTelTelemetryOptions } from "./otel.cjs";
8
- import { TelemetryOptions, setupTelemetry, shutdownTelemetry } from "./setup.cjs";
9
- export { type EndpointAttributes, type HonoTelemetryMiddlewareOptions, type HttpRequestAttributes, type HttpResponseAttributes, type HttpSpanAttributes, NoopSpanProcessor, OTelTelemetry, type OTelTelemetryOptions, type SpanProcessorOptions, type SpanProcessorStrategy, type TelemetryMiddlewareOptions, type TelemetryOptions, type UserAttributes, addSpanEvent, createChildSpan, createHttpServerSpan, createSpan, createSpanProcessor, enableTelemetryDebug, endHttpSpan, extractTraceContext, flushTelemetry, getActiveSpan, getConstructsTracer, getContextFromEvent, getRecommendedStrategy, getSpanFromContext, getSpanFromEvent, getSpanId, getTraceContextFromHono, getTraceId, honoTelemetryMiddleware, injectTraceContext, isTelemetryInitialized, isTracingEnabled, recordException, setGlobalLogProcessor, setGlobalSpanProcessor, setSpanAttributes, setupTelemetry, shutdownTelemetry, telemetryMiddleware, toOtelAttributes, withChildSpan, withEventContext, withHonoSpanContext, withHttpSpan, withSpan, withSpanSync, withTelemetryFlush };
8
+ import { InvalidSampleRatio, TelemetryOptions, setupTelemetry, shutdownTelemetry } from "./setup.cjs";
9
+ export { type EndpointAttributes, type HonoTelemetryMiddlewareOptions, type HttpRequestAttributes, type HttpResponseAttributes, type HttpSpanAttributes, InvalidSampleRatio, NoopSpanProcessor, OTelTelemetry, type OTelTelemetryOptions, type SpanProcessorOptions, type SpanProcessorStrategy, type TelemetryMiddlewareOptions, type TelemetryOptions, type UserAttributes, addSpanEvent, createChildSpan, createHttpServerSpan, createSpan, createSpanProcessor, enableTelemetryDebug, endHttpSpan, extractTraceContext, flushTelemetry, getActiveSpan, getConstructsTracer, getContextFromEvent, getRecommendedStrategy, getSpanFromContext, getSpanFromEvent, getSpanId, getTraceContextFromHono, getTraceId, honoTelemetryMiddleware, injectTraceContext, isTelemetryInitialized, isTracingEnabled, recordException, setGlobalLogProcessor, setGlobalSpanProcessor, setSpanAttributes, setupTelemetry, shutdownTelemetry, telemetryMiddleware, toOtelAttributes, withChildSpan, withEventContext, withHonoSpanContext, withHttpSpan, withSpan, withSpanSync, withTelemetryFlush };
@@ -5,5 +5,5 @@ import { HonoTelemetryMiddlewareOptions, getSpanFromContext, getTraceContextFrom
5
5
  import { EndpointAttributes, HttpRequestAttributes, HttpResponseAttributes, HttpSpanAttributes, UserAttributes, createChildSpan, createHttpServerSpan, endHttpSpan, extractTraceContext, getConstructsTracer, injectTraceContext, isTracingEnabled, toOtelAttributes, withChildSpan, withHttpSpan } from "./http.mjs";
6
6
  import { TelemetryMiddlewareOptions, getContextFromEvent, getSpanFromEvent, telemetryMiddleware, withEventContext } from "./middleware.mjs";
7
7
  import { OTelTelemetry, OTelTelemetryOptions } from "./otel.mjs";
8
- import { TelemetryOptions, setupTelemetry, shutdownTelemetry } from "./setup.mjs";
9
- export { type EndpointAttributes, type HonoTelemetryMiddlewareOptions, type HttpRequestAttributes, type HttpResponseAttributes, type HttpSpanAttributes, NoopSpanProcessor, OTelTelemetry, type OTelTelemetryOptions, type SpanProcessorOptions, type SpanProcessorStrategy, type TelemetryMiddlewareOptions, type TelemetryOptions, type UserAttributes, addSpanEvent, createChildSpan, createHttpServerSpan, createSpan, createSpanProcessor, enableTelemetryDebug, endHttpSpan, extractTraceContext, flushTelemetry, getActiveSpan, getConstructsTracer, getContextFromEvent, getRecommendedStrategy, getSpanFromContext, getSpanFromEvent, getSpanId, getTraceContextFromHono, getTraceId, honoTelemetryMiddleware, injectTraceContext, isTelemetryInitialized, isTracingEnabled, recordException, setGlobalLogProcessor, setGlobalSpanProcessor, setSpanAttributes, setupTelemetry, shutdownTelemetry, telemetryMiddleware, toOtelAttributes, withChildSpan, withEventContext, withHonoSpanContext, withHttpSpan, withSpan, withSpanSync, withTelemetryFlush };
8
+ import { InvalidSampleRatio, TelemetryOptions, setupTelemetry, shutdownTelemetry } from "./setup.mjs";
9
+ export { type EndpointAttributes, type HonoTelemetryMiddlewareOptions, type HttpRequestAttributes, type HttpResponseAttributes, type HttpSpanAttributes, InvalidSampleRatio, NoopSpanProcessor, OTelTelemetry, type OTelTelemetryOptions, type SpanProcessorOptions, type SpanProcessorStrategy, type TelemetryMiddlewareOptions, type TelemetryOptions, type UserAttributes, addSpanEvent, createChildSpan, createHttpServerSpan, createSpan, createSpanProcessor, enableTelemetryDebug, endHttpSpan, extractTraceContext, flushTelemetry, getActiveSpan, getConstructsTracer, getContextFromEvent, getRecommendedStrategy, getSpanFromContext, getSpanFromEvent, getSpanId, getTraceContextFromHono, getTraceId, honoTelemetryMiddleware, injectTraceContext, isTelemetryInitialized, isTracingEnabled, recordException, setGlobalLogProcessor, setGlobalSpanProcessor, setSpanAttributes, setupTelemetry, shutdownTelemetry, telemetryMiddleware, toOtelAttributes, withChildSpan, withEventContext, withHonoSpanContext, withHttpSpan, withSpan, withSpanSync, withTelemetryFlush };
@@ -4,5 +4,5 @@ import { createChildSpan, createHttpServerSpan, endHttpSpan, extractTraceContext
4
4
  import { getSpanFromContext, getTraceContextFromHono, honoTelemetryMiddleware, withHonoSpanContext } from "./hono.mjs";
5
5
  import { getContextFromEvent, getSpanFromEvent, telemetryMiddleware, withEventContext } from "./middleware.mjs";
6
6
  import { OTelTelemetry } from "./otel.mjs";
7
- import { setupTelemetry, shutdownTelemetry } from "./setup.mjs";
8
- export { NoopSpanProcessor, OTelTelemetry, addSpanEvent, createChildSpan, createHttpServerSpan, createSpan, createSpanProcessor, enableTelemetryDebug, endHttpSpan, extractTraceContext, flushTelemetry, getActiveSpan, getConstructsTracer, getContextFromEvent, getRecommendedStrategy, getSpanFromContext, getSpanFromEvent, getSpanId, getTraceContextFromHono, getTraceId, honoTelemetryMiddleware, injectTraceContext, isTelemetryInitialized, isTracingEnabled, recordException, setGlobalLogProcessor, setGlobalSpanProcessor, setSpanAttributes, setupTelemetry, shutdownTelemetry, telemetryMiddleware, toOtelAttributes, withChildSpan, withEventContext, withHonoSpanContext, withHttpSpan, withSpan, withSpanSync, withTelemetryFlush };
7
+ import { InvalidSampleRatio, setupTelemetry, shutdownTelemetry } from "./setup.mjs";
8
+ export { InvalidSampleRatio, NoopSpanProcessor, OTelTelemetry, addSpanEvent, createChildSpan, createHttpServerSpan, createSpan, createSpanProcessor, enableTelemetryDebug, endHttpSpan, extractTraceContext, flushTelemetry, getActiveSpan, getConstructsTracer, getContextFromEvent, getRecommendedStrategy, getSpanFromContext, getSpanFromEvent, getSpanId, getTraceContextFromHono, getTraceId, honoTelemetryMiddleware, injectTraceContext, isTelemetryInitialized, isTracingEnabled, recordException, setGlobalLogProcessor, setGlobalSpanProcessor, setSpanAttributes, setupTelemetry, shutdownTelemetry, telemetryMiddleware, toOtelAttributes, withChildSpan, withEventContext, withHonoSpanContext, withHttpSpan, withSpan, withSpanSync, withTelemetryFlush };
@@ -12,6 +12,29 @@ let _opentelemetry_sdk_trace_node = require("@opentelemetry/sdk-trace-node");
12
12
  let _opentelemetry_semantic_conventions = require("@opentelemetry/semantic-conventions");
13
13
  //#region src/instrumentation/setup.ts
14
14
  let sdk = null;
15
+ let onSigterm = null;
16
+ /** A `sampleRatio` outside 0–1, which no sampler can honour. */
17
+ var InvalidSampleRatio = class extends Error {
18
+ sampleRatio;
19
+ constructor(sampleRatio) {
20
+ super(`sampleRatio must be a number from 0 (no traces) to 1 (every trace), got ${sampleRatio}. Pass a fraction such as 0.1 to keep one trace in ten.`);
21
+ this.sampleRatio = sampleRatio;
22
+ this.name = "InvalidSampleRatio";
23
+ }
24
+ };
25
+ /** Whether the standard OTLP environment variables name a collector. */
26
+ function otlpEndpointFromEnv(signal) {
27
+ return Boolean(process.env[`OTEL_EXPORTER_OTLP_${signal}_ENDPOINT`] || process.env.OTEL_EXPORTER_OTLP_ENDPOINT);
28
+ }
29
+ /**
30
+ * The sampler for an explicit ratio, or undefined to let the SDK read
31
+ * `OTEL_TRACES_SAMPLER` / `OTEL_TRACES_SAMPLER_ARG`.
32
+ */
33
+ function samplerFor(sampleRatio) {
34
+ if (sampleRatio === void 0) return void 0;
35
+ if (!Number.isFinite(sampleRatio) || sampleRatio < 0 || sampleRatio > 1) throw new InvalidSampleRatio(sampleRatio);
36
+ return new _opentelemetry_sdk_trace_node.ParentBasedSampler({ root: new _opentelemetry_sdk_trace_node.TraceIdRatioBasedSampler(sampleRatio) });
37
+ }
15
38
  /**
16
39
  * Set up OpenTelemetry instrumentation.
17
40
  * Call this BEFORE importing your application code.
@@ -33,11 +56,17 @@ let sdk = null;
33
56
  */
34
57
  function setupTelemetry(options) {
35
58
  if (sdk) return;
36
- const { serviceName, serviceVersion = "1.0.0", endpoint, instrumentPino = true, autoInstrument = true, debug = false, resourceAttributes = {}, headers = {}, spanProcessorStrategy, environment = "server" } = options;
59
+ const { serviceName, serviceVersion = "1.0.0", serviceNamespace, deploymentEnvironment, endpoint, sampleRatio, handleSignals = true, instrumentPino = true, autoInstrument = true, debug = false, resourceAttributes = {}, headers, spanProcessorStrategy, environment = "server" } = options;
60
+ const sampler = samplerFor(sampleRatio);
37
61
  if (debug) _opentelemetry_api.diag.setLogger(new _opentelemetry_api.DiagConsoleLogger(), _opentelemetry_api.DiagLogLevel.DEBUG);
38
62
  const resource = (0, _opentelemetry_resources.resourceFromAttributes)({
39
63
  [_opentelemetry_semantic_conventions.ATTR_SERVICE_NAME]: serviceName,
40
64
  [_opentelemetry_semantic_conventions.ATTR_SERVICE_VERSION]: serviceVersion,
65
+ ...serviceNamespace && { "service.namespace": serviceNamespace },
66
+ ...deploymentEnvironment && {
67
+ "deployment.environment.name": deploymentEnvironment,
68
+ "deployment.environment": deploymentEnvironment
69
+ },
41
70
  ...resourceAttributes
42
71
  });
43
72
  const instrumentations = [];
@@ -56,45 +85,53 @@ function setupTelemetry(options) {
56
85
  let traceExporter;
57
86
  if (endpoint) traceExporter = new _opentelemetry_exporter_trace_otlp_http.OTLPTraceExporter({
58
87
  url: `${endpoint}/traces`,
59
- headers
88
+ headers: headers ?? {}
60
89
  });
90
+ else if (otlpEndpointFromEnv("TRACES")) traceExporter = new _opentelemetry_exporter_trace_otlp_http.OTLPTraceExporter(headers ? { headers } : {});
61
91
  else traceExporter = new _opentelemetry_sdk_trace_node.ConsoleSpanExporter();
62
92
  const strategy = spanProcessorStrategy ?? require_instrumentation_core.getRecommendedStrategy(environment);
63
93
  const spanProcessor = require_instrumentation_core.createSpanProcessor(traceExporter, { strategy });
64
94
  require_instrumentation_core.setGlobalSpanProcessor(spanProcessor);
95
+ let logProcessor;
96
+ if (endpoint || otlpEndpointFromEnv("LOGS")) {
97
+ const logExporter = endpoint ? new _opentelemetry_exporter_logs_otlp_http.OTLPLogExporter({
98
+ url: `${endpoint}/logs`,
99
+ headers: headers ?? {}
100
+ }) : new _opentelemetry_exporter_logs_otlp_http.OTLPLogExporter(headers ? { headers } : {});
101
+ logProcessor = new _opentelemetry_sdk_logs.BatchLogRecordProcessor({ exporter: logExporter });
102
+ require_instrumentation_core.setGlobalLogProcessor(logProcessor);
103
+ }
65
104
  sdk = new _opentelemetry_sdk_node.NodeSDK({
66
105
  resource,
67
106
  traceExporter,
68
107
  spanProcessors: [spanProcessor],
108
+ ...logProcessor && { logRecordProcessors: [logProcessor] },
109
+ ...sampler && { sampler },
69
110
  instrumentations
70
111
  });
71
- if (endpoint) {
72
- const logExporter = new _opentelemetry_exporter_logs_otlp_http.OTLPLogExporter({
73
- url: `${endpoint}/logs`,
74
- headers
75
- });
76
- const logProcessor = new _opentelemetry_sdk_logs.BatchLogRecordProcessor({ exporter: logExporter });
77
- new _opentelemetry_sdk_logs.LoggerProvider({
78
- resource,
79
- processors: [logProcessor]
80
- });
81
- require_instrumentation_core.setGlobalLogProcessor(logProcessor);
82
- }
83
112
  sdk.start();
84
- process.on("SIGTERM", () => {
85
- sdk?.shutdown().then(() => {}).catch((_error) => {}).finally(() => process.exit(0));
86
- });
113
+ if (handleSignals) {
114
+ onSigterm = () => {
115
+ sdk?.shutdown().then(() => {}).catch((_error) => {}).finally(() => process.exit(0));
116
+ };
117
+ process.on("SIGTERM", onSigterm);
118
+ }
87
119
  }
88
120
  /**
89
121
  * Shut down telemetry (for testing or graceful shutdown)
90
122
  */
91
123
  async function shutdownTelemetry() {
124
+ if (onSigterm) {
125
+ process.off("SIGTERM", onSigterm);
126
+ onSigterm = null;
127
+ }
92
128
  if (sdk) {
93
129
  await sdk.shutdown();
94
130
  sdk = null;
95
131
  }
96
132
  }
97
133
  //#endregion
134
+ exports.InvalidSampleRatio = InvalidSampleRatio;
98
135
  exports.setupTelemetry = setupTelemetry;
99
136
  exports.shutdownTelemetry = shutdownTelemetry;
100
137
 
@@ -1 +1 @@
1
- {"version":3,"file":"setup.cjs","names":["DiagConsoleLogger","DiagLogLevel","resourceFromAttributes","ATTR_SERVICE_NAME","ATTR_SERVICE_VERSION","PinoInstrumentation","getNodeAutoInstrumentations","OTLPTraceExporter","ConsoleSpanExporter","getRecommendedStrategy","createSpanProcessor","NodeSDK","OTLPLogExporter","BatchLogRecordProcessor","LoggerProvider"],"sources":["../../src/instrumentation/setup.ts"],"sourcesContent":["import { DiagConsoleLogger, DiagLogLevel, diag } from '@opentelemetry/api';\nimport { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';\nimport { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';\nimport { PinoInstrumentation } from '@opentelemetry/instrumentation-pino';\nimport { resourceFromAttributes } from '@opentelemetry/resources';\nimport {\n\tBatchLogRecordProcessor,\n\tLoggerProvider,\n} from '@opentelemetry/sdk-logs';\nimport { NodeSDK } from '@opentelemetry/sdk-node';\nimport {\n\tConsoleSpanExporter,\n\ttype SpanExporter,\n} from '@opentelemetry/sdk-trace-node';\nimport {\n\tATTR_SERVICE_NAME,\n\tATTR_SERVICE_VERSION,\n} from '@opentelemetry/semantic-conventions';\nimport type { SpanProcessorStrategy } from '../adapters/types';\nimport {\n\tcreateSpanProcessor,\n\tgetRecommendedStrategy,\n\tsetGlobalLogProcessor,\n\tsetGlobalSpanProcessor,\n} from './core';\n\n/**\n * Options for configuring telemetry\n */\nexport interface TelemetryOptions {\n\t/**\n\t * Service name for resource identification\n\t */\n\tserviceName: string;\n\n\t/**\n\t * Service version\n\t */\n\tserviceVersion?: string;\n\n\t/**\n\t * OTLP endpoint URL (e.g., 'http://localhost:3000/__telescope/v1')\n\t * If not provided, traces will be logged to console\n\t */\n\tendpoint?: string;\n\n\t/**\n\t * Whether to instrument Pino for log correlation\n\t * @default true\n\t */\n\tinstrumentPino?: boolean;\n\n\t/**\n\t * Whether to enable auto-instrumentation for common libraries\n\t * (http, fetch, express, etc.)\n\t * @default true\n\t */\n\tautoInstrument?: boolean;\n\n\t/**\n\t * Enable debug logging for OTel SDK\n\t * @default false\n\t */\n\tdebug?: boolean;\n\n\t/**\n\t * Additional resource attributes\n\t */\n\tresourceAttributes?: Record<string, string>;\n\n\t/**\n\t * Headers to send with OTLP requests\n\t */\n\theaders?: Record<string, string>;\n\n\t/**\n\t * Span processor strategy.\n\t * - 'batch': Efficient batching for long-running servers (default)\n\t * - 'simple': Immediate export for serverless environments (Lambda, Edge)\n\t *\n\t * If not specified, automatically selected based on environment detection.\n\t */\n\tspanProcessorStrategy?: SpanProcessorStrategy;\n\n\t/**\n\t * Environment type for automatic configuration\n\t * @default 'server'\n\t */\n\tenvironment?: 'server' | 'lambda' | 'edge' | 'custom';\n}\n\nlet sdk: NodeSDK | null = null;\n\n/**\n * Set up OpenTelemetry instrumentation.\n * Call this BEFORE importing your application code.\n *\n * @example\n * ```typescript\n * // instrumentation.ts (create this file)\n * import { setupTelemetry } from '@geekmidas/telescope/instrumentation';\n *\n * setupTelemetry({\n * serviceName: 'my-api',\n * endpoint: 'http://localhost:3000/__telescope/v1',\n * });\n *\n * // Then in your entry point:\n * // import './instrumentation';\n * // import { app } from './app';\n * ```\n */\nexport function setupTelemetry(options: TelemetryOptions): void {\n\tif (sdk) {\n\t\treturn;\n\t}\n\n\tconst {\n\t\tserviceName,\n\t\tserviceVersion = '1.0.0',\n\t\tendpoint,\n\t\tinstrumentPino = true,\n\t\tautoInstrument = true,\n\t\tdebug = false,\n\t\tresourceAttributes = {},\n\t\theaders = {},\n\t\tspanProcessorStrategy,\n\t\tenvironment = 'server',\n\t} = options;\n\n\t// Enable debug logging if requested\n\tif (debug) {\n\t\tdiag.setLogger(new DiagConsoleLogger(), DiagLogLevel.DEBUG);\n\t}\n\n\t// Create resource\n\t// `new Resource(...)` went in resources 2.x — a resource is now made from\n\t// attributes by a function, so a detector and a literal produce the same\n\t// thing rather than two shapes that had to be merged.\n\tconst resource = resourceFromAttributes({\n\t\t[ATTR_SERVICE_NAME]: serviceName,\n\t\t[ATTR_SERVICE_VERSION]: serviceVersion,\n\t\t...resourceAttributes,\n\t});\n\n\t// Build instrumentations list\n\tconst instrumentations = [];\n\n\t// Add Pino instrumentation for log correlation\n\tif (instrumentPino) {\n\t\tinstrumentations.push(\n\t\t\tnew PinoInstrumentation({\n\t\t\t\t// Inject trace context into log records\n\t\t\t\tlogHook: (span, record) => {\n\t\t\t\t\trecord.trace_id = span.spanContext().traceId;\n\t\t\t\t\trecord.span_id = span.spanContext().spanId;\n\t\t\t\t\trecord.trace_flags = span.spanContext().traceFlags;\n\t\t\t\t},\n\t\t\t}),\n\t\t);\n\t}\n\n\t// Add auto-instrumentations for common libraries\n\tif (autoInstrument) {\n\t\tinstrumentations.push(\n\t\t\tgetNodeAutoInstrumentations({\n\t\t\t\t// Disable file system instrumentation (too noisy)\n\t\t\t\t'@opentelemetry/instrumentation-fs': { enabled: false },\n\t\t\t\t// Configure HTTP instrumentation\n\t\t\t\t'@opentelemetry/instrumentation-http': {\n\t\t\t\t\tignoreIncomingRequestHook: (request) => {\n\t\t\t\t\t\t// Ignore health checks and internal routes\n\t\t\t\t\t\tconst path = request.url || '';\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\tpath.includes('/__health') ||\n\t\t\t\t\t\t\tpath.includes('/__telescope') ||\n\t\t\t\t\t\t\tpath.includes('/favicon')\n\t\t\t\t\t\t);\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}),\n\t\t);\n\t}\n\n\t// Create exporters\n\tlet traceExporter: SpanExporter;\n\tif (endpoint) {\n\t\ttraceExporter = new OTLPTraceExporter({\n\t\t\turl: `${endpoint}/traces`,\n\t\t\theaders,\n\t\t});\n\t} else {\n\t\t// Fall back to console exporter for debugging\n\t\ttraceExporter = new ConsoleSpanExporter();\n\t}\n\n\t// Determine span processor strategy\n\tconst strategy = spanProcessorStrategy ?? getRecommendedStrategy(environment);\n\tconst spanProcessor = createSpanProcessor(traceExporter, { strategy });\n\n\t// Register globally for flush operations\n\tsetGlobalSpanProcessor(spanProcessor);\n\n\t// Create and configure SDK\n\tsdk = new NodeSDK({\n\t\tresource,\n\t\ttraceExporter,\n\t\tspanProcessors: [spanProcessor],\n\t\tinstrumentations,\n\t});\n\n\t// Set up log exporter if endpoint is provided\n\tif (endpoint) {\n\t\tconst logExporter = new OTLPLogExporter({\n\t\t\turl: `${endpoint}/logs`,\n\t\t\theaders,\n\t\t});\n\n\t\t// Both changed in sdk-logs 2.x, and for the same reason the trace SDK\n\t\t// changed: a provider whose processors could be swapped after it had begun\n\t\t// emitting was never safe, so processors are constructor-only and the\n\t\t// exporter is named rather than positional.\n\t\tconst logProcessor = new BatchLogRecordProcessor({\n\t\t\texporter: logExporter,\n\t\t});\n\t\tconst loggerProvider = new LoggerProvider({\n\t\t\tresource,\n\t\t\tprocessors: [logProcessor],\n\t\t});\n\n\t\t// Register for flush operations\n\t\tsetGlobalLogProcessor(logProcessor);\n\t}\n\n\t// Start the SDK\n\tsdk.start();\n\n\t// Graceful shutdown\n\tprocess.on('SIGTERM', () => {\n\t\tsdk\n\t\t\t?.shutdown()\n\t\t\t.then(() => {})\n\t\t\t.catch((_error) => {})\n\t\t\t.finally(() => process.exit(0));\n\t});\n}\n\n/**\n * Shut down telemetry (for testing or graceful shutdown)\n */\nexport async function shutdownTelemetry(): Promise<void> {\n\tif (sdk) {\n\t\tawait sdk.shutdown();\n\t\tsdk = null;\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;AA4FA,IAAI,MAAsB;;;;;;;;;;;;;;;;;;;;AAqB1B,SAAgB,eAAe,SAAiC;CAC/D,IAAI,KACH;CAGD,MAAM,EACL,aACA,iBAAiB,SACjB,UACA,iBAAiB,MACjB,iBAAiB,MACjB,QAAQ,OACR,qBAAqB,CAAC,GACtB,UAAU,CAAC,GACX,uBACA,cAAc,aACX;CAGJ,IAAI,OACH,mBAAA,KAAK,UAAU,IAAIA,mBAAAA,kBAAkB,GAAGC,mBAAAA,aAAa,KAAK;CAO3D,MAAM,YAAA,GAAWC,yBAAAA,uBAAAA,CAAuB;GACtCC,oCAAAA,oBAAoB;GACpBC,oCAAAA,uBAAuB;EACxB,GAAG;CACJ,CAAC;CAGD,MAAM,mBAAmB,CAAC;CAG1B,IAAI,gBACH,iBAAiB,KAChB,IAAIC,oCAAAA,oBAAoB,EAEvB,UAAU,MAAM,WAAW;EAC1B,OAAO,WAAW,KAAK,YAAY,CAAC,CAAC;EACrC,OAAO,UAAU,KAAK,YAAY,CAAC,CAAC;EACpC,OAAO,cAAc,KAAK,YAAY,CAAC,CAAC;CACzC,EACD,CAAC,CACF;CAID,IAAI,gBACH,iBAAiB,MAAA,GAChBC,0CAAAA,4BAAAA,CAA4B;EAE3B,qCAAqC,EAAE,SAAS,MAAM;EAEtD,uCAAuC,EACtC,4BAA4B,YAAY;GAEvC,MAAM,OAAO,QAAQ,OAAO;GAC5B,OACC,KAAK,SAAS,WAAW,KACzB,KAAK,SAAS,cAAc,KAC5B,KAAK,SAAS,UAAU;EAE1B,EACD;CACD,CAAC,CACF;CAID,IAAI;CACJ,IAAI,UACH,gBAAgB,IAAIC,wCAAAA,kBAAkB;EACrC,KAAK,GAAG,SAAS;EACjB;CACD,CAAC;MAGD,gBAAgB,IAAIC,8BAAAA,oBAAoB;CAIzC,MAAM,WAAW,yBAAyBC,6BAAAA,uBAAuB,WAAW;CAC5E,MAAM,gBAAgBC,6BAAAA,oBAAoB,eAAe,EAAE,SAAS,CAAC;CAGrE,6BAAA,uBAAuB,aAAa;CAGpC,MAAM,IAAIC,wBAAAA,QAAQ;EACjB;EACA;EACA,gBAAgB,CAAC,aAAa;EAC9B;CACD,CAAC;CAGD,IAAI,UAAU;EACb,MAAM,cAAc,IAAIC,uCAAAA,gBAAgB;GACvC,KAAK,GAAG,SAAS;GACjB;EACD,CAAC;EAMD,MAAM,eAAe,IAAIC,wBAAAA,wBAAwB,EAChD,UAAU,YACX,CAAC;EACsB,IAAIC,wBAAAA,eAAe;GACzC;GACA,YAAY,CAAC,YAAY;EAC1B,CAAC;EAGD,6BAAA,sBAAsB,YAAY;CACnC;CAGA,IAAI,MAAM;CAGV,QAAQ,GAAG,iBAAiB;EAC3B,KACG,SAAS,CAAC,CACX,WAAW,CAAC,CAAC,CAAC,CACd,OAAO,WAAW,CAAC,CAAC,CAAC,CACrB,cAAc,QAAQ,KAAK,CAAC,CAAC;CAChC,CAAC;AACF;;;;AAKA,eAAsB,oBAAmC;CACxD,IAAI,KAAK;EACR,MAAM,IAAI,SAAS;EACnB,MAAM;CACP;AACD"}
1
+ {"version":3,"file":"setup.cjs","names":["ParentBasedSampler","TraceIdRatioBasedSampler","DiagConsoleLogger","DiagLogLevel","resourceFromAttributes","ATTR_SERVICE_NAME","ATTR_SERVICE_VERSION","PinoInstrumentation","getNodeAutoInstrumentations","OTLPTraceExporter","ConsoleSpanExporter","getRecommendedStrategy","createSpanProcessor","OTLPLogExporter","BatchLogRecordProcessor","NodeSDK"],"sources":["../../src/instrumentation/setup.ts"],"sourcesContent":["import { DiagConsoleLogger, DiagLogLevel, diag } from '@opentelemetry/api';\nimport { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';\nimport { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';\nimport { PinoInstrumentation } from '@opentelemetry/instrumentation-pino';\nimport { resourceFromAttributes } from '@opentelemetry/resources';\nimport { BatchLogRecordProcessor } from '@opentelemetry/sdk-logs';\nimport { NodeSDK } from '@opentelemetry/sdk-node';\nimport {\n\tConsoleSpanExporter,\n\tParentBasedSampler,\n\ttype Sampler,\n\ttype SpanExporter,\n\tTraceIdRatioBasedSampler,\n} from '@opentelemetry/sdk-trace-node';\nimport {\n\tATTR_SERVICE_NAME,\n\tATTR_SERVICE_VERSION,\n} from '@opentelemetry/semantic-conventions';\nimport type { SpanProcessorStrategy } from '../adapters/types';\nimport {\n\tcreateSpanProcessor,\n\tgetRecommendedStrategy,\n\tsetGlobalLogProcessor,\n\tsetGlobalSpanProcessor,\n} from './core';\n\n/**\n * Options for configuring telemetry\n */\nexport interface TelemetryOptions {\n\t/**\n\t * Service name for resource identification\n\t */\n\tserviceName: string;\n\n\t/**\n\t * Service version\n\t */\n\tserviceVersion?: string;\n\n\t/**\n\t * The `service.namespace` resource attribute — the product an app is part\n\t * of, so several services from one workspace group together.\n\t */\n\tserviceNamespace?: string;\n\n\t/**\n\t * The stage this process runs as (`production`, `staging`, …). Sent as\n\t * `deployment.environment.name`, and as the older `deployment.environment`\n\t * many backends still filter on.\n\t */\n\tdeploymentEnvironment?: string;\n\n\t/**\n\t * OTLP endpoint URL (e.g., 'http://localhost:3000/__telescope/v1').\n\t * Traces go to `<endpoint>/traces` and logs to `<endpoint>/logs`.\n\t *\n\t * If not provided, the standard `OTEL_EXPORTER_OTLP_ENDPOINT` (and its\n\t * per-signal `_TRACES_`/`_LOGS_` variants and `OTEL_EXPORTER_OTLP_HEADERS`)\n\t * configure the exporters, with the spec's `/v1/traces` and `/v1/logs`\n\t * paths. With neither, traces are written to the console.\n\t */\n\tendpoint?: string;\n\n\t/**\n\t * The fraction of new traces to sample, from 0 to 1. A span whose parent\n\t * was sampled (or not) follows its parent, so a trace is never cut in half.\n\t *\n\t * When left out, the standard `OTEL_TRACES_SAMPLER` and\n\t * `OTEL_TRACES_SAMPLER_ARG` decide (e.g. `parentbased_traceidratio` and\n\t * `0.1`); with neither, every trace is sampled.\n\t */\n\tsampleRatio?: number;\n\n\t/**\n\t * Shut the SDK down and exit on `SIGTERM`.\n\t *\n\t * Turn it off when the process has its own graceful shutdown — exiting as\n\t * soon as telemetry is flushed would cut in-flight requests short.\n\t * @default true\n\t */\n\thandleSignals?: boolean;\n\n\t/**\n\t * Whether to instrument Pino for log correlation\n\t * @default true\n\t */\n\tinstrumentPino?: boolean;\n\n\t/**\n\t * Whether to enable auto-instrumentation for common libraries\n\t * (http, fetch, express, etc.)\n\t * @default true\n\t */\n\tautoInstrument?: boolean;\n\n\t/**\n\t * Enable debug logging for OTel SDK\n\t * @default false\n\t */\n\tdebug?: boolean;\n\n\t/**\n\t * Additional resource attributes\n\t */\n\tresourceAttributes?: Record<string, string>;\n\n\t/**\n\t * Headers to send with OTLP requests\n\t */\n\theaders?: Record<string, string>;\n\n\t/**\n\t * Span processor strategy.\n\t * - 'batch': Efficient batching for long-running servers (default)\n\t * - 'simple': Immediate export for serverless environments (Lambda, Edge)\n\t *\n\t * If not specified, automatically selected based on environment detection.\n\t */\n\tspanProcessorStrategy?: SpanProcessorStrategy;\n\n\t/**\n\t * Environment type for automatic configuration\n\t * @default 'server'\n\t */\n\tenvironment?: 'server' | 'lambda' | 'edge' | 'custom';\n}\n\nlet sdk: NodeSDK | null = null;\nlet onSigterm: (() => void) | null = null;\n\n/** A `sampleRatio` outside 0–1, which no sampler can honour. */\nexport class InvalidSampleRatio extends Error {\n\tconstructor(readonly sampleRatio: number) {\n\t\tsuper(\n\t\t\t`sampleRatio must be a number from 0 (no traces) to 1 (every trace), got ${sampleRatio}. Pass a fraction such as 0.1 to keep one trace in ten.`,\n\t\t);\n\t\tthis.name = 'InvalidSampleRatio';\n\t}\n}\n\n/** Whether the standard OTLP environment variables name a collector. */\nfunction otlpEndpointFromEnv(signal: 'TRACES' | 'LOGS'): boolean {\n\treturn Boolean(\n\t\tprocess.env[`OTEL_EXPORTER_OTLP_${signal}_ENDPOINT`] ||\n\t\t\tprocess.env.OTEL_EXPORTER_OTLP_ENDPOINT,\n\t);\n}\n\n/**\n * The sampler for an explicit ratio, or undefined to let the SDK read\n * `OTEL_TRACES_SAMPLER` / `OTEL_TRACES_SAMPLER_ARG`.\n */\nfunction samplerFor(sampleRatio: number | undefined): Sampler | undefined {\n\tif (sampleRatio === undefined) return undefined;\n\tif (!Number.isFinite(sampleRatio) || sampleRatio < 0 || sampleRatio > 1) {\n\t\tthrow new InvalidSampleRatio(sampleRatio);\n\t}\n\treturn new ParentBasedSampler({\n\t\troot: new TraceIdRatioBasedSampler(sampleRatio),\n\t});\n}\n\n/**\n * Set up OpenTelemetry instrumentation.\n * Call this BEFORE importing your application code.\n *\n * @example\n * ```typescript\n * // instrumentation.ts (create this file)\n * import { setupTelemetry } from '@geekmidas/telescope/instrumentation';\n *\n * setupTelemetry({\n * serviceName: 'my-api',\n * endpoint: 'http://localhost:3000/__telescope/v1',\n * });\n *\n * // Then in your entry point:\n * // import './instrumentation';\n * // import { app } from './app';\n * ```\n */\nexport function setupTelemetry(options: TelemetryOptions): void {\n\tif (sdk) {\n\t\treturn;\n\t}\n\n\tconst {\n\t\tserviceName,\n\t\tserviceVersion = '1.0.0',\n\t\tserviceNamespace,\n\t\tdeploymentEnvironment,\n\t\tendpoint,\n\t\tsampleRatio,\n\t\thandleSignals = true,\n\t\tinstrumentPino = true,\n\t\tautoInstrument = true,\n\t\tdebug = false,\n\t\tresourceAttributes = {},\n\t\theaders,\n\t\tspanProcessorStrategy,\n\t\tenvironment = 'server',\n\t} = options;\n\n\t// Before anything starts, so a bad ratio leaves nothing half set up.\n\tconst sampler = samplerFor(sampleRatio);\n\n\t// Enable debug logging if requested\n\tif (debug) {\n\t\tdiag.setLogger(new DiagConsoleLogger(), DiagLogLevel.DEBUG);\n\t}\n\n\t// Create resource\n\t// `new Resource(...)` went in resources 2.x — a resource is now made from\n\t// attributes by a function, so a detector and a literal produce the same\n\t// thing rather than two shapes that had to be merged.\n\t//\n\t// The SDK merges what `OTEL_RESOURCE_ATTRIBUTES` and `OTEL_SERVICE_NAME`\n\t// say over this, so an operator can still rename or relabel a service.\n\tconst resource = resourceFromAttributes({\n\t\t[ATTR_SERVICE_NAME]: serviceName,\n\t\t[ATTR_SERVICE_VERSION]: serviceVersion,\n\t\t...(serviceNamespace && { 'service.namespace': serviceNamespace }),\n\t\t...(deploymentEnvironment && {\n\t\t\t'deployment.environment.name': deploymentEnvironment,\n\t\t\t'deployment.environment': deploymentEnvironment,\n\t\t}),\n\t\t...resourceAttributes,\n\t});\n\n\t// Build instrumentations list\n\tconst instrumentations = [];\n\n\t// Add Pino instrumentation for log correlation\n\tif (instrumentPino) {\n\t\tinstrumentations.push(\n\t\t\tnew PinoInstrumentation({\n\t\t\t\t// Inject trace context into log records\n\t\t\t\tlogHook: (span, record) => {\n\t\t\t\t\trecord.trace_id = span.spanContext().traceId;\n\t\t\t\t\trecord.span_id = span.spanContext().spanId;\n\t\t\t\t\trecord.trace_flags = span.spanContext().traceFlags;\n\t\t\t\t},\n\t\t\t}),\n\t\t);\n\t}\n\n\t// Add auto-instrumentations for common libraries\n\tif (autoInstrument) {\n\t\tinstrumentations.push(\n\t\t\tgetNodeAutoInstrumentations({\n\t\t\t\t// Disable file system instrumentation (too noisy)\n\t\t\t\t'@opentelemetry/instrumentation-fs': { enabled: false },\n\t\t\t\t// Configure HTTP instrumentation\n\t\t\t\t'@opentelemetry/instrumentation-http': {\n\t\t\t\t\tignoreIncomingRequestHook: (request) => {\n\t\t\t\t\t\t// Ignore health checks and internal routes\n\t\t\t\t\t\tconst path = request.url || '';\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\tpath.includes('/__health') ||\n\t\t\t\t\t\t\tpath.includes('/__telescope') ||\n\t\t\t\t\t\t\tpath.includes('/favicon')\n\t\t\t\t\t\t);\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}),\n\t\t);\n\t}\n\n\t// Create exporters\n\tlet traceExporter: SpanExporter;\n\tif (endpoint) {\n\t\ttraceExporter = new OTLPTraceExporter({\n\t\t\turl: `${endpoint}/traces`,\n\t\t\theaders: headers ?? {},\n\t\t});\n\t} else if (otlpEndpointFromEnv('TRACES')) {\n\t\t// No URL: the exporter reads the OTLP variables itself, the way any\n\t\t// other OpenTelemetry SDK would.\n\t\ttraceExporter = new OTLPTraceExporter(headers ? { headers } : {});\n\t} else {\n\t\t// Fall back to console exporter for debugging\n\t\ttraceExporter = new ConsoleSpanExporter();\n\t}\n\n\t// Determine span processor strategy\n\tconst strategy = spanProcessorStrategy ?? getRecommendedStrategy(environment);\n\tconst spanProcessor = createSpanProcessor(traceExporter, { strategy });\n\n\t// Register globally for flush operations\n\tsetGlobalSpanProcessor(spanProcessor);\n\n\t// Set up log exporter if a collector is named\n\tlet logProcessor: BatchLogRecordProcessor | undefined;\n\tif (endpoint || otlpEndpointFromEnv('LOGS')) {\n\t\tconst logExporter = endpoint\n\t\t\t? new OTLPLogExporter({\n\t\t\t\t\turl: `${endpoint}/logs`,\n\t\t\t\t\theaders: headers ?? {},\n\t\t\t\t})\n\t\t\t: new OTLPLogExporter(headers ? { headers } : {});\n\n\t\t// sdk-logs 2.x: processors are constructor-only and the exporter is\n\t\t// named rather than positional.\n\t\tlogProcessor = new BatchLogRecordProcessor({ exporter: logExporter });\n\n\t\t// Register for flush operations\n\t\tsetGlobalLogProcessor(logProcessor);\n\t}\n\n\t// Create and configure SDK. The SDK owns the logger provider, so the log\n\t// processor is handed to it rather than to a provider of our own — one\n\t// nothing registered would never receive a record.\n\tsdk = new NodeSDK({\n\t\tresource,\n\t\ttraceExporter,\n\t\tspanProcessors: [spanProcessor],\n\t\t...(logProcessor && { logRecordProcessors: [logProcessor] }),\n\t\t...(sampler && { sampler }),\n\t\tinstrumentations,\n\t});\n\n\t// Start the SDK\n\tsdk.start();\n\n\tif (handleSignals) {\n\t\t// Graceful shutdown\n\t\tonSigterm = () => {\n\t\t\tsdk\n\t\t\t\t?.shutdown()\n\t\t\t\t.then(() => {})\n\t\t\t\t.catch((_error) => {})\n\t\t\t\t.finally(() => process.exit(0));\n\t\t};\n\t\tprocess.on('SIGTERM', onSigterm);\n\t}\n}\n\n/**\n * Shut down telemetry (for testing or graceful shutdown)\n */\nexport async function shutdownTelemetry(): Promise<void> {\n\tif (onSigterm) {\n\t\tprocess.off('SIGTERM', onSigterm);\n\t\tonSigterm = null;\n\t}\n\tif (sdk) {\n\t\tawait sdk.shutdown();\n\t\tsdk = null;\n\t}\n}\n"],"mappings":"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"}
@@ -13,10 +13,43 @@ export interface TelemetryOptions {
13
13
  */
14
14
  serviceVersion?: string;
15
15
  /**
16
- * OTLP endpoint URL (e.g., 'http://localhost:3000/__telescope/v1')
17
- * If not provided, traces will be logged to console
16
+ * The `service.namespace` resource attribute — the product an app is part
17
+ * of, so several services from one workspace group together.
18
+ */
19
+ serviceNamespace?: string;
20
+ /**
21
+ * The stage this process runs as (`production`, `staging`, …). Sent as
22
+ * `deployment.environment.name`, and as the older `deployment.environment`
23
+ * many backends still filter on.
24
+ */
25
+ deploymentEnvironment?: string;
26
+ /**
27
+ * OTLP endpoint URL (e.g., 'http://localhost:3000/__telescope/v1').
28
+ * Traces go to `<endpoint>/traces` and logs to `<endpoint>/logs`.
29
+ *
30
+ * If not provided, the standard `OTEL_EXPORTER_OTLP_ENDPOINT` (and its
31
+ * per-signal `_TRACES_`/`_LOGS_` variants and `OTEL_EXPORTER_OTLP_HEADERS`)
32
+ * configure the exporters, with the spec's `/v1/traces` and `/v1/logs`
33
+ * paths. With neither, traces are written to the console.
18
34
  */
19
35
  endpoint?: string;
36
+ /**
37
+ * The fraction of new traces to sample, from 0 to 1. A span whose parent
38
+ * was sampled (or not) follows its parent, so a trace is never cut in half.
39
+ *
40
+ * When left out, the standard `OTEL_TRACES_SAMPLER` and
41
+ * `OTEL_TRACES_SAMPLER_ARG` decide (e.g. `parentbased_traceidratio` and
42
+ * `0.1`); with neither, every trace is sampled.
43
+ */
44
+ sampleRatio?: number;
45
+ /**
46
+ * Shut the SDK down and exit on `SIGTERM`.
47
+ *
48
+ * Turn it off when the process has its own graceful shutdown — exiting as
49
+ * soon as telemetry is flushed would cut in-flight requests short.
50
+ * @default true
51
+ */
52
+ handleSignals?: boolean;
20
53
  /**
21
54
  * Whether to instrument Pino for log correlation
22
55
  * @default true
@@ -55,6 +88,11 @@ export interface TelemetryOptions {
55
88
  */
56
89
  environment?: 'server' | 'lambda' | 'edge' | 'custom';
57
90
  }
91
+ /** A `sampleRatio` outside 0–1, which no sampler can honour. */
92
+ export declare class InvalidSampleRatio extends Error {
93
+ readonly sampleRatio: number;
94
+ constructor(sampleRatio: number);
95
+ }
58
96
  /**
59
97
  * Set up OpenTelemetry instrumentation.
60
98
  * Call this BEFORE importing your application code.
@@ -1 +1 @@
1
- {"version":3,"file":"setup.d.cts","names":[],"sources":["../../src/instrumentation/setup.ts"],"mappings":";;;;;iBA8BiB;;;;EAIhB;;;;EAKA;;;;;EAMA;;;;;EAMA;;;;;;EAOA;;;;;EAMA;;;;EAKA,qBAAqB;;;;EAKrB,UAAU;;;;;;;;EASV,wBAAwB;;;;;EAMxB;;;;;;;;;;;;;;;;;;;;;wBAwBe,eAAe,SAAS;;;;wBA0IlB,qBAAqB"}
1
+ {"version":3,"file":"setup.d.cts","names":[],"sources":["../../src/instrumentation/setup.ts"],"mappings":";;;;;iBA8BiB;;;;EAIhB;;;;EAKA;;;;;EAMA;;;;;;EAOA;;;;;;;;;;EAWA;;;;;;;;;EAUA;;;;;;;;EASA;;;;;EAMA;;;;;;EAOA;;;;;EAMA;;;;EAKA,qBAAqB;;;;EAKrB,UAAU;;;;;;;;EASV,wBAAwB;;;;;EAMxB;;;qBAOY,2BAA2B;WAClB;EAArB,YAAqB;;;;;;;;;;;;;;;;;;;;;wBAiDN,eAAe,SAAS;;;;wBA+JlB,qBAAqB"}
@@ -13,10 +13,43 @@ export interface TelemetryOptions {
13
13
  */
14
14
  serviceVersion?: string;
15
15
  /**
16
- * OTLP endpoint URL (e.g., 'http://localhost:3000/__telescope/v1')
17
- * If not provided, traces will be logged to console
16
+ * The `service.namespace` resource attribute — the product an app is part
17
+ * of, so several services from one workspace group together.
18
+ */
19
+ serviceNamespace?: string;
20
+ /**
21
+ * The stage this process runs as (`production`, `staging`, …). Sent as
22
+ * `deployment.environment.name`, and as the older `deployment.environment`
23
+ * many backends still filter on.
24
+ */
25
+ deploymentEnvironment?: string;
26
+ /**
27
+ * OTLP endpoint URL (e.g., 'http://localhost:3000/__telescope/v1').
28
+ * Traces go to `<endpoint>/traces` and logs to `<endpoint>/logs`.
29
+ *
30
+ * If not provided, the standard `OTEL_EXPORTER_OTLP_ENDPOINT` (and its
31
+ * per-signal `_TRACES_`/`_LOGS_` variants and `OTEL_EXPORTER_OTLP_HEADERS`)
32
+ * configure the exporters, with the spec's `/v1/traces` and `/v1/logs`
33
+ * paths. With neither, traces are written to the console.
18
34
  */
19
35
  endpoint?: string;
36
+ /**
37
+ * The fraction of new traces to sample, from 0 to 1. A span whose parent
38
+ * was sampled (or not) follows its parent, so a trace is never cut in half.
39
+ *
40
+ * When left out, the standard `OTEL_TRACES_SAMPLER` and
41
+ * `OTEL_TRACES_SAMPLER_ARG` decide (e.g. `parentbased_traceidratio` and
42
+ * `0.1`); with neither, every trace is sampled.
43
+ */
44
+ sampleRatio?: number;
45
+ /**
46
+ * Shut the SDK down and exit on `SIGTERM`.
47
+ *
48
+ * Turn it off when the process has its own graceful shutdown — exiting as
49
+ * soon as telemetry is flushed would cut in-flight requests short.
50
+ * @default true
51
+ */
52
+ handleSignals?: boolean;
20
53
  /**
21
54
  * Whether to instrument Pino for log correlation
22
55
  * @default true
@@ -55,6 +88,11 @@ export interface TelemetryOptions {
55
88
  */
56
89
  environment?: 'server' | 'lambda' | 'edge' | 'custom';
57
90
  }
91
+ /** A `sampleRatio` outside 0–1, which no sampler can honour. */
92
+ export declare class InvalidSampleRatio extends Error {
93
+ readonly sampleRatio: number;
94
+ constructor(sampleRatio: number);
95
+ }
58
96
  /**
59
97
  * Set up OpenTelemetry instrumentation.
60
98
  * Call this BEFORE importing your application code.
@@ -1 +1 @@
1
- {"version":3,"file":"setup.d.mts","names":[],"sources":["../../src/instrumentation/setup.ts"],"mappings":";;;;;iBA8BiB;;;;EAIhB;;;;EAKA;;;;;EAMA;;;;;EAMA;;;;;;EAOA;;;;;EAMA;;;;EAKA,qBAAqB;;;;EAKrB,UAAU;;;;;;;;EASV,wBAAwB;;;;;EAMxB;;;;;;;;;;;;;;;;;;;;;wBAwBe,eAAe,SAAS;;;;wBA0IlB,qBAAqB"}
1
+ {"version":3,"file":"setup.d.mts","names":[],"sources":["../../src/instrumentation/setup.ts"],"mappings":";;;;;iBA8BiB;;;;EAIhB;;;;EAKA;;;;;EAMA;;;;;;EAOA;;;;;;;;;;EAWA;;;;;;;;;EAUA;;;;;;;;EASA;;;;;EAMA;;;;;;EAOA;;;;;EAMA;;;;EAKA,qBAAqB;;;;EAKrB,UAAU;;;;;;;;EASV,wBAAwB;;;;;EAMxB;;;qBAOY,2BAA2B;WAClB;EAArB,YAAqB;;;;;;;;;;;;;;;;;;;;;wBAiDN,eAAe,SAAS;;;;wBA+JlB,qBAAqB"}
@@ -5,12 +5,35 @@ import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
5
5
  import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
6
6
  import { PinoInstrumentation } from "@opentelemetry/instrumentation-pino";
7
7
  import { resourceFromAttributes } from "@opentelemetry/resources";
8
- import { BatchLogRecordProcessor, LoggerProvider } from "@opentelemetry/sdk-logs";
8
+ import { BatchLogRecordProcessor } from "@opentelemetry/sdk-logs";
9
9
  import { NodeSDK } from "@opentelemetry/sdk-node";
10
- import { ConsoleSpanExporter } from "@opentelemetry/sdk-trace-node";
10
+ import { ConsoleSpanExporter, ParentBasedSampler, TraceIdRatioBasedSampler } from "@opentelemetry/sdk-trace-node";
11
11
  import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from "@opentelemetry/semantic-conventions";
12
12
  //#region src/instrumentation/setup.ts
13
13
  let sdk = null;
14
+ let onSigterm = null;
15
+ /** A `sampleRatio` outside 0–1, which no sampler can honour. */
16
+ var InvalidSampleRatio = class extends Error {
17
+ sampleRatio;
18
+ constructor(sampleRatio) {
19
+ super(`sampleRatio must be a number from 0 (no traces) to 1 (every trace), got ${sampleRatio}. Pass a fraction such as 0.1 to keep one trace in ten.`);
20
+ this.sampleRatio = sampleRatio;
21
+ this.name = "InvalidSampleRatio";
22
+ }
23
+ };
24
+ /** Whether the standard OTLP environment variables name a collector. */
25
+ function otlpEndpointFromEnv(signal) {
26
+ return Boolean(process.env[`OTEL_EXPORTER_OTLP_${signal}_ENDPOINT`] || process.env.OTEL_EXPORTER_OTLP_ENDPOINT);
27
+ }
28
+ /**
29
+ * The sampler for an explicit ratio, or undefined to let the SDK read
30
+ * `OTEL_TRACES_SAMPLER` / `OTEL_TRACES_SAMPLER_ARG`.
31
+ */
32
+ function samplerFor(sampleRatio) {
33
+ if (sampleRatio === void 0) return void 0;
34
+ if (!Number.isFinite(sampleRatio) || sampleRatio < 0 || sampleRatio > 1) throw new InvalidSampleRatio(sampleRatio);
35
+ return new ParentBasedSampler({ root: new TraceIdRatioBasedSampler(sampleRatio) });
36
+ }
14
37
  /**
15
38
  * Set up OpenTelemetry instrumentation.
16
39
  * Call this BEFORE importing your application code.
@@ -32,11 +55,17 @@ let sdk = null;
32
55
  */
33
56
  function setupTelemetry(options) {
34
57
  if (sdk) return;
35
- const { serviceName, serviceVersion = "1.0.0", endpoint, instrumentPino = true, autoInstrument = true, debug = false, resourceAttributes = {}, headers = {}, spanProcessorStrategy, environment = "server" } = options;
58
+ const { serviceName, serviceVersion = "1.0.0", serviceNamespace, deploymentEnvironment, endpoint, sampleRatio, handleSignals = true, instrumentPino = true, autoInstrument = true, debug = false, resourceAttributes = {}, headers, spanProcessorStrategy, environment = "server" } = options;
59
+ const sampler = samplerFor(sampleRatio);
36
60
  if (debug) diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.DEBUG);
37
61
  const resource = resourceFromAttributes({
38
62
  [ATTR_SERVICE_NAME]: serviceName,
39
63
  [ATTR_SERVICE_VERSION]: serviceVersion,
64
+ ...serviceNamespace && { "service.namespace": serviceNamespace },
65
+ ...deploymentEnvironment && {
66
+ "deployment.environment.name": deploymentEnvironment,
67
+ "deployment.environment": deploymentEnvironment
68
+ },
40
69
  ...resourceAttributes
41
70
  });
42
71
  const instrumentations = [];
@@ -55,45 +84,52 @@ function setupTelemetry(options) {
55
84
  let traceExporter;
56
85
  if (endpoint) traceExporter = new OTLPTraceExporter({
57
86
  url: `${endpoint}/traces`,
58
- headers
87
+ headers: headers ?? {}
59
88
  });
89
+ else if (otlpEndpointFromEnv("TRACES")) traceExporter = new OTLPTraceExporter(headers ? { headers } : {});
60
90
  else traceExporter = new ConsoleSpanExporter();
61
91
  const strategy = spanProcessorStrategy ?? getRecommendedStrategy(environment);
62
92
  const spanProcessor = createSpanProcessor(traceExporter, { strategy });
63
93
  setGlobalSpanProcessor(spanProcessor);
94
+ let logProcessor;
95
+ if (endpoint || otlpEndpointFromEnv("LOGS")) {
96
+ const logExporter = endpoint ? new OTLPLogExporter({
97
+ url: `${endpoint}/logs`,
98
+ headers: headers ?? {}
99
+ }) : new OTLPLogExporter(headers ? { headers } : {});
100
+ logProcessor = new BatchLogRecordProcessor({ exporter: logExporter });
101
+ setGlobalLogProcessor(logProcessor);
102
+ }
64
103
  sdk = new NodeSDK({
65
104
  resource,
66
105
  traceExporter,
67
106
  spanProcessors: [spanProcessor],
107
+ ...logProcessor && { logRecordProcessors: [logProcessor] },
108
+ ...sampler && { sampler },
68
109
  instrumentations
69
110
  });
70
- if (endpoint) {
71
- const logExporter = new OTLPLogExporter({
72
- url: `${endpoint}/logs`,
73
- headers
74
- });
75
- const logProcessor = new BatchLogRecordProcessor({ exporter: logExporter });
76
- new LoggerProvider({
77
- resource,
78
- processors: [logProcessor]
79
- });
80
- setGlobalLogProcessor(logProcessor);
81
- }
82
111
  sdk.start();
83
- process.on("SIGTERM", () => {
84
- sdk?.shutdown().then(() => {}).catch((_error) => {}).finally(() => process.exit(0));
85
- });
112
+ if (handleSignals) {
113
+ onSigterm = () => {
114
+ sdk?.shutdown().then(() => {}).catch((_error) => {}).finally(() => process.exit(0));
115
+ };
116
+ process.on("SIGTERM", onSigterm);
117
+ }
86
118
  }
87
119
  /**
88
120
  * Shut down telemetry (for testing or graceful shutdown)
89
121
  */
90
122
  async function shutdownTelemetry() {
123
+ if (onSigterm) {
124
+ process.off("SIGTERM", onSigterm);
125
+ onSigterm = null;
126
+ }
91
127
  if (sdk) {
92
128
  await sdk.shutdown();
93
129
  sdk = null;
94
130
  }
95
131
  }
96
132
  //#endregion
97
- export { setupTelemetry, shutdownTelemetry };
133
+ export { InvalidSampleRatio, setupTelemetry, shutdownTelemetry };
98
134
 
99
135
  //# sourceMappingURL=setup.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"setup.mjs","names":[],"sources":["../../src/instrumentation/setup.ts"],"sourcesContent":["import { DiagConsoleLogger, DiagLogLevel, diag } from '@opentelemetry/api';\nimport { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';\nimport { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';\nimport { PinoInstrumentation } from '@opentelemetry/instrumentation-pino';\nimport { resourceFromAttributes } from '@opentelemetry/resources';\nimport {\n\tBatchLogRecordProcessor,\n\tLoggerProvider,\n} from '@opentelemetry/sdk-logs';\nimport { NodeSDK } from '@opentelemetry/sdk-node';\nimport {\n\tConsoleSpanExporter,\n\ttype SpanExporter,\n} from '@opentelemetry/sdk-trace-node';\nimport {\n\tATTR_SERVICE_NAME,\n\tATTR_SERVICE_VERSION,\n} from '@opentelemetry/semantic-conventions';\nimport type { SpanProcessorStrategy } from '../adapters/types';\nimport {\n\tcreateSpanProcessor,\n\tgetRecommendedStrategy,\n\tsetGlobalLogProcessor,\n\tsetGlobalSpanProcessor,\n} from './core';\n\n/**\n * Options for configuring telemetry\n */\nexport interface TelemetryOptions {\n\t/**\n\t * Service name for resource identification\n\t */\n\tserviceName: string;\n\n\t/**\n\t * Service version\n\t */\n\tserviceVersion?: string;\n\n\t/**\n\t * OTLP endpoint URL (e.g., 'http://localhost:3000/__telescope/v1')\n\t * If not provided, traces will be logged to console\n\t */\n\tendpoint?: string;\n\n\t/**\n\t * Whether to instrument Pino for log correlation\n\t * @default true\n\t */\n\tinstrumentPino?: boolean;\n\n\t/**\n\t * Whether to enable auto-instrumentation for common libraries\n\t * (http, fetch, express, etc.)\n\t * @default true\n\t */\n\tautoInstrument?: boolean;\n\n\t/**\n\t * Enable debug logging for OTel SDK\n\t * @default false\n\t */\n\tdebug?: boolean;\n\n\t/**\n\t * Additional resource attributes\n\t */\n\tresourceAttributes?: Record<string, string>;\n\n\t/**\n\t * Headers to send with OTLP requests\n\t */\n\theaders?: Record<string, string>;\n\n\t/**\n\t * Span processor strategy.\n\t * - 'batch': Efficient batching for long-running servers (default)\n\t * - 'simple': Immediate export for serverless environments (Lambda, Edge)\n\t *\n\t * If not specified, automatically selected based on environment detection.\n\t */\n\tspanProcessorStrategy?: SpanProcessorStrategy;\n\n\t/**\n\t * Environment type for automatic configuration\n\t * @default 'server'\n\t */\n\tenvironment?: 'server' | 'lambda' | 'edge' | 'custom';\n}\n\nlet sdk: NodeSDK | null = null;\n\n/**\n * Set up OpenTelemetry instrumentation.\n * Call this BEFORE importing your application code.\n *\n * @example\n * ```typescript\n * // instrumentation.ts (create this file)\n * import { setupTelemetry } from '@geekmidas/telescope/instrumentation';\n *\n * setupTelemetry({\n * serviceName: 'my-api',\n * endpoint: 'http://localhost:3000/__telescope/v1',\n * });\n *\n * // Then in your entry point:\n * // import './instrumentation';\n * // import { app } from './app';\n * ```\n */\nexport function setupTelemetry(options: TelemetryOptions): void {\n\tif (sdk) {\n\t\treturn;\n\t}\n\n\tconst {\n\t\tserviceName,\n\t\tserviceVersion = '1.0.0',\n\t\tendpoint,\n\t\tinstrumentPino = true,\n\t\tautoInstrument = true,\n\t\tdebug = false,\n\t\tresourceAttributes = {},\n\t\theaders = {},\n\t\tspanProcessorStrategy,\n\t\tenvironment = 'server',\n\t} = options;\n\n\t// Enable debug logging if requested\n\tif (debug) {\n\t\tdiag.setLogger(new DiagConsoleLogger(), DiagLogLevel.DEBUG);\n\t}\n\n\t// Create resource\n\t// `new Resource(...)` went in resources 2.x — a resource is now made from\n\t// attributes by a function, so a detector and a literal produce the same\n\t// thing rather than two shapes that had to be merged.\n\tconst resource = resourceFromAttributes({\n\t\t[ATTR_SERVICE_NAME]: serviceName,\n\t\t[ATTR_SERVICE_VERSION]: serviceVersion,\n\t\t...resourceAttributes,\n\t});\n\n\t// Build instrumentations list\n\tconst instrumentations = [];\n\n\t// Add Pino instrumentation for log correlation\n\tif (instrumentPino) {\n\t\tinstrumentations.push(\n\t\t\tnew PinoInstrumentation({\n\t\t\t\t// Inject trace context into log records\n\t\t\t\tlogHook: (span, record) => {\n\t\t\t\t\trecord.trace_id = span.spanContext().traceId;\n\t\t\t\t\trecord.span_id = span.spanContext().spanId;\n\t\t\t\t\trecord.trace_flags = span.spanContext().traceFlags;\n\t\t\t\t},\n\t\t\t}),\n\t\t);\n\t}\n\n\t// Add auto-instrumentations for common libraries\n\tif (autoInstrument) {\n\t\tinstrumentations.push(\n\t\t\tgetNodeAutoInstrumentations({\n\t\t\t\t// Disable file system instrumentation (too noisy)\n\t\t\t\t'@opentelemetry/instrumentation-fs': { enabled: false },\n\t\t\t\t// Configure HTTP instrumentation\n\t\t\t\t'@opentelemetry/instrumentation-http': {\n\t\t\t\t\tignoreIncomingRequestHook: (request) => {\n\t\t\t\t\t\t// Ignore health checks and internal routes\n\t\t\t\t\t\tconst path = request.url || '';\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\tpath.includes('/__health') ||\n\t\t\t\t\t\t\tpath.includes('/__telescope') ||\n\t\t\t\t\t\t\tpath.includes('/favicon')\n\t\t\t\t\t\t);\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}),\n\t\t);\n\t}\n\n\t// Create exporters\n\tlet traceExporter: SpanExporter;\n\tif (endpoint) {\n\t\ttraceExporter = new OTLPTraceExporter({\n\t\t\turl: `${endpoint}/traces`,\n\t\t\theaders,\n\t\t});\n\t} else {\n\t\t// Fall back to console exporter for debugging\n\t\ttraceExporter = new ConsoleSpanExporter();\n\t}\n\n\t// Determine span processor strategy\n\tconst strategy = spanProcessorStrategy ?? getRecommendedStrategy(environment);\n\tconst spanProcessor = createSpanProcessor(traceExporter, { strategy });\n\n\t// Register globally for flush operations\n\tsetGlobalSpanProcessor(spanProcessor);\n\n\t// Create and configure SDK\n\tsdk = new NodeSDK({\n\t\tresource,\n\t\ttraceExporter,\n\t\tspanProcessors: [spanProcessor],\n\t\tinstrumentations,\n\t});\n\n\t// Set up log exporter if endpoint is provided\n\tif (endpoint) {\n\t\tconst logExporter = new OTLPLogExporter({\n\t\t\turl: `${endpoint}/logs`,\n\t\t\theaders,\n\t\t});\n\n\t\t// Both changed in sdk-logs 2.x, and for the same reason the trace SDK\n\t\t// changed: a provider whose processors could be swapped after it had begun\n\t\t// emitting was never safe, so processors are constructor-only and the\n\t\t// exporter is named rather than positional.\n\t\tconst logProcessor = new BatchLogRecordProcessor({\n\t\t\texporter: logExporter,\n\t\t});\n\t\tconst loggerProvider = new LoggerProvider({\n\t\t\tresource,\n\t\t\tprocessors: [logProcessor],\n\t\t});\n\n\t\t// Register for flush operations\n\t\tsetGlobalLogProcessor(logProcessor);\n\t}\n\n\t// Start the SDK\n\tsdk.start();\n\n\t// Graceful shutdown\n\tprocess.on('SIGTERM', () => {\n\t\tsdk\n\t\t\t?.shutdown()\n\t\t\t.then(() => {})\n\t\t\t.catch((_error) => {})\n\t\t\t.finally(() => process.exit(0));\n\t});\n}\n\n/**\n * Shut down telemetry (for testing or graceful shutdown)\n */\nexport async function shutdownTelemetry(): Promise<void> {\n\tif (sdk) {\n\t\tawait sdk.shutdown();\n\t\tsdk = null;\n\t}\n}\n"],"mappings":";;;;;;;;;;;;AA4FA,IAAI,MAAsB;;;;;;;;;;;;;;;;;;;;AAqB1B,SAAgB,eAAe,SAAiC;CAC/D,IAAI,KACH;CAGD,MAAM,EACL,aACA,iBAAiB,SACjB,UACA,iBAAiB,MACjB,iBAAiB,MACjB,QAAQ,OACR,qBAAqB,CAAC,GACtB,UAAU,CAAC,GACX,uBACA,cAAc,aACX;CAGJ,IAAI,OACH,KAAK,UAAU,IAAI,kBAAkB,GAAG,aAAa,KAAK;CAO3D,MAAM,WAAW,uBAAuB;GACtC,oBAAoB;GACpB,uBAAuB;EACxB,GAAG;CACJ,CAAC;CAGD,MAAM,mBAAmB,CAAC;CAG1B,IAAI,gBACH,iBAAiB,KAChB,IAAI,oBAAoB,EAEvB,UAAU,MAAM,WAAW;EAC1B,OAAO,WAAW,KAAK,YAAY,CAAC,CAAC;EACrC,OAAO,UAAU,KAAK,YAAY,CAAC,CAAC;EACpC,OAAO,cAAc,KAAK,YAAY,CAAC,CAAC;CACzC,EACD,CAAC,CACF;CAID,IAAI,gBACH,iBAAiB,KAChB,4BAA4B;EAE3B,qCAAqC,EAAE,SAAS,MAAM;EAEtD,uCAAuC,EACtC,4BAA4B,YAAY;GAEvC,MAAM,OAAO,QAAQ,OAAO;GAC5B,OACC,KAAK,SAAS,WAAW,KACzB,KAAK,SAAS,cAAc,KAC5B,KAAK,SAAS,UAAU;EAE1B,EACD;CACD,CAAC,CACF;CAID,IAAI;CACJ,IAAI,UACH,gBAAgB,IAAI,kBAAkB;EACrC,KAAK,GAAG,SAAS;EACjB;CACD,CAAC;MAGD,gBAAgB,IAAI,oBAAoB;CAIzC,MAAM,WAAW,yBAAyB,uBAAuB,WAAW;CAC5E,MAAM,gBAAgB,oBAAoB,eAAe,EAAE,SAAS,CAAC;CAGrE,uBAAuB,aAAa;CAGpC,MAAM,IAAI,QAAQ;EACjB;EACA;EACA,gBAAgB,CAAC,aAAa;EAC9B;CACD,CAAC;CAGD,IAAI,UAAU;EACb,MAAM,cAAc,IAAI,gBAAgB;GACvC,KAAK,GAAG,SAAS;GACjB;EACD,CAAC;EAMD,MAAM,eAAe,IAAI,wBAAwB,EAChD,UAAU,YACX,CAAC;EACsB,IAAI,eAAe;GACzC;GACA,YAAY,CAAC,YAAY;EAC1B,CAAC;EAGD,sBAAsB,YAAY;CACnC;CAGA,IAAI,MAAM;CAGV,QAAQ,GAAG,iBAAiB;EAC3B,KACG,SAAS,CAAC,CACX,WAAW,CAAC,CAAC,CAAC,CACd,OAAO,WAAW,CAAC,CAAC,CAAC,CACrB,cAAc,QAAQ,KAAK,CAAC,CAAC;CAChC,CAAC;AACF;;;;AAKA,eAAsB,oBAAmC;CACxD,IAAI,KAAK;EACR,MAAM,IAAI,SAAS;EACnB,MAAM;CACP;AACD"}
1
+ {"version":3,"file":"setup.mjs","names":[],"sources":["../../src/instrumentation/setup.ts"],"sourcesContent":["import { DiagConsoleLogger, DiagLogLevel, diag } from '@opentelemetry/api';\nimport { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';\nimport { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';\nimport { PinoInstrumentation } from '@opentelemetry/instrumentation-pino';\nimport { resourceFromAttributes } from '@opentelemetry/resources';\nimport { BatchLogRecordProcessor } from '@opentelemetry/sdk-logs';\nimport { NodeSDK } from '@opentelemetry/sdk-node';\nimport {\n\tConsoleSpanExporter,\n\tParentBasedSampler,\n\ttype Sampler,\n\ttype SpanExporter,\n\tTraceIdRatioBasedSampler,\n} from '@opentelemetry/sdk-trace-node';\nimport {\n\tATTR_SERVICE_NAME,\n\tATTR_SERVICE_VERSION,\n} from '@opentelemetry/semantic-conventions';\nimport type { SpanProcessorStrategy } from '../adapters/types';\nimport {\n\tcreateSpanProcessor,\n\tgetRecommendedStrategy,\n\tsetGlobalLogProcessor,\n\tsetGlobalSpanProcessor,\n} from './core';\n\n/**\n * Options for configuring telemetry\n */\nexport interface TelemetryOptions {\n\t/**\n\t * Service name for resource identification\n\t */\n\tserviceName: string;\n\n\t/**\n\t * Service version\n\t */\n\tserviceVersion?: string;\n\n\t/**\n\t * The `service.namespace` resource attribute — the product an app is part\n\t * of, so several services from one workspace group together.\n\t */\n\tserviceNamespace?: string;\n\n\t/**\n\t * The stage this process runs as (`production`, `staging`, …). Sent as\n\t * `deployment.environment.name`, and as the older `deployment.environment`\n\t * many backends still filter on.\n\t */\n\tdeploymentEnvironment?: string;\n\n\t/**\n\t * OTLP endpoint URL (e.g., 'http://localhost:3000/__telescope/v1').\n\t * Traces go to `<endpoint>/traces` and logs to `<endpoint>/logs`.\n\t *\n\t * If not provided, the standard `OTEL_EXPORTER_OTLP_ENDPOINT` (and its\n\t * per-signal `_TRACES_`/`_LOGS_` variants and `OTEL_EXPORTER_OTLP_HEADERS`)\n\t * configure the exporters, with the spec's `/v1/traces` and `/v1/logs`\n\t * paths. With neither, traces are written to the console.\n\t */\n\tendpoint?: string;\n\n\t/**\n\t * The fraction of new traces to sample, from 0 to 1. A span whose parent\n\t * was sampled (or not) follows its parent, so a trace is never cut in half.\n\t *\n\t * When left out, the standard `OTEL_TRACES_SAMPLER` and\n\t * `OTEL_TRACES_SAMPLER_ARG` decide (e.g. `parentbased_traceidratio` and\n\t * `0.1`); with neither, every trace is sampled.\n\t */\n\tsampleRatio?: number;\n\n\t/**\n\t * Shut the SDK down and exit on `SIGTERM`.\n\t *\n\t * Turn it off when the process has its own graceful shutdown — exiting as\n\t * soon as telemetry is flushed would cut in-flight requests short.\n\t * @default true\n\t */\n\thandleSignals?: boolean;\n\n\t/**\n\t * Whether to instrument Pino for log correlation\n\t * @default true\n\t */\n\tinstrumentPino?: boolean;\n\n\t/**\n\t * Whether to enable auto-instrumentation for common libraries\n\t * (http, fetch, express, etc.)\n\t * @default true\n\t */\n\tautoInstrument?: boolean;\n\n\t/**\n\t * Enable debug logging for OTel SDK\n\t * @default false\n\t */\n\tdebug?: boolean;\n\n\t/**\n\t * Additional resource attributes\n\t */\n\tresourceAttributes?: Record<string, string>;\n\n\t/**\n\t * Headers to send with OTLP requests\n\t */\n\theaders?: Record<string, string>;\n\n\t/**\n\t * Span processor strategy.\n\t * - 'batch': Efficient batching for long-running servers (default)\n\t * - 'simple': Immediate export for serverless environments (Lambda, Edge)\n\t *\n\t * If not specified, automatically selected based on environment detection.\n\t */\n\tspanProcessorStrategy?: SpanProcessorStrategy;\n\n\t/**\n\t * Environment type for automatic configuration\n\t * @default 'server'\n\t */\n\tenvironment?: 'server' | 'lambda' | 'edge' | 'custom';\n}\n\nlet sdk: NodeSDK | null = null;\nlet onSigterm: (() => void) | null = null;\n\n/** A `sampleRatio` outside 0–1, which no sampler can honour. */\nexport class InvalidSampleRatio extends Error {\n\tconstructor(readonly sampleRatio: number) {\n\t\tsuper(\n\t\t\t`sampleRatio must be a number from 0 (no traces) to 1 (every trace), got ${sampleRatio}. Pass a fraction such as 0.1 to keep one trace in ten.`,\n\t\t);\n\t\tthis.name = 'InvalidSampleRatio';\n\t}\n}\n\n/** Whether the standard OTLP environment variables name a collector. */\nfunction otlpEndpointFromEnv(signal: 'TRACES' | 'LOGS'): boolean {\n\treturn Boolean(\n\t\tprocess.env[`OTEL_EXPORTER_OTLP_${signal}_ENDPOINT`] ||\n\t\t\tprocess.env.OTEL_EXPORTER_OTLP_ENDPOINT,\n\t);\n}\n\n/**\n * The sampler for an explicit ratio, or undefined to let the SDK read\n * `OTEL_TRACES_SAMPLER` / `OTEL_TRACES_SAMPLER_ARG`.\n */\nfunction samplerFor(sampleRatio: number | undefined): Sampler | undefined {\n\tif (sampleRatio === undefined) return undefined;\n\tif (!Number.isFinite(sampleRatio) || sampleRatio < 0 || sampleRatio > 1) {\n\t\tthrow new InvalidSampleRatio(sampleRatio);\n\t}\n\treturn new ParentBasedSampler({\n\t\troot: new TraceIdRatioBasedSampler(sampleRatio),\n\t});\n}\n\n/**\n * Set up OpenTelemetry instrumentation.\n * Call this BEFORE importing your application code.\n *\n * @example\n * ```typescript\n * // instrumentation.ts (create this file)\n * import { setupTelemetry } from '@geekmidas/telescope/instrumentation';\n *\n * setupTelemetry({\n * serviceName: 'my-api',\n * endpoint: 'http://localhost:3000/__telescope/v1',\n * });\n *\n * // Then in your entry point:\n * // import './instrumentation';\n * // import { app } from './app';\n * ```\n */\nexport function setupTelemetry(options: TelemetryOptions): void {\n\tif (sdk) {\n\t\treturn;\n\t}\n\n\tconst {\n\t\tserviceName,\n\t\tserviceVersion = '1.0.0',\n\t\tserviceNamespace,\n\t\tdeploymentEnvironment,\n\t\tendpoint,\n\t\tsampleRatio,\n\t\thandleSignals = true,\n\t\tinstrumentPino = true,\n\t\tautoInstrument = true,\n\t\tdebug = false,\n\t\tresourceAttributes = {},\n\t\theaders,\n\t\tspanProcessorStrategy,\n\t\tenvironment = 'server',\n\t} = options;\n\n\t// Before anything starts, so a bad ratio leaves nothing half set up.\n\tconst sampler = samplerFor(sampleRatio);\n\n\t// Enable debug logging if requested\n\tif (debug) {\n\t\tdiag.setLogger(new DiagConsoleLogger(), DiagLogLevel.DEBUG);\n\t}\n\n\t// Create resource\n\t// `new Resource(...)` went in resources 2.x — a resource is now made from\n\t// attributes by a function, so a detector and a literal produce the same\n\t// thing rather than two shapes that had to be merged.\n\t//\n\t// The SDK merges what `OTEL_RESOURCE_ATTRIBUTES` and `OTEL_SERVICE_NAME`\n\t// say over this, so an operator can still rename or relabel a service.\n\tconst resource = resourceFromAttributes({\n\t\t[ATTR_SERVICE_NAME]: serviceName,\n\t\t[ATTR_SERVICE_VERSION]: serviceVersion,\n\t\t...(serviceNamespace && { 'service.namespace': serviceNamespace }),\n\t\t...(deploymentEnvironment && {\n\t\t\t'deployment.environment.name': deploymentEnvironment,\n\t\t\t'deployment.environment': deploymentEnvironment,\n\t\t}),\n\t\t...resourceAttributes,\n\t});\n\n\t// Build instrumentations list\n\tconst instrumentations = [];\n\n\t// Add Pino instrumentation for log correlation\n\tif (instrumentPino) {\n\t\tinstrumentations.push(\n\t\t\tnew PinoInstrumentation({\n\t\t\t\t// Inject trace context into log records\n\t\t\t\tlogHook: (span, record) => {\n\t\t\t\t\trecord.trace_id = span.spanContext().traceId;\n\t\t\t\t\trecord.span_id = span.spanContext().spanId;\n\t\t\t\t\trecord.trace_flags = span.spanContext().traceFlags;\n\t\t\t\t},\n\t\t\t}),\n\t\t);\n\t}\n\n\t// Add auto-instrumentations for common libraries\n\tif (autoInstrument) {\n\t\tinstrumentations.push(\n\t\t\tgetNodeAutoInstrumentations({\n\t\t\t\t// Disable file system instrumentation (too noisy)\n\t\t\t\t'@opentelemetry/instrumentation-fs': { enabled: false },\n\t\t\t\t// Configure HTTP instrumentation\n\t\t\t\t'@opentelemetry/instrumentation-http': {\n\t\t\t\t\tignoreIncomingRequestHook: (request) => {\n\t\t\t\t\t\t// Ignore health checks and internal routes\n\t\t\t\t\t\tconst path = request.url || '';\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\tpath.includes('/__health') ||\n\t\t\t\t\t\t\tpath.includes('/__telescope') ||\n\t\t\t\t\t\t\tpath.includes('/favicon')\n\t\t\t\t\t\t);\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}),\n\t\t);\n\t}\n\n\t// Create exporters\n\tlet traceExporter: SpanExporter;\n\tif (endpoint) {\n\t\ttraceExporter = new OTLPTraceExporter({\n\t\t\turl: `${endpoint}/traces`,\n\t\t\theaders: headers ?? {},\n\t\t});\n\t} else if (otlpEndpointFromEnv('TRACES')) {\n\t\t// No URL: the exporter reads the OTLP variables itself, the way any\n\t\t// other OpenTelemetry SDK would.\n\t\ttraceExporter = new OTLPTraceExporter(headers ? { headers } : {});\n\t} else {\n\t\t// Fall back to console exporter for debugging\n\t\ttraceExporter = new ConsoleSpanExporter();\n\t}\n\n\t// Determine span processor strategy\n\tconst strategy = spanProcessorStrategy ?? getRecommendedStrategy(environment);\n\tconst spanProcessor = createSpanProcessor(traceExporter, { strategy });\n\n\t// Register globally for flush operations\n\tsetGlobalSpanProcessor(spanProcessor);\n\n\t// Set up log exporter if a collector is named\n\tlet logProcessor: BatchLogRecordProcessor | undefined;\n\tif (endpoint || otlpEndpointFromEnv('LOGS')) {\n\t\tconst logExporter = endpoint\n\t\t\t? new OTLPLogExporter({\n\t\t\t\t\turl: `${endpoint}/logs`,\n\t\t\t\t\theaders: headers ?? {},\n\t\t\t\t})\n\t\t\t: new OTLPLogExporter(headers ? { headers } : {});\n\n\t\t// sdk-logs 2.x: processors are constructor-only and the exporter is\n\t\t// named rather than positional.\n\t\tlogProcessor = new BatchLogRecordProcessor({ exporter: logExporter });\n\n\t\t// Register for flush operations\n\t\tsetGlobalLogProcessor(logProcessor);\n\t}\n\n\t// Create and configure SDK. The SDK owns the logger provider, so the log\n\t// processor is handed to it rather than to a provider of our own — one\n\t// nothing registered would never receive a record.\n\tsdk = new NodeSDK({\n\t\tresource,\n\t\ttraceExporter,\n\t\tspanProcessors: [spanProcessor],\n\t\t...(logProcessor && { logRecordProcessors: [logProcessor] }),\n\t\t...(sampler && { sampler }),\n\t\tinstrumentations,\n\t});\n\n\t// Start the SDK\n\tsdk.start();\n\n\tif (handleSignals) {\n\t\t// Graceful shutdown\n\t\tonSigterm = () => {\n\t\t\tsdk\n\t\t\t\t?.shutdown()\n\t\t\t\t.then(() => {})\n\t\t\t\t.catch((_error) => {})\n\t\t\t\t.finally(() => process.exit(0));\n\t\t};\n\t\tprocess.on('SIGTERM', onSigterm);\n\t}\n}\n\n/**\n * Shut down telemetry (for testing or graceful shutdown)\n */\nexport async function shutdownTelemetry(): Promise<void> {\n\tif (onSigterm) {\n\t\tprocess.off('SIGTERM', onSigterm);\n\t\tonSigterm = null;\n\t}\n\tif (sdk) {\n\t\tawait sdk.shutdown();\n\t\tsdk = null;\n\t}\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geekmidas/telescope",
3
- "version": "10.0.0-alpha.57",
3
+ "version": "10.0.0-alpha.59",
4
4
  "description": "Laravel Telescope-style request, exception and log recording for web applications, exposed as a JSON API",
5
5
  "type": "module",
6
6
  "exports": {
@@ -159,7 +159,7 @@
159
159
  "dependencies": {
160
160
  "@pinojs/redact": "~0.4.0",
161
161
  "nanoid": "~6.0.1",
162
- "@geekmidas/logger": "^10.0.0-alpha.57"
162
+ "@geekmidas/logger": "^10.0.0-alpha.59"
163
163
  },
164
164
  "peerDependencies": {
165
165
  "@middy/core": ">=7.9.2",
@@ -232,6 +232,7 @@
232
232
  "devDependencies": {
233
233
  "@middy/core": "~7.9.2",
234
234
  "@opentelemetry/api": "~1.9.1",
235
+ "@opentelemetry/api-logs": "~0.222.0",
235
236
  "@opentelemetry/auto-instrumentations-node": "~0.80.0",
236
237
  "@opentelemetry/exporter-logs-otlp-http": "~0.222.0",
237
238
  "@opentelemetry/exporter-trace-otlp-http": "~0.222.0",
@@ -249,6 +250,6 @@
249
250
  "pg": "~8.23.0",
250
251
  "pino": "~10.3.1",
251
252
  "pino-abstract-transport": "~3.0.0",
252
- "@geekmidas/logger": "^10.0.0-alpha.57"
253
+ "@geekmidas/logger": "^10.0.0-alpha.59"
253
254
  }
254
255
  }