apitally 1.0.0-beta.3 → 1.0.0-beta.5
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/README.md +120 -120
- package/dist/activation.cjs +2 -2
- package/dist/activation.cjs.map +1 -1
- package/dist/activation.js +2 -2
- package/dist/activation.js.map +1 -1
- package/dist/config.cjs +6 -3
- package/dist/config.cjs.map +1 -1
- package/dist/config.js +6 -3
- package/dist/config.js.map +1 -1
- package/dist/exportWorker.cjs +6 -5
- package/dist/exportWorker.cjs.map +1 -1
- package/dist/exportWorker.js +6 -5
- package/dist/exportWorker.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/metrics.cjs +164 -35
- package/dist/metrics.cjs.map +1 -1
- package/dist/metrics.d.cts +4 -6
- package/dist/metrics.d.ts +4 -6
- package/dist/metrics.js +165 -36
- package/dist/metrics.js.map +1 -1
- package/dist/providers.cjs +10 -11
- package/dist/providers.cjs.map +1 -1
- package/dist/providers.d.cts +0 -2
- package/dist/providers.d.ts +0 -2
- package/dist/providers.js +11 -11
- package/dist/providers.js.map +1 -1
- package/dist/redaction.cjs +9 -5
- package/dist/redaction.cjs.map +1 -1
- package/dist/redaction.js +9 -5
- package/dist/redaction.js.map +1 -1
- package/dist/requestObservation.cjs +22 -17
- package/dist/requestObservation.cjs.map +1 -1
- package/dist/requestObservation.js +22 -17
- package/dist/requestObservation.js.map +1 -1
- package/dist/serverErrors.cjs +30 -28
- package/dist/serverErrors.cjs.map +1 -1
- package/dist/serverErrors.js +30 -28
- package/dist/serverErrors.js.map +1 -1
- package/dist/spanExporter.cjs +10 -7
- package/dist/spanExporter.cjs.map +1 -1
- package/dist/spanExporter.js +10 -7
- package/dist/spanExporter.js.map +1 -1
- package/dist/spanProcessor.cjs +9 -20
- package/dist/spanProcessor.cjs.map +1 -1
- package/dist/spanProcessor.d.cts +2 -1
- package/dist/spanProcessor.d.ts +2 -1
- package/dist/spanProcessor.js +8 -21
- package/dist/spanProcessor.js.map +1 -1
- package/dist/spool.cjs +5 -0
- package/dist/spool.cjs.map +1 -1
- package/dist/spool.d.cts +2 -1
- package/dist/spool.d.ts +2 -1
- package/dist/spool.js +5 -0
- package/dist/spool.js.map +1 -1
- package/dist/validationErrors.cjs +29 -26
- package/dist/validationErrors.cjs.map +1 -1
- package/dist/validationErrors.js +29 -26
- package/dist/validationErrors.js.map +1 -1
- package/package.json +3 -3
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
|
|
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
|
|
|
@@ -62,13 +62,13 @@ 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)
|
|
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
|
-
|
|
71
|
+
Install the SDK in your project:
|
|
72
72
|
|
|
73
73
|
```bash
|
|
74
74
|
npm install apitally
|
|
@@ -78,35 +78,32 @@ See the [SDK reference](https://docs.apitally.io/sdk-reference/javascript/v1/con
|
|
|
78
78
|
|
|
79
79
|
### AdonisJS
|
|
80
80
|
|
|
81
|
-
Run the Ace add command from your application directory:
|
|
81
|
+
Run the Ace `add` command from your application directory:
|
|
82
82
|
|
|
83
83
|
```bash
|
|
84
84
|
node ace add apitally
|
|
85
85
|
```
|
|
86
86
|
|
|
87
|
-
The command installs
|
|
88
|
-
|
|
89
|
-
If needed, you can change settings in `config/apitally.ts`.
|
|
87
|
+
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
88
|
|
|
91
89
|
For further instructions, see our [setup guide for AdonisJS](https://docs.apitally.io/sdk-reference/javascript/v1/setup-guides/adonisjs).
|
|
92
90
|
|
|
93
91
|
### Elysia
|
|
94
92
|
|
|
95
|
-
Register `apitallyPlugin()` immediately after creating the app, before
|
|
93
|
+
Register `apitallyPlugin()` immediately after creating the app, before registering plugins and routes:
|
|
96
94
|
|
|
97
95
|
```javascript
|
|
98
96
|
import { Elysia } from "elysia";
|
|
99
97
|
import { apitallyPlugin } from "apitally/elysia";
|
|
100
98
|
|
|
101
|
-
const app = new Elysia()
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
.get("/items/:id", ({ params }) => ({ id: params.id }));
|
|
99
|
+
const app = new Elysia().use(
|
|
100
|
+
apitallyPlugin({
|
|
101
|
+
writeToken: "your-write-token",
|
|
102
|
+
env: "dev",
|
|
103
|
+
}),
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
// register plugins and routes below this point
|
|
110
107
|
```
|
|
111
108
|
|
|
112
109
|
For further instructions, see our [setup guide for Elysia](https://docs.apitally.io/sdk-reference/javascript/v1/setup-guides/elysia).
|
|
@@ -124,8 +121,8 @@ import { useApitally } from "apitally";
|
|
|
124
121
|
const app = express();
|
|
125
122
|
|
|
126
123
|
useApitally(app, {
|
|
127
|
-
writeToken: "your-write-token",
|
|
128
|
-
env: "dev",
|
|
124
|
+
writeToken: "your-write-token",
|
|
125
|
+
env: "dev",
|
|
129
126
|
});
|
|
130
127
|
```
|
|
131
128
|
|
|
@@ -142,8 +139,8 @@ import { useApitally } from "apitally";
|
|
|
142
139
|
const app = Fastify();
|
|
143
140
|
|
|
144
141
|
useApitally(app, {
|
|
145
|
-
writeToken: "your-write-token",
|
|
146
|
-
env: "dev",
|
|
142
|
+
writeToken: "your-write-token",
|
|
143
|
+
env: "dev",
|
|
147
144
|
});
|
|
148
145
|
|
|
149
146
|
// register plugins and routes below this point
|
|
@@ -151,30 +148,9 @@ useApitally(app, {
|
|
|
151
148
|
|
|
152
149
|
For further instructions, see our [setup guide for Fastify](https://docs.apitally.io/sdk-reference/javascript/v1/setup-guides/fastify).
|
|
153
150
|
|
|
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
151
|
### H3
|
|
176
152
|
|
|
177
|
-
Add `apitallyPlugin()`
|
|
153
|
+
Add `apitallyPlugin()` to the `plugins` of your root H3 app:
|
|
178
154
|
|
|
179
155
|
```javascript
|
|
180
156
|
import { H3 } from "h3";
|
|
@@ -183,8 +159,8 @@ import { apitallyPlugin } from "apitally/h3";
|
|
|
183
159
|
const app = new H3({
|
|
184
160
|
plugins: [
|
|
185
161
|
apitallyPlugin({
|
|
186
|
-
writeToken: "your-write-token",
|
|
187
|
-
env: "dev",
|
|
162
|
+
writeToken: "your-write-token",
|
|
163
|
+
env: "dev",
|
|
188
164
|
}),
|
|
189
165
|
],
|
|
190
166
|
});
|
|
@@ -194,7 +170,7 @@ For further instructions, see our [setup guide for H3](https://docs.apitally.io/
|
|
|
194
170
|
|
|
195
171
|
### Hapi
|
|
196
172
|
|
|
197
|
-
Register `apitallyPlugin()`
|
|
173
|
+
Register `apitallyPlugin()` immediately after creating the server, before registering other plugins and routes:
|
|
198
174
|
|
|
199
175
|
```javascript
|
|
200
176
|
import Hapi from "@hapi/hapi";
|
|
@@ -204,12 +180,12 @@ const server = Hapi.server({ port: 3000 });
|
|
|
204
180
|
|
|
205
181
|
await server.register(
|
|
206
182
|
apitallyPlugin({
|
|
207
|
-
writeToken: "your-write-token",
|
|
208
|
-
env: "dev",
|
|
183
|
+
writeToken: "your-write-token",
|
|
184
|
+
env: "dev",
|
|
209
185
|
}),
|
|
210
186
|
);
|
|
211
187
|
|
|
212
|
-
// register
|
|
188
|
+
// register other plugins and routes below this point
|
|
213
189
|
await server.start();
|
|
214
190
|
```
|
|
215
191
|
|
|
@@ -226,8 +202,8 @@ import { useApitally } from "apitally";
|
|
|
226
202
|
const app = new Hono();
|
|
227
203
|
|
|
228
204
|
useApitally(app, {
|
|
229
|
-
writeToken: "your-write-token",
|
|
230
|
-
env: "dev",
|
|
205
|
+
writeToken: "your-write-token",
|
|
206
|
+
env: "dev",
|
|
231
207
|
});
|
|
232
208
|
|
|
233
209
|
// register middleware and routes below this point
|
|
@@ -240,14 +216,14 @@ For further instructions, see our [setup guide for Hono](https://docs.apitally.i
|
|
|
240
216
|
Call `useApitally(app)` immediately after creating the app, before registering middleware and routes:
|
|
241
217
|
|
|
242
218
|
```javascript
|
|
243
|
-
|
|
244
|
-
|
|
219
|
+
import Koa from "koa";
|
|
220
|
+
import { useApitally } from "apitally";
|
|
245
221
|
|
|
246
222
|
const app = new Koa();
|
|
247
223
|
|
|
248
224
|
useApitally(app, {
|
|
249
|
-
writeToken: "your-write-token",
|
|
250
|
-
env: "dev",
|
|
225
|
+
writeToken: "your-write-token",
|
|
226
|
+
env: "dev",
|
|
251
227
|
});
|
|
252
228
|
|
|
253
229
|
// register middleware and routes below this point
|
|
@@ -255,11 +231,42 @@ useApitally(app, {
|
|
|
255
231
|
|
|
256
232
|
For further instructions, see our [setup guide for Koa](https://docs.apitally.io/sdk-reference/javascript/v1/setup-guides/koa).
|
|
257
233
|
|
|
234
|
+
### NestJS
|
|
235
|
+
|
|
236
|
+
Call `useApitally(app)` immediately after creating the app, before calling `app.init()` or `app.listen()`:
|
|
237
|
+
|
|
238
|
+
```javascript
|
|
239
|
+
import { NestFactory } from "@nestjs/core";
|
|
240
|
+
import { useApitally } from "apitally/nestjs";
|
|
241
|
+
import { AppModule } from "./app.module.js";
|
|
242
|
+
|
|
243
|
+
const app = await NestFactory.create(AppModule);
|
|
244
|
+
|
|
245
|
+
useApitally(app, {
|
|
246
|
+
writeToken: "your-write-token",
|
|
247
|
+
env: "dev",
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
await app.listen(3000);
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
For further instructions, see our [setup guide for NestJS](https://docs.apitally.io/sdk-reference/javascript/v1/setup-guides/nestjs).
|
|
254
|
+
|
|
258
255
|
## Configuration
|
|
259
256
|
|
|
260
|
-
|
|
257
|
+
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.
|
|
261
258
|
|
|
262
|
-
|
|
259
|
+
### Environment variables
|
|
260
|
+
|
|
261
|
+
To keep configuration out of your code, set these environment variables instead of passing the matching options:
|
|
262
|
+
|
|
263
|
+
- `APITALLY_WRITE_TOKEN`: the write token (`writeToken`)
|
|
264
|
+
- `APITALLY_ENV`: the environment name (`env`), defaults to `dev`
|
|
265
|
+
- `APITALLY_DISABLED`: set to `true` to disable Apitally (`disabled`)
|
|
266
|
+
|
|
267
|
+
### Request and response data
|
|
268
|
+
|
|
269
|
+
Apitally captures response headers by default. To also capture request headers and request and response bodies, pass these options:
|
|
263
270
|
|
|
264
271
|
```javascript
|
|
265
272
|
useApitally(app, {
|
|
@@ -271,112 +278,105 @@ useApitally(app, {
|
|
|
271
278
|
});
|
|
272
279
|
```
|
|
273
280
|
|
|
274
|
-
|
|
281
|
+
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.
|
|
282
|
+
|
|
283
|
+
### Exclusions and sampling
|
|
284
|
+
|
|
285
|
+
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`.
|
|
275
286
|
|
|
276
|
-
|
|
287
|
+
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`.
|
|
277
288
|
|
|
278
|
-
|
|
289
|
+
Metrics always include all requests. See our [documentation on sampling](https://docs.apitally.io/sdk-reference/javascript/v1/sampling) for details.
|
|
279
290
|
|
|
280
|
-
|
|
291
|
+
### Application logs
|
|
281
292
|
|
|
282
|
-
|
|
293
|
+
Apitally captures logs written with these loggers and correlates them with the requests they belong to:
|
|
283
294
|
|
|
284
|
-
|
|
295
|
+
- `console`
|
|
296
|
+
- Pino
|
|
297
|
+
- Winston
|
|
298
|
+
- NestJS `Logger`
|
|
299
|
+
- Hapi `request.log()`
|
|
300
|
+
|
|
301
|
+
To mask or drop individual log records, pass a callback to `maskLogRecord`. To turn off log capture, set `captureLogs: false`.
|
|
302
|
+
|
|
303
|
+
See our [documentation on masking](https://docs.apitally.io/sdk-reference/javascript/v1/masking#log-record-masking-callback) for details.
|
|
304
|
+
|
|
305
|
+
## Adding context to requests
|
|
306
|
+
|
|
307
|
+
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.
|
|
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
|
|
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
|
});
|
|
296
|
-
|
|
297
|
-
// Attach a custom attribute to the current request
|
|
298
|
-
setRequestAttribute("tenant", tenantId);
|
|
299
|
-
|
|
300
|
-
// Capture a handled exception for the current request
|
|
301
|
-
captureException(error);
|
|
302
320
|
```
|
|
303
321
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
For further details, check out our [documentation](https://docs.apitally.io).
|
|
322
|
+
To attach custom data to the request's trace, call `setRequestAttribute`. Sampling and masking callbacks can also read these attributes.
|
|
307
323
|
|
|
308
|
-
|
|
324
|
+
```javascript
|
|
325
|
+
import { setRequestAttribute } from "apitally";
|
|
309
326
|
|
|
310
|
-
|
|
327
|
+
setRequestAttribute("tenant.id", tenantId);
|
|
328
|
+
```
|
|
311
329
|
|
|
312
|
-
|
|
330
|
+
To capture an exception that your code handles itself, call `captureException`. Unhandled exceptions are captured automatically.
|
|
313
331
|
|
|
314
332
|
```javascript
|
|
315
|
-
import {
|
|
316
|
-
import { ApitallySpanProcessor } from "apitally";
|
|
333
|
+
import { captureException } from "apitally";
|
|
317
334
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
335
|
+
try {
|
|
336
|
+
processOrder();
|
|
337
|
+
} catch (error) {
|
|
338
|
+
captureException(error);
|
|
339
|
+
}
|
|
322
340
|
```
|
|
323
341
|
|
|
324
|
-
|
|
342
|
+
See our documentation on [custom attributes](https://docs.apitally.io/sdk-reference/javascript/v1/attributes#set-custom-attributes) and [handled exceptions](https://docs.apitally.io/sdk-reference/javascript/v1/tracing#capture-handled-exceptions) for details.
|
|
325
343
|
|
|
326
|
-
|
|
344
|
+
## Existing OpenTelemetry setup
|
|
327
345
|
|
|
328
|
-
|
|
346
|
+
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.
|
|
329
347
|
|
|
330
348
|
```javascript
|
|
331
|
-
import
|
|
349
|
+
import { NodeSDK } from "@opentelemetry/sdk-node";
|
|
332
350
|
import { ApitallySpanProcessor } from "apitally";
|
|
333
351
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
//
|
|
337
|
-
tracesSampleRate: 1.0,
|
|
338
|
-
// Add Apitally's span processor
|
|
339
|
-
openTelemetrySpanProcessors: [new ApitallySpanProcessor()],
|
|
340
|
-
// Optional: send only errors to Sentry
|
|
341
|
-
beforeSendTransaction: () => null,
|
|
352
|
+
const sdk = new NodeSDK({
|
|
353
|
+
spanProcessors: [new ApitallySpanProcessor()],
|
|
354
|
+
// ...your existing configuration
|
|
342
355
|
});
|
|
343
356
|
```
|
|
344
357
|
|
|
345
|
-
|
|
358
|
+
If you use versions 8 to 10 of the Sentry SDK, which register their own tracer provider, add `ApitallySpanProcessor` to the `openTelemetrySpanProcessors` option in `Sentry.init()` and enable Sentry tracing.
|
|
346
359
|
|
|
347
|
-
|
|
360
|
+
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.
|
|
348
361
|
|
|
349
|
-
|
|
362
|
+
## Graceful shutdown
|
|
350
363
|
|
|
351
|
-
|
|
364
|
+
If your code calls `process.exit()`, await `shutdown()` before it so Apitally can send any remaining telemetry:
|
|
352
365
|
|
|
353
366
|
```javascript
|
|
354
|
-
import {
|
|
355
|
-
import { Elysia } from "elysia";
|
|
356
|
-
import { apitallyPlugin } from "apitally/elysia";
|
|
367
|
+
import { shutdown } from "apitally";
|
|
357
368
|
|
|
358
|
-
|
|
359
|
-
.
|
|
360
|
-
|
|
369
|
+
process.on("SIGTERM", () => {
|
|
370
|
+
server.close(async () => {
|
|
371
|
+
await shutdown();
|
|
372
|
+
process.exit(0);
|
|
373
|
+
});
|
|
374
|
+
});
|
|
361
375
|
```
|
|
362
376
|
|
|
363
377
|
## Trusted proxies
|
|
364
378
|
|
|
365
|
-
If your
|
|
366
|
-
|
|
367
|
-
## Graceful shutdown
|
|
368
|
-
|
|
369
|
-
Apitally sends telemetry in batches and automatically attempts a final export during normal process shutdown.
|
|
370
|
-
|
|
371
|
-
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:
|
|
372
|
-
|
|
373
|
-
```javascript
|
|
374
|
-
import { shutdown } from "apitally";
|
|
375
|
-
|
|
376
|
-
// After the server has closed and requests have finished:
|
|
377
|
-
await shutdown();
|
|
378
|
-
process.exit(0);
|
|
379
|
-
```
|
|
379
|
+
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.
|
|
380
380
|
|
|
381
381
|
## Getting help
|
|
382
382
|
|
package/dist/activation.cjs
CHANGED
|
@@ -6,12 +6,12 @@ const require_exportWorker = require("./exportWorker.cjs");
|
|
|
6
6
|
const require_logCapture = require("./logCapture.cjs");
|
|
7
7
|
const require_logRecordExporter = require("./logRecordExporter.cjs");
|
|
8
8
|
const require_logRecordProcessor = require("./logRecordProcessor.cjs");
|
|
9
|
-
const require_providers = require("./providers.cjs");
|
|
10
9
|
const require_metrics = require("./metrics.cjs");
|
|
10
|
+
const require_spanProcessor = require("./spanProcessor.cjs");
|
|
11
|
+
const require_providers = require("./providers.cjs");
|
|
11
12
|
const require_redaction = require("./redaction.cjs");
|
|
12
13
|
const require_sentry = require("./sentry.cjs");
|
|
13
14
|
const require_serverErrors = require("./serverErrors.cjs");
|
|
14
|
-
const require_spanProcessor = require("./spanProcessor.cjs");
|
|
15
15
|
const require_spanExporter = require("./spanExporter.cjs");
|
|
16
16
|
const require_spool = require("./spool.cjs");
|
|
17
17
|
const require_startup = require("./startup.cjs");
|
package/dist/activation.cjs.map
CHANGED
|
@@ -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 aggregate group, 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":";;;;;;;;;;;;;;;;;;;;;;;;AA6CA,MAAM,8BAA8B;AACpC,MAAM,8BAA8B;AACpC,MAAM,uBAAuB;AAC7B,MAAM,8BAA8B;AACpC,MAAM,gDAAgD;AACtD,MAAM,8BAA8B;AAOpC,MAAM,uBAAuB,OAAO,IAAI,qBAAqB;AA2B7D,SAAgB,UAAU,UAA2B,CAAC,GAAmB;CACvE,MAAM,SAASA,eAAAA,UAAU,OAAO;CAGhC,IAAI,CAAC,OAAO,YAAY,QAAQ,IAAI,kCAAkC,KAAA,GACpE,QAAQ,IAAI,gCAAgC;CAE9C,sBAAA,gCAAgC,QAAQ;CACxC,OAAO;AACT;AAGA,SAAgB,yBAAyB,MAA8B;CACrE,MAAM,kBAAkB,mBAAmB;CAC3C,gBAAgB,qBAAqB;AACvC;AAIA,SAAgB,WAAiB;CAC/B,MAAM,kBAAkB,mBAAmB;CAC3C,IAAI,gBAAgB,qBAClB;CAEF,gBAAgB,sBAAsB;CACtC,IAAI,qBAAqB,GACvB;CAEF,IAAI;EACF,MAAM,UAAU,eAAe,gBAAgB,gBAAgB;EAC/D,gBAAgB,UAAU;EAC1B,gBAAgB,oBAAoB,aAAa,OAAO;EACxD,sBAAsB,eAAe;EACrC,mBAAmB,eAAe;CACpC,SAAS,OAAO;EACd,eAAA,SAAS,+BAA+B,OAAO,KAAK,GAAG;CACzD;AACF;AAEA,SAAgB,cAAuB;CACrC,OAAO,mBAAmB,CAAC,CAAC,YAAY,KAAA;AAC1C;AAEA,SAAgB,uBAAsD;CACpE,OAAO,mBAAmB,CAAC,CAAC;AAC9B;AAEA,eAAsB,iBAAgC;CACpD,IAAI;EACF,MAAM,mBAAmB,CAAC,CAAC,SAAS,OAAO,SAAS;CACtD,SAAS,OAAO;EACd,eAAA,SAAS,6BAA6B,OAAO,KAAK,GAAG;CACvD;AACF;AAIA,SAAgB,WAA0B;CACxC,MAAM,kBAAkB,mBAAmB;CAC3C,MAAM,UAAU,gBAAgB;CAChC,MAAM,cAAc,gBAAgB;CACpC,IAAI,CAAC,WAAW,CAAC,aACf,OAAO,QAAQ,QAAQ;CAEzB,IAAI,CAAC,gBAAgB,iBAAiB;EACpC,qBAAqB,eAAe;EACpC,MAAM,qBAAqB,gBAAgB;EAC3C,gBAAgB,mBACd,qBACI,mBAAmB,WAAW,QAAQ,OAAO,YAAY,CAAC,IAC1D,QAAQ,QAAQ,EAAA,CACpB,KAAK,WAAW;CACpB;CACA,OAAO,gBAAgB;AACzB;AAGA,MAAa,sBAAsB;CACjC,mBAA0B,IAAIC,cAAAA,MAAM;CACpC,qBAAqB,YAA+C,IAAIC,qBAAAA,aAAa,OAAO;AAC9F;AACA,MAAM,mBAAmB,EAAE,GAAG,oBAAoB;AAGlD,eAAsB,kBAAiC;CACrD,OAAO,OAAO,qBAAqB,gBAAgB;CACnD,MAAM,SAAS;CACf,MAAM,kBAAkB,OAAO;CAC/B,OAAO,OAAO;CACd,4BAA4B;CAC5B,yBAAA,sBAAsB;CACtB,qBAAA,kBAAkB;CAClB,IAAI,CAAC,iBACH;CAEF,qBAAqB,eAAe;CACpC,MAAM,UAAU,gBAAgB;CAChC,IAAI,CAAC,SACH;CAEF,QAAQ,uBAAuB,QAAQ;CACvC,MAAM,QAAQ,OAAO,KAAK;CAC1B,MAAM,QAAQ,aAAa,SAAS;CACpC,MAAM,QAAQ,eAAe,SAAS;CACtC,MAAM,QAAQ,MAAM,MAAM;AAC5B;AAEA,IAAI,4BAA4B;AAIhC,SAAS,gBAAgB,gBAAsC;CAC7D,MAAM,SAAS,eAAe,UAAU,UAAU;CAClD,MAAM,SAAoC,CACxC,CAACC,yBAAAA,6BAA6BC,yBAAAA,sBAAsB,CAAC,GACrD,CAACC,qBAAAA,yBAAyBC,qBAAAA,kBAAkB,CAAC,CAC/C;CACA,KAAK,MAAM,CAAC,WAAW,WAAW,QAChC,KAAK,MAAM,QAAQ,QACjB,OAAO,KAAK;EAAE,WAAW,KAAK,IAAI;EAAG,SAASC,mBAAAA;EAAc;EAAW;CAAK,CAAC;AAGnF;AAEA,SAAS,qBAAsC;CAC7C,MAAM,SAAS;CACf,MAAM,WAAW,OAAO;CACxB,IAAI,UAAU;EACZ,IAAI,CAAC,6BAA6B,SAAS,eAAeC,uBAAAA,iBAAiB,GAAG;GAC5E,4BAA4B;GAC5B,eAAA,WACE,+DAA+D,SAAS,WAAW,OAAOA,uBAAAA,iBAAiB,EAAE,wCAAwC,SAAS,WAAW,uEAC3K;EACF;EACA,OAAO;CACT;CACA,MAAM,kBAAmC;EACvC,YAAYA,uBAAAA,iBAAiB;EAC7B,qBAAqB;CACvB;CACA,OAAO,wBAAwB;CAC/B,OAAO;AACT;AAEA,SAAS,uBAAgC;CACvC,OACE,QAAQ,QAAQ,IAAI,cAAc,KAClC,QAAQ,QAAQ,IAAI,MAAM,KAC1B,QAAQ,IAAI,aAAa,UACzBC,eAAAA,UAAU,CAAC,CAAC;AAEhB;AAEA,SAAS,eAAe,kBAAmE;CACzF,MAAM,SAASA,eAAAA,UAAU;CACzB,IAAI,kBAAkBC,sBAAAA,gCAAgC;CACtD,MAAM,EAAE,KAAK,aAAaC,kBAAAA,4BAA4B;CACtD,MAAM,aAAa,SAAS,WAAW;CACvC,MAAM,QAAQ,oBAAoB,YAAY;CAC9C,MAAM,eAAe,IAAIC,qBAAAA,qBAAqB;EAC5C,WAAW,IAAIC,kBAAAA,UAAU;EACzB;EACA;EACA;EACA,iBAAiB,OAAO;EACxB,kBAAkB,OAAO;CAC3B,CAAC;CAGD,MAAM,qBAAqB,IAAIC,8BAAAA,mBAAmB,cAAc;EAC9D,sBAAsB;EACtB,qBAAqB;EACrB,cAAc;EACd,oBACE,OAAO,sBAAsB,OAAO,sBAChC,gDACA;CACR,CAAC;CACD,MAAM,eAAe,IAAIC,sBAAAA,aAAa,0BAA0B,aAAa,WAAW,CAAC;CACzF,IAAI;CACJ,IAAI,CAAC,iBAAiB;EACpB,iBAAiBC,kBAAAA,oBAAoB,UAAU,CAAC,YAAY,CAAC;EAC7D,IAAI,CAAC,gBAAgB;GACnB,kBAAkB;GAClB,eAAA,WACE,gVACF;EACF;CACF;CACA,sBAAA,sBAAsB,YAAY;CAGlC,MAAM,oBAAoB,IAAIC,wBAAAA,wBAAwB;EACpD,UAAU,IAAIC,0BAAAA,0BAA0B,KAAK;EAC7C,sBAAsB;EACtB,qBAAqB;EACrB,cAAc;EACd,oBAAoB;CACtB,CAAC;CACD,MAAM,qBAAqB,IAAIC,2BAAAA,2BAC7B,mBACA,cACA,OAAO,aACT;CACA,MAAM,iBAAiBC,kBAAAA,qBAAqB,UAAU,CAAC,kBAAkB,CAAC;CAC1E,MAAM,kBAAkB,IAAIC,gBAAAA,gBAAgB,UAAU,KAAK;CAC3D,aAAa,mBAAmB,WAAW,gBAAgB,kBAAkB,MAAM;CACnF,MAAM,SAAS,oBAAoB,mBAAmB;EACpD;EACA,cAAc,OAAO;EACrB,YAAY,OAAO;EACnB;CACF,CAAC;CACD,OAAO,eAAe,WACd,gBAAgB,iBAAiB,SACjC,aAAa,WAAW,SACxB,gBAAgB,cAAc,SAC9B,kBAAkB,WAAW,CACrC;CACA,IAAI,OAAO,aAAa;EACtB,mBAAA,sBAAsB,cAAc;EACpC,mBAAA,yBAAyB,cAAc;EACvC,mBAAA,sBAAsB,cAAc;EACpC,mBAAA,mBAAmB,cAAc;CACnC;CACA,eAAA,8BAA8B;CAC9B,IAAI;CACJ,IAAI,CAAC,iBACH,wBAAwB,4BAA4B,OAAO,YAAY;CAEzE,OAAO,MAAM;CACb,IAAI,kBACF,gBAAA,iBAAiB,gBAAgB,gBAAgB;CAEnD,OAAO;EACL;EACA;EACA;EACA,sCAAsB,IAAI,IAAI;EAC9B;EACA;EACA;EACA;CACF;AACF;AAIA,SAAS,4BAA4B,cAA6C;CAChF,MAAM,iBAAiB,IAAI,IAAI,YAAY,CAAC,CAAC;CAC7C,OAAO,IAAIC,sCAAAA,sBAAsB,EAC/B,oBAAoB,YAAY;EAC9B,IAAI;GACF,OAAO,IAAI,IAAI,OAAO,QAAQ,MAAM,CAAC,CAAC,CAAC,WAAW;EACpD,QAAQ;GACN,OAAO;EACT;CACF,EACF,CAAC;AACH;AAEA,eAAe,aAAa,SAA2C;CACrE,IAAI;EACF,MAAM,QAAQ,aAAa,SAAS;EACpC,MAAM,QAAQ,OAAO,WAAW;EAChC,MAAM,QAAQ,eAAe,SAAS;EACtC,MAAM,QAAQ,OAAO,KAAK;CAC5B,SAAS,OAAO;EACd,eAAA,WAAW,yCAAyC,OAAO,KAAK,GAAG;CACrE;AACF;AAEA,SAAS,mBAAmB,iBAAwC;CAClE,IAAI,QAAQ,aAAa,WAAW,CAACC,oBAAAA,cACnC;CAEF,IAAI,YACQ;EACV,eAAe,wBAAwB,iBAAiB,WAAW,SAAS;EAC5E,cAAc,wBAAwB,iBAAiB,WAAW,QAAQ;CAC5E;CACA,gBAAgB,kBAAkB;CAClC,IAAI;EACF,QAAQ,oBAAoB,WAAW,UAAU,OAAO;EACxD,QAAQ,oBAAoB,UAAU,UAAU,MAAM;CACxD,SAAS,OAAO;EACd,kBAAkB,iBAAiB,SAAS;EAC5C,eAAA,WAAW,+CAA+C,OAAO,KAAK,GAAG;CAC3E;AACF;AAEA,SAAS,wBACP,iBACA,WACA,QACM;CACN,MAAM,yBAAyB,QAC5B,UAAU,MAAM,CAAC,CACjB,MAAM,aAAa,aAAa,UAAU,OAAO;CACpD,kBAAkB,iBAAiB,SAAS;CAC5C,MAAM,eAAe,4BAA4B,iBAAiB,QAAQ,sBAAsB;CAChG,gBAAgB,qBAAqB;CACrC,aAAkB,OAAO,UAAmB;EAC1C,eAAA,SAAS,+BAA+B,OAAO,IAAI,OAAO,KAAK,GAAG;CACpE,CAAC;AACH;AAEA,eAAe,4BACb,iBACA,QACA,wBACe;CACf,MAAM,UAAU,gBAAgB;CAChC,IAAI,CAAC,SACH;CAEF,IAAI;EACF,IAAI,gBAAgB,iBAClB,MAAM,sBAAsB,gBAAgB,eAAe;OAE3D,MAAM,QAAQ,OAAO,WAAW,2BAA2B;CAE/D,SAAS,OAAO;EACd,eAAA,SAAS,+BAA+B,OAAO,IAAI,OAAO,KAAK,GAAG;CACpE;CACA,IAAI,0BAA0B,QAAQ,UAAU,MAAM,CAAC,CAAC,SAAS,GAC/D;CAEF,MAAM,WAAW,WAAW,YAAY,MAAM;CAC9C,IAAI,QAAQ,aAAa,WAAW,QAAQ,QAAQ,GAClD,QAAQ,KAAK,QAAQ;CAEvB,IAAI;EACF,QAAQ,KAAK,QAAQ,KAAK,MAAM;CAClC,QAAQ;EACN,QAAQ,KAAK,QAAQ;CACvB;AACF;AAEA,eAAe,sBAAsB,SAAuC;CAC1E,IAAI;CACJ,MAAM,kBAAkB,IAAI,SAAe,YAAY;EACrD,gBAAgB,WAAW,SAAS,2BAA2B;CACjE,CAAC;CACD,IAAI;EACF,MAAM,QAAQ,KAAK,CAAC,SAAS,eAAe,CAAC;CAC/C,UAAU;EACR,IAAI,eACF,aAAa,aAAa;CAE9B;AACF;AAEA,SAAS,kBACP,iBACA,YAAY,gBAAgB,iBACtB;CACN,IAAI,CAAC,WACH;CAEF,QAAQ,eAAe,WAAW,UAAU,OAAO;CACnD,QAAQ,eAAe,UAAU,UAAU,MAAM;CACjD,IAAI,gBAAgB,oBAAoB,WACtC,gBAAgB,kBAAkB,KAAA;AAEtC;AAEA,SAAS,qBAAqB,iBAAwC;CACpE,kBAAkB,eAAe;CACjC,IAAI,gBAAgB,oBAAoB;EACtC,QAAQ,eAAe,cAAc,gBAAgB,kBAAkB;EACvE,gBAAgB,qBAAqB,KAAA;CACvC;AACF;AAEA,SAAS,sBAAsB,iBAAwC;CACrE,MAAM,iBAAiB;EAErB,SAAc,CAAC,CAAC,OAAO,UAAmB;GACxC,eAAA,SAAS,yCAAyC,OAAO,KAAK,GAAG;EACnE,CAAC;CACH;CACA,gBAAgB,qBAAqB;CACrC,QAAQ,GAAG,cAAc,QAAQ;AACnC"}
|
|
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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"}
|
package/dist/activation.js
CHANGED
|
@@ -5,12 +5,12 @@ import { ExportWorker } from "./exportWorker.js";
|
|
|
5
5
|
import { installConsoleCapture, installNestLoggerCapture, installPinoCapture, installWinstonCapture } from "./logCapture.js";
|
|
6
6
|
import { ApitallyLogRecordExporter } from "./logRecordExporter.js";
|
|
7
7
|
import { ApitallyLogRecordProcessor } from "./logRecordProcessor.js";
|
|
8
|
-
import { createLoggerProvider, resolveEnvAndCreateResource, setupTracerProvider } from "./providers.js";
|
|
9
8
|
import { MetricsPipeline } from "./metrics.js";
|
|
9
|
+
import { SpanPipeline, isApitallySpanProcessorDeclared, setActiveSpanPipeline, setServerSpanActivationCallback } from "./spanProcessor.js";
|
|
10
|
+
import { createLoggerProvider, resolveEnvAndCreateResource, setupTracerProvider } from "./providers.js";
|
|
10
11
|
import { Redaction } from "./redaction.js";
|
|
11
12
|
import { installSentryEventIdRecording } from "./sentry.js";
|
|
12
13
|
import { SERVER_ERROR_EVENT_NAME, drainServerErrors, resetServerErrors } from "./serverErrors.js";
|
|
13
|
-
import { SpanPipeline, isApitallySpanProcessorDeclared, setActiveSpanPipeline, setServerSpanActivationCallback } from "./spanProcessor.js";
|
|
14
14
|
import { ApitallySpanExporter } from "./spanExporter.js";
|
|
15
15
|
import { Spool } from "./spool.js";
|
|
16
16
|
import { emitStartupEvent } from "./startup.js";
|