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 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('tctx');const { measureFn:t } = require('rian/utils');var n=new WeakMap,a=(e,t)=>{n.has(e)?n.get(e).push(t):n.set(e,[t])},r=(t,n)=>!n||e.is_sampled(n),o={"telemetry.sdk.name":"rian","telemetry.sdk.version":"0.2.2"},s=(a,s)=>{let i=new Set,p=s.sampler||r,d="boolean"!=typeof p,c=(n,a)=>{let r=d?p(n,a,s.context):p,o=a?a.child(r):e.make(r),m={id:o,parent:a,start:Date.now(),name:n,events:[],context:{}};r&&i.add(m);let l=e=>t(l,e);return l.traceparent=o,l.fork=e=>c(e,o),l.set_context=e=>{"function"!=typeof e?Object.assign(m.context,e):m.context=e(m.context)},l.add_event=(e,t)=>{m.events.push({name:e,timestamp:Date.now(),attributes:t||{}})},l.end=()=>{null==m.end&&(m.end=Date.now())},l},m=c(a,"string"==typeof s.traceparent?e.parse(s.traceparent):void 0),l=m.end.bind(m);return m.end=async()=>(l(),n.has(m)&&await Promise.all(n.get(m)),s.exporter(i,{...s.context||{},...o})),m};exports.ADD_PROMISE=a;exports.PROMISES=n;exports.create=s;
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*as e from"tctx";import{measureFn as t}from"rian/utils";var n=new WeakMap,a=(e,t)=>{n.has(e)?n.get(e).push(t):n.set(e,[t])},r=(t,n)=>!n||e.is_sampled(n),o={"telemetry.sdk.name":"rian","telemetry.sdk.version":"0.2.2"},s=(a,s)=>{let i=new Set,p=s.sampler||r,d="boolean"!=typeof p,c=(n,a)=>{let r=d?p(n,a,s.context):p,o=a?a.child(r):e.make(r),m={id:o,parent:a,start:Date.now(),name:n,events:[],context:{}};r&&i.add(m);let l=e=>t(l,e);return l.traceparent=o,l.fork=e=>c(e,o),l.set_context=e=>{"function"!=typeof e?Object.assign(m.context,e):m.context=e(m.context)},l.add_event=(e,t)=>{m.events.push({name:e,timestamp:Date.now(),attributes:t||{}})},l.end=()=>{null==m.end&&(m.end=Date.now())},l},m=c(a,"string"==typeof s.traceparent?e.parse(s.traceparent):void 0),l=m.end.bind(m);return m.end=async()=>(l(),n.has(m)&&await Promise.all(n.get(m)),s.exporter(i,{...s.context||{},...o})),m};export{a as ADD_PROMISE,n as PROMISES,s as create};
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.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
- "build": "bundt --minify"
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
- "bundt": "2.0.0-next.5"
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
- const { ADD_PROMISE:r, PROMISES:t } = require('rian');var n=(n,o,...e)=>{try{var a=o(...e,n),c=a instanceof Promise;return c&&!t.has(n)&&r(n,a.catch((r=>{n.set_context({error:r})})).finally((()=>n.end()))),a}catch(r){throw r instanceof Error&&n.set_context({error:r}),r}finally{!0!==c&&n.end()}},o=(r,t,o,...e)=>n(r.fork(t),o,...e),e=(r,t,o)=>function(){return n(r.fork(t),o,...arguments)};exports.measure=o;exports.measureFn=n;exports.wrap=e;
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
- import{ADD_PROMISE as r,PROMISES as t}from"rian";var n=(n,o,...e)=>{try{var a=o(...e,n),c=a instanceof Promise;return c&&!t.has(n)&&r(n,a.catch((r=>{n.set_context({error:r})})).finally((()=>n.end()))),a}catch(r){throw r instanceof Error&&n.set_context({error:r}),r}finally{!0!==c&&n.end()}},o=(r,t,o,...e)=>n(r.fork(t),o,...e),e=(r,t,o)=>function(){return n(r.fork(t),o,...arguments)};export{o as measure,n as measureFn,e as wrap};
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};