@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.js
CHANGED
@@ -47,6 +47,7 @@ __export(http_exports, {
|
|
47
47
|
getCodeForStatus: () => getCodeForStatus,
|
48
48
|
head: () => head,
|
49
49
|
httpRequestsTotalCounter: () => httpRequestsTotalCounter,
|
50
|
+
httpServerDurationHistogram: () => httpServerDurationHistogram,
|
50
51
|
isClientError: () => isClientError,
|
51
52
|
isForklaunchRequest: () => isForklaunchRequest,
|
52
53
|
isForklaunchRouter: () => isForklaunchRouter,
|
@@ -56,6 +57,7 @@ __export(http_exports, {
|
|
56
57
|
isSuccessful: () => isSuccessful,
|
57
58
|
isValidStatusCode: () => isValidStatusCode,
|
58
59
|
logger: () => logger,
|
60
|
+
meta: () => meta,
|
59
61
|
metricsDefinitions: () => metricsDefinitions,
|
60
62
|
middleware: () => middleware,
|
61
63
|
options: () => options,
|
@@ -63,7 +65,7 @@ __export(http_exports, {
|
|
63
65
|
post: () => post,
|
64
66
|
put: () => put,
|
65
67
|
recordMetric: () => recordMetric,
|
66
|
-
trace: () =>
|
68
|
+
trace: () => trace3,
|
67
69
|
typedHandler: () => typedHandler
|
68
70
|
});
|
69
71
|
module.exports = __toCommonJS(http_exports);
|
@@ -201,7 +203,7 @@ async function checkAuthorizationToken(authorizationMethod, authorizationToken,
|
|
201
203
|
}
|
202
204
|
resourceId = decodedJwt.payload.sub;
|
203
205
|
} catch (error) {
|
204
|
-
|
206
|
+
req.openTelemetryCollector.error(error);
|
205
207
|
return invalidAuthorizationToken;
|
206
208
|
}
|
207
209
|
break;
|
@@ -282,14 +284,267 @@ async function parseRequestAuth(req, res, next) {
|
|
282
284
|
next?.();
|
283
285
|
}
|
284
286
|
|
287
|
+
// src/services/getEnvVar.ts
|
288
|
+
function getEnvVar(name) {
|
289
|
+
const value = process.env[name];
|
290
|
+
return value;
|
291
|
+
}
|
292
|
+
|
293
|
+
// src/http/telemetry/openTelemetryCollector.ts
|
294
|
+
var import_opentelemetry_instrumentation_hyper_express = require("@forklaunch/opentelemetry-instrumentation-hyper-express");
|
295
|
+
var import_api3 = require("@opentelemetry/api");
|
296
|
+
var import_exporter_logs_otlp_http = require("@opentelemetry/exporter-logs-otlp-http");
|
297
|
+
var import_exporter_metrics_otlp_http = require("@opentelemetry/exporter-metrics-otlp-http");
|
298
|
+
var import_exporter_trace_otlp_http = require("@opentelemetry/exporter-trace-otlp-http");
|
299
|
+
var import_instrumentation_express = require("@opentelemetry/instrumentation-express");
|
300
|
+
var import_instrumentation_http = require("@opentelemetry/instrumentation-http");
|
301
|
+
var import_resources = require("@opentelemetry/resources");
|
302
|
+
var import_sdk_logs = require("@opentelemetry/sdk-logs");
|
303
|
+
var import_sdk_metrics = require("@opentelemetry/sdk-metrics");
|
304
|
+
var import_sdk_node = require("@opentelemetry/sdk-node");
|
305
|
+
var import_semantic_conventions2 = require("@opentelemetry/semantic-conventions");
|
306
|
+
var import_dotenv = __toESM(require("dotenv"));
|
307
|
+
|
308
|
+
// src/http/guards/isForklaunchRequest.ts
|
309
|
+
function isForklaunchRequest(request) {
|
310
|
+
return request != null && typeof request === "object" && "contractDetails" in request;
|
311
|
+
}
|
312
|
+
|
313
|
+
// src/http/telemetry/pinoLogger.ts
|
314
|
+
var import_common2 = require("@forklaunch/common");
|
315
|
+
var import_api2 = require("@opentelemetry/api");
|
316
|
+
var import_api_logs = require("@opentelemetry/api-logs");
|
317
|
+
var import_pino = __toESM(require("pino"));
|
318
|
+
|
319
|
+
// src/http/guards/isLoggerMeta.ts
|
320
|
+
function isLoggerMeta(arg) {
|
321
|
+
return typeof arg === "object" && arg !== null && "_meta" in arg;
|
322
|
+
}
|
323
|
+
|
324
|
+
// src/http/telemetry/pinoLogger.ts
|
325
|
+
var import_pino_pretty = __toESM(require("pino-pretty"));
|
326
|
+
function meta(meta2) {
|
327
|
+
return meta2;
|
328
|
+
}
|
329
|
+
function mapSeverity(level) {
|
330
|
+
switch (level) {
|
331
|
+
case "silent":
|
332
|
+
return 0;
|
333
|
+
case "trace":
|
334
|
+
return 1;
|
335
|
+
case "debug":
|
336
|
+
return 5;
|
337
|
+
case "info":
|
338
|
+
return 9;
|
339
|
+
case "warn":
|
340
|
+
return 13;
|
341
|
+
case "error":
|
342
|
+
return 17;
|
343
|
+
case "fatal":
|
344
|
+
return 21;
|
345
|
+
default:
|
346
|
+
(0, import_common2.isNever)(level);
|
347
|
+
return 0;
|
348
|
+
}
|
349
|
+
}
|
350
|
+
var PinoLogger = class _PinoLogger {
|
351
|
+
pinoLogger;
|
352
|
+
meta;
|
353
|
+
prettyPrinter = import_pino_pretty.default.prettyFactory({
|
354
|
+
colorize: true
|
355
|
+
});
|
356
|
+
constructor(level, meta2 = {}) {
|
357
|
+
this.pinoLogger = (0, import_pino.default)({
|
358
|
+
level: level || "info",
|
359
|
+
formatters: {
|
360
|
+
level(label) {
|
361
|
+
return { level: label };
|
362
|
+
}
|
363
|
+
},
|
364
|
+
timestamp: import_pino.default.stdTimeFunctions.isoTime,
|
365
|
+
transport: {
|
366
|
+
target: "pino-pretty",
|
367
|
+
options: { colorize: true }
|
368
|
+
}
|
369
|
+
});
|
370
|
+
this.meta = meta2;
|
371
|
+
}
|
372
|
+
log(level, ...args) {
|
373
|
+
let meta2 = {};
|
374
|
+
const filteredArgs = args.filter((arg) => {
|
375
|
+
if (isLoggerMeta(arg)) {
|
376
|
+
Object.assign(meta2, arg);
|
377
|
+
return false;
|
378
|
+
}
|
379
|
+
return true;
|
380
|
+
});
|
381
|
+
const activeSpan = import_api2.trace.getActiveSpan();
|
382
|
+
if (activeSpan) {
|
383
|
+
const activeSpanContext = activeSpan.spanContext();
|
384
|
+
meta2.trace_id = activeSpanContext.traceId;
|
385
|
+
meta2.span_id = activeSpanContext.spanId;
|
386
|
+
meta2.trace_flags = activeSpanContext.traceFlags;
|
387
|
+
meta2 = {
|
388
|
+
// @ts-expect-error accessing private property
|
389
|
+
...activeSpan.attributes,
|
390
|
+
...meta2
|
391
|
+
};
|
392
|
+
}
|
393
|
+
meta2 = {
|
394
|
+
"api.name": "none",
|
395
|
+
"correlation.id": "none",
|
396
|
+
...meta2
|
397
|
+
};
|
398
|
+
this.pinoLogger[level](...filteredArgs);
|
399
|
+
import_api_logs.logs.getLogger(process.env.OTEL_SERVICE_NAME ?? "unknown").emit({
|
400
|
+
severityText: level,
|
401
|
+
severityNumber: mapSeverity(level),
|
402
|
+
body: this.prettyPrinter(filteredArgs),
|
403
|
+
attributes: { ...this.meta, ...meta2 }
|
404
|
+
});
|
405
|
+
}
|
406
|
+
error = (msg, ...args) => this.log("error", msg, ...args);
|
407
|
+
info = (msg, ...args) => this.log("info", msg, ...args);
|
408
|
+
debug = (msg, ...args) => this.log("debug", msg, ...args);
|
409
|
+
warn = (msg, ...args) => this.log("warn", msg, ...args);
|
410
|
+
trace = (msg, ...args) => this.log("trace", msg, ...args);
|
411
|
+
child(meta2 = {}) {
|
412
|
+
return new _PinoLogger(this.pinoLogger.level, { ...this.meta, ...meta2 });
|
413
|
+
}
|
414
|
+
getBaseLogger() {
|
415
|
+
return this.pinoLogger;
|
416
|
+
}
|
417
|
+
};
|
418
|
+
function logger(level, meta2 = {}) {
|
419
|
+
return new PinoLogger(level, meta2);
|
420
|
+
}
|
421
|
+
|
422
|
+
// src/http/telemetry/openTelemetryCollector.ts
|
423
|
+
var OpenTelemetryCollector = class {
|
424
|
+
// scoped creation and create this in middleware when api execute. Also add correlation id
|
425
|
+
constructor(serviceName, level, metricDefinitions) {
|
426
|
+
this.serviceName = serviceName;
|
427
|
+
this.logger = logger(level || "info");
|
428
|
+
this.metrics = {};
|
429
|
+
for (const [metricId, metricType] of Object.entries(
|
430
|
+
metricDefinitions ?? {}
|
431
|
+
)) {
|
432
|
+
switch (metricType) {
|
433
|
+
case "counter":
|
434
|
+
this.metrics[metricId] = import_api3.metrics.getMeter(this.serviceName).createCounter(metricId);
|
435
|
+
break;
|
436
|
+
case "gauge":
|
437
|
+
this.metrics[metricId] = import_api3.metrics.getMeter(this.serviceName).createGauge(metricId);
|
438
|
+
break;
|
439
|
+
case "histogram":
|
440
|
+
this.metrics[metricId] = import_api3.metrics.getMeter(this.serviceName).createHistogram(metricId);
|
441
|
+
break;
|
442
|
+
case "upDownCounter":
|
443
|
+
this.metrics[metricId] = import_api3.metrics.getMeter(this.serviceName).createUpDownCounter(metricId);
|
444
|
+
break;
|
445
|
+
case "observableCounter":
|
446
|
+
this.metrics[metricId] = import_api3.metrics.getMeter(this.serviceName).createObservableCounter(metricId);
|
447
|
+
break;
|
448
|
+
case "observableGauge":
|
449
|
+
this.metrics[metricId] = import_api3.metrics.getMeter(this.serviceName).createObservableGauge(metricId);
|
450
|
+
break;
|
451
|
+
case "observableUpDownCounter":
|
452
|
+
this.metrics[metricId] = import_api3.metrics.getMeter(this.serviceName).createObservableUpDownCounter(metricId);
|
453
|
+
break;
|
454
|
+
}
|
455
|
+
}
|
456
|
+
this.log("info", "OpenTelemetry (Traces + Logs + Metrics) started");
|
457
|
+
}
|
458
|
+
logger;
|
459
|
+
metrics;
|
460
|
+
log(level, ...args) {
|
461
|
+
this.logger.log(level, ...args);
|
462
|
+
}
|
463
|
+
info = (msg, ...args) => {
|
464
|
+
this.logger.log("info", msg, ...args);
|
465
|
+
};
|
466
|
+
error = (msg, ...args) => {
|
467
|
+
this.logger.log("error", msg, ...args);
|
468
|
+
};
|
469
|
+
warn = (msg, ...args) => {
|
470
|
+
this.logger.log("warn", msg, ...args);
|
471
|
+
};
|
472
|
+
debug = (msg, ...args) => {
|
473
|
+
this.logger.log("debug", msg, ...args);
|
474
|
+
};
|
475
|
+
trace = (msg, ...args) => {
|
476
|
+
this.logger.log("trace", msg, ...args);
|
477
|
+
};
|
478
|
+
getMetric(metricId) {
|
479
|
+
return this.metrics[metricId];
|
480
|
+
}
|
481
|
+
};
|
482
|
+
import_dotenv.default.config({ path: getEnvVar("ENV_FILE_PATH") });
|
483
|
+
new import_sdk_node.NodeSDK({
|
484
|
+
resource: new import_resources.Resource({
|
485
|
+
[import_semantic_conventions2.ATTR_SERVICE_NAME]: getEnvVar("OTEL_SERVICE_NAME")
|
486
|
+
}),
|
487
|
+
traceExporter: new import_exporter_trace_otlp_http.OTLPTraceExporter({
|
488
|
+
url: `${getEnvVar("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/traces`
|
489
|
+
}),
|
490
|
+
metricReader: new import_sdk_metrics.PeriodicExportingMetricReader({
|
491
|
+
exporter: new import_exporter_metrics_otlp_http.OTLPMetricExporter({
|
492
|
+
url: `${getEnvVar("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/metrics`
|
493
|
+
}),
|
494
|
+
exportIntervalMillis: 5e3
|
495
|
+
}),
|
496
|
+
logRecordProcessors: [
|
497
|
+
new import_sdk_logs.BatchLogRecordProcessor(
|
498
|
+
new import_exporter_logs_otlp_http.OTLPLogExporter({
|
499
|
+
url: `${getEnvVar("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/logs`
|
500
|
+
})
|
501
|
+
)
|
502
|
+
],
|
503
|
+
instrumentations: [
|
504
|
+
new import_instrumentation_http.HttpInstrumentation({
|
505
|
+
applyCustomAttributesOnSpan: (span, request) => {
|
506
|
+
span.setAttribute(
|
507
|
+
"service.name",
|
508
|
+
getEnvVar("OTEL_SERVICE_NAME") ?? "unknown"
|
509
|
+
);
|
510
|
+
if (isForklaunchRequest(request)) {
|
511
|
+
span.setAttribute("api.name", request.contractDetails?.name);
|
512
|
+
}
|
513
|
+
}
|
514
|
+
}),
|
515
|
+
new import_instrumentation_express.ExpressInstrumentation(),
|
516
|
+
new import_opentelemetry_instrumentation_hyper_express.HyperExpressInstrumentation()
|
517
|
+
]
|
518
|
+
}).start();
|
519
|
+
var httpRequestsTotalCounter = import_api3.metrics.getMeter(getEnvVar("OTEL_SERVICE_NAME") || "unknown").createCounter("http_requests_total", {
|
520
|
+
description: "Number of HTTP requests"
|
521
|
+
});
|
522
|
+
var httpServerDurationHistogram = import_api3.metrics.getMeter(getEnvVar("OTEL_SERVICE_NAME") || "unknown").createHistogram("http_server_duration", {
|
523
|
+
description: "Duration of HTTP server requests",
|
524
|
+
unit: "s"
|
525
|
+
});
|
526
|
+
|
285
527
|
// src/http/middleware/request/enrichDetails.middleware.ts
|
286
|
-
function enrichDetails(path, contractDetails, requestSchema, responseSchemas) {
|
528
|
+
function enrichDetails(path, contractDetails, requestSchema, responseSchemas, openTelemetryCollector) {
|
287
529
|
return (req, res, next) => {
|
288
530
|
req.originalPath = path;
|
289
531
|
req.contractDetails = contractDetails;
|
290
532
|
req.requestSchema = requestSchema;
|
291
533
|
res.responseSchemas = responseSchemas;
|
534
|
+
req.openTelemetryCollector = openTelemetryCollector;
|
292
535
|
req.context.span?.setAttribute(ATTR_API_NAME, req.contractDetails?.name);
|
536
|
+
const startTime = process.hrtime();
|
537
|
+
res.on("finish", () => {
|
538
|
+
const [seconds, nanoseconds] = process.hrtime(startTime);
|
539
|
+
const durationMs = seconds + nanoseconds / 1e9;
|
540
|
+
httpServerDurationHistogram.record(durationMs, {
|
541
|
+
[import_semantic_conventions.ATTR_SERVICE_NAME]: getEnvVar("OTEL_SERVICE_NAME") || "unknown",
|
542
|
+
[ATTR_API_NAME]: req.contractDetails?.name || "unknown",
|
543
|
+
[import_semantic_conventions.ATTR_HTTP_REQUEST_METHOD]: req.method,
|
544
|
+
[import_semantic_conventions.ATTR_HTTP_ROUTE]: req.originalPath || "unknown",
|
545
|
+
[import_semantic_conventions.ATTR_HTTP_RESPONSE_STATUS_CODE]: res.statusCode
|
546
|
+
});
|
547
|
+
});
|
293
548
|
next?.();
|
294
549
|
};
|
295
550
|
}
|
@@ -297,13 +552,18 @@ function enrichDetails(path, contractDetails, requestSchema, responseSchemas) {
|
|
297
552
|
// src/http/middleware/request/parse.middleware.ts
|
298
553
|
var import_validator = require("@forklaunch/validator");
|
299
554
|
|
555
|
+
// src/http/guards/hasSend.ts
|
556
|
+
function hasSend(res) {
|
557
|
+
return typeof res === "object" && res !== null && "send" in res;
|
558
|
+
}
|
559
|
+
|
300
560
|
// src/http/guards/isResponseShape.ts
|
301
561
|
function isResponseShape(maybeResponseShape) {
|
302
562
|
return maybeResponseShape != null && "body" in maybeResponseShape && "query" in maybeResponseShape && "params" in maybeResponseShape && "headers" in maybeResponseShape;
|
303
563
|
}
|
304
564
|
|
305
565
|
// src/http/middleware/request/parse.middleware.ts
|
306
|
-
function parse(req,
|
566
|
+
function parse(req, res, next) {
|
307
567
|
const request = {
|
308
568
|
params: req.params,
|
309
569
|
query: req.query,
|
@@ -324,12 +584,22 @@ function parse(req, _res, next) {
|
|
324
584
|
switch (req.contractDetails.options?.requestValidation) {
|
325
585
|
default:
|
326
586
|
case "error":
|
327
|
-
|
328
|
-
|
329
|
-
)
|
330
|
-
|
587
|
+
res.type("application/json");
|
588
|
+
res.status(400);
|
589
|
+
if (hasSend(res)) {
|
590
|
+
res.send(
|
591
|
+
`${(0, import_validator.prettyPrintParseErrors)(parsedRequest.errors, "Request")}
|
592
|
+
|
593
|
+
Correlation id: ${req.context.correlationId ?? "No correlation ID"}`
|
594
|
+
);
|
595
|
+
} else {
|
596
|
+
next?.(new Error("Request is not sendable."));
|
597
|
+
}
|
598
|
+
return;
|
331
599
|
case "warning":
|
332
|
-
|
600
|
+
req.openTelemetryCollector.warn(
|
601
|
+
(0, import_validator.prettyPrintParseErrors)(parsedRequest.errors, "Request")
|
602
|
+
);
|
333
603
|
break;
|
334
604
|
case "none":
|
335
605
|
break;
|
@@ -340,9 +610,10 @@ function parse(req, _res, next) {
|
|
340
610
|
|
341
611
|
// src/http/router/expressLikeRouter.ts
|
342
612
|
var ForklaunchExpressLikeRouter = class {
|
343
|
-
constructor(basePath, schemaValidator, internal) {
|
613
|
+
constructor(basePath, schemaValidator, internal, openTelemetryCollector) {
|
344
614
|
this.schemaValidator = schemaValidator;
|
345
615
|
this.internal = internal;
|
616
|
+
this.openTelemetryCollector = openTelemetryCollector;
|
346
617
|
this.basePath = basePath;
|
347
618
|
}
|
348
619
|
requestHandler;
|
@@ -361,7 +632,8 @@ var ForklaunchExpressLikeRouter = class {
|
|
361
632
|
`${this.basePath}${path}`,
|
362
633
|
contractDetails,
|
363
634
|
requestSchema,
|
364
|
-
responseSchemas
|
635
|
+
responseSchemas,
|
636
|
+
this.openTelemetryCollector
|
365
637
|
),
|
366
638
|
parse,
|
367
639
|
parseRequestAuth
|
@@ -922,20 +1194,16 @@ var ForklaunchExpressLikeApplication = class extends ForklaunchExpressLikeRouter
|
|
922
1194
|
*
|
923
1195
|
* @param {SV} schemaValidator - The schema validator.
|
924
1196
|
*/
|
925
|
-
constructor(schemaValidator, internal) {
|
926
|
-
super("/", schemaValidator, internal);
|
1197
|
+
constructor(schemaValidator, internal, openTelemetryCollector) {
|
1198
|
+
super("/", schemaValidator, internal, openTelemetryCollector);
|
927
1199
|
this.schemaValidator = schemaValidator;
|
928
1200
|
this.internal = internal;
|
1201
|
+
this.openTelemetryCollector = openTelemetryCollector;
|
929
1202
|
this.internal.use(createContext(this.schemaValidator));
|
930
1203
|
this.internal.use(cors);
|
931
1204
|
}
|
932
1205
|
};
|
933
1206
|
|
934
|
-
// src/http/guards/isForklaunchRequest.ts
|
935
|
-
function isForklaunchRequest(request) {
|
936
|
-
return request != null && typeof request === "object" && "contractDetails" in request;
|
937
|
-
}
|
938
|
-
|
939
1207
|
// src/http/guards/isPath.ts
|
940
1208
|
function isPath(path) {
|
941
1209
|
return path.startsWith("/");
|
@@ -1009,7 +1277,7 @@ var put = (_schemaValidator, path, contractDetails, ...handlers) => {
|
|
1009
1277
|
};
|
1010
1278
|
|
1011
1279
|
// src/http/handlers/trace.ts
|
1012
|
-
var
|
1280
|
+
var trace3 = (_schemaValidator, path, contractDetails, ...handlers) => {
|
1013
1281
|
return typedHandler(_schemaValidator, path, "trace", contractDetails, ...handlers);
|
1014
1282
|
};
|
1015
1283
|
|
@@ -2028,12 +2296,24 @@ function parse2(req, res, next) {
|
|
2028
2296
|
switch (req.contractDetails.options?.responseValidation) {
|
2029
2297
|
default:
|
2030
2298
|
case "error":
|
2031
|
-
|
2032
|
-
|
2033
|
-
|
2299
|
+
res.type("text/plain");
|
2300
|
+
res.status(400);
|
2301
|
+
if (hasSend(res)) {
|
2302
|
+
res.send(
|
2303
|
+
`Invalid response:
|
2304
|
+
${parseErrors.join("\n\n")}
|
2305
|
+
|
2306
|
+
Correlation id: ${req.context.correlationId ?? "No correlation ID"}`
|
2307
|
+
);
|
2308
|
+
} else {
|
2309
|
+
next?.(new Error("Response is not sendable."));
|
2310
|
+
}
|
2311
|
+
return;
|
2034
2312
|
case "warning":
|
2035
|
-
|
2036
|
-
|
2313
|
+
req.openTelemetryCollector.warn(
|
2314
|
+
`Invalid response:
|
2315
|
+
${parseErrors.join("\n\n")}`
|
2316
|
+
);
|
2037
2317
|
break;
|
2038
2318
|
case "none":
|
2039
2319
|
break;
|
@@ -2042,96 +2322,6 @@ ${parseErrors.join("\n\n")}`);
|
|
2042
2322
|
next?.();
|
2043
2323
|
}
|
2044
2324
|
|
2045
|
-
// src/http/telemetry/pinoLogger.ts
|
2046
|
-
var import_common2 = require("@forklaunch/common");
|
2047
|
-
var import_api2 = require("@opentelemetry/api");
|
2048
|
-
var import_api_logs = require("@opentelemetry/api-logs");
|
2049
|
-
var import_pino = __toESM(require("pino"));
|
2050
|
-
function mapSeverity(level) {
|
2051
|
-
switch (level) {
|
2052
|
-
case "silent":
|
2053
|
-
return 0;
|
2054
|
-
case "trace":
|
2055
|
-
return 1;
|
2056
|
-
case "debug":
|
2057
|
-
return 5;
|
2058
|
-
case "info":
|
2059
|
-
return 9;
|
2060
|
-
case "warn":
|
2061
|
-
return 13;
|
2062
|
-
case "error":
|
2063
|
-
return 17;
|
2064
|
-
case "fatal":
|
2065
|
-
return 21;
|
2066
|
-
default:
|
2067
|
-
(0, import_common2.isNever)(level);
|
2068
|
-
return 0;
|
2069
|
-
}
|
2070
|
-
}
|
2071
|
-
var PinoLogger = class _PinoLogger {
|
2072
|
-
pinoLogger;
|
2073
|
-
meta;
|
2074
|
-
constructor(level, meta = {}) {
|
2075
|
-
this.pinoLogger = (0, import_pino.default)({
|
2076
|
-
level: level || "info",
|
2077
|
-
formatters: {
|
2078
|
-
level(label) {
|
2079
|
-
return { level: label };
|
2080
|
-
}
|
2081
|
-
},
|
2082
|
-
timestamp: import_pino.default.stdTimeFunctions.isoTime,
|
2083
|
-
transport: {
|
2084
|
-
target: "pino-pretty",
|
2085
|
-
options: { colorize: true }
|
2086
|
-
}
|
2087
|
-
});
|
2088
|
-
this.meta = meta;
|
2089
|
-
}
|
2090
|
-
log(level, msg, meta = {}) {
|
2091
|
-
const activeSpan = import_api2.trace.getActiveSpan();
|
2092
|
-
if (activeSpan) {
|
2093
|
-
const activeSpanContext = activeSpan.spanContext();
|
2094
|
-
meta.trace_id = activeSpanContext.traceId;
|
2095
|
-
meta.span_id = activeSpanContext.spanId;
|
2096
|
-
meta.trace_flags = activeSpanContext.traceFlags;
|
2097
|
-
if (!meta.api_name) {
|
2098
|
-
meta = { ...meta, ...activeSpan?.attributes };
|
2099
|
-
}
|
2100
|
-
}
|
2101
|
-
this.pinoLogger[level](msg);
|
2102
|
-
import_api_logs.logs.getLogger(process.env.OTEL_SERVICE_NAME ?? "unknown").emit({
|
2103
|
-
severityText: level,
|
2104
|
-
severityNumber: mapSeverity(level),
|
2105
|
-
body: msg,
|
2106
|
-
attributes: { ...this.meta, ...meta }
|
2107
|
-
});
|
2108
|
-
}
|
2109
|
-
error(msg, meta = {}) {
|
2110
|
-
this.log("error", msg, meta);
|
2111
|
-
}
|
2112
|
-
info(msg, meta = {}) {
|
2113
|
-
this.log("info", msg, meta);
|
2114
|
-
}
|
2115
|
-
debug(msg, meta = {}) {
|
2116
|
-
this.log("debug", msg, meta);
|
2117
|
-
}
|
2118
|
-
warn(msg, meta = {}) {
|
2119
|
-
this.log("warn", msg, meta);
|
2120
|
-
}
|
2121
|
-
trace(msg, meta = {}) {
|
2122
|
-
this.log("trace", msg, meta);
|
2123
|
-
}
|
2124
|
-
child(meta = {}) {
|
2125
|
-
return new _PinoLogger(this.pinoLogger.level, { ...this.meta, ...meta });
|
2126
|
-
}
|
2127
|
-
getBaseLogger() {
|
2128
|
-
return this.pinoLogger;
|
2129
|
-
}
|
2130
|
-
};
|
2131
|
-
function logger(level, meta = {}) {
|
2132
|
-
return new PinoLogger(level, meta);
|
2133
|
-
}
|
2134
|
-
|
2135
2325
|
// src/http/telemetry/recordMetric.ts
|
2136
2326
|
var import_semantic_conventions3 = require("@opentelemetry/semantic-conventions");
|
2137
2327
|
|
@@ -2139,126 +2329,6 @@ var import_semantic_conventions3 = require("@opentelemetry/semantic-conventions"
|
|
2139
2329
|
var import_common3 = require("@forklaunch/common");
|
2140
2330
|
var import_validator3 = require("@forklaunch/validator");
|
2141
2331
|
|
2142
|
-
// src/services/getEnvVar.ts
|
2143
|
-
function getEnvVar(name) {
|
2144
|
-
const value = process.env[name];
|
2145
|
-
return value;
|
2146
|
-
}
|
2147
|
-
|
2148
|
-
// src/http/telemetry/openTelemetryCollector.ts
|
2149
|
-
var import_opentelemetry_instrumentation_hyper_express = require("@forklaunch/opentelemetry-instrumentation-hyper-express");
|
2150
|
-
var import_api3 = require("@opentelemetry/api");
|
2151
|
-
var import_exporter_logs_otlp_http = require("@opentelemetry/exporter-logs-otlp-http");
|
2152
|
-
var import_exporter_metrics_otlp_http = require("@opentelemetry/exporter-metrics-otlp-http");
|
2153
|
-
var import_exporter_trace_otlp_http = require("@opentelemetry/exporter-trace-otlp-http");
|
2154
|
-
var import_instrumentation_express = require("@opentelemetry/instrumentation-express");
|
2155
|
-
var import_instrumentation_http = require("@opentelemetry/instrumentation-http");
|
2156
|
-
var import_resources = require("@opentelemetry/resources");
|
2157
|
-
var import_sdk_logs = require("@opentelemetry/sdk-logs");
|
2158
|
-
var import_sdk_metrics = require("@opentelemetry/sdk-metrics");
|
2159
|
-
var import_sdk_node = require("@opentelemetry/sdk-node");
|
2160
|
-
var import_semantic_conventions2 = require("@opentelemetry/semantic-conventions");
|
2161
|
-
var import_dotenv = __toESM(require("dotenv"));
|
2162
|
-
var OpenTelemetryCollector = class {
|
2163
|
-
// scoped creation and create this in middleware when api execute. Also add correlation id
|
2164
|
-
constructor(serviceName, level, metricDefinitions) {
|
2165
|
-
this.serviceName = serviceName;
|
2166
|
-
this.logger = logger(level ?? "info");
|
2167
|
-
this.metrics = {};
|
2168
|
-
for (const [metricId, metricType] of Object.entries(
|
2169
|
-
metricDefinitions ?? {}
|
2170
|
-
)) {
|
2171
|
-
switch (metricType) {
|
2172
|
-
case "counter":
|
2173
|
-
this.metrics[metricId] = import_api3.metrics.getMeter(this.serviceName).createCounter(metricId);
|
2174
|
-
break;
|
2175
|
-
case "gauge":
|
2176
|
-
this.metrics[metricId] = import_api3.metrics.getMeter(this.serviceName).createGauge(metricId);
|
2177
|
-
break;
|
2178
|
-
case "histogram":
|
2179
|
-
this.metrics[metricId] = import_api3.metrics.getMeter(this.serviceName).createHistogram(metricId);
|
2180
|
-
break;
|
2181
|
-
case "upDownCounter":
|
2182
|
-
this.metrics[metricId] = import_api3.metrics.getMeter(this.serviceName).createUpDownCounter(metricId);
|
2183
|
-
break;
|
2184
|
-
case "observableCounter":
|
2185
|
-
this.metrics[metricId] = import_api3.metrics.getMeter(this.serviceName).createObservableCounter(metricId);
|
2186
|
-
break;
|
2187
|
-
case "observableGauge":
|
2188
|
-
this.metrics[metricId] = import_api3.metrics.getMeter(this.serviceName).createObservableGauge(metricId);
|
2189
|
-
break;
|
2190
|
-
case "observableUpDownCounter":
|
2191
|
-
this.metrics[metricId] = import_api3.metrics.getMeter(this.serviceName).createObservableUpDownCounter(metricId);
|
2192
|
-
break;
|
2193
|
-
}
|
2194
|
-
}
|
2195
|
-
this.log("info", "OpenTelemetry (Traces + Logs + Metrics) started");
|
2196
|
-
}
|
2197
|
-
logger;
|
2198
|
-
metrics;
|
2199
|
-
log(level, msg, meta = {}) {
|
2200
|
-
this.logger.log(level, msg, meta);
|
2201
|
-
}
|
2202
|
-
info(msg, meta = {}) {
|
2203
|
-
this.logger.info(msg, meta);
|
2204
|
-
}
|
2205
|
-
error(msg, meta = {}) {
|
2206
|
-
this.logger.error(msg, meta);
|
2207
|
-
}
|
2208
|
-
warn(msg, meta = {}) {
|
2209
|
-
this.logger.warn(msg, meta);
|
2210
|
-
}
|
2211
|
-
debug(msg, meta = {}) {
|
2212
|
-
this.logger.debug(msg, meta);
|
2213
|
-
}
|
2214
|
-
trace(msg, meta = {}) {
|
2215
|
-
this.logger.trace(msg, meta);
|
2216
|
-
}
|
2217
|
-
getMetric(metricId) {
|
2218
|
-
return this.metrics[metricId];
|
2219
|
-
}
|
2220
|
-
};
|
2221
|
-
import_dotenv.default.config({ path: getEnvVar("ENV_FILE_PATH") });
|
2222
|
-
new import_sdk_node.NodeSDK({
|
2223
|
-
resource: new import_resources.Resource({
|
2224
|
-
[import_semantic_conventions2.ATTR_SERVICE_NAME]: getEnvVar("OTEL_SERVICE_NAME")
|
2225
|
-
}),
|
2226
|
-
traceExporter: new import_exporter_trace_otlp_http.OTLPTraceExporter({
|
2227
|
-
url: `${getEnvVar("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/traces`
|
2228
|
-
}),
|
2229
|
-
metricReader: new import_sdk_metrics.PeriodicExportingMetricReader({
|
2230
|
-
exporter: new import_exporter_metrics_otlp_http.OTLPMetricExporter({
|
2231
|
-
url: `${getEnvVar("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/metrics`
|
2232
|
-
}),
|
2233
|
-
exportIntervalMillis: 5e3
|
2234
|
-
}),
|
2235
|
-
logRecordProcessors: [
|
2236
|
-
new import_sdk_logs.BatchLogRecordProcessor(
|
2237
|
-
new import_exporter_logs_otlp_http.OTLPLogExporter({
|
2238
|
-
url: `${getEnvVar("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/logs`
|
2239
|
-
})
|
2240
|
-
)
|
2241
|
-
],
|
2242
|
-
instrumentations: [
|
2243
|
-
new import_instrumentation_http.HttpInstrumentation({
|
2244
|
-
applyCustomAttributesOnSpan: (span, request) => {
|
2245
|
-
span.setAttribute(
|
2246
|
-
"service.name",
|
2247
|
-
getEnvVar("OTEL_SERVICE_NAME") ?? "unknown"
|
2248
|
-
);
|
2249
|
-
if (isForklaunchRequest(request)) {
|
2250
|
-
span.setAttribute("api.name", request.contractDetails?.name);
|
2251
|
-
}
|
2252
|
-
}
|
2253
|
-
}),
|
2254
|
-
new import_instrumentation_express.ExpressInstrumentation(),
|
2255
|
-
new import_opentelemetry_instrumentation_hyper_express.HyperExpressInstrumentation()
|
2256
|
-
]
|
2257
|
-
}).start();
|
2258
|
-
var httpRequestsTotalCounter = import_api3.metrics.getMeter(getEnvVar("OTEL_SERVICE_NAME") || "unknown").createCounter("http_requests_total", {
|
2259
|
-
description: "Number of HTTP requests"
|
2260
|
-
});
|
2261
|
-
|
2262
2332
|
// src/http/telemetry/recordMetric.ts
|
2263
2333
|
function recordMetric(req, res) {
|
2264
2334
|
httpRequestsTotalCounter.add(1, {
|
@@ -2277,6 +2347,7 @@ function enrichExpressLikeSend(instance, req, res, originalSend, data, shouldEnr
|
|
2277
2347
|
if (shouldEnrich) {
|
2278
2348
|
recordMetric(req, res);
|
2279
2349
|
if (res.statusCode === 404) {
|
2350
|
+
res.type("text/plain");
|
2280
2351
|
res.status(404);
|
2281
2352
|
logger("error").error("Not Found");
|
2282
2353
|
originalSend.call(instance, "Not Found");
|
@@ -2290,6 +2361,7 @@ function enrichExpressLikeSend(instance, req, res, originalSend, data, shouldEnr
|
|
2290
2361
|
${res.locals.errorMessage}`;
|
2291
2362
|
}
|
2292
2363
|
logger("error").error(errorString);
|
2364
|
+
res.type("text/plain");
|
2293
2365
|
res.status(500);
|
2294
2366
|
originalSend.call(instance, errorString);
|
2295
2367
|
parseErrorSent = true;
|
@@ -2467,6 +2539,7 @@ function metricsDefinitions(metrics2) {
|
|
2467
2539
|
getCodeForStatus,
|
2468
2540
|
head,
|
2469
2541
|
httpRequestsTotalCounter,
|
2542
|
+
httpServerDurationHistogram,
|
2470
2543
|
isClientError,
|
2471
2544
|
isForklaunchRequest,
|
2472
2545
|
isForklaunchRouter,
|
@@ -2476,6 +2549,7 @@ function metricsDefinitions(metrics2) {
|
|
2476
2549
|
isSuccessful,
|
2477
2550
|
isValidStatusCode,
|
2478
2551
|
logger,
|
2552
|
+
meta,
|
2479
2553
|
metricsDefinitions,
|
2480
2554
|
middleware,
|
2481
2555
|
options,
|