@nxgt/telemetry 0.2.0 → 0.2.1
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 +129 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,6 +24,135 @@ vocabulary exactly: the same severities, span kinds, statuses, `traceparent`
|
|
|
24
24
|
rules, sampling rule and attribute names. A trace started in one and continued
|
|
25
25
|
in the other is one trace.
|
|
26
26
|
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
The whole life of a service, in the order you write it. Each step links to the
|
|
30
|
+
section that explains it.
|
|
31
|
+
|
|
32
|
+
**1. Install one telemetry at startup**, before anything logs.
|
|
33
|
+
[The root](#the-root)
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
// telemetry.ts
|
|
37
|
+
import { consoleExporter, createTelemetry } from '@nxgt/telemetry';
|
|
38
|
+
|
|
39
|
+
export const telemetry = createTelemetry('checkout', {
|
|
40
|
+
version: '1.4.0',
|
|
41
|
+
environment: 'production',
|
|
42
|
+
exporters: [consoleExporter()],
|
|
43
|
+
}).install();
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**2. Give each part of the code a logger.** A logger is cheap and has no
|
|
47
|
+
state; create it at module level. [Logging](#logging)
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import { createLogger } from '@nxgt/telemetry';
|
|
51
|
+
|
|
52
|
+
const log = createLogger('CheckoutService');
|
|
53
|
+
|
|
54
|
+
log.info('checkout started', { cartSize: 3 });
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**3. Declare the events that matter**, so a log line carries only the fields
|
|
58
|
+
you chose. [Declared event](#declared-event)
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
import { event } from '@nxgt/telemetry';
|
|
62
|
+
import { z } from 'zod';
|
|
63
|
+
|
|
64
|
+
const Charged = event('checkout.charged', z.object({ orderId: z.string(), amount: z.number() }));
|
|
65
|
+
|
|
66
|
+
log.info(Charged({ orderId: 'o-1', amount: 42 }));
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**4. Wrap units of work in spans.** Everything inside a span, logs included,
|
|
70
|
+
carries its `traceId`. [Spans](#spans)
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import { span } from '@nxgt/telemetry';
|
|
74
|
+
|
|
75
|
+
export async function checkout(orderId: string) {
|
|
76
|
+
return span('checkout', { attributes: { orderId } }, async (scope) => {
|
|
77
|
+
const order = await span('order.load', () => orders.find(orderId));
|
|
78
|
+
scope.attribute('order.total', order.total);
|
|
79
|
+
await span('payment.charge', { kind: 'client' }, () => payments.charge(order));
|
|
80
|
+
log.info(Charged({ orderId, amount: order.total })); // carries the trace
|
|
81
|
+
return order;
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**5. Carry the trace across services.** Continue the caller's `traceparent` on
|
|
87
|
+
the way in and send the current one on the way out.
|
|
88
|
+
[Propagation](#propagation-and-traceparent)
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
import { continuing, currentTraceparent } from '@nxgt/telemetry';
|
|
92
|
+
|
|
93
|
+
// in: an HTTP handler
|
|
94
|
+
await continuing(request.headers.get('traceparent'), 'POST /checkout', { kind: 'server' }, () =>
|
|
95
|
+
checkout(orderId),
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
// out: an HTTP call made inside a span
|
|
99
|
+
await fetch('https://stock.internal/reserve', {
|
|
100
|
+
method: 'POST',
|
|
101
|
+
headers: { traceparent: currentTraceparent() ?? '' },
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
With Hono and httpyz, the two integrations do both halves for you:
|
|
106
|
+
[`@nxgt/telemetry-hono`](https://www.npmjs.com/package/@nxgt/telemetry-hono)
|
|
107
|
+
and [`@nxgt/telemetry-httpyz`](https://www.npmjs.com/package/@nxgt/telemetry-httpyz).
|
|
108
|
+
|
|
109
|
+
**6. Put request-wide facts in scope once**, instead of passing them to every
|
|
110
|
+
log call. [Context](#context)
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
import { withAttributes } from '@nxgt/telemetry';
|
|
114
|
+
|
|
115
|
+
await withAttributes({ tenant: 'acme' }, () => checkout(orderId));
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**7. Ship the signals somewhere.** Swap the console for a collector, a file or
|
|
119
|
+
MongoDB. The code from steps 2 to 6 does not change. [Exporters](#exporters)
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
import { otlpExporter } from '@nxgt/telemetry-otlp';
|
|
123
|
+
|
|
124
|
+
createTelemetry('checkout', {
|
|
125
|
+
sampler: ratioSampler(0.1), // keep one trace in ten; logs are never sampled
|
|
126
|
+
exporters: [otlpExporter({ endpoint: 'http://localhost:4318' })],
|
|
127
|
+
}).install();
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**8. Close it on shutdown, and await it**, or the last batch is lost.
|
|
131
|
+
[The root](#the-root)
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
process.on('SIGTERM', async () => {
|
|
135
|
+
await telemetry.close();
|
|
136
|
+
process.exit(0);
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**In a test**, give each suite its own telemetry and an exporter that keeps
|
|
141
|
+
what it receives, instead of installing one globally.
|
|
142
|
+
[Context](#context)
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
import type { Exporter, Signal } from '@nxgt/telemetry';
|
|
146
|
+
import { createTelemetry, withTelemetry } from '@nxgt/telemetry';
|
|
147
|
+
|
|
148
|
+
const received: Signal[] = [];
|
|
149
|
+
const collect: Exporter = { export: (_resource, batch) => void received.push(...batch) };
|
|
150
|
+
const telemetry = createTelemetry('test', { exporters: [collect] });
|
|
151
|
+
|
|
152
|
+
await withTelemetry(telemetry, () => checkout('o-1'));
|
|
153
|
+
await telemetry.close(); // flushes: `received` now holds the spans and logs
|
|
154
|
+
```
|
|
155
|
+
|
|
27
156
|
## Concepts
|
|
28
157
|
|
|
29
158
|
Every word this library uses, once, with the smallest example that shows it.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nxgt/telemetry",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Logs and traces for a TypeScript service, with no OpenTelemetry SDK: spans over AsyncLocalStorage, W3C traceparent propagation, declared events, and a pipeline that never blocks the caller",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|