@forklaunch/core 0.4.0 → 0.5.0

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.
@@ -9,7 +9,20 @@ function cors(req, res, next) {
9
9
  }
10
10
 
11
11
  // src/http/middleware/request/createContext.middleware.ts
12
+ import { trace } from "@opentelemetry/api";
12
13
  import { v4 } from "uuid";
14
+
15
+ // src/http/telemetry/constants.ts
16
+ import {
17
+ ATTR_HTTP_REQUEST_METHOD,
18
+ ATTR_HTTP_RESPONSE_STATUS_CODE,
19
+ ATTR_HTTP_ROUTE,
20
+ ATTR_SERVICE_NAME
21
+ } from "@opentelemetry/semantic-conventions";
22
+ var ATTR_API_NAME = "api.name";
23
+ var ATTR_CORRELATION_ID = "correlation.id";
24
+
25
+ // src/http/middleware/request/createContext.middleware.ts
13
26
  function createContext(schemaValidator) {
14
27
  return function setContext(req, res, next) {
15
28
  req.schemaValidator = schemaValidator;
@@ -21,6 +34,11 @@ function createContext(schemaValidator) {
21
34
  req.context = {
22
35
  correlationId
23
36
  };
37
+ const span = trace.getActiveSpan();
38
+ if (span != null) {
39
+ req.context.span = span;
40
+ req.context.span?.setAttribute(ATTR_CORRELATION_ID, correlationId);
41
+ }
24
42
  next?.();
25
43
  };
26
44
  }
@@ -206,6 +224,7 @@ function enrichDetails(path, contractDetails, requestSchema, responseSchemas) {
206
224
  req.contractDetails = contractDetails;
207
225
  req.requestSchema = requestSchema;
208
226
  res.responseSchemas = responseSchemas;
227
+ req.context.span?.setAttribute(ATTR_API_NAME, req.contractDetails?.name);
209
228
  next?.();
210
229
  };
211
230
  }
@@ -228,7 +247,10 @@ function parse(req, _res, next) {
228
247
  headers: req.headers,
229
248
  body: req.body
230
249
  };
231
- const parsedRequest = req.schemaValidator.parse(req.requestSchema, request);
250
+ const parsedRequest = req.schemaValidator.parse(
251
+ req.requestSchema,
252
+ request
253
+ );
232
254
  if (parsedRequest.ok && isResponseShape(parsedRequest.value)) {
233
255
  req.body = parsedRequest.value.body;
234
256
  req.params = parsedRequest.value.params;
@@ -302,7 +324,7 @@ var ForklaunchExpressLikeRouter = class {
302
324
  try {
303
325
  await requestHandler(req, res, next);
304
326
  } catch (error) {
305
- if (next) {
327
+ if (next && typeof next === "function") {
306
328
  next(error);
307
329
  } else {
308
330
  throw error;
@@ -338,7 +360,7 @@ var ForklaunchExpressLikeRouter = class {
338
360
  ...contractDetails.params ? { params: contractDetails.params } : {},
339
361
  ...contractDetails.requestHeaders ? { headers: contractDetails.requestHeaders } : {},
340
362
  ...contractDetails.query ? { query: contractDetails.query } : {},
341
- ...isHttpContractDetails(contractDetails) && contractDetails.body ? { body: contractDetails.body } : {}
363
+ ...isHttpContractDetails(contractDetails) && contractDetails.body != null ? { body: contractDetails.body } : {}
342
364
  })
343
365
  );
344
366
  const responseEntries = {
@@ -347,7 +369,9 @@ var ForklaunchExpressLikeRouter = class {
347
369
  403: schemaValidator.string,
348
370
  404: schemaValidator.string,
349
371
  500: schemaValidator.string,
350
- ...isPathParamHttpContractDetails(contractDetails) || isHttpContractDetails(contractDetails) ? { ...contractDetails.responses } : {}
372
+ ...isPathParamHttpContractDetails(contractDetails) || isHttpContractDetails(contractDetails) ? {
373
+ ...contractDetails.responses
374
+ } : {}
351
375
  };
352
376
  const responseSchemas = {
353
377
  responses: {},
@@ -477,9 +501,12 @@ var ForklaunchExpressLikeRouter = class {
477
501
  const controllerHandler = this.#extractControllerHandler(handlers);
478
502
  registrationMethod.bind(this.internal)(
479
503
  path,
480
- ...this.#resolveMiddlewares(path, contractDetails, requestSchema, responseSchemas).concat(
481
- handlers
482
- ),
504
+ ...this.#resolveMiddlewares(
505
+ path,
506
+ contractDetails,
507
+ requestSchema,
508
+ responseSchemas
509
+ ).concat(handlers),
483
510
  this.#parseAndRunControllerHandler(controllerHandler)
484
511
  );
485
512
  return this.#localParamRequest(
@@ -535,9 +562,7 @@ var ForklaunchExpressLikeRouter = class {
535
562
  contractDetailsOrMiddlewareOrTypedHandler,
536
563
  middleware2
537
564
  );
538
- if (isForklaunchExpressLikeRouter(
539
- contractDetailsOrMiddlewareOrTypedHandler
540
- )) {
565
+ if (isForklaunchExpressLikeRouter(contractDetailsOrMiddlewareOrTypedHandler)) {
541
566
  middleware2.push(contractDetailsOrMiddlewareOrTypedHandler.internal);
542
567
  }
543
568
  middleware2.push(
@@ -843,8 +868,18 @@ var ForklaunchExpressLikeApplication = class extends ForklaunchExpressLikeRouter
843
868
  }
844
869
  };
845
870
 
871
+ // src/http/guards/isForklaunchRequest.ts
872
+ function isForklaunchRequest(request) {
873
+ return request != null && typeof request === "object" && "contractDetails" in request;
874
+ }
875
+
876
+ // src/http/guards/isPath.ts
877
+ function isPath(path) {
878
+ return path.startsWith("/");
879
+ }
880
+
846
881
  // src/http/handlers/typedHandler.ts
847
- function typedHandler(_schemaValidator, _pathOrContractDetails, contractMethodOrContractDetails, contractDetailsOrHandler, ...handlerArray) {
882
+ function typedHandler(_schemaValidator, pathOrContractMethod, contractMethodOrContractDetails, contractDetailsOrHandler, ...handlerArray) {
848
883
  let contractDetails;
849
884
  let handlers;
850
885
  if (typeof contractMethodOrContractDetails === "string") {
@@ -864,6 +899,7 @@ function typedHandler(_schemaValidator, _pathOrContractDetails, contractMethodOr
864
899
  }
865
900
  return {
866
901
  _typedHandler: true,
902
+ _path: isPath(pathOrContractMethod) ? pathOrContractMethod : void 0,
867
903
  contractDetails,
868
904
  handlers
869
905
  };
@@ -910,11 +946,11 @@ var put = (_schemaValidator, path, contractDetails, ...handlers) => {
910
946
  };
911
947
 
912
948
  // src/http/handlers/trace.ts
913
- var trace = (_schemaValidator, path, contractDetails, ...handlers) => {
949
+ var trace2 = (_schemaValidator, path, contractDetails, ...handlers) => {
914
950
  return typedHandler(_schemaValidator, path, "trace", contractDetails, ...handlers);
915
951
  };
916
952
 
917
- // src/http/utils/httpStatusCodes.ts
953
+ // src/http/httpStatusCodes.ts
918
954
  var HTTPStatuses = {
919
955
  /**
920
956
  * The initial part of a request has been received and has not yet been
@@ -1897,6 +1933,324 @@ var getCodeForStatus = (status) => {
1897
1933
  };
1898
1934
  var httpStatusCodes_default = HTTPStatuses;
1899
1935
 
1936
+ // src/http/middleware/response/parse.middleware.ts
1937
+ import {
1938
+ prettyPrintParseErrors as prettyPrintParseErrors2
1939
+ } from "@forklaunch/validator";
1940
+ function parse2(req, res, next) {
1941
+ const { headers, responses } = res.responseSchemas;
1942
+ const parsedResponse = req.schemaValidator.parse(
1943
+ responses?.[res.statusCode],
1944
+ res.bodyData
1945
+ );
1946
+ const parsedHeaders = req.schemaValidator.parse(
1947
+ headers ?? req.schemaValidator.unknown,
1948
+ res.getHeaders()
1949
+ );
1950
+ const parseErrors = [];
1951
+ if (!parsedHeaders.ok) {
1952
+ const headerErrors = prettyPrintParseErrors2(parsedHeaders.errors, "Header");
1953
+ if (headerErrors) {
1954
+ parseErrors.push(headerErrors);
1955
+ }
1956
+ }
1957
+ if (!parsedResponse.ok) {
1958
+ const responseErrors = prettyPrintParseErrors2(
1959
+ parsedResponse.errors,
1960
+ "Response"
1961
+ );
1962
+ if (responseErrors) {
1963
+ parseErrors.push(responseErrors);
1964
+ }
1965
+ }
1966
+ if (parseErrors.length > 0) {
1967
+ switch (req.contractDetails.options?.responseValidation) {
1968
+ default:
1969
+ case "error":
1970
+ next?.(new Error(`Invalid response:
1971
+ ${parseErrors.join("\n\n")}`));
1972
+ break;
1973
+ case "warning":
1974
+ console.warn(`Invalid response:
1975
+ ${parseErrors.join("\n\n")}`);
1976
+ break;
1977
+ case "none":
1978
+ break;
1979
+ }
1980
+ }
1981
+ next?.();
1982
+ }
1983
+
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
+ // src/http/telemetry/recordMetric.ts
2075
+ import {
2076
+ ATTR_HTTP_REQUEST_METHOD as ATTR_HTTP_REQUEST_METHOD3,
2077
+ ATTR_HTTP_RESPONSE_STATUS_CODE as ATTR_HTTP_RESPONSE_STATUS_CODE3,
2078
+ ATTR_HTTP_ROUTE as ATTR_HTTP_ROUTE3,
2079
+ ATTR_SERVICE_NAME as ATTR_SERVICE_NAME3
2080
+ } from "@opentelemetry/semantic-conventions";
2081
+
2082
+ // src/services/configInjector.ts
2083
+ import { extractArgumentNames as extractArgumentNames2, isNever as isNever2 } from "@forklaunch/common";
2084
+ import {
2085
+ prettyPrintParseErrors as prettyPrintParseErrors3
2086
+ } from "@forklaunch/validator";
2087
+
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
+ // src/http/telemetry/recordMetric.ts
2213
+ function recordMetric(req, res) {
2214
+ httpRequestsTotalCounter.add(1, {
2215
+ [ATTR_SERVICE_NAME3]: getEnvVar("OTEL_SERVICE_NAME"),
2216
+ [ATTR_API_NAME]: req.contractDetails?.name,
2217
+ [ATTR_CORRELATION_ID]: req.context.correlationId,
2218
+ [ATTR_HTTP_REQUEST_METHOD3]: req.method,
2219
+ [ATTR_HTTP_ROUTE3]: req.originalPath,
2220
+ [ATTR_HTTP_RESPONSE_STATUS_CODE3]: res.statusCode || 0
2221
+ });
2222
+ }
2223
+
2224
+ // src/http/middleware/response/enrichExpressLikeSend.middleware.ts
2225
+ function enrichExpressLikeSend(instance, req, res, originalSend, data, shouldEnrich) {
2226
+ let parseErrorSent;
2227
+ if (shouldEnrich) {
2228
+ recordMetric(req, res);
2229
+ if (res.statusCode === 404) {
2230
+ res.status(404);
2231
+ logger("error").error("Not Found");
2232
+ originalSend.call(instance, "Not Found");
2233
+ }
2234
+ parse2(req, res, (err) => {
2235
+ if (err) {
2236
+ let errorString = err.message;
2237
+ if (res.locals.errorMessage) {
2238
+ errorString += `
2239
+ ------------------
2240
+ ${res.locals.errorMessage}`;
2241
+ }
2242
+ logger("error").error(errorString);
2243
+ res.status(500);
2244
+ originalSend.call(instance, errorString);
2245
+ parseErrorSent = true;
2246
+ }
2247
+ });
2248
+ }
2249
+ if (!parseErrorSent) {
2250
+ originalSend.call(instance, data);
2251
+ }
2252
+ }
2253
+
1900
2254
  // src/http/openApiV3Generator/openApiV3Generator.ts
1901
2255
  function toUpperCase(str) {
1902
2256
  return str.charAt(0).toUpperCase() + str.slice(1);
@@ -2040,267 +2394,22 @@ function generateSwaggerDocument(schemaValidator, port, routers) {
2040
2394
  return generateOpenApiDocument(port, tags, paths);
2041
2395
  }
2042
2396
 
2043
- // src/http/tracing/emitLoggerError.ts
2044
- import { logs } from "@opentelemetry/api-logs";
2045
- import {
2046
- SEMATTRS_HTTP_METHOD,
2047
- SEMATTRS_HTTP_ROUTE,
2048
- SEMATTRS_HTTP_STATUS_CODE,
2049
- SEMATTRS_HTTP_TARGET
2050
- } from "@opentelemetry/semantic-conventions";
2051
-
2052
- // src/http/tracing/pinoLogger.ts
2053
- import pino from "pino";
2054
- function pinoLogger(level) {
2055
- return pino({
2056
- level: level || "info",
2057
- formatters: {
2058
- level(label) {
2059
- return { level: label };
2060
- }
2061
- },
2062
- timestamp: pino.stdTimeFunctions.isoTime,
2063
- transport: {
2064
- target: "pino-pretty",
2065
- options: { colorize: true }
2066
- }
2067
- });
2068
- }
2069
-
2070
- // src/http/tracing/emitLoggerError.ts
2071
- var logger = pinoLogger("error");
2072
- var emitLoggerError = (req, res, errorString) => {
2073
- logs.getLogger(process.env.SERVICE_NAME ?? "unknown").emit({
2074
- severityText: "ERROR",
2075
- severityNumber: 17,
2076
- body: errorString,
2077
- attributes: {
2078
- "service.name": process.env.SERVICE_NAME,
2079
- "api.name": req.contractDetails?.name,
2080
- [SEMATTRS_HTTP_TARGET]: req.path,
2081
- [SEMATTRS_HTTP_ROUTE]: req.originalPath,
2082
- [SEMATTRS_HTTP_METHOD]: req.method,
2083
- [SEMATTRS_HTTP_STATUS_CODE]: res.statusCode
2084
- }
2085
- });
2086
- logger.error(errorString);
2087
- };
2088
-
2089
- // src/http/tracing/openTelemetryCollector.ts
2090
- import { HyperExpressInstrumentation } from "@forklaunch/opentelemetry-instrumentation-hyper-express";
2091
- import {
2092
- metrics,
2093
- trace as trace2
2094
- } from "@opentelemetry/api";
2095
- import { logs as logs2 } from "@opentelemetry/api-logs";
2096
- import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
2097
- import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
2098
- import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
2099
- import { ExpressInstrumentation } from "@opentelemetry/instrumentation-express";
2100
- import { HttpInstrumentation } from "@opentelemetry/instrumentation-http";
2101
- import { Resource } from "@opentelemetry/resources";
2102
- import { BatchLogRecordProcessor } from "@opentelemetry/sdk-logs";
2103
- import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
2104
- import { NodeSDK } from "@opentelemetry/sdk-node";
2105
- import {
2106
- ATTR_SERVICE_NAME
2107
- } from "@opentelemetry/semantic-conventions";
2108
- var OpenTelemetryCollector = class {
2109
- // scoped creation and create this in middleware when api execute. Also add correlation id
2110
- constructor(serviceName, level, metricDefinitions) {
2111
- this.serviceName = serviceName;
2112
- this.logger = pinoLogger(level ?? "info");
2113
- this.metrics = {};
2114
- for (const [metricId, metricType] of Object.entries(
2115
- metricDefinitions ?? {}
2116
- )) {
2117
- switch (metricType) {
2118
- case "counter":
2119
- this.metrics[metricId] = metrics.getMeter(this.serviceName).createCounter(metricId);
2120
- break;
2121
- case "gauge":
2122
- this.metrics[metricId] = metrics.getMeter(this.serviceName).createGauge(metricId);
2123
- break;
2124
- case "histogram":
2125
- this.metrics[metricId] = metrics.getMeter(this.serviceName).createHistogram(metricId);
2126
- break;
2127
- case "upDownCounter":
2128
- this.metrics[metricId] = metrics.getMeter(this.serviceName).createUpDownCounter(metricId);
2129
- break;
2130
- case "observableCounter":
2131
- this.metrics[metricId] = metrics.getMeter(this.serviceName).createObservableCounter(metricId);
2132
- break;
2133
- case "observableGauge":
2134
- this.metrics[metricId] = metrics.getMeter(this.serviceName).createObservableGauge(metricId);
2135
- break;
2136
- case "observableUpDownCounter":
2137
- this.metrics[metricId] = metrics.getMeter(this.serviceName).createObservableUpDownCounter(metricId);
2138
- break;
2139
- }
2140
- }
2141
- this.log("info", "OpenTelemetry (Traces + Logs + Metrics) started");
2142
- }
2143
- logger;
2144
- metrics;
2145
- log(level, msg, meta = {}) {
2146
- const activeSpan = trace2.getActiveSpan();
2147
- if (activeSpan) {
2148
- const activeSpanContext = activeSpan.spanContext();
2149
- meta.trace_id = activeSpanContext.traceId;
2150
- meta.span_id = activeSpanContext.spanId;
2151
- meta.trace_flags = activeSpanContext.traceFlags;
2152
- }
2153
- if (!meta.api_name) {
2154
- meta.api_name = activeSpan?.attributes["api.name"] ?? "undefined";
2155
- }
2156
- this.logger[level](msg);
2157
- logs2.getLogger(this.serviceName).emit({
2158
- severityText: level,
2159
- body: msg,
2160
- attributes: meta
2161
- });
2162
- }
2163
- getMetric(metricId) {
2164
- return this.metrics[metricId];
2165
- }
2166
- };
2167
- new NodeSDK({
2168
- resource: new Resource({
2169
- [ATTR_SERVICE_NAME]: process.env.SERVICE_NAME
2170
- }),
2171
- traceExporter: new OTLPTraceExporter({
2172
- url: `${process.env.OTEL_EXPORTER_OTLP_ENDPOINT ?? "http://localhost:4318"}/v1/traces`
2173
- }),
2174
- metricReader: new PeriodicExportingMetricReader({
2175
- exporter: new OTLPMetricExporter({
2176
- url: `${process.env.OTEL_EXPORTER_OTLP_ENDPOINT ?? "http://localhost:4318"}/v1/metrics`
2177
- }),
2178
- exportIntervalMillis: 5e3
2179
- }),
2180
- logRecordProcessors: [
2181
- new BatchLogRecordProcessor(
2182
- new OTLPLogExporter({
2183
- url: `${process.env.OTEL_EXPORTER_OTLP_ENDPOINT ?? "http://localhost:4318"}/v1/logs`
2184
- })
2185
- )
2186
- ],
2187
- instrumentations: [
2188
- new HttpInstrumentation(),
2189
- new ExpressInstrumentation({
2190
- requestHook: (span, info) => {
2191
- span.setAttribute(
2192
- "service.name",
2193
- process.env.SERVICE_NAME ?? "unknown"
2194
- );
2195
- span.setAttribute("api.name", info.request.contractDetails?.name);
2196
- }
2197
- }),
2198
- new HyperExpressInstrumentation()
2199
- ]
2200
- }).start();
2201
- var httpRequestsTotalCounter = metrics.getMeter(process.env.SERVICE_NAME || "unknown").createCounter("http_requests_total", {
2202
- description: "Number of HTTP requests"
2203
- });
2204
-
2205
- // src/http/middleware/response/parse.middleware.ts
2206
- import {
2207
- prettyPrintParseErrors as prettyPrintParseErrors2
2208
- } from "@forklaunch/validator";
2209
- function parse2(req, res, next) {
2210
- const { headers, responses } = res.responseSchemas;
2211
- const parsedResponse = req.schemaValidator.parse(
2212
- responses?.[res.statusCode],
2213
- res.bodyData
2214
- );
2215
- const parsedHeaders = req.schemaValidator.parse(
2216
- headers ?? req.schemaValidator.unknown,
2217
- res.getHeaders()
2218
- );
2219
- const parseErrors = [];
2220
- if (!parsedHeaders.ok) {
2221
- const headerErrors = prettyPrintParseErrors2(parsedHeaders.errors, "Header");
2222
- if (headerErrors) {
2223
- parseErrors.push(headerErrors);
2224
- }
2225
- }
2226
- if (!parsedResponse.ok) {
2227
- const responseErrors = prettyPrintParseErrors2(
2228
- parsedResponse.errors,
2229
- "Response"
2230
- );
2231
- if (responseErrors) {
2232
- parseErrors.push(responseErrors);
2233
- }
2234
- }
2235
- if (parseErrors.length > 0) {
2236
- switch (req.contractDetails.options?.responseValidation) {
2237
- default:
2238
- case "error":
2239
- next?.(new Error(`Invalid response:
2240
- ${parseErrors.join("\n\n")}`));
2241
- break;
2242
- case "warning":
2243
- console.warn(`Invalid response:
2244
- ${parseErrors.join("\n\n")}`);
2245
- break;
2246
- case "none":
2247
- break;
2248
- }
2249
- }
2250
- next?.();
2251
- }
2252
-
2253
- // src/http/utils/enrichExpressLikeSend.ts
2254
- function enrichExpressLikeSend(instance, req, res, originalSend, data, shouldEnrich) {
2255
- let parseErrorSent;
2256
- if (shouldEnrich) {
2257
- if (res.statusCode === 404) {
2258
- res.status(404);
2259
- emitLoggerError(req, res, "Not Found");
2260
- originalSend.call(instance, "Not Found");
2261
- }
2262
- parse2(req, res, (err) => {
2263
- if (err) {
2264
- let errorString = err.message;
2265
- if (res.locals.errorMessage) {
2266
- errorString += `
2267
- ------------------
2268
- ${res.locals.errorMessage}`;
2269
- }
2270
- emitLoggerError(req, res, errorString);
2271
- res.status(500);
2272
- originalSend.call(instance, errorString);
2273
- parseErrorSent = true;
2274
- }
2275
- });
2276
- }
2277
- if (!parseErrorSent) {
2278
- originalSend.call(instance, data);
2279
- }
2280
- }
2281
-
2282
- // src/http/utils/recordMetric.ts
2283
- import {
2284
- SEMATTRS_HTTP_METHOD as SEMATTRS_HTTP_METHOD3,
2285
- SEMATTRS_HTTP_ROUTE as SEMATTRS_HTTP_ROUTE3,
2286
- SEMATTRS_HTTP_STATUS_CODE as SEMATTRS_HTTP_STATUS_CODE3
2287
- } from "@opentelemetry/semantic-conventions";
2288
- function recordMetric(req, res) {
2289
- httpRequestsTotalCounter.add(1, {
2290
- "service.name": process.env.SERVICE_NAME ?? "unknown",
2291
- "api.name": req.contractDetails?.name,
2292
- [SEMATTRS_HTTP_METHOD3]: req.method,
2293
- [SEMATTRS_HTTP_ROUTE3]: req.originalPath,
2294
- [SEMATTRS_HTTP_STATUS_CODE3]: res.statusCode || 0
2295
- });
2397
+ // src/http/telemetry/metricsDefinition.ts
2398
+ function metricsDefinitions(metrics2) {
2399
+ return metrics2;
2296
2400
  }
2297
2401
  export {
2402
+ ATTR_API_NAME,
2403
+ ATTR_CORRELATION_ID,
2404
+ ATTR_HTTP_REQUEST_METHOD,
2405
+ ATTR_HTTP_RESPONSE_STATUS_CODE,
2406
+ ATTR_HTTP_ROUTE,
2407
+ ATTR_SERVICE_NAME,
2298
2408
  ForklaunchExpressLikeApplication,
2299
2409
  ForklaunchExpressLikeRouter,
2300
2410
  HTTPStatuses,
2301
2411
  OpenTelemetryCollector,
2302
2412
  delete_,
2303
- emitLoggerError,
2304
2413
  enrichExpressLikeSend,
2305
2414
  generateSwaggerDocument,
2306
2415
  get,
@@ -2308,19 +2417,22 @@ export {
2308
2417
  head,
2309
2418
  httpRequestsTotalCounter,
2310
2419
  isClientError,
2420
+ isForklaunchRequest,
2311
2421
  isForklaunchRouter,
2312
2422
  isInformational,
2313
2423
  isRedirection,
2314
2424
  isServerError,
2315
2425
  isSuccessful,
2316
2426
  isValidStatusCode,
2427
+ logger,
2428
+ metricsDefinitions,
2317
2429
  middleware,
2318
2430
  options,
2319
2431
  patch,
2320
2432
  post,
2321
2433
  put,
2322
2434
  recordMetric,
2323
- trace,
2435
+ trace2 as trace,
2324
2436
  typedHandler
2325
2437
  };
2326
2438
  //# sourceMappingURL=index.mjs.map