@forklaunch/core 0.5.3 → 0.5.5
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/lib/cache/index.d.mts +5 -1
- package/lib/cache/index.d.ts +5 -1
- package/lib/cache/index.js +4 -3
- package/lib/cache/index.js.map +1 -1
- package/lib/cache/index.mjs +4 -3
- package/lib/cache/index.mjs.map +1 -1
- package/lib/http/index.d.mts +43 -35
- package/lib/http/index.d.ts +43 -35
- package/lib/http/index.js +308 -234
- package/lib/http/index.js.map +1 -1
- package/lib/http/index.mjs +310 -238
- package/lib/http/index.mjs.map +1 -1
- package/lib/openTelemetryCollector-CWrfzmmW.d.mts +44 -0
- package/lib/openTelemetryCollector-CWrfzmmW.d.ts +44 -0
- package/lib/services/index.d.mts +9 -3
- package/lib/services/index.d.ts +9 -3
- package/lib/services/index.js +32 -16
- package/lib/services/index.js.map +1 -1
- package/lib/services/index.mjs +32 -16
- package/lib/services/index.mjs.map +1 -1
- package/package.json +2 -1
package/lib/http/index.mjs
CHANGED
@@ -136,7 +136,7 @@ async function checkAuthorizationToken(authorizationMethod, authorizationToken,
|
|
136
136
|
}
|
137
137
|
resourceId = decodedJwt.payload.sub;
|
138
138
|
} catch (error) {
|
139
|
-
|
139
|
+
req.openTelemetryCollector.error(error);
|
140
140
|
return invalidAuthorizationToken;
|
141
141
|
}
|
142
142
|
break;
|
@@ -217,14 +217,271 @@ async function parseRequestAuth(req, res, next) {
|
|
217
217
|
next?.();
|
218
218
|
}
|
219
219
|
|
220
|
+
// src/services/getEnvVar.ts
|
221
|
+
function getEnvVar(name) {
|
222
|
+
const value = process.env[name];
|
223
|
+
return value;
|
224
|
+
}
|
225
|
+
|
226
|
+
// src/http/telemetry/openTelemetryCollector.ts
|
227
|
+
import { HyperExpressInstrumentation } from "@forklaunch/opentelemetry-instrumentation-hyper-express";
|
228
|
+
import {
|
229
|
+
metrics
|
230
|
+
} from "@opentelemetry/api";
|
231
|
+
import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
|
232
|
+
import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
|
233
|
+
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
|
234
|
+
import { ExpressInstrumentation } from "@opentelemetry/instrumentation-express";
|
235
|
+
import { HttpInstrumentation } from "@opentelemetry/instrumentation-http";
|
236
|
+
import { Resource } from "@opentelemetry/resources";
|
237
|
+
import { BatchLogRecordProcessor } from "@opentelemetry/sdk-logs";
|
238
|
+
import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
|
239
|
+
import { NodeSDK } from "@opentelemetry/sdk-node";
|
240
|
+
import {
|
241
|
+
ATTR_SERVICE_NAME as ATTR_SERVICE_NAME2
|
242
|
+
} from "@opentelemetry/semantic-conventions";
|
243
|
+
import dotenv from "dotenv";
|
244
|
+
|
245
|
+
// src/http/guards/isForklaunchRequest.ts
|
246
|
+
function isForklaunchRequest(request) {
|
247
|
+
return request != null && typeof request === "object" && "contractDetails" in request;
|
248
|
+
}
|
249
|
+
|
250
|
+
// src/http/telemetry/pinoLogger.ts
|
251
|
+
import { isNever } from "@forklaunch/common";
|
252
|
+
import { trace as trace2 } from "@opentelemetry/api";
|
253
|
+
import { logs } from "@opentelemetry/api-logs";
|
254
|
+
import pino from "pino";
|
255
|
+
|
256
|
+
// src/http/guards/isLoggerMeta.ts
|
257
|
+
function isLoggerMeta(arg) {
|
258
|
+
return typeof arg === "object" && arg !== null && "_meta" in arg;
|
259
|
+
}
|
260
|
+
|
261
|
+
// src/http/telemetry/pinoLogger.ts
|
262
|
+
import PinoPretty from "pino-pretty";
|
263
|
+
function meta(meta2) {
|
264
|
+
return meta2;
|
265
|
+
}
|
266
|
+
function mapSeverity(level) {
|
267
|
+
switch (level) {
|
268
|
+
case "silent":
|
269
|
+
return 0;
|
270
|
+
case "trace":
|
271
|
+
return 1;
|
272
|
+
case "debug":
|
273
|
+
return 5;
|
274
|
+
case "info":
|
275
|
+
return 9;
|
276
|
+
case "warn":
|
277
|
+
return 13;
|
278
|
+
case "error":
|
279
|
+
return 17;
|
280
|
+
case "fatal":
|
281
|
+
return 21;
|
282
|
+
default:
|
283
|
+
isNever(level);
|
284
|
+
return 0;
|
285
|
+
}
|
286
|
+
}
|
287
|
+
var PinoLogger = class _PinoLogger {
|
288
|
+
pinoLogger;
|
289
|
+
meta;
|
290
|
+
prettyPrinter = PinoPretty.prettyFactory({
|
291
|
+
colorize: true
|
292
|
+
});
|
293
|
+
constructor(level, meta2 = {}) {
|
294
|
+
this.pinoLogger = pino({
|
295
|
+
level: level || "info",
|
296
|
+
formatters: {
|
297
|
+
level(label) {
|
298
|
+
return { level: label };
|
299
|
+
}
|
300
|
+
},
|
301
|
+
timestamp: pino.stdTimeFunctions.isoTime,
|
302
|
+
transport: {
|
303
|
+
target: "pino-pretty",
|
304
|
+
options: { colorize: true }
|
305
|
+
}
|
306
|
+
});
|
307
|
+
this.meta = meta2;
|
308
|
+
}
|
309
|
+
log(level, ...args) {
|
310
|
+
let meta2 = {};
|
311
|
+
const filteredArgs = args.filter((arg) => {
|
312
|
+
if (isLoggerMeta(arg)) {
|
313
|
+
Object.assign(meta2, arg);
|
314
|
+
return false;
|
315
|
+
}
|
316
|
+
return true;
|
317
|
+
});
|
318
|
+
const activeSpan = trace2.getActiveSpan();
|
319
|
+
if (activeSpan) {
|
320
|
+
const activeSpanContext = activeSpan.spanContext();
|
321
|
+
meta2.trace_id = activeSpanContext.traceId;
|
322
|
+
meta2.span_id = activeSpanContext.spanId;
|
323
|
+
meta2.trace_flags = activeSpanContext.traceFlags;
|
324
|
+
meta2 = {
|
325
|
+
// @ts-expect-error accessing private property
|
326
|
+
...activeSpan.attributes,
|
327
|
+
...meta2
|
328
|
+
};
|
329
|
+
}
|
330
|
+
meta2 = {
|
331
|
+
"api.name": "none",
|
332
|
+
"correlation.id": "none",
|
333
|
+
...meta2
|
334
|
+
};
|
335
|
+
this.pinoLogger[level](...filteredArgs);
|
336
|
+
logs.getLogger(process.env.OTEL_SERVICE_NAME ?? "unknown").emit({
|
337
|
+
severityText: level,
|
338
|
+
severityNumber: mapSeverity(level),
|
339
|
+
body: this.prettyPrinter(filteredArgs),
|
340
|
+
attributes: { ...this.meta, ...meta2 }
|
341
|
+
});
|
342
|
+
}
|
343
|
+
error = (msg, ...args) => this.log("error", msg, ...args);
|
344
|
+
info = (msg, ...args) => this.log("info", msg, ...args);
|
345
|
+
debug = (msg, ...args) => this.log("debug", msg, ...args);
|
346
|
+
warn = (msg, ...args) => this.log("warn", msg, ...args);
|
347
|
+
trace = (msg, ...args) => this.log("trace", msg, ...args);
|
348
|
+
child(meta2 = {}) {
|
349
|
+
return new _PinoLogger(this.pinoLogger.level, { ...this.meta, ...meta2 });
|
350
|
+
}
|
351
|
+
getBaseLogger() {
|
352
|
+
return this.pinoLogger;
|
353
|
+
}
|
354
|
+
};
|
355
|
+
function logger(level, meta2 = {}) {
|
356
|
+
return new PinoLogger(level, meta2);
|
357
|
+
}
|
358
|
+
|
359
|
+
// src/http/telemetry/openTelemetryCollector.ts
|
360
|
+
var OpenTelemetryCollector = class {
|
361
|
+
// scoped creation and create this in middleware when api execute. Also add correlation id
|
362
|
+
constructor(serviceName, level, metricDefinitions) {
|
363
|
+
this.serviceName = serviceName;
|
364
|
+
this.logger = logger(level || "info");
|
365
|
+
this.metrics = {};
|
366
|
+
for (const [metricId, metricType] of Object.entries(
|
367
|
+
metricDefinitions ?? {}
|
368
|
+
)) {
|
369
|
+
switch (metricType) {
|
370
|
+
case "counter":
|
371
|
+
this.metrics[metricId] = metrics.getMeter(this.serviceName).createCounter(metricId);
|
372
|
+
break;
|
373
|
+
case "gauge":
|
374
|
+
this.metrics[metricId] = metrics.getMeter(this.serviceName).createGauge(metricId);
|
375
|
+
break;
|
376
|
+
case "histogram":
|
377
|
+
this.metrics[metricId] = metrics.getMeter(this.serviceName).createHistogram(metricId);
|
378
|
+
break;
|
379
|
+
case "upDownCounter":
|
380
|
+
this.metrics[metricId] = metrics.getMeter(this.serviceName).createUpDownCounter(metricId);
|
381
|
+
break;
|
382
|
+
case "observableCounter":
|
383
|
+
this.metrics[metricId] = metrics.getMeter(this.serviceName).createObservableCounter(metricId);
|
384
|
+
break;
|
385
|
+
case "observableGauge":
|
386
|
+
this.metrics[metricId] = metrics.getMeter(this.serviceName).createObservableGauge(metricId);
|
387
|
+
break;
|
388
|
+
case "observableUpDownCounter":
|
389
|
+
this.metrics[metricId] = metrics.getMeter(this.serviceName).createObservableUpDownCounter(metricId);
|
390
|
+
break;
|
391
|
+
}
|
392
|
+
}
|
393
|
+
this.log("info", "OpenTelemetry (Traces + Logs + Metrics) started");
|
394
|
+
}
|
395
|
+
logger;
|
396
|
+
metrics;
|
397
|
+
log(level, ...args) {
|
398
|
+
this.logger.log(level, ...args);
|
399
|
+
}
|
400
|
+
info = (msg, ...args) => {
|
401
|
+
this.logger.log("info", msg, ...args);
|
402
|
+
};
|
403
|
+
error = (msg, ...args) => {
|
404
|
+
this.logger.log("error", msg, ...args);
|
405
|
+
};
|
406
|
+
warn = (msg, ...args) => {
|
407
|
+
this.logger.log("warn", msg, ...args);
|
408
|
+
};
|
409
|
+
debug = (msg, ...args) => {
|
410
|
+
this.logger.log("debug", msg, ...args);
|
411
|
+
};
|
412
|
+
trace = (msg, ...args) => {
|
413
|
+
this.logger.log("trace", msg, ...args);
|
414
|
+
};
|
415
|
+
getMetric(metricId) {
|
416
|
+
return this.metrics[metricId];
|
417
|
+
}
|
418
|
+
};
|
419
|
+
dotenv.config({ path: getEnvVar("ENV_FILE_PATH") });
|
420
|
+
new NodeSDK({
|
421
|
+
resource: new Resource({
|
422
|
+
[ATTR_SERVICE_NAME2]: getEnvVar("OTEL_SERVICE_NAME")
|
423
|
+
}),
|
424
|
+
traceExporter: new OTLPTraceExporter({
|
425
|
+
url: `${getEnvVar("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/traces`
|
426
|
+
}),
|
427
|
+
metricReader: new PeriodicExportingMetricReader({
|
428
|
+
exporter: new OTLPMetricExporter({
|
429
|
+
url: `${getEnvVar("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/metrics`
|
430
|
+
}),
|
431
|
+
exportIntervalMillis: 5e3
|
432
|
+
}),
|
433
|
+
logRecordProcessors: [
|
434
|
+
new BatchLogRecordProcessor(
|
435
|
+
new OTLPLogExporter({
|
436
|
+
url: `${getEnvVar("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/logs`
|
437
|
+
})
|
438
|
+
)
|
439
|
+
],
|
440
|
+
instrumentations: [
|
441
|
+
new HttpInstrumentation({
|
442
|
+
applyCustomAttributesOnSpan: (span, request) => {
|
443
|
+
span.setAttribute(
|
444
|
+
"service.name",
|
445
|
+
getEnvVar("OTEL_SERVICE_NAME") ?? "unknown"
|
446
|
+
);
|
447
|
+
if (isForklaunchRequest(request)) {
|
448
|
+
span.setAttribute("api.name", request.contractDetails?.name);
|
449
|
+
}
|
450
|
+
}
|
451
|
+
}),
|
452
|
+
new ExpressInstrumentation(),
|
453
|
+
new HyperExpressInstrumentation()
|
454
|
+
]
|
455
|
+
}).start();
|
456
|
+
var httpRequestsTotalCounter = metrics.getMeter(getEnvVar("OTEL_SERVICE_NAME") || "unknown").createCounter("http_requests_total", {
|
457
|
+
description: "Number of HTTP requests"
|
458
|
+
});
|
459
|
+
var httpServerDurationHistogram = metrics.getMeter(getEnvVar("OTEL_SERVICE_NAME") || "unknown").createHistogram("http_server_duration", {
|
460
|
+
description: "Duration of HTTP server requests",
|
461
|
+
unit: "s"
|
462
|
+
});
|
463
|
+
|
220
464
|
// src/http/middleware/request/enrichDetails.middleware.ts
|
221
|
-
function enrichDetails(path, contractDetails, requestSchema, responseSchemas) {
|
465
|
+
function enrichDetails(path, contractDetails, requestSchema, responseSchemas, openTelemetryCollector) {
|
222
466
|
return (req, res, next) => {
|
223
467
|
req.originalPath = path;
|
224
468
|
req.contractDetails = contractDetails;
|
225
469
|
req.requestSchema = requestSchema;
|
226
470
|
res.responseSchemas = responseSchemas;
|
471
|
+
req.openTelemetryCollector = openTelemetryCollector;
|
227
472
|
req.context.span?.setAttribute(ATTR_API_NAME, req.contractDetails?.name);
|
473
|
+
const startTime = process.hrtime();
|
474
|
+
res.on("finish", () => {
|
475
|
+
const [seconds, nanoseconds] = process.hrtime(startTime);
|
476
|
+
const durationMs = seconds + nanoseconds / 1e9;
|
477
|
+
httpServerDurationHistogram.record(durationMs, {
|
478
|
+
[ATTR_SERVICE_NAME]: getEnvVar("OTEL_SERVICE_NAME") || "unknown",
|
479
|
+
[ATTR_API_NAME]: req.contractDetails?.name || "unknown",
|
480
|
+
[ATTR_HTTP_REQUEST_METHOD]: req.method,
|
481
|
+
[ATTR_HTTP_ROUTE]: req.originalPath || "unknown",
|
482
|
+
[ATTR_HTTP_RESPONSE_STATUS_CODE]: res.statusCode
|
483
|
+
});
|
484
|
+
});
|
228
485
|
next?.();
|
229
486
|
};
|
230
487
|
}
|
@@ -234,13 +491,18 @@ import {
|
|
234
491
|
prettyPrintParseErrors
|
235
492
|
} from "@forklaunch/validator";
|
236
493
|
|
494
|
+
// src/http/guards/hasSend.ts
|
495
|
+
function hasSend(res) {
|
496
|
+
return typeof res === "object" && res !== null && "send" in res;
|
497
|
+
}
|
498
|
+
|
237
499
|
// src/http/guards/isResponseShape.ts
|
238
500
|
function isResponseShape(maybeResponseShape) {
|
239
501
|
return maybeResponseShape != null && "body" in maybeResponseShape && "query" in maybeResponseShape && "params" in maybeResponseShape && "headers" in maybeResponseShape;
|
240
502
|
}
|
241
503
|
|
242
504
|
// src/http/middleware/request/parse.middleware.ts
|
243
|
-
function parse(req,
|
505
|
+
function parse(req, res, next) {
|
244
506
|
const request = {
|
245
507
|
params: req.params,
|
246
508
|
query: req.query,
|
@@ -261,12 +523,22 @@ function parse(req, _res, next) {
|
|
261
523
|
switch (req.contractDetails.options?.requestValidation) {
|
262
524
|
default:
|
263
525
|
case "error":
|
264
|
-
|
265
|
-
|
266
|
-
)
|
267
|
-
|
526
|
+
res.type("application/json");
|
527
|
+
res.status(400);
|
528
|
+
if (hasSend(res)) {
|
529
|
+
res.send(
|
530
|
+
`${prettyPrintParseErrors(parsedRequest.errors, "Request")}
|
531
|
+
|
532
|
+
Correlation id: ${req.context.correlationId ?? "No correlation ID"}`
|
533
|
+
);
|
534
|
+
} else {
|
535
|
+
next?.(new Error("Request is not sendable."));
|
536
|
+
}
|
537
|
+
return;
|
268
538
|
case "warning":
|
269
|
-
|
539
|
+
req.openTelemetryCollector.warn(
|
540
|
+
prettyPrintParseErrors(parsedRequest.errors, "Request")
|
541
|
+
);
|
270
542
|
break;
|
271
543
|
case "none":
|
272
544
|
break;
|
@@ -277,9 +549,10 @@ function parse(req, _res, next) {
|
|
277
549
|
|
278
550
|
// src/http/router/expressLikeRouter.ts
|
279
551
|
var ForklaunchExpressLikeRouter = class {
|
280
|
-
constructor(basePath, schemaValidator, internal) {
|
552
|
+
constructor(basePath, schemaValidator, internal, openTelemetryCollector) {
|
281
553
|
this.schemaValidator = schemaValidator;
|
282
554
|
this.internal = internal;
|
555
|
+
this.openTelemetryCollector = openTelemetryCollector;
|
283
556
|
this.basePath = basePath;
|
284
557
|
}
|
285
558
|
requestHandler;
|
@@ -298,7 +571,8 @@ var ForklaunchExpressLikeRouter = class {
|
|
298
571
|
`${this.basePath}${path}`,
|
299
572
|
contractDetails,
|
300
573
|
requestSchema,
|
301
|
-
responseSchemas
|
574
|
+
responseSchemas,
|
575
|
+
this.openTelemetryCollector
|
302
576
|
),
|
303
577
|
parse,
|
304
578
|
parseRequestAuth
|
@@ -859,20 +1133,16 @@ var ForklaunchExpressLikeApplication = class extends ForklaunchExpressLikeRouter
|
|
859
1133
|
*
|
860
1134
|
* @param {SV} schemaValidator - The schema validator.
|
861
1135
|
*/
|
862
|
-
constructor(schemaValidator, internal) {
|
863
|
-
super("/", schemaValidator, internal);
|
1136
|
+
constructor(schemaValidator, internal, openTelemetryCollector) {
|
1137
|
+
super("/", schemaValidator, internal, openTelemetryCollector);
|
864
1138
|
this.schemaValidator = schemaValidator;
|
865
1139
|
this.internal = internal;
|
1140
|
+
this.openTelemetryCollector = openTelemetryCollector;
|
866
1141
|
this.internal.use(createContext(this.schemaValidator));
|
867
1142
|
this.internal.use(cors);
|
868
1143
|
}
|
869
1144
|
};
|
870
1145
|
|
871
|
-
// src/http/guards/isForklaunchRequest.ts
|
872
|
-
function isForklaunchRequest(request) {
|
873
|
-
return request != null && typeof request === "object" && "contractDetails" in request;
|
874
|
-
}
|
875
|
-
|
876
1146
|
// src/http/guards/isPath.ts
|
877
1147
|
function isPath(path) {
|
878
1148
|
return path.startsWith("/");
|
@@ -946,7 +1216,7 @@ var put = (_schemaValidator, path, contractDetails, ...handlers) => {
|
|
946
1216
|
};
|
947
1217
|
|
948
1218
|
// src/http/handlers/trace.ts
|
949
|
-
var
|
1219
|
+
var trace3 = (_schemaValidator, path, contractDetails, ...handlers) => {
|
950
1220
|
return typedHandler(_schemaValidator, path, "trace", contractDetails, ...handlers);
|
951
1221
|
};
|
952
1222
|
|
@@ -1967,12 +2237,24 @@ function parse2(req, res, next) {
|
|
1967
2237
|
switch (req.contractDetails.options?.responseValidation) {
|
1968
2238
|
default:
|
1969
2239
|
case "error":
|
1970
|
-
|
1971
|
-
|
1972
|
-
|
2240
|
+
res.type("text/plain");
|
2241
|
+
res.status(400);
|
2242
|
+
if (hasSend(res)) {
|
2243
|
+
res.send(
|
2244
|
+
`Invalid response:
|
2245
|
+
${parseErrors.join("\n\n")}
|
2246
|
+
|
2247
|
+
Correlation id: ${req.context.correlationId ?? "No correlation ID"}`
|
2248
|
+
);
|
2249
|
+
} else {
|
2250
|
+
next?.(new Error("Response is not sendable."));
|
2251
|
+
}
|
2252
|
+
return;
|
1973
2253
|
case "warning":
|
1974
|
-
|
1975
|
-
|
2254
|
+
req.openTelemetryCollector.warn(
|
2255
|
+
`Invalid response:
|
2256
|
+
${parseErrors.join("\n\n")}`
|
2257
|
+
);
|
1976
2258
|
break;
|
1977
2259
|
case "none":
|
1978
2260
|
break;
|
@@ -1981,96 +2263,6 @@ ${parseErrors.join("\n\n")}`);
|
|
1981
2263
|
next?.();
|
1982
2264
|
}
|
1983
2265
|
|
1984
|
-
// src/http/telemetry/pinoLogger.ts
|
1985
|
-
import { isNever } from "@forklaunch/common";
|
1986
|
-
import { trace as trace3 } from "@opentelemetry/api";
|
1987
|
-
import { logs } from "@opentelemetry/api-logs";
|
1988
|
-
import pino from "pino";
|
1989
|
-
function mapSeverity(level) {
|
1990
|
-
switch (level) {
|
1991
|
-
case "silent":
|
1992
|
-
return 0;
|
1993
|
-
case "trace":
|
1994
|
-
return 1;
|
1995
|
-
case "debug":
|
1996
|
-
return 5;
|
1997
|
-
case "info":
|
1998
|
-
return 9;
|
1999
|
-
case "warn":
|
2000
|
-
return 13;
|
2001
|
-
case "error":
|
2002
|
-
return 17;
|
2003
|
-
case "fatal":
|
2004
|
-
return 21;
|
2005
|
-
default:
|
2006
|
-
isNever(level);
|
2007
|
-
return 0;
|
2008
|
-
}
|
2009
|
-
}
|
2010
|
-
var PinoLogger = class _PinoLogger {
|
2011
|
-
pinoLogger;
|
2012
|
-
meta;
|
2013
|
-
constructor(level, meta = {}) {
|
2014
|
-
this.pinoLogger = pino({
|
2015
|
-
level: level || "info",
|
2016
|
-
formatters: {
|
2017
|
-
level(label) {
|
2018
|
-
return { level: label };
|
2019
|
-
}
|
2020
|
-
},
|
2021
|
-
timestamp: pino.stdTimeFunctions.isoTime,
|
2022
|
-
transport: {
|
2023
|
-
target: "pino-pretty",
|
2024
|
-
options: { colorize: true }
|
2025
|
-
}
|
2026
|
-
});
|
2027
|
-
this.meta = meta;
|
2028
|
-
}
|
2029
|
-
log(level, msg, meta = {}) {
|
2030
|
-
const activeSpan = trace3.getActiveSpan();
|
2031
|
-
if (activeSpan) {
|
2032
|
-
const activeSpanContext = activeSpan.spanContext();
|
2033
|
-
meta.trace_id = activeSpanContext.traceId;
|
2034
|
-
meta.span_id = activeSpanContext.spanId;
|
2035
|
-
meta.trace_flags = activeSpanContext.traceFlags;
|
2036
|
-
if (!meta.api_name) {
|
2037
|
-
meta = { ...meta, ...activeSpan?.attributes };
|
2038
|
-
}
|
2039
|
-
}
|
2040
|
-
this.pinoLogger[level](msg);
|
2041
|
-
logs.getLogger(process.env.OTEL_SERVICE_NAME ?? "unknown").emit({
|
2042
|
-
severityText: level,
|
2043
|
-
severityNumber: mapSeverity(level),
|
2044
|
-
body: msg,
|
2045
|
-
attributes: { ...this.meta, ...meta }
|
2046
|
-
});
|
2047
|
-
}
|
2048
|
-
error(msg, meta = {}) {
|
2049
|
-
this.log("error", msg, meta);
|
2050
|
-
}
|
2051
|
-
info(msg, meta = {}) {
|
2052
|
-
this.log("info", msg, meta);
|
2053
|
-
}
|
2054
|
-
debug(msg, meta = {}) {
|
2055
|
-
this.log("debug", msg, meta);
|
2056
|
-
}
|
2057
|
-
warn(msg, meta = {}) {
|
2058
|
-
this.log("warn", msg, meta);
|
2059
|
-
}
|
2060
|
-
trace(msg, meta = {}) {
|
2061
|
-
this.log("trace", msg, meta);
|
2062
|
-
}
|
2063
|
-
child(meta = {}) {
|
2064
|
-
return new _PinoLogger(this.pinoLogger.level, { ...this.meta, ...meta });
|
2065
|
-
}
|
2066
|
-
getBaseLogger() {
|
2067
|
-
return this.pinoLogger;
|
2068
|
-
}
|
2069
|
-
};
|
2070
|
-
function logger(level, meta = {}) {
|
2071
|
-
return new PinoLogger(level, meta);
|
2072
|
-
}
|
2073
|
-
|
2074
2266
|
// src/http/telemetry/recordMetric.ts
|
2075
2267
|
import {
|
2076
2268
|
ATTR_HTTP_REQUEST_METHOD as ATTR_HTTP_REQUEST_METHOD3,
|
@@ -2085,130 +2277,6 @@ import {
|
|
2085
2277
|
prettyPrintParseErrors as prettyPrintParseErrors3
|
2086
2278
|
} from "@forklaunch/validator";
|
2087
2279
|
|
2088
|
-
// src/services/getEnvVar.ts
|
2089
|
-
function getEnvVar(name) {
|
2090
|
-
const value = process.env[name];
|
2091
|
-
return value;
|
2092
|
-
}
|
2093
|
-
|
2094
|
-
// src/http/telemetry/openTelemetryCollector.ts
|
2095
|
-
import { HyperExpressInstrumentation } from "@forklaunch/opentelemetry-instrumentation-hyper-express";
|
2096
|
-
import {
|
2097
|
-
metrics
|
2098
|
-
} from "@opentelemetry/api";
|
2099
|
-
import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
|
2100
|
-
import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
|
2101
|
-
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
|
2102
|
-
import { ExpressInstrumentation } from "@opentelemetry/instrumentation-express";
|
2103
|
-
import { HttpInstrumentation } from "@opentelemetry/instrumentation-http";
|
2104
|
-
import { Resource } from "@opentelemetry/resources";
|
2105
|
-
import { BatchLogRecordProcessor } from "@opentelemetry/sdk-logs";
|
2106
|
-
import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
|
2107
|
-
import { NodeSDK } from "@opentelemetry/sdk-node";
|
2108
|
-
import {
|
2109
|
-
ATTR_SERVICE_NAME as ATTR_SERVICE_NAME2
|
2110
|
-
} from "@opentelemetry/semantic-conventions";
|
2111
|
-
import dotenv from "dotenv";
|
2112
|
-
var OpenTelemetryCollector = class {
|
2113
|
-
// scoped creation and create this in middleware when api execute. Also add correlation id
|
2114
|
-
constructor(serviceName, level, metricDefinitions) {
|
2115
|
-
this.serviceName = serviceName;
|
2116
|
-
this.logger = logger(level ?? "info");
|
2117
|
-
this.metrics = {};
|
2118
|
-
for (const [metricId, metricType] of Object.entries(
|
2119
|
-
metricDefinitions ?? {}
|
2120
|
-
)) {
|
2121
|
-
switch (metricType) {
|
2122
|
-
case "counter":
|
2123
|
-
this.metrics[metricId] = metrics.getMeter(this.serviceName).createCounter(metricId);
|
2124
|
-
break;
|
2125
|
-
case "gauge":
|
2126
|
-
this.metrics[metricId] = metrics.getMeter(this.serviceName).createGauge(metricId);
|
2127
|
-
break;
|
2128
|
-
case "histogram":
|
2129
|
-
this.metrics[metricId] = metrics.getMeter(this.serviceName).createHistogram(metricId);
|
2130
|
-
break;
|
2131
|
-
case "upDownCounter":
|
2132
|
-
this.metrics[metricId] = metrics.getMeter(this.serviceName).createUpDownCounter(metricId);
|
2133
|
-
break;
|
2134
|
-
case "observableCounter":
|
2135
|
-
this.metrics[metricId] = metrics.getMeter(this.serviceName).createObservableCounter(metricId);
|
2136
|
-
break;
|
2137
|
-
case "observableGauge":
|
2138
|
-
this.metrics[metricId] = metrics.getMeter(this.serviceName).createObservableGauge(metricId);
|
2139
|
-
break;
|
2140
|
-
case "observableUpDownCounter":
|
2141
|
-
this.metrics[metricId] = metrics.getMeter(this.serviceName).createObservableUpDownCounter(metricId);
|
2142
|
-
break;
|
2143
|
-
}
|
2144
|
-
}
|
2145
|
-
this.log("info", "OpenTelemetry (Traces + Logs + Metrics) started");
|
2146
|
-
}
|
2147
|
-
logger;
|
2148
|
-
metrics;
|
2149
|
-
log(level, msg, meta = {}) {
|
2150
|
-
this.logger.log(level, msg, meta);
|
2151
|
-
}
|
2152
|
-
info(msg, meta = {}) {
|
2153
|
-
this.logger.info(msg, meta);
|
2154
|
-
}
|
2155
|
-
error(msg, meta = {}) {
|
2156
|
-
this.logger.error(msg, meta);
|
2157
|
-
}
|
2158
|
-
warn(msg, meta = {}) {
|
2159
|
-
this.logger.warn(msg, meta);
|
2160
|
-
}
|
2161
|
-
debug(msg, meta = {}) {
|
2162
|
-
this.logger.debug(msg, meta);
|
2163
|
-
}
|
2164
|
-
trace(msg, meta = {}) {
|
2165
|
-
this.logger.trace(msg, meta);
|
2166
|
-
}
|
2167
|
-
getMetric(metricId) {
|
2168
|
-
return this.metrics[metricId];
|
2169
|
-
}
|
2170
|
-
};
|
2171
|
-
dotenv.config({ path: getEnvVar("ENV_FILE_PATH") });
|
2172
|
-
new NodeSDK({
|
2173
|
-
resource: new Resource({
|
2174
|
-
[ATTR_SERVICE_NAME2]: getEnvVar("OTEL_SERVICE_NAME")
|
2175
|
-
}),
|
2176
|
-
traceExporter: new OTLPTraceExporter({
|
2177
|
-
url: `${getEnvVar("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/traces`
|
2178
|
-
}),
|
2179
|
-
metricReader: new PeriodicExportingMetricReader({
|
2180
|
-
exporter: new OTLPMetricExporter({
|
2181
|
-
url: `${getEnvVar("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/metrics`
|
2182
|
-
}),
|
2183
|
-
exportIntervalMillis: 5e3
|
2184
|
-
}),
|
2185
|
-
logRecordProcessors: [
|
2186
|
-
new BatchLogRecordProcessor(
|
2187
|
-
new OTLPLogExporter({
|
2188
|
-
url: `${getEnvVar("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/logs`
|
2189
|
-
})
|
2190
|
-
)
|
2191
|
-
],
|
2192
|
-
instrumentations: [
|
2193
|
-
new HttpInstrumentation({
|
2194
|
-
applyCustomAttributesOnSpan: (span, request) => {
|
2195
|
-
span.setAttribute(
|
2196
|
-
"service.name",
|
2197
|
-
getEnvVar("OTEL_SERVICE_NAME") ?? "unknown"
|
2198
|
-
);
|
2199
|
-
if (isForklaunchRequest(request)) {
|
2200
|
-
span.setAttribute("api.name", request.contractDetails?.name);
|
2201
|
-
}
|
2202
|
-
}
|
2203
|
-
}),
|
2204
|
-
new ExpressInstrumentation(),
|
2205
|
-
new HyperExpressInstrumentation()
|
2206
|
-
]
|
2207
|
-
}).start();
|
2208
|
-
var httpRequestsTotalCounter = metrics.getMeter(getEnvVar("OTEL_SERVICE_NAME") || "unknown").createCounter("http_requests_total", {
|
2209
|
-
description: "Number of HTTP requests"
|
2210
|
-
});
|
2211
|
-
|
2212
2280
|
// src/http/telemetry/recordMetric.ts
|
2213
2281
|
function recordMetric(req, res) {
|
2214
2282
|
httpRequestsTotalCounter.add(1, {
|
@@ -2227,6 +2295,7 @@ function enrichExpressLikeSend(instance, req, res, originalSend, data, shouldEnr
|
|
2227
2295
|
if (shouldEnrich) {
|
2228
2296
|
recordMetric(req, res);
|
2229
2297
|
if (res.statusCode === 404) {
|
2298
|
+
res.type("text/plain");
|
2230
2299
|
res.status(404);
|
2231
2300
|
logger("error").error("Not Found");
|
2232
2301
|
originalSend.call(instance, "Not Found");
|
@@ -2240,6 +2309,7 @@ function enrichExpressLikeSend(instance, req, res, originalSend, data, shouldEnr
|
|
2240
2309
|
${res.locals.errorMessage}`;
|
2241
2310
|
}
|
2242
2311
|
logger("error").error(errorString);
|
2312
|
+
res.type("text/plain");
|
2243
2313
|
res.status(500);
|
2244
2314
|
originalSend.call(instance, errorString);
|
2245
2315
|
parseErrorSent = true;
|
@@ -2416,6 +2486,7 @@ export {
|
|
2416
2486
|
getCodeForStatus,
|
2417
2487
|
head,
|
2418
2488
|
httpRequestsTotalCounter,
|
2489
|
+
httpServerDurationHistogram,
|
2419
2490
|
isClientError,
|
2420
2491
|
isForklaunchRequest,
|
2421
2492
|
isForklaunchRouter,
|
@@ -2425,6 +2496,7 @@ export {
|
|
2425
2496
|
isSuccessful,
|
2426
2497
|
isValidStatusCode,
|
2427
2498
|
logger,
|
2499
|
+
meta,
|
2428
2500
|
metricsDefinitions,
|
2429
2501
|
middleware,
|
2430
2502
|
options,
|
@@ -2432,7 +2504,7 @@ export {
|
|
2432
2504
|
post,
|
2433
2505
|
put,
|
2434
2506
|
recordMetric,
|
2435
|
-
|
2507
|
+
trace3 as trace,
|
2436
2508
|
typedHandler
|
2437
2509
|
};
|
2438
2510
|
//# sourceMappingURL=index.mjs.map
|