@adonisjs/otel 1.0.0-next.3 → 1.0.0-next.4

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.
@@ -0,0 +1,24 @@
1
+ import type { InstrumentationConfigMap } from '@opentelemetry/auto-instrumentations-node';
2
+ import type { HttpInstrumentationConfig } from './types/instrumentations.js';
3
+ /**
4
+ * Handles URL filtering for OpenTelemetry HTTP instrumentation.
5
+ * Determines which requests should be ignored (not traced).
6
+ */
7
+ export declare class HttpUrlFilter {
8
+ #private;
9
+ static readonly DEFAULT_IGNORED_URLS: string[];
10
+ static readonly STATIC_FILE_EXTENSIONS: Set<string>;
11
+ constructor(config?: HttpInstrumentationConfig);
12
+ /**
13
+ * Check if a URL should be ignored (not traced).
14
+ * A URL is ignored if:
15
+ * - It's a static file and ignoreStaticFiles is true (default)
16
+ * - It matches the ignored URLs list (exact or prefix pattern)
17
+ * - The user's custom hook returns true
18
+ */
19
+ shouldIgnore(url: string | undefined): boolean;
20
+ /**
21
+ * Apply HTTP instrumentation config to the merged config map
22
+ */
23
+ applyToConfig(mergedConfig: InstrumentationConfigMap, userHttpConfig?: HttpInstrumentationConfig): void;
24
+ }
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Handles URL filtering for OpenTelemetry HTTP instrumentation.
3
+ * Determines which requests should be ignored (not traced).
4
+ */
5
+ export class HttpUrlFilter {
6
+ static DEFAULT_IGNORED_URLS = [
7
+ '/health',
8
+ '/healthz',
9
+ '/internal/healthz',
10
+ '/ready',
11
+ '/readiness',
12
+ '/metrics',
13
+ '/internal/metrics',
14
+ ];
15
+ static STATIC_FILE_EXTENSIONS = new Set([
16
+ 'css',
17
+ 'js',
18
+ 'mjs',
19
+ 'cjs',
20
+ 'ts',
21
+ 'tsx',
22
+ 'jsx',
23
+ 'map',
24
+ 'woff',
25
+ 'woff2',
26
+ 'ttf',
27
+ 'eot',
28
+ 'otf',
29
+ 'svg',
30
+ 'png',
31
+ 'jpg',
32
+ 'jpeg',
33
+ 'gif',
34
+ 'webp',
35
+ 'ico',
36
+ 'avif',
37
+ 'mp4',
38
+ 'webm',
39
+ 'mp3',
40
+ 'ogg',
41
+ 'wav',
42
+ 'pdf',
43
+ ]);
44
+ #ignoredUrls;
45
+ #ignoreStaticFiles;
46
+ #userIgnoreHook;
47
+ constructor(config) {
48
+ this.#ignoredUrls = this.#buildIgnoredUrls(config);
49
+ this.#ignoreStaticFiles = config?.ignoreStaticFiles !== false;
50
+ this.#userIgnoreHook = config?.ignoreIncomingRequestHook;
51
+ }
52
+ #buildIgnoredUrls(config) {
53
+ const userUrls = config?.ignoredUrls || [];
54
+ const mergeWithDefaults = config?.mergeIgnoredUrls !== false;
55
+ if (!mergeWithDefaults)
56
+ return userUrls;
57
+ return [...HttpUrlFilter.DEFAULT_IGNORED_URLS, ...userUrls];
58
+ }
59
+ #isStaticFile(url) {
60
+ const lastSegment = url.split('/').pop() || '';
61
+ const dotIndex = lastSegment.lastIndexOf('.');
62
+ if (dotIndex === -1)
63
+ return false;
64
+ const extension = lastSegment.slice(dotIndex + 1).toLowerCase();
65
+ return HttpUrlFilter.STATIC_FILE_EXTENSIONS.has(extension);
66
+ }
67
+ #matchesPattern(urlPath, pattern) {
68
+ if (pattern.endsWith('/*')) {
69
+ const prefix = pattern.slice(0, -2);
70
+ return urlPath === prefix || urlPath.startsWith(prefix + '/');
71
+ }
72
+ return urlPath === pattern || urlPath.startsWith(pattern + '/');
73
+ }
74
+ /**
75
+ * Check if a URL should be ignored (not traced).
76
+ * A URL is ignored if:
77
+ * - It's a static file and ignoreStaticFiles is true (default)
78
+ * - It matches the ignored URLs list (exact or prefix pattern)
79
+ * - The user's custom hook returns true
80
+ */
81
+ shouldIgnore(url) {
82
+ if (!url)
83
+ return false;
84
+ const urlPath = url.split('?')[0];
85
+ if (this.#ignoreStaticFiles && this.#isStaticFile(urlPath))
86
+ return true;
87
+ if (this.#ignoredUrls.some((pattern) => this.#matchesPattern(urlPath, pattern)))
88
+ return true;
89
+ if (this.#userIgnoreHook)
90
+ return this.#userIgnoreHook({ url });
91
+ return false;
92
+ }
93
+ /**
94
+ * Apply HTTP instrumentation config to the merged config map
95
+ */
96
+ applyToConfig(mergedConfig, userHttpConfig) {
97
+ const httpKey = '@opentelemetry/instrumentation-http';
98
+ const currentConfig = mergedConfig[httpKey];
99
+ if (currentConfig && 'enabled' in currentConfig && currentConfig.enabled === false) {
100
+ return;
101
+ }
102
+ mergedConfig[httpKey] = {
103
+ ...currentConfig,
104
+ ...userHttpConfig,
105
+ ignoreIncomingRequestHook: (req) => this.shouldIgnore(req.url),
106
+ };
107
+ }
108
+ }
@@ -20,7 +20,6 @@ import type { OtelConfig } from './types/index.js';
20
20
  */
21
21
  export declare class OtelManager {
22
22
  #private;
23
- static readonly DEFAULT_IGNORED_URLS: string[];
24
23
  readonly sdk: NodeSDK;
25
24
  readonly serviceName: string;
26
25
  readonly serviceVersion: string;
package/build/src/otel.js CHANGED
@@ -5,6 +5,7 @@ import { ConsoleSpanExporter, ParentBasedSampler, SimpleSpanProcessor, TraceIdRa
5
5
  import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions';
6
6
  import { ATTR_DEPLOYMENT_ENVIRONMENT_NAME, ATTR_SERVICE_INSTANCE_ID, } from '@opentelemetry/semantic-conventions/incubating';
7
7
  import { HttpContext } from '@adonisjs/core/http';
8
+ import { HttpUrlFilter } from './http_url_filter.js';
8
9
  /**
9
10
  * OpenTelemetry SDK manager for AdonisJS.
10
11
  *
@@ -24,16 +25,6 @@ import { HttpContext } from '@adonisjs/core/http';
24
25
  * ```
25
26
  */
26
27
  export class OtelManager {
27
- static DEFAULT_IGNORED_URLS = [
28
- '/health',
29
- '/healthz',
30
- '/internal/healthz',
31
- '/ready',
32
- '/readiness',
33
- '/metrics',
34
- '/internal/metrics',
35
- '/favicon.ico',
36
- ];
37
28
  static #instance = null;
38
29
  sdk;
39
30
  serviceName;
@@ -80,32 +71,6 @@ export class OtelManager {
80
71
  ...this.#config.resourceAttributes,
81
72
  });
82
73
  }
83
- /**
84
- * Get the resolved list of ignored URLs from HTTP instrumentation config
85
- */
86
- #getIgnoredUrls(httpConfig) {
87
- const userUrls = httpConfig?.ignoredUrls || [];
88
- const mergeWithDefaults = httpConfig?.mergeIgnoredUrls !== false;
89
- if (!mergeWithDefaults) {
90
- return userUrls;
91
- }
92
- return [...OtelManager.DEFAULT_IGNORED_URLS, ...userUrls];
93
- }
94
- /**
95
- * Check if a URL should be ignored based on the ignored URLs list.
96
- * Supports exact matches and wildcard patterns (e.g., '/internal/*')
97
- */
98
- #shouldIgnoreUrl(url, ignoredUrls) {
99
- if (!url)
100
- return false;
101
- return ignoredUrls.some((pattern) => {
102
- if (pattern.endsWith('/*')) {
103
- const prefix = pattern.slice(0, -1);
104
- return url.startsWith(prefix);
105
- }
106
- return url.startsWith(pattern);
107
- });
108
- }
109
74
  /**
110
75
  * Check if a value is an Instrumentation instance
111
76
  */
@@ -185,34 +150,12 @@ export class OtelManager {
185
150
  for (const name of disabledSet) {
186
151
  mergedConfig[name] = { enabled: false };
187
152
  }
188
- this.#applyHttpInstrumentationConfig(mergedConfig, httpConfig);
153
+ const httpUrlFilter = new HttpUrlFilter(httpConfig);
154
+ httpUrlFilter.applyToConfig(mergedConfig, httpConfig);
189
155
  this.#applyPinoInstrumentationConfig(mergedConfig, pinoConfig);
190
156
  const autoInstrumentations = getNodeAutoInstrumentations(mergedConfig);
191
157
  return [...autoInstrumentations, ...customInstances];
192
158
  }
193
- /**
194
- * Apply HTTP instrumentation configuration with smart merging of ignoreIncomingRequestHook
195
- */
196
- #applyHttpInstrumentationConfig(mergedConfig, userHttpConfig) {
197
- const httpKey = '@opentelemetry/instrumentation-http';
198
- const currentConfig = mergedConfig[httpKey];
199
- if (currentConfig && 'enabled' in currentConfig && currentConfig.enabled === false) {
200
- return;
201
- }
202
- const ignoredUrls = this.#getIgnoredUrls(userHttpConfig);
203
- const userIgnoreHook = userHttpConfig?.ignoreIncomingRequestHook;
204
- mergedConfig[httpKey] = {
205
- ...currentConfig,
206
- ...userHttpConfig,
207
- ignoreIncomingRequestHook: (req) => {
208
- if (this.#shouldIgnoreUrl(req.url, ignoredUrls))
209
- return true;
210
- if (userIgnoreHook)
211
- return userIgnoreHook(req);
212
- return false;
213
- },
214
- };
215
- }
216
159
  /**
217
160
  * Apply Pino instrumentation configuration with smart merging of logHook
218
161
  */
@@ -21,9 +21,9 @@ export interface HttpInstrumentationConfig extends Omit<NonNullable<Instrumentat
21
21
  /**
22
22
  * URLs to ignore in HTTP instrumentation.
23
23
  * Merged with defaults unless `mergeIgnoredUrls` is false.
24
- * Supports wildcards: '/internal/*'
24
+ * Supports exact matches and prefix patterns ('/internal/*').
25
25
  *
26
- * @default ['/health', '/healthz', '/ready', '/metrics', '/favicon.ico', ...]
26
+ * @default ['/health', '/healthz', '/ready', '/metrics', ...]
27
27
  */
28
28
  ignoredUrls?: string[];
29
29
  /**
@@ -31,9 +31,14 @@ export interface HttpInstrumentationConfig extends Omit<NonNullable<Instrumentat
31
31
  * @default true
32
32
  */
33
33
  mergeIgnoredUrls?: boolean;
34
+ /**
35
+ * Whether to automatically ignore static files (css, js, images, fonts, etc.).
36
+ * @default true
37
+ */
38
+ ignoreStaticFiles?: boolean;
34
39
  /**
35
40
  * Custom hook to ignore specific incoming requests.
36
- * Called AFTER the ignoredUrls check.
41
+ * Called AFTER the ignoredUrls and static files checks.
37
42
  */
38
43
  ignoreIncomingRequestHook?: (request: {
39
44
  url?: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@adonisjs/otel",
3
3
  "description": "OpenTelemetry integration for AdonisJS with sensible defaults and zero-config setup",
4
- "version": "1.0.0-next.3",
4
+ "version": "1.0.0-next.4",
5
5
  "engines": {
6
6
  "node": ">=20.6.0"
7
7
  },
@@ -37,6 +37,7 @@
37
37
  "test": "c8 npm run quick:test",
38
38
  "prebuild": "npm run lint && npm run clean",
39
39
  "build": "tsc",
40
+ "checks": "npm run lint && npm run typecheck && npm run test",
40
41
  "postbuild": "npm run copy:templates",
41
42
  "release": "release-it",
42
43
  "version": "npm run build",