@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/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: () => trace2,
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
- console.error(error);
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, _res, next) {
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
- next?.(
328
- new Error((0, import_validator.prettyPrintParseErrors)(parsedRequest.errors, "Request"))
329
- );
330
- break;
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
- console.warn((0, import_validator.prettyPrintParseErrors)(parsedRequest.errors, "Request"));
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 trace2 = (_schemaValidator, path, contractDetails, ...handlers) => {
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
- next?.(new Error(`Invalid response:
2032
- ${parseErrors.join("\n\n")}`));
2033
- break;
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
- console.warn(`Invalid response:
2036
- ${parseErrors.join("\n\n")}`);
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,