@openapi-typescript-infra/service 4.17.0 → 4.19.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openapi-typescript-infra/service",
3
- "version": "4.17.0",
3
+ "version": "4.19.0",
4
4
  "description": "An opinionated framework for building configuration driven services - web, api, or ob. Uses OpenAPI, pino logging, express, confit, Typescript and vitest.",
5
5
  "main": "build/index.js",
6
6
  "scripts": {
@@ -59,26 +59,22 @@
59
59
  "homepage": "https://github.com/openapi-typescript-infra/service#readme",
60
60
  "dependencies": {
61
61
  "@godaddy/terminus": "^4.12.1",
62
- "@opentelemetry/api": "^1.7.0",
63
- "@opentelemetry/exporter-prometheus": "^0.43.0",
64
- "@opentelemetry/exporter-trace-otlp-proto": "^0.43.0",
65
- "@opentelemetry/instrumentation": "^0.43.0",
66
- "@opentelemetry/instrumentation-dns": "^0.32.4",
67
- "@opentelemetry/instrumentation-express": "^0.33.3",
68
- "@opentelemetry/instrumentation-generic-pool": "^0.32.4",
69
- "@opentelemetry/instrumentation-graphql": "^0.35.2",
70
- "@opentelemetry/instrumentation-http": "^0.43.0",
71
- "@opentelemetry/instrumentation-ioredis": "^0.35.3",
72
- "@opentelemetry/instrumentation-net": "^0.32.4",
73
- "@opentelemetry/instrumentation-pg": "^0.36.2",
74
- "@opentelemetry/instrumentation-pino": "^0.34.4",
75
- "@opentelemetry/resource-detector-container": "^0.3.4",
76
- "@opentelemetry/resource-detector-gcp": "^0.29.4",
77
- "@opentelemetry/resources": "^1.18.1",
78
- "@opentelemetry/sdk-metrics": "^1.18.1",
79
- "@opentelemetry/sdk-node": "^0.43.0",
80
- "@opentelemetry/sdk-trace-base": "^1.18.1",
81
- "@opentelemetry/semantic-conventions": "^1.18.1",
62
+ "@opentelemetry/api": "^1.8.0",
63
+ "@opentelemetry/exporter-prometheus": "^0.51.0",
64
+ "@opentelemetry/instrumentation-dns": "^0.36.0",
65
+ "@opentelemetry/instrumentation-express": "^0.38.0",
66
+ "@opentelemetry/instrumentation-generic-pool": "^0.36.0",
67
+ "@opentelemetry/instrumentation-graphql": "^0.40.0",
68
+ "@opentelemetry/instrumentation-http": "^0.51.0",
69
+ "@opentelemetry/instrumentation-ioredis": "^0.40.0",
70
+ "@opentelemetry/instrumentation-net": "^0.36.0",
71
+ "@opentelemetry/instrumentation-pg": "^0.41.0",
72
+ "@opentelemetry/instrumentation-pino": "^0.38.0",
73
+ "@opentelemetry/instrumentation-undici": "^0.2.0",
74
+ "@opentelemetry/resource-detector-container": "^0.3.9",
75
+ "@opentelemetry/resource-detector-gcp": "^0.29.9",
76
+ "@opentelemetry/sdk-node": "^0.51.0",
77
+ "@opentelemetry/semantic-conventions": "^1.24.0",
82
78
  "@sesamecare-oss/confit": "^2.2.1",
83
79
  "@sesamecare-oss/opentelemetry-node-metrics": "^1.0.1",
84
80
  "ajv": "^8.12.0",
@@ -89,7 +85,6 @@
89
85
  "glob": "^8.1.0",
90
86
  "lodash": "^4.17.21",
91
87
  "minimist": "^1.2.8",
92
- "opentelemetry-instrumentation-fetch-node": "^1.2.0",
93
88
  "pino": "^8.21.0",
94
89
  "read-pkg-up": "^7.0.1",
95
90
  "request-ip": "^3.3.0"
@@ -97,7 +92,7 @@
97
92
  "devDependencies": {
98
93
  "@commitlint/cli": "^19.3.0",
99
94
  "@commitlint/config-conventional": "^19.2.2",
100
- "@openapi-typescript-infra/coconfig": "^4.3.0",
95
+ "@openapi-typescript-infra/coconfig": "^4.4.0",
101
96
  "@semantic-release/commit-analyzer": "^12.0.0",
102
97
  "@semantic-release/exec": "^6.0.3",
103
98
  "@semantic-release/github": "^10.0.3",
@@ -1,4 +1,5 @@
1
1
  import fs from 'fs';
2
+ import net from 'net';
2
3
  import path from 'path';
3
4
 
4
5
  import {
@@ -10,6 +11,7 @@ import {
10
11
  } from '@sesamecare-oss/confit';
11
12
 
12
13
  import { findPort } from '../development/port-finder';
14
+ import { isTest } from '../env';
13
15
 
14
16
  import type { ConfigurationSchema } from './schema';
15
17
 
@@ -55,6 +57,28 @@ async function addDefaultConfiguration<Config extends ConfigurationSchema = Conf
55
57
  }
56
58
  }
57
59
 
60
+ async function getEphemeralPort(): Promise<number> {
61
+ return new Promise((resolve, reject) => {
62
+ const server = net.createServer();
63
+
64
+ server.listen(0, () => {
65
+ const address = server.address();
66
+ if (typeof address === 'string' || !address) {
67
+ reject(new Error('Invalid address'));
68
+ return;
69
+ }
70
+ const port = address.port; // Retrieve the ephemeral port
71
+ server.close((err) => {
72
+ if (err) {
73
+ reject(err);
74
+ } else {
75
+ resolve(port);
76
+ }
77
+ });
78
+ });
79
+ });
80
+ }
81
+
58
82
  export interface ServiceConfigurationSpec {
59
83
  // The LAST configuration is the most "specific" - if a configuration value
60
84
  // exists in all directories, the last one wins
@@ -93,7 +117,8 @@ export async function loadConfiguration<Config extends ConfigurationSchema>({
93
117
  // configured to auto-select
94
118
  const serverConfig = loaded.get().server;
95
119
  if (serverConfig.port === 0) {
96
- const port = (await findPort(8001)) as number;
120
+ const portPromise: Promise<number> = isTest() ? getEphemeralPort() : findPort(8001);
121
+ const port = await portPromise;
97
122
  const store = loaded.get();
98
123
  store.server = store.server || {};
99
124
  store.server.port = port;
@@ -36,5 +36,5 @@ export async function findPort(start: number) {
36
36
  return p;
37
37
  }
38
38
  }
39
- return null;
39
+ return 0;
40
40
  }
@@ -1,4 +1,3 @@
1
- import { diag, DiagConsoleLogger, DiagLogLevel } from '@opentelemetry/api';
2
1
  import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto';
3
2
  import * as opentelemetry from '@opentelemetry/sdk-node';
4
3
  import { PrometheusExporter } from '@opentelemetry/exporter-prometheus';
@@ -13,11 +12,11 @@ import type {
13
12
  import type { ListenFn, StartAppFn } from '../express-app/index';
14
13
  import type { ConfigurationSchema } from '../config/schema';
15
14
 
16
- import { getAutoInstrumentations, getResourceDetectors } from './instrumentations';
15
+ import { getAutoInstrumentations, getResource } from './instrumentations';
17
16
  import { DummySpanExporter } from './DummyExporter';
18
17
 
19
18
  // For troubleshooting, set the log level to DiagLogLevel.DEBUG
20
- diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.INFO);
19
+ opentelemetry.api.diag.setLogger(new (opentelemetry.api.DiagConsoleLogger)(), opentelemetry.api.DiagLogLevel.INFO);
21
20
 
22
21
  function getExporter() {
23
22
  if (
@@ -46,16 +45,17 @@ let telemetrySdk: opentelemetry.NodeSDK | undefined;
46
45
  * In addition, since we have to load it right away before configuration
47
46
  * is available, we can't use configuration to decide anything.
48
47
  */
49
- export function startGlobalTelemetry(serviceName: string) {
48
+ export async function startGlobalTelemetry(serviceName: string) {
50
49
  if (!prometheusExporter) {
51
50
  prometheusExporter = new PrometheusExporter({ preventServerStart: true });
51
+ const instrumentations = getAutoInstrumentations();
52
52
  telemetrySdk = new opentelemetry.NodeSDK({
53
53
  serviceName,
54
- autoDetectResources: true,
54
+ autoDetectResources: false,
55
55
  traceExporter: getExporter(),
56
- resourceDetectors: getResourceDetectors(),
56
+ resource: await getResource(),
57
57
  metricReader: prometheusExporter,
58
- instrumentations: [getAutoInstrumentations()],
58
+ instrumentations,
59
59
  views: [
60
60
  new opentelemetry.metrics.View({
61
61
  instrumentName: 'http_request_duration_seconds',
@@ -76,7 +76,6 @@ export function getGlobalPrometheusExporter() {
76
76
  }
77
77
 
78
78
  export async function shutdownGlobalTelemetry() {
79
- await prometheusExporter?.shutdown();
80
79
  await telemetrySdk?.shutdown();
81
80
  telemetrySdk = undefined;
82
81
  prometheusExporter = undefined;
@@ -86,7 +85,7 @@ export async function startWithTelemetry<
86
85
  SLocals extends AnyServiceLocals = ServiceLocals<ConfigurationSchema>,
87
86
  RLocals extends RequestLocals = RequestLocals,
88
87
  >(options: DelayLoadServiceStartOptions) {
89
- startGlobalTelemetry(options.name);
88
+ await startGlobalTelemetry(options.name);
90
89
 
91
90
  // eslint-disable-next-line import/no-unresolved, @typescript-eslint/no-var-requires
92
91
  const { startApp, listen } = require('../express-app/app.js') as {
@@ -1,7 +1,7 @@
1
1
  import type { Instrumentation } from '@opentelemetry/instrumentation';
2
2
  import { DnsInstrumentation } from '@opentelemetry/instrumentation-dns';
3
3
  import { ExpressInstrumentation } from '@opentelemetry/instrumentation-express';
4
- import { FetchInstrumentation } from 'opentelemetry-instrumentation-fetch-node';
4
+ import { UndiciInstrumentation } from '@opentelemetry/instrumentation-undici';
5
5
  import { GenericPoolInstrumentation } from '@opentelemetry/instrumentation-generic-pool';
6
6
  import { HttpInstrumentation } from '@opentelemetry/instrumentation-http';
7
7
  import { IORedisInstrumentation } from '@opentelemetry/instrumentation-ioredis';
@@ -11,8 +11,9 @@ import { PinoInstrumentation } from '@opentelemetry/instrumentation-pino';
11
11
  import { containerDetector } from '@opentelemetry/resource-detector-container';
12
12
  import { gcpDetector } from '@opentelemetry/resource-detector-gcp';
13
13
  import {
14
- Detector,
15
- DetectorSync,
14
+ Resource,
15
+ detectResources,
16
+ detectResourcesSync,
16
17
  envDetectorSync,
17
18
  hostDetectorSync,
18
19
  osDetectorSync,
@@ -23,7 +24,7 @@ const InstrumentationMap = {
23
24
  '@opentelemetry/instrumentation-http': HttpInstrumentation,
24
25
  '@opentelemetry/instrumentation-dns': DnsInstrumentation,
25
26
  '@opentelemetry/instrumentation-express': ExpressInstrumentation,
26
- 'opentelemetry-instrumentation-fetch-node': FetchInstrumentation,
27
+ '@opentelemetry/instrumentation-undici': UndiciInstrumentation,
27
28
  '@opentelemetry/instrumentation-generic-pool': GenericPoolInstrumentation,
28
29
  '@opentelemetry/instrumentation-ioredis': IORedisInstrumentation,
29
30
  '@opentelemetry/instrumentation-net': NetInstrumentation,
@@ -58,13 +59,22 @@ export function getAutoInstrumentations(
58
59
  .filter((i) => !!i) as Instrumentation[];
59
60
  }
60
61
 
61
- export function getResourceDetectors(): (Detector | DetectorSync)[] {
62
- return [
63
- containerDetector,
62
+ // Async function to get combined resources
63
+ export async function getResource(): Promise<Resource> {
64
+ const syncDetectors = [
64
65
  envDetectorSync,
65
66
  hostDetectorSync,
66
67
  osDetectorSync,
67
68
  processDetectorSync,
69
+ ];
70
+ const asyncDetectors = [
71
+ containerDetector,
68
72
  gcpDetector,
69
73
  ];
74
+
75
+ const asyncResources = await detectResources({ detectors: asyncDetectors });
76
+ const syncResources = detectResourcesSync({ detectors: syncDetectors });
77
+
78
+ // Combine async and sync resources
79
+ return syncResources.merge(asyncResources);
70
80
  }