@vercube/telemetry 1.3.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 +87 -0
- package/dist/Api.d.mts +1 -0
- package/dist/Api.mjs +2 -0
- package/dist/Attributes-QVHj8ZgV.mjs +48 -0
- package/dist/Attributes.d.mts +47 -0
- package/dist/Attributes.mjs +2 -0
- package/dist/Instrument.d.mts +132 -0
- package/dist/Instrument.mjs +103 -0
- package/dist/Otlp.d.mts +31 -0
- package/dist/Otlp.mjs +52 -0
- package/dist/Sdk.d.mts +156 -0
- package/dist/Sdk.mjs +229 -0
- package/dist/SpanUtils-t3NCeswB.mjs +147 -0
- package/dist/Testing.d.mts +53 -0
- package/dist/Testing.mjs +63 -0
- package/dist/VercubeContextManager-n4jHlB-w.mjs +197 -0
- package/dist/index.d.mts +125 -0
- package/dist/index.mjs +1085 -0
- package/package.json +99 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-present - Vercube
|
|
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,87 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/vercube/vercube/refs/heads/main/.github/assets/cover.png" width="100%" alt="Vercube - Unleash your server development." />
|
|
3
|
+
<br>
|
|
4
|
+
<br>
|
|
5
|
+
|
|
6
|
+
# @vercube/telemetry
|
|
7
|
+
|
|
8
|
+
### OpenTelemetry for Vercube apps
|
|
9
|
+
|
|
10
|
+
[&labelColor=%23000&color=%232f2f2f>)](https://deepwiki.com/vercube/vercube)
|
|
11
|
+
&labelColor=%23000&color=%232e2e2e&link=https%3A%2F%2Fwww.npmjs.com%2Fpackage%2F%40vercube%2Ftelemetry>)
|
|
12
|
+
&labelColor=%23000&color=%232f2f2f>)
|
|
13
|
+
&labelColor=%23000&color=%232f2f2f>)
|
|
14
|
+
|
|
15
|
+
**Standards-first observability: every request becomes an OpenTelemetry span, W3C trace context flows in and out, and any OTLP backend works. Off in production until you opt in, and free when it is off.**
|
|
16
|
+
|
|
17
|
+
[Website](https://vercube.dev) • [Documentation](https://vercube.dev/docs/getting-started)
|
|
18
|
+
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
## ✨ Features
|
|
22
|
+
|
|
23
|
+
- **A span per request** - route template, controller, handler and the stable HTTP semantic conventions
|
|
24
|
+
- **W3C trace context** - `traceparent` read on the way in, injectable on the way out
|
|
25
|
+
- **One AsyncLocalStorage** - trace context rides in the request context Vercube already opens, not a second frame
|
|
26
|
+
- **One OpenTelemetry dependency** - this is the only package in the framework that declares one; everything else reaches OpenTelemetry through a subpath here
|
|
27
|
+
- **Zero cost when off** - core sees a `null` check, and the allocation-free route fast path stays synchronous
|
|
28
|
+
|
|
29
|
+
## 📦 Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pnpm add @vercube/telemetry
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
That is the whole installation: the tracer and meter providers, the samplers and the in-memory test providers all ship with it. Exporting to an OTLP collector is the one thing that needs more, because the exporter pulls a protobuf stack an application doing local tracing has no use for:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pnpm add @opentelemetry/exporter-trace-otlp-http
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## 📖 Usage
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
// vercube.config.ts
|
|
45
|
+
import { defineConfig } from '@vercube/core';
|
|
46
|
+
import { TelemetryPlugin } from '@vercube/telemetry';
|
|
47
|
+
|
|
48
|
+
export default defineConfig({
|
|
49
|
+
telemetry: true,
|
|
50
|
+
plugins: [TelemetryPlugin],
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
// src/index.ts
|
|
56
|
+
import { startNodeTelemetry } from '@vercube/telemetry/sdk';
|
|
57
|
+
|
|
58
|
+
await startNodeTelemetry({
|
|
59
|
+
serviceName: 'checkout',
|
|
60
|
+
endpoint: 'http://localhost:4318',
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Annotate your own work with the `Telemetry` token:
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
import { Inject } from '@vercube/di';
|
|
68
|
+
import { Telemetry } from '@vercube/telemetry';
|
|
69
|
+
|
|
70
|
+
class InvoiceService {
|
|
71
|
+
@Inject(Telemetry)
|
|
72
|
+
private gTelemetry!: Telemetry;
|
|
73
|
+
|
|
74
|
+
public refund(id: string) {
|
|
75
|
+
return this.gTelemetry.span('invoice.refund', (span) => {
|
|
76
|
+
span.setAttribute('invoice.id', id);
|
|
77
|
+
return this.process(id);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Check out the full [documentation](https://vercube.dev/docs/modules/telemetry/overview)
|
|
84
|
+
|
|
85
|
+
## 📜 License
|
|
86
|
+
|
|
87
|
+
[MIT](https://github.com/vercube/vercube/blob/main/LICENSE)
|
package/dist/Api.d.mts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "@opentelemetry/api";
|
package/dist/Api.mjs
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
//#region src/Common/Attributes.ts
|
|
2
|
+
/**
|
|
3
|
+
* Attribute keys used by Vercube's instrumentation.
|
|
4
|
+
*
|
|
5
|
+
* The HTTP names are the stable OpenTelemetry semantic conventions. They are
|
|
6
|
+
* written out here as plain constants rather than imported from
|
|
7
|
+
* `@opentelemetry/semantic-conventions` so that installing `@vercube/telemetry`
|
|
8
|
+
* does not drag in a second package for a handful of strings; the values are
|
|
9
|
+
* asserted against the published conventions in the package tests.
|
|
10
|
+
*/
|
|
11
|
+
/** HTTP request method, e.g. `GET`. */
|
|
12
|
+
const HTTP_REQUEST_METHOD = "http.request.method";
|
|
13
|
+
/** Matched route template, e.g. `/users/:id`. */
|
|
14
|
+
const HTTP_ROUTE = "http.route";
|
|
15
|
+
/** HTTP response status code. */
|
|
16
|
+
const HTTP_RESPONSE_STATUS_CODE = "http.response.status_code";
|
|
17
|
+
/** Request path, without the query string. */
|
|
18
|
+
const URL_PATH = "url.path";
|
|
19
|
+
/** Query string, without the leading `?`. */
|
|
20
|
+
const URL_QUERY = "url.query";
|
|
21
|
+
/** URL scheme, `http` or `https`. */
|
|
22
|
+
const URL_SCHEME = "url.scheme";
|
|
23
|
+
/** Host the request was addressed to. */
|
|
24
|
+
const SERVER_ADDRESS = "server.address";
|
|
25
|
+
/** Port the request was addressed to. */
|
|
26
|
+
const SERVER_PORT = "server.port";
|
|
27
|
+
/** Raw `User-Agent` header. */
|
|
28
|
+
const USER_AGENT_ORIGINAL = "user_agent.original";
|
|
29
|
+
/** Class name or type of the error that made the operation fail. */
|
|
30
|
+
const ERROR_TYPE = "error.type";
|
|
31
|
+
/** Controller class that owns the matched handler. */
|
|
32
|
+
const VERCUBE_CONTROLLER = "vercube.controller";
|
|
33
|
+
/** Handler method that served the request. */
|
|
34
|
+
const VERCUBE_HANDLER = "vercube.handler";
|
|
35
|
+
/** Middleware class name. */
|
|
36
|
+
const VERCUBE_MIDDLEWARE = "vercube.middleware";
|
|
37
|
+
/** Middleware phase, `before` or `after`. */
|
|
38
|
+
const VERCUBE_MIDDLEWARE_PHASE = "vercube.middleware.phase";
|
|
39
|
+
/** Dependency-injection service key. */
|
|
40
|
+
const VERCUBE_DI_KEY = "vercube.di.key";
|
|
41
|
+
/** Dependency-injection binding kind: `singleton`, `transient` or `instance`. */
|
|
42
|
+
const VERCUBE_DI_KIND = "vercube.di.kind";
|
|
43
|
+
/** Whether the route was served by the allocation-free fast path. */
|
|
44
|
+
const VERCUBE_ROUTE_SIMPLE = "vercube.route.simple";
|
|
45
|
+
/** Instrumentation scope name reported for framework spans. */
|
|
46
|
+
const INSTRUMENTATION_SCOPE = "@vercube/telemetry";
|
|
47
|
+
//#endregion
|
|
48
|
+
export { VERCUBE_MIDDLEWARE_PHASE as _, INSTRUMENTATION_SCOPE as a, URL_PATH as c, USER_AGENT_ORIGINAL as d, VERCUBE_CONTROLLER as f, VERCUBE_MIDDLEWARE as g, VERCUBE_HANDLER as h, HTTP_ROUTE as i, URL_QUERY as l, VERCUBE_DI_KIND as m, HTTP_REQUEST_METHOD as n, SERVER_ADDRESS as o, VERCUBE_DI_KEY as p, HTTP_RESPONSE_STATUS_CODE as r, SERVER_PORT as s, ERROR_TYPE as t, URL_SCHEME as u, VERCUBE_ROUTE_SIMPLE as v };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
//#region src/Common/Attributes.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Attribute keys used by Vercube's instrumentation.
|
|
4
|
+
*
|
|
5
|
+
* The HTTP names are the stable OpenTelemetry semantic conventions. They are
|
|
6
|
+
* written out here as plain constants rather than imported from
|
|
7
|
+
* `@opentelemetry/semantic-conventions` so that installing `@vercube/telemetry`
|
|
8
|
+
* does not drag in a second package for a handful of strings; the values are
|
|
9
|
+
* asserted against the published conventions in the package tests.
|
|
10
|
+
*/
|
|
11
|
+
/** HTTP request method, e.g. `GET`. */
|
|
12
|
+
export declare const HTTP_REQUEST_METHOD = "http.request.method";
|
|
13
|
+
/** Matched route template, e.g. `/users/:id`. */
|
|
14
|
+
export declare const HTTP_ROUTE = "http.route";
|
|
15
|
+
/** HTTP response status code. */
|
|
16
|
+
export declare const HTTP_RESPONSE_STATUS_CODE = "http.response.status_code";
|
|
17
|
+
/** Request path, without the query string. */
|
|
18
|
+
export declare const URL_PATH = "url.path";
|
|
19
|
+
/** Query string, without the leading `?`. */
|
|
20
|
+
export declare const URL_QUERY = "url.query";
|
|
21
|
+
/** URL scheme, `http` or `https`. */
|
|
22
|
+
export declare const URL_SCHEME = "url.scheme";
|
|
23
|
+
/** Host the request was addressed to. */
|
|
24
|
+
export declare const SERVER_ADDRESS = "server.address";
|
|
25
|
+
/** Port the request was addressed to. */
|
|
26
|
+
export declare const SERVER_PORT = "server.port";
|
|
27
|
+
/** Raw `User-Agent` header. */
|
|
28
|
+
export declare const USER_AGENT_ORIGINAL = "user_agent.original";
|
|
29
|
+
/** Class name or type of the error that made the operation fail. */
|
|
30
|
+
export declare const ERROR_TYPE = "error.type";
|
|
31
|
+
/** Controller class that owns the matched handler. */
|
|
32
|
+
export declare const VERCUBE_CONTROLLER = "vercube.controller";
|
|
33
|
+
/** Handler method that served the request. */
|
|
34
|
+
export declare const VERCUBE_HANDLER = "vercube.handler";
|
|
35
|
+
/** Middleware class name. */
|
|
36
|
+
export declare const VERCUBE_MIDDLEWARE = "vercube.middleware";
|
|
37
|
+
/** Middleware phase, `before` or `after`. */
|
|
38
|
+
export declare const VERCUBE_MIDDLEWARE_PHASE = "vercube.middleware.phase";
|
|
39
|
+
/** Dependency-injection service key. */
|
|
40
|
+
export declare const VERCUBE_DI_KEY = "vercube.di.key";
|
|
41
|
+
/** Dependency-injection binding kind: `singleton`, `transient` or `instance`. */
|
|
42
|
+
export declare const VERCUBE_DI_KIND = "vercube.di.kind";
|
|
43
|
+
/** Whether the route was served by the allocation-free fast path. */
|
|
44
|
+
export declare const VERCUBE_ROUTE_SIMPLE = "vercube.route.simple";
|
|
45
|
+
/** Instrumentation scope name reported for framework spans. */
|
|
46
|
+
export declare const INSTRUMENTATION_SCOPE = "@vercube/telemetry";
|
|
47
|
+
//#endregion
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { _ as VERCUBE_MIDDLEWARE_PHASE, a as INSTRUMENTATION_SCOPE, c as URL_PATH, d as USER_AGENT_ORIGINAL, f as VERCUBE_CONTROLLER, g as VERCUBE_MIDDLEWARE, h as VERCUBE_HANDLER, i as HTTP_ROUTE, l as URL_QUERY, m as VERCUBE_DI_KIND, n as HTTP_REQUEST_METHOD, o as SERVER_ADDRESS, p as VERCUBE_DI_KEY, r as HTTP_RESPONSE_STATUS_CODE, s as SERVER_PORT, t as ERROR_TYPE, u as URL_SCHEME, v as VERCUBE_ROUTE_SIMPLE } from "./Attributes-QVHj8ZgV.mjs";
|
|
2
|
+
export { ERROR_TYPE, HTTP_REQUEST_METHOD, HTTP_RESPONSE_STATUS_CODE, HTTP_ROUTE, INSTRUMENTATION_SCOPE, SERVER_ADDRESS, SERVER_PORT, URL_PATH, URL_QUERY, URL_SCHEME, USER_AGENT_ORIGINAL, VERCUBE_CONTROLLER, VERCUBE_DI_KEY, VERCUBE_DI_KIND, VERCUBE_HANDLER, VERCUBE_MIDDLEWARE, VERCUBE_MIDDLEWARE_PHASE, VERCUBE_ROUTE_SIMPLE };
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { Attributes, Context, Context as Context$1, Counter, Counter as Counter$1, Exception, Histogram, Histogram as Histogram$1, Link, MetricOptions, MetricOptions as MetricOptions$1, Span, Span as Span$1, SpanContext, SpanKind, SpanOptions, SpanOptions as SpanOptions$1, SpanStatusCode, Tracer, UpDownCounter, UpDownCounter as UpDownCounter$1, ValueType } from "@opentelemetry/api";
|
|
2
|
+
//#region src/Instrument/Factory.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* The instrumentation toolkit an instrumented package works against.
|
|
5
|
+
*
|
|
6
|
+
* Every method is a no-op until the application registers a provider, which is
|
|
7
|
+
* what lets a package produce telemetry unconditionally.
|
|
8
|
+
*/
|
|
9
|
+
interface Instrument {
|
|
10
|
+
/**
|
|
11
|
+
* Runs `fn` in a new span parented on the active context.
|
|
12
|
+
*
|
|
13
|
+
* The result is returned unchanged, so wrapping synchronous code does not
|
|
14
|
+
* make it asynchronous. A synchronous throw, a rejection and a plain return
|
|
15
|
+
* all end the span.
|
|
16
|
+
*
|
|
17
|
+
* @param name - Span name
|
|
18
|
+
* @param options - Span kind, attributes and links
|
|
19
|
+
* @param fn - The work to trace
|
|
20
|
+
* @returns Whatever `fn` returned
|
|
21
|
+
*/
|
|
22
|
+
span<T>(name: string, options: SpanOptions$1, fn: (span: Span$1) => T): T;
|
|
23
|
+
/**
|
|
24
|
+
* Same as {@link Instrument.span}, parented on an explicit context.
|
|
25
|
+
*
|
|
26
|
+
* This is how a background job continues the trace of the request that
|
|
27
|
+
* queued it: the parent comes from {@link Instrument.extract} rather than
|
|
28
|
+
* from whatever happens to be active in the worker.
|
|
29
|
+
*
|
|
30
|
+
* @param name - Span name
|
|
31
|
+
* @param options - Span kind, attributes and links
|
|
32
|
+
* @param parent - Context the span is a child of
|
|
33
|
+
* @param fn - The work to trace
|
|
34
|
+
* @returns Whatever `fn` returned
|
|
35
|
+
*/
|
|
36
|
+
spanFrom<T>(name: string, options: SpanOptions$1, parent: Context$1, fn: (span: Span$1) => T): T;
|
|
37
|
+
/**
|
|
38
|
+
* A monotonic counter, created on first use and memoized by name.
|
|
39
|
+
*
|
|
40
|
+
* @param name - Instrument name
|
|
41
|
+
* @param options - Description, unit and value type
|
|
42
|
+
* @returns The counter
|
|
43
|
+
*/
|
|
44
|
+
counter(name: string, options?: MetricOptions$1): Counter$1;
|
|
45
|
+
/**
|
|
46
|
+
* A counter that can go down, created on first use and memoized by name.
|
|
47
|
+
*
|
|
48
|
+
* @param name - Instrument name
|
|
49
|
+
* @param options - Description, unit and value type
|
|
50
|
+
* @returns The counter
|
|
51
|
+
*/
|
|
52
|
+
upDownCounter(name: string, options?: MetricOptions$1): UpDownCounter$1;
|
|
53
|
+
/**
|
|
54
|
+
* A histogram, created on first use and memoized by name.
|
|
55
|
+
*
|
|
56
|
+
* @param name - Instrument name
|
|
57
|
+
* @param options - Description, unit and value type
|
|
58
|
+
* @returns The histogram
|
|
59
|
+
*/
|
|
60
|
+
histogram(name: string, options?: MetricOptions$1): Histogram$1;
|
|
61
|
+
/** The span active on this async execution path, if any. */
|
|
62
|
+
activeSpan(): Span$1 | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* Writes W3C trace context for the active span into a carrier.
|
|
65
|
+
*
|
|
66
|
+
* @param carrier - Header record to write into
|
|
67
|
+
*/
|
|
68
|
+
inject(carrier: Record<string, string>): void;
|
|
69
|
+
/**
|
|
70
|
+
* Reads W3C trace context out of a carrier into a context usable as a parent.
|
|
71
|
+
*
|
|
72
|
+
* @param carrier - Header record the message arrived with
|
|
73
|
+
* @returns A context carrying the remote parent
|
|
74
|
+
*/
|
|
75
|
+
extract(carrier: Record<string, string> | undefined): Context$1;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Builds the instrumentation toolkit for one instrumentation scope.
|
|
79
|
+
*
|
|
80
|
+
* A `@vercube/*` package calls this once at module level and uses the result
|
|
81
|
+
* everywhere it produces telemetry:
|
|
82
|
+
*
|
|
83
|
+
* ```ts
|
|
84
|
+
* import { createInstrument, SpanKind } from '@vercube/telemetry/instrument';
|
|
85
|
+
*
|
|
86
|
+
* const instrument = createInstrument('@vercube/storage');
|
|
87
|
+
*
|
|
88
|
+
* export function traceOperation<T>(name: string, attributes: Attributes, fn: () => Promise<T>): Promise<T> {
|
|
89
|
+
* return instrument.span(name, { kind: SpanKind.CLIENT, attributes }, fn);
|
|
90
|
+
* }
|
|
91
|
+
* ```
|
|
92
|
+
*
|
|
93
|
+
* Creating the toolkit creates nothing: the tracer is resolved lazily and the
|
|
94
|
+
* instruments only on first use. That matters for metrics, because the
|
|
95
|
+
* OpenTelemetry metrics API has no proxy meter and an instrument created before
|
|
96
|
+
* a `MeterProvider` is registered stays a no-op for the life of the process.
|
|
97
|
+
*
|
|
98
|
+
* @param scope - Instrumentation scope, conventionally the package name
|
|
99
|
+
* @returns The toolkit bound to that scope
|
|
100
|
+
*/
|
|
101
|
+
export declare function createInstrument(scope: string): Instrument;
|
|
102
|
+
//#endregion
|
|
103
|
+
//#region src/Common/SpanUtils.d.ts
|
|
104
|
+
/**
|
|
105
|
+
* Records a thrown value on a span and marks the span as failed, whatever the
|
|
106
|
+
* error looks like.
|
|
107
|
+
*
|
|
108
|
+
* This is the variant for spans that carry no HTTP status semantics - the
|
|
109
|
+
* `CLIENT`, `PRODUCER` and `CONSUMER` spans an instrumented package produces.
|
|
110
|
+
* `failSpan` is the server-span variant and deliberately behaves differently;
|
|
111
|
+
* see the comment there before merging the two.
|
|
112
|
+
*
|
|
113
|
+
* @param span - The span to update
|
|
114
|
+
* @param error - The thrown value
|
|
115
|
+
*/
|
|
116
|
+
export declare function recordFailure(span: Span$1, error: unknown): void;
|
|
117
|
+
/**
|
|
118
|
+
* Names the type of a thrown value for the `error.type` attribute.
|
|
119
|
+
*
|
|
120
|
+
* @param error - The thrown value
|
|
121
|
+
* @returns The error type name
|
|
122
|
+
*/
|
|
123
|
+
export declare function errorType(error: unknown): string;
|
|
124
|
+
/**
|
|
125
|
+
* Extracts a message from a thrown value.
|
|
126
|
+
*
|
|
127
|
+
* @param error - The thrown value
|
|
128
|
+
* @returns The message, or an empty string
|
|
129
|
+
*/
|
|
130
|
+
export declare function errorMessage(error: unknown): string;
|
|
131
|
+
//#endregion
|
|
132
|
+
export { type Attributes, type Context, type Counter, type Exception, type Histogram, type Instrument, type Link, type MetricOptions, type Span, type SpanContext, SpanKind, type SpanOptions, SpanStatusCode, type UpDownCounter, ValueType };
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { a as recordFailure, i as isPromiseLike, n as errorType, t as errorMessage } from "./SpanUtils-t3NCeswB.mjs";
|
|
2
|
+
import { ROOT_CONTEXT, SpanKind, SpanStatusCode, ValueType, context, metrics, propagation, trace } from "@opentelemetry/api";
|
|
3
|
+
//#region src/Instrument/Factory.ts
|
|
4
|
+
/**
|
|
5
|
+
* Builds the instrumentation toolkit for one instrumentation scope.
|
|
6
|
+
*
|
|
7
|
+
* A `@vercube/*` package calls this once at module level and uses the result
|
|
8
|
+
* everywhere it produces telemetry:
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* import { createInstrument, SpanKind } from '@vercube/telemetry/instrument';
|
|
12
|
+
*
|
|
13
|
+
* const instrument = createInstrument('@vercube/storage');
|
|
14
|
+
*
|
|
15
|
+
* export function traceOperation<T>(name: string, attributes: Attributes, fn: () => Promise<T>): Promise<T> {
|
|
16
|
+
* return instrument.span(name, { kind: SpanKind.CLIENT, attributes }, fn);
|
|
17
|
+
* }
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* Creating the toolkit creates nothing: the tracer is resolved lazily and the
|
|
21
|
+
* instruments only on first use. That matters for metrics, because the
|
|
22
|
+
* OpenTelemetry metrics API has no proxy meter and an instrument created before
|
|
23
|
+
* a `MeterProvider` is registered stays a no-op for the life of the process.
|
|
24
|
+
*
|
|
25
|
+
* @param scope - Instrumentation scope, conventionally the package name
|
|
26
|
+
* @returns The toolkit bound to that scope
|
|
27
|
+
*/
|
|
28
|
+
function createInstrument(scope) {
|
|
29
|
+
let tracer;
|
|
30
|
+
const counters = /* @__PURE__ */ new Map();
|
|
31
|
+
const upDownCounters = /* @__PURE__ */ new Map();
|
|
32
|
+
const histograms = /* @__PURE__ */ new Map();
|
|
33
|
+
/**
|
|
34
|
+
* Starts a span, runs the work inside it and ends it once the work settles.
|
|
35
|
+
*
|
|
36
|
+
* @param name - Span name
|
|
37
|
+
* @param options - Span kind, attributes and links
|
|
38
|
+
* @param parent - Context the span is a child of
|
|
39
|
+
* @param fn - The work to trace
|
|
40
|
+
* @returns Whatever the work returned
|
|
41
|
+
*/
|
|
42
|
+
function run(name, options, parent, fn) {
|
|
43
|
+
tracer ??= trace.getTracer(scope);
|
|
44
|
+
const span = tracer.startSpan(name, options, parent);
|
|
45
|
+
return context.with(trace.setSpan(parent, span), () => {
|
|
46
|
+
let result;
|
|
47
|
+
try {
|
|
48
|
+
result = fn(span);
|
|
49
|
+
} catch (error) {
|
|
50
|
+
recordFailure(span, error);
|
|
51
|
+
span.end();
|
|
52
|
+
throw error;
|
|
53
|
+
}
|
|
54
|
+
if (!isPromiseLike(result)) {
|
|
55
|
+
span.end();
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
return result.then((value) => {
|
|
59
|
+
span.end();
|
|
60
|
+
return value;
|
|
61
|
+
}, (error) => {
|
|
62
|
+
recordFailure(span, error);
|
|
63
|
+
span.end();
|
|
64
|
+
throw error;
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
span: (name, options, fn) => run(name, options, context.active(), fn),
|
|
70
|
+
spanFrom: (name, options, parent, fn) => run(name, options, parent, fn),
|
|
71
|
+
counter(name, options) {
|
|
72
|
+
let instrument = counters.get(name);
|
|
73
|
+
if (!instrument) {
|
|
74
|
+
instrument = metrics.getMeter(scope).createCounter(name, options);
|
|
75
|
+
counters.set(name, instrument);
|
|
76
|
+
}
|
|
77
|
+
return instrument;
|
|
78
|
+
},
|
|
79
|
+
upDownCounter(name, options) {
|
|
80
|
+
let instrument = upDownCounters.get(name);
|
|
81
|
+
if (!instrument) {
|
|
82
|
+
instrument = metrics.getMeter(scope).createUpDownCounter(name, options);
|
|
83
|
+
upDownCounters.set(name, instrument);
|
|
84
|
+
}
|
|
85
|
+
return instrument;
|
|
86
|
+
},
|
|
87
|
+
histogram(name, options) {
|
|
88
|
+
let instrument = histograms.get(name);
|
|
89
|
+
if (!instrument) {
|
|
90
|
+
instrument = metrics.getMeter(scope).createHistogram(name, options);
|
|
91
|
+
histograms.set(name, instrument);
|
|
92
|
+
}
|
|
93
|
+
return instrument;
|
|
94
|
+
},
|
|
95
|
+
activeSpan: () => trace.getActiveSpan(),
|
|
96
|
+
inject(carrier) {
|
|
97
|
+
propagation.inject(context.active(), carrier);
|
|
98
|
+
},
|
|
99
|
+
extract: (carrier) => propagation.extract(ROOT_CONTEXT, carrier ?? {})
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
//#endregion
|
|
103
|
+
export { SpanKind, SpanStatusCode, ValueType, createInstrument, errorMessage, errorType, recordFailure };
|
package/dist/Otlp.d.mts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { JsonMetricsSerializer, JsonTraceSerializer } from "@opentelemetry/otlp-transformer";
|
|
2
|
+
import { PushMetricExporter } from "@opentelemetry/sdk-metrics";
|
|
3
|
+
import { SpanExporter } from "@opentelemetry/sdk-trace-base";
|
|
4
|
+
//#region src/Otlp.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Where and how to reach an OTLP/HTTP collector.
|
|
7
|
+
*/
|
|
8
|
+
export interface OtlpExporterOptions {
|
|
9
|
+
/** OTLP/HTTP endpoint root, e.g. `http://localhost:4318`. */
|
|
10
|
+
endpoint: string;
|
|
11
|
+
/** Headers sent with every request, for authenticated collectors. */
|
|
12
|
+
headers?: Record<string, string>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Builds an OTLP/HTTP span exporter.
|
|
16
|
+
*
|
|
17
|
+
* @param options - Endpoint and headers
|
|
18
|
+
* @returns The exporter
|
|
19
|
+
* @throws When `@opentelemetry/exporter-trace-otlp-http` is not installed
|
|
20
|
+
*/
|
|
21
|
+
export declare function createOtlpTraceExporter(options: OtlpExporterOptions): Promise<SpanExporter>;
|
|
22
|
+
/**
|
|
23
|
+
* Builds an OTLP/HTTP metric exporter.
|
|
24
|
+
*
|
|
25
|
+
* @param options - Endpoint and headers
|
|
26
|
+
* @returns The exporter
|
|
27
|
+
* @throws When `@opentelemetry/exporter-metrics-otlp-http` is not installed
|
|
28
|
+
*/
|
|
29
|
+
export declare function createOtlpMetricExporter(options: OtlpExporterOptions): Promise<PushMetricExporter>;
|
|
30
|
+
//#endregion
|
|
31
|
+
export { JsonMetricsSerializer, JsonTraceSerializer };
|
package/dist/Otlp.mjs
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { JsonMetricsSerializer, JsonTraceSerializer } from "@opentelemetry/otlp-transformer";
|
|
2
|
+
//#region src/Otlp.ts
|
|
3
|
+
/**
|
|
4
|
+
* Builds an OTLP/HTTP span exporter.
|
|
5
|
+
*
|
|
6
|
+
* @param options - Endpoint and headers
|
|
7
|
+
* @returns The exporter
|
|
8
|
+
* @throws When `@opentelemetry/exporter-trace-otlp-http` is not installed
|
|
9
|
+
*/
|
|
10
|
+
async function createOtlpTraceExporter(options) {
|
|
11
|
+
let module;
|
|
12
|
+
try {
|
|
13
|
+
module = await import("@opentelemetry/exporter-trace-otlp-http");
|
|
14
|
+
} catch {
|
|
15
|
+
throw new Error("An OTLP endpoint is configured but @opentelemetry/exporter-trace-otlp-http is not installed. Install it, or pass your own `exporter` to startNodeTelemetry().");
|
|
16
|
+
}
|
|
17
|
+
return new module.OTLPTraceExporter({
|
|
18
|
+
url: signalUrl(options.endpoint, "traces"),
|
|
19
|
+
headers: options.headers
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Builds an OTLP/HTTP metric exporter.
|
|
24
|
+
*
|
|
25
|
+
* @param options - Endpoint and headers
|
|
26
|
+
* @returns The exporter
|
|
27
|
+
* @throws When `@opentelemetry/exporter-metrics-otlp-http` is not installed
|
|
28
|
+
*/
|
|
29
|
+
async function createOtlpMetricExporter(options) {
|
|
30
|
+
let module;
|
|
31
|
+
try {
|
|
32
|
+
module = await import("@opentelemetry/exporter-metrics-otlp-http");
|
|
33
|
+
} catch {
|
|
34
|
+
throw new Error("An OTLP endpoint is configured for metrics but @opentelemetry/exporter-metrics-otlp-http is not installed. Install it, or pass your own reader to addMetricReader().");
|
|
35
|
+
}
|
|
36
|
+
return new module.OTLPMetricExporter({
|
|
37
|
+
url: signalUrl(options.endpoint, "metrics"),
|
|
38
|
+
headers: options.headers
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Joins an endpoint root with an OTLP signal path.
|
|
43
|
+
*
|
|
44
|
+
* @param endpoint - Endpoint root, with or without a trailing slash
|
|
45
|
+
* @param signal - The signal path segment
|
|
46
|
+
* @returns The full URL
|
|
47
|
+
*/
|
|
48
|
+
function signalUrl(endpoint, signal) {
|
|
49
|
+
return `${endpoint.replace(/\/$/, "")}/v1/${signal}`;
|
|
50
|
+
}
|
|
51
|
+
//#endregion
|
|
52
|
+
export { JsonMetricsSerializer, JsonTraceSerializer, createOtlpMetricExporter, createOtlpTraceExporter };
|