@forklaunch/core 0.4.0 → 0.5.1

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
@@ -30,12 +30,17 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/http/index.ts
31
31
  var http_exports = {};
32
32
  __export(http_exports, {
33
+ ATTR_API_NAME: () => ATTR_API_NAME,
34
+ ATTR_CORRELATION_ID: () => ATTR_CORRELATION_ID,
35
+ ATTR_HTTP_REQUEST_METHOD: () => import_semantic_conventions.ATTR_HTTP_REQUEST_METHOD,
36
+ ATTR_HTTP_RESPONSE_STATUS_CODE: () => import_semantic_conventions.ATTR_HTTP_RESPONSE_STATUS_CODE,
37
+ ATTR_HTTP_ROUTE: () => import_semantic_conventions.ATTR_HTTP_ROUTE,
38
+ ATTR_SERVICE_NAME: () => import_semantic_conventions.ATTR_SERVICE_NAME,
33
39
  ForklaunchExpressLikeApplication: () => ForklaunchExpressLikeApplication,
34
40
  ForklaunchExpressLikeRouter: () => ForklaunchExpressLikeRouter,
35
41
  HTTPStatuses: () => HTTPStatuses,
36
42
  OpenTelemetryCollector: () => OpenTelemetryCollector,
37
43
  delete_: () => delete_,
38
- emitLoggerError: () => emitLoggerError,
39
44
  enrichExpressLikeSend: () => enrichExpressLikeSend,
40
45
  generateSwaggerDocument: () => generateSwaggerDocument,
41
46
  get: () => get,
@@ -43,19 +48,22 @@ __export(http_exports, {
43
48
  head: () => head,
44
49
  httpRequestsTotalCounter: () => httpRequestsTotalCounter,
45
50
  isClientError: () => isClientError,
51
+ isForklaunchRequest: () => isForklaunchRequest,
46
52
  isForklaunchRouter: () => isForklaunchRouter,
47
53
  isInformational: () => isInformational,
48
54
  isRedirection: () => isRedirection,
49
55
  isServerError: () => isServerError,
50
56
  isSuccessful: () => isSuccessful,
51
57
  isValidStatusCode: () => isValidStatusCode,
58
+ logger: () => logger,
59
+ metricsDefinitions: () => metricsDefinitions,
52
60
  middleware: () => middleware,
53
61
  options: () => options,
54
62
  patch: () => patch,
55
63
  post: () => post,
56
64
  put: () => put,
57
65
  recordMetric: () => recordMetric,
58
- trace: () => trace,
66
+ trace: () => trace2,
59
67
  typedHandler: () => typedHandler
60
68
  });
61
69
  module.exports = __toCommonJS(http_exports);
@@ -71,7 +79,15 @@ function cors(req, res, next) {
71
79
  }
72
80
 
73
81
  // src/http/middleware/request/createContext.middleware.ts
82
+ var import_api = require("@opentelemetry/api");
74
83
  var import_uuid = require("uuid");
84
+
85
+ // src/http/telemetry/constants.ts
86
+ var import_semantic_conventions = require("@opentelemetry/semantic-conventions");
87
+ var ATTR_API_NAME = "api.name";
88
+ var ATTR_CORRELATION_ID = "correlation.id";
89
+
90
+ // src/http/middleware/request/createContext.middleware.ts
75
91
  function createContext(schemaValidator) {
76
92
  return function setContext(req, res, next) {
77
93
  req.schemaValidator = schemaValidator;
@@ -83,6 +99,11 @@ function createContext(schemaValidator) {
83
99
  req.context = {
84
100
  correlationId
85
101
  };
102
+ const span = import_api.trace.getActiveSpan();
103
+ if (span != null) {
104
+ req.context.span = span;
105
+ req.context.span?.setAttribute(ATTR_CORRELATION_ID, correlationId);
106
+ }
86
107
  next?.();
87
108
  };
88
109
  }
@@ -268,6 +289,7 @@ function enrichDetails(path, contractDetails, requestSchema, responseSchemas) {
268
289
  req.contractDetails = contractDetails;
269
290
  req.requestSchema = requestSchema;
270
291
  res.responseSchemas = responseSchemas;
292
+ req.context.span?.setAttribute(ATTR_API_NAME, req.contractDetails?.name);
271
293
  next?.();
272
294
  };
273
295
  }
@@ -288,7 +310,10 @@ function parse(req, _res, next) {
288
310
  headers: req.headers,
289
311
  body: req.body
290
312
  };
291
- const parsedRequest = req.schemaValidator.parse(req.requestSchema, request);
313
+ const parsedRequest = req.schemaValidator.parse(
314
+ req.requestSchema,
315
+ request
316
+ );
292
317
  if (parsedRequest.ok && isResponseShape(parsedRequest.value)) {
293
318
  req.body = parsedRequest.value.body;
294
319
  req.params = parsedRequest.value.params;
@@ -362,7 +387,7 @@ var ForklaunchExpressLikeRouter = class {
362
387
  try {
363
388
  await requestHandler(req, res, next);
364
389
  } catch (error) {
365
- if (next) {
390
+ if (next && typeof next === "function") {
366
391
  next(error);
367
392
  } else {
368
393
  throw error;
@@ -398,7 +423,7 @@ var ForklaunchExpressLikeRouter = class {
398
423
  ...contractDetails.params ? { params: contractDetails.params } : {},
399
424
  ...contractDetails.requestHeaders ? { headers: contractDetails.requestHeaders } : {},
400
425
  ...contractDetails.query ? { query: contractDetails.query } : {},
401
- ...isHttpContractDetails(contractDetails) && contractDetails.body ? { body: contractDetails.body } : {}
426
+ ...isHttpContractDetails(contractDetails) && contractDetails.body != null ? { body: contractDetails.body } : {}
402
427
  })
403
428
  );
404
429
  const responseEntries = {
@@ -407,7 +432,9 @@ var ForklaunchExpressLikeRouter = class {
407
432
  403: schemaValidator.string,
408
433
  404: schemaValidator.string,
409
434
  500: schemaValidator.string,
410
- ...isPathParamHttpContractDetails(contractDetails) || isHttpContractDetails(contractDetails) ? { ...contractDetails.responses } : {}
435
+ ...isPathParamHttpContractDetails(contractDetails) || isHttpContractDetails(contractDetails) ? {
436
+ ...contractDetails.responses
437
+ } : {}
411
438
  };
412
439
  const responseSchemas = {
413
440
  responses: {},
@@ -537,9 +564,12 @@ var ForklaunchExpressLikeRouter = class {
537
564
  const controllerHandler = this.#extractControllerHandler(handlers);
538
565
  registrationMethod.bind(this.internal)(
539
566
  path,
540
- ...this.#resolveMiddlewares(path, contractDetails, requestSchema, responseSchemas).concat(
541
- handlers
542
- ),
567
+ ...this.#resolveMiddlewares(
568
+ path,
569
+ contractDetails,
570
+ requestSchema,
571
+ responseSchemas
572
+ ).concat(handlers),
543
573
  this.#parseAndRunControllerHandler(controllerHandler)
544
574
  );
545
575
  return this.#localParamRequest(
@@ -595,9 +625,7 @@ var ForklaunchExpressLikeRouter = class {
595
625
  contractDetailsOrMiddlewareOrTypedHandler,
596
626
  middleware2
597
627
  );
598
- if (isForklaunchExpressLikeRouter(
599
- contractDetailsOrMiddlewareOrTypedHandler
600
- )) {
628
+ if (isForklaunchExpressLikeRouter(contractDetailsOrMiddlewareOrTypedHandler)) {
601
629
  middleware2.push(contractDetailsOrMiddlewareOrTypedHandler.internal);
602
630
  }
603
631
  middleware2.push(
@@ -903,8 +931,18 @@ var ForklaunchExpressLikeApplication = class extends ForklaunchExpressLikeRouter
903
931
  }
904
932
  };
905
933
 
934
+ // src/http/guards/isForklaunchRequest.ts
935
+ function isForklaunchRequest(request) {
936
+ return request != null && typeof request === "object" && "contractDetails" in request;
937
+ }
938
+
939
+ // src/http/guards/isPath.ts
940
+ function isPath(path) {
941
+ return path.startsWith("/");
942
+ }
943
+
906
944
  // src/http/handlers/typedHandler.ts
907
- function typedHandler(_schemaValidator, _pathOrContractDetails, contractMethodOrContractDetails, contractDetailsOrHandler, ...handlerArray) {
945
+ function typedHandler(_schemaValidator, pathOrContractMethod, contractMethodOrContractDetails, contractDetailsOrHandler, ...handlerArray) {
908
946
  let contractDetails;
909
947
  let handlers;
910
948
  if (typeof contractMethodOrContractDetails === "string") {
@@ -924,6 +962,7 @@ function typedHandler(_schemaValidator, _pathOrContractDetails, contractMethodOr
924
962
  }
925
963
  return {
926
964
  _typedHandler: true,
965
+ _path: isPath(pathOrContractMethod) ? pathOrContractMethod : void 0,
927
966
  contractDetails,
928
967
  handlers
929
968
  };
@@ -970,11 +1009,11 @@ var put = (_schemaValidator, path, contractDetails, ...handlers) => {
970
1009
  };
971
1010
 
972
1011
  // src/http/handlers/trace.ts
973
- var trace = (_schemaValidator, path, contractDetails, ...handlers) => {
1012
+ var trace2 = (_schemaValidator, path, contractDetails, ...handlers) => {
974
1013
  return typedHandler(_schemaValidator, path, "trace", contractDetails, ...handlers);
975
1014
  };
976
1015
 
977
- // src/http/utils/httpStatusCodes.ts
1016
+ // src/http/httpStatusCodes.ts
978
1017
  var HTTPStatuses = {
979
1018
  /**
980
1019
  * The initial part of a request has been received and has not yet been
@@ -1957,6 +1996,311 @@ var getCodeForStatus = (status) => {
1957
1996
  };
1958
1997
  var httpStatusCodes_default = HTTPStatuses;
1959
1998
 
1999
+ // src/http/middleware/response/parse.middleware.ts
2000
+ var import_validator2 = require("@forklaunch/validator");
2001
+ function parse2(req, res, next) {
2002
+ const { headers, responses } = res.responseSchemas;
2003
+ const parsedResponse = req.schemaValidator.parse(
2004
+ responses?.[res.statusCode],
2005
+ res.bodyData
2006
+ );
2007
+ const parsedHeaders = req.schemaValidator.parse(
2008
+ headers ?? req.schemaValidator.unknown,
2009
+ res.getHeaders()
2010
+ );
2011
+ const parseErrors = [];
2012
+ if (!parsedHeaders.ok) {
2013
+ const headerErrors = (0, import_validator2.prettyPrintParseErrors)(parsedHeaders.errors, "Header");
2014
+ if (headerErrors) {
2015
+ parseErrors.push(headerErrors);
2016
+ }
2017
+ }
2018
+ if (!parsedResponse.ok) {
2019
+ const responseErrors = (0, import_validator2.prettyPrintParseErrors)(
2020
+ parsedResponse.errors,
2021
+ "Response"
2022
+ );
2023
+ if (responseErrors) {
2024
+ parseErrors.push(responseErrors);
2025
+ }
2026
+ }
2027
+ if (parseErrors.length > 0) {
2028
+ switch (req.contractDetails.options?.responseValidation) {
2029
+ default:
2030
+ case "error":
2031
+ next?.(new Error(`Invalid response:
2032
+ ${parseErrors.join("\n\n")}`));
2033
+ break;
2034
+ case "warning":
2035
+ console.warn(`Invalid response:
2036
+ ${parseErrors.join("\n\n")}`);
2037
+ break;
2038
+ case "none":
2039
+ break;
2040
+ }
2041
+ }
2042
+ next?.();
2043
+ }
2044
+
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
+ // src/http/telemetry/recordMetric.ts
2136
+ var import_semantic_conventions3 = require("@opentelemetry/semantic-conventions");
2137
+
2138
+ // src/services/configInjector.ts
2139
+ var import_common3 = require("@forklaunch/common");
2140
+ var import_validator3 = require("@forklaunch/validator");
2141
+
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
+ // src/http/telemetry/recordMetric.ts
2263
+ function recordMetric(req, res) {
2264
+ httpRequestsTotalCounter.add(1, {
2265
+ [import_semantic_conventions3.ATTR_SERVICE_NAME]: getEnvVar("OTEL_SERVICE_NAME"),
2266
+ [ATTR_API_NAME]: req.contractDetails?.name,
2267
+ [ATTR_CORRELATION_ID]: req.context.correlationId,
2268
+ [import_semantic_conventions3.ATTR_HTTP_REQUEST_METHOD]: req.method,
2269
+ [import_semantic_conventions3.ATTR_HTTP_ROUTE]: req.originalPath,
2270
+ [import_semantic_conventions3.ATTR_HTTP_RESPONSE_STATUS_CODE]: res.statusCode || 0
2271
+ });
2272
+ }
2273
+
2274
+ // src/http/middleware/response/enrichExpressLikeSend.middleware.ts
2275
+ function enrichExpressLikeSend(instance, req, res, originalSend, data, shouldEnrich) {
2276
+ let parseErrorSent;
2277
+ if (shouldEnrich) {
2278
+ recordMetric(req, res);
2279
+ if (res.statusCode === 404) {
2280
+ res.status(404);
2281
+ logger("error").error("Not Found");
2282
+ originalSend.call(instance, "Not Found");
2283
+ }
2284
+ parse2(req, res, (err) => {
2285
+ if (err) {
2286
+ let errorString = err.message;
2287
+ if (res.locals.errorMessage) {
2288
+ errorString += `
2289
+ ------------------
2290
+ ${res.locals.errorMessage}`;
2291
+ }
2292
+ logger("error").error(errorString);
2293
+ res.status(500);
2294
+ originalSend.call(instance, errorString);
2295
+ parseErrorSent = true;
2296
+ }
2297
+ });
2298
+ }
2299
+ if (!parseErrorSent) {
2300
+ originalSend.call(instance, data);
2301
+ }
2302
+ }
2303
+
1960
2304
  // src/http/openApiV3Generator/openApiV3Generator.ts
1961
2305
  function toUpperCase(str) {
1962
2306
  return str.charAt(0).toUpperCase() + str.slice(1);
@@ -2100,252 +2444,23 @@ function generateSwaggerDocument(schemaValidator, port, routers) {
2100
2444
  return generateOpenApiDocument(port, tags, paths);
2101
2445
  }
2102
2446
 
2103
- // src/http/tracing/emitLoggerError.ts
2104
- var import_api_logs = require("@opentelemetry/api-logs");
2105
- var import_semantic_conventions = require("@opentelemetry/semantic-conventions");
2106
-
2107
- // src/http/tracing/pinoLogger.ts
2108
- var import_pino = __toESM(require("pino"));
2109
- function pinoLogger(level) {
2110
- return (0, import_pino.default)({
2111
- level: level || "info",
2112
- formatters: {
2113
- level(label) {
2114
- return { level: label };
2115
- }
2116
- },
2117
- timestamp: import_pino.default.stdTimeFunctions.isoTime,
2118
- transport: {
2119
- target: "pino-pretty",
2120
- options: { colorize: true }
2121
- }
2122
- });
2123
- }
2124
-
2125
- // src/http/tracing/emitLoggerError.ts
2126
- var logger = pinoLogger("error");
2127
- var emitLoggerError = (req, res, errorString) => {
2128
- import_api_logs.logs.getLogger(process.env.SERVICE_NAME ?? "unknown").emit({
2129
- severityText: "ERROR",
2130
- severityNumber: 17,
2131
- body: errorString,
2132
- attributes: {
2133
- "service.name": process.env.SERVICE_NAME,
2134
- "api.name": req.contractDetails?.name,
2135
- [import_semantic_conventions.SEMATTRS_HTTP_TARGET]: req.path,
2136
- [import_semantic_conventions.SEMATTRS_HTTP_ROUTE]: req.originalPath,
2137
- [import_semantic_conventions.SEMATTRS_HTTP_METHOD]: req.method,
2138
- [import_semantic_conventions.SEMATTRS_HTTP_STATUS_CODE]: res.statusCode
2139
- }
2140
- });
2141
- logger.error(errorString);
2142
- };
2143
-
2144
- // src/http/tracing/openTelemetryCollector.ts
2145
- var import_opentelemetry_instrumentation_hyper_express = require("@forklaunch/opentelemetry-instrumentation-hyper-express");
2146
- var import_api = require("@opentelemetry/api");
2147
- var import_api_logs2 = require("@opentelemetry/api-logs");
2148
- var import_exporter_logs_otlp_http = require("@opentelemetry/exporter-logs-otlp-http");
2149
- var import_exporter_metrics_otlp_http = require("@opentelemetry/exporter-metrics-otlp-http");
2150
- var import_exporter_trace_otlp_http = require("@opentelemetry/exporter-trace-otlp-http");
2151
- var import_instrumentation_express = require("@opentelemetry/instrumentation-express");
2152
- var import_instrumentation_http = require("@opentelemetry/instrumentation-http");
2153
- var import_resources = require("@opentelemetry/resources");
2154
- var import_sdk_logs = require("@opentelemetry/sdk-logs");
2155
- var import_sdk_metrics = require("@opentelemetry/sdk-metrics");
2156
- var import_sdk_node = require("@opentelemetry/sdk-node");
2157
- var import_semantic_conventions2 = require("@opentelemetry/semantic-conventions");
2158
- var OpenTelemetryCollector = class {
2159
- // scoped creation and create this in middleware when api execute. Also add correlation id
2160
- constructor(serviceName, level, metricDefinitions) {
2161
- this.serviceName = serviceName;
2162
- this.logger = pinoLogger(level ?? "info");
2163
- this.metrics = {};
2164
- for (const [metricId, metricType] of Object.entries(
2165
- metricDefinitions ?? {}
2166
- )) {
2167
- switch (metricType) {
2168
- case "counter":
2169
- this.metrics[metricId] = import_api.metrics.getMeter(this.serviceName).createCounter(metricId);
2170
- break;
2171
- case "gauge":
2172
- this.metrics[metricId] = import_api.metrics.getMeter(this.serviceName).createGauge(metricId);
2173
- break;
2174
- case "histogram":
2175
- this.metrics[metricId] = import_api.metrics.getMeter(this.serviceName).createHistogram(metricId);
2176
- break;
2177
- case "upDownCounter":
2178
- this.metrics[metricId] = import_api.metrics.getMeter(this.serviceName).createUpDownCounter(metricId);
2179
- break;
2180
- case "observableCounter":
2181
- this.metrics[metricId] = import_api.metrics.getMeter(this.serviceName).createObservableCounter(metricId);
2182
- break;
2183
- case "observableGauge":
2184
- this.metrics[metricId] = import_api.metrics.getMeter(this.serviceName).createObservableGauge(metricId);
2185
- break;
2186
- case "observableUpDownCounter":
2187
- this.metrics[metricId] = import_api.metrics.getMeter(this.serviceName).createObservableUpDownCounter(metricId);
2188
- break;
2189
- }
2190
- }
2191
- this.log("info", "OpenTelemetry (Traces + Logs + Metrics) started");
2192
- }
2193
- logger;
2194
- metrics;
2195
- log(level, msg, meta = {}) {
2196
- const activeSpan = import_api.trace.getActiveSpan();
2197
- if (activeSpan) {
2198
- const activeSpanContext = activeSpan.spanContext();
2199
- meta.trace_id = activeSpanContext.traceId;
2200
- meta.span_id = activeSpanContext.spanId;
2201
- meta.trace_flags = activeSpanContext.traceFlags;
2202
- }
2203
- if (!meta.api_name) {
2204
- meta.api_name = activeSpan?.attributes["api.name"] ?? "undefined";
2205
- }
2206
- this.logger[level](msg);
2207
- import_api_logs2.logs.getLogger(this.serviceName).emit({
2208
- severityText: level,
2209
- body: msg,
2210
- attributes: meta
2211
- });
2212
- }
2213
- getMetric(metricId) {
2214
- return this.metrics[metricId];
2215
- }
2216
- };
2217
- new import_sdk_node.NodeSDK({
2218
- resource: new import_resources.Resource({
2219
- [import_semantic_conventions2.ATTR_SERVICE_NAME]: process.env.SERVICE_NAME
2220
- }),
2221
- traceExporter: new import_exporter_trace_otlp_http.OTLPTraceExporter({
2222
- url: `${process.env.OTEL_EXPORTER_OTLP_ENDPOINT ?? "http://localhost:4318"}/v1/traces`
2223
- }),
2224
- metricReader: new import_sdk_metrics.PeriodicExportingMetricReader({
2225
- exporter: new import_exporter_metrics_otlp_http.OTLPMetricExporter({
2226
- url: `${process.env.OTEL_EXPORTER_OTLP_ENDPOINT ?? "http://localhost:4318"}/v1/metrics`
2227
- }),
2228
- exportIntervalMillis: 5e3
2229
- }),
2230
- logRecordProcessors: [
2231
- new import_sdk_logs.BatchLogRecordProcessor(
2232
- new import_exporter_logs_otlp_http.OTLPLogExporter({
2233
- url: `${process.env.OTEL_EXPORTER_OTLP_ENDPOINT ?? "http://localhost:4318"}/v1/logs`
2234
- })
2235
- )
2236
- ],
2237
- instrumentations: [
2238
- new import_instrumentation_http.HttpInstrumentation(),
2239
- new import_instrumentation_express.ExpressInstrumentation({
2240
- requestHook: (span, info) => {
2241
- span.setAttribute(
2242
- "service.name",
2243
- process.env.SERVICE_NAME ?? "unknown"
2244
- );
2245
- span.setAttribute("api.name", info.request.contractDetails?.name);
2246
- }
2247
- }),
2248
- new import_opentelemetry_instrumentation_hyper_express.HyperExpressInstrumentation()
2249
- ]
2250
- }).start();
2251
- var httpRequestsTotalCounter = import_api.metrics.getMeter(process.env.SERVICE_NAME || "unknown").createCounter("http_requests_total", {
2252
- description: "Number of HTTP requests"
2253
- });
2254
-
2255
- // src/http/middleware/response/parse.middleware.ts
2256
- var import_validator2 = require("@forklaunch/validator");
2257
- function parse2(req, res, next) {
2258
- const { headers, responses } = res.responseSchemas;
2259
- const parsedResponse = req.schemaValidator.parse(
2260
- responses?.[res.statusCode],
2261
- res.bodyData
2262
- );
2263
- const parsedHeaders = req.schemaValidator.parse(
2264
- headers ?? req.schemaValidator.unknown,
2265
- res.getHeaders()
2266
- );
2267
- const parseErrors = [];
2268
- if (!parsedHeaders.ok) {
2269
- const headerErrors = (0, import_validator2.prettyPrintParseErrors)(parsedHeaders.errors, "Header");
2270
- if (headerErrors) {
2271
- parseErrors.push(headerErrors);
2272
- }
2273
- }
2274
- if (!parsedResponse.ok) {
2275
- const responseErrors = (0, import_validator2.prettyPrintParseErrors)(
2276
- parsedResponse.errors,
2277
- "Response"
2278
- );
2279
- if (responseErrors) {
2280
- parseErrors.push(responseErrors);
2281
- }
2282
- }
2283
- if (parseErrors.length > 0) {
2284
- switch (req.contractDetails.options?.responseValidation) {
2285
- default:
2286
- case "error":
2287
- next?.(new Error(`Invalid response:
2288
- ${parseErrors.join("\n\n")}`));
2289
- break;
2290
- case "warning":
2291
- console.warn(`Invalid response:
2292
- ${parseErrors.join("\n\n")}`);
2293
- break;
2294
- case "none":
2295
- break;
2296
- }
2297
- }
2298
- next?.();
2299
- }
2300
-
2301
- // src/http/utils/enrichExpressLikeSend.ts
2302
- function enrichExpressLikeSend(instance, req, res, originalSend, data, shouldEnrich) {
2303
- let parseErrorSent;
2304
- if (shouldEnrich) {
2305
- if (res.statusCode === 404) {
2306
- res.status(404);
2307
- emitLoggerError(req, res, "Not Found");
2308
- originalSend.call(instance, "Not Found");
2309
- }
2310
- parse2(req, res, (err) => {
2311
- if (err) {
2312
- let errorString = err.message;
2313
- if (res.locals.errorMessage) {
2314
- errorString += `
2315
- ------------------
2316
- ${res.locals.errorMessage}`;
2317
- }
2318
- emitLoggerError(req, res, errorString);
2319
- res.status(500);
2320
- originalSend.call(instance, errorString);
2321
- parseErrorSent = true;
2322
- }
2323
- });
2324
- }
2325
- if (!parseErrorSent) {
2326
- originalSend.call(instance, data);
2327
- }
2328
- }
2329
-
2330
- // src/http/utils/recordMetric.ts
2331
- var import_semantic_conventions3 = require("@opentelemetry/semantic-conventions");
2332
- function recordMetric(req, res) {
2333
- httpRequestsTotalCounter.add(1, {
2334
- "service.name": process.env.SERVICE_NAME ?? "unknown",
2335
- "api.name": req.contractDetails?.name,
2336
- [import_semantic_conventions3.SEMATTRS_HTTP_METHOD]: req.method,
2337
- [import_semantic_conventions3.SEMATTRS_HTTP_ROUTE]: req.originalPath,
2338
- [import_semantic_conventions3.SEMATTRS_HTTP_STATUS_CODE]: res.statusCode || 0
2339
- });
2447
+ // src/http/telemetry/metricsDefinition.ts
2448
+ function metricsDefinitions(metrics2) {
2449
+ return metrics2;
2340
2450
  }
2341
2451
  // Annotate the CommonJS export names for ESM import in node:
2342
2452
  0 && (module.exports = {
2453
+ ATTR_API_NAME,
2454
+ ATTR_CORRELATION_ID,
2455
+ ATTR_HTTP_REQUEST_METHOD,
2456
+ ATTR_HTTP_RESPONSE_STATUS_CODE,
2457
+ ATTR_HTTP_ROUTE,
2458
+ ATTR_SERVICE_NAME,
2343
2459
  ForklaunchExpressLikeApplication,
2344
2460
  ForklaunchExpressLikeRouter,
2345
2461
  HTTPStatuses,
2346
2462
  OpenTelemetryCollector,
2347
2463
  delete_,
2348
- emitLoggerError,
2349
2464
  enrichExpressLikeSend,
2350
2465
  generateSwaggerDocument,
2351
2466
  get,
@@ -2353,12 +2468,15 @@ function recordMetric(req, res) {
2353
2468
  head,
2354
2469
  httpRequestsTotalCounter,
2355
2470
  isClientError,
2471
+ isForklaunchRequest,
2356
2472
  isForklaunchRouter,
2357
2473
  isInformational,
2358
2474
  isRedirection,
2359
2475
  isServerError,
2360
2476
  isSuccessful,
2361
2477
  isValidStatusCode,
2478
+ logger,
2479
+ metricsDefinitions,
2362
2480
  middleware,
2363
2481
  options,
2364
2482
  patch,