@payloops/observability 0.0.5 → 0.0.7
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 +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +141 -16
- package/package.json +7 -2
package/README.md
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -18,6 +18,8 @@ declare function shutdownTelemetry(): Promise<void>;
|
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Base logger with trace context mixin
|
|
21
|
+
* In development: pretty print to console + OTLP
|
|
22
|
+
* In production: JSON to stdout + OTLP
|
|
21
23
|
*/
|
|
22
24
|
declare const logger: pino.Logger<never, boolean>;
|
|
23
25
|
/**
|
|
@@ -94,7 +96,7 @@ declare function recordWebhookDelivery(status: 'success' | 'failed', attempt: nu
|
|
|
94
96
|
declare function recordWebhookLatency(durationMs: number, status: 'success' | 'failed'): void;
|
|
95
97
|
declare function recordHttpRequest(method: string, path: string, statusCode: number, durationMs: number): void;
|
|
96
98
|
declare function recordWorkflowStarted(workflowType: string, taskQueue: string): void;
|
|
97
|
-
declare function recordWorkflowCompleted(workflowType: string, taskQueue: string,
|
|
99
|
+
declare function recordWorkflowCompleted(workflowType: string, taskQueue: string, _durationMs: number): void;
|
|
98
100
|
declare function recordWorkflowFailed(workflowType: string, taskQueue: string, errorType: string): void;
|
|
99
101
|
declare function recordActivityLatency(activityType: string, durationMs: number, status: 'success' | 'failed'): void;
|
|
100
102
|
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { NodeSDK } from "@opentelemetry/sdk-node";
|
|
3
3
|
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
|
|
4
4
|
import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
|
|
5
|
+
import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
|
|
5
6
|
import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
|
|
6
7
|
|
|
7
8
|
// node_modules/@opentelemetry/resources/build/esm/Resource.js
|
|
@@ -229,18 +230,31 @@ var Resource = (
|
|
|
229
230
|
// src/lib/otel.ts
|
|
230
231
|
import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from "@opentelemetry/semantic-conventions";
|
|
231
232
|
import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
|
|
233
|
+
import {
|
|
234
|
+
LoggerProvider,
|
|
235
|
+
BatchLogRecordProcessor
|
|
236
|
+
} from "@opentelemetry/sdk-logs";
|
|
237
|
+
import { logs } from "@opentelemetry/api-logs";
|
|
232
238
|
var sdk = null;
|
|
239
|
+
var loggerProvider = null;
|
|
233
240
|
function initTelemetry(config, serviceVersion = "0.0.1") {
|
|
234
241
|
if (sdk) return sdk;
|
|
235
242
|
const cfg = typeof config === "string" ? { serviceName: config, serviceVersion } : config;
|
|
236
243
|
const otlpEndpoint = cfg.otlpEndpoint || process.env.OTEL_EXPORTER_OTLP_ENDPOINT || "http://localhost:4318";
|
|
237
244
|
const environment = cfg.environment || process.env.NODE_ENV || "development";
|
|
245
|
+
const resource = new Resource({
|
|
246
|
+
[ATTR_SERVICE_NAME]: cfg.serviceName,
|
|
247
|
+
[ATTR_SERVICE_VERSION]: cfg.serviceVersion || "0.0.1",
|
|
248
|
+
"deployment.environment": environment
|
|
249
|
+
});
|
|
250
|
+
const logExporter = new OTLPLogExporter({
|
|
251
|
+
url: `${otlpEndpoint}/v1/logs`
|
|
252
|
+
});
|
|
253
|
+
loggerProvider = new LoggerProvider({ resource });
|
|
254
|
+
loggerProvider.addLogRecordProcessor(new BatchLogRecordProcessor(logExporter));
|
|
255
|
+
logs.setGlobalLoggerProvider(loggerProvider);
|
|
238
256
|
sdk = new NodeSDK({
|
|
239
|
-
resource
|
|
240
|
-
[ATTR_SERVICE_NAME]: cfg.serviceName,
|
|
241
|
-
[ATTR_SERVICE_VERSION]: cfg.serviceVersion || "0.0.1",
|
|
242
|
-
"deployment.environment": environment
|
|
243
|
-
}),
|
|
257
|
+
resource,
|
|
244
258
|
traceExporter: new OTLPTraceExporter({
|
|
245
259
|
url: `${otlpEndpoint}/v1/traces`
|
|
246
260
|
}),
|
|
@@ -260,7 +274,7 @@ function initTelemetry(config, serviceVersion = "0.0.1") {
|
|
|
260
274
|
});
|
|
261
275
|
sdk.start();
|
|
262
276
|
process.on("SIGTERM", () => {
|
|
263
|
-
sdk?.shutdown().then(() => console.log("Telemetry shut down")).catch((err) => console.error("Telemetry shutdown error", err));
|
|
277
|
+
Promise.all([sdk?.shutdown(), loggerProvider?.shutdown()]).then(() => console.log("Telemetry shut down")).catch((err) => console.error("Telemetry shutdown error", err));
|
|
264
278
|
});
|
|
265
279
|
return sdk;
|
|
266
280
|
}
|
|
@@ -272,6 +286,7 @@ function shutdownTelemetry() {
|
|
|
272
286
|
// src/lib/logger.ts
|
|
273
287
|
import pino from "pino";
|
|
274
288
|
import { trace } from "@opentelemetry/api";
|
|
289
|
+
import { logs as logs2, SeverityNumber as SeverityNumber2 } from "@opentelemetry/api-logs";
|
|
275
290
|
|
|
276
291
|
// src/lib/context.ts
|
|
277
292
|
import { AsyncLocalStorage } from "async_hooks";
|
|
@@ -318,6 +333,20 @@ function createContextFromMemo(memo) {
|
|
|
318
333
|
// src/lib/logger.ts
|
|
319
334
|
var NODE_ENV = process.env.NODE_ENV || "development";
|
|
320
335
|
var SERVICE_NAME = process.env.OTEL_SERVICE_NAME || "loop";
|
|
336
|
+
var pinoLevelToOtelSeverity = {
|
|
337
|
+
10: SeverityNumber2.TRACE,
|
|
338
|
+
// trace
|
|
339
|
+
20: SeverityNumber2.DEBUG,
|
|
340
|
+
// debug
|
|
341
|
+
30: SeverityNumber2.INFO,
|
|
342
|
+
// info
|
|
343
|
+
40: SeverityNumber2.WARN,
|
|
344
|
+
// warn
|
|
345
|
+
50: SeverityNumber2.ERROR,
|
|
346
|
+
// error
|
|
347
|
+
60: SeverityNumber2.FATAL
|
|
348
|
+
// fatal
|
|
349
|
+
};
|
|
321
350
|
var traceMixin = () => {
|
|
322
351
|
const mixinData = {};
|
|
323
352
|
const span = trace.getActiveSpan();
|
|
@@ -335,16 +364,112 @@ var traceMixin = () => {
|
|
|
335
364
|
}
|
|
336
365
|
return mixinData;
|
|
337
366
|
};
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
367
|
+
function createOtelDestination() {
|
|
368
|
+
const stdout = pino.destination(1);
|
|
369
|
+
return {
|
|
370
|
+
write(msg) {
|
|
371
|
+
stdout.write(msg);
|
|
372
|
+
try {
|
|
373
|
+
const logRecord = JSON.parse(msg);
|
|
374
|
+
const otelLogger = logs2.getLogger(SERVICE_NAME);
|
|
375
|
+
const span = trace.getActiveSpan();
|
|
376
|
+
const spanContext = span?.spanContext();
|
|
377
|
+
otelLogger.emit({
|
|
378
|
+
severityNumber: pinoLevelToOtelSeverity[logRecord.level] || SeverityNumber2.INFO,
|
|
379
|
+
severityText: pino.levels.labels[logRecord.level] || "INFO",
|
|
380
|
+
body: logRecord.msg,
|
|
381
|
+
attributes: {
|
|
382
|
+
...logRecord,
|
|
383
|
+
// Remove fields that are part of the log record structure
|
|
384
|
+
msg: void 0,
|
|
385
|
+
level: void 0,
|
|
386
|
+
time: void 0
|
|
387
|
+
},
|
|
388
|
+
timestamp: logRecord.time ? new Date(logRecord.time).getTime() * 1e6 : Date.now() * 1e6,
|
|
389
|
+
// nanoseconds
|
|
390
|
+
...spanContext && {
|
|
391
|
+
spanId: spanContext.spanId,
|
|
392
|
+
traceId: spanContext.traceId,
|
|
393
|
+
traceFlags: spanContext.traceFlags
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
} catch {
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
var logger = pino(
|
|
402
|
+
{
|
|
403
|
+
level: NODE_ENV === "production" ? "info" : "debug",
|
|
404
|
+
mixin: traceMixin,
|
|
405
|
+
base: {
|
|
406
|
+
service: SERVICE_NAME,
|
|
407
|
+
env: NODE_ENV
|
|
408
|
+
},
|
|
409
|
+
timestamp: pino.stdTimeFunctions.isoTime,
|
|
410
|
+
// In development, use pino-pretty for console but also send to OTLP
|
|
411
|
+
...NODE_ENV !== "production" && {
|
|
412
|
+
transport: {
|
|
413
|
+
targets: [
|
|
414
|
+
{
|
|
415
|
+
target: "pino-pretty",
|
|
416
|
+
options: { colorize: true },
|
|
417
|
+
level: "debug"
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
target: "pino/file",
|
|
421
|
+
options: { destination: 1 },
|
|
422
|
+
// stdout for OTLP bridge
|
|
423
|
+
level: "debug"
|
|
424
|
+
}
|
|
425
|
+
]
|
|
426
|
+
}
|
|
427
|
+
}
|
|
344
428
|
},
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
429
|
+
// In production, use the OTLP destination
|
|
430
|
+
NODE_ENV === "production" ? createOtelDestination() : void 0
|
|
431
|
+
);
|
|
432
|
+
if (NODE_ENV !== "production") {
|
|
433
|
+
const originalWrite = process.stdout.write.bind(process.stdout);
|
|
434
|
+
let buffer = "";
|
|
435
|
+
process.stdout.write = function(chunk, encodingOrCallback, callback) {
|
|
436
|
+
const str = typeof chunk === "string" ? chunk : chunk.toString();
|
|
437
|
+
buffer += str;
|
|
438
|
+
const lines = buffer.split("\n");
|
|
439
|
+
buffer = lines.pop() || "";
|
|
440
|
+
for (const line of lines) {
|
|
441
|
+
if (line.startsWith("{") && line.includes('"level"')) {
|
|
442
|
+
try {
|
|
443
|
+
const logRecord = JSON.parse(line);
|
|
444
|
+
const otelLogger = logs2.getLogger(SERVICE_NAME);
|
|
445
|
+
otelLogger.emit({
|
|
446
|
+
severityNumber: pinoLevelToOtelSeverity[logRecord.level] || SeverityNumber2.INFO,
|
|
447
|
+
severityText: pino.levels.labels[logRecord.level] || "INFO",
|
|
448
|
+
body: logRecord.msg,
|
|
449
|
+
attributes: {
|
|
450
|
+
service: logRecord.service,
|
|
451
|
+
env: logRecord.env,
|
|
452
|
+
trace_id: logRecord.trace_id,
|
|
453
|
+
span_id: logRecord.span_id,
|
|
454
|
+
correlation_id: logRecord.correlation_id,
|
|
455
|
+
merchant_id: logRecord.merchant_id,
|
|
456
|
+
order_id: logRecord.order_id,
|
|
457
|
+
workflow_id: logRecord.workflow_id
|
|
458
|
+
},
|
|
459
|
+
timestamp: logRecord.time ? new Date(logRecord.time).getTime() * 1e6 : Date.now() * 1e6
|
|
460
|
+
});
|
|
461
|
+
} catch {
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
if (typeof encodingOrCallback === "function") {
|
|
466
|
+
return originalWrite(chunk, encodingOrCallback);
|
|
467
|
+
} else if (encodingOrCallback) {
|
|
468
|
+
return originalWrite(chunk, encodingOrCallback, callback);
|
|
469
|
+
}
|
|
470
|
+
return originalWrite(chunk);
|
|
471
|
+
};
|
|
472
|
+
}
|
|
348
473
|
function createActivityLogger(activityName, correlationId) {
|
|
349
474
|
return logger.child({
|
|
350
475
|
activity: activityName,
|
|
@@ -453,7 +578,7 @@ function recordHttpRequest(method, path, statusCode, durationMs) {
|
|
|
453
578
|
function recordWorkflowStarted(workflowType, taskQueue) {
|
|
454
579
|
workflowStartedCounter.add(1, { workflow_type: workflowType, task_queue: taskQueue });
|
|
455
580
|
}
|
|
456
|
-
function recordWorkflowCompleted(workflowType, taskQueue,
|
|
581
|
+
function recordWorkflowCompleted(workflowType, taskQueue, _durationMs) {
|
|
457
582
|
workflowCompletedCounter.add(1, { workflow_type: workflowType, task_queue: taskQueue });
|
|
458
583
|
}
|
|
459
584
|
function recordWorkflowFailed(workflowType, taskQueue, errorType) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@payloops/observability",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -27,9 +27,12 @@
|
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@opentelemetry/api": "^1.9.0",
|
|
30
|
+
"@opentelemetry/api-logs": "^0.57.0",
|
|
30
31
|
"@opentelemetry/auto-instrumentations-node": "^0.53.0",
|
|
32
|
+
"@opentelemetry/exporter-logs-otlp-http": "^0.57.0",
|
|
31
33
|
"@opentelemetry/exporter-metrics-otlp-http": "^0.57.0",
|
|
32
34
|
"@opentelemetry/exporter-trace-otlp-http": "^0.57.0",
|
|
35
|
+
"@opentelemetry/sdk-logs": "^0.57.0",
|
|
33
36
|
"@opentelemetry/sdk-metrics": "^1.30.1",
|
|
34
37
|
"@opentelemetry/sdk-node": "^0.57.0",
|
|
35
38
|
"@opentelemetry/semantic-conventions": "^1.28.0",
|
|
@@ -37,10 +40,12 @@
|
|
|
37
40
|
"pino": "^9.6.0"
|
|
38
41
|
},
|
|
39
42
|
"devDependencies": {
|
|
43
|
+
"@eslint/js": "^9.39.2",
|
|
40
44
|
"@types/node": "^22.0.0",
|
|
41
45
|
"eslint": "^9.0.0",
|
|
42
46
|
"pino-pretty": "^13.0.0",
|
|
43
47
|
"tsup": "^8.3.5",
|
|
44
|
-
"typescript": "^5.9.3"
|
|
48
|
+
"typescript": "^5.9.3",
|
|
49
|
+
"typescript-eslint": "^8.53.0"
|
|
45
50
|
}
|
|
46
51
|
}
|