@openapi-typescript-infra/service 6.10.1 → 6.10.2
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/package.json +8 -2
- package/.github/workflows/codeql-analysis.yml +0 -77
- package/.github/workflows/nodejs.yml +0 -62
- package/.trunk/configs/.markdownlint.yaml +0 -10
- package/.trunk/configs/.yamllint.yaml +0 -10
- package/.trunk/trunk.yaml +0 -35
- package/.yarn/patches/confit-npm-3.0.0-eade8c7ce1.patch +0 -52
- package/.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs +0 -541
- package/.yarn/releases/yarn-3.2.3.cjs +0 -783
- package/.yarnrc.yml +0 -7
- package/CHANGELOG.md +0 -525
- package/SECURITY.md +0 -12
- package/__tests__/config.test.ts +0 -53
- package/__tests__/fake-serv/api/fake-serv.yaml +0 -48
- package/__tests__/fake-serv/config/config.json +0 -13
- package/__tests__/fake-serv/src/handlers/hello.ts +0 -17
- package/__tests__/fake-serv/src/index.ts +0 -36
- package/__tests__/fake-serv/src/routes/error.ts +0 -16
- package/__tests__/fake-serv/src/routes/index.ts +0 -19
- package/__tests__/fake-serv/src/routes/other/world.ts +0 -7
- package/__tests__/fake-serv.test.ts +0 -119
- package/__tests__/vitest.test-setup.ts +0 -15
- package/src/bin/start-service.ts +0 -32
- package/src/bootstrap.ts +0 -160
- package/src/config/index.ts +0 -124
- package/src/config/schema.ts +0 -70
- package/src/config/shortstops.ts +0 -155
- package/src/config/validation.ts +0 -23
- package/src/development/port-finder.ts +0 -67
- package/src/development/repl.ts +0 -131
- package/src/env.ts +0 -29
- package/src/error.ts +0 -47
- package/src/express-app/app.ts +0 -438
- package/src/express-app/index.ts +0 -3
- package/src/express-app/internal-server.ts +0 -43
- package/src/express-app/modules.ts +0 -10
- package/src/express-app/route-loader.ts +0 -40
- package/src/express-app/types.ts +0 -32
- package/src/hook.ts +0 -36
- package/src/index.ts +0 -9
- package/src/openapi.ts +0 -184
- package/src/telemetry/DummyExporter.ts +0 -17
- package/src/telemetry/hook-modules.ts +0 -8
- package/src/telemetry/index.ts +0 -168
- package/src/telemetry/instrumentations.ts +0 -103
- package/src/telemetry/requestLogger.ts +0 -267
- package/src/tsx.d.ts +0 -1
- package/src/types.ts +0 -223
package/src/openapi.ts
DELETED
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
|
|
3
|
-
import { merge } from 'moderndash';
|
|
4
|
-
import * as OpenApiValidator from 'express-openapi-validator';
|
|
5
|
-
import { OpenAPIFramework } from 'express-openapi-validator/dist/framework/index.js';
|
|
6
|
-
import type { Handler, Request, RequestHandler } from 'express';
|
|
7
|
-
|
|
8
|
-
import type { AnyServiceLocals, ServiceExpress, ServiceLocals } from './types.js';
|
|
9
|
-
import { getNodeEnv, isProd, isStaging } from './env.js';
|
|
10
|
-
import { getFilesInDir } from './express-app/modules.js';
|
|
11
|
-
import type { ConfigurationSchema } from './config/schema.js';
|
|
12
|
-
|
|
13
|
-
const notImplementedHandler: Handler = (req, res) => {
|
|
14
|
-
res.status(501).json({
|
|
15
|
-
code: 'NotImplemented',
|
|
16
|
-
domain: 'http',
|
|
17
|
-
message: 'This method is not yet implemented',
|
|
18
|
-
});
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
type OAPIOpts = Parameters<typeof OpenApiValidator.middleware>[0];
|
|
22
|
-
|
|
23
|
-
function stripExtension(filename: string) {
|
|
24
|
-
return filename.slice(0, filename.lastIndexOf('.'));
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export async function openApi<
|
|
28
|
-
SLocals extends AnyServiceLocals = ServiceLocals<ConfigurationSchema>,
|
|
29
|
-
>(
|
|
30
|
-
app: ServiceExpress<SLocals>,
|
|
31
|
-
rootDirectory: string,
|
|
32
|
-
codepath: string,
|
|
33
|
-
pattern: string,
|
|
34
|
-
openApiOptions?: Partial<OAPIOpts>,
|
|
35
|
-
) {
|
|
36
|
-
const apiSpec = openApiOptions?.apiSpec
|
|
37
|
-
? undefined
|
|
38
|
-
: path.resolve(rootDirectory, `./api/${app.locals.name}.yaml`);
|
|
39
|
-
if (apiSpec) {
|
|
40
|
-
app.locals.logger.debug({ apiSpec, codepath }, 'Serving OpenAPI');
|
|
41
|
-
} else if (openApiOptions?.apiSpec) {
|
|
42
|
-
app.locals.logger.debug({ codepath }, 'Serving OpenAPI');
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const basePath = path.resolve(rootDirectory, `${codepath}/handlers`);
|
|
46
|
-
// Because of the weirdness of ESM/CJS interop, and the synchronous nature of
|
|
47
|
-
// the OpenAPI resolver, we need to preload all the modules we might need
|
|
48
|
-
const moduleFiles = await getFilesInDir(
|
|
49
|
-
pattern,
|
|
50
|
-
path.resolve(rootDirectory, `${codepath}/handlers`),
|
|
51
|
-
);
|
|
52
|
-
const preloadedModules = await Promise.all(
|
|
53
|
-
moduleFiles.map((file) => {
|
|
54
|
-
const fullPath = path.join(basePath, file);
|
|
55
|
-
return import(fullPath).catch((error) => {
|
|
56
|
-
if (isStaging() || isProd()) {
|
|
57
|
-
app.locals.logger.fatal(
|
|
58
|
-
{ file: fullPath, message: error.message },
|
|
59
|
-
'Could not load potential API handler',
|
|
60
|
-
);
|
|
61
|
-
process.exit(1);
|
|
62
|
-
}
|
|
63
|
-
app.locals.logger.warn(
|
|
64
|
-
{ file: fullPath, message: error.message },
|
|
65
|
-
'Could not load potential API handler',
|
|
66
|
-
);
|
|
67
|
-
return undefined;
|
|
68
|
-
});
|
|
69
|
-
}),
|
|
70
|
-
);
|
|
71
|
-
const modulesByPath = moduleFiles.reduce<Record<string, Record<string, unknown>>>(
|
|
72
|
-
(acc, file, index) => {
|
|
73
|
-
const m = preloadedModules[index];
|
|
74
|
-
if (m) {
|
|
75
|
-
acc[`/${stripExtension(file)}`] = m;
|
|
76
|
-
}
|
|
77
|
-
return acc;
|
|
78
|
-
},
|
|
79
|
-
{},
|
|
80
|
-
);
|
|
81
|
-
|
|
82
|
-
// This is nuts, but there are testing frameworks or some other things
|
|
83
|
-
// that seem to set window in Node. The OpenAPI infra will fail under that
|
|
84
|
-
// circumstance.
|
|
85
|
-
const _window = global.window;
|
|
86
|
-
if (_window) {
|
|
87
|
-
delete (global as { window: unknown }).window;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
try {
|
|
91
|
-
app.locals.openApiSpecification = await new OpenAPIFramework({
|
|
92
|
-
apiDoc: (openApiOptions?.apiSpec ?? apiSpec) as string,
|
|
93
|
-
})
|
|
94
|
-
.initialize({ visitApi() {} })
|
|
95
|
-
.then((docs) => docs.apiDoc)
|
|
96
|
-
.catch((error) => {
|
|
97
|
-
app.locals.logger.error(error, 'Failed to parse and load OpenAPI spec');
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
const defaultOptions: OAPIOpts = {
|
|
101
|
-
apiSpec: app.locals.openApiSpecification,
|
|
102
|
-
// We force full dereferencing of the OpenAPI spec so that all `$ref` schemas are
|
|
103
|
-
// inlined before express-openapi-validator builds its request coercion rules.
|
|
104
|
-
// Without this, the coercer sees `schema: { $ref: ... }` for query parameters and
|
|
105
|
-
// cannot resolve the underlying type, so it falls back to treating the parameter
|
|
106
|
-
// as a potentially repeated value (i.e., an array). That causes AJV to reject
|
|
107
|
-
// valid requests with “should be array” errors. Dereferencing ensures the
|
|
108
|
-
// coercer sees the real primitive type/enum and stops misclassifying params.
|
|
109
|
-
//
|
|
110
|
-
// The one downside is that circular references are not supported, so if you
|
|
111
|
-
// need that, you need to be very careful with enums, and override this option.
|
|
112
|
-
//
|
|
113
|
-
// See https://github.com/cdimascio/express-openapi-validator/issues/1119
|
|
114
|
-
$refParser: {
|
|
115
|
-
mode: 'dereference',
|
|
116
|
-
},
|
|
117
|
-
ignoreUndocumented: true,
|
|
118
|
-
validateRequests: {
|
|
119
|
-
allowUnknownQueryParameters: true,
|
|
120
|
-
coerceTypes: 'array',
|
|
121
|
-
},
|
|
122
|
-
operationHandlers: {
|
|
123
|
-
basePath,
|
|
124
|
-
resolver(
|
|
125
|
-
basePath: string,
|
|
126
|
-
route: Parameters<typeof OpenApiValidator.resolvers.defaultResolver>[1],
|
|
127
|
-
) {
|
|
128
|
-
const pathKey = route.openApiRoute.substring(route.basePath.length);
|
|
129
|
-
const modulePath = path.join(basePath, pathKey);
|
|
130
|
-
|
|
131
|
-
try {
|
|
132
|
-
const module = modulesByPath[pathKey];
|
|
133
|
-
const method = module
|
|
134
|
-
? Object.keys(module).find((m) => m.toUpperCase() === route.method)
|
|
135
|
-
: undefined;
|
|
136
|
-
if (!module || !method) {
|
|
137
|
-
throw new Error(
|
|
138
|
-
`Could not find a [${route.method}] function in ${modulePath} when trying to route [${route.method} ${route.expressRoute}].`,
|
|
139
|
-
);
|
|
140
|
-
}
|
|
141
|
-
return module[method] as RequestHandler;
|
|
142
|
-
} catch (error) {
|
|
143
|
-
app.locals.logger.error(
|
|
144
|
-
{
|
|
145
|
-
error: (error as Error).message,
|
|
146
|
-
pathKey,
|
|
147
|
-
modulePath: path.relative(rootDirectory, modulePath),
|
|
148
|
-
},
|
|
149
|
-
'Failed to load API method handler',
|
|
150
|
-
);
|
|
151
|
-
return notImplementedHandler;
|
|
152
|
-
}
|
|
153
|
-
},
|
|
154
|
-
},
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
const { routing } = app.locals.config;
|
|
158
|
-
const combinedOptions = {
|
|
159
|
-
// In test mode, validate returned swagger responses. This can easily be disabled
|
|
160
|
-
// by setting validateResponses to false in the config.
|
|
161
|
-
...(getNodeEnv() === 'test'
|
|
162
|
-
? {
|
|
163
|
-
validateResponses: {
|
|
164
|
-
onError(error: Error, body: unknown, req: Request) {
|
|
165
|
-
console.log('Response body fails validation: ', error);
|
|
166
|
-
console.log('Emitted from:', req.originalUrl);
|
|
167
|
-
// eslint-disable-next-line no-console
|
|
168
|
-
console.debug(body);
|
|
169
|
-
throw error;
|
|
170
|
-
},
|
|
171
|
-
},
|
|
172
|
-
}
|
|
173
|
-
: {}),
|
|
174
|
-
...(typeof routing.openapi === 'object' ? routing.openapi : {}),
|
|
175
|
-
...openApiOptions,
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
return OpenApiValidator.middleware(merge(defaultOptions, combinedOptions));
|
|
179
|
-
} finally {
|
|
180
|
-
if (_window) {
|
|
181
|
-
(global as { window: unknown }).window = _window;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type { ExportResult } from '@opentelemetry/core';
|
|
2
|
-
import { ExportResultCode } from '@opentelemetry/core';
|
|
3
|
-
import type { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base';
|
|
4
|
-
|
|
5
|
-
export class DummySpanExporter implements SpanExporter {
|
|
6
|
-
export(spans: ReadableSpan[], resultCallback: (r: ExportResult) => void) {
|
|
7
|
-
setImmediate(() =>
|
|
8
|
-
resultCallback({
|
|
9
|
-
code: ExportResultCode.SUCCESS,
|
|
10
|
-
}),
|
|
11
|
-
);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
async shutdown() {
|
|
15
|
-
// Nothing to do
|
|
16
|
-
}
|
|
17
|
-
}
|
package/src/telemetry/index.ts
DELETED
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-proto';
|
|
2
|
-
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto';
|
|
3
|
-
import {
|
|
4
|
-
detectResources,
|
|
5
|
-
envDetector,
|
|
6
|
-
hostDetector,
|
|
7
|
-
osDetector,
|
|
8
|
-
processDetector,
|
|
9
|
-
} from '@opentelemetry/resources';
|
|
10
|
-
import { containerDetector } from '@opentelemetry/resource-detector-container';
|
|
11
|
-
import { gcpDetector } from '@opentelemetry/resource-detector-gcp';
|
|
12
|
-
import * as opentelemetry from '@opentelemetry/sdk-node';
|
|
13
|
-
import { PrometheusExporter } from '@opentelemetry/exporter-prometheus';
|
|
14
|
-
|
|
15
|
-
import type {
|
|
16
|
-
AnyServiceLocals,
|
|
17
|
-
DelayLoadServiceStartOptions,
|
|
18
|
-
RequestLocals,
|
|
19
|
-
ServiceLocals,
|
|
20
|
-
ServiceStartOptions,
|
|
21
|
-
} from '../types.js';
|
|
22
|
-
import type { ListenFn, StartAppFn } from '../express-app/index.js';
|
|
23
|
-
import type { ConfigurationSchema } from '../config/schema.js';
|
|
24
|
-
|
|
25
|
-
import { getAutoInstrumentations } from './instrumentations.js';
|
|
26
|
-
import { DummySpanExporter } from './DummyExporter.js';
|
|
27
|
-
|
|
28
|
-
// OTLP seems to only support http, and this is a default on the local network so I'm keeping it.
|
|
29
|
-
// NOSONAR
|
|
30
|
-
const baseDefaultOtlpUrl = new URL('http://otlp-exporter:4318/v1').toString();
|
|
31
|
-
|
|
32
|
-
function getSpanExporter() {
|
|
33
|
-
if (
|
|
34
|
-
!process.env.DISABLE_OLTP_EXPORTER &&
|
|
35
|
-
(['production', 'staging'].includes(process.env.APP_ENV || process.env.NODE_ENV || '') ||
|
|
36
|
-
process.env.OTLP_EXPORTER)
|
|
37
|
-
) {
|
|
38
|
-
return new OTLPTraceExporter({
|
|
39
|
-
url: process.env.OTLP_EXPORTER || `${baseDefaultOtlpUrl}/traces`,
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
if (process.env.ENABLE_CONSOLE_OLTP_EXPORTER) {
|
|
43
|
-
return new opentelemetry.tracing.ConsoleSpanExporter();
|
|
44
|
-
}
|
|
45
|
-
return new DummySpanExporter();
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function getLogExporter() {
|
|
49
|
-
if (
|
|
50
|
-
!process.env.DISABLE_OLTP_EXPORTER &&
|
|
51
|
-
(['production', 'staging'].includes(process.env.APP_ENV || process.env.NODE_ENV || '') ||
|
|
52
|
-
process.env.OTLP_EXPORTER)
|
|
53
|
-
) {
|
|
54
|
-
return new OTLPLogExporter({
|
|
55
|
-
url: process.env.OTLP_EXPORTER || `${baseDefaultOtlpUrl}/logs`,
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
if (process.env.ENABLE_CONSOLE_OLTP_EXPORTER) {
|
|
59
|
-
return new opentelemetry.logs.ConsoleLogRecordExporter();
|
|
60
|
-
}
|
|
61
|
-
return undefined;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
let prometheusExporter: PrometheusExporter | undefined;
|
|
65
|
-
let telemetrySdk: opentelemetry.NodeSDK | undefined;
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* OpenTelemetry is not friendly to the idea of stopping
|
|
69
|
-
* and starting itself, it seems. So we can only keep a global
|
|
70
|
-
* instance of the infrastructure no matter how many times
|
|
71
|
-
* you start/stop your service (this is mostly only relevant for testing).
|
|
72
|
-
* In addition, since we have to load it right away before configuration
|
|
73
|
-
* is available, we can't use configuration to decide anything.
|
|
74
|
-
*/
|
|
75
|
-
export async function startGlobalTelemetry(
|
|
76
|
-
serviceName: string,
|
|
77
|
-
customizer?:
|
|
78
|
-
| ((
|
|
79
|
-
options: Partial<opentelemetry.NodeSDKConfiguration>,
|
|
80
|
-
) => Partial<opentelemetry.NodeSDKConfiguration>)
|
|
81
|
-
| undefined,
|
|
82
|
-
) {
|
|
83
|
-
if (!prometheusExporter) {
|
|
84
|
-
const { metrics, logs, NodeSDK } = opentelemetry;
|
|
85
|
-
|
|
86
|
-
const resource = detectResources({
|
|
87
|
-
detectors: [
|
|
88
|
-
envDetector,
|
|
89
|
-
hostDetector,
|
|
90
|
-
osDetector,
|
|
91
|
-
processDetector,
|
|
92
|
-
containerDetector,
|
|
93
|
-
gcpDetector,
|
|
94
|
-
],
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
prometheusExporter = new PrometheusExporter({ preventServerStart: true });
|
|
98
|
-
const instrumentations = getAutoInstrumentations();
|
|
99
|
-
const logExporter = getLogExporter();
|
|
100
|
-
const options: Partial<opentelemetry.NodeSDKConfiguration> = {
|
|
101
|
-
serviceName,
|
|
102
|
-
autoDetectResources: false,
|
|
103
|
-
resource,
|
|
104
|
-
traceExporter: getSpanExporter(),
|
|
105
|
-
metricReader: prometheusExporter,
|
|
106
|
-
instrumentations,
|
|
107
|
-
logRecordProcessors: logExporter ? [new logs.BatchLogRecordProcessor(logExporter)] : [],
|
|
108
|
-
views: [
|
|
109
|
-
{
|
|
110
|
-
instrumentName: 'http_request_duration_seconds',
|
|
111
|
-
instrumentType: metrics.InstrumentType.HISTOGRAM,
|
|
112
|
-
aggregation: {
|
|
113
|
-
type: metrics.AggregationType.EXPLICIT_BUCKET_HISTOGRAM,
|
|
114
|
-
options: {
|
|
115
|
-
boundaries: [0.003, 0.03, 0.1, 0.3, 1.5, 10],
|
|
116
|
-
recordMinMax: true,
|
|
117
|
-
},
|
|
118
|
-
},
|
|
119
|
-
},
|
|
120
|
-
],
|
|
121
|
-
};
|
|
122
|
-
telemetrySdk = new NodeSDK(customizer ? customizer(options) : options);
|
|
123
|
-
telemetrySdk.start();
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
export function getNodeTelemetrySdk() {
|
|
128
|
-
return telemetrySdk;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
export function getGlobalPrometheusExporter() {
|
|
132
|
-
return prometheusExporter;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
export async function shutdownGlobalTelemetry() {
|
|
136
|
-
await telemetrySdk?.shutdown();
|
|
137
|
-
telemetrySdk = undefined;
|
|
138
|
-
prometheusExporter = undefined;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
export async function startWithTelemetry<
|
|
142
|
-
SLocals extends AnyServiceLocals = ServiceLocals<ConfigurationSchema>,
|
|
143
|
-
RLocals extends RequestLocals = RequestLocals,
|
|
144
|
-
>(options: DelayLoadServiceStartOptions) {
|
|
145
|
-
await startGlobalTelemetry(options.name, options.customizer);
|
|
146
|
-
|
|
147
|
-
const { startApp, listen } = (await import('../express-app/app.js')) as {
|
|
148
|
-
startApp: StartAppFn<SLocals, RLocals>;
|
|
149
|
-
listen: ListenFn<SLocals>;
|
|
150
|
-
};
|
|
151
|
-
const serviceModule = await import(options.service);
|
|
152
|
-
const service = serviceModule.default || serviceModule.service;
|
|
153
|
-
const startOptions: ServiceStartOptions<SLocals> = {
|
|
154
|
-
...options,
|
|
155
|
-
service,
|
|
156
|
-
locals: { ...options.locals } as unknown as Partial<SLocals>,
|
|
157
|
-
};
|
|
158
|
-
const app = await startApp(startOptions);
|
|
159
|
-
app.locals.logger.info('OpenTelemetry enabled');
|
|
160
|
-
|
|
161
|
-
const server = await listen(app, async () => {
|
|
162
|
-
await shutdownGlobalTelemetry();
|
|
163
|
-
app.locals.logger.info('OpenTelemetry shut down');
|
|
164
|
-
});
|
|
165
|
-
return { app, codepath: options.codepath, server };
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
export { setTelemetryHooks } from './instrumentations.js';
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import type { Instrumentation } from '@opentelemetry/instrumentation';
|
|
2
|
-
import { DnsInstrumentation } from '@opentelemetry/instrumentation-dns';
|
|
3
|
-
import type { SpanNameHook } from '@opentelemetry/instrumentation-express';
|
|
4
|
-
import { ExpressInstrumentation } from '@opentelemetry/instrumentation-express';
|
|
5
|
-
import { UndiciInstrumentation } from '@opentelemetry/instrumentation-undici';
|
|
6
|
-
import { GenericPoolInstrumentation } from '@opentelemetry/instrumentation-generic-pool';
|
|
7
|
-
import type { IgnoreIncomingRequestFunction } from '@opentelemetry/instrumentation-http';
|
|
8
|
-
import { HttpInstrumentation } from '@opentelemetry/instrumentation-http';
|
|
9
|
-
import { RedisInstrumentation } from '@opentelemetry/instrumentation-redis';
|
|
10
|
-
import { NetInstrumentation } from '@opentelemetry/instrumentation-net';
|
|
11
|
-
import { GraphQLInstrumentation } from '@opentelemetry/instrumentation-graphql';
|
|
12
|
-
import { PgInstrumentation } from '@opentelemetry/instrumentation-pg';
|
|
13
|
-
import { PinoInstrumentation } from '@opentelemetry/instrumentation-pino';
|
|
14
|
-
|
|
15
|
-
const InstrumentationMap = {
|
|
16
|
-
'@opentelemetry/instrumentation-http': HttpInstrumentation,
|
|
17
|
-
'@opentelemetry/instrumentation-dns': DnsInstrumentation,
|
|
18
|
-
'@opentelemetry/instrumentation-express': ExpressInstrumentation,
|
|
19
|
-
'@opentelemetry/instrumentation-graphql': GraphQLInstrumentation,
|
|
20
|
-
'@opentelemetry/instrumentation-undici': UndiciInstrumentation,
|
|
21
|
-
'@opentelemetry/instrumentation-generic-pool': GenericPoolInstrumentation,
|
|
22
|
-
'@opentelemetry/instrumentation-redis': RedisInstrumentation,
|
|
23
|
-
'@opentelemetry/instrumentation-net': NetInstrumentation,
|
|
24
|
-
'@opentelemetry/instrumentation-pg': PgInstrumentation,
|
|
25
|
-
'@opentelemetry/instrumentation-pino': PinoInstrumentation,
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
// Config types inferred automatically from the first argument of the constructor
|
|
29
|
-
type ConfigArg<T> = T extends new (...args: infer U) => unknown ? U[0] : never;
|
|
30
|
-
export type InstrumentationConfigMap = {
|
|
31
|
-
[Name in keyof typeof InstrumentationMap]?: ConfigArg<(typeof InstrumentationMap)[Name]>;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
let ignoreIncomingRequestHook: IgnoreIncomingRequestFunction | undefined = (req) => {
|
|
35
|
-
return req.url === '/health' || req.url === '/metrics';
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
let spanNameHook: SpanNameHook | undefined;
|
|
39
|
-
|
|
40
|
-
export function setTelemetryHooks(hooks: {
|
|
41
|
-
ignoreIncomingRequestHook?: IgnoreIncomingRequestFunction;
|
|
42
|
-
spanNameHook?: SpanNameHook;
|
|
43
|
-
}) {
|
|
44
|
-
if ('ignoreIncomingRequestHook' in hooks) {
|
|
45
|
-
ignoreIncomingRequestHook = hooks.ignoreIncomingRequestHook;
|
|
46
|
-
}
|
|
47
|
-
if ('spanNameHook' in hooks) {
|
|
48
|
-
spanNameHook = hooks.spanNameHook;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const defaultConfigs: InstrumentationConfigMap = {
|
|
53
|
-
'@opentelemetry/instrumentation-http': {
|
|
54
|
-
ignoreIncomingRequestHook(req) {
|
|
55
|
-
if (ignoreIncomingRequestHook) {
|
|
56
|
-
return ignoreIncomingRequestHook(req);
|
|
57
|
-
}
|
|
58
|
-
return false;
|
|
59
|
-
},
|
|
60
|
-
},
|
|
61
|
-
'@opentelemetry/instrumentation-express': {
|
|
62
|
-
spanNameHook(info, defaultName) {
|
|
63
|
-
if (spanNameHook) {
|
|
64
|
-
return spanNameHook(info, defaultName);
|
|
65
|
-
}
|
|
66
|
-
return defaultName;
|
|
67
|
-
},
|
|
68
|
-
ignoreLayers: [
|
|
69
|
-
'middleware - serviceLogger',
|
|
70
|
-
'middleware - jsonParser',
|
|
71
|
-
'middleware - attachServiceLocals',
|
|
72
|
-
'middleware - cookieParser',
|
|
73
|
-
'middleware - corsMiddleware',
|
|
74
|
-
'middleware - addReturnHeaders',
|
|
75
|
-
'middleware - freezeQuery',
|
|
76
|
-
'middleware - pathParamsMiddleware',
|
|
77
|
-
'middleware - metadataMiddleware',
|
|
78
|
-
'middleware - multipartMiddleware',
|
|
79
|
-
'middleware - securityMiddleware',
|
|
80
|
-
],
|
|
81
|
-
},
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
export function getAutoInstrumentations(
|
|
85
|
-
inputConfigs: InstrumentationConfigMap = defaultConfigs,
|
|
86
|
-
): Instrumentation[] {
|
|
87
|
-
const keys = Object.keys(InstrumentationMap) as (keyof typeof InstrumentationMap)[];
|
|
88
|
-
return keys
|
|
89
|
-
.map((name) => {
|
|
90
|
-
const Instance = InstrumentationMap[name];
|
|
91
|
-
// Defaults are defined by the instrumentation itself
|
|
92
|
-
const userConfig = inputConfigs[name] ?? {};
|
|
93
|
-
|
|
94
|
-
try {
|
|
95
|
-
return new Instance(userConfig);
|
|
96
|
-
} catch (e) {
|
|
97
|
-
// eslint-disable-next-line no-console
|
|
98
|
-
console.error(`Failed to load ${name}`, e);
|
|
99
|
-
return null;
|
|
100
|
-
}
|
|
101
|
-
})
|
|
102
|
-
.filter((i) => !!i) as Instrumentation[];
|
|
103
|
-
}
|