@nxgt/telemetry 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +156 -0
- package/dist/attributes/attributes.d.ts +36 -0
- package/dist/attributes/attributes.d.ts.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +217 -0
- package/dist/index.js.map +13 -0
- package/dist/model/signal.d.ts +90 -0
- package/dist/model/signal.d.ts.map +1 -0
- package/dist/trace/ids.d.ts +60 -0
- package/dist/trace/ids.d.ts.map +1 -0
- package/dist/trace/sampler.d.ts +31 -0
- package/dist/trace/sampler.d.ts.map +1 -0
- package/package.json +58 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Steve Tsala
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# @nxgt/telemetry
|
|
2
|
+
|
|
3
|
+
Logs and traces for a TypeScript service, with no OpenTelemetry SDK.
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
bun add @nxgt/telemetry
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Zero runtime dependencies. Bun and Node; a browser gets everything but the
|
|
10
|
+
automatic context.
|
|
11
|
+
|
|
12
|
+
## What it is
|
|
13
|
+
|
|
14
|
+
A span is a block you `await`, and the current one travels with your work —
|
|
15
|
+
through every `await`, every timer, every promise — in an `AsyncLocalStorage`.
|
|
16
|
+
Logs written inside it carry its `traceId` and `spanId` without being told. A
|
|
17
|
+
`traceparent` header is read on the way in and written on the way out, so the
|
|
18
|
+
trace continues across services. Signals are handed to a pipeline that never
|
|
19
|
+
blocks the caller and never throws at it, and exporters ship them: the console,
|
|
20
|
+
a file, [OTLP](https://www.npmjs.com/package/@nxgt/telemetry-otlp), MongoDB.
|
|
21
|
+
|
|
22
|
+
It is the TypeScript counterpart of `stx-telemetry` on the JVM, and shares its
|
|
23
|
+
vocabulary exactly: the same severities, span kinds, statuses, `traceparent`
|
|
24
|
+
rules, sampling rule and attribute names. A trace started in one and continued
|
|
25
|
+
in the other is one trace.
|
|
26
|
+
|
|
27
|
+
## Trace identity
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { randomTraceId, parseTraceparent, renderTraceparent } from '@nxgt/telemetry';
|
|
31
|
+
|
|
32
|
+
const incoming = parseTraceparent(request.headers.get('traceparent'));
|
|
33
|
+
// → { traceId, spanId, sampled, remote: true }, or null
|
|
34
|
+
|
|
35
|
+
renderTraceparent({ traceId, spanId, sampled: true, remote: false });
|
|
36
|
+
// → '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01'
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`parseTraceparent` **returns `null` and never throws.** A header a stranger
|
|
40
|
+
controls must not be able to fail a request: an unusable one simply starts a
|
|
41
|
+
fresh trace. It rejects fewer than four fields, a version that is not two hex
|
|
42
|
+
characters, the forbidden version `ff`, an id of the wrong length or all zeros
|
|
43
|
+
or not hex, and flags that are not hex. A later version is accepted and its
|
|
44
|
+
extra fields ignored. `tracestate` is not read, and nothing here writes one.
|
|
45
|
+
|
|
46
|
+
`TraceId` and `SpanId` are branded strings: the hex is the representation, so
|
|
47
|
+
there is nothing to convert before writing a header, and the compiler still
|
|
48
|
+
refuses a span id where a trace id belongs. `isValidTraceId` and
|
|
49
|
+
`isValidSpanId` are the guards that turn a `string` into one.
|
|
50
|
+
|
|
51
|
+
## Sampling
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import { ratioSampler, alwaysSample, neverSample } from '@nxgt/telemetry';
|
|
55
|
+
|
|
56
|
+
const sampler = ratioSampler(0.1); // keep a tenth of traces
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The sampler is asked **once, for the root span**, and the answer travels with
|
|
60
|
+
the trace — down through every child and out in the `traceparent`. A sampler
|
|
61
|
+
consulted per span produces traces missing their middles, and a gap in a trace
|
|
62
|
+
looks like work that never happened.
|
|
63
|
+
|
|
64
|
+
The decision is a function of the trace id, not a coin toss, and the rule is
|
|
65
|
+
OpenTelemetry's: two services at the same ratio make the **same** decision about
|
|
66
|
+
the same trace. Two services tossing independently at 10% keep a whole trace 1%
|
|
67
|
+
of the time.
|
|
68
|
+
|
|
69
|
+
**Logs are never sampled.** A log dropped because its trace was not kept is a
|
|
70
|
+
log missing at precisely the moment somebody is reading logs to find out what
|
|
71
|
+
happened. The `traceId` is attached either way, so an unsampled trace's logs
|
|
72
|
+
still group.
|
|
73
|
+
|
|
74
|
+
## Attributes
|
|
75
|
+
|
|
76
|
+
An attribute is a **scalar, or a list of scalars** — that is what a backend can
|
|
77
|
+
index, filter and group by, and what OTLP accepts. Anything with structure is an
|
|
78
|
+
event type, not an attribute.
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import { attributesOf } from '@nxgt/telemetry';
|
|
82
|
+
|
|
83
|
+
attributesOf({ orderId: 'o-1', amount: 4200, retried: false });
|
|
84
|
+
attributesOf({ orderId: 'o-1', code: undefined }); // → { orderId: 'o-1' }
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The coercion never refuses a value, because a log call must not fail: a
|
|
88
|
+
non-finite number, a `bigint` and a `Date` become text, an object becomes its
|
|
89
|
+
JSON, and a circular object still says something. `undefined` is dropped — one
|
|
90
|
+
absent field should not log `null` — while an explicit `null` is kept, because
|
|
91
|
+
somebody wrote it.
|
|
92
|
+
|
|
93
|
+
## API
|
|
94
|
+
|
|
95
|
+
### Trace identity
|
|
96
|
+
|
|
97
|
+
| | |
|
|
98
|
+
| --- | --- |
|
|
99
|
+
| `TraceId`, `SpanId` | branded 32- and 16-character lowercase hex strings |
|
|
100
|
+
| `SpanContext` | `{ traceId, spanId, sampled, remote }` — a span's place in a trace, as it travels |
|
|
101
|
+
| `randomTraceId()`, `randomSpanId()` | from `crypto.getRandomValues`, never `Math.random()` |
|
|
102
|
+
| `isValidTraceId(hex)`, `isValidSpanId(hex)` | type guards: right length, lowercase hex, not all zeros |
|
|
103
|
+
| `renderTraceparent(context)` | the header value, version `00` |
|
|
104
|
+
| `parseTraceparent(header)` | a `SpanContext` with `remote: true`, or `null` |
|
|
105
|
+
| `INVALID_TRACE_ID`, `INVALID_SPAN_ID` | all zeros |
|
|
106
|
+
| `DETACHED_SPAN_CONTEXT`, `isDetached(context)` | what a span carries when no telemetry is installed |
|
|
107
|
+
|
|
108
|
+
### Sampling
|
|
109
|
+
|
|
110
|
+
| | |
|
|
111
|
+
| --- | --- |
|
|
112
|
+
| `Sampler` | `{ sample(traceId): boolean }` |
|
|
113
|
+
| `alwaysSample`, `neverSample` | the constants |
|
|
114
|
+
| `ratioSampler(ratio)` | keeps `ratio` of traces, by trace id. Throws `RangeError` outside `0..1` |
|
|
115
|
+
|
|
116
|
+
### Attributes
|
|
117
|
+
|
|
118
|
+
| | |
|
|
119
|
+
| --- | --- |
|
|
120
|
+
| `AttributeScalar`, `AttributeValue`, `Attributes` | a scalar, a scalar or list of them, and a read-only record of those |
|
|
121
|
+
| `attributesOf(record)` | coerces a record of anything; drops `undefined` |
|
|
122
|
+
| `coerceAttribute(value)` | one value, the same way |
|
|
123
|
+
| `mergeAttributes(left, right)` | the right-hand side wins; neither argument is modified |
|
|
124
|
+
| `isEmptyAttributes(attributes)`, `EMPTY_ATTRIBUTES` | |
|
|
125
|
+
|
|
126
|
+
### The signal model
|
|
127
|
+
|
|
128
|
+
| | |
|
|
129
|
+
| --- | --- |
|
|
130
|
+
| `Severity` | `'debug' \| 'info' \| 'warn' \| 'error'`. There is no `trace` level |
|
|
131
|
+
| `SEVERITY_NUMBER`, `SEVERITIES`, `meetsSeverity(severity, minimum)` | OTLP's numbers, ascending, and the floor test |
|
|
132
|
+
| `SpanKind`, `SPAN_KIND_NUMBER` | `internal` 1, `server` 2, `client` 3, `producer` 4, `consumer` 5 |
|
|
133
|
+
| `SpanStatus` | `'ok' \| 'error' \| 'cancelled'` |
|
|
134
|
+
| `Resource` | `{ service, version?, environment?, attributes }` |
|
|
135
|
+
| `LogRecord`, `SpanRecord`, `SpanEvent`, `ErrorInfo` | the records an exporter receives |
|
|
136
|
+
| `Signal` | `LogRecord \| SpanRecord`, discriminated on `type` |
|
|
137
|
+
| `signalAt(signal)`, `signalSpan(signal)` | when it happened, and which span it belongs to, whichever kind it is |
|
|
138
|
+
|
|
139
|
+
## Traps
|
|
140
|
+
|
|
141
|
+
- **There is no `trace` severity.** Detail below `debug` belongs on a span as an
|
|
142
|
+
attribute, attached to the work it describes, rather than on a line somebody
|
|
143
|
+
has to correlate by hand.
|
|
144
|
+
- **Instants are epoch milliseconds**, which is the precision a JavaScript wall
|
|
145
|
+
clock has. OTLP wants nanoseconds, and the exporter multiplies; do not read a
|
|
146
|
+
sub-millisecond duration out of a span.
|
|
147
|
+
- **A nested list is not an attribute.** `['a', ['b']]` is flattened into text
|
|
148
|
+
rather than dropped — the value survives, but it stops being something a
|
|
149
|
+
backend can group by. Put structure in an event type.
|
|
150
|
+
- **`ratioSampler` throws on a bad ratio**, at construction. That is the one
|
|
151
|
+
place in this library that refuses an argument, and it is deliberate: it is
|
|
152
|
+
not on the path that writes a signal.
|
|
153
|
+
|
|
154
|
+
## License
|
|
155
|
+
|
|
156
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An attribute is a scalar, or a list of scalars. That is what a backend can
|
|
3
|
+
* index, filter and group by, and it is what OTLP accepts. Anything with
|
|
4
|
+
* structure is an event type, not an attribute.
|
|
5
|
+
*
|
|
6
|
+
* The coercion below never refuses a value: a log call must not fail, so a
|
|
7
|
+
* shape that does not belong here is rendered rather than rejected.
|
|
8
|
+
*/
|
|
9
|
+
export type AttributeScalar = string | number | boolean | null;
|
|
10
|
+
export type AttributeValue = AttributeScalar | readonly AttributeScalar[];
|
|
11
|
+
export type Attributes = Readonly<Record<string, AttributeValue>>;
|
|
12
|
+
/** Shared, frozen, and the one to return when there is nothing to say. */
|
|
13
|
+
export declare const EMPTY_ATTRIBUTES: Attributes;
|
|
14
|
+
/**
|
|
15
|
+
* Whatever a caller passed, as something that can be indexed.
|
|
16
|
+
*
|
|
17
|
+
* - `undefined` and a symbol render as `null`;
|
|
18
|
+
* - a non-finite number renders as its text, because `NaN` is not JSON;
|
|
19
|
+
* - a `bigint` renders as its text, for the same reason;
|
|
20
|
+
* - a `Date` renders as an ISO-8601 instant;
|
|
21
|
+
* - an array renders element by element, and a nested array is flattened into
|
|
22
|
+
* text rather than dropped;
|
|
23
|
+
* - anything else is JSON if it will serialise, and `String(value)` if it will
|
|
24
|
+
* not — a circular object still says something.
|
|
25
|
+
*/
|
|
26
|
+
export declare function coerceAttribute(value: unknown): AttributeValue;
|
|
27
|
+
/**
|
|
28
|
+
* A record of anything, as attributes. Keys whose value is `undefined` are
|
|
29
|
+
* dropped — `{ orderId, code }` with no `code` should not log `code: null` —
|
|
30
|
+
* while an explicit `null` is kept, because somebody wrote it.
|
|
31
|
+
*/
|
|
32
|
+
export declare function attributesOf(input: Readonly<Record<string, unknown>> | undefined): Attributes;
|
|
33
|
+
/** Both, with the right-hand side winning. Neither argument is modified. */
|
|
34
|
+
export declare function mergeAttributes(left: Attributes, right: Attributes): Attributes;
|
|
35
|
+
export declare function isEmptyAttributes(attributes: Attributes): boolean;
|
|
36
|
+
//# sourceMappingURL=attributes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attributes.d.ts","sourceRoot":"","sources":["../../src/attributes/attributes.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;AAE/D,MAAM,MAAM,cAAc,GAAG,eAAe,GAAG,SAAS,eAAe,EAAE,CAAC;AAE1E,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC;AAElE,0EAA0E;AAC1E,eAAO,MAAM,gBAAgB,EAAE,UAA8B,CAAC;AAE9D;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,CAwB9D;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAC3B,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,SAAS,GAClD,UAAU,CAcZ;AAED,4EAA4E;AAC5E,wBAAgB,eAAe,CAC9B,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,UAAU,GACf,UAAU,CAKZ;AAED,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAEjE"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC;AACxC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
// src/attributes/attributes.ts
|
|
2
|
+
var EMPTY_ATTRIBUTES = Object.freeze({});
|
|
3
|
+
function coerceAttribute(value) {
|
|
4
|
+
if (value === null || value === undefined)
|
|
5
|
+
return null;
|
|
6
|
+
switch (typeof value) {
|
|
7
|
+
case "string":
|
|
8
|
+
case "boolean":
|
|
9
|
+
return value;
|
|
10
|
+
case "number":
|
|
11
|
+
return Number.isFinite(value) ? value : String(value);
|
|
12
|
+
case "bigint":
|
|
13
|
+
return String(value);
|
|
14
|
+
case "symbol":
|
|
15
|
+
case "function":
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
if (value instanceof Date) {
|
|
19
|
+
const at = value.getTime();
|
|
20
|
+
return Number.isNaN(at) ? String(value) : value.toISOString();
|
|
21
|
+
}
|
|
22
|
+
if (Array.isArray(value))
|
|
23
|
+
return value.map(coerceScalar);
|
|
24
|
+
return render(value);
|
|
25
|
+
}
|
|
26
|
+
function attributesOf(input) {
|
|
27
|
+
if (input === undefined)
|
|
28
|
+
return EMPTY_ATTRIBUTES;
|
|
29
|
+
const attributes = {};
|
|
30
|
+
let any = false;
|
|
31
|
+
for (const key of Object.keys(input)) {
|
|
32
|
+
const value = input[key];
|
|
33
|
+
if (value === undefined)
|
|
34
|
+
continue;
|
|
35
|
+
attributes[key] = coerceAttribute(value);
|
|
36
|
+
any = true;
|
|
37
|
+
}
|
|
38
|
+
return any ? attributes : EMPTY_ATTRIBUTES;
|
|
39
|
+
}
|
|
40
|
+
function mergeAttributes(left, right) {
|
|
41
|
+
if (left === right)
|
|
42
|
+
return left;
|
|
43
|
+
if (isEmpty(left))
|
|
44
|
+
return right;
|
|
45
|
+
if (isEmpty(right))
|
|
46
|
+
return left;
|
|
47
|
+
return { ...left, ...right };
|
|
48
|
+
}
|
|
49
|
+
function isEmptyAttributes(attributes) {
|
|
50
|
+
return isEmpty(attributes);
|
|
51
|
+
}
|
|
52
|
+
function isEmpty(attributes) {
|
|
53
|
+
for (const _ in attributes)
|
|
54
|
+
return false;
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
function coerceScalar(value) {
|
|
58
|
+
const coerced = coerceAttribute(value);
|
|
59
|
+
return Array.isArray(coerced) ? render(value) : coerced;
|
|
60
|
+
}
|
|
61
|
+
function render(value) {
|
|
62
|
+
try {
|
|
63
|
+
const json = JSON.stringify(value);
|
|
64
|
+
if (json !== undefined)
|
|
65
|
+
return json;
|
|
66
|
+
} catch {}
|
|
67
|
+
try {
|
|
68
|
+
return String(value);
|
|
69
|
+
} catch {
|
|
70
|
+
return "[unrenderable]";
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// src/model/signal.ts
|
|
74
|
+
var SEVERITY_NUMBER = Object.freeze({
|
|
75
|
+
debug: 5,
|
|
76
|
+
info: 9,
|
|
77
|
+
warn: 13,
|
|
78
|
+
error: 17
|
|
79
|
+
});
|
|
80
|
+
var SEVERITIES = Object.freeze([
|
|
81
|
+
"debug",
|
|
82
|
+
"info",
|
|
83
|
+
"warn",
|
|
84
|
+
"error"
|
|
85
|
+
]);
|
|
86
|
+
function meetsSeverity(severity, minimum) {
|
|
87
|
+
return SEVERITY_NUMBER[severity] >= SEVERITY_NUMBER[minimum];
|
|
88
|
+
}
|
|
89
|
+
var SPAN_KIND_NUMBER = Object.freeze({
|
|
90
|
+
internal: 1,
|
|
91
|
+
server: 2,
|
|
92
|
+
client: 3,
|
|
93
|
+
producer: 4,
|
|
94
|
+
consumer: 5
|
|
95
|
+
});
|
|
96
|
+
function signalAt(signal) {
|
|
97
|
+
return signal.type === "log" ? signal.at : signal.startedAt;
|
|
98
|
+
}
|
|
99
|
+
function signalSpan(signal) {
|
|
100
|
+
return signal.type === "log" ? signal.span : signal.context;
|
|
101
|
+
}
|
|
102
|
+
// src/trace/ids.ts
|
|
103
|
+
var INVALID_TRACE_ID = "0".repeat(32);
|
|
104
|
+
var INVALID_SPAN_ID = "0".repeat(16);
|
|
105
|
+
var TRACE_ID_PATTERN = /^[0-9a-f]{32}$/;
|
|
106
|
+
var SPAN_ID_PATTERN = /^[0-9a-f]{16}$/;
|
|
107
|
+
var HEX_PATTERN = /^[0-9a-f]+$/;
|
|
108
|
+
function isValidTraceId(hex) {
|
|
109
|
+
return TRACE_ID_PATTERN.test(hex) && hex !== INVALID_TRACE_ID;
|
|
110
|
+
}
|
|
111
|
+
function isValidSpanId(hex) {
|
|
112
|
+
return SPAN_ID_PATTERN.test(hex) && hex !== INVALID_SPAN_ID;
|
|
113
|
+
}
|
|
114
|
+
function randomTraceId() {
|
|
115
|
+
return randomHex(16);
|
|
116
|
+
}
|
|
117
|
+
function randomSpanId() {
|
|
118
|
+
return randomHex(8);
|
|
119
|
+
}
|
|
120
|
+
var DETACHED_SPAN_CONTEXT = Object.freeze({
|
|
121
|
+
traceId: INVALID_TRACE_ID,
|
|
122
|
+
spanId: INVALID_SPAN_ID,
|
|
123
|
+
sampled: false,
|
|
124
|
+
remote: false
|
|
125
|
+
});
|
|
126
|
+
function isDetached(context) {
|
|
127
|
+
return !isValidTraceId(context.traceId);
|
|
128
|
+
}
|
|
129
|
+
function renderTraceparent(context) {
|
|
130
|
+
return `00-${context.traceId}-${context.spanId}-${context.sampled ? "01" : "00"}`;
|
|
131
|
+
}
|
|
132
|
+
function parseTraceparent(header) {
|
|
133
|
+
if (!header)
|
|
134
|
+
return null;
|
|
135
|
+
const parts = header.trim().toLowerCase().split("-");
|
|
136
|
+
if (parts.length < 4)
|
|
137
|
+
return null;
|
|
138
|
+
const [version, traceId, spanId, flags] = parts;
|
|
139
|
+
if (version.length !== 2 || !HEX_PATTERN.test(version) || version === "ff") {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
if (!isValidTraceId(traceId) || !isValidSpanId(spanId))
|
|
143
|
+
return null;
|
|
144
|
+
if (!HEX_PATTERN.test(flags))
|
|
145
|
+
return null;
|
|
146
|
+
const parsed = Number.parseInt(flags, 16);
|
|
147
|
+
if (Number.isNaN(parsed))
|
|
148
|
+
return null;
|
|
149
|
+
return {
|
|
150
|
+
traceId,
|
|
151
|
+
spanId,
|
|
152
|
+
sampled: (parsed & 1) === 1,
|
|
153
|
+
remote: true
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
var HEX = "0123456789abcdef";
|
|
157
|
+
function randomHex(bytes) {
|
|
158
|
+
const buffer = new Uint8Array(bytes);
|
|
159
|
+
crypto.getRandomValues(buffer);
|
|
160
|
+
let hex = "";
|
|
161
|
+
for (const byte of buffer) {
|
|
162
|
+
hex += HEX[byte >> 4];
|
|
163
|
+
hex += HEX[byte & 15];
|
|
164
|
+
}
|
|
165
|
+
return hex;
|
|
166
|
+
}
|
|
167
|
+
// src/trace/sampler.ts
|
|
168
|
+
var alwaysSample = { sample: () => true };
|
|
169
|
+
var neverSample = { sample: () => false };
|
|
170
|
+
var MAX_UINT64 = (1n << 64n) - 1n;
|
|
171
|
+
var SCALE = 1n << 53n;
|
|
172
|
+
function ratioSampler(ratio) {
|
|
173
|
+
if (!Number.isFinite(ratio) || ratio < 0 || ratio > 1) {
|
|
174
|
+
throw new RangeError(`A sampling ratio is between 0 and 1, not ${ratio}`);
|
|
175
|
+
}
|
|
176
|
+
if (ratio === 0)
|
|
177
|
+
return neverSample;
|
|
178
|
+
if (ratio === 1)
|
|
179
|
+
return alwaysSample;
|
|
180
|
+
const threshold = BigInt(Math.round(ratio * Number(SCALE))) * MAX_UINT64 / SCALE;
|
|
181
|
+
return {
|
|
182
|
+
sample(traceId) {
|
|
183
|
+
if (!isValidTraceId(traceId))
|
|
184
|
+
return false;
|
|
185
|
+
return BigInt(`0x${traceId.slice(-16)}`) < threshold;
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
export {
|
|
190
|
+
DETACHED_SPAN_CONTEXT,
|
|
191
|
+
EMPTY_ATTRIBUTES,
|
|
192
|
+
INVALID_SPAN_ID,
|
|
193
|
+
INVALID_TRACE_ID,
|
|
194
|
+
SEVERITIES,
|
|
195
|
+
SEVERITY_NUMBER,
|
|
196
|
+
SPAN_KIND_NUMBER,
|
|
197
|
+
alwaysSample,
|
|
198
|
+
attributesOf,
|
|
199
|
+
coerceAttribute,
|
|
200
|
+
isDetached,
|
|
201
|
+
isEmptyAttributes,
|
|
202
|
+
isValidSpanId,
|
|
203
|
+
isValidTraceId,
|
|
204
|
+
meetsSeverity,
|
|
205
|
+
mergeAttributes,
|
|
206
|
+
neverSample,
|
|
207
|
+
parseTraceparent,
|
|
208
|
+
randomSpanId,
|
|
209
|
+
randomTraceId,
|
|
210
|
+
ratioSampler,
|
|
211
|
+
renderTraceparent,
|
|
212
|
+
signalAt,
|
|
213
|
+
signalSpan
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
//# debugId=909718EB5895F3DC64756E2164756E21
|
|
217
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/attributes/attributes.ts", "../src/model/signal.ts", "../src/trace/ids.ts", "../src/trace/sampler.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * An attribute is a scalar, or a list of scalars. That is what a backend can\n * index, filter and group by, and it is what OTLP accepts. Anything with\n * structure is an event type, not an attribute.\n *\n * The coercion below never refuses a value: a log call must not fail, so a\n * shape that does not belong here is rendered rather than rejected.\n */\n\nexport type AttributeScalar = string | number | boolean | null;\n\nexport type AttributeValue = AttributeScalar | readonly AttributeScalar[];\n\nexport type Attributes = Readonly<Record<string, AttributeValue>>;\n\n/** Shared, frozen, and the one to return when there is nothing to say. */\nexport const EMPTY_ATTRIBUTES: Attributes = Object.freeze({});\n\n/**\n * Whatever a caller passed, as something that can be indexed.\n *\n * - `undefined` and a symbol render as `null`;\n * - a non-finite number renders as its text, because `NaN` is not JSON;\n * - a `bigint` renders as its text, for the same reason;\n * - a `Date` renders as an ISO-8601 instant;\n * - an array renders element by element, and a nested array is flattened into\n * text rather than dropped;\n * - anything else is JSON if it will serialise, and `String(value)` if it will\n * not — a circular object still says something.\n */\nexport function coerceAttribute(value: unknown): AttributeValue {\n\tif (value === null || value === undefined) return null;\n\n\tswitch (typeof value) {\n\t\tcase 'string':\n\t\tcase 'boolean':\n\t\t\treturn value;\n\t\tcase 'number':\n\t\t\treturn Number.isFinite(value) ? value : String(value);\n\t\tcase 'bigint':\n\t\t\treturn String(value);\n\t\tcase 'symbol':\n\t\tcase 'function':\n\t\t\treturn null;\n\t}\n\n\tif (value instanceof Date) {\n\t\tconst at = value.getTime();\n\t\treturn Number.isNaN(at) ? String(value) : value.toISOString();\n\t}\n\n\tif (Array.isArray(value)) return value.map(coerceScalar);\n\n\treturn render(value);\n}\n\n/**\n * A record of anything, as attributes. Keys whose value is `undefined` are\n * dropped — `{ orderId, code }` with no `code` should not log `code: null` —\n * while an explicit `null` is kept, because somebody wrote it.\n */\nexport function attributesOf(\n\tinput: Readonly<Record<string, unknown>> | undefined,\n): Attributes {\n\tif (input === undefined) return EMPTY_ATTRIBUTES;\n\n\tconst attributes: Record<string, AttributeValue> = {};\n\tlet any = false;\n\n\tfor (const key of Object.keys(input)) {\n\t\tconst value = input[key];\n\t\tif (value === undefined) continue;\n\t\tattributes[key] = coerceAttribute(value);\n\t\tany = true;\n\t}\n\n\treturn any ? attributes : EMPTY_ATTRIBUTES;\n}\n\n/** Both, with the right-hand side winning. Neither argument is modified. */\nexport function mergeAttributes(\n\tleft: Attributes,\n\tright: Attributes,\n): Attributes {\n\tif (left === right) return left;\n\tif (isEmpty(left)) return right;\n\tif (isEmpty(right)) return left;\n\treturn { ...left, ...right };\n}\n\nexport function isEmptyAttributes(attributes: Attributes): boolean {\n\treturn isEmpty(attributes);\n}\n\nfunction isEmpty(attributes: Attributes): boolean {\n\tfor (const _ in attributes) return false;\n\treturn true;\n}\n\nfunction coerceScalar(value: unknown): AttributeScalar {\n\tconst coerced = coerceAttribute(value);\n\treturn Array.isArray(coerced) ? render(value) : (coerced as AttributeScalar);\n}\n\nfunction render(value: unknown): string {\n\ttry {\n\t\tconst json = JSON.stringify(value);\n\t\tif (json !== undefined) return json;\n\t} catch {\n\t\t// Circular, or a `toJSON` that threw. `String` still says something.\n\t}\n\n\ttry {\n\t\treturn String(value);\n\t} catch {\n\t\treturn '[unrenderable]';\n\t}\n}\n",
|
|
6
|
+
"import type { Attributes } from '../attributes/attributes';\nimport type { SpanContext, SpanId } from '../trace/ids';\n\n/**\n * The signal model. It is shared with `stx-telemetry`, the Kotlin counterpart:\n * the same four severities, the same span kinds and statuses, the same\n * discriminator. Changing anything here means the two estates have diverged.\n *\n * Instants are epoch **milliseconds**, which is the precision a JavaScript wall\n * clock has. OTLP wants nanoseconds, and the exporter multiplies.\n */\n\n/**\n * Four levels, and there is no `trace`: detail below `debug` is an attribute on\n * a span, where it is attached to the work it describes rather than to a line\n * somebody has to correlate by hand.\n */\nexport type Severity = 'debug' | 'info' | 'warn' | 'error';\n\n/** OTLP's numbers, which is what makes a backend order them correctly. */\nexport const SEVERITY_NUMBER: Readonly<Record<Severity, number>> =\n\tObject.freeze({\n\t\tdebug: 5,\n\t\tinfo: 9,\n\t\twarn: 13,\n\t\terror: 17,\n\t});\n\n/** Ascending, so a floor can be compared by index as well as by number. */\nexport const SEVERITIES: readonly Severity[] = Object.freeze([\n\t'debug',\n\t'info',\n\t'warn',\n\t'error',\n] as const);\n\n/** Whether `severity` clears the `minimum` floor. */\nexport function meetsSeverity(severity: Severity, minimum: Severity): boolean {\n\treturn SEVERITY_NUMBER[severity] >= SEVERITY_NUMBER[minimum];\n}\n\nexport type SpanKind =\n\t| 'internal'\n\t| 'server'\n\t| 'client'\n\t| 'producer'\n\t| 'consumer';\n\nexport const SPAN_KIND_NUMBER: Readonly<Record<SpanKind, number>> =\n\tObject.freeze({\n\t\tinternal: 1,\n\t\tserver: 2,\n\t\tclient: 3,\n\t\tproducer: 4,\n\t\tconsumer: 5,\n\t});\n\n/**\n * `cancelled` is distinct from `error` on purpose: a shutdown and a timeout are\n * not failures, and a dashboard that counts them as such is a dashboard nobody\n * trusts.\n */\nexport type SpanStatus = 'ok' | 'error' | 'cancelled';\n\n/** What every signal from one telemetry is about. */\nexport interface Resource {\n\treadonly service: string;\n\treadonly version?: string;\n\treadonly environment?: string;\n\treadonly attributes: Attributes;\n}\n\n/** A failure, flattened: the stack is text, because it crosses the wire. */\nexport interface ErrorInfo {\n\treadonly type: string;\n\treadonly message?: string;\n\treadonly stackTrace?: string;\n}\n\nexport interface LogRecord {\n\treadonly type: 'log';\n\t/** Epoch milliseconds. */\n\treadonly at: number;\n\treadonly severity: Severity;\n\t/** The event's name, or the message when there is no type for it yet. */\n\treadonly name: string;\n\t/** The logger's name. It becomes the OTLP instrumentation scope. */\n\treadonly source: string;\n\treadonly attributes: Attributes;\n\t/** Absent for a log written outside any span. */\n\treadonly span?: SpanContext;\n\treadonly error?: ErrorInfo;\n}\n\nexport interface SpanEvent {\n\treadonly name: string;\n\t/** Epoch milliseconds. */\n\treadonly at: number;\n\treadonly attributes: Attributes;\n}\n\nexport interface SpanRecord {\n\treadonly type: 'span';\n\treadonly name: string;\n\treadonly context: SpanContext;\n\t/** Absent for a root span. */\n\treadonly parent?: SpanId;\n\treadonly kind: SpanKind;\n\t/** Epoch milliseconds. */\n\treadonly startedAt: number;\n\t/** Epoch milliseconds. */\n\treadonly endedAt: number;\n\treadonly status: SpanStatus;\n\treadonly attributes: Attributes;\n\treadonly events: readonly SpanEvent[];\n\treadonly error?: ErrorInfo;\n}\n\n/**\n * The discriminator is the field `type`, with the values `\"log\"` and `\"span\"`.\n * It is not a class name, so that metrics can arrive as a third variant without\n * anything in the model or in the `Exporter` contract changing.\n */\nexport type Signal = LogRecord | SpanRecord;\n\n/** When a signal happened, whichever kind it is. */\nexport function signalAt(signal: Signal): number {\n\treturn signal.type === 'log' ? signal.at : signal.startedAt;\n}\n\n/** The span a signal belongs to, whichever kind it is. */\nexport function signalSpan(signal: Signal): SpanContext | undefined {\n\treturn signal.type === 'log' ? signal.span : signal.context;\n}\n",
|
|
7
|
+
"/**\n * Trace identity, and the W3C `traceparent` header.\n *\n * The ids are branded strings: the hex representation is the representation, so\n * there is nothing to allocate and nothing to convert before writing a header,\n * and the brand still stops a span id being passed where a trace id belongs.\n */\n\ndeclare const traceIdBrand: unique symbol;\ndeclare const spanIdBrand: unique symbol;\n\n/** 32 lowercase hex characters. */\nexport type TraceId = string & { readonly [traceIdBrand]: true };\n\n/** 16 lowercase hex characters. */\nexport type SpanId = string & { readonly [spanIdBrand]: true };\n\nexport const INVALID_TRACE_ID = '0'.repeat(32) as TraceId;\nexport const INVALID_SPAN_ID = '0'.repeat(16) as SpanId;\n\nconst TRACE_ID_PATTERN = /^[0-9a-f]{32}$/;\nconst SPAN_ID_PATTERN = /^[0-9a-f]{16}$/;\nconst HEX_PATTERN = /^[0-9a-f]+$/;\n\nexport function isValidTraceId(hex: string): hex is TraceId {\n\treturn TRACE_ID_PATTERN.test(hex) && hex !== INVALID_TRACE_ID;\n}\n\nexport function isValidSpanId(hex: string): hex is SpanId {\n\treturn SPAN_ID_PATTERN.test(hex) && hex !== INVALID_SPAN_ID;\n}\n\n/**\n * From `crypto.getRandomValues`, never `Math.random()`: ids that collide join\n * two unrelated traces into one, which reads as a request that did work it\n * never did.\n */\nexport function randomTraceId(): TraceId {\n\treturn randomHex(16) as TraceId;\n}\n\nexport function randomSpanId(): SpanId {\n\treturn randomHex(8) as SpanId;\n}\n\n/** A span's place in a trace, as it travels. */\nexport interface SpanContext {\n\treadonly traceId: TraceId;\n\treadonly spanId: SpanId;\n\t/** Decided once, by the root, and inherited by everything below it. */\n\treadonly sampled: boolean;\n\t/** True when this context was parsed from an inbound header. */\n\treadonly remote: boolean;\n}\n\n/**\n * What a span carries when there is no telemetry installed. The block still\n * runs — a library that traces must work inside an application that has never\n * heard of this one — and nothing is emitted.\n */\nexport const DETACHED_SPAN_CONTEXT: SpanContext = Object.freeze({\n\ttraceId: INVALID_TRACE_ID,\n\tspanId: INVALID_SPAN_ID,\n\tsampled: false,\n\tremote: false,\n});\n\nexport function isDetached(context: SpanContext): boolean {\n\treturn !isValidTraceId(context.traceId);\n}\n\n/** `00-<trace id>-<span id>-<flags>`, version 00, the only one we write. */\nexport function renderTraceparent(context: SpanContext): string {\n\treturn `00-${context.traceId}-${context.spanId}-${context.sampled ? '01' : '00'}`;\n}\n\n/**\n * The inbound half. It **returns null, and never throws**: an unusable header\n * is a request that starts a fresh trace, not a request that fails.\n *\n * Rejected: fewer than four fields, a version that is not two characters, the\n * forbidden version `ff`, an id of the wrong length or all zeros or not hex,\n * and flags that are not hex. A later version is accepted and its extra fields\n * ignored, which is what the specification asks of a version-00 reader.\n *\n * `tracestate` is not read. Nothing here writes one either.\n */\nexport function parseTraceparent(\n\theader: string | null | undefined,\n): SpanContext | null {\n\tif (!header) return null;\n\n\tconst parts = header.trim().toLowerCase().split('-');\n\tif (parts.length < 4) return null;\n\n\tconst [version, traceId, spanId, flags] = parts as [\n\t\tstring,\n\t\tstring,\n\t\tstring,\n\t\tstring,\n\t];\n\n\tif (version.length !== 2 || !HEX_PATTERN.test(version) || version === 'ff') {\n\t\treturn null;\n\t}\n\tif (!isValidTraceId(traceId) || !isValidSpanId(spanId)) return null;\n\tif (!HEX_PATTERN.test(flags)) return null;\n\n\tconst parsed = Number.parseInt(flags, 16);\n\tif (Number.isNaN(parsed)) return null;\n\n\treturn {\n\t\ttraceId,\n\t\tspanId,\n\t\tsampled: (parsed & 0x01) === 0x01,\n\t\tremote: true,\n\t};\n}\n\nconst HEX = '0123456789abcdef';\n\nfunction randomHex(bytes: number): string {\n\tconst buffer = new Uint8Array(bytes);\n\tcrypto.getRandomValues(buffer);\n\n\tlet hex = '';\n\tfor (const byte of buffer) {\n\t\thex += HEX[byte >> 4];\n\t\thex += HEX[byte & 0x0f];\n\t}\n\treturn hex;\n}\n",
|
|
8
|
+
"import { isValidTraceId, type TraceId } from './ids';\n\n/**\n * Sampling is decided once, by the root span, and the answer travels with the\n * trace — down through every child and out over the wire in the `traceparent`.\n * A sampler consulted per span produces traces missing their middles, and a gap\n * in a trace looks like work that never happened.\n *\n * Logs are not sampled. That decision is about the volume of traces, and a log\n * dropped because its trace was not kept is a log missing at precisely the\n * moment somebody is reading logs to find out what happened.\n */\nexport interface Sampler {\n\tsample(traceId: TraceId): boolean;\n}\n\nexport const alwaysSample: Sampler = { sample: () => true };\n\nexport const neverSample: Sampler = { sample: () => false };\n\nconst MAX_UINT64 = (1n << 64n) - 1n;\nconst SCALE = 1n << 53n;\n\n/**\n * Keep this fraction of traces, decided **from the trace id** rather than by a\n * coin toss: two services at the same ratio then make the same decision about\n * the same trace. Two services tossing independently at 10% keep a whole trace\n * 1% of the time.\n *\n * The rule is OpenTelemetry's — the low 64 bits of the trace id against\n * `ratio × 2⁶⁴` — so anything else implementing it agrees with us.\n *\n * @throws RangeError if `ratio` is not between 0 and 1. This is a construction\n * error, raised where the mistake is, and never on the path that writes a\n * signal.\n */\nexport function ratioSampler(ratio: number): Sampler {\n\tif (!Number.isFinite(ratio) || ratio < 0 || ratio > 1) {\n\t\tthrow new RangeError(`A sampling ratio is between 0 and 1, not ${ratio}`);\n\t}\n\tif (ratio === 0) return neverSample;\n\tif (ratio === 1) return alwaysSample;\n\n\tconst threshold =\n\t\t(BigInt(Math.round(ratio * Number(SCALE))) * MAX_UINT64) / SCALE;\n\n\treturn {\n\t\tsample(traceId: TraceId): boolean {\n\t\t\tif (!isValidTraceId(traceId)) return false;\n\t\t\treturn BigInt(`0x${traceId.slice(-16)}`) < threshold;\n\t\t},\n\t};\n}\n"
|
|
9
|
+
],
|
|
10
|
+
"mappings": "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",
|
|
11
|
+
"debugId": "909718EB5895F3DC64756E2164756E21",
|
|
12
|
+
"names": []
|
|
13
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { Attributes } from '../attributes/attributes';
|
|
2
|
+
import type { SpanContext, SpanId } from '../trace/ids';
|
|
3
|
+
/**
|
|
4
|
+
* The signal model. It is shared with `stx-telemetry`, the Kotlin counterpart:
|
|
5
|
+
* the same four severities, the same span kinds and statuses, the same
|
|
6
|
+
* discriminator. Changing anything here means the two estates have diverged.
|
|
7
|
+
*
|
|
8
|
+
* Instants are epoch **milliseconds**, which is the precision a JavaScript wall
|
|
9
|
+
* clock has. OTLP wants nanoseconds, and the exporter multiplies.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Four levels, and there is no `trace`: detail below `debug` is an attribute on
|
|
13
|
+
* a span, where it is attached to the work it describes rather than to a line
|
|
14
|
+
* somebody has to correlate by hand.
|
|
15
|
+
*/
|
|
16
|
+
export type Severity = 'debug' | 'info' | 'warn' | 'error';
|
|
17
|
+
/** OTLP's numbers, which is what makes a backend order them correctly. */
|
|
18
|
+
export declare const SEVERITY_NUMBER: Readonly<Record<Severity, number>>;
|
|
19
|
+
/** Ascending, so a floor can be compared by index as well as by number. */
|
|
20
|
+
export declare const SEVERITIES: readonly Severity[];
|
|
21
|
+
/** Whether `severity` clears the `minimum` floor. */
|
|
22
|
+
export declare function meetsSeverity(severity: Severity, minimum: Severity): boolean;
|
|
23
|
+
export type SpanKind = 'internal' | 'server' | 'client' | 'producer' | 'consumer';
|
|
24
|
+
export declare const SPAN_KIND_NUMBER: Readonly<Record<SpanKind, number>>;
|
|
25
|
+
/**
|
|
26
|
+
* `cancelled` is distinct from `error` on purpose: a shutdown and a timeout are
|
|
27
|
+
* not failures, and a dashboard that counts them as such is a dashboard nobody
|
|
28
|
+
* trusts.
|
|
29
|
+
*/
|
|
30
|
+
export type SpanStatus = 'ok' | 'error' | 'cancelled';
|
|
31
|
+
/** What every signal from one telemetry is about. */
|
|
32
|
+
export interface Resource {
|
|
33
|
+
readonly service: string;
|
|
34
|
+
readonly version?: string;
|
|
35
|
+
readonly environment?: string;
|
|
36
|
+
readonly attributes: Attributes;
|
|
37
|
+
}
|
|
38
|
+
/** A failure, flattened: the stack is text, because it crosses the wire. */
|
|
39
|
+
export interface ErrorInfo {
|
|
40
|
+
readonly type: string;
|
|
41
|
+
readonly message?: string;
|
|
42
|
+
readonly stackTrace?: string;
|
|
43
|
+
}
|
|
44
|
+
export interface LogRecord {
|
|
45
|
+
readonly type: 'log';
|
|
46
|
+
/** Epoch milliseconds. */
|
|
47
|
+
readonly at: number;
|
|
48
|
+
readonly severity: Severity;
|
|
49
|
+
/** The event's name, or the message when there is no type for it yet. */
|
|
50
|
+
readonly name: string;
|
|
51
|
+
/** The logger's name. It becomes the OTLP instrumentation scope. */
|
|
52
|
+
readonly source: string;
|
|
53
|
+
readonly attributes: Attributes;
|
|
54
|
+
/** Absent for a log written outside any span. */
|
|
55
|
+
readonly span?: SpanContext;
|
|
56
|
+
readonly error?: ErrorInfo;
|
|
57
|
+
}
|
|
58
|
+
export interface SpanEvent {
|
|
59
|
+
readonly name: string;
|
|
60
|
+
/** Epoch milliseconds. */
|
|
61
|
+
readonly at: number;
|
|
62
|
+
readonly attributes: Attributes;
|
|
63
|
+
}
|
|
64
|
+
export interface SpanRecord {
|
|
65
|
+
readonly type: 'span';
|
|
66
|
+
readonly name: string;
|
|
67
|
+
readonly context: SpanContext;
|
|
68
|
+
/** Absent for a root span. */
|
|
69
|
+
readonly parent?: SpanId;
|
|
70
|
+
readonly kind: SpanKind;
|
|
71
|
+
/** Epoch milliseconds. */
|
|
72
|
+
readonly startedAt: number;
|
|
73
|
+
/** Epoch milliseconds. */
|
|
74
|
+
readonly endedAt: number;
|
|
75
|
+
readonly status: SpanStatus;
|
|
76
|
+
readonly attributes: Attributes;
|
|
77
|
+
readonly events: readonly SpanEvent[];
|
|
78
|
+
readonly error?: ErrorInfo;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* The discriminator is the field `type`, with the values `"log"` and `"span"`.
|
|
82
|
+
* It is not a class name, so that metrics can arrive as a third variant without
|
|
83
|
+
* anything in the model or in the `Exporter` contract changing.
|
|
84
|
+
*/
|
|
85
|
+
export type Signal = LogRecord | SpanRecord;
|
|
86
|
+
/** When a signal happened, whichever kind it is. */
|
|
87
|
+
export declare function signalAt(signal: Signal): number;
|
|
88
|
+
/** The span a signal belongs to, whichever kind it is. */
|
|
89
|
+
export declare function signalSpan(signal: Signal): SpanContext | undefined;
|
|
90
|
+
//# sourceMappingURL=signal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signal.d.ts","sourceRoot":"","sources":["../../src/model/signal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAExD;;;;;;;GAOG;AAEH;;;;GAIG;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAE3D,0EAA0E;AAC1E,eAAO,MAAM,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAM5D,CAAC;AAEJ,2EAA2E;AAC3E,eAAO,MAAM,UAAU,EAAE,SAAS,QAAQ,EAK/B,CAAC;AAEZ,qDAAqD;AACrD,wBAAgB,aAAa,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,GAAG,OAAO,CAE5E;AAED,MAAM,MAAM,QAAQ,GACjB,UAAU,GACV,QAAQ,GACR,QAAQ,GACR,UAAU,GACV,UAAU,CAAC;AAEd,eAAO,MAAM,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAO7D,CAAC;AAEJ;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,OAAO,GAAG,WAAW,CAAC;AAEtD,qDAAqD;AACrD,MAAM,WAAW,QAAQ;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;CAChC;AAED,4EAA4E;AAC5E,MAAM,WAAW,SAAS;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,SAAS;IACzB,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IACrB,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,yEAAyE;IACzE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,oEAAoE;IACpE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,iDAAiD;IACjD,QAAQ,CAAC,IAAI,CAAC,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC;CAC3B;AAED,MAAM,WAAW,SAAS;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;CAChC;AAED,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,8BAA8B;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,0BAA0B;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,0BAA0B;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,CAAC;IACtC,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC;CAC3B;AAED;;;;GAIG;AACH,MAAM,MAAM,MAAM,GAAG,SAAS,GAAG,UAAU,CAAC;AAE5C,oDAAoD;AACpD,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,0DAA0D;AAC1D,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAElE"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trace identity, and the W3C `traceparent` header.
|
|
3
|
+
*
|
|
4
|
+
* The ids are branded strings: the hex representation is the representation, so
|
|
5
|
+
* there is nothing to allocate and nothing to convert before writing a header,
|
|
6
|
+
* and the brand still stops a span id being passed where a trace id belongs.
|
|
7
|
+
*/
|
|
8
|
+
declare const traceIdBrand: unique symbol;
|
|
9
|
+
declare const spanIdBrand: unique symbol;
|
|
10
|
+
/** 32 lowercase hex characters. */
|
|
11
|
+
export type TraceId = string & {
|
|
12
|
+
readonly [traceIdBrand]: true;
|
|
13
|
+
};
|
|
14
|
+
/** 16 lowercase hex characters. */
|
|
15
|
+
export type SpanId = string & {
|
|
16
|
+
readonly [spanIdBrand]: true;
|
|
17
|
+
};
|
|
18
|
+
export declare const INVALID_TRACE_ID: TraceId;
|
|
19
|
+
export declare const INVALID_SPAN_ID: SpanId;
|
|
20
|
+
export declare function isValidTraceId(hex: string): hex is TraceId;
|
|
21
|
+
export declare function isValidSpanId(hex: string): hex is SpanId;
|
|
22
|
+
/**
|
|
23
|
+
* From `crypto.getRandomValues`, never `Math.random()`: ids that collide join
|
|
24
|
+
* two unrelated traces into one, which reads as a request that did work it
|
|
25
|
+
* never did.
|
|
26
|
+
*/
|
|
27
|
+
export declare function randomTraceId(): TraceId;
|
|
28
|
+
export declare function randomSpanId(): SpanId;
|
|
29
|
+
/** A span's place in a trace, as it travels. */
|
|
30
|
+
export interface SpanContext {
|
|
31
|
+
readonly traceId: TraceId;
|
|
32
|
+
readonly spanId: SpanId;
|
|
33
|
+
/** Decided once, by the root, and inherited by everything below it. */
|
|
34
|
+
readonly sampled: boolean;
|
|
35
|
+
/** True when this context was parsed from an inbound header. */
|
|
36
|
+
readonly remote: boolean;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* What a span carries when there is no telemetry installed. The block still
|
|
40
|
+
* runs — a library that traces must work inside an application that has never
|
|
41
|
+
* heard of this one — and nothing is emitted.
|
|
42
|
+
*/
|
|
43
|
+
export declare const DETACHED_SPAN_CONTEXT: SpanContext;
|
|
44
|
+
export declare function isDetached(context: SpanContext): boolean;
|
|
45
|
+
/** `00-<trace id>-<span id>-<flags>`, version 00, the only one we write. */
|
|
46
|
+
export declare function renderTraceparent(context: SpanContext): string;
|
|
47
|
+
/**
|
|
48
|
+
* The inbound half. It **returns null, and never throws**: an unusable header
|
|
49
|
+
* is a request that starts a fresh trace, not a request that fails.
|
|
50
|
+
*
|
|
51
|
+
* Rejected: fewer than four fields, a version that is not two characters, the
|
|
52
|
+
* forbidden version `ff`, an id of the wrong length or all zeros or not hex,
|
|
53
|
+
* and flags that are not hex. A later version is accepted and its extra fields
|
|
54
|
+
* ignored, which is what the specification asks of a version-00 reader.
|
|
55
|
+
*
|
|
56
|
+
* `tracestate` is not read. Nothing here writes one either.
|
|
57
|
+
*/
|
|
58
|
+
export declare function parseTraceparent(header: string | null | undefined): SpanContext | null;
|
|
59
|
+
export {};
|
|
60
|
+
//# sourceMappingURL=ids.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ids.d.ts","sourceRoot":"","sources":["../../src/trace/ids.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,CAAC,MAAM,YAAY,EAAE,OAAO,MAAM,CAAC;AAC1C,OAAO,CAAC,MAAM,WAAW,EAAE,OAAO,MAAM,CAAC;AAEzC,mCAAmC;AACnC,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,CAAC,YAAY,CAAC,EAAE,IAAI,CAAA;CAAE,CAAC;AAEjE,mCAAmC;AACnC,MAAM,MAAM,MAAM,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,CAAC,WAAW,CAAC,EAAE,IAAI,CAAA;CAAE,CAAC;AAE/D,eAAO,MAAM,gBAAgB,EAAqB,OAAO,CAAC;AAC1D,eAAO,MAAM,eAAe,EAAqB,MAAM,CAAC;AAMxD,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,IAAI,OAAO,CAE1D;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,IAAI,MAAM,CAExD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAEvC;AAED,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED,gDAAgD;AAChD,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,uEAAuE;IACvE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,gEAAgE;IAChE,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CACzB;AAED;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,EAAE,WAKlC,CAAC;AAEH,wBAAgB,UAAU,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAExD;AAED,4EAA4E;AAC5E,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAE9D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAC/B,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC/B,WAAW,GAAG,IAAI,CA4BpB"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type TraceId } from './ids';
|
|
2
|
+
/**
|
|
3
|
+
* Sampling is decided once, by the root span, and the answer travels with the
|
|
4
|
+
* trace — down through every child and out over the wire in the `traceparent`.
|
|
5
|
+
* A sampler consulted per span produces traces missing their middles, and a gap
|
|
6
|
+
* in a trace looks like work that never happened.
|
|
7
|
+
*
|
|
8
|
+
* Logs are not sampled. That decision is about the volume of traces, and a log
|
|
9
|
+
* dropped because its trace was not kept is a log missing at precisely the
|
|
10
|
+
* moment somebody is reading logs to find out what happened.
|
|
11
|
+
*/
|
|
12
|
+
export interface Sampler {
|
|
13
|
+
sample(traceId: TraceId): boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare const alwaysSample: Sampler;
|
|
16
|
+
export declare const neverSample: Sampler;
|
|
17
|
+
/**
|
|
18
|
+
* Keep this fraction of traces, decided **from the trace id** rather than by a
|
|
19
|
+
* coin toss: two services at the same ratio then make the same decision about
|
|
20
|
+
* the same trace. Two services tossing independently at 10% keep a whole trace
|
|
21
|
+
* 1% of the time.
|
|
22
|
+
*
|
|
23
|
+
* The rule is OpenTelemetry's — the low 64 bits of the trace id against
|
|
24
|
+
* `ratio × 2⁶⁴` — so anything else implementing it agrees with us.
|
|
25
|
+
*
|
|
26
|
+
* @throws RangeError if `ratio` is not between 0 and 1. This is a construction
|
|
27
|
+
* error, raised where the mistake is, and never on the path that writes a
|
|
28
|
+
* signal.
|
|
29
|
+
*/
|
|
30
|
+
export declare function ratioSampler(ratio: number): Sampler;
|
|
31
|
+
//# sourceMappingURL=sampler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sampler.d.ts","sourceRoot":"","sources":["../../src/trace/sampler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,OAAO,EAAE,MAAM,OAAO,CAAC;AAErD;;;;;;;;;GASG;AACH,MAAM,WAAW,OAAO;IACvB,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC;CAClC;AAED,eAAO,MAAM,YAAY,EAAE,OAAgC,CAAC;AAE5D,eAAO,MAAM,WAAW,EAAE,OAAiC,CAAC;AAK5D;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAgBnD"}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nxgt/telemetry",
|
|
3
|
+
"version": "0.1.0",
|
|
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
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"README.md",
|
|
12
|
+
"package.json",
|
|
13
|
+
"LICENSE"
|
|
14
|
+
],
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"import": "./dist/index.js",
|
|
19
|
+
"default": "./dist/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./package.json": "./package.json"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"telemetry",
|
|
25
|
+
"observability",
|
|
26
|
+
"tracing",
|
|
27
|
+
"logging",
|
|
28
|
+
"opentelemetry",
|
|
29
|
+
"otlp",
|
|
30
|
+
"traceparent",
|
|
31
|
+
"typescript"
|
|
32
|
+
],
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "git+https://github.com/softistx/nxgt-telemetry.git",
|
|
36
|
+
"directory": "packages/telemetry"
|
|
37
|
+
},
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"registry": "https://registry.npmjs.org",
|
|
40
|
+
"access": "public"
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "bun run ../../build.ts",
|
|
44
|
+
"test": "bun test src",
|
|
45
|
+
"typecheck": "tsc --noEmit"
|
|
46
|
+
},
|
|
47
|
+
"nxgt": {
|
|
48
|
+
"entrypoints": [
|
|
49
|
+
"src/index.ts"
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@types/bun": "^1.4.0"
|
|
54
|
+
},
|
|
55
|
+
"peerDependencies": {
|
|
56
|
+
"typescript": "^6.0.3"
|
|
57
|
+
}
|
|
58
|
+
}
|