@sophonz/node-sdk 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -35,6 +35,59 @@ console.log('Application running');
35
35
  await shutdown();
36
36
  ```
37
37
 
38
+ ### Framework Middleware
39
+
40
+ The SDK ships request middleware so applications never need to import
41
+ `@opentelemetry/api` directly. The middleware copies baggage entries onto the
42
+ active span, captures the client IP and user-agent, and forwards them to the
43
+ mutable trace context.
44
+
45
+ ```ts
46
+ import express from 'express';
47
+ import { sophonzExpressMiddleware } from '@sophonz/node-sdk';
48
+
49
+ const app = express();
50
+ app.use(sophonzExpressMiddleware());
51
+ ```
52
+
53
+ ```ts
54
+ import Koa from 'koa';
55
+ import { sophonzKoaMiddleware } from '@sophonz/node-sdk';
56
+
57
+ const app = new Koa();
58
+ app.use(sophonzKoaMiddleware());
59
+ ```
60
+
61
+ Both accept the same options:
62
+
63
+ - `captureBaggage?: boolean` - Copy baggage entries (prefixed `baggage.`). Default `true`.
64
+ - `captureClientIp?: boolean` - Capture `http.client.ip`. Default `true`.
65
+ - `captureUserAgent?: boolean` - Capture `http.user_agent`. Default `true`.
66
+ - `attributes?: Attributes | ((req) => Attributes)` - Extra static or per-request attributes.
67
+
68
+ ### Re-exported OpenTelemetry API
69
+
70
+ The OTel API surface is re-exported so you don't have to add
71
+ `@opentelemetry/api` / `@opentelemetry/api-logs` as direct dependencies:
72
+
73
+ ```ts
74
+ import { trace, context, propagation, metrics, logs } from '@sophonz/node-sdk';
75
+
76
+ const span = trace.getActiveSpan();
77
+ ```
78
+
79
+ ### Debugging payloads
80
+
81
+ ```ts
82
+ import { init, enableDebugPayloadExporters } from '@sophonz/node-sdk';
83
+
84
+ init({ service: 'my-app' });
85
+ if (process.env.SOPHONZ_DEBUG_PAYLOAD === 'true') {
86
+ // Dump every exported span/log to the terminal alongside the OTLP exporter.
87
+ void enableDebugPayloadExporters();
88
+ }
89
+ ```
90
+
38
91
  ### Command-line Instrumentation
39
92
 
40
93
  Wrap your Node.js application with the preload binary:
@@ -67,6 +120,18 @@ Gracefully shut down the SDK and flush all telemetry.
67
120
 
68
121
  Set global trace attributes on the mutable context.
69
122
 
123
+ ### `sophonzExpressMiddleware(options?): RequestHandler`
124
+
125
+ Express request middleware that enriches the active span and mutable trace context with baggage, client IP, and user-agent.
126
+
127
+ ### `sophonzKoaMiddleware(options?): Middleware`
128
+
129
+ Koa equivalent of `sophonzExpressMiddleware`.
130
+
131
+ ### `enableDebugPayloadExporters(): Promise<void>`
132
+
133
+ Attach console exporters to the initialized providers to print every span and log record. Call after `init`/`initSDK`.
134
+
70
135
  ### SDKConfig Options
71
136
 
72
137
  - `service?: string` - Service name (default from env or auto-detected)
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sophonz/node-sdk",
3
- "version": "0.0.1",
3
+ "version": "0.1.0",
4
4
  "homepage": "https://sophonz.com",
5
5
  "repository": {
6
6
  "type": "git",
@@ -0,0 +1,5 @@
1
+ export { context, diag, metrics, propagation, trace, ProxyTracerProvider, SpanKind, SpanStatusCode, ValueType, createContextKey, isSpanContextValid, isValidSpanId, isValidTraceId, } from '@opentelemetry/api';
2
+ export type { Attributes, AttributeValue, Baggage, BaggageEntry, Context, Counter, Histogram, Meter, Span, SpanContext, SpanOptions, TextMapPropagator, Tracer, TracerProvider, } from '@opentelemetry/api';
3
+ export { logs, SeverityNumber } from '@opentelemetry/api-logs';
4
+ export type { Logger as OtelApiLogger, LogRecord } from '@opentelemetry/api-logs';
5
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/api.ts"],"names":[],"mappings":"AAQA,OAAO,EACL,OAAO,EACP,IAAI,EACJ,OAAO,EACP,WAAW,EACX,KAAK,EACL,mBAAmB,EACnB,QAAQ,EACR,cAAc,EACd,SAAS,EACT,gBAAgB,EAChB,kBAAkB,EAClB,aAAa,EACb,cAAc,GACf,MAAM,oBAAoB,CAAC;AAE5B,YAAY,EACV,UAAU,EACV,cAAc,EACd,OAAO,EACP,YAAY,EACZ,OAAO,EACP,OAAO,EACP,SAAS,EACT,KAAK,EACL,IAAI,EACJ,WAAW,EACX,WAAW,EACX,iBAAiB,EACjB,MAAM,EACN,cAAc,GACf,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE/D,YAAY,EAAE,MAAM,IAAI,aAAa,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC"}
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SeverityNumber = exports.logs = exports.isValidTraceId = exports.isValidSpanId = exports.isSpanContextValid = exports.createContextKey = exports.ValueType = exports.SpanStatusCode = exports.SpanKind = exports.ProxyTracerProvider = exports.trace = exports.propagation = exports.metrics = exports.diag = exports.context = void 0;
4
+ var api_1 = require("@opentelemetry/api");
5
+ Object.defineProperty(exports, "context", { enumerable: true, get: function () { return api_1.context; } });
6
+ Object.defineProperty(exports, "diag", { enumerable: true, get: function () { return api_1.diag; } });
7
+ Object.defineProperty(exports, "metrics", { enumerable: true, get: function () { return api_1.metrics; } });
8
+ Object.defineProperty(exports, "propagation", { enumerable: true, get: function () { return api_1.propagation; } });
9
+ Object.defineProperty(exports, "trace", { enumerable: true, get: function () { return api_1.trace; } });
10
+ Object.defineProperty(exports, "ProxyTracerProvider", { enumerable: true, get: function () { return api_1.ProxyTracerProvider; } });
11
+ Object.defineProperty(exports, "SpanKind", { enumerable: true, get: function () { return api_1.SpanKind; } });
12
+ Object.defineProperty(exports, "SpanStatusCode", { enumerable: true, get: function () { return api_1.SpanStatusCode; } });
13
+ Object.defineProperty(exports, "ValueType", { enumerable: true, get: function () { return api_1.ValueType; } });
14
+ Object.defineProperty(exports, "createContextKey", { enumerable: true, get: function () { return api_1.createContextKey; } });
15
+ Object.defineProperty(exports, "isSpanContextValid", { enumerable: true, get: function () { return api_1.isSpanContextValid; } });
16
+ Object.defineProperty(exports, "isValidSpanId", { enumerable: true, get: function () { return api_1.isValidSpanId; } });
17
+ Object.defineProperty(exports, "isValidTraceId", { enumerable: true, get: function () { return api_1.isValidTraceId; } });
18
+ var api_logs_1 = require("@opentelemetry/api-logs");
19
+ Object.defineProperty(exports, "logs", { enumerable: true, get: function () { return api_logs_1.logs; } });
20
+ Object.defineProperty(exports, "SeverityNumber", { enumerable: true, get: function () { return api_logs_1.SeverityNumber; } });
@@ -0,0 +1,2 @@
1
+ export declare const enableDebugPayloadExporters: () => Promise<void>;
2
+ //# sourceMappingURL=debug.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"debug.d.ts","sourceRoot":"","sources":["../../src/debug.ts"],"names":[],"mappings":"AAuBA,eAAO,MAAM,2BAA2B,QAAa,OAAO,CAAC,IAAI,CAyBhE,CAAC"}
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.enableDebugPayloadExporters = void 0;
37
+ const enableDebugPayloadExporters = async () => {
38
+ var _a, _b, _c, _d;
39
+ const { trace } = await Promise.resolve().then(() => __importStar(require('@opentelemetry/api')));
40
+ const { logs } = await Promise.resolve().then(() => __importStar(require('@opentelemetry/api-logs')));
41
+ const { SimpleSpanProcessor, ConsoleSpanExporter } = await Promise.resolve().then(() => __importStar(require('@opentelemetry/sdk-trace-base')));
42
+ const { SimpleLogRecordProcessor, ConsoleLogRecordExporter } = await Promise.resolve().then(() => __importStar(require('@opentelemetry/sdk-logs')));
43
+ const traceProvider = trace.getTracerProvider();
44
+ const traceDelegate = (_b = (_a = traceProvider.getDelegate) === null || _a === void 0 ? void 0 : _a.call(traceProvider)) !== null && _b !== void 0 ? _b : traceProvider;
45
+ (_c = traceDelegate.addSpanProcessor) === null || _c === void 0 ? void 0 : _c.call(traceDelegate, new SimpleSpanProcessor(new ConsoleSpanExporter()));
46
+ const logProvider = logs.getLoggerProvider();
47
+ (_d = logProvider.addLogRecordProcessor) === null || _d === void 0 ? void 0 : _d.call(logProvider, new SimpleLogRecordProcessor(new ConsoleLogRecordExporter()));
48
+ };
49
+ exports.enableDebugPayloadExporters = enableDebugPayloadExporters;
@@ -1,5 +1,8 @@
1
+ export * from './api';
2
+ export * from './debug';
1
3
  export * from './gcp';
2
4
  export * from './logger';
5
+ export * from './middleware';
3
6
  export * from './otel';
4
7
  export { recordException, setupExpressErrorHandler, setupKoaErrorHandler, } from '@sophonz/instrumentation-node-exception';
5
8
  export type { MutableAsyncLocalStorageContextManager } from "./MutableAsyncLocalStorageContextManager";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,OAAO,CAAC;AACtB,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,OAAO,EACL,eAAe,EACf,wBAAwB,EACxB,oBAAoB,GACrB,MAAM,yCAAyC,CAAC;AAEjD,YAAY,EAAE,sCAAsC,EAAE,MAAM,0CAA0C,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,OAAO,CAAC;AACtB,cAAc,SAAS,CAAC;AACxB,cAAc,OAAO,CAAC;AACtB,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,QAAQ,CAAC;AACvB,OAAO,EACL,eAAe,EACf,wBAAwB,EACxB,oBAAoB,GACrB,MAAM,yCAAyC,CAAC;AAEjD,YAAY,EAAE,sCAAsC,EAAE,MAAM,0CAA0C,CAAA"}
@@ -2,8 +2,11 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.setupKoaErrorHandler = exports.setupExpressErrorHandler = exports.recordException = void 0;
4
4
  const tslib_1 = require("tslib");
5
+ tslib_1.__exportStar(require("./api"), exports);
6
+ tslib_1.__exportStar(require("./debug"), exports);
5
7
  tslib_1.__exportStar(require("./gcp"), exports);
6
8
  tslib_1.__exportStar(require("./logger"), exports);
9
+ tslib_1.__exportStar(require("./middleware"), exports);
7
10
  tslib_1.__exportStar(require("./otel"), exports);
8
11
  var instrumentation_node_exception_1 = require("@sophonz/instrumentation-node-exception");
9
12
  Object.defineProperty(exports, "recordException", { enumerable: true, get: function () { return instrumentation_node_exception_1.recordException; } });
@@ -0,0 +1,25 @@
1
+ import type * as http from 'node:http';
2
+ import { type Attributes } from '@opentelemetry/api';
3
+ export interface SophonzMiddlewareOptions {
4
+ captureBaggage?: boolean;
5
+ captureClientIp?: boolean;
6
+ captureUserAgent?: boolean;
7
+ attributes?: Attributes | ((requestOrContext: unknown) => Attributes);
8
+ }
9
+ interface ExpressLikeRequest extends http.IncomingMessage {
10
+ ip?: string;
11
+ get?: (name: string) => string | undefined;
12
+ }
13
+ interface KoaLikeContext {
14
+ ip?: string;
15
+ get?: (name: string) => string;
16
+ request?: {
17
+ ip?: string;
18
+ get?: (name: string) => string;
19
+ };
20
+ }
21
+ type ExpressNext = (error?: unknown) => void;
22
+ export declare const sophonzExpressMiddleware: (options?: SophonzMiddlewareOptions) => (req: ExpressLikeRequest, _res: http.ServerResponse, next: ExpressNext) => void;
23
+ export declare const sophonzKoaMiddleware: (options?: SophonzMiddlewareOptions) => (ctx: KoaLikeContext, next: () => Promise<void>) => Promise<void>;
24
+ export {};
25
+ //# sourceMappingURL=middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,IAAI,MAAM,WAAW,CAAC;AAEvC,OAAO,EACL,KAAK,UAAU,EAIhB,MAAM,oBAAoB,CAAC;AAO5B,MAAM,WAAW,wBAAwB;IAKvC,cAAc,CAAC,EAAE,OAAO,CAAC;IAIzB,eAAe,CAAC,EAAE,OAAO,CAAC;IAI1B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAK3B,UAAU,CAAC,EAAE,UAAU,GAAG,CAAC,CAAC,gBAAgB,EAAE,OAAO,KAAK,UAAU,CAAC,CAAC;CACvE;AAED,UAAU,kBAAmB,SAAQ,IAAI,CAAC,eAAe;IACvD,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;CAC5C;AAED,UAAU,cAAc;IACtB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAC/B,OAAO,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;KAAE,CAAC;CAC3D;AAED,KAAK,WAAW,GAAG,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;AAyD7C,eAAO,MAAM,wBAAwB,GACnC,UAAS,wBAA6B,MAUpC,KAAK,kBAAkB,EACvB,MAAM,IAAI,CAAC,cAAc,EACzB,MAAM,WAAW,KAChB,IAqBJ,CAAC;AAcF,eAAO,MAAM,oBAAoB,GAC/B,UAAS,wBAA6B,MAUpC,KAAK,cAAc,EACnB,MAAM,MAAM,OAAO,CAAC,IAAI,CAAC,KACxB,OAAO,CAAC,IAAI,CAyBhB,CAAC"}
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sophonzKoaMiddleware = exports.sophonzExpressMiddleware = void 0;
4
+ const api_1 = require("@opentelemetry/api");
5
+ const otel_1 = require("./otel");
6
+ const getBaggageAttributes = () => {
7
+ const baggage = api_1.propagation.getBaggage(api_1.context.active());
8
+ const attrs = {};
9
+ baggage === null || baggage === void 0 ? void 0 : baggage.getAllEntries().forEach(([key, entry]) => {
10
+ attrs[`baggage.${key}`] = entry.value;
11
+ });
12
+ return attrs;
13
+ };
14
+ const getHeader = (req, name) => {
15
+ var _a;
16
+ if (typeof req.get === 'function') {
17
+ return req.get(name);
18
+ }
19
+ const value = (_a = req.headers) === null || _a === void 0 ? void 0 : _a[name.toLowerCase()];
20
+ return Array.isArray(value) ? value[0] : value;
21
+ };
22
+ const applyAttributes = (attrs) => {
23
+ const span = api_1.trace.getActiveSpan();
24
+ if (span) {
25
+ for (const [key, value] of Object.entries(attrs)) {
26
+ if (value != null) {
27
+ span.setAttribute(key, value);
28
+ }
29
+ }
30
+ }
31
+ (0, otel_1.setTraceAttributes)(attrs);
32
+ };
33
+ const resolveExtraAttributes = (attributes, requestOrContext) => {
34
+ if (typeof attributes === 'function') {
35
+ return attributes(requestOrContext);
36
+ }
37
+ return attributes !== null && attributes !== void 0 ? attributes : {};
38
+ };
39
+ const sophonzExpressMiddleware = (options = {}) => {
40
+ const { captureBaggage = true, captureClientIp = true, captureUserAgent = true, attributes, } = options;
41
+ return (req, _res, next) => {
42
+ var _a, _b, _c, _d;
43
+ try {
44
+ const attrs = {};
45
+ if (captureBaggage) {
46
+ Object.assign(attrs, getBaggageAttributes());
47
+ }
48
+ if (captureClientIp) {
49
+ attrs['http.client.ip'] = (_c = (_a = req.ip) !== null && _a !== void 0 ? _a : (_b = req.socket) === null || _b === void 0 ? void 0 : _b.remoteAddress) !== null && _c !== void 0 ? _c : '';
50
+ }
51
+ if (captureUserAgent) {
52
+ attrs['http.user_agent'] = (_d = getHeader(req, 'user-agent')) !== null && _d !== void 0 ? _d : '';
53
+ }
54
+ Object.assign(attrs, resolveExtraAttributes(attributes, req));
55
+ applyAttributes(attrs);
56
+ }
57
+ catch {
58
+ }
59
+ next();
60
+ };
61
+ };
62
+ exports.sophonzExpressMiddleware = sophonzExpressMiddleware;
63
+ const sophonzKoaMiddleware = (options = {}) => {
64
+ const { captureBaggage = true, captureClientIp = true, captureUserAgent = true, attributes, } = options;
65
+ return async (ctx, next) => {
66
+ var _a, _b, _c, _d, _e;
67
+ try {
68
+ const attrs = {};
69
+ if (captureBaggage) {
70
+ Object.assign(attrs, getBaggageAttributes());
71
+ }
72
+ if (captureClientIp) {
73
+ attrs['http.client.ip'] = (_c = (_a = ctx.ip) !== null && _a !== void 0 ? _a : (_b = ctx.request) === null || _b === void 0 ? void 0 : _b.ip) !== null && _c !== void 0 ? _c : '';
74
+ }
75
+ if (captureUserAgent) {
76
+ const ua = typeof ctx.get === 'function'
77
+ ? ctx.get('user-agent')
78
+ : (_e = (_d = ctx.request) === null || _d === void 0 ? void 0 : _d.get) === null || _e === void 0 ? void 0 : _e.call(_d, 'user-agent');
79
+ attrs['http.user_agent'] = ua !== null && ua !== void 0 ? ua : '';
80
+ }
81
+ Object.assign(attrs, resolveExtraAttributes(attributes, ctx));
82
+ applyAttributes(attrs);
83
+ }
84
+ catch {
85
+ }
86
+ await next();
87
+ };
88
+ };
89
+ exports.sophonzKoaMiddleware = sophonzKoaMiddleware;
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "0.0.1";
1
+ export declare const VERSION = "0.1.0";
2
2
  //# sourceMappingURL=version.d.ts.map
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.VERSION = void 0;
4
- exports.VERSION = '0.0.1';
4
+ exports.VERSION = '0.1.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sophonz/node-sdk",
3
- "version": "0.0.1",
3
+ "version": "0.1.0",
4
4
  "homepage": "https://sophonz.com",
5
5
  "repository": {
6
6
  "type": "git",