@r_masseater/vitest-otlp 0.0.0-stage → 0.0.2
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 +87 -0
- package/README.md +170 -2
- package/package.json +75 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 masseater
|
|
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.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
@tanstack/eslint-plugin-start
|
|
26
|
+
|
|
27
|
+
MIT License
|
|
28
|
+
|
|
29
|
+
Copyright (c) 2021-present Tanner Linsley
|
|
30
|
+
|
|
31
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
32
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
33
|
+
in the Software without restriction, including without limitation the rights
|
|
34
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
35
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
36
|
+
furnished to do so, subject to the following conditions:
|
|
37
|
+
|
|
38
|
+
The above copyright notice and this permission notice shall be included in all
|
|
39
|
+
copies or substantial portions of the Software.
|
|
40
|
+
|
|
41
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
42
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
43
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
44
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
45
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
46
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
47
|
+
SOFTWARE.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
@typescript-eslint/utils
|
|
52
|
+
|
|
53
|
+
MIT License
|
|
54
|
+
|
|
55
|
+
Copyright (c) 2019 typescript-eslint and other contributors
|
|
56
|
+
|
|
57
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
58
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
59
|
+
in the Software without restriction, including without limitation the rights
|
|
60
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
61
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
62
|
+
furnished to do so, subject to the following conditions:
|
|
63
|
+
|
|
64
|
+
The above copyright notice and this permission notice shall be included in all
|
|
65
|
+
copies or substantial portions of the Software.
|
|
66
|
+
|
|
67
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
68
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
69
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
70
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
71
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
72
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
73
|
+
SOFTWARE.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
globals
|
|
78
|
+
|
|
79
|
+
MIT License
|
|
80
|
+
|
|
81
|
+
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
|
|
82
|
+
|
|
83
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
84
|
+
|
|
85
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
86
|
+
|
|
87
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,171 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @r_masseater/vitest-otlp
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A Vitest plugin that receives OTLP during tests. A test can then assert the
|
|
4
|
+
spans, metrics and logs that the code under test really exported. It shows that
|
|
5
|
+
auto-instrumentation and `OTEL_*` settings work. It also shows that telemetry
|
|
6
|
+
actually leaves the process.
|
|
7
|
+
|
|
8
|
+
It requires [Vite+](https://viteplus.dev/) and Node.js 26 or later.
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
vp add -D @r_masseater/vitest-otlp
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
import { otlp } from "@r_masseater/vitest-otlp";
|
|
16
|
+
import { defineConfig } from "vite-plus";
|
|
17
|
+
|
|
18
|
+
export default defineConfig({
|
|
19
|
+
plugins: [otlp()],
|
|
20
|
+
});
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { NodeSDK } from "@opentelemetry/sdk-node";
|
|
25
|
+
import { waitForSpans } from "@r_masseater/vitest-otlp";
|
|
26
|
+
import { Effect } from "effect";
|
|
27
|
+
import { expect, test } from "vite-plus/test";
|
|
28
|
+
|
|
29
|
+
test("checkout is traced", () =>
|
|
30
|
+
Effect.gen(function* () {
|
|
31
|
+
const sdk = new NodeSDK();
|
|
32
|
+
sdk.start();
|
|
33
|
+
yield* checkout;
|
|
34
|
+
yield* Effect.promise(() => sdk.shutdown());
|
|
35
|
+
|
|
36
|
+
const spans = yield* waitForSpans({
|
|
37
|
+
where: (span) => span.name === "checkout",
|
|
38
|
+
});
|
|
39
|
+
expect(spans).toMatchObject([{ attributes: { "order.id": "o-1" } }]);
|
|
40
|
+
}).pipe(Effect.runPromise));
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## How it works
|
|
44
|
+
|
|
45
|
+
Each test file gets its own OTLP receiver on free loopback ports. It serves
|
|
46
|
+
OTLP/HTTP with protobuf or JSON, and OTLP/gRPC. Both accept gzip. Messages are
|
|
47
|
+
decoded with code that [protobuf-es](https://github.com/bufbuild/protobuf-es)
|
|
48
|
+
generated from
|
|
49
|
+
[opentelemetry-proto](https://github.com/open-telemetry/opentelemetry-proto)
|
|
50
|
+
v1.11.1.
|
|
51
|
+
|
|
52
|
+
The test learns the receiver through the standard environment variables. An SDK
|
|
53
|
+
that reads its configuration from the environment sends to it without code
|
|
54
|
+
changes. Child processes of the test inherit the variables. They send to the
|
|
55
|
+
same receiver.
|
|
56
|
+
|
|
57
|
+
An in-memory exporter skips the real export path. This plugin instead sends
|
|
58
|
+
over OTLP and reads the environment, just as production does. A Collector or
|
|
59
|
+
grafana/otel-lgtm in a container needs Docker. This plugin needs none, and the
|
|
60
|
+
test reads what arrived directly.
|
|
61
|
+
|
|
62
|
+
## API
|
|
63
|
+
|
|
64
|
+
The read API is written with Effect.
|
|
65
|
+
|
|
66
|
+
`waitForSpans`, `waitForLogs` and `waitForMetrics` wait for items that match
|
|
67
|
+
`where`. They reread every `interval` until `count` items arrive, and return all
|
|
68
|
+
matches. The defaults are 20 ms and 1. After `timeout` they fail with
|
|
69
|
+
`TelemetryNotReceived`, which carries how many arrived. The default timeout is
|
|
70
|
+
5 seconds.
|
|
71
|
+
|
|
72
|
+
`receivedSpans`, `receivedLogs` and `receivedMetrics` return everything that has
|
|
73
|
+
arrived in the test file so far. `clearTelemetry` discards what has arrived.
|
|
74
|
+
`receiverAddress` returns the `httpEndpoint` and `grpcEndpoint` of the receiver.
|
|
75
|
+
|
|
76
|
+
Attributes become plain objects such as `{ "order.id": "o-1" }`. IDs become hex
|
|
77
|
+
strings. Spans and logs carry `resource` and `scope`. They hold the resource
|
|
78
|
+
attributes and the instrumentation scope.
|
|
79
|
+
|
|
80
|
+
## Environment
|
|
81
|
+
|
|
82
|
+
`OTEL_EXPORTER_OTLP_ENDPOINT` is the HTTP URL of the receiver. When
|
|
83
|
+
`OTEL_EXPORTER_OTLP_PROTOCOL` is `grpc`, it is the gRPC URL instead.
|
|
84
|
+
`OTEL_TRACES_EXPORTER`, `OTEL_METRICS_EXPORTER` and `OTEL_LOGS_EXPORTER` are
|
|
85
|
+
`otlp`.
|
|
86
|
+
|
|
87
|
+
Export delays are shortened, but only when they are unset.
|
|
88
|
+
|
|
89
|
+
| Variable | Value |
|
|
90
|
+
| ----------------------------- | ----- |
|
|
91
|
+
| `OTEL_BSP_SCHEDULE_DELAY` | 50 |
|
|
92
|
+
| `OTEL_BLRP_SCHEDULE_DELAY` | 50 |
|
|
93
|
+
| `OTEL_METRIC_EXPORT_INTERVAL` | 100 |
|
|
94
|
+
| `OTEL_METRIC_EXPORT_TIMEOUT` | 100 |
|
|
95
|
+
|
|
96
|
+
The per-signal variables `OTEL_EXPORTER_OTLP_<SIGNAL>_ENDPOINT` are emptied.
|
|
97
|
+
Test telemetry therefore never leaks to an outside Collector.
|
|
98
|
+
|
|
99
|
+
The receiver closes when the test file ends. With `isolate: false`, files may
|
|
100
|
+
share one SDK. Then telemetry from the second file on is lost.
|
|
101
|
+
|
|
102
|
+
## Semantic conventions
|
|
103
|
+
|
|
104
|
+
`otlp({ semconv: true })` also checks every export against the OpenTelemetry
|
|
105
|
+
semantic conventions. It starts the live-check of
|
|
106
|
+
[OpenTelemetry Weaver](https://github.com/open-telemetry/weaver) once for the
|
|
107
|
+
run. Each receiver forwards what it gets to Weaver before storing it. When the
|
|
108
|
+
run ends, the plugin reads the report of Weaver. The run then fails and lists
|
|
109
|
+
every finding at or above `failOn`.
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
otlp({
|
|
113
|
+
semconv: {
|
|
114
|
+
registry: "https://github.com/open-telemetry/semantic-conventions.git@v1.45.0[model]",
|
|
115
|
+
weaver: "weaver",
|
|
116
|
+
failOn: "violation",
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The values above are the defaults. `failOn` is `violation`, `improvement` or
|
|
122
|
+
`information`. A custom attribute is a violation unless `registry` points to a
|
|
123
|
+
registry that defines it. The `weaver` binary must be on `PATH`. In GitHub
|
|
124
|
+
Actions, the
|
|
125
|
+
[setup-weaver](https://github.com/open-telemetry/weaver/tree/main/.github/actions/setup-weaver)
|
|
126
|
+
action installs it.
|
|
127
|
+
|
|
128
|
+
## Cloudflare Workers
|
|
129
|
+
|
|
130
|
+
Workers trace KV, D1, R2 and `fetch` calls without code changes, and hand the
|
|
131
|
+
spans and logs to a
|
|
132
|
+
[streaming tail worker](https://developers.cloudflare.com/workers/observability/logs/tail-workers/).
|
|
133
|
+
`tailWorker()` from `@r_masseater/vitest-otlp/workers` builds a tail worker for
|
|
134
|
+
[Miniflare](https://developers.cloudflare.com/workers/testing/miniflare/). It
|
|
135
|
+
turns each invocation into OTLP and sends it to the receiver of the test file.
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
import { waitForSpans } from "@r_masseater/vitest-otlp";
|
|
139
|
+
import { tailWorker } from "@r_masseater/vitest-otlp/workers";
|
|
140
|
+
import { Effect } from "effect";
|
|
141
|
+
import { Miniflare } from "miniflare";
|
|
142
|
+
|
|
143
|
+
test("caching writes to KV", () =>
|
|
144
|
+
Effect.gen(function* () {
|
|
145
|
+
const tail = yield* tailWorker();
|
|
146
|
+
const miniflare = new Miniflare({
|
|
147
|
+
workers: [
|
|
148
|
+
{
|
|
149
|
+
name: "app",
|
|
150
|
+
scriptPath: "worker.js",
|
|
151
|
+
kvNamespaces: ["CACHE"],
|
|
152
|
+
streamingTails: [tail.name],
|
|
153
|
+
},
|
|
154
|
+
tail,
|
|
155
|
+
],
|
|
156
|
+
});
|
|
157
|
+
yield* Effect.promise(() => miniflare.dispatchFetch("http://localhost/"));
|
|
158
|
+
yield* waitForSpans({ where: (span) => span.name === "kv_put" });
|
|
159
|
+
yield* Effect.promise(() => miniflare.dispose());
|
|
160
|
+
}).pipe(Effect.runPromise));
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
The invocation becomes a server span named after its trigger, such as `fetch`.
|
|
164
|
+
Each traced call becomes a client span under it, and `console` calls become log
|
|
165
|
+
records in the same trace. The resource `service.name` is the script name of
|
|
166
|
+
the Worker. Miniflare reports no script name, so pass `serviceName` to
|
|
167
|
+
`tailWorker()` instead.
|
|
168
|
+
|
|
169
|
+
## License
|
|
170
|
+
|
|
171
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,78 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@r_masseater/vitest-otlp",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"description": "A Vitest plugin that receives OTLP traces, metrics and logs during tests so tests can assert on the telemetry the code under test actually exported.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"opentelemetry",
|
|
7
|
+
"otel",
|
|
8
|
+
"otlp",
|
|
9
|
+
"testing",
|
|
10
|
+
"vitest",
|
|
11
|
+
"vitest-plugin"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://github.com/masseater/measure-everything/tree/main/libs/vitest-otlp#readme",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/masseater/measure-everything/issues"
|
|
16
|
+
},
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/masseater/measure-everything.git",
|
|
21
|
+
"directory": "libs/vitest-otlp"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"LICENSE"
|
|
26
|
+
],
|
|
27
|
+
"type": "module",
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"types": "./dist/index.d.mts",
|
|
31
|
+
"default": "./dist/index.mjs"
|
|
32
|
+
},
|
|
33
|
+
"./setup": {
|
|
34
|
+
"types": "./dist/setup.d.mts",
|
|
35
|
+
"default": "./dist/setup.mjs"
|
|
36
|
+
},
|
|
37
|
+
"./semconv": {
|
|
38
|
+
"types": "./dist/semconv.d.mts",
|
|
39
|
+
"default": "./dist/semconv.mjs"
|
|
40
|
+
},
|
|
41
|
+
"./workers": {
|
|
42
|
+
"types": "./dist/workers.d.mts",
|
|
43
|
+
"default": "./dist/workers.mjs"
|
|
44
|
+
},
|
|
45
|
+
"./tail-worker": "./dist/tail.worker.js",
|
|
46
|
+
"./package.json": "./package.json"
|
|
47
|
+
},
|
|
48
|
+
"publishConfig": {
|
|
49
|
+
"access": "public",
|
|
50
|
+
"provenance": true
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"@bufbuild/protobuf": "2.16.0",
|
|
54
|
+
"@effect/platform-node": "4.0.7",
|
|
55
|
+
"effect": "4.0.7"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@bufbuild/buf": "1.75.0",
|
|
59
|
+
"@bufbuild/protoc-gen-es": "2.16.0",
|
|
60
|
+
"@cloudflare/workers-types": "5.20261101.1",
|
|
61
|
+
"@opentelemetry/api": "1.10.0",
|
|
62
|
+
"@opentelemetry/sdk-node": "0.300.0",
|
|
63
|
+
"@types/node": "26.6.6",
|
|
64
|
+
"miniflare": "4.20260730.0",
|
|
65
|
+
"vite-plus": "1.4.0"
|
|
66
|
+
},
|
|
67
|
+
"peerDependencies": {
|
|
68
|
+
"vite-plus": "1.4.0"
|
|
69
|
+
},
|
|
70
|
+
"engines": {
|
|
71
|
+
"node": ">=26"
|
|
72
|
+
},
|
|
73
|
+
"scripts": {
|
|
74
|
+
"build": "vp pack",
|
|
75
|
+
"generate": "buf generate",
|
|
76
|
+
"test": "vp test run"
|
|
77
|
+
}
|
|
6
78
|
}
|