@foam-ai/node-cliengo 0.1.0-alpha.2 → 0.1.0-alpha.20
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/dist/node-cliengo/src/constants.d.ts +1 -2
- package/dist/node-cliengo/src/constants.js +5 -2
- package/dist/node-cliengo/src/http/express.d.ts +21 -0
- package/dist/node-cliengo/src/http/express.js +69 -5
- package/dist/node-cliengo/src/http/fastify.d.ts +18 -4
- package/dist/node-cliengo/src/http/fastify.js +69 -14
- package/dist/node-cliengo/src/index.d.ts +7 -4
- package/dist/node-cliengo/src/index.js +11 -3
- package/dist/node-cliengo/src/init.d.ts +24 -1
- package/dist/node-cliengo/src/init.js +150 -53
- package/dist/node-cliengo/src/job.d.ts +1 -1
- package/dist/node-cliengo/src/job.js +1 -5
- package/dist/node-cliengo/src/logs/pino-destination.d.ts +7 -2
- package/dist/node-cliengo/src/logs/pino-destination.js +34 -9
- package/dist/node-cliengo/src/logs/pino-mixin.d.ts +5 -4
- package/dist/node-cliengo/src/logs/pino-mixin.js +9 -5
- package/dist/node-cliengo/src/logs/winston-transport.js +11 -0
- package/dist/node-cliengo/src/nr.d.ts +17 -0
- package/dist/node-cliengo/src/nr.js +29 -0
- package/dist/node-cliengo/src/sns.d.ts +1 -4
- package/dist/node-cliengo/src/sns.js +2 -9
- package/dist/node-cliengo/src/trace-bridge.d.ts +30 -26
- package/dist/node-cliengo/src/trace-bridge.js +66 -35
- package/dist/node-cliengo/src/types.d.ts +37 -4
- package/package.json +11 -9
|
@@ -2,38 +2,42 @@
|
|
|
2
2
|
* W3C trace context utilities for New Relic and OpenTelemetry interop.
|
|
3
3
|
*
|
|
4
4
|
* Why this file exists:
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* Reads trace context from two sources (NR first, then OTel's own active
|
|
6
|
+
* span) and converts it to/from W3C traceparent format. This means trace
|
|
7
|
+
* propagation works whether NR is installed, partially loaded, or completely
|
|
8
|
+
* removed.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* so the trace context is always current.
|
|
10
|
+
* Two resolution modes:
|
|
11
|
+
* resolveActiveTraceIds() — NR → OTel active span → empty strings.
|
|
12
|
+
* Used by getTraceContext() (log enrichment) and createPinoMixin().
|
|
13
|
+
* Never generates IDs — returns empty when no real source exists, so
|
|
14
|
+
* it doesn't overwrite valid IDs injected by pino-http's customProps.
|
|
15
|
+
*
|
|
16
|
+
* buildTraceparent() — NR → OTel active span → generate fresh IDs.
|
|
17
|
+
* Used by producers (injectSnsAttributes, injectJobData) and the
|
|
18
|
+
* Express/Fastify middleware at request start. Always returns a
|
|
19
|
+
* traceparent so every message/request gets trace context.
|
|
21
20
|
*
|
|
22
21
|
* Edge cases covered:
|
|
23
|
-
* - NR
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* -
|
|
27
|
-
*
|
|
28
|
-
* - Malformed traceparent (wrong number of parts, wrong hex lengths):
|
|
29
|
-
* extractParentContext returns ROOT_CONTEXT — consumer creates a fresh
|
|
30
|
-
* trace instead of crashing.
|
|
31
|
-
* - Flags field always set to 01 (sampled) on buildTraceparent because we
|
|
32
|
-
* want all async boundary crossings traced.
|
|
22
|
+
* - NR's getTraceMetadata() can throw TypeError when the transaction is
|
|
23
|
+
* null (pino-http fires after NR ends the transaction). Guarded with
|
|
24
|
+
* try/catch.
|
|
25
|
+
* - Malformed traceparent: extractParentContext returns ROOT_CONTEXT.
|
|
26
|
+
* - Flags always 01 (sampled) on buildTraceparent.
|
|
33
27
|
*/
|
|
34
28
|
import { type Context } from '@opentelemetry/api';
|
|
35
29
|
import type { TraceContext } from './types';
|
|
36
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Always returns a traceparent — from NR, OTel active span, or freshly
|
|
32
|
+
* generated. Used by producers to inject into SNS attributes / job data,
|
|
33
|
+
* and by the Express/Fastify middleware at request start.
|
|
34
|
+
*/
|
|
35
|
+
export declare function buildTraceparent(): string;
|
|
37
36
|
export declare function extractParentContext(traceparent: string): Context;
|
|
37
|
+
/**
|
|
38
|
+
* Returns trace context from active sources only (NR or OTel active span).
|
|
39
|
+
* Returns empty strings when neither is active — never generates fresh IDs.
|
|
40
|
+
* Used for log enrichment where fake IDs would create false correlations.
|
|
41
|
+
*/
|
|
38
42
|
export declare function getTraceContext(): TraceContext;
|
|
39
43
|
export declare function getActiveContext(): Context;
|
|
@@ -3,50 +3,79 @@
|
|
|
3
3
|
* W3C trace context utilities for New Relic and OpenTelemetry interop.
|
|
4
4
|
*
|
|
5
5
|
* Why this file exists:
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* Reads trace context from two sources (NR first, then OTel's own active
|
|
7
|
+
* span) and converts it to/from W3C traceparent format. This means trace
|
|
8
|
+
* propagation works whether NR is installed, partially loaded, or completely
|
|
9
|
+
* removed.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* so the trace context is always current.
|
|
11
|
+
* Two resolution modes:
|
|
12
|
+
* resolveActiveTraceIds() — NR → OTel active span → empty strings.
|
|
13
|
+
* Used by getTraceContext() (log enrichment) and createPinoMixin().
|
|
14
|
+
* Never generates IDs — returns empty when no real source exists, so
|
|
15
|
+
* it doesn't overwrite valid IDs injected by pino-http's customProps.
|
|
16
|
+
*
|
|
17
|
+
* buildTraceparent() — NR → OTel active span → generate fresh IDs.
|
|
18
|
+
* Used by producers (injectSnsAttributes, injectJobData) and the
|
|
19
|
+
* Express/Fastify middleware at request start. Always returns a
|
|
20
|
+
* traceparent so every message/request gets trace context.
|
|
22
21
|
*
|
|
23
22
|
* Edge cases covered:
|
|
24
|
-
* - NR
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* -
|
|
28
|
-
*
|
|
29
|
-
* - Malformed traceparent (wrong number of parts, wrong hex lengths):
|
|
30
|
-
* extractParentContext returns ROOT_CONTEXT — consumer creates a fresh
|
|
31
|
-
* trace instead of crashing.
|
|
32
|
-
* - Flags field always set to 01 (sampled) on buildTraceparent because we
|
|
33
|
-
* want all async boundary crossings traced.
|
|
23
|
+
* - NR's getTraceMetadata() can throw TypeError when the transaction is
|
|
24
|
+
* null (pino-http fires after NR ends the transaction). Guarded with
|
|
25
|
+
* try/catch.
|
|
26
|
+
* - Malformed traceparent: extractParentContext returns ROOT_CONTEXT.
|
|
27
|
+
* - Flags always 01 (sampled) on buildTraceparent.
|
|
34
28
|
*/
|
|
35
29
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
30
|
exports.buildTraceparent = buildTraceparent;
|
|
37
31
|
exports.extractParentContext = extractParentContext;
|
|
38
32
|
exports.getTraceContext = getTraceContext;
|
|
39
33
|
exports.getActiveContext = getActiveContext;
|
|
34
|
+
const node_crypto_1 = require("node:crypto");
|
|
40
35
|
const api_1 = require("@opentelemetry/api");
|
|
41
36
|
const nr_1 = require("./nr");
|
|
37
|
+
function generateTraceId() {
|
|
38
|
+
return (0, node_crypto_1.randomBytes)(16).toString('hex');
|
|
39
|
+
}
|
|
40
|
+
function generateSpanId() {
|
|
41
|
+
return (0, node_crypto_1.randomBytes)(8).toString('hex');
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Resolves trace IDs from active sources only (NR or OTel active span).
|
|
45
|
+
* Returns empty strings when neither source has an active trace — never
|
|
46
|
+
* generates fresh IDs.
|
|
47
|
+
*/
|
|
48
|
+
function resolveActiveTraceIds() {
|
|
49
|
+
try {
|
|
50
|
+
const nr = (0, nr_1.getNr)();
|
|
51
|
+
const meta = nr.getTraceMetadata?.() ?? { traceId: '', spanId: '' };
|
|
52
|
+
if (meta.traceId && meta.spanId) {
|
|
53
|
+
return { traceId: meta.traceId, spanId: meta.spanId };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// NR transaction is null or getTraceMetadata threw
|
|
58
|
+
}
|
|
59
|
+
const activeSpan = api_1.trace.getActiveSpan();
|
|
60
|
+
if (activeSpan) {
|
|
61
|
+
const sc = activeSpan.spanContext();
|
|
62
|
+
if (sc.traceId && sc.spanId) {
|
|
63
|
+
return { traceId: sc.traceId, spanId: sc.spanId };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return { traceId: '', spanId: '' };
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Always returns a traceparent — from NR, OTel active span, or freshly
|
|
70
|
+
* generated. Used by producers to inject into SNS attributes / job data,
|
|
71
|
+
* and by the Express/Fastify middleware at request start.
|
|
72
|
+
*/
|
|
42
73
|
function buildTraceparent() {
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (!traceId || !spanId) {
|
|
47
|
-
return undefined;
|
|
74
|
+
const { traceId, spanId } = resolveActiveTraceIds();
|
|
75
|
+
if (traceId && spanId) {
|
|
76
|
+
return `00-${traceId}-${spanId}-01`;
|
|
48
77
|
}
|
|
49
|
-
return `00-${
|
|
78
|
+
return `00-${generateTraceId()}-${generateSpanId()}-01`;
|
|
50
79
|
}
|
|
51
80
|
function extractParentContext(traceparent) {
|
|
52
81
|
const parts = traceparent.split('-');
|
|
@@ -66,11 +95,13 @@ function extractParentContext(traceparent) {
|
|
|
66
95
|
};
|
|
67
96
|
return api_1.trace.setSpanContext(api_1.ROOT_CONTEXT, spanContext);
|
|
68
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* Returns trace context from active sources only (NR or OTel active span).
|
|
100
|
+
* Returns empty strings when neither is active — never generates fresh IDs.
|
|
101
|
+
* Used for log enrichment where fake IDs would create false correlations.
|
|
102
|
+
*/
|
|
69
103
|
function getTraceContext() {
|
|
70
|
-
const
|
|
71
|
-
const meta = nr.getTraceMetadata?.() ?? { traceId: '', spanId: '' };
|
|
72
|
-
const traceId = meta.traceId ?? '';
|
|
73
|
-
const spanId = meta.spanId ?? '';
|
|
104
|
+
const { traceId, spanId } = resolveActiveTraceIds();
|
|
74
105
|
const traceparent = traceId && spanId ? `00-${traceId}-${spanId}-01` : '';
|
|
75
106
|
return { traceId, spanId, traceparent };
|
|
76
107
|
}
|
|
@@ -27,12 +27,12 @@ import type { MeterProvider } from '@opentelemetry/sdk-metrics';
|
|
|
27
27
|
import type { BasicTracerProvider } from '@opentelemetry/sdk-trace-base';
|
|
28
28
|
export interface InitOptions {
|
|
29
29
|
enabled?: boolean;
|
|
30
|
+
token?: string;
|
|
30
31
|
newrelic?: NewRelicAgent;
|
|
31
32
|
winston?: WinstonLogger;
|
|
32
33
|
enableTraces?: boolean;
|
|
33
34
|
enableLogs?: boolean;
|
|
34
35
|
enableMetrics?: boolean;
|
|
35
|
-
endpoint?: string;
|
|
36
36
|
forceExport?: boolean;
|
|
37
37
|
autoShutdown?: boolean;
|
|
38
38
|
}
|
|
@@ -49,7 +49,6 @@ export interface FoamMetrics {
|
|
|
49
49
|
export interface FoamInstance {
|
|
50
50
|
tracer: Tracer;
|
|
51
51
|
meter: Meter;
|
|
52
|
-
logger: LoggerProvider;
|
|
53
52
|
traceProvider: BasicTracerProvider;
|
|
54
53
|
meterProvider: MeterProvider;
|
|
55
54
|
loggerProvider: LoggerProvider;
|
|
@@ -61,14 +60,48 @@ export interface FoamInstance {
|
|
|
61
60
|
createWinstonFormat(): WinstonFormat;
|
|
62
61
|
createWinstonTransport(): WinstonTransport;
|
|
63
62
|
createPinoMixin(): () => Record<string, string>;
|
|
63
|
+
createPinoHttpOptions(): {
|
|
64
|
+
customProps: (req: any) => Record<string, string>;
|
|
65
|
+
};
|
|
64
66
|
createPinoDestination(): NodeJS.WritableStream;
|
|
65
|
-
|
|
67
|
+
/**
|
|
68
|
+
* Creates a Pino logger with trace-context mixin and OTLP destination pre-wired.
|
|
69
|
+
* Pass `streams` to replace the default stdout output (e.g., for pino-pretty):
|
|
70
|
+
*
|
|
71
|
+
* foam.createPinoLogger({ streams: [{ stream: pinoPretty() }] })
|
|
72
|
+
*
|
|
73
|
+
* The OTLP destination is always appended automatically.
|
|
74
|
+
*/
|
|
75
|
+
createPinoLogger(options?: Record<string, any>): any;
|
|
76
|
+
createPinoHttpConfig(opts?: {
|
|
77
|
+
logger?: any;
|
|
78
|
+
}): Record<string, any>;
|
|
79
|
+
/**
|
|
80
|
+
* Returns a Pino config object for Fastify's `logger` constructor option.
|
|
81
|
+
* Fastify constructs its own Pino instance from this, so request.log gets
|
|
82
|
+
* child loggers with request context automatically.
|
|
83
|
+
*
|
|
84
|
+
* Fastify({ logger: foam.createFastifyLoggerConfig({ level: 'info' }) })
|
|
85
|
+
*
|
|
86
|
+
* Pass `streams` to replace stdout (e.g., pino-pretty in dev):
|
|
87
|
+
*
|
|
88
|
+
* foam.createFastifyLoggerConfig({
|
|
89
|
+
* level: 'debug',
|
|
90
|
+
* streams: [{ stream: pinoPretty() }],
|
|
91
|
+
* })
|
|
92
|
+
*/
|
|
93
|
+
createFastifyLoggerConfig(opts?: Record<string, any>): Record<string, any>;
|
|
94
|
+
buildTraceparent(): string;
|
|
66
95
|
injectSnsAttributes(attrs: Record<string, SnsMessageAttributeValue>): Record<string, SnsMessageAttributeValue>;
|
|
67
96
|
injectJobData<T extends Record<string, unknown>>(data: T): T & {
|
|
68
|
-
traceparent
|
|
97
|
+
traceparent: string;
|
|
69
98
|
};
|
|
99
|
+
/** @deprecated Pass (spanName, msg, fn) — the tracer arg is no longer needed. */
|
|
70
100
|
wrapSqsConsumer(tracer: Tracer, spanName: string, msg: SqsMessage, fn: () => Promise<void>): Promise<void>;
|
|
101
|
+
wrapSqsConsumer(spanName: string, msg: SqsMessage, fn: () => Promise<void>): Promise<void>;
|
|
102
|
+
/** @deprecated Pass (spanName, jobData, fn) — the tracer arg is no longer needed. */
|
|
71
103
|
wrapJobConsumer(tracer: Tracer, spanName: string, jobData: Record<string, unknown>, fn: () => Promise<void>): Promise<void>;
|
|
104
|
+
wrapJobConsumer(spanName: string, jobData: Record<string, unknown>, fn: () => Promise<void>): Promise<void>;
|
|
72
105
|
extractParentContext(traceparent: string): Context;
|
|
73
106
|
shutdown(): Promise<void>;
|
|
74
107
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@foam-ai/node-cliengo",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.20",
|
|
4
4
|
"description": "Unified observability (traces, logs, metrics) for Cliengo Node.js services, connecting New Relic APM with Foam's OTel collector.",
|
|
5
5
|
"main": "dist/node-cliengo/src/index.js",
|
|
6
6
|
"types": "dist/node-cliengo/src/index.d.ts",
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
"peerDependencies": {
|
|
21
21
|
"@opentelemetry/api": "^1.9.0",
|
|
22
22
|
"newrelic": ">=12.0.0",
|
|
23
|
-
"
|
|
24
|
-
"
|
|
23
|
+
"pino": "^8.0.0 || ^9.0.0",
|
|
24
|
+
"winston": "^3.0.0"
|
|
25
25
|
},
|
|
26
26
|
"peerDependenciesMeta": {
|
|
27
27
|
"newrelic": {
|
|
@@ -35,18 +35,20 @@
|
|
|
35
35
|
}
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@opentelemetry/
|
|
39
|
-
"@opentelemetry/sdk-logs": "^0.203.0",
|
|
40
|
-
"@opentelemetry/sdk-metrics": "^2.0.1",
|
|
41
|
-
"@opentelemetry/exporter-trace-otlp-http": "^0.203.0",
|
|
38
|
+
"@opentelemetry/api-logs": "^0.203.0",
|
|
42
39
|
"@opentelemetry/exporter-logs-otlp-http": "^0.203.0",
|
|
43
40
|
"@opentelemetry/exporter-metrics-otlp-http": "^0.201.1",
|
|
41
|
+
"@opentelemetry/exporter-trace-otlp-http": "^0.203.0",
|
|
44
42
|
"@opentelemetry/resources": "^2.0.1",
|
|
45
|
-
"@opentelemetry/
|
|
43
|
+
"@opentelemetry/sdk-logs": "^0.203.0",
|
|
44
|
+
"@opentelemetry/sdk-metrics": "^2.0.1",
|
|
45
|
+
"@opentelemetry/sdk-trace-base": "^2.0.0"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
+
"@opentelemetry/api": "^1.9.1",
|
|
49
|
+
"@types/node": "^20.11.0",
|
|
48
50
|
"typescript": "^6.0.2",
|
|
49
|
-
"
|
|
51
|
+
"vitest": "^4.1.8"
|
|
50
52
|
},
|
|
51
53
|
"engines": {
|
|
52
54
|
"node": ">=18"
|