Skip to content

Commit 43d17ed

Browse files
committed
feat: try and fix logs to include trace information
1 parent fde9295 commit 43d17ed

File tree

6 files changed

+986
-53
lines changed

6 files changed

+986
-53
lines changed

package.json

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,20 +72,21 @@
7272
"dependencies": {
7373
"@godaddy/terminus": "^4.12.1",
7474
"@opentelemetry/api": "^1.9.0",
75-
"@opentelemetry/exporter-prometheus": "^0.53.0",
75+
"@opentelemetry/auto-instrumentations-node": "^0.51.0",
76+
"@opentelemetry/exporter-prometheus": "^0.54.0",
7677
"@opentelemetry/instrumentation-dns": "^0.39.0",
7778
"@opentelemetry/instrumentation-express": "^0.43.0",
7879
"@opentelemetry/instrumentation-generic-pool": "^0.39.0",
7980
"@opentelemetry/instrumentation-graphql": "^0.43.0",
80-
"@opentelemetry/instrumentation-http": "^0.53.0",
81+
"@opentelemetry/instrumentation-http": "^0.54.0",
8182
"@opentelemetry/instrumentation-ioredis": "^0.43.0",
8283
"@opentelemetry/instrumentation-net": "^0.39.0",
8384
"@opentelemetry/instrumentation-pg": "^0.46.0",
8485
"@opentelemetry/instrumentation-pino": "^0.42.0",
8586
"@opentelemetry/instrumentation-undici": "^0.6.0",
8687
"@opentelemetry/resource-detector-container": "^0.4.4",
8788
"@opentelemetry/resource-detector-gcp": "^0.29.12",
88-
"@opentelemetry/sdk-node": "^0.53.0",
89+
"@opentelemetry/sdk-node": "^0.54.0",
8990
"@opentelemetry/semantic-conventions": "^1.27.0",
9091
"@sesamecare-oss/confit": "^2.2.1",
9192
"@sesamecare-oss/opentelemetry-node-metrics": "^1.1.0",
@@ -96,6 +97,7 @@
9697
"express": "^5.0.1",
9798
"express-openapi-validator": "^5.3.7",
9899
"glob": "^11.0.0",
100+
"import-in-the-middle": "^1.11.2",
99101
"minimist": "^1.2.8",
100102
"moderndash": "^3.12.0",
101103
"pino": "^9.5.0",
@@ -113,12 +115,12 @@
113115
"@types/cookie-parser": "^1.4.7",
114116
"@types/express": "^5.0.0",
115117
"@types/minimist": "^1.2.5",
116-
"@types/node": "^22.7.7",
118+
"@types/node": "^22.7.9",
117119
"@types/request-ip": "^0.0.41",
118120
"@types/supertest": "^6.0.2",
119121
"@typescript-eslint/eslint-plugin": "^7.18.0",
120122
"@typescript-eslint/parser": "^7.18.0",
121-
"coconfig": "^1.6.0",
123+
"coconfig": "^1.6.1",
122124
"eslint": "^8.57.1",
123125
"eslint-config-prettier": "^9.1.0",
124126
"eslint-import-resolver-typescript": "^3.6.3",

src/bin/start-service.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ const argv = minimist(process.argv.slice(2), {
1515
boolean: ['built', 'repl', 'telemetry', 'nobind'],
1616
});
1717

18+
if (argv.telemetry) {
19+
await import('../telemetry/hook-modules.js');
20+
}
21+
1822
const noTelemetry = (argv.repl || isDev()) && !argv.telemetry;
1923
bootstrap({
2024
...argv,

src/express-app/app.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import path from 'path';
55

66
import { pino } from 'pino';
77
import cookieParser from 'cookie-parser';
8-
import { metrics } from '@opentelemetry/api';
8+
import { context, metrics, trace } from '@opentelemetry/api';
99
import { setupNodeMetrics } from '@sesamecare-oss/opentelemetry-node-metrics';
1010
import { createTerminus } from '@godaddy/terminus';
1111
import type { RequestHandler, Response } from 'express';
@@ -52,6 +52,22 @@ export async function startApp<
5252
dest: process.env.LOG_TO_FILE || process.stdout.fd,
5353
minLength: process.env.LOG_BUFFER ? Number(process.env.LOG_BUFFER) : undefined,
5454
});
55+
56+
function poorMansOtlp(mergeObject: object) {
57+
if (!('trace_id' in mergeObject)) {
58+
const activeSpan = trace.getSpan(context.active());
59+
if (activeSpan) {
60+
const ctx = activeSpan.spanContext();
61+
Object.assign(mergeObject, {
62+
trace_id: ctx.traceId,
63+
span_id: ctx.spanId,
64+
trace_flags: ctx.traceFlags
65+
});
66+
}
67+
}
68+
return mergeObject;
69+
}
70+
5571
const logger = shouldPrettyPrint
5672
? pino(
5773
{
@@ -61,6 +77,7 @@ export async function startApp<
6177
colorize: true,
6278
},
6379
},
80+
mixin: poorMansOtlp,
6481
},
6582
destination,
6683
)
@@ -71,6 +88,7 @@ export async function startApp<
7188
return { level: label };
7289
},
7390
},
91+
mixin: poorMansOtlp,
7492
},
7593
destination,
7694
);

src/telemetry/hook-modules.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import module from 'node:module';
2+
3+
module.register('import-in-the-middle/hook.mjs', import.meta.url, {
4+
parentURL: import.meta.url,
5+
data: {
6+
include: [
7+
'express',
8+
'pino',
9+
'http',
10+
'dns',
11+
'net',
12+
'pg',
13+
'ioredis',
14+
'undici',
15+
'generic-pool',
16+
],
17+
},
18+
});

src/telemetry/index.ts

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
1+
import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-proto';
12
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto';
23
import {
4+
Detector,
5+
DetectorSync,
36
envDetectorSync,
47
hostDetectorSync,
8+
IResource,
59
osDetectorSync,
610
processDetectorSync,
11+
ResourceDetectionConfig,
712
} from '@opentelemetry/resources';
813
import { containerDetector } from '@opentelemetry/resource-detector-container';
914
import { gcpDetector } from '@opentelemetry/resource-detector-gcp';
@@ -23,7 +28,7 @@ import type { ConfigurationSchema } from '../config/schema.js';
2328
import { getAutoInstrumentations } from './instrumentations.js';
2429
import { DummySpanExporter } from './DummyExporter.js';
2530

26-
function getExporter() {
31+
function getSpanExporter() {
2732
if (
2833
!process.env.DISABLE_OLTP_EXPORTER &&
2934
(['production', 'staging'].includes(process.env.APP_ENV || process.env.NODE_ENV || '') ||
@@ -39,9 +44,36 @@ function getExporter() {
3944
return new DummySpanExporter();
4045
}
4146

47+
function getLogExporter() {
48+
if (
49+
!process.env.DISABLE_OLTP_EXPORTER &&
50+
(['production', 'staging'].includes(process.env.APP_ENV || process.env.NODE_ENV || '') ||
51+
process.env.OTLP_EXPORTER)
52+
) {
53+
return new OTLPLogExporter({
54+
url: process.env.OTLP_EXPORTER || 'http://otlp-exporter:4318/v1/logs',
55+
});
56+
}
57+
if (process.env.ENABLE_CONSOLE_OLTP_EXPORTER) {
58+
return new opentelemetry.logs.ConsoleLogRecordExporter();
59+
}
60+
return undefined;
61+
}
62+
4263
let prometheusExporter: PrometheusExporter | undefined;
4364
let telemetrySdk: opentelemetry.NodeSDK | undefined;
4465

66+
function awaitAttributes(detector: DetectorSync): Detector {
67+
return {
68+
async detect(config?: ResourceDetectionConfig): Promise<IResource> {
69+
const resource = detector.detect(config)
70+
await resource.waitForAsyncAttributes?.()
71+
72+
return resource
73+
},
74+
}
75+
}
76+
4577
/**
4678
* OpenTelemetry is not friendly to the idea of stopping
4779
* and starting itself, it seems. So we can only keep a global
@@ -52,31 +84,31 @@ let telemetrySdk: opentelemetry.NodeSDK | undefined;
5284
*/
5385
export async function startGlobalTelemetry(serviceName: string) {
5486
if (!prometheusExporter) {
55-
// For troubleshooting, set the log level to DiagLogLevel.DEBUG
56-
opentelemetry.api.diag.setLogger(new (opentelemetry.api.DiagConsoleLogger)(), opentelemetry.api.DiagLogLevel.INFO);
87+
const { metrics, logs, NodeSDK } = opentelemetry;
5788

5889
prometheusExporter = new PrometheusExporter({ preventServerStart: true });
5990
const instrumentations = getAutoInstrumentations();
60-
telemetrySdk = new opentelemetry.NodeSDK({
91+
const logExporter = getLogExporter();
92+
telemetrySdk = new NodeSDK({
6193
serviceName,
6294
autoDetectResources: false,
63-
traceExporter: getExporter(),
6495
resourceDetectors: [
65-
envDetectorSync,
66-
hostDetectorSync,
67-
osDetectorSync,
68-
processDetectorSync,
69-
containerDetector,
70-
gcpDetector,
96+
awaitAttributes(envDetectorSync),
97+
awaitAttributes(hostDetectorSync),
98+
awaitAttributes(osDetectorSync),
99+
awaitAttributes(processDetectorSync),
100+
awaitAttributes(containerDetector),
101+
awaitAttributes(gcpDetector),
71102
],
103+
traceExporter: getSpanExporter(),
72104
metricReader: prometheusExporter,
73105
instrumentations,
74-
logRecordProcessors: [],
106+
logRecordProcessors: logExporter ? [new logs.BatchLogRecordProcessor(logExporter)] : [],
75107
views: [
76-
new opentelemetry.metrics.View({
108+
new metrics.View({
77109
instrumentName: 'http_request_duration_seconds',
78-
instrumentType: opentelemetry.metrics.InstrumentType.HISTOGRAM,
79-
aggregation: new opentelemetry.metrics.ExplicitBucketHistogramAggregation(
110+
instrumentType: metrics.InstrumentType.HISTOGRAM,
111+
aggregation: new metrics.ExplicitBucketHistogramAggregation(
80112
[0.003, 0.03, 0.1, 0.3, 1.5, 10],
81113
true,
82114
),

0 commit comments

Comments
 (0)