@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.
- package/lib/controllers/index.d.mts +6 -1
- package/lib/controllers/index.d.ts +6 -1
- package/lib/http/index.d.mts +200 -199
- package/lib/http/index.d.ts +200 -199
- package/lib/http/index.js +371 -253
- package/lib/http/index.js.map +1 -1
- package/lib/http/index.mjs +380 -268
- package/lib/http/index.mjs.map +1 -1
- package/package.json +7 -6
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: () =>
|
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(
|
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) ? {
|
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(
|
541
|
-
|
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,
|
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
|
1012
|
+
var trace2 = (_schemaValidator, path, contractDetails, ...handlers) => {
|
974
1013
|
return typedHandler(_schemaValidator, path, "trace", contractDetails, ...handlers);
|
975
1014
|
};
|
976
1015
|
|
977
|
-
// src/http/
|
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/
|
2104
|
-
|
2105
|
-
|
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,
|