rian 0.2.2 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- package/index.d.ts +1 -7
- package/index.js +1 -1
- package/index.mjs +1 -1
- package/license +21 -0
- package/package.json +20 -9
- package/readme.md +278 -0
- package/utils/index.js +1 -1
- package/utils/index.mjs +1 -1
package/index.d.ts
CHANGED
@@ -170,7 +170,7 @@ export interface Options {
|
|
170
170
|
* If the id is malformed, the {@link create} method will throw an exception. If no root is
|
171
171
|
* provided then one will be created obeying the {@link Options.sampler|sampling} rules.
|
172
172
|
*/
|
173
|
-
traceparent?: string;
|
173
|
+
traceparent?: string | null;
|
174
174
|
}
|
175
175
|
|
176
176
|
export const create: (name: string, options: Options) => Tracer;
|
@@ -181,9 +181,3 @@ export const create: (name: string, options: Options) => Tracer;
|
|
181
181
|
export interface CallableScope extends Scope {
|
182
182
|
(cb: (scope: Omit<Scope, 'end'>) => void): ReturnType<typeof cb>;
|
183
183
|
}
|
184
|
-
|
185
|
-
/** @internal */
|
186
|
-
export const PROMISES: WeakMap<Scope, Promise<any>[]>;
|
187
|
-
|
188
|
-
/** @internal */
|
189
|
-
export const ADD_PROMISE: (scope: Scope, promise: Promise<any>) => void;
|
package/index.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
const e = require('
|
1
|
+
const { measureFn:e } = require('rian/utils');const t = require('tctx');var n=(e,n)=>!n||t.is_sampled(n),a={"telemetry.sdk.name":"rian","telemetry.sdk.version":"0.2.4"},r=(r,o)=>{let s=new Set,d=new Set,i=o.sampler||n,p="boolean"!=typeof i,m=(n,a)=>{let r=p?i(n,a,o.context):i,c=a?a.child(r):t.make(r),l={id:c,parent:a,start:Date.now(),name:n,events:[],context:{}};r&&s.add(l);let x=t=>e(x,t);return x.traceparent=c,x.fork=e=>m(e,c),x.set_context=e=>{"function"!=typeof e?Object.assign(l.context,e):l.context=e(l.context)},x.add_event=(e,t)=>{l.events.push({name:e,timestamp:Date.now(),attributes:t||{}})},x.end=()=>{null==l.end&&(l.end=Date.now())},x.__add_promise=d.add.bind(d),x},c=m(r,"string"==typeof o.traceparent?t.parse(o.traceparent):void 0),l=c.end.bind(c);return c.end=async()=>(l(),d.size>0&&await Promise.all([...d.values()]),o.exporter(s,{...o.context||{},...a})),c};exports.create=r;
|
package/index.mjs
CHANGED
@@ -1 +1 @@
|
|
1
|
-
import
|
1
|
+
import{measureFn as e}from"rian/utils";import*as t from"tctx";var n=(e,n)=>!n||t.is_sampled(n),a={"telemetry.sdk.name":"rian","telemetry.sdk.version":"0.2.4"},r=(r,o)=>{let s=new Set,d=new Set,i=o.sampler||n,p="boolean"!=typeof i,m=(n,a)=>{let r=p?i(n,a,o.context):i,c=a?a.child(r):t.make(r),l={id:c,parent:a,start:Date.now(),name:n,events:[],context:{}};r&&s.add(l);let x=t=>e(x,t);return x.traceparent=c,x.fork=e=>m(e,c),x.set_context=e=>{"function"!=typeof e?Object.assign(l.context,e):l.context=e(l.context)},x.add_event=(e,t)=>{l.events.push({name:e,timestamp:Date.now(),attributes:t||{}})},x.end=()=>{null==l.end&&(l.end=Date.now())},x.__add_promise=d.add.bind(d),x},c=m(r,"string"==typeof o.traceparent?t.parse(o.traceparent):void 0),l=c.end.bind(c);return c.end=async()=>(l(),d.size>0&&await Promise.all([...d.values()]),o.exporter(s,{...o.context||{},...a})),c};export{r as create};
|
package/license
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) Marais Rossouw <me@marais.dev>
|
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/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "rian",
|
3
|
-
"version": "0.2.
|
3
|
+
"version": "0.2.4",
|
4
4
|
"description": "Effective tracing for the edge and origins",
|
5
5
|
"keywords": [
|
6
6
|
"opentelemetry",
|
@@ -17,13 +17,8 @@
|
|
17
17
|
],
|
18
18
|
"repository": "maraisr/rian",
|
19
19
|
"license": "MIT",
|
20
|
-
"author":
|
21
|
-
"name": "Marais Rossouw",
|
22
|
-
"email": "me@marais.dev",
|
23
|
-
"url": "https://marais.io"
|
24
|
-
},
|
20
|
+
"author": "Marais Rossow <me@marais.dev> (https://marais.io)",
|
25
21
|
"sideEffects": false,
|
26
|
-
"type": "module",
|
27
22
|
"exports": {
|
28
23
|
".": {
|
29
24
|
"types": "./index.d.ts",
|
@@ -58,13 +53,29 @@
|
|
58
53
|
"utils/*"
|
59
54
|
],
|
60
55
|
"scripts": {
|
61
|
-
"
|
56
|
+
"bench": "node -r tsm bench/index.ts",
|
57
|
+
"build": "bundt --minify",
|
58
|
+
"format": "prettier --write \"{*,{src,test}/**/*,examples/*/**,bench/*,.github/**/*}.+(ts|js|json|yml|md)\"",
|
59
|
+
"pretest": "pnpm run build",
|
60
|
+
"test": "uvu -r tsm test \".spec.m?ts$\"",
|
61
|
+
"typecheck": "tsc --noEmit --skipLibCheck"
|
62
62
|
},
|
63
63
|
"dependencies": {
|
64
64
|
"flattie": "^1.1.0",
|
65
65
|
"tctx": "^0.0.10"
|
66
66
|
},
|
67
67
|
"devDependencies": {
|
68
|
-
"
|
68
|
+
"@marais/prettier": "0.0.1",
|
69
|
+
"@marais/tsconfig": "0.0.3",
|
70
|
+
"bundt": "2.0.0-next.5",
|
71
|
+
"nanospy": "0.5.0",
|
72
|
+
"prettier": "2.7.1",
|
73
|
+
"tsm": "2.2.2",
|
74
|
+
"typescript": "4.8.4",
|
75
|
+
"uvu": "0.5.6"
|
76
|
+
},
|
77
|
+
"prettier": "@marais/prettier",
|
78
|
+
"volta": {
|
79
|
+
"node": "17.2.0"
|
69
80
|
}
|
70
81
|
}
|
package/readme.md
ADDED
@@ -0,0 +1,278 @@
|
|
1
|
+
<div align="right">
|
2
|
+
<img src="files/logo-light.svg#gh-light-mode-only" alt="rian light mode logo" width="200px">
|
3
|
+
<img src="files/logo-dark.svg#gh-dark-mode-only" alt="rian dark mode logo" width="200px">
|
4
|
+
<br />
|
5
|
+
<br />
|
6
|
+
|
7
|
+
<p><code>npm add rian</code> doesn't overcomplicate tracing</p>
|
8
|
+
<span>
|
9
|
+
<a href="https://github.com/maraisr/rian/actions/workflows/ci.yml">
|
10
|
+
<img src="https://github.com/maraisr/rian/actions/workflows/ci.yml/badge.svg"/>
|
11
|
+
</a>
|
12
|
+
<a href="https://npm-stat.com/charts.html?package=rian">
|
13
|
+
<img src="https://badgen.net/npm/dw/rian?labelColor=black&color=black&cache=600" alt="downloads"/>
|
14
|
+
</a>
|
15
|
+
<a href="https://packagephobia.com/result?p=rian">
|
16
|
+
<img src="https://badgen.net/packagephobia/install/rian?labelColor=black&color=black" alt="size"/>
|
17
|
+
</a>
|
18
|
+
<a href="https://bundlephobia.com/result?p=rian">
|
19
|
+
<img src="https://badgen.net/bundlephobia/minzip/rian?labelColor=black&color=black" alt="size"/>
|
20
|
+
</a>
|
21
|
+
</span>
|
22
|
+
|
23
|
+
<br />
|
24
|
+
<br />
|
25
|
+
</div>
|
26
|
+
|
27
|
+
## ⚡ Features
|
28
|
+
|
29
|
+
- 🤔 **Familiar** — looks very much like OpenTracing.
|
30
|
+
|
31
|
+
- ✅ **Simple** — `create` a tracer, and `.end()` a tracer, done.
|
32
|
+
|
33
|
+
- 🏎 **Performant** — check the [benchmarks](#-benchmark).
|
34
|
+
|
35
|
+
- 🪶 **Lightweight** — a mere 1Kb and next to no [dependencies](https://npm.anvaka.com/#/view/2d/rian/).
|
36
|
+
|
37
|
+
## 🚀 Usage
|
38
|
+
|
39
|
+
> Visit [/examples](/examples) for more info!
|
40
|
+
|
41
|
+
```ts
|
42
|
+
import { create } from 'rian';
|
43
|
+
import { measure } from 'rian/utils';
|
44
|
+
import { exporter } from 'rian/exporter.otel.http';
|
45
|
+
|
46
|
+
// ~> Where to send the spans.
|
47
|
+
const otel_endpoint = exporter((payload) =>
|
48
|
+
fetch('/traces/otlp', {
|
49
|
+
method: 'POST',
|
50
|
+
body: JSON.stringify(payload),
|
51
|
+
}),
|
52
|
+
);
|
53
|
+
|
54
|
+
// ~> Create a tracer — typically "per request" or "per operation"
|
55
|
+
const tracer = create('GET ~> /data', {
|
56
|
+
exporter: otel_endpoint,
|
57
|
+
});
|
58
|
+
|
59
|
+
// Let us trace
|
60
|
+
|
61
|
+
tracer.set_context({
|
62
|
+
user: request_context.user_id,
|
63
|
+
});
|
64
|
+
|
65
|
+
// ~> Wrap any method and be timed 🕺🏻
|
66
|
+
const data = await measure(tracer.fork('db::read'), get_data);
|
67
|
+
|
68
|
+
// ~> Maybe have some in-flow spanning
|
69
|
+
const span = tracer.span('process records');
|
70
|
+
|
71
|
+
for (let row of data) {
|
72
|
+
span.add_event('doing stuff', { id: row.id });
|
73
|
+
do_stuff(row);
|
74
|
+
}
|
75
|
+
|
76
|
+
span.end();
|
77
|
+
|
78
|
+
// ~> And finally let's export — will also end the root span.
|
79
|
+
await tracer.end();
|
80
|
+
|
81
|
+
/*
|
82
|
+
And we end up with something like this in our reporting tool:
|
83
|
+
|
84
|
+
[ GET ~> /data .................................... (1.2ms) ]
|
85
|
+
[ db::read .... (0.5ms) ]
|
86
|
+
[ process records .... (0.5ms) ]
|
87
|
+
*/
|
88
|
+
```
|
89
|
+
|
90
|
+
## 🔎 API
|
91
|
+
|
92
|
+
#### Module: [`rian`](./packages/rian/src/index.ts)
|
93
|
+
|
94
|
+
The main and _default_ module responsible for creating and provisioning spans.
|
95
|
+
|
96
|
+
> 💡 Note ~> when providing span context values, please stick to
|
97
|
+
> [Semantic Conventions](https://github.com/opentracing/specification/blob/master/semantic_conventions.md), but won't be
|
98
|
+
> enforced.
|
99
|
+
|
100
|
+
#### Module: [`rian/exporter.zipkin`](./packages/rian/src/exporter.zipkin.ts)
|
101
|
+
|
102
|
+
Exports the spans created using the zipkin protocol and leaves the shipping up to you.
|
103
|
+
|
104
|
+
> 💡 Note ~> with the nature of zipkin, the `localEndpoint` must be set in your span context.
|
105
|
+
>
|
106
|
+
> <details><summary>Example</summary>
|
107
|
+
>
|
108
|
+
> ```ts
|
109
|
+
> const tracer = create('example', {
|
110
|
+
> context: {
|
111
|
+
> localEndpoint: {
|
112
|
+
> serviceName: 'my-service', // 👈 important part
|
113
|
+
> },
|
114
|
+
> },
|
115
|
+
> });
|
116
|
+
> ```
|
117
|
+
>
|
118
|
+
> Both of these are functionally equivalent. `service.name` will be used if no `localEndpoint.serviceName` is set.
|
119
|
+
>
|
120
|
+
> ```ts
|
121
|
+
> const tracer = create('example', {
|
122
|
+
> context: {
|
123
|
+
> 'service.name': 'my-service',
|
124
|
+
> },
|
125
|
+
> });
|
126
|
+
> ```
|
127
|
+
>
|
128
|
+
> </details>
|
129
|
+
|
130
|
+
#### Module: [`rian/exporter.otel.http`](./packages/rian/src/exporter.otel.http.ts)
|
131
|
+
|
132
|
+
Implements the OpenTelemetry protocol for use with http transports.
|
133
|
+
|
134
|
+
> 💡 Note ~> services require a `service.name` context value.
|
135
|
+
>
|
136
|
+
> <details><summary>Example</summary>
|
137
|
+
>
|
138
|
+
> ```ts
|
139
|
+
> const tracer = create('example', {
|
140
|
+
> context: {
|
141
|
+
> 'service.name': 'my-service', // 👈 important part
|
142
|
+
> },
|
143
|
+
> });
|
144
|
+
> ```
|
145
|
+
>
|
146
|
+
> </details>
|
147
|
+
|
148
|
+
## 🧑🍳 Exporter Recipes
|
149
|
+
|
150
|
+
<details><summary>NewRelic</summary>
|
151
|
+
|
152
|
+
```ts
|
153
|
+
import { create } from 'rian';
|
154
|
+
import { exporter } from 'rian/exporter.zipkin';
|
155
|
+
|
156
|
+
const newrelic = exporter((payload) =>
|
157
|
+
fetch('https://trace-api.newrelic.com/trace/v1', {
|
158
|
+
method: 'POST',
|
159
|
+
headers: {
|
160
|
+
'api-key': '<your api key>',
|
161
|
+
'content-type': 'application/json',
|
162
|
+
'data-format': 'zipkin',
|
163
|
+
'data-format-version': '2',
|
164
|
+
},
|
165
|
+
body: JSON.stringify(payload),
|
166
|
+
}),
|
167
|
+
);
|
168
|
+
|
169
|
+
const tracer = create('example', {
|
170
|
+
context: {
|
171
|
+
'service.name': 'my-service', // 👈 important part
|
172
|
+
},
|
173
|
+
exporter: newrelic,
|
174
|
+
});
|
175
|
+
```
|
176
|
+
|
177
|
+
[learn more](https://docs.newrelic.com/docs/distributed-tracing/trace-api/introduction-trace-api/)
|
178
|
+
|
179
|
+
</details>
|
180
|
+
|
181
|
+
<details><summary>LightStep</summary>
|
182
|
+
|
183
|
+
```ts
|
184
|
+
import { create } from 'rian';
|
185
|
+
import { exporter } from 'rian/exporter.otel.http';
|
186
|
+
|
187
|
+
const lightstep = exporter((payload) =>
|
188
|
+
fetch('https://ingest.lightstep.com/traces/otlp/v0.6', {
|
189
|
+
method: 'POST',
|
190
|
+
headers: {
|
191
|
+
'lightstep-access-token': '<your api key>',
|
192
|
+
'content-type': 'application/json',
|
193
|
+
},
|
194
|
+
body: JSON.stringify(payload),
|
195
|
+
}),
|
196
|
+
);
|
197
|
+
|
198
|
+
const tracer = create('example', {
|
199
|
+
context: {
|
200
|
+
'service.name': 'my-service', // 👈 important part
|
201
|
+
},
|
202
|
+
exporter: lightstep,
|
203
|
+
});
|
204
|
+
```
|
205
|
+
|
206
|
+
[learn more](https://opentelemetry.lightstep.com/tracing/)
|
207
|
+
|
208
|
+
</details>
|
209
|
+
|
210
|
+
## 🤔 Motivation
|
211
|
+
|
212
|
+
Firstly, what is `rian`? _trace_ in Irish is `rian`.
|
213
|
+
|
214
|
+
In efforts to be better observant citizens, we generally reach for the — NewRelic, LightStep, DataDog's. Which, and in
|
215
|
+
no offence to them, is bloated and slow! Where they more often than not do way too much or and relatively speaking, ship
|
216
|
+
useless traces. Which ramp up your bill — see... every span you trace, costs.
|
217
|
+
|
218
|
+
And here we are, introducing **rian** — a lightweight, fast effective tracer. Inspired by the giants in the industry,
|
219
|
+
OpenTracing and OpenTelemetry.
|
220
|
+
|
221
|
+
You might have not heard of those before — and that is okay. It means the design goals from OpenTelemetry or OpenTracing
|
222
|
+
has been met. They are frameworks built to abstract the telemetry part from vendors. So folk like NewRelic can wrap
|
223
|
+
their layers on top of open telemetry — and have libraries instrument theirs without knowing about the vendor. Which
|
224
|
+
allows consumers to ship those spans to the vendor of their choosing. OpenTracing has a very similar design goal, so
|
225
|
+
please do go checkout their documentation's, to help decide.
|
226
|
+
|
227
|
+
Rian does not intend to align or compete with them. rian's intent is to be used to instrument your application and
|
228
|
+
**only** your application. Rian is primed in that critical business paths — where you don't care " which handlers
|
229
|
+
MongoDB ran", or how many network calls your ORM made. Cardinality will destroy you. Although rian can scale to support
|
230
|
+
those as well. But the reality is; there are profiler tools far more capable — "right tool for the job".
|
231
|
+
|
232
|
+
Rian is simply a tracer you can use to see what your application is doing, have better insight into why something failed
|
233
|
+
and stitch it with your logs. It starts by capturing a [`w3c trace-context`](https://www.w3.org/TR/trace-context/),
|
234
|
+
tracing some business steps. "inbound request /data", "getting data", "sending email", or as granular as you'd like. And
|
235
|
+
have that forwarded onto all sub-services.
|
236
|
+
|
237
|
+
You see, the primary design goal is targeted at edge or service workers — where lean quick tracers is favoured.
|
238
|
+
|
239
|
+
Rian is still in active development, but ready for production!
|
240
|
+
|
241
|
+
## 💨 Benchmark
|
242
|
+
|
243
|
+
> via the [`/bench`](/bench) directory with Node v17.2.0
|
244
|
+
|
245
|
+
```
|
246
|
+
Validation :: single span
|
247
|
+
✔ rian
|
248
|
+
✔ opentelemetry
|
249
|
+
✔ opentracing
|
250
|
+
|
251
|
+
Benchmark :: single span
|
252
|
+
rian x 137,181 ops/sec ±2.82% (82 runs sampled)
|
253
|
+
opentelemetry x 114,197 ops/sec ±11.37% (75 runs sampled)
|
254
|
+
opentracing x 33,363 ops/sec ±1.27% (89 runs sampled)
|
255
|
+
|
256
|
+
Validation :: child span
|
257
|
+
✔ rian
|
258
|
+
✔ opentelemetry
|
259
|
+
✔ opentracing
|
260
|
+
|
261
|
+
Benchmark :: child span
|
262
|
+
rian x 75,567 ops/sec ±7.95% (77 runs sampled)
|
263
|
+
opentelemetry x 65,618 ops/sec ±8.45% (82 runs sampled)
|
264
|
+
opentracing x 15,452 ops/sec ±15.35% (77 runs sampled)
|
265
|
+
|
266
|
+
```
|
267
|
+
|
268
|
+
> And please... I know these results are anything but the full story. But it's a number and point on comparison.
|
269
|
+
|
270
|
+
## License
|
271
|
+
|
272
|
+
MIT © [Marais Rossouw](https://marais.io)
|
273
|
+
|
274
|
+
##### Disclaimer
|
275
|
+
|
276
|
+
<sup>- NewRelic is a registered trademark of https://newrelic.com/ and not affiliated with this project.</sup><br />
|
277
|
+
<sup>- DataDog is a registered trademark of https://www.datadoghq.com/ and not affiliated with this project.</sup><br />
|
278
|
+
<sup>- LightStep is a registered trademark of https://lightstep.com/ and not affiliated with this project.</sup>
|
package/utils/index.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
var r=(r,t,...e)=>{try{var n=t(...e,r),o=n instanceof Promise;return o&&r.__add_promise(n.catch((t=>{r.set_context({error:t})})).finally((()=>r.end()))),n}catch(t){throw t instanceof Error&&r.set_context({error:t}),t}finally{!0!==o&&r.end()}},t=(t,e,n,...o)=>r(t.fork(e),n,...o),e=(t,e,n)=>function(){return r(t.fork(e),n,...arguments)};exports.measure=t;exports.measureFn=r;exports.wrap=e;
|
package/utils/index.mjs
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
var r=(r,t,...e)=>{try{var n=t(...e,r),o=n instanceof Promise;return o&&r.__add_promise(n.catch((t=>{r.set_context({error:t})})).finally((()=>r.end()))),n}catch(t){throw t instanceof Error&&r.set_context({error:t}),t}finally{!0!==o&&r.end()}},t=(t,e,n,...o)=>r(t.fork(e),n,...o),e=(t,e,n)=>function(){return r(t.fork(e),n,...arguments)};export{t as measure,r as measureFn,e as wrap};
|