@ciq-dev/neoiq-foundation-node 1.0.0 → 1.0.1-beta.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 +228 -68
- package/dist/index.d.mts +573 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.d.ts +570 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +885 -28
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +832 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +27 -21
- package/dist/http-client.d.ts +0 -80
- package/dist/http-client.d.ts.map +0 -1
- package/dist/http-client.js +0 -188
- package/dist/http-client.js.map +0 -1
- package/dist/observability.d.ts +0 -132
- package/dist/observability.d.ts.map +0 -1
- package/dist/observability.js +0 -246
- package/dist/observability.js.map +0 -1
- package/dist/plugin.d.ts +0 -40
- package/dist/plugin.d.ts.map +0 -1
- package/dist/plugin.js +0 -176
- package/dist/plugin.js.map +0 -1
package/dist/observability.js
DELETED
|
@@ -1,246 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* OpenTelemetry initialization for Node.js services.
|
|
4
|
-
*
|
|
5
|
-
* Follows Groundcover Integration Guide - Option 2 (Via OTEL Collector)
|
|
6
|
-
*
|
|
7
|
-
* Flow: App → OpenTelemetry SDK → OTEL Collector → Groundcover
|
|
8
|
-
*
|
|
9
|
-
* Environment Variables:
|
|
10
|
-
* OTEL_EXPORTER_OTLP_ENDPOINT: Collector URL (default: cluster OTEL Collector)
|
|
11
|
-
* OTEL_SERVICE_NAME: Service name
|
|
12
|
-
* OTEL_SERVICE_VERSION: Service version (default: 1.0.0)
|
|
13
|
-
* OTEL_ENVIRONMENT: Deployment environment (default: development)
|
|
14
|
-
*
|
|
15
|
-
* Usage:
|
|
16
|
-
* import { init, logger, getMeter } from '@commerceiq/neoiq-foundation-node';
|
|
17
|
-
*
|
|
18
|
-
* init({ serviceName: 'my-service' });
|
|
19
|
-
*
|
|
20
|
-
* logger.info({ action: 'startup' }, 'Service started');
|
|
21
|
-
*
|
|
22
|
-
* const meter = getMeter('my-service');
|
|
23
|
-
* const counter = meter.createCounter('requests_total');
|
|
24
|
-
*/
|
|
25
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
-
};
|
|
28
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
-
exports.SpanStatusCode = exports.logger = exports.als = void 0;
|
|
30
|
-
exports.init = init;
|
|
31
|
-
exports.getTracer = getTracer;
|
|
32
|
-
exports.getMeter = getMeter;
|
|
33
|
-
exports.getRequestContext = getRequestContext;
|
|
34
|
-
exports.runWithContext = runWithContext;
|
|
35
|
-
exports.getTraceContext = getTraceContext;
|
|
36
|
-
exports.shutdown = shutdown;
|
|
37
|
-
const sdk_node_1 = require("@opentelemetry/sdk-node");
|
|
38
|
-
const auto_instrumentations_node_1 = require("@opentelemetry/auto-instrumentations-node");
|
|
39
|
-
const exporter_trace_otlp_grpc_1 = require("@opentelemetry/exporter-trace-otlp-grpc");
|
|
40
|
-
const exporter_metrics_otlp_grpc_1 = require("@opentelemetry/exporter-metrics-otlp-grpc");
|
|
41
|
-
const sdk_metrics_1 = require("@opentelemetry/sdk-metrics");
|
|
42
|
-
const resources_1 = require("@opentelemetry/resources");
|
|
43
|
-
const semantic_conventions_1 = require("@opentelemetry/semantic-conventions");
|
|
44
|
-
const api_1 = require("@opentelemetry/api");
|
|
45
|
-
Object.defineProperty(exports, "SpanStatusCode", { enumerable: true, get: function () { return api_1.SpanStatusCode; } });
|
|
46
|
-
const pino_1 = __importDefault(require("pino"));
|
|
47
|
-
const async_hooks_1 = require("async_hooks");
|
|
48
|
-
// -----------------------------------------------------------------------------
|
|
49
|
-
// Configuration
|
|
50
|
-
// -----------------------------------------------------------------------------
|
|
51
|
-
// Default OTEL Collector endpoint (per Groundcover guide - Kubernetes internal)
|
|
52
|
-
const DEFAULT_OTEL_COLLECTOR = 'http://otel-stack-deployment-collector.observability.svc.cluster.local:4317';
|
|
53
|
-
exports.als = new async_hooks_1.AsyncLocalStorage();
|
|
54
|
-
// -----------------------------------------------------------------------------
|
|
55
|
-
// Module State
|
|
56
|
-
// -----------------------------------------------------------------------------
|
|
57
|
-
let sdk = null;
|
|
58
|
-
let meterProvider = null;
|
|
59
|
-
let baseLogger;
|
|
60
|
-
let serviceName = 'unknown';
|
|
61
|
-
let initialized = false;
|
|
62
|
-
// -----------------------------------------------------------------------------
|
|
63
|
-
// Initialization
|
|
64
|
-
// -----------------------------------------------------------------------------
|
|
65
|
-
/**
|
|
66
|
-
* Initialize OpenTelemetry SDK with traces and metrics.
|
|
67
|
-
*
|
|
68
|
-
* Sends telemetry to OTEL Collector which forwards to Groundcover.
|
|
69
|
-
* Call this at the very start of your application, before other imports.
|
|
70
|
-
*
|
|
71
|
-
* @example
|
|
72
|
-
* init({ serviceName: 'canvas-weaver' });
|
|
73
|
-
*/
|
|
74
|
-
function init(options) {
|
|
75
|
-
if (initialized) {
|
|
76
|
-
console.warn('[neoiq-foundation-node] Already initialized, skipping...');
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
const { serviceName: svcName, serviceVersion = process.env.OTEL_SERVICE_VERSION || '1.0.0', environment = process.env.OTEL_ENVIRONMENT || 'development', otlpEndpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT || DEFAULT_OTEL_COLLECTOR, logLevel = process.env.LOG_LEVEL || 'info', metricsIntervalMs = 5000, // 5 seconds per Groundcover guide
|
|
80
|
-
} = options;
|
|
81
|
-
serviceName = svcName;
|
|
82
|
-
// 1. Create Resource (attached to all telemetry)
|
|
83
|
-
const resource = (0, resources_1.resourceFromAttributes)({
|
|
84
|
-
[semantic_conventions_1.ATTR_SERVICE_NAME]: serviceName,
|
|
85
|
-
[semantic_conventions_1.ATTR_SERVICE_VERSION]: serviceVersion,
|
|
86
|
-
'deployment.environment': environment,
|
|
87
|
-
});
|
|
88
|
-
// 2. Setup Metrics Exporter (per Groundcover guide)
|
|
89
|
-
const metricExporter = new exporter_metrics_otlp_grpc_1.OTLPMetricExporter({ url: otlpEndpoint });
|
|
90
|
-
const metricReader = new sdk_metrics_1.PeriodicExportingMetricReader({
|
|
91
|
-
exporter: metricExporter,
|
|
92
|
-
exportIntervalMillis: metricsIntervalMs,
|
|
93
|
-
});
|
|
94
|
-
meterProvider = new sdk_metrics_1.MeterProvider({
|
|
95
|
-
resource,
|
|
96
|
-
readers: [metricReader],
|
|
97
|
-
});
|
|
98
|
-
api_1.metrics.setGlobalMeterProvider(meterProvider);
|
|
99
|
-
// 3. Setup Trace Exporter
|
|
100
|
-
const traceExporter = new exporter_trace_otlp_grpc_1.OTLPTraceExporter({ url: otlpEndpoint });
|
|
101
|
-
// 4. Initialize NodeSDK with auto-instrumentation
|
|
102
|
-
sdk = new sdk_node_1.NodeSDK({
|
|
103
|
-
resource,
|
|
104
|
-
traceExporter,
|
|
105
|
-
instrumentations: [
|
|
106
|
-
(0, auto_instrumentations_node_1.getNodeAutoInstrumentations)({
|
|
107
|
-
// Disable fs instrumentation (too noisy)
|
|
108
|
-
'@opentelemetry/instrumentation-fs': { enabled: false },
|
|
109
|
-
}),
|
|
110
|
-
],
|
|
111
|
-
});
|
|
112
|
-
sdk.start();
|
|
113
|
-
// 5. Setup Pino logger with trace context injection
|
|
114
|
-
baseLogger = (0, pino_1.default)({
|
|
115
|
-
level: logLevel,
|
|
116
|
-
base: {
|
|
117
|
-
service: serviceName,
|
|
118
|
-
version: serviceVersion,
|
|
119
|
-
env: environment,
|
|
120
|
-
},
|
|
121
|
-
mixin: () => {
|
|
122
|
-
// Inject trace context into every log (correlates logs with traces in Groundcover)
|
|
123
|
-
const span = api_1.trace.getActiveSpan();
|
|
124
|
-
const ctx = exports.als.getStore();
|
|
125
|
-
const spanContext = span?.spanContext();
|
|
126
|
-
return {
|
|
127
|
-
traceId: spanContext?.traceId || ctx?.traceId,
|
|
128
|
-
spanId: spanContext?.spanId || ctx?.spanId,
|
|
129
|
-
correlationId: ctx?.correlationId,
|
|
130
|
-
};
|
|
131
|
-
},
|
|
132
|
-
formatters: {
|
|
133
|
-
level: (label) => ({ level: label }),
|
|
134
|
-
},
|
|
135
|
-
transport: environment === 'development'
|
|
136
|
-
? { target: 'pino-pretty', options: { colorize: true } }
|
|
137
|
-
: undefined,
|
|
138
|
-
});
|
|
139
|
-
baseLogger.info({
|
|
140
|
-
endpoint: otlpEndpoint,
|
|
141
|
-
metricsInterval: `${metricsIntervalMs}ms`,
|
|
142
|
-
}, `OpenTelemetry initialized. Sending to OTEL Collector.`);
|
|
143
|
-
initialized = true;
|
|
144
|
-
}
|
|
145
|
-
// -----------------------------------------------------------------------------
|
|
146
|
-
// Logger API
|
|
147
|
-
// -----------------------------------------------------------------------------
|
|
148
|
-
/**
|
|
149
|
-
* Structured logger with automatic trace context injection.
|
|
150
|
-
* All logs include traceId, spanId, and correlationId when available.
|
|
151
|
-
*/
|
|
152
|
-
exports.logger = {
|
|
153
|
-
info: (obj, msg) => baseLogger?.info(obj, msg),
|
|
154
|
-
error: (obj, msg) => baseLogger?.error(obj, msg),
|
|
155
|
-
warn: (obj, msg) => baseLogger?.warn(obj, msg),
|
|
156
|
-
debug: (obj, msg) => baseLogger?.debug(obj, msg),
|
|
157
|
-
child: (bindings) => baseLogger?.child(bindings),
|
|
158
|
-
};
|
|
159
|
-
// -----------------------------------------------------------------------------
|
|
160
|
-
// Tracer API
|
|
161
|
-
// -----------------------------------------------------------------------------
|
|
162
|
-
/**
|
|
163
|
-
* Get a tracer instance for creating spans.
|
|
164
|
-
*
|
|
165
|
-
* @param name - Tracer name (defaults to service name)
|
|
166
|
-
*
|
|
167
|
-
* @example
|
|
168
|
-
* const tracer = getTracer();
|
|
169
|
-
* tracer.startActiveSpan('db.query', (span) => {
|
|
170
|
-
* // ... do work
|
|
171
|
-
* span.end();
|
|
172
|
-
* });
|
|
173
|
-
*/
|
|
174
|
-
function getTracer(name) {
|
|
175
|
-
return api_1.trace.getTracer(name || serviceName);
|
|
176
|
-
}
|
|
177
|
-
// -----------------------------------------------------------------------------
|
|
178
|
-
// Meter API
|
|
179
|
-
// -----------------------------------------------------------------------------
|
|
180
|
-
/**
|
|
181
|
-
* Get a meter instance for creating metrics.
|
|
182
|
-
* Metrics are exported to OTEL Collector → Groundcover.
|
|
183
|
-
*
|
|
184
|
-
* @param name - Meter name (typically service name)
|
|
185
|
-
* @param version - Meter version (default: 1.0.0)
|
|
186
|
-
*
|
|
187
|
-
* @example
|
|
188
|
-
* const meter = getMeter('canvas-weaver');
|
|
189
|
-
* const counter = meter.createCounter('http.requests.total');
|
|
190
|
-
* counter.add(1, { method: 'GET', route: '/api/reports' });
|
|
191
|
-
*
|
|
192
|
-
* const histogram = meter.createHistogram('http.request.duration');
|
|
193
|
-
* histogram.record(150, { method: 'GET', route: '/api/reports' });
|
|
194
|
-
*/
|
|
195
|
-
function getMeter(name, version = '1.0.0') {
|
|
196
|
-
return api_1.metrics.getMeter(name, version);
|
|
197
|
-
}
|
|
198
|
-
// -----------------------------------------------------------------------------
|
|
199
|
-
// Context Helpers
|
|
200
|
-
// -----------------------------------------------------------------------------
|
|
201
|
-
/**
|
|
202
|
-
* Get current request context from AsyncLocalStorage.
|
|
203
|
-
*/
|
|
204
|
-
function getRequestContext() {
|
|
205
|
-
return exports.als.getStore();
|
|
206
|
-
}
|
|
207
|
-
/**
|
|
208
|
-
* Run a function with a specific context (for manual context propagation).
|
|
209
|
-
*/
|
|
210
|
-
function runWithContext(ctx, fn) {
|
|
211
|
-
return exports.als.run(ctx, fn);
|
|
212
|
-
}
|
|
213
|
-
/**
|
|
214
|
-
* Get current trace context as a dictionary.
|
|
215
|
-
* Useful for logging or passing to external systems.
|
|
216
|
-
*/
|
|
217
|
-
function getTraceContext() {
|
|
218
|
-
const span = api_1.trace.getActiveSpan();
|
|
219
|
-
if (!span)
|
|
220
|
-
return {};
|
|
221
|
-
const ctx = span.spanContext();
|
|
222
|
-
return {
|
|
223
|
-
traceId: ctx.traceId,
|
|
224
|
-
spanId: ctx.spanId,
|
|
225
|
-
};
|
|
226
|
-
}
|
|
227
|
-
// -----------------------------------------------------------------------------
|
|
228
|
-
// Shutdown
|
|
229
|
-
// -----------------------------------------------------------------------------
|
|
230
|
-
/**
|
|
231
|
-
* Gracefully shutdown OTEL providers.
|
|
232
|
-
* Call this on application shutdown to flush pending telemetry.
|
|
233
|
-
*/
|
|
234
|
-
async function shutdown() {
|
|
235
|
-
if (!initialized)
|
|
236
|
-
return;
|
|
237
|
-
try {
|
|
238
|
-
await meterProvider?.shutdown();
|
|
239
|
-
await sdk?.shutdown();
|
|
240
|
-
console.log('[neoiq-foundation-node] OTEL shutdown complete');
|
|
241
|
-
}
|
|
242
|
-
catch (error) {
|
|
243
|
-
console.error('[neoiq-foundation-node] Error during shutdown:', error);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
//# sourceMappingURL=observability.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"observability.js","sourceRoot":"","sources":["../src/observability.ts"],"names":[],"mappings":"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"}
|
package/dist/plugin.d.ts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Fastify Observability Plugin
|
|
3
|
-
*
|
|
4
|
-
* Automatically handles:
|
|
5
|
-
* - Correlation ID extraction/generation (x-request-id header)
|
|
6
|
-
* - OpenTelemetry trace context propagation
|
|
7
|
-
* - Request/response logging with trace context
|
|
8
|
-
* - HTTP server metrics (request count, duration)
|
|
9
|
-
*
|
|
10
|
-
* Usage:
|
|
11
|
-
* import { observabilityPlugin } from '@commerceiq/neoiq-foundation-node';
|
|
12
|
-
* app.register(observabilityPlugin, { serviceName: 'my-service' });
|
|
13
|
-
*/
|
|
14
|
-
import { FastifyPluginAsync } from 'fastify';
|
|
15
|
-
import { Span } from '@opentelemetry/api';
|
|
16
|
-
interface PluginRequestContext {
|
|
17
|
-
correlationId: string;
|
|
18
|
-
traceId: string;
|
|
19
|
-
spanId: string;
|
|
20
|
-
startTime: number;
|
|
21
|
-
}
|
|
22
|
-
export interface PluginOptions {
|
|
23
|
-
/**
|
|
24
|
-
* Service name for metrics (required).
|
|
25
|
-
*/
|
|
26
|
-
serviceName: string;
|
|
27
|
-
/**
|
|
28
|
-
* Routes to exclude from tracing/metrics (e.g., ['/health']).
|
|
29
|
-
*/
|
|
30
|
-
excludeRoutes?: string[];
|
|
31
|
-
}
|
|
32
|
-
declare module 'fastify' {
|
|
33
|
-
interface FastifyRequest {
|
|
34
|
-
__span?: Span;
|
|
35
|
-
__requestContext?: PluginRequestContext;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
export declare const observabilityPlugin: FastifyPluginAsync<PluginOptions>;
|
|
39
|
-
export {};
|
|
40
|
-
//# sourceMappingURL=plugin.d.ts.map
|
package/dist/plugin.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,kBAAkB,EAAgC,MAAM,SAAS,CAAC;AAG3E,OAAO,EAA+C,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAOvF,UAAU,oBAAoB;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AAGD,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,cAAc;QACtB,MAAM,CAAC,EAAE,IAAI,CAAC;QACd,gBAAgB,CAAC,EAAE,oBAAoB,CAAC;KACzC;CACF;AA8LD,eAAO,MAAM,mBAAmB,mCAG9B,CAAC"}
|
package/dist/plugin.js
DELETED
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Fastify Observability Plugin
|
|
4
|
-
*
|
|
5
|
-
* Automatically handles:
|
|
6
|
-
* - Correlation ID extraction/generation (x-request-id header)
|
|
7
|
-
* - OpenTelemetry trace context propagation
|
|
8
|
-
* - Request/response logging with trace context
|
|
9
|
-
* - HTTP server metrics (request count, duration)
|
|
10
|
-
*
|
|
11
|
-
* Usage:
|
|
12
|
-
* import { observabilityPlugin } from '@commerceiq/neoiq-foundation-node';
|
|
13
|
-
* app.register(observabilityPlugin, { serviceName: 'my-service' });
|
|
14
|
-
*/
|
|
15
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
16
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
17
|
-
};
|
|
18
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
-
exports.observabilityPlugin = void 0;
|
|
20
|
-
const fastify_plugin_1 = __importDefault(require("fastify-plugin"));
|
|
21
|
-
const crypto_1 = require("crypto");
|
|
22
|
-
const api_1 = require("@opentelemetry/api");
|
|
23
|
-
const observability_1 = require("./observability");
|
|
24
|
-
// -----------------------------------------------------------------------------
|
|
25
|
-
// Plugin Implementation
|
|
26
|
-
// -----------------------------------------------------------------------------
|
|
27
|
-
const plugin = async (fastify, options) => {
|
|
28
|
-
const { serviceName, excludeRoutes = ['/health', '/health/'] } = options;
|
|
29
|
-
const tracer = api_1.trace.getTracer('neoiq-foundation-node');
|
|
30
|
-
// Setup metrics (per Groundcover guide)
|
|
31
|
-
const meter = (0, observability_1.getMeter)(serviceName);
|
|
32
|
-
const requestCounter = meter.createCounter('http.server.requests.total', {
|
|
33
|
-
description: 'Total number of HTTP requests',
|
|
34
|
-
});
|
|
35
|
-
const requestDuration = meter.createHistogram('http.server.request.duration', {
|
|
36
|
-
description: 'HTTP request duration in milliseconds',
|
|
37
|
-
unit: 'ms',
|
|
38
|
-
});
|
|
39
|
-
const requestErrors = meter.createCounter('http.server.requests.errors', {
|
|
40
|
-
description: 'Total number of HTTP request errors',
|
|
41
|
-
});
|
|
42
|
-
// ---------------------------------------------------------------------------
|
|
43
|
-
// ON REQUEST - Extract context, start span
|
|
44
|
-
// ---------------------------------------------------------------------------
|
|
45
|
-
fastify.addHook('onRequest', (request, reply, done) => {
|
|
46
|
-
// Skip health checks
|
|
47
|
-
if (excludeRoutes.some((route) => request.url.startsWith(route))) {
|
|
48
|
-
done();
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
// 1. Extract or generate correlation ID from x-request-id header
|
|
52
|
-
const correlationId = request.headers['x-request-id'] || (0, crypto_1.randomUUID)();
|
|
53
|
-
// 2. Set correlation ID in response header
|
|
54
|
-
reply.header('x-request-id', correlationId);
|
|
55
|
-
// 3. Extract parent trace context from incoming headers (W3C traceparent)
|
|
56
|
-
const parentContext = api_1.propagation.extract(api_1.context.active(), request.headers);
|
|
57
|
-
// 4. Start a new span for this request
|
|
58
|
-
const span = tracer.startSpan(`${request.method} ${request.routeOptions?.url || request.url}`, {
|
|
59
|
-
kind: 1, // SpanKind.SERVER
|
|
60
|
-
attributes: {
|
|
61
|
-
'http.method': request.method,
|
|
62
|
-
'http.url': request.url,
|
|
63
|
-
'http.route': request.routeOptions?.url || request.url,
|
|
64
|
-
'http.user_agent': request.headers['user-agent'] || '',
|
|
65
|
-
'http.correlation_id': correlationId,
|
|
66
|
-
},
|
|
67
|
-
}, parentContext);
|
|
68
|
-
// 5. Get trace/span IDs
|
|
69
|
-
const spanContext = span.spanContext();
|
|
70
|
-
const traceId = spanContext.traceId;
|
|
71
|
-
const spanId = spanContext.spanId;
|
|
72
|
-
// 6. Store context
|
|
73
|
-
const requestContext = {
|
|
74
|
-
correlationId,
|
|
75
|
-
traceId,
|
|
76
|
-
spanId,
|
|
77
|
-
startTime: Date.now(),
|
|
78
|
-
};
|
|
79
|
-
request.__span = span;
|
|
80
|
-
request.__requestContext = requestContext;
|
|
81
|
-
// 7. Run in AsyncLocalStorage context
|
|
82
|
-
observability_1.als.run({ correlationId, traceId, spanId }, () => {
|
|
83
|
-
observability_1.logger.info({
|
|
84
|
-
correlationId,
|
|
85
|
-
traceId,
|
|
86
|
-
spanId,
|
|
87
|
-
method: request.method,
|
|
88
|
-
url: request.url,
|
|
89
|
-
route: request.routeOptions?.url,
|
|
90
|
-
userAgent: request.headers['user-agent'],
|
|
91
|
-
}, 'Request received');
|
|
92
|
-
done();
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
// ---------------------------------------------------------------------------
|
|
96
|
-
// ON RESPONSE - End span, record metrics
|
|
97
|
-
// ---------------------------------------------------------------------------
|
|
98
|
-
fastify.addHook('onResponse', (request, reply, done) => {
|
|
99
|
-
const ctx = request.__requestContext;
|
|
100
|
-
const span = request.__span;
|
|
101
|
-
if (!ctx) {
|
|
102
|
-
done();
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
const durationMs = Date.now() - ctx.startTime;
|
|
106
|
-
const route = request.routeOptions?.url || request.url;
|
|
107
|
-
const labels = {
|
|
108
|
-
method: request.method,
|
|
109
|
-
route,
|
|
110
|
-
status_code: String(reply.statusCode),
|
|
111
|
-
};
|
|
112
|
-
// Run in context for proper log correlation
|
|
113
|
-
observability_1.als.run({ correlationId: ctx.correlationId, traceId: ctx.traceId, spanId: ctx.spanId }, () => {
|
|
114
|
-
// Log response
|
|
115
|
-
observability_1.logger.info({
|
|
116
|
-
correlationId: ctx.correlationId,
|
|
117
|
-
traceId: ctx.traceId,
|
|
118
|
-
spanId: ctx.spanId,
|
|
119
|
-
method: request.method,
|
|
120
|
-
url: request.url,
|
|
121
|
-
route,
|
|
122
|
-
statusCode: reply.statusCode,
|
|
123
|
-
durationMs,
|
|
124
|
-
}, 'Request completed');
|
|
125
|
-
// Record metrics (per Groundcover guide)
|
|
126
|
-
requestCounter.add(1, labels);
|
|
127
|
-
requestDuration.record(durationMs, labels);
|
|
128
|
-
if (reply.statusCode >= 400) {
|
|
129
|
-
requestErrors.add(1, labels);
|
|
130
|
-
}
|
|
131
|
-
// End span
|
|
132
|
-
if (span) {
|
|
133
|
-
span.setStatus({
|
|
134
|
-
code: reply.statusCode < 400 ? api_1.SpanStatusCode.OK : api_1.SpanStatusCode.ERROR,
|
|
135
|
-
});
|
|
136
|
-
span.setAttribute('http.status_code', reply.statusCode);
|
|
137
|
-
span.setAttribute('http.response_time_ms', durationMs);
|
|
138
|
-
span.end();
|
|
139
|
-
}
|
|
140
|
-
done();
|
|
141
|
-
});
|
|
142
|
-
});
|
|
143
|
-
// ---------------------------------------------------------------------------
|
|
144
|
-
// ON ERROR - Record exception on span
|
|
145
|
-
// ---------------------------------------------------------------------------
|
|
146
|
-
fastify.addHook('onError', (request, reply, error, done) => {
|
|
147
|
-
const ctx = request.__requestContext;
|
|
148
|
-
const span = request.__span;
|
|
149
|
-
if (!ctx) {
|
|
150
|
-
done();
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
observability_1.als.run({ correlationId: ctx.correlationId, traceId: ctx.traceId, spanId: ctx.spanId }, () => {
|
|
154
|
-
observability_1.logger.error({
|
|
155
|
-
correlationId: ctx.correlationId,
|
|
156
|
-
traceId: ctx.traceId,
|
|
157
|
-
spanId: ctx.spanId,
|
|
158
|
-
method: request.method,
|
|
159
|
-
url: request.url,
|
|
160
|
-
error: error.message,
|
|
161
|
-
stack: error.stack,
|
|
162
|
-
}, 'Request failed');
|
|
163
|
-
if (span) {
|
|
164
|
-
span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: error.message });
|
|
165
|
-
span.recordException(error);
|
|
166
|
-
}
|
|
167
|
-
done();
|
|
168
|
-
});
|
|
169
|
-
});
|
|
170
|
-
};
|
|
171
|
-
// Export plugin
|
|
172
|
-
exports.observabilityPlugin = (0, fastify_plugin_1.default)(plugin, {
|
|
173
|
-
name: 'neoiq-observability',
|
|
174
|
-
fastify: '4.x',
|
|
175
|
-
});
|
|
176
|
-
//# sourceMappingURL=plugin.js.map
|
package/dist/plugin.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
|