apitally 1.0.0-beta.4 → 1.0.0-beta.6

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.
Files changed (99) hide show
  1. package/README.md +128 -101
  2. package/dist/_virtual/_rolldown/runtime.cjs +23 -0
  3. package/dist/activation.cjs +31 -1
  4. package/dist/activation.cjs.map +1 -1
  5. package/dist/activation.d.cts +1 -0
  6. package/dist/activation.d.ts +1 -0
  7. package/dist/activation.js +29 -2
  8. package/dist/activation.js.map +1 -1
  9. package/dist/adonisjs/middleware.cjs +1 -1
  10. package/dist/adonisjs/middleware.cjs.map +1 -1
  11. package/dist/adonisjs/middleware.js +1 -1
  12. package/dist/adonisjs/middleware.js.map +1 -1
  13. package/dist/config.cjs +6 -3
  14. package/dist/config.cjs.map +1 -1
  15. package/dist/config.js +6 -3
  16. package/dist/config.js.map +1 -1
  17. package/dist/elysia/index.cjs.map +1 -1
  18. package/dist/elysia/index.js.map +1 -1
  19. package/dist/elysia/middleware.cjs +8 -10
  20. package/dist/elysia/middleware.cjs.map +1 -1
  21. package/dist/elysia/middleware.js +8 -10
  22. package/dist/elysia/middleware.js.map +1 -1
  23. package/dist/elysia/routes.cjs +2 -1
  24. package/dist/elysia/routes.cjs.map +1 -1
  25. package/dist/elysia/routes.js +2 -1
  26. package/dist/elysia/routes.js.map +1 -1
  27. package/dist/express/index.cjs.map +1 -1
  28. package/dist/express/index.js.map +1 -1
  29. package/dist/express/install.cjs +1 -0
  30. package/dist/express/install.cjs.map +1 -1
  31. package/dist/express/install.js +2 -1
  32. package/dist/express/install.js.map +1 -1
  33. package/dist/express/middleware.cjs +1 -1
  34. package/dist/express/middleware.cjs.map +1 -1
  35. package/dist/express/middleware.js +1 -1
  36. package/dist/express/middleware.js.map +1 -1
  37. package/dist/fastify/middleware.cjs +1 -1
  38. package/dist/fastify/middleware.cjs.map +1 -1
  39. package/dist/fastify/middleware.js +1 -1
  40. package/dist/fastify/middleware.js.map +1 -1
  41. package/dist/h3/index.cjs +1 -0
  42. package/dist/h3/index.cjs.map +1 -1
  43. package/dist/h3/index.js +2 -1
  44. package/dist/h3/index.js.map +1 -1
  45. package/dist/h3/middleware.cjs +1 -1
  46. package/dist/h3/middleware.cjs.map +1 -1
  47. package/dist/h3/middleware.js +1 -1
  48. package/dist/h3/middleware.js.map +1 -1
  49. package/dist/hapi/middleware.cjs +1 -1
  50. package/dist/hapi/middleware.cjs.map +1 -1
  51. package/dist/hapi/middleware.js +1 -1
  52. package/dist/hapi/middleware.js.map +1 -1
  53. package/dist/hono/index.cjs.map +1 -1
  54. package/dist/hono/index.js.map +1 -1
  55. package/dist/hono/middleware.cjs +2 -1
  56. package/dist/hono/middleware.cjs.map +1 -1
  57. package/dist/hono/middleware.js +3 -2
  58. package/dist/hono/middleware.js.map +1 -1
  59. package/dist/index.cjs +1 -1
  60. package/dist/index.js +1 -1
  61. package/dist/koa/index.cjs +1 -0
  62. package/dist/koa/index.cjs.map +1 -1
  63. package/dist/koa/index.js +2 -1
  64. package/dist/koa/index.js.map +1 -1
  65. package/dist/koa/middleware.cjs +1 -1
  66. package/dist/koa/middleware.cjs.map +1 -1
  67. package/dist/koa/middleware.js +1 -1
  68. package/dist/koa/middleware.js.map +1 -1
  69. package/dist/metrics.cjs +0 -3
  70. package/dist/metrics.cjs.map +1 -1
  71. package/dist/metrics.js +0 -3
  72. package/dist/metrics.js.map +1 -1
  73. package/dist/providers.cjs +10 -3
  74. package/dist/providers.cjs.map +1 -1
  75. package/dist/providers.js +10 -3
  76. package/dist/providers.js.map +1 -1
  77. package/dist/redaction.cjs +9 -5
  78. package/dist/redaction.cjs.map +1 -1
  79. package/dist/redaction.js +9 -5
  80. package/dist/redaction.js.map +1 -1
  81. package/dist/requestObservation.cjs +24 -18
  82. package/dist/requestObservation.cjs.map +1 -1
  83. package/dist/requestObservation.js +24 -18
  84. package/dist/requestObservation.js.map +1 -1
  85. package/dist/spanExporter.cjs +10 -7
  86. package/dist/spanExporter.cjs.map +1 -1
  87. package/dist/spanExporter.js +10 -7
  88. package/dist/spanExporter.js.map +1 -1
  89. package/dist/spanProcessor.cjs +9 -20
  90. package/dist/spanProcessor.cjs.map +1 -1
  91. package/dist/spanProcessor.d.cts +2 -1
  92. package/dist/spanProcessor.d.ts +2 -1
  93. package/dist/spanProcessor.js +8 -21
  94. package/dist/spanProcessor.js.map +1 -1
  95. package/dist/tracing.cjs +2 -1
  96. package/dist/tracing.cjs.map +1 -1
  97. package/dist/tracing.js +2 -1
  98. package/dist/tracing.js.map +1 -1
  99. package/package.json +14 -14
package/README.md CHANGED
@@ -33,7 +33,7 @@ The SDK is an [OpenTelemetry](https://opentelemetry.io) distribution and works a
33
33
  Learn more about Apitally on our 🌎 [website](https://apitally.io) or check out the 📚 [documentation](https://docs.apitally.io).
34
34
 
35
35
  > [!IMPORTANT]
36
- > **Upgrading from 0.x?** Version 1.0 is a full rewrite with a new setup API. See the [migration guide](MIGRATION.md) for a full 0.x to 1.x mapping.
36
+ > **Upgrading from 0.x?** Version 1.0 is a full rewrite with a new setup API. See the [migration guide](MIGRATION.md) for how to upgrade.
37
37
 
38
38
  ## Key features
39
39
 
@@ -46,7 +46,7 @@ Learn more about Apitally on our 🌎 [website](https://apitally.io) or check ou
46
46
 
47
47
  ## Supported frameworks
48
48
 
49
- The SDK supports **Node.js** `>= 20.6` and **Bun** `>= 1.2.0`.
49
+ The SDK supports **Node.js** `^20.16` or `>= 22.3` and **Bun** `>= 1.2.0`.
50
50
 
51
51
  | Framework | Supported versions | Setup guide |
52
52
  | --- | --- | --- |
@@ -62,51 +62,46 @@ The SDK supports **Node.js** `>= 20.6` and **Bun** `>= 1.2.0`.
62
62
 
63
63
  \* For Hono on Cloudflare Workers use our [Serverless SDK](https://github.com/apitally/apitally-js-serverless) instead.
64
64
 
65
- Apitally also supports many other web frameworks in [Python](https://github.com/apitally/apitally-py), [Go](https://github.com/apitally/apitally-go), [.NET](https://github.com/apitally/apitally-dotnet) and [Java](https://github.com/apitally/apitally-java) via our other SDKs.
65
+ Apitally also supports many other web frameworks in [Python](https://github.com/apitally/apitally-py), [Go](https://github.com/apitally/apitally-go) and [.NET](https://github.com/apitally/apitally-dotnet) via our other SDKs.
66
66
 
67
67
  ## Getting started
68
68
 
69
69
  If you don't have an Apitally account yet, first [sign up here](https://app.apitally.io/?signup). Then create an app in the Apitally dashboard. You'll see detailed setup instructions with code snippets you can copy and paste. These also include your write token.
70
70
 
71
- To install the SDK as a dependency in your project run:
71
+ Install the SDK in your project:
72
72
 
73
73
  ```bash
74
74
  npm install apitally
75
75
  ```
76
76
 
77
- See the [SDK reference](https://docs.apitally.io/sdk-reference/javascript/v1/configuration) for all available configuration options, including how to mask sensitive data, capture request and response payloads, and more.
78
-
79
77
  ### AdonisJS
80
78
 
81
- Run the Ace add command from your application directory:
79
+ Run the Ace `add` command from your application directory:
82
80
 
83
81
  ```bash
84
82
  node ace add apitally
85
83
  ```
86
84
 
87
- The command installs and configures Apitally. It creates `config/apitally.ts`, adds the required environment declarations, registers the service provider and server middleware, and updates the conventional exception handler to capture validation and server errors.
88
-
89
- If needed, you can change settings in `config/apitally.ts`.
85
+ The command installs Apitally, asks for your write token and environment, and sets up the provider, middleware, and exception handling. To change settings later, edit `config/apitally.ts`.
90
86
 
91
87
  For further instructions, see our [setup guide for AdonisJS](https://docs.apitally.io/sdk-reference/javascript/v1/setup-guides/adonisjs).
92
88
 
93
89
  ### Elysia
94
90
 
95
- Register `apitallyPlugin()` immediately after creating the app, before routes or plugins that add routes:
91
+ Register `apitallyPlugin()` immediately after creating the app, before registering plugins and routes:
96
92
 
97
93
  ```javascript
98
94
  import { Elysia } from "elysia";
99
95
  import { apitallyPlugin } from "apitally/elysia";
100
96
 
101
- const app = new Elysia()
102
- .use(
103
- apitallyPlugin({
104
- writeToken: "your-write-token", // or set APITALLY_WRITE_TOKEN
105
- env: "dev", // optional, defaults to "dev"
106
- }),
107
- )
108
- // register plugins and routes below this point
109
- .get("/items/:id", ({ params }) => ({ id: params.id }));
97
+ const app = new Elysia().use(
98
+ apitallyPlugin({
99
+ writeToken: "your-write-token",
100
+ env: "dev",
101
+ }),
102
+ );
103
+
104
+ // register plugins and routes below this point
110
105
  ```
111
106
 
112
107
  For further instructions, see our [setup guide for Elysia](https://docs.apitally.io/sdk-reference/javascript/v1/setup-guides/elysia).
@@ -124,8 +119,8 @@ import { useApitally } from "apitally";
124
119
  const app = express();
125
120
 
126
121
  useApitally(app, {
127
- writeToken: "your-write-token", // or set APITALLY_WRITE_TOKEN
128
- env: "dev", // or "prod" etc.
122
+ writeToken: "your-write-token",
123
+ env: "dev",
129
124
  });
130
125
  ```
131
126
 
@@ -142,8 +137,8 @@ import { useApitally } from "apitally";
142
137
  const app = Fastify();
143
138
 
144
139
  useApitally(app, {
145
- writeToken: "your-write-token", // or set APITALLY_WRITE_TOKEN
146
- env: "dev", // or "prod" etc.
140
+ writeToken: "your-write-token",
141
+ env: "dev",
147
142
  });
148
143
 
149
144
  // register plugins and routes below this point
@@ -151,30 +146,9 @@ useApitally(app, {
151
146
 
152
147
  For further instructions, see our [setup guide for Fastify](https://docs.apitally.io/sdk-reference/javascript/v1/setup-guides/fastify).
153
148
 
154
- ### NestJS
155
-
156
- Call `useApitally(app)` immediately after creating the Nest application, before `app.init()` or `app.listen()`:
157
-
158
- ```javascript
159
- import { NestFactory } from "@nestjs/core";
160
- import { useApitally } from "apitally/nestjs";
161
- import { AppModule } from "./app.module.js";
162
-
163
- const app = await NestFactory.create(AppModule);
164
-
165
- useApitally(app, {
166
- writeToken: "your-write-token", // or set APITALLY_WRITE_TOKEN
167
- env: "dev", // or "prod" etc.
168
- });
169
-
170
- await app.listen(3000);
171
- ```
172
-
173
- For further instructions, see our [setup guide for NestJS](https://docs.apitally.io/sdk-reference/javascript/v1/setup-guides/nestjs).
174
-
175
149
  ### H3
176
150
 
177
- Add `apitallyPlugin()` when constructing the root H3 app:
151
+ Add `apitallyPlugin()` to the `plugins` of your root H3 app:
178
152
 
179
153
  ```javascript
180
154
  import { H3 } from "h3";
@@ -183,8 +157,8 @@ import { apitallyPlugin } from "apitally/h3";
183
157
  const app = new H3({
184
158
  plugins: [
185
159
  apitallyPlugin({
186
- writeToken: "your-write-token", // or set APITALLY_WRITE_TOKEN
187
- env: "dev", // or "prod" etc.
160
+ writeToken: "your-write-token",
161
+ env: "dev",
188
162
  }),
189
163
  ],
190
164
  });
@@ -194,7 +168,7 @@ For further instructions, see our [setup guide for H3](https://docs.apitally.io/
194
168
 
195
169
  ### Hapi
196
170
 
197
- Register `apitallyPlugin()` before calling `server.initialize()` or `server.start()`:
171
+ Register `apitallyPlugin()` immediately after creating the server, before registering other plugins and routes:
198
172
 
199
173
  ```javascript
200
174
  import Hapi from "@hapi/hapi";
@@ -204,12 +178,12 @@ const server = Hapi.server({ port: 3000 });
204
178
 
205
179
  await server.register(
206
180
  apitallyPlugin({
207
- writeToken: "your-write-token", // or set APITALLY_WRITE_TOKEN
208
- env: "dev", // or "prod" etc.
181
+ writeToken: "your-write-token",
182
+ env: "dev",
209
183
  }),
210
184
  );
211
185
 
212
- // register application plugins and routes below this point
186
+ // register other plugins and routes below this point
213
187
  await server.start();
214
188
  ```
215
189
 
@@ -226,8 +200,8 @@ import { useApitally } from "apitally";
226
200
  const app = new Hono();
227
201
 
228
202
  useApitally(app, {
229
- writeToken: "your-write-token", // or set APITALLY_WRITE_TOKEN
230
- env: "dev", // or "prod" etc.
203
+ writeToken: "your-write-token",
204
+ env: "dev",
231
205
  });
232
206
 
233
207
  // register middleware and routes below this point
@@ -240,14 +214,14 @@ For further instructions, see our [setup guide for Hono](https://docs.apitally.i
240
214
  Call `useApitally(app)` immediately after creating the app, before registering middleware and routes:
241
215
 
242
216
  ```javascript
243
- const Koa = require("koa");
244
- const { useApitally } = require("apitally");
217
+ import Koa from "koa";
218
+ import { useApitally } from "apitally";
245
219
 
246
220
  const app = new Koa();
247
221
 
248
222
  useApitally(app, {
249
- writeToken: "your-write-token", // or set APITALLY_WRITE_TOKEN
250
- env: "dev", // or "prod" etc.
223
+ writeToken: "your-write-token",
224
+ env: "dev",
251
225
  });
252
226
 
253
227
  // register middleware and routes below this point
@@ -255,11 +229,42 @@ useApitally(app, {
255
229
 
256
230
  For further instructions, see our [setup guide for Koa](https://docs.apitally.io/sdk-reference/javascript/v1/setup-guides/koa).
257
231
 
232
+ ### NestJS
233
+
234
+ Call `useApitally(app)` immediately after creating the app, before calling `app.init()` or `app.listen()`:
235
+
236
+ ```javascript
237
+ import { NestFactory } from "@nestjs/core";
238
+ import { useApitally } from "apitally/nestjs";
239
+ import { AppModule } from "./app.module.js";
240
+
241
+ const app = await NestFactory.create(AppModule);
242
+
243
+ useApitally(app, {
244
+ writeToken: "your-write-token",
245
+ env: "dev",
246
+ });
247
+
248
+ await app.listen(3000);
249
+ ```
250
+
251
+ For further instructions, see our [setup guide for NestJS](https://docs.apitally.io/sdk-reference/javascript/v1/setup-guides/nestjs).
252
+
258
253
  ## Configuration
259
254
 
260
- The write token and environment can also be provided via the `APITALLY_WRITE_TOKEN` and `APITALLY_ENV` environment variables instead of the `writeToken` and `env` options. The environment defaults to `dev`.
255
+ Pass configuration options to `useApitally()`, or to `apitallyPlugin()` for Elysia, H3, and Hapi. For AdonisJS, set them in `config/apitally.ts`. See the [SDK reference](https://docs.apitally.io/sdk-reference/javascript/v1/configuration) for all available options.
256
+
257
+ ### Environment variables
258
+
259
+ To keep configuration out of your code, set these environment variables instead of passing the matching options:
260
+
261
+ - `APITALLY_WRITE_TOKEN`: the write token (`writeToken`)
262
+ - `APITALLY_ENV`: the environment name (`env`), defaults to `dev`
263
+ - `APITALLY_DISABLED`: set to `true` to disable Apitally (`disabled`)
261
264
 
262
- By default, Apitally captures response headers but not request headers or request and response bodies. You can opt in with options:
265
+ ### Request and response data
266
+
267
+ Apitally captures response headers by default. To also capture request headers and request and response bodies, pass these options:
263
268
 
264
269
  ```javascript
265
270
  useApitally(app, {
@@ -271,45 +276,78 @@ useApitally(app, {
271
276
  });
272
277
  ```
273
278
 
274
- Sensitive values in query parameters, headers, and body fields are masked automatically based on built-in patterns, and you can add your own via the `maskQueryParams`, `maskHeaders`, and `maskBodyFields` options.
279
+ Apitally automatically masks common sensitive values, such as `password` fields and the `Authorization` header. To mask additional values, pass regular expressions to `maskQueryParams`, `maskHeaders`, and `maskBodyFields`. See our [documentation on masking](https://docs.apitally.io/sdk-reference/javascript/v1/masking) for details.
280
+
281
+ ### Exclusions and sampling
282
+
283
+ Apitally automatically excludes common health checks and static assets, such as `/healthz` and `/robots.txt`, from logs and traces. To exclude additional paths, pass regular expressions to `excludePaths`.
284
+
285
+ To capture logs and traces for only a fraction of the remaining requests, set `sampleRate`, for example to `0.1` for 10%. To decide for each request individually, pass a callback to `sampleOnRequest` or `sampleOnResponse`.
286
+
287
+ Metrics always include all requests. See our [documentation on sampling](https://docs.apitally.io/sdk-reference/javascript/v1/sampling) for details.
288
+
289
+ ### Application logs
275
290
 
276
- On high-traffic applications you can capture logs and traces for only a fraction of requests by setting `sampleRate` (e.g. `0.1` for 10%), or decide per request with the `sampleOnRequest` and `sampleOnResponse` callbacks. Metrics always count every request, regardless of sampling.
291
+ Apitally captures logs written with these loggers and correlates them with the requests they belong to:
277
292
 
278
- Application logs written via `console` and other supported loggers are captured and correlated with requests by default. Use `maskLogRecord` to transform or drop Apitally's captured copy, or opt out with `captureLogs: false`.
293
+ - `console`
294
+ - Pino
295
+ - Winston
296
+ - NestJS `Logger`
297
+ - Hapi `request.log()`
279
298
 
280
- See the [SDK reference](https://docs.apitally.io/sdk-reference/javascript/v1/configuration) for all configuration options.
299
+ To mask or drop individual log records, pass a callback to `maskLogRecord`. To turn off log capture, set `captureLogs: false`.
281
300
 
282
- ## Identifying consumers and more
301
+ See our [documentation on masking](https://docs.apitally.io/sdk-reference/javascript/v1/masking#log-record-masking-callback) for details.
283
302
 
284
- The top-level `apitally` package provides functions you can call from anywhere in your request handling code:
303
+ ## Adding context to requests
304
+
305
+ The following functions add context to the current request. Call them while handling a request, for example in a route handler, middleware, or authentication code.
306
+
307
+ ### Consumers
308
+
309
+ To associate the request with the API consumer making it, call `setConsumer`. Only `identifier` is required. You can also pass it as a string, for example `setConsumer(user.identifier)`.
285
310
 
286
311
  ```javascript
287
- import { setConsumer, setRequestAttribute, captureException } from "apitally";
312
+ import { setConsumer } from "apitally";
288
313
 
289
- // Associate the current request with an API consumer, with custom attributes
290
314
  setConsumer({
291
315
  identifier: user.identifier,
292
316
  name: user.name,
293
317
  group: user.group,
294
318
  attributes: { plan: user.plan },
295
319
  });
320
+ ```
321
+
322
+ ### Custom attributes
296
323
 
297
- // Attach a custom attribute to the current request
298
- setRequestAttribute("tenant", tenantId);
324
+ To attach custom data to the request's trace, call `setRequestAttribute`. The `sampleOnResponse`, `maskRequestBody`, and `maskResponseBody` callbacks can also read these attributes.
299
325
 
300
- // Capture a handled exception for the current request
301
- captureException(error);
326
+ ```javascript
327
+ import { setRequestAttribute } from "apitally";
328
+
329
+ setRequestAttribute("tenant.id", tenantId);
302
330
  ```
303
331
 
304
- `setConsumer()` also accepts an identifier string, for example `setConsumer(user.identifier)`.
332
+ See our [documentation on attributes](https://docs.apitally.io/sdk-reference/javascript/v1/attributes#set-custom-attributes) for details.
305
333
 
306
- For further details, check out our [documentation](https://docs.apitally.io).
334
+ ### Handled exceptions
307
335
 
308
- ## Existing OpenTelemetry setup
336
+ To capture an exception that your code handles itself, call `captureException`. Unhandled exceptions are captured automatically.
337
+
338
+ ```javascript
339
+ import { captureException } from "apitally";
340
+
341
+ try {
342
+ processOrder();
343
+ } catch (error) {
344
+ captureException(error);
345
+ }
346
+ ```
309
347
 
310
- If your app doesn't already use OpenTelemetry, you don't need to know it's there. The Apitally SDK configures OpenTelemetry automatically.
348
+ ## Existing OpenTelemetry setup
311
349
 
312
- If your app already registers its own tracer provider (e.g. via `NodeSDK`), Apitally does not replace it. Instead, you need to add the `ApitallySpanProcessor` to your provider's span processors:
350
+ If your app already registers its own tracer provider, for example with `NodeSDK`, add `ApitallySpanProcessor` to its span processors. Apitally then uses your tracer provider instead of registering its own, and keeps your existing exporters.
313
351
 
314
352
  ```javascript
315
353
  import { NodeSDK } from "@opentelemetry/sdk-node";
@@ -321,40 +359,29 @@ const sdk = new NodeSDK({
321
359
  });
322
360
  ```
323
361
 
324
- Your tracer provider's sampling settings also affect Apitally. Requests excluded by the sampler will not have request logs or traces in Apitally. Metrics still include all requests, regardless of sampling.
325
-
326
- ### Elysia's OpenTelemetry plugin
362
+ If you use version 10.x of the Sentry SDK, which registers its own tracer provider, add `ApitallySpanProcessor` to the `openTelemetrySpanProcessors` option in `Sentry.init()` and enable Sentry tracing.
327
363
 
328
- When using Elysia's `@elysia/opentelemetry` plugin, register Apitally first so it adopts the OpenTelemetry SERVER span:
329
-
330
- ```javascript
331
- import { opentelemetry } from "@elysia/opentelemetry";
332
- import { Elysia } from "elysia";
333
- import { apitallyPlugin } from "apitally/elysia";
334
-
335
- const app = new Elysia()
336
- .use(apitallyPlugin(options))
337
- .use(opentelemetry());
338
- ```
339
-
340
- ## Trusted proxies
341
-
342
- If your application runs behind a reverse proxy or load balancer, configure trusted proxies in your framework so Apitally can record the real client IP for GeoIP. Apitally uses the client IP reported by your framework. It does not read forwarding headers itself to determine the client IP.
364
+ Your tracer provider's sampler also applies to Apitally. Requests it drops have no request logs or traces in Apitally, but still count in metrics.
343
365
 
344
366
  ## Graceful shutdown
345
367
 
346
- Apitally sends telemetry in batches and automatically attempts a final export during normal process shutdown.
347
-
348
- If your code calls `process.exit()`, await `shutdown()` first to give Apitally time to send buffered telemetry. Close your server and let any remaining requests finish before doing this:
368
+ If your code calls `process.exit()`, await `shutdown()` before it so Apitally can send any remaining telemetry:
349
369
 
350
370
  ```javascript
351
371
  import { shutdown } from "apitally";
352
372
 
353
- // After the server has closed and requests have finished:
354
- await shutdown();
355
- process.exit(0);
373
+ process.on("SIGTERM", () => {
374
+ server.close(async () => {
375
+ await shutdown();
376
+ process.exit(0);
377
+ });
378
+ });
356
379
  ```
357
380
 
381
+ ## Trusted proxies
382
+
383
+ If your app runs behind a reverse proxy or load balancer, configure trusted proxies in your framework, for example with Express's `trust proxy` setting or Fastify's `trustProxy` option. Apitally records the client IP your framework reports and uses it for GeoIP.
384
+
358
385
  ## Getting help
359
386
 
360
387
  If you need help please [create a new discussion](https://github.com/orgs/apitally/discussions/categories/q-a) on GitHub or email us at [support@apitally.io](mailto:support@apitally.io). We'll get back to you as soon as possible.
@@ -0,0 +1,23 @@
1
+ //#region \0rolldown/runtime.js
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
+ key = keys[i];
11
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
+ get: ((k) => from[k]).bind(null, key),
13
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
+ });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
19
+ value: mod,
20
+ enumerable: true
21
+ }) : target, mod));
22
+ //#endregion
23
+ exports.__toESM = __toESM;
@@ -1,4 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_runtime = require("./_virtual/_rolldown/runtime.cjs");
2
3
  const require_logger = require("./logger.cjs");
3
4
  const require_config = require("./config.cjs");
4
5
  const require_packageVersion = require("./packageVersion.cjs");
@@ -7,15 +8,17 @@ const require_logCapture = require("./logCapture.cjs");
7
8
  const require_logRecordExporter = require("./logRecordExporter.cjs");
8
9
  const require_logRecordProcessor = require("./logRecordProcessor.cjs");
9
10
  const require_metrics = require("./metrics.cjs");
11
+ const require_spanProcessor = require("./spanProcessor.cjs");
10
12
  const require_providers = require("./providers.cjs");
11
13
  const require_redaction = require("./redaction.cjs");
12
14
  const require_sentry = require("./sentry.cjs");
13
15
  const require_serverErrors = require("./serverErrors.cjs");
14
- const require_spanProcessor = require("./spanProcessor.cjs");
15
16
  const require_spanExporter = require("./spanExporter.cjs");
16
17
  const require_spool = require("./spool.cjs");
17
18
  const require_startup = require("./startup.cjs");
18
19
  const require_validationErrors = require("./validationErrors.cjs");
20
+ let node_diagnostics_channel = require("node:diagnostics_channel");
21
+ node_diagnostics_channel = require_runtime.__toESM(node_diagnostics_channel, 1);
19
22
  let node_worker_threads = require("node:worker_threads");
20
23
  let _opentelemetry_api = require("@opentelemetry/api");
21
24
  let _opentelemetry_instrumentation_undici = require("@opentelemetry/instrumentation-undici");
@@ -28,6 +31,7 @@ const BATCH_MAX_QUEUE_SIZE = 2048;
28
31
  const BATCH_MAX_EXPORT_BATCH_SIZE = 512;
29
32
  const BATCH_MAX_EXPORT_BATCH_SIZE_WITH_BODY_CAPTURE = 32;
30
33
  const SIGNAL_FLUSH_TIMEOUT_MILLIS = 5e3;
34
+ const NET_SERVER_LISTEN_CHANNEL = "tracing:net.server.listen:asyncEnd";
31
35
  const ACTIVATION_STATE_KEY = Symbol.for("apitally.activation");
32
36
  function configure(options = {}) {
33
37
  const config = require_config.setConfig(options);
@@ -54,6 +58,25 @@ function activate() {
54
58
  require_logger.logError(`Apitally activation failed: ${String(error)}`);
55
59
  }
56
60
  }
61
+ function activateWhenHttpServerListens() {
62
+ const activationState = getActivationState();
63
+ if (activationState.httpServerListenHooks) return;
64
+ const onNetServerListen = (message) => {
65
+ if (message.server.listenerCount("request") > 0) activate();
66
+ };
67
+ node_diagnostics_channel.default.subscribe(NET_SERVER_LISTEN_CHANNEL, onNetServerListen);
68
+ const bun = globalThis.Bun;
69
+ const originalBunServe = bun?.serve;
70
+ if (bun && originalBunServe) bun.serve = function(...args) {
71
+ const server = originalBunServe.apply(this, args);
72
+ activate();
73
+ return server;
74
+ };
75
+ activationState.httpServerListenHooks = {
76
+ onNetServerListen,
77
+ originalBunServe
78
+ };
79
+ }
57
80
  function isActivated() {
58
81
  return getActivationState().handles !== void 0;
59
82
  }
@@ -94,6 +117,12 @@ async function resetActivation() {
94
117
  require_serverErrors.resetServerErrors();
95
118
  if (!activationState) return;
96
119
  removeLifecycleHooks(activationState);
120
+ const listenHooks = activationState.httpServerListenHooks;
121
+ if (listenHooks) {
122
+ node_diagnostics_channel.default.unsubscribe(NET_SERVER_LISTEN_CHANNEL, listenHooks.onNetServerListen);
123
+ const bun = globalThis.Bun;
124
+ if (bun && listenHooks.originalBunServe) bun.serve = listenHooks.originalBunServe;
125
+ }
97
126
  const handles = activationState.handles;
98
127
  if (!handles) return;
99
128
  handles.undiciInstrumentation?.disable();
@@ -300,6 +329,7 @@ function installBeforeExitHook(activationState) {
300
329
  }
301
330
  //#endregion
302
331
  exports.activate = activate;
332
+ exports.activateWhenHttpServerListens = activateWhenHttpServerListens;
303
333
  exports.activationFactories = activationFactories;
304
334
  exports.configure = configure;
305
335
  exports.flushTelemetry = flushTelemetry;
@@ -1 +1 @@
1
- {"version":3,"file":"activation.cjs","names":["setConfig","Spool","ExportWorker","VALIDATION_ERROR_EVENT_NAME","drainValidationErrors","SERVER_ERROR_EVENT_NAME","drainServerErrors","ROOT_CONTEXT","getDistroVersion","getConfig","isApitallySpanProcessorDeclared","resolveEnvAndCreateResource","ApitallySpanExporter","Redaction","BatchSpanProcessor","SpanPipeline","setupTracerProvider","BatchLogRecordProcessor","ApitallyLogRecordExporter","ApitallyLogRecordProcessor","createLoggerProvider","MetricsPipeline","UndiciInstrumentation","isMainThread"],"sources":["../src/activation.ts"],"sourcesContent":["import { isMainThread } from \"node:worker_threads\";\nimport { ROOT_CONTEXT } from \"@opentelemetry/api\";\nimport type { AnyValueMap } from \"@opentelemetry/api-logs\";\nimport { UndiciInstrumentation } from \"@opentelemetry/instrumentation-undici\";\nimport { BatchLogRecordProcessor, type LoggerProvider } from \"@opentelemetry/sdk-logs\";\nimport { BatchSpanProcessor } from \"@opentelemetry/sdk-trace-base\";\nimport type { NodeTracerProvider } from \"@opentelemetry/sdk-trace-node\";\nimport { type ApitallyConfig, type ApitallyOptions, getConfig, setConfig } from \"./config.js\";\nimport { ExportWorker, type ExportWorkerOptions } from \"./exportWorker.js\";\nimport {\n installConsoleCapture,\n installNestLoggerCapture,\n installPinoCapture,\n installWinstonCapture,\n} from \"./logCapture.js\";\nimport { logDebug, logError, logWarning } from \"./logger.js\";\nimport { ApitallyLogRecordExporter } from \"./logRecordExporter.js\";\nimport { ApitallyLogRecordProcessor } from \"./logRecordProcessor.js\";\nimport { MetricsPipeline } from \"./metrics.js\";\nimport { getDistroVersion } from \"./packageVersion.js\";\nimport {\n createLoggerProvider,\n resolveEnvAndCreateResource,\n setupTracerProvider,\n} from \"./providers.js\";\nimport { Redaction } from \"./redaction.js\";\nimport { installSentryEventIdRecording } from \"./sentry.js\";\nimport { drainServerErrors, resetServerErrors, SERVER_ERROR_EVENT_NAME } from \"./serverErrors.js\";\nimport { ApitallySpanExporter } from \"./spanExporter.js\";\nimport {\n isApitallySpanProcessorDeclared,\n SpanPipeline,\n setActiveSpanPipeline,\n setServerSpanActivationCallback,\n} from \"./spanProcessor.js\";\nimport { Spool } from \"./spool.js\";\nimport { emitStartupEvent, type StartupEventInfo } from \"./startup.js\";\nimport {\n drainValidationErrors,\n resetValidationErrors,\n VALIDATION_ERROR_EVENT_NAME,\n} from \"./validationErrors.js\";\n\n// The worker sends every 15 seconds, so batches only need to reach the spool\n// within that interval.\nconst BATCH_SCHEDULE_DELAY_MILLIS = 1_000;\nconst BATCH_EXPORT_TIMEOUT_MILLIS = 30_000;\nconst BATCH_MAX_QUEUE_SIZE = 2_048;\nconst BATCH_MAX_EXPORT_BATCH_SIZE = 512;\nconst BATCH_MAX_EXPORT_BATCH_SIZE_WITH_BODY_CAPTURE = 32;\nconst SIGNAL_FLUSH_TIMEOUT_MILLIS = 5_000;\n\ntype TerminationSignal = \"SIGTERM\" | \"SIGINT\";\ntype SignalListeners = Record<TerminationSignal, () => void>;\n\n// The ESM and CJS builds can load together, so a Symbol.for key gives both\n// copies the same activation state.\nconst ACTIVATION_STATE_KEY = Symbol.for(\"apitally.activation\");\n\ninterface ActivationState {\n sdkVersion: string;\n activationAttempted: boolean;\n startupEventInfo?: StartupEventInfo;\n handles?: ActivationHandles;\n runShutdown?: () => Promise<void>;\n shutdownPromise?: Promise<void>;\n beforeExitListener?: () => void;\n signalListeners?: SignalListeners;\n signalDrainPromise?: Promise<void>;\n}\n\ninterface ActivationHandles {\n spanPipeline: SpanPipeline;\n tracerProvider?: NodeTracerProvider;\n loggerProvider: LoggerProvider;\n // Consumer identifier to the hash of its last emitted consumer update, in LRU order.\n consumerUpdateHashes: Map<string, string>;\n metricsPipeline: MetricsPipeline;\n spool: Spool;\n worker: ExportWorker;\n undiciInstrumentation?: UndiciInstrumentation;\n}\n\n// Configuration stays synchronous; activate() defers timers and I/O.\nexport function configure(options: ApitallyOptions = {}): ApitallyConfig {\n const config = setConfig(options);\n // User instrumentations read this at initialization. `http/dup` adds stable\n // HTTP attributes alongside legacy ones; a user-set value takes precedence.\n if (!config.disabled && process.env.OTEL_SEMCONV_STABILITY_OPT_IN === undefined) {\n process.env.OTEL_SEMCONV_STABILITY_OPT_IN = \"http/dup\";\n }\n setServerSpanActivationCallback(activate);\n return config;\n}\n\n// The first integration registration wins so one process emits one startup event.\nexport function registerStartupEventInfo(info: StartupEventInfo): void {\n const activationState = getActivationState();\n activationState.startupEventInfo ??= info;\n}\n\n// Activation is synchronous and attempted once, so concurrent first requests\n// observe either no activation or completed activation.\nexport function activate(): void {\n const activationState = getActivationState();\n if (activationState.activationAttempted) {\n return;\n }\n activationState.activationAttempted = true;\n if (shouldSkipActivation()) {\n return;\n }\n try {\n const handles = startPipelines(activationState.startupEventInfo);\n activationState.handles = handles;\n activationState.runShutdown = () => drainAndStop(handles);\n installBeforeExitHook(activationState);\n installSignalHooks(activationState);\n } catch (error) {\n logError(`Apitally activation failed: ${String(error)}`);\n }\n}\n\nexport function isActivated(): boolean {\n return getActivationState().handles !== undefined;\n}\n\nexport function getActivationHandles(): ActivationHandles | undefined {\n return getActivationState().handles;\n}\n\nexport async function flushTelemetry(): Promise<void> {\n try {\n await getActivationState().handles?.worker.runCycle();\n } catch (error) {\n logDebug(`Error flushing telemetry: ${String(error)}`);\n }\n}\n\n// Concurrent calls and lifecycle hooks share one final drain; calls before\n// activation are no-ops.\nexport function shutdown(): Promise<void> {\n const activationState = getActivationState();\n const handles = activationState.handles;\n const runShutdown = activationState.runShutdown;\n if (!handles || !runShutdown) {\n return Promise.resolve();\n }\n if (!activationState.shutdownPromise) {\n removeLifecycleHooks(activationState);\n const signalDrainPromise = activationState.signalDrainPromise;\n activationState.shutdownPromise = (\n signalDrainPromise\n ? signalDrainPromise.then(() => handles.worker.waitForIdle())\n : Promise.resolve()\n ).then(runShutdown);\n }\n return activationState.shutdownPromise;\n}\n\n// Tests replace these factories to isolate spool files and worker timing.\nexport const activationFactories = {\n createSpool: (): Spool => new Spool(),\n createExportWorker: (options: ExportWorkerOptions): ExportWorker => new ExportWorker(options),\n};\nconst defaultFactories = { ...activationFactories };\n\n// Tests reset process-global activation and its side effects between cases.\nexport async function resetActivation(): Promise<void> {\n Object.assign(activationFactories, defaultFactories);\n const holder = globalThis as Record<symbol, ActivationState | undefined>;\n const activationState = holder[ACTIVATION_STATE_KEY];\n delete holder[ACTIVATION_STATE_KEY];\n hasWarnedAboutVersionSkew = false;\n resetValidationErrors();\n resetServerErrors();\n if (!activationState) {\n return;\n }\n removeLifecycleHooks(activationState);\n const handles = activationState.handles;\n if (!handles) {\n return;\n }\n handles.undiciInstrumentation?.disable();\n await handles.worker.stop();\n await handles.spanPipeline.shutdown();\n await handles.loggerProvider.shutdown();\n await handles.spool.clear();\n}\n\nlet hasWarnedAboutVersionSkew = false;\n\n// One root-context record per aggregated error, drained right before the log\n// processor flush so the records leave with the same export cycle.\nfunction emitErrorEvents(loggerProvider: LoggerProvider): void {\n const logger = loggerProvider.getLogger(\"apitally\");\n const events: [string, AnyValueMap[]][] = [\n [VALIDATION_ERROR_EVENT_NAME, drainValidationErrors()],\n [SERVER_ERROR_EVENT_NAME, drainServerErrors()],\n ];\n for (const [eventName, bodies] of events) {\n for (const body of bodies) {\n logger.emit({ timestamp: Date.now(), context: ROOT_CONTEXT, eventName, body });\n }\n }\n}\n\nfunction getActivationState(): ActivationState {\n const holder = globalThis as Record<symbol, ActivationState | undefined>;\n const existing = holder[ACTIVATION_STATE_KEY];\n if (existing) {\n if (!hasWarnedAboutVersionSkew && existing.sdkVersion !== getDistroVersion()) {\n hasWarnedAboutVersionSkew = true;\n logWarning(\n `Two copies of the apitally package with different versions (${existing.sdkVersion} and ${getDistroVersion()}) are loaded in this process; version ${existing.sdkVersion} stays active. To resolve this, deduplicate the apitally installation.`,\n );\n }\n return existing;\n }\n const activationState: ActivationState = {\n sdkVersion: getDistroVersion(),\n activationAttempted: false,\n };\n holder[ACTIVATION_STATE_KEY] = activationState;\n return activationState;\n}\n\nfunction shouldSkipActivation(): boolean {\n return (\n Boolean(process.env.JEST_WORKER_ID) ||\n Boolean(process.env.VITEST) ||\n process.env.NODE_ENV === \"test\" ||\n getConfig().disabled\n );\n}\n\nfunction startPipelines(startupEventInfo: StartupEventInfo | undefined): ActivationHandles {\n const config = getConfig();\n let hasUserProvider = isApitallySpanProcessorDeclared();\n const { env, resource } = resolveEnvAndCreateResource();\n const instanceId = resource.attributes[\"service.instance.id\"] as string;\n const spool = activationFactories.createSpool();\n const spanExporter = new ApitallySpanExporter({\n redaction: new Redaction(),\n env,\n instanceId,\n spool,\n maskRequestBody: config.maskRequestBody,\n maskResponseBody: config.maskResponseBody,\n });\n // Concrete options prevent OTel environment variables from changing batch\n // behavior. Each processor gets a fresh object because its config is mutated.\n const batchSpanProcessor = new BatchSpanProcessor(spanExporter, {\n scheduledDelayMillis: BATCH_SCHEDULE_DELAY_MILLIS,\n exportTimeoutMillis: BATCH_EXPORT_TIMEOUT_MILLIS,\n maxQueueSize: BATCH_MAX_QUEUE_SIZE,\n maxExportBatchSize:\n config.captureRequestBody || config.captureResponseBody\n ? BATCH_MAX_EXPORT_BATCH_SIZE_WITH_BODY_CAPTURE\n : BATCH_MAX_EXPORT_BATCH_SIZE,\n });\n const spanPipeline = new SpanPipeline(batchSpanProcessor, () => spanExporter.forceFlush());\n let tracerProvider: NodeTracerProvider | undefined;\n if (!hasUserProvider) {\n tracerProvider = setupTracerProvider(resource, [spanPipeline]);\n if (!tracerProvider) {\n hasUserProvider = true;\n logWarning(\n \"Apitally could not register its OpenTelemetry tracer provider because another provider is already registered. Only metrics and the startup event are sent to Apitally until you add ApitallySpanProcessor (exported by the apitally package) to your tracer provider's spanProcessors constructor option or the NodeSDK spanProcessors option.\",\n );\n }\n }\n setActiveSpanPipeline(spanPipeline);\n // BatchLogRecordProcessor requires its single-options-object form; positional\n // arguments create a broken processor.\n const batchLogProcessor = new BatchLogRecordProcessor({\n exporter: new ApitallyLogRecordExporter(spool),\n scheduledDelayMillis: BATCH_SCHEDULE_DELAY_MILLIS,\n exportTimeoutMillis: BATCH_EXPORT_TIMEOUT_MILLIS,\n maxQueueSize: BATCH_MAX_QUEUE_SIZE,\n maxExportBatchSize: BATCH_MAX_EXPORT_BATCH_SIZE,\n });\n const logRecordProcessor = new ApitallyLogRecordProcessor(\n batchLogProcessor,\n spanPipeline,\n config.maskLogRecord,\n );\n const loggerProvider = createLoggerProvider(resource, [logRecordProcessor]);\n const metricsPipeline = new MetricsPipeline(resource, spool);\n spanPipeline.metricsRecorder = (record) => metricsPipeline.recordFromRequest(record);\n const worker = activationFactories.createExportWorker({\n spool,\n otlpEndpoint: config.otlpEndpoint,\n writeToken: config.writeToken,\n env,\n });\n worker.flushCallbacks.push(\n () => metricsPipeline.collectAndExport(),\n () => spanPipeline.forceFlush(),\n () => emitErrorEvents(loggerProvider),\n () => batchLogProcessor.forceFlush(),\n );\n if (config.captureLogs) {\n installConsoleCapture(loggerProvider);\n installNestLoggerCapture(loggerProvider);\n installWinstonCapture(loggerProvider);\n installPinoCapture(loggerProvider);\n }\n installSentryEventIdRecording();\n let undiciInstrumentation: UndiciInstrumentation | undefined;\n if (!hasUserProvider) {\n undiciInstrumentation = createUndiciInstrumentation(config.otlpEndpoint);\n }\n worker.start();\n if (startupEventInfo) {\n emitStartupEvent(loggerProvider, startupEventInfo);\n }\n return {\n spanPipeline,\n tracerProvider,\n loggerProvider,\n consumerUpdateHashes: new Map(),\n metricsPipeline,\n spool,\n worker,\n undiciInstrumentation,\n };\n}\n\n// ignoreRequestHook prevents telemetry for export POSTs. The request origin can\n// be a string or URL at runtime despite its string type.\nfunction createUndiciInstrumentation(otlpEndpoint: string): UndiciInstrumentation {\n const endpointOrigin = new URL(otlpEndpoint).origin;\n return new UndiciInstrumentation({\n ignoreRequestHook: (request) => {\n try {\n return new URL(String(request.origin)).origin === endpointOrigin;\n } catch {\n return false;\n }\n },\n });\n}\n\nasync function drainAndStop(handles: ActivationHandles): Promise<void> {\n try {\n await handles.spanPipeline.shutdown();\n await handles.worker.finalDrain();\n await handles.loggerProvider.shutdown();\n await handles.worker.stop();\n } catch (error) {\n logWarning(`Error draining telemetry on shutdown: ${String(error)}`);\n }\n}\n\nfunction installSignalHooks(activationState: ActivationState): void {\n if (process.platform === \"win32\" || !isMainThread) {\n return;\n }\n let listeners: SignalListeners;\n listeners = {\n SIGTERM: () => handleTerminationSignal(activationState, listeners, \"SIGTERM\"),\n SIGINT: () => handleTerminationSignal(activationState, listeners, \"SIGINT\"),\n };\n activationState.signalListeners = listeners;\n try {\n process.prependOnceListener(\"SIGTERM\", listeners.SIGTERM);\n process.prependOnceListener(\"SIGINT\", listeners.SIGINT);\n } catch (error) {\n removeSignalHooks(activationState, listeners);\n logWarning(`Unable to install Apitally signal handlers: ${String(error)}`);\n }\n}\n\nfunction handleTerminationSignal(\n activationState: ActivationState,\n listeners: SignalListeners,\n signal: TerminationSignal,\n): void {\n const hasApplicationListener = process\n .listeners(signal)\n .some((listener) => listener !== listeners[signal]);\n removeSignalHooks(activationState, listeners);\n const drainPromise = flushAfterTerminationSignal(activationState, signal, hasApplicationListener);\n activationState.signalDrainPromise = drainPromise;\n void drainPromise.catch((error: unknown) => {\n logDebug(`Error flushing telemetry on ${signal}: ${String(error)}`);\n });\n}\n\nasync function flushAfterTerminationSignal(\n activationState: ActivationState,\n signal: TerminationSignal,\n hasApplicationListener: boolean,\n): Promise<void> {\n const handles = activationState.handles;\n if (!handles) {\n return;\n }\n try {\n if (activationState.shutdownPromise) {\n await waitForSignalDeadline(activationState.shutdownPromise);\n } else {\n await handles.worker.finalDrain(SIGNAL_FLUSH_TIMEOUT_MILLIS);\n }\n } catch (error) {\n logDebug(`Error flushing telemetry on ${signal}: ${String(error)}`);\n }\n if (hasApplicationListener || process.listeners(signal).length > 0) {\n return;\n }\n const exitCode = signal === \"SIGTERM\" ? 143 : 130;\n if (process.platform === \"linux\" && process.pid === 1) {\n process.exit(exitCode);\n }\n try {\n process.kill(process.pid, signal);\n } catch {\n process.exit(exitCode);\n }\n}\n\nasync function waitForSignalDeadline(promise: Promise<void>): Promise<void> {\n let deadlineTimer: NodeJS.Timeout | undefined;\n const deadlineReached = new Promise<void>((resolve) => {\n deadlineTimer = setTimeout(resolve, SIGNAL_FLUSH_TIMEOUT_MILLIS);\n });\n try {\n await Promise.race([promise, deadlineReached]);\n } finally {\n if (deadlineTimer) {\n clearTimeout(deadlineTimer);\n }\n }\n}\n\nfunction removeSignalHooks(\n activationState: ActivationState,\n listeners = activationState.signalListeners,\n): void {\n if (!listeners) {\n return;\n }\n process.removeListener(\"SIGTERM\", listeners.SIGTERM);\n process.removeListener(\"SIGINT\", listeners.SIGINT);\n if (activationState.signalListeners === listeners) {\n activationState.signalListeners = undefined;\n }\n}\n\nfunction removeLifecycleHooks(activationState: ActivationState): void {\n removeSignalHooks(activationState);\n if (activationState.beforeExitListener) {\n process.removeListener(\"beforeExit\", activationState.beforeExitListener);\n activationState.beforeExitListener = undefined;\n }\n}\n\nfunction installBeforeExitHook(activationState: ActivationState): void {\n const listener = () => {\n // The drain keeps the event loop alive until shutdown settles.\n void shutdown().catch((error: unknown) => {\n logDebug(`Error draining telemetry on shutdown: ${String(error)}`);\n });\n };\n activationState.beforeExitListener = listener;\n process.on(\"beforeExit\", listener);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"activation.cjs","names":["setConfig","Spool","ExportWorker","VALIDATION_ERROR_EVENT_NAME","drainValidationErrors","SERVER_ERROR_EVENT_NAME","drainServerErrors","ROOT_CONTEXT","getDistroVersion","getConfig","isApitallySpanProcessorDeclared","resolveEnvAndCreateResource","ApitallySpanExporter","Redaction","BatchSpanProcessor","SpanPipeline","setupTracerProvider","BatchLogRecordProcessor","ApitallyLogRecordExporter","ApitallyLogRecordProcessor","createLoggerProvider","MetricsPipeline","UndiciInstrumentation","isMainThread"],"sources":["../src/activation.ts"],"sourcesContent":["import diagnosticsChannel from \"node:diagnostics_channel\";\nimport type { Server } from \"node:net\";\nimport { isMainThread } from \"node:worker_threads\";\nimport { ROOT_CONTEXT } from \"@opentelemetry/api\";\nimport type { AnyValueMap } from \"@opentelemetry/api-logs\";\nimport { UndiciInstrumentation } from \"@opentelemetry/instrumentation-undici\";\nimport { BatchLogRecordProcessor, type LoggerProvider } from \"@opentelemetry/sdk-logs\";\nimport { BatchSpanProcessor } from \"@opentelemetry/sdk-trace-base\";\nimport type { NodeTracerProvider } from \"@opentelemetry/sdk-trace-node\";\nimport { type ApitallyConfig, type ApitallyOptions, getConfig, setConfig } from \"./config.js\";\nimport { ExportWorker, type ExportWorkerOptions } from \"./exportWorker.js\";\nimport {\n installConsoleCapture,\n installNestLoggerCapture,\n installPinoCapture,\n installWinstonCapture,\n} from \"./logCapture.js\";\nimport { logDebug, logError, logWarning } from \"./logger.js\";\nimport { ApitallyLogRecordExporter } from \"./logRecordExporter.js\";\nimport { ApitallyLogRecordProcessor } from \"./logRecordProcessor.js\";\nimport { MetricsPipeline } from \"./metrics.js\";\nimport { getDistroVersion } from \"./packageVersion.js\";\nimport {\n createLoggerProvider,\n resolveEnvAndCreateResource,\n setupTracerProvider,\n} from \"./providers.js\";\nimport { Redaction } from \"./redaction.js\";\nimport { installSentryEventIdRecording } from \"./sentry.js\";\nimport { drainServerErrors, resetServerErrors, SERVER_ERROR_EVENT_NAME } from \"./serverErrors.js\";\nimport { ApitallySpanExporter } from \"./spanExporter.js\";\nimport {\n isApitallySpanProcessorDeclared,\n SpanPipeline,\n setActiveSpanPipeline,\n setServerSpanActivationCallback,\n} from \"./spanProcessor.js\";\nimport { Spool } from \"./spool.js\";\nimport { emitStartupEvent, type StartupEventInfo } from \"./startup.js\";\nimport {\n drainValidationErrors,\n resetValidationErrors,\n VALIDATION_ERROR_EVENT_NAME,\n} from \"./validationErrors.js\";\n\n// The worker sends every 15 seconds, so batches only need to reach the spool\n// within that interval.\nconst BATCH_SCHEDULE_DELAY_MILLIS = 1_000;\nconst BATCH_EXPORT_TIMEOUT_MILLIS = 30_000;\nconst BATCH_MAX_QUEUE_SIZE = 2_048;\nconst BATCH_MAX_EXPORT_BATCH_SIZE = 512;\nconst BATCH_MAX_EXPORT_BATCH_SIZE_WITH_BODY_CAPTURE = 32;\nconst SIGNAL_FLUSH_TIMEOUT_MILLIS = 5_000;\n\n// Published by Node >= 20.16 and >= 22.3 once a net.Server is listening.\nconst NET_SERVER_LISTEN_CHANNEL = \"tracing:net.server.listen:asyncEnd\";\n\ntype TerminationSignal = \"SIGTERM\" | \"SIGINT\";\ntype BunServe = (this: unknown, ...args: unknown[]) => unknown;\ntype SignalListeners = Record<TerminationSignal, () => void>;\n\n// The ESM and CJS builds can load together, so a Symbol.for key gives both\n// copies the same activation state.\nconst ACTIVATION_STATE_KEY = Symbol.for(\"apitally.activation\");\n\ninterface ActivationState {\n sdkVersion: string;\n activationAttempted: boolean;\n startupEventInfo?: StartupEventInfo;\n handles?: ActivationHandles;\n runShutdown?: () => Promise<void>;\n shutdownPromise?: Promise<void>;\n beforeExitListener?: () => void;\n signalListeners?: SignalListeners;\n signalDrainPromise?: Promise<void>;\n httpServerListenHooks?: HttpServerListenHooks;\n}\n\ninterface HttpServerListenHooks {\n onNetServerListen: (message: unknown) => void;\n originalBunServe?: BunServe;\n}\n\ninterface ActivationHandles {\n spanPipeline: SpanPipeline;\n tracerProvider?: NodeTracerProvider;\n loggerProvider: LoggerProvider;\n // Consumer identifier to the hash of its last emitted consumer update, in LRU order.\n consumerUpdateHashes: Map<string, string>;\n metricsPipeline: MetricsPipeline;\n spool: Spool;\n worker: ExportWorker;\n undiciInstrumentation?: UndiciInstrumentation;\n}\n\n// Configuration stays synchronous; activate() defers timers and I/O.\nexport function configure(options: ApitallyOptions = {}): ApitallyConfig {\n const config = setConfig(options);\n // User instrumentations read this at initialization. `http/dup` adds stable\n // HTTP attributes alongside legacy ones; a user-set value takes precedence.\n if (!config.disabled && process.env.OTEL_SEMCONV_STABILITY_OPT_IN === undefined) {\n process.env.OTEL_SEMCONV_STABILITY_OPT_IN = \"http/dup\";\n }\n setServerSpanActivationCallback(activate);\n return config;\n}\n\n// The first integration registration wins so one process emits one startup event.\nexport function registerStartupEventInfo(info: StartupEventInfo): void {\n const activationState = getActivationState();\n activationState.startupEventInfo ??= info;\n}\n\n// Activation is synchronous and attempted once, so concurrent first requests\n// observe either no activation or completed activation.\nexport function activate(): void {\n const activationState = getActivationState();\n if (activationState.activationAttempted) {\n return;\n }\n activationState.activationAttempted = true;\n if (shouldSkipActivation()) {\n return;\n }\n try {\n const handles = startPipelines(activationState.startupEventInfo);\n activationState.handles = handles;\n activationState.runShutdown = () => drainAndStop(handles);\n installBeforeExitHook(activationState);\n installSignalHooks(activationState);\n } catch (error) {\n logError(`Apitally activation failed: ${String(error)}`);\n }\n}\n\n// For integrations without a framework startup hook. A server that started\n// listening before this call is missed and activates on its first request.\nexport function activateWhenHttpServerListens(): void {\n const activationState = getActivationState();\n if (activationState.httpServerListenHooks) {\n return;\n }\n // HTTP servers carry the app as a request listener; a cluster primary's\n // internal round-robin server has none, so only workers activate.\n const onNetServerListen = (message: unknown) => {\n if ((message as { server: Server }).server.listenerCount(\"request\") > 0) {\n activate();\n }\n };\n diagnosticsChannel.subscribe(NET_SERVER_LISTEN_CHANNEL, onNetServerListen);\n // Bun publishes no net listen channel, but every Bun HTTP server starts through Bun.serve.\n const bun = (globalThis as { Bun?: { serve: BunServe } }).Bun;\n const originalBunServe = bun?.serve;\n if (bun && originalBunServe) {\n bun.serve = function (this: unknown, ...args: unknown[]) {\n const server = originalBunServe.apply(this, args);\n activate();\n return server;\n };\n }\n activationState.httpServerListenHooks = { onNetServerListen, originalBunServe };\n}\n\nexport function isActivated(): boolean {\n return getActivationState().handles !== undefined;\n}\n\nexport function getActivationHandles(): ActivationHandles | undefined {\n return getActivationState().handles;\n}\n\nexport async function flushTelemetry(): Promise<void> {\n try {\n await getActivationState().handles?.worker.runCycle();\n } catch (error) {\n logDebug(`Error flushing telemetry: ${String(error)}`);\n }\n}\n\n// Concurrent calls and lifecycle hooks share one final drain; calls before\n// activation are no-ops.\nexport function shutdown(): Promise<void> {\n const activationState = getActivationState();\n const handles = activationState.handles;\n const runShutdown = activationState.runShutdown;\n if (!handles || !runShutdown) {\n return Promise.resolve();\n }\n if (!activationState.shutdownPromise) {\n removeLifecycleHooks(activationState);\n const signalDrainPromise = activationState.signalDrainPromise;\n activationState.shutdownPromise = (\n signalDrainPromise\n ? signalDrainPromise.then(() => handles.worker.waitForIdle())\n : Promise.resolve()\n ).then(runShutdown);\n }\n return activationState.shutdownPromise;\n}\n\n// Tests replace these factories to isolate spool files and worker timing.\nexport const activationFactories = {\n createSpool: (): Spool => new Spool(),\n createExportWorker: (options: ExportWorkerOptions): ExportWorker => new ExportWorker(options),\n};\nconst defaultFactories = { ...activationFactories };\n\n// Tests reset process-global activation and its side effects between cases.\nexport async function resetActivation(): Promise<void> {\n Object.assign(activationFactories, defaultFactories);\n const holder = globalThis as Record<symbol, ActivationState | undefined>;\n const activationState = holder[ACTIVATION_STATE_KEY];\n delete holder[ACTIVATION_STATE_KEY];\n hasWarnedAboutVersionSkew = false;\n resetValidationErrors();\n resetServerErrors();\n if (!activationState) {\n return;\n }\n removeLifecycleHooks(activationState);\n const listenHooks = activationState.httpServerListenHooks;\n if (listenHooks) {\n diagnosticsChannel.unsubscribe(NET_SERVER_LISTEN_CHANNEL, listenHooks.onNetServerListen);\n const bun = (globalThis as { Bun?: { serve: BunServe } }).Bun;\n if (bun && listenHooks.originalBunServe) {\n bun.serve = listenHooks.originalBunServe;\n }\n }\n const handles = activationState.handles;\n if (!handles) {\n return;\n }\n handles.undiciInstrumentation?.disable();\n await handles.worker.stop();\n await handles.spanPipeline.shutdown();\n await handles.loggerProvider.shutdown();\n await handles.spool.clear();\n}\n\nlet hasWarnedAboutVersionSkew = false;\n\n// One root-context record per aggregated error, drained right before the log\n// processor flush so the records leave with the same export cycle.\nfunction emitErrorEvents(loggerProvider: LoggerProvider): void {\n const logger = loggerProvider.getLogger(\"apitally\");\n const events: [string, AnyValueMap[]][] = [\n [VALIDATION_ERROR_EVENT_NAME, drainValidationErrors()],\n [SERVER_ERROR_EVENT_NAME, drainServerErrors()],\n ];\n for (const [eventName, bodies] of events) {\n for (const body of bodies) {\n logger.emit({ timestamp: Date.now(), context: ROOT_CONTEXT, eventName, body });\n }\n }\n}\n\nfunction getActivationState(): ActivationState {\n const holder = globalThis as Record<symbol, ActivationState | undefined>;\n const existing = holder[ACTIVATION_STATE_KEY];\n if (existing) {\n if (!hasWarnedAboutVersionSkew && existing.sdkVersion !== getDistroVersion()) {\n hasWarnedAboutVersionSkew = true;\n logWarning(\n `Two copies of the apitally package with different versions (${existing.sdkVersion} and ${getDistroVersion()}) are loaded in this process; version ${existing.sdkVersion} stays active. To resolve this, deduplicate the apitally installation.`,\n );\n }\n return existing;\n }\n const activationState: ActivationState = {\n sdkVersion: getDistroVersion(),\n activationAttempted: false,\n };\n holder[ACTIVATION_STATE_KEY] = activationState;\n return activationState;\n}\n\nfunction shouldSkipActivation(): boolean {\n return (\n Boolean(process.env.JEST_WORKER_ID) ||\n Boolean(process.env.VITEST) ||\n process.env.NODE_ENV === \"test\" ||\n getConfig().disabled\n );\n}\n\nfunction startPipelines(startupEventInfo: StartupEventInfo | undefined): ActivationHandles {\n const config = getConfig();\n let hasUserProvider = isApitallySpanProcessorDeclared();\n const { env, resource } = resolveEnvAndCreateResource();\n const instanceId = resource.attributes[\"service.instance.id\"] as string;\n const spool = activationFactories.createSpool();\n const spanExporter = new ApitallySpanExporter({\n redaction: new Redaction(),\n env,\n instanceId,\n spool,\n maskRequestBody: config.maskRequestBody,\n maskResponseBody: config.maskResponseBody,\n });\n // Concrete options prevent OTel environment variables from changing batch\n // behavior. Each processor gets a fresh object because its config is mutated.\n const batchSpanProcessor = new BatchSpanProcessor(spanExporter, {\n scheduledDelayMillis: BATCH_SCHEDULE_DELAY_MILLIS,\n exportTimeoutMillis: BATCH_EXPORT_TIMEOUT_MILLIS,\n maxQueueSize: BATCH_MAX_QUEUE_SIZE,\n maxExportBatchSize:\n config.captureRequestBody || config.captureResponseBody\n ? BATCH_MAX_EXPORT_BATCH_SIZE_WITH_BODY_CAPTURE\n : BATCH_MAX_EXPORT_BATCH_SIZE,\n });\n const spanPipeline = new SpanPipeline(batchSpanProcessor, () => spanExporter.forceFlush());\n let tracerProvider: NodeTracerProvider | undefined;\n if (!hasUserProvider) {\n tracerProvider = setupTracerProvider(resource, [spanPipeline]);\n if (!tracerProvider) {\n hasUserProvider = true;\n logWarning(\n \"Apitally could not register its OpenTelemetry tracer provider because another provider is already registered. Only metrics and the startup event are sent to Apitally until you add ApitallySpanProcessor (exported by the apitally package) to your tracer provider's spanProcessors constructor option or the NodeSDK spanProcessors option.\",\n );\n }\n }\n setActiveSpanPipeline(spanPipeline);\n // BatchLogRecordProcessor requires its single-options-object form; positional\n // arguments create a broken processor.\n const batchLogProcessor = new BatchLogRecordProcessor({\n exporter: new ApitallyLogRecordExporter(spool),\n scheduledDelayMillis: BATCH_SCHEDULE_DELAY_MILLIS,\n exportTimeoutMillis: BATCH_EXPORT_TIMEOUT_MILLIS,\n maxQueueSize: BATCH_MAX_QUEUE_SIZE,\n maxExportBatchSize: BATCH_MAX_EXPORT_BATCH_SIZE,\n });\n const logRecordProcessor = new ApitallyLogRecordProcessor(\n batchLogProcessor,\n spanPipeline,\n config.maskLogRecord,\n );\n const loggerProvider = createLoggerProvider(resource, [logRecordProcessor]);\n const metricsPipeline = new MetricsPipeline(resource, spool);\n spanPipeline.metricsRecorder = (record) => metricsPipeline.recordFromRequest(record);\n const worker = activationFactories.createExportWorker({\n spool,\n otlpEndpoint: config.otlpEndpoint,\n writeToken: config.writeToken,\n env,\n });\n worker.flushCallbacks.push(\n () => metricsPipeline.collectAndExport(),\n () => spanPipeline.forceFlush(),\n () => emitErrorEvents(loggerProvider),\n () => batchLogProcessor.forceFlush(),\n );\n if (config.captureLogs) {\n installConsoleCapture(loggerProvider);\n installNestLoggerCapture(loggerProvider);\n installWinstonCapture(loggerProvider);\n installPinoCapture(loggerProvider);\n }\n installSentryEventIdRecording();\n let undiciInstrumentation: UndiciInstrumentation | undefined;\n if (!hasUserProvider) {\n undiciInstrumentation = createUndiciInstrumentation(config.otlpEndpoint);\n }\n worker.start();\n if (startupEventInfo) {\n emitStartupEvent(loggerProvider, startupEventInfo);\n }\n return {\n spanPipeline,\n tracerProvider,\n loggerProvider,\n consumerUpdateHashes: new Map(),\n metricsPipeline,\n spool,\n worker,\n undiciInstrumentation,\n };\n}\n\n// ignoreRequestHook prevents telemetry for export POSTs. The request origin can\n// be a string or URL at runtime despite its string type.\nfunction createUndiciInstrumentation(otlpEndpoint: string): UndiciInstrumentation {\n const endpointOrigin = new URL(otlpEndpoint).origin;\n return new UndiciInstrumentation({\n ignoreRequestHook: (request) => {\n try {\n return new URL(String(request.origin)).origin === endpointOrigin;\n } catch {\n return false;\n }\n },\n });\n}\n\nasync function drainAndStop(handles: ActivationHandles): Promise<void> {\n try {\n await handles.spanPipeline.shutdown();\n await handles.worker.finalDrain();\n await handles.loggerProvider.shutdown();\n await handles.worker.stop();\n } catch (error) {\n logWarning(`Error draining telemetry on shutdown: ${String(error)}`);\n }\n}\n\nfunction installSignalHooks(activationState: ActivationState): void {\n if (process.platform === \"win32\" || !isMainThread) {\n return;\n }\n let listeners: SignalListeners;\n listeners = {\n SIGTERM: () => handleTerminationSignal(activationState, listeners, \"SIGTERM\"),\n SIGINT: () => handleTerminationSignal(activationState, listeners, \"SIGINT\"),\n };\n activationState.signalListeners = listeners;\n try {\n process.prependOnceListener(\"SIGTERM\", listeners.SIGTERM);\n process.prependOnceListener(\"SIGINT\", listeners.SIGINT);\n } catch (error) {\n removeSignalHooks(activationState, listeners);\n logWarning(`Unable to install Apitally signal handlers: ${String(error)}`);\n }\n}\n\nfunction handleTerminationSignal(\n activationState: ActivationState,\n listeners: SignalListeners,\n signal: TerminationSignal,\n): void {\n const hasApplicationListener = process\n .listeners(signal)\n .some((listener) => listener !== listeners[signal]);\n removeSignalHooks(activationState, listeners);\n const drainPromise = flushAfterTerminationSignal(activationState, signal, hasApplicationListener);\n activationState.signalDrainPromise = drainPromise;\n void drainPromise.catch((error: unknown) => {\n logDebug(`Error flushing telemetry on ${signal}: ${String(error)}`);\n });\n}\n\nasync function flushAfterTerminationSignal(\n activationState: ActivationState,\n signal: TerminationSignal,\n hasApplicationListener: boolean,\n): Promise<void> {\n const handles = activationState.handles;\n if (!handles) {\n return;\n }\n try {\n if (activationState.shutdownPromise) {\n await waitForSignalDeadline(activationState.shutdownPromise);\n } else {\n await handles.worker.finalDrain(SIGNAL_FLUSH_TIMEOUT_MILLIS);\n }\n } catch (error) {\n logDebug(`Error flushing telemetry on ${signal}: ${String(error)}`);\n }\n if (hasApplicationListener || process.listeners(signal).length > 0) {\n return;\n }\n const exitCode = signal === \"SIGTERM\" ? 143 : 130;\n if (process.platform === \"linux\" && process.pid === 1) {\n process.exit(exitCode);\n }\n try {\n process.kill(process.pid, signal);\n } catch {\n process.exit(exitCode);\n }\n}\n\nasync function waitForSignalDeadline(promise: Promise<void>): Promise<void> {\n let deadlineTimer: NodeJS.Timeout | undefined;\n const deadlineReached = new Promise<void>((resolve) => {\n deadlineTimer = setTimeout(resolve, SIGNAL_FLUSH_TIMEOUT_MILLIS);\n });\n try {\n await Promise.race([promise, deadlineReached]);\n } finally {\n if (deadlineTimer) {\n clearTimeout(deadlineTimer);\n }\n }\n}\n\nfunction removeSignalHooks(\n activationState: ActivationState,\n listeners = activationState.signalListeners,\n): void {\n if (!listeners) {\n return;\n }\n process.removeListener(\"SIGTERM\", listeners.SIGTERM);\n process.removeListener(\"SIGINT\", listeners.SIGINT);\n if (activationState.signalListeners === listeners) {\n activationState.signalListeners = undefined;\n }\n}\n\nfunction removeLifecycleHooks(activationState: ActivationState): void {\n removeSignalHooks(activationState);\n if (activationState.beforeExitListener) {\n process.removeListener(\"beforeExit\", activationState.beforeExitListener);\n activationState.beforeExitListener = undefined;\n }\n}\n\nfunction installBeforeExitHook(activationState: ActivationState): void {\n const listener = () => {\n // The drain keeps the event loop alive until shutdown settles.\n void shutdown().catch((error: unknown) => {\n logDebug(`Error draining telemetry on shutdown: ${String(error)}`);\n });\n };\n activationState.beforeExitListener = listener;\n process.on(\"beforeExit\", listener);\n}\n"],"mappings":"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"}
@@ -21,6 +21,7 @@ interface ActivationHandles {
21
21
  export declare function configure(options?: ApitallyOptions): ApitallyConfig;
22
22
  export declare function registerStartupEventInfo(info: StartupEventInfo): void;
23
23
  export declare function activate(): void;
24
+ export declare function activateWhenHttpServerListens(): void;
24
25
  export declare function isActivated(): boolean;
25
26
  export declare function getActivationHandles(): ActivationHandles | undefined;
26
27
  export declare function flushTelemetry(): Promise<void>;
@@ -21,6 +21,7 @@ interface ActivationHandles {
21
21
  export declare function configure(options?: ApitallyOptions): ApitallyConfig;
22
22
  export declare function registerStartupEventInfo(info: StartupEventInfo): void;
23
23
  export declare function activate(): void;
24
+ export declare function activateWhenHttpServerListens(): void;
24
25
  export declare function isActivated(): boolean;
25
26
  export declare function getActivationHandles(): ActivationHandles | undefined;
26
27
  export declare function flushTelemetry(): Promise<void>;