rian 0.3.0-next.1 → 0.3.0-next.11

Sign up to get free protection for your applications and to get access to all the features.
package/async.d.ts ADDED
@@ -0,0 +1,59 @@
1
+ import type { CallableScope, Options, Scope } from 'rian';
2
+
3
+ export { report, configure } from 'rian';
4
+
5
+ /**
6
+ * Returns the current span in the current execution context.
7
+ *
8
+ * This will throw an error if there is no current span.
9
+ *
10
+ * @example
11
+ *
12
+ * ```ts
13
+ * function doWork() {
14
+ * const span = currentSpan();
15
+ * span.set_context({ foo: 'bar' });
16
+ * }
17
+ *
18
+ * span('some-name')(() => {
19
+ * doWork(); // will guarantee `currentSpan` returns this span
20
+ * });
21
+ * ```
22
+ */
23
+ export function currentSpan(): Scope;
24
+
25
+ /**
26
+ * Creates a new span for the currently active tracer.
27
+ *
28
+ * @example
29
+ *
30
+ * ```ts
31
+ * tracer('some-name')(() => {
32
+ * // some deeply nested moments later
33
+ * const s = span('my-span');
34
+ * });
35
+ * ```
36
+ */
37
+ export function span(name: string): CallableScope;
38
+
39
+ export type Tracer<T> = (cb: T) => ReturnType<T>;
40
+
41
+ /**
42
+ * A tracer is a logical unit in your application. This alleviates the need to pass around a tracer instance.
43
+ *
44
+ * All spans produced by a tracer will all collect into a single span collection that is given to {@link report}.
45
+ *
46
+ * @example
47
+ *
48
+ * ```ts
49
+ * const trace = tracer('server');
50
+ *
51
+ * trace(() => {
52
+ * // application logic
53
+ * });
54
+ * ```
55
+ */
56
+ export function tracer<T extends () => any>(
57
+ name: string,
58
+ options?: Options,
59
+ ): Tracer<T>;
package/async.js ADDED
@@ -0,0 +1 @@
1
+ const e = require('node:async_hooks');const { measure:t } = require('rian/utils');const { make:n, parse:r, SAMPLED_FLAG:o } = require('tctx');const { is_sampled:a } = require('tctx');var s={};function c(e,t={}){s={...t,"service.name":e,"telemetry.sdk.name":"rian","telemetry.sdk.version":"0.3.0-next.11"}}var l=new Set,i=new WeakMap;async function p(e){let t=[],n=new Map;for(let[e,r]of l){let o;n.has(r)?o=n.get(r).spans:n.set(r,{scope:r,spans:o=[]}),o.push(e),i.has(r)&&(t.push(...i.get(r)),i.delete(r))}return l.clear(),t.length&&await Promise.all(t),e({resource:s,scopeSpans:n.values()})}function u(e,t){return a(t)}var d=new e.AsyncLocalStorage;function m(){let e=d.getStore()?.[1];if(null==e)throw new Error("no current span");return e}function f(e){let r=d.getStore();if(!r)throw Error("TODO");let o=r[0],a=o.scope,s=r[1],c=o.sampler,p=s?.traceparent??o.root_id,u=p?p.child():n(),m="boolean"!=typeof c?c(e,u,a):c;u.flags;let w={id:u,parent:p,start:o.clock.now(),name:e,events:[],context:{}};m&&l.add([w,a]);let g=e=>d.run([o,g],t,g,e);g.traceparent=u,g.span=f,g.set_context=e=>{"function"!=typeof e?Object.assign(w.context,e):w.context=e(w.context)},g.add_event=(e,t)=>{w.events.push({name:e,timestamp:o.clock.now(),attributes:t||{}})},g.end=()=>{null==w.end&&(w.end=o.clock.now())};let h=i.get(a);return g.__add_promise=e=>{h.add(e),e.then((()=>h.delete(e)))},g}function w(e,t){let n=t?.sampler??u,o={name:e},a={root_id:"string"==typeof t?.traceparent?r(t.traceparent):void 0,scope:o,sampler:n,clock:t?.clock??Date};return i.set(o,new Set),function(e){let t=d.getStore();return a.root_id||(a.root_id=t?.[0].root_id),d.run([a,t?.[1]||null],e)}}exports.configure=c;exports.currentSpan=m;exports.report=p;exports.span=f;exports.tracer=w;
package/async.mjs ADDED
@@ -0,0 +1 @@
1
+ import*as e from"node:async_hooks";import{measure as t}from"rian/utils";import{make as n,parse as r,SAMPLED_FLAG as o}from"tctx";import{is_sampled as a}from"tctx";var s={};function c(e,t={}){s={...t,"service.name":e,"telemetry.sdk.name":"rian","telemetry.sdk.version":"0.3.0-next.11"}}var l=new Set,i=new WeakMap;async function p(e){let t=[],n=new Map;for(let[e,r]of l){let o;n.has(r)?o=n.get(r).spans:n.set(r,{scope:r,spans:o=[]}),o.push(e),i.has(r)&&(t.push(...i.get(r)),i.delete(r))}return l.clear(),t.length&&await Promise.all(t),e({resource:s,scopeSpans:n.values()})}function u(e,t){return a(t)}var d=new e.AsyncLocalStorage;function m(){let e=d.getStore()?.[1];if(null==e)throw new Error("no current span");return e}function f(e){let r=d.getStore();if(!r)throw Error("TODO");let o=r[0],a=o.scope,s=r[1],c=o.sampler,p=s?.traceparent??o.root_id,u=p?p.child():n(),m="boolean"!=typeof c?c(e,u,a):c;u.flags;let w={id:u,parent:p,start:o.clock.now(),name:e,events:[],context:{}};m&&l.add([w,a]);let g=e=>d.run([o,g],t,g,e);g.traceparent=u,g.span=f,g.set_context=e=>{"function"!=typeof e?Object.assign(w.context,e):w.context=e(w.context)},g.add_event=(e,t)=>{w.events.push({name:e,timestamp:o.clock.now(),attributes:t||{}})},g.end=()=>{null==w.end&&(w.end=o.clock.now())};let h=i.get(a);return g.__add_promise=e=>{h.add(e),e.then((()=>h.delete(e)))},g}function w(e,t){let n=t?.sampler??u,o={name:e},a={root_id:"string"==typeof t?.traceparent?r(t.traceparent):void 0,scope:o,sampler:n,clock:t?.clock??Date};return i.set(o,new Set),function(e){let t=d.getStore();return a.root_id||(a.root_id=t?.[0].root_id),d.run([a,t?.[1]||null],e)}}export{c as configure,m as currentSpan,p as report,f as span,w as tracer};
@@ -0,0 +1 @@
1
+ var e=r=>{let a=typeof r,s={};return"string"===a?s.stringValue=r:"number"===a?Number.isInteger(r)?s.intValue=r:s.doubleValue=r:"boolean"===a?s.boolValue=r:Array.isArray(r)?s.arrayValue={values:r.map((t=>e(t)))}:r&&(s.kvlistValue={values:t(r)}),s},t=t=>{let r=[];for(let a of Object.keys(t))r.push({key:a,value:e(t[a])});return r},r=e=>{switch(e){default:case"INTERNAL":return 1;case"SERVER":return 2;case"CLIENT":return 3;case"PRODUCER":return 4;case"CONSUMER":return 5}},a=e=>a=>{let s=[];for(let e of a.scopeSpans){let a=[];s.push({scope:e.scope,spans:a});for(let s of e.spans){let e,{kind:n,error:u,...o}=s.context;u&&(e={code:2},"message"in u&&(e.message=u.message)),a.push({traceId:s.id.trace_id,spanId:s.id.parent_id,parentSpanId:s.parent?.parent_id,name:s.name,kind:r(n||"INTERNAL"),startTimeUnixNano:1e6*s.start,endTimeUnixNano:s.end?1e6*s.end:void 0,droppedAttributesCount:0,droppedEventsCount:0,droppedLinksCount:0,attributes:t(o),status:e||{code:0},events:s.events.map((e=>({name:e.name,attributes:t(e.attributes),droppedAttributesCount:0,timeUnixNano:1e6*e.timestamp})))})}}return e({resourceSpans:[{resource:{attributes:t(a.resource),droppedAttributesCount:0},scopeSpans:s}]})};exports.exporter=a;
@@ -0,0 +1 @@
1
+ var e=r=>{let a=typeof r,s={};return"string"===a?s.stringValue=r:"number"===a?Number.isInteger(r)?s.intValue=r:s.doubleValue=r:"boolean"===a?s.boolValue=r:Array.isArray(r)?s.arrayValue={values:r.map((t=>e(t)))}:r&&(s.kvlistValue={values:t(r)}),s},t=t=>{let r=[];for(let a of Object.keys(t))r.push({key:a,value:e(t[a])});return r},r=e=>{switch(e){default:case"INTERNAL":return 1;case"SERVER":return 2;case"CLIENT":return 3;case"PRODUCER":return 4;case"CONSUMER":return 5}},a=e=>a=>{let s=[];for(let e of a.scopeSpans){let a=[];s.push({scope:e.scope,spans:a});for(let s of e.spans){let e,{kind:n,error:u,...o}=s.context;u&&(e={code:2},"message"in u&&(e.message=u.message)),a.push({traceId:s.id.trace_id,spanId:s.id.parent_id,parentSpanId:s.parent?.parent_id,name:s.name,kind:r(n||"INTERNAL"),startTimeUnixNano:1e6*s.start,endTimeUnixNano:s.end?1e6*s.end:void 0,droppedAttributesCount:0,droppedEventsCount:0,droppedLinksCount:0,attributes:t(o),status:e||{code:0},events:s.events.map((e=>({name:e.name,attributes:t(e.attributes),droppedAttributesCount:0,timeUnixNano:1e6*e.timestamp})))})}}return e({resourceSpans:[{resource:{attributes:t(a.resource),droppedAttributesCount:0},scopeSpans:s}]})};export{a as exporter};
@@ -0,0 +1 @@
1
+ const { flattie:e } = require('flattie');var t=t=>a=>{let r=[];for(let t of a.scopeSpans)for(let n of t.spans){let{kind:s,error:i,...o}=n.context;i&&(o.error=!("message"in i)||{name:i.name,message:i.message,stack:i.stack}),r.push({id:n.id.parent_id,traceId:n.id.trace_id,parentId:n.parent?n.parent.parent_id:void 0,name:n.name,kind:"INTERNAL"===s?void 0:s,timestamp:1e3*n.start,duration:n.end?1e3*(n.end-n.start):void 0,localEndpoint:{serviceName:`${a.resource["service.name"]}@${t.scope.name}`},tags:e({...a.resource,...o},".",!0),annotations:n.events.map((e=>({value:`${e.name} :: ${JSON.stringify(e.attributes)}`,timestamp:1e3*e.timestamp})))})}return t(r)};exports.exporter=t;
@@ -0,0 +1 @@
1
+ import{flattie as e}from"flattie";var t=t=>a=>{let r=[];for(let t of a.scopeSpans)for(let n of t.spans){let{kind:s,error:i,...o}=n.context;i&&(o.error=!("message"in i)||{name:i.name,message:i.message,stack:i.stack}),r.push({id:n.id.parent_id,traceId:n.id.trace_id,parentId:n.parent?n.parent.parent_id:void 0,name:n.name,kind:"INTERNAL"===s?void 0:s,timestamp:1e3*n.start,duration:n.end?1e3*(n.end-n.start):void 0,localEndpoint:{serviceName:`${a.resource["service.name"]}@${t.scope.name}`},tags:e({...a.resource,...o},".",!0),annotations:n.events.map((e=>({value:`${e.name} :: ${JSON.stringify(e.attributes)}`,timestamp:1e3*e.timestamp})))})}return t(r)};export{t as exporter};
package/index.d.ts CHANGED
@@ -1,31 +1,91 @@
1
1
  import type { Traceparent } from 'tctx';
2
2
 
3
+ // --- tracer
4
+
5
+ /**
6
+ * The exporter is called when the {@link report} method is called.
7
+ */
8
+ export type Exporter = (trace: {
9
+ resource: Context;
10
+ scopeSpans: IterableIterator<ScopedSpans>;
11
+ }) => any;
12
+
13
+ export type ScopedSpans = {
14
+ readonly scope: { readonly name: string };
15
+ readonly spans: ReadonlyArray<Readonly<Span>>;
16
+ };
17
+
18
+ export type Options = {
19
+ /**
20
+ * @borrows {@link Sampler}
21
+ */
22
+ sampler?: Sampler | boolean;
23
+
24
+ /**
25
+ * A root, or extracted w3c traceparent string header.
26
+ *
27
+ * If the id is malformed, the {@link create} method will throw an exception. If no root is
28
+ * provided then one will be created obeying the {@link Options.sampler|sampling} rules on each span.
29
+ */
30
+ traceparent?: string | null;
31
+
32
+ clock?: ClockLike;
33
+ };
34
+
35
+ export type Tracer = Pick<Scope, 'span'>;
36
+
37
+ /**
38
+ * @borrows {@link Span.context}
39
+ */
40
+ export type Context = {
41
+ [property: string]: any;
42
+ };
43
+
44
+ /**
45
+ * Allows a sampling decision to be made. This method will influence the {@link Span.id|traceparent} sampling flag.
46
+ *
47
+ * Return true if the span should be sampled, and reported to the {@link Exporter}.
48
+ * Return false if the span should not be sampled, and not reported to the {@link Exporter}.
49
+ */
50
+ export type Sampler = (
51
+ /**
52
+ * The name of the span.
53
+ */
54
+ readonly name: string,
55
+ /**
56
+ * The traceparent id of the span.
57
+ */
58
+ readonly id: Traceparent,
59
+ /**
60
+ * The tracer this span belongs to.
61
+ */
62
+ readonly tracer: { readonly name: string },
63
+ ) => boolean;
64
+
65
+ // --- spans
66
+
3
67
  /**
4
68
  * Spans are units within a distributed trace. Spans encapsulate mainly 3 pieces of information, a
5
69
  * {@link Span.name|name}, and a {@link Span.start|start} and {@link Span.end|end} time.
6
70
  *
7
71
  * Each span should be named, not too vague, and not too precise. For example, "resolve_user_ids"
8
- * and not "resolver_user_ids[1,2,3]" nor "resolve".
72
+ * and not "resolver_user_ids[1,2,3]" nor "resolver".
9
73
  *
10
74
  * A span forms part of a wider trace, and can be visualized like:
11
75
  *
12
76
  * ```plain
13
77
  * [Span A················································(2ms)]
14
78
  * [Span B·········································(1.7ms)]
15
- * [Span D···············(0.8ms)] [Span C......(0.6ms)]
79
+ * [Span D···············(0.8ms)] [Span C......(0.6ms)]
16
80
  * ```
17
- *
18
- * ---
19
- *
20
- * Spans are aimed to interoperate with
21
- * {@link https://github.com/opentracing/specification/blob/master/specification.md|OpenTracing's Spans}, albeit not entirely api compatible — they do share principles.
22
81
  */
23
- export interface Span {
82
+ export type Span = {
24
83
  /**
25
84
  * A human-readable name for this span. For example the function name, the name of a subtask,
26
85
  * or stage of the larger stack.
27
86
  *
28
87
  * @example
88
+ *
29
89
  * "resolve_user_ids"
30
90
  * "[POST] /api"
31
91
  */
@@ -49,7 +109,7 @@ export interface Span {
49
109
  /**
50
110
  * The time represented as a UNIX epoch timestamp in milliseconds when this span was created.
51
111
  * Typically, via
52
- * {@link Scope.fork|tracer.fork()}.
112
+ * {@link Scope.span|scope.span()}.
53
113
  */
54
114
  start: number;
55
115
 
@@ -63,7 +123,7 @@ export interface Span {
63
123
  * An arbitrary context object useful for storing information during a trace.
64
124
  *
65
125
  * Usually following a convention such as `tag.*`, `http.*` or any of the
66
- * {@link https://github.com/opentracing/specification/blob/master/semantic_conventions.md|Semantic Conventions outlined by OpenTracing}.
126
+ * {@link https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/|OpenTelemetry Trace Semantic Conventions}.
67
127
  *
68
128
  * ### Note!
69
129
  *
@@ -84,9 +144,11 @@ export interface Span {
84
144
  * new span.
85
145
  */
86
146
  events: { name: string; timestamp: number; attributes: Context }[];
87
- }
147
+ };
88
148
 
89
- export interface Scope {
149
+ // --- scopes
150
+
151
+ export type Scope = {
90
152
  /**
91
153
  * A W3C traceparent. One can .toString() this if you want to cross a network.
92
154
  */
@@ -95,7 +157,12 @@ export interface Scope {
95
157
  /**
96
158
  * Forks the span into a new child span.
97
159
  */
98
- span(name: string): CallableScope;
160
+ span(
161
+ /**
162
+ * @borrows {@link Span.name}
163
+ */
164
+ name: string,
165
+ ): CallableScope;
99
166
 
100
167
  /**
101
168
  * Allows the span's context to be set. Passing an object will be `Object.assign`ed into the
@@ -116,70 +183,67 @@ export interface Scope {
116
183
  * timestamp nulled out — when the tracer ends.
117
184
  */
118
185
  end(): void;
119
- }
186
+ };
187
+
188
+ export type CallableScope = Scope & {
189
+ <Fn extends (scope: Omit<Scope, 'end'>) => any>(cb: Fn): ReturnType<Fn>;
190
+ };
120
191
 
121
- export interface Tracer extends Pick<Scope, 'span'> {
122
- report(): ReturnType<Exporter>;
123
- }
192
+ // --- main api
124
193
 
125
194
  /**
126
- * An exporter is a method called when the parent scope ends, gets given a Set of all spans traced
127
- * during this execution.
195
+ * A tracer is a logical unit in your application. This alleviates the need to pass around a tracer instance.
196
+ *
197
+ * All spans produced by a tracer will all collect into a single span collection that is given to {@link report}.
198
+ *
199
+ * @example
200
+ *
201
+ * ```ts
202
+ * // file: server.ts
203
+ * const trace = tracer('server');
204
+ *
205
+ * // file: orm.ts
206
+ * const trace = tracer('orm');
207
+ *
208
+ * // file: api.ts
209
+ * const trace = tracer('api');
210
+ * ```
128
211
  */
129
- export type Exporter = (
130
- spans: ReadonlySet<Readonly<Span>>,
131
- context: Context,
132
- ) => any;
212
+ export function tracer(name: string, options?: Options): Tracer;
213
+
214
+ // -- general api
133
215
 
134
216
  /**
135
- * @borrows {@link Span.context}
217
+ * Awaits all active promises, and then calls the {@link Options.exporter|exporter}. Passing all collected spans.
136
218
  */
137
- export interface Context {
138
- [property: string]: any;
139
- }
219
+ export async function report<T extends Exporter>(
220
+ exporter: T,
221
+ ): Promise<ReturnType<T>>;
140
222
 
141
223
  /**
142
- * Should return true when you want to sample the span, this is ran before the span is traced — so
143
- * decisions is made preemptively.
224
+ * Calling this method will set the resource attributes for this runtime. This is useful for things like:
225
+ * - setting the deployment environment of the application
226
+ * - setting the k8s namespace
227
+ * - ...
144
228
  *
145
- * The Span itself will still be included in the {@link Options.exporter|exporter}, and can be
146
- * filtered out there.
229
+ * The `name` argument will set the `service.name` attribute. And is required.
147
230
  *
148
- * Sampling does impact the traceparent, for injection and is encoded there.
231
+ * The fields can be whatever you want, but it is recommended to follow the {@link https://opentelemetry.io/docs/reference/specification/resource/semantic_conventions/|OpenTelemetry Resource Semantic Conventions}.
232
+ *
233
+ * @example
234
+ *
235
+ * ```ts
236
+ * configure('my-service', { 'deployment.environment': 'production', 'k8s.namespace.name': 'default' });
237
+ * ```
149
238
  */
150
- export type Sampler = (
151
- name: string,
152
- parentId?: Traceparent,
153
- context?: Context,
154
- ) => boolean;
155
-
156
- export interface Options {
157
- /**
158
- * @borrows {@link Exporter}
159
- */
160
- exporter: Exporter;
161
-
162
- /**
163
- * @borrows {@link Sampler}
164
- */
165
- sampler?: Sampler | boolean;
166
-
167
- context?: Context;
239
+ export function configure(name: string, attributes: Context = {}): void;
168
240
 
241
+ /**
242
+ * Provinding a clock allows you to control the time of the span.
243
+ */
244
+ export type ClockLike = {
169
245
  /**
170
- * A root, or extracted w3c traceparent stringed header.
171
- *
172
- * If the id is malformed, the {@link create} method will throw an exception. If no root is
173
- * provided then one will be created obeying the {@link Options.sampler|sampling} rules.
246
+ * Must return the number of milliseconds since the epoch.
174
247
  */
175
- traceparent?: string | null;
176
- }
177
-
178
- export const create: (name: string, options: Options) => Tracer;
179
-
180
- // ==> internals
181
-
182
- /** @internal */
183
- export interface CallableScope extends Scope {
184
- (cb: (scope: Omit<Scope, 'end'>) => void): ReturnType<typeof cb>;
185
- }
248
+ now(): number;
249
+ };
package/index.js CHANGED
@@ -1 +1 @@
1
- const { measureFn:e } = require('rian/utils');const t = require('tctx');var n=(e,n)=>!n||t.is_sampled(n),a=(a,r)=>{let o=new Set,s=new Set,i=r.sampler||n,d="boolean"!=typeof i,p=r.context||{};p["service.name"]=a,p["telemetry.sdk.name"]="rian",p["telemetry.sdk.version"]="0.3.0-next.1";let m="string"==typeof r.traceparent?t.parse(r.traceparent):void 0,c=(n,a)=>{let p=d?i(n,a,r.context):i,m=a?a.child(p):t.make(p),l={id:m,parent:a,start:Date.now(),name:n,events:[],context:{}};p&&o.add(l);let x=t=>e(x,t);return x.traceparent=m,x.span=e=>c(e,m),x.set_context=e=>"function"==typeof e?void(l.context=e(l.context)):void Object.assign(l.context,e),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=s.add.bind(s),x};return{span:e=>c(e,m),report:async()=>(s.size>0&&await Promise.all([...s.values()]),r.exporter(o,p))}};exports.create=a;
1
+ const { measure:e } = require('rian/utils');const { make:t, parse:n, SAMPLED_FLAG:a } = require('tctx');const { is_sampled:r } = require('tctx');var o={};function s(e,t={}){o={...t,"service.name":e,"telemetry.sdk.name":"rian","telemetry.sdk.version":"0.3.0-next.11"}}var p=new Set,c=new WeakMap;async function i(e){let t=[],n=new Map;for(let[e,a]of p){let r;n.has(a)?r=n.get(a).spans:n.set(a,{scope:a,spans:r=[]}),r.push(e),c.has(a)&&(t.push(...c.get(a)),c.delete(a))}return p.clear(),t.length&&await Promise.all(t),e({resource:o,scopeSpans:n.values()})}function l(e,t){return r(t)}function d(a,r){let o=r?.sampler??l,s=r?.clock??Date,i={name:a},d=new Set;c.set(i,d);let m="string"==typeof r?.traceparent?n(r.traceparent):void 0,u=(n,a)=>{let r=a?a.child():t(),c="boolean"!=typeof o?o(n,r,i):o;r.flags;let l={id:r,parent:a,start:s.now(),name:n,events:[],context:{}};c&&p.add([l,i]);let m=t=>e(m,t);return m.traceparent=r,m.span=e=>u(e,r),m.set_context=e=>"function"==typeof e?void(l.context=e(l.context)):void Object.assign(l.context,e),m.add_event=(e,t)=>{l.events.push({name:e,timestamp:s.now(),attributes:t||{}})},m.end=()=>{null==l.end&&(l.end=s.now())},m.__add_promise=e=>{d.add(e),e.then((()=>d.delete(e)))},m};return{span:e=>u(e,m)}}exports.configure=s;exports.report=i;exports.tracer=d;
package/index.mjs CHANGED
@@ -1 +1 @@
1
- import{measureFn as e}from"rian/utils";import*as t from"tctx";var n=(e,n)=>!n||t.is_sampled(n),a=(a,r)=>{let o=new Set,s=new Set,i=r.sampler||n,d="boolean"!=typeof i,p=r.context||{};p["service.name"]=a,p["telemetry.sdk.name"]="rian",p["telemetry.sdk.version"]="0.3.0-next.1";let m="string"==typeof r.traceparent?t.parse(r.traceparent):void 0,c=(n,a)=>{let p=d?i(n,a,r.context):i,m=a?a.child(p):t.make(p),l={id:m,parent:a,start:Date.now(),name:n,events:[],context:{}};p&&o.add(l);let x=t=>e(x,t);return x.traceparent=m,x.span=e=>c(e,m),x.set_context=e=>"function"==typeof e?void(l.context=e(l.context)):void Object.assign(l.context,e),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=s.add.bind(s),x};return{span:e=>c(e,m),report:async()=>(s.size>0&&await Promise.all([...s.values()]),r.exporter(o,p))}};export{a as create};
1
+ import{measure as e}from"rian/utils";import{make as t,parse as n,SAMPLED_FLAG as a}from"tctx";import{is_sampled as r}from"tctx";var o={};function s(e,t={}){o={...t,"service.name":e,"telemetry.sdk.name":"rian","telemetry.sdk.version":"0.3.0-next.11"}}var p=new Set,c=new WeakMap;async function i(e){let t=[],n=new Map;for(let[e,a]of p){let r;n.has(a)?r=n.get(a).spans:n.set(a,{scope:a,spans:r=[]}),r.push(e),c.has(a)&&(t.push(...c.get(a)),c.delete(a))}return p.clear(),t.length&&await Promise.all(t),e({resource:o,scopeSpans:n.values()})}function l(e,t){return r(t)}function d(a,r){let o=r?.sampler??l,s=r?.clock??Date,i={name:a},d=new Set;c.set(i,d);let m="string"==typeof r?.traceparent?n(r.traceparent):void 0,u=(n,a)=>{let r=a?a.child():t(),c="boolean"!=typeof o?o(n,r,i):o;r.flags;let l={id:r,parent:a,start:s.now(),name:n,events:[],context:{}};c&&p.add([l,i]);let m=t=>e(m,t);return m.traceparent=r,m.span=e=>u(e,r),m.set_context=e=>"function"==typeof e?void(l.context=e(l.context)):void Object.assign(l.context,e),m.add_event=(e,t)=>{l.events.push({name:e,timestamp:s.now(),attributes:t||{}})},m.end=()=>{null==l.end&&(l.end=s.now())},m.__add_promise=e=>{d.add(e),e.then((()=>d.delete(e)))},m};return{span:e=>u(e,m)}}export{s as configure,i as report,d as tracer};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rian",
3
- "version": "0.3.0-next.1",
3
+ "version": "0.3.0-next.11",
4
4
  "description": "Effective tracing for the edge and origins",
5
5
  "keywords": [
6
6
  "opentelemetry",
@@ -25,20 +25,25 @@
25
25
  "import": "./index.mjs",
26
26
  "require": "./index.js"
27
27
  },
28
+ "./async": {
29
+ "types": "./async.d.ts",
30
+ "import": "./async.mjs",
31
+ "require": "./async.js"
32
+ },
28
33
  "./exporter.otel.http": {
29
- "types": "./exporter.otel.http/index.d.ts",
30
- "import": "./exporter.otel.http/index.mjs",
31
- "require": "./exporter.otel.http/index.js"
34
+ "types": "./exporter.otel.http.d.ts",
35
+ "import": "./exporter.otel.http.mjs",
36
+ "require": "./exporter.otel.http.js"
32
37
  },
33
38
  "./exporter.zipkin": {
34
- "types": "./exporter.zipkin/index.d.ts",
35
- "import": "./exporter.zipkin/index.mjs",
36
- "require": "./exporter.zipkin/index.js"
39
+ "types": "./exporter.zipkin.d.ts",
40
+ "import": "./exporter.zipkin.mjs",
41
+ "require": "./exporter.zipkin.js"
37
42
  },
38
43
  "./utils": {
39
- "types": "./utils/index.d.ts",
40
- "import": "./utils/index.mjs",
41
- "require": "./utils/index.js"
44
+ "types": "./utils.d.ts",
45
+ "import": "./utils.mjs",
46
+ "require": "./utils.js"
42
47
  },
43
48
  "./package.json": "./package.json"
44
49
  },
@@ -49,6 +54,7 @@
49
54
  "*.mjs",
50
55
  "*.js",
51
56
  "*.d.ts",
57
+ "!global.d.ts",
52
58
  "exporter.*/*",
53
59
  "utils/*"
54
60
  ],
package/readme.md CHANGED
@@ -6,9 +6,6 @@
6
6
 
7
7
  <p><code>npm add rian</code> doesn't overcomplicate tracing</p>
8
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
9
  <a href="https://npm-stat.com/charts.html?package=rian">
13
10
  <img src="https://badgen.net/npm/dw/rian?labelColor=black&color=black&cache=600" alt="downloads"/>
14
11
  </a>
@@ -26,80 +23,136 @@
26
23
 
27
24
  ## ⚡ Features
28
25
 
29
- - 🤔 **Familiar** — looks very much like OpenTracing.
26
+ - 🤔 **Familiar** — looks very much like opentelemetry.
30
27
 
31
- - ✅ **Simple** — `create` a tracer, and `.report()` a tracer, done.
28
+ - ✅ **Simple** — `configure()` an environment, create a `tracer()`, `report()` and done.
32
29
 
33
30
  - 🏎 **Performant** — check the [benchmarks](#-benchmark).
34
31
 
35
- - 🪶 **Lightweight** — a mere 1Kb and next to no [dependencies](https://npm.anvaka.com/#/view/2d/rian/).
32
+ - 🪶 **Lightweight** — a mere 1KB and next to no [dependencies](https://npm.anvaka.com/#/view/2d/rian/).
36
33
 
37
34
  ## 🚀 Usage
38
35
 
39
- > Visit [/examples](/examples) for more info!
36
+ > Visit [/examples](/examples) for more!
40
37
 
41
38
  ```ts
42
- import { create } from 'rian';
43
- import { measure } from 'rian/utils';
39
+ import { configure, tracer, report } from 'rian';
44
40
  import { exporter } from 'rian/exporter.otel.http';
45
41
 
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('my-service', {
56
- exporter: otel_endpoint,
42
+ // ~> configure the environment, all tracers will inherit this
43
+ configure('my-service' {
44
+ 'service.version': 'DEV'
57
45
  });
58
46
 
59
- // Let us trace
47
+ // ~> create a tracer — typically "per request" or "per operation".
48
+ const trace = tracer('request');
60
49
 
61
- const req = tracer.span('GET ~> /data');
50
+ function handler(req) {
51
+ // ~> start a span
52
+ return trace.span(`${req.method} ${req.path}`)(async (s) => {
53
+ // set some fields on this span's context
54
+ s.set_context({ user_id: req.params.user_id });
62
55
 
63
- req.set_context({
64
- user: request_context.user_id,
65
- });
56
+ // ~> span again for `db::read`
57
+ const data = await s.span('db::read')(() => db_execute('SELECT * FROM users'));
66
58
 
67
- // ~> Wrap any method and be timed 🕺🏻
68
- const data = await measure(req.span('db::read'), get_data);
59
+ // ~> maybe have some manual spanning
60
+ const processing_span = s.span('process records');
69
61
 
70
- // ~> Maybe have some in-flow spanning
71
- const span = req.span('process records');
62
+ for (let row of data) {
63
+ processing_span.add_event('doing stuff', { id: row.id });
64
+ do_stuff(row);
65
+ }
72
66
 
73
- for (let row of data) {
74
- span.add_event('doing stuff', { id: row.id });
75
- do_stuff(row);
76
- }
67
+ // don't forget to end
68
+ processing_span.end();
77
69
 
78
- span.end();
70
+ return reply(200, { data });
71
+ });
72
+ }
79
73
 
80
- req.end();
74
+ const otel_exporter = exporter((payload) =>
75
+ fetch('/traces/otlp', {
76
+ method: 'POST',
77
+ body: JSON.stringify(payload),
78
+ }),
79
+ );
81
80
 
82
- // ~> And finally let's export — will also end the root span.
83
- await tracer.report();
81
+ http.listen((req, executionCtx) => {
82
+ // ~> report all the spans once the response is sent
83
+ executionCtx.defer(() => report(otel_exporter));
84
+ return handler(req);
85
+ });
84
86
 
85
87
  /*
86
88
  And we end up with something like this in our reporting tool:
87
89
 
88
- [ GET ~> /data .................................... (1.2ms) ]
89
- [ db::read .... (0.5ms) ]
90
- [ process records .... (0.5ms) ]
90
+ [ GET /data .................,,...................... (1.2ms) ] { request }
91
+ [ db::read .... (0.5ms) ] [ process records .... (0.5ms) ]
92
+ ^ ^ ^ ^
93
+ { user_id } ev { id: 1 } | |
94
+ ev { id: 2 } |
95
+ ev { id: 3 }
91
96
  */
92
97
  ```
93
98
 
99
+ You only need to `report` in your application once somewhere. All spans are collected into the same "bucket".
100
+
94
101
  ## 🔎 API
95
102
 
96
103
  #### Module: [`rian`](./packages/rian/src/index.ts)
97
104
 
98
105
  The main and _default_ module responsible for creating and provisioning spans.
99
106
 
100
- > 💡 Note ~> when providing span context values, please stick to
101
- > [Semantic Conventions](https://github.com/opentracing/specification/blob/master/semantic_conventions.md), but won't be
102
- > enforced.
107
+ > 💡 Note ~> when providing span context values, you can use
108
+ > [Semantic Conventions](https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/), but won't
109
+ > be enforced.
110
+
111
+ #### Module: [`rian/async`](./packages/rian/src/async.ts)
112
+
113
+ A module that utilizes the `async_hooks` API to provide a `tracer` and `spans` that can be used where the current span
114
+ isn't accessible.
115
+
116
+ > 💡 Note ~> this module should be used mutually exclusively with the main `rian` module.
117
+
118
+ <detials>
119
+
120
+ <summary>Example</summary>
121
+
122
+ ```ts
123
+ import { configure, tracer, span, currentSpan, report } from 'rian/async';
124
+ import { exporter } from 'rian/exporter.otel.http';
125
+
126
+ function handler(req) {
127
+ return span(`${req.method} ${req.path}`)(async () => {
128
+ const s = currentSpan();
129
+
130
+ s.set_context({ user_id: req.params.user_id });
131
+
132
+ const data = await s.span('db::read')(() => db_execute('SELECT * FROM users'));
133
+
134
+ const processing_span = s.span('process records');
135
+
136
+ for (let row of data) {
137
+ processing_span.add_event('doing stuff', { id: row.id });
138
+ do_stuff(row);
139
+ }
140
+
141
+ processing_span.end();
142
+
143
+ return reply(200, { data });
144
+ });
145
+ }
146
+
147
+ const httpTrace = tracer('http');
148
+
149
+ http.listen((req, executionCtx) => {
150
+ executionCtx.defer(() => report(exporter));
151
+ return httpTrace(() => handler(req));
152
+ });
153
+ ```
154
+
155
+ </details>
103
156
 
104
157
  #### Module: [`rian/exporter.zipkin`](./packages/rian/src/exporter.zipkin.ts)
105
158
 
@@ -114,7 +167,7 @@ Implements the OpenTelemetry protocol for use with http transports.
114
167
  <details><summary>NewRelic</summary>
115
168
 
116
169
  ```ts
117
- import { create } from 'rian';
170
+ import { configure, tracer, report } from 'rian';
118
171
  import { exporter } from 'rian/exporter.zipkin';
119
172
 
120
173
  const newrelic = exporter((payload) =>
@@ -130,23 +183,25 @@ const newrelic = exporter((payload) =>
130
183
  }),
131
184
  );
132
185
 
133
- const tracer = create('my-service', {
134
- exporter: newrelic,
135
- });
186
+ configure('my-service');
187
+
188
+ const tracer = tracer('app');
189
+
190
+ await report(newrelic);
136
191
  ```
137
192
 
138
193
  [learn more](https://docs.newrelic.com/docs/distributed-tracing/trace-api/introduction-trace-api/)
139
194
 
140
195
  </details>
141
196
 
142
- <details><summary>LightStep</summary>
197
+ <details><summary>Lightstep</summary>
143
198
 
144
199
  ```ts
145
- import { create } from 'rian';
200
+ import { configure, tracer, report } from 'rian';
146
201
  import { exporter } from 'rian/exporter.otel.http';
147
202
 
148
203
  const lightstep = exporter((payload) =>
149
- fetch('https://ingest.lightstep.com/traces/otlp/v0.6', {
204
+ fetch('https://ingest.lightstep.com/traces/otlp/v0.9', {
150
205
  method: 'POST',
151
206
  headers: {
152
207
  'lightstep-access-token': '<your api key>',
@@ -156,9 +211,11 @@ const lightstep = exporter((payload) =>
156
211
  }),
157
212
  );
158
213
 
159
- const tracer = create('my-service', {
160
- exporter: lightstep,
161
- });
214
+ configure('my-service');
215
+
216
+ const tracer = tracer('app');
217
+
218
+ await report(lightstep);
162
219
  ```
163
220
 
164
221
  [learn more](https://opentelemetry.lightstep.com/tracing/)
@@ -167,34 +224,23 @@ const tracer = create('my-service', {
167
224
 
168
225
  ## 🤔 Motivation
169
226
 
170
- Firstly, what is `rian`? _trace_ in Irish is `rian`.
171
-
172
- In efforts to be better observant citizens, we generally reach for the — NewRelic, LightStep, DataDog's. Which, and in
173
- no offence to them, is bloated and slow! Where they more often than not do way too much or and relatively speaking, ship
174
- useless traces. Which ramp up your bill — see... every span you trace, costs.
175
-
176
- And here we are, introducing **rian** — a lightweight, fast effective tracer. Inspired by the giants in the industry,
177
- OpenTracing and OpenTelemetry.
178
-
179
- You might have not heard of those before — and that is okay. It means the design goals from OpenTelemetry or OpenTracing
180
- has been met. They are frameworks built to abstract the telemetry part from vendors. So folk like NewRelic can wrap
181
- their layers on top of open telemetry — and have libraries instrument theirs without knowing about the vendor. Which
182
- allows consumers to ship those spans to the vendor of their choosing. OpenTracing has a very similar design goal, so
183
- please do go checkout their documentation's, to help decide.
227
+ To clarify, `rian` is the Irish word for "trace".
184
228
 
185
- Rian does not intend to align or compete with them. rian's intent is to be used to instrument your application and
186
- **only** your application. Rian is primed in that critical business paths where you don't care " which handlers
187
- MongoDB ran", or how many network calls your ORM made. Cardinality will destroy you. Although rian can scale to support
188
- those as well. But the reality is; there are profiler tools far more capable — "right tool for the job".
229
+ In our efforts to be observant citizens, we often rely on tools such as NewRelic, Lightstep, and Datadog. However, these
230
+ tools can be bloated and slow, often performing too many unnecessary tasks and driving up costs, as every span costs.
189
231
 
190
- Rian is simply a tracer you can use to see what your application is doing, have better insight into why something failed
191
- and stitch it with your logs. It starts by capturing a [`w3c trace-context`](https://www.w3.org/TR/trace-context/),
192
- tracing some business steps. "inbound request /data", "getting data", "sending email", or as granular as you'd like. And
193
- have that forwarded onto all sub-services.
232
+ This is where rian comes in as a lightweight, fast, and effective tracer inspired by industry giants OpenTracing and
233
+ OpenTelemetry. These frameworks were designed to abstract the telemetry part from vendors, allowing libraries to be
234
+ instrumented without needing to know about the vendor.
194
235
 
195
- You see, the primary design goal is targeted at edge or service workers where lean quick tracers is favoured.
236
+ Rian does not intend to align or compete with them, slightly different goals. Rian aims to be used exclusively for
237
+ instrumenting your application, particularly critical business paths. While rian can scale to support more complex
238
+ constructs, there are profiler tools that are better suited for those jobs. Rian's primary design goal is to provide
239
+ better insights into your application's behavior, particularly for edge or service workers where a lean tracer is
240
+ favored.
196
241
 
197
- Rian is still in active development, but ready for production!
242
+ Rian does not by design handle injecting [`w3c trace-context`](https://www.w3.org/TR/trace-context/), or
243
+ [propagating baggage](https://www.w3.org/TR/baggage/). But we do expose api's for achieving this.
198
244
 
199
245
  ## 💨 Benchmark
200
246
 
@@ -204,22 +250,18 @@ Rian is still in active development, but ready for production!
204
250
  Validation :: single span
205
251
  ✔ rian
206
252
  ✔ opentelemetry
207
- ✔ opentracing
208
253
 
209
254
  Benchmark :: single span
210
- rian x 392,796 ops/sec ±3.73% (86 runs sampled)
211
- opentelemetry x 205,544 ops/sec ±11.98% (65 runs sampled)
212
- opentracing x 60,043 ops/sec ±35.25% (97 runs sampled)
255
+ rian x 339,110 ops/sec ±2.11% (89 runs sampled)
256
+ opentelemetry x 199,246 ops/sec ±14.78% (67 runs sampled)
213
257
 
214
258
  Validation :: child span
215
259
  ✔ rian
216
260
  ✔ opentelemetry
217
- ✔ opentracing
218
261
 
219
262
  Benchmark :: child span
220
- rian x 208,186 ops/sec ±6.28% (82 runs sampled)
221
- opentelemetry x 121,740 ops/sec ±16.56% (62 runs sampled)
222
- opentracing x 37,405 ops/sec ±0.48% (99 runs sampled)
263
+ rian x 176,936 ops/sec ±2.30% (88 runs sampled)
264
+ opentelemetry x 124,447 ops/sec ±13.72% (70 runs sampled)
223
265
  ```
224
266
 
225
267
  > And please... I know these results are anything but the full story. But it's a number and point on comparison.
@@ -231,5 +273,5 @@ MIT © [Marais Rossouw](https://marais.io)
231
273
  ##### Disclaimer
232
274
 
233
275
  <sup>- NewRelic is a registered trademark of https://newrelic.com/ and not affiliated with this project.</sup><br />
234
- <sup>- DataDog is a registered trademark of https://www.datadoghq.com/ and not affiliated with this project.</sup><br />
235
- <sup>- LightStep is a registered trademark of https://lightstep.com/ and not affiliated with this project.</sup>
276
+ <sup>- Datadog is a registered trademark of https://www.datadoghq.com/ and not affiliated with this project.</sup><br />
277
+ <sup>- Lightstep is a registered trademark of https://lightstep.com/ and not affiliated with this project.</sup>
package/utils.d.ts ADDED
@@ -0,0 +1,25 @@
1
+ import type { Scope } from 'rian';
2
+
3
+ /**
4
+ * With a passed function, `measure` will run the function and once finishes, will end the span.
5
+ *
6
+ * The measure method will return whatever the function is, so if it's a promise, it returns a
7
+ * promise and so on. Any error is caught and re thrown, and automatically tracked in the
8
+ * context under the `error` property.
9
+ *
10
+ * All promises are tracked, and awaited on a `report`.
11
+ *
12
+ * This is a utility method, but is functionally equivalent to `scope.span('name')(fn)`.
13
+ *
14
+ * @example
15
+ *
16
+ * ```text
17
+ * const data = await measure(scope, get_data);
18
+ * // or with arguments:
19
+ * const data = await measure(scope, () => get_data('foo', 'bar'));
20
+ * ```
21
+ */
22
+ export function measure<Fn extends (scope: Scope) => any>(
23
+ scope: Scope,
24
+ fn: Fn,
25
+ ): ReturnType<Fn>;
package/utils.js ADDED
@@ -0,0 +1 @@
1
+ function r(r,t){try{var e=t(r),n=e instanceof Promise;return n&&r.__add_promise(e.catch((t=>{r.set_context({error:t})})).finally((()=>r.end()))),e}catch(t){throw t instanceof Error&&r.set_context({error:t}),t}finally{!0!==n&&r.end()}}exports.measure=r;
package/utils.mjs ADDED
@@ -0,0 +1 @@
1
+ function r(r,t){try{var e=t(r),n=e instanceof Promise;return n&&r.__add_promise(e.catch((t=>{r.set_context({error:t})})).finally((()=>r.end()))),e}catch(t){throw t instanceof Error&&r.set_context({error:t}),t}finally{!0!==n&&r.end()}}export{r as measure};
@@ -1 +0,0 @@
1
- var e=r=>{let n=typeof r,a={};return"string"===n?a.stringValue=r:"number"===n?Number.isInteger(r)?a.intValue=r:a.doubleValue=r:"boolean"===n?a.boolValue=r:Array.isArray(r)?a.arrayValue={values:r.map((t=>e(t)))}:r&&(a.kvlistValue={values:t(r)}),a},t=t=>{let r=[];for(let n of Object.keys(t))r.push({key:n,value:e(t[n])});return r},r=e=>{switch(e){default:case"INTERNAL":return 1;case"SERVER":return 2;case"CLIENT":return 3;case"PRODUCER":return 4;case"CONSUMER":return 5}},n=e=>(n,a)=>{let s=[];for(let e of n){let n,{kind:a,error:u,...i}=e.context;u&&(n={code:2},"message"in u&&(n.message=u.message)),s.push({traceId:e.id.trace_id,spanId:e.id.parent_id,parentSpanId:e.parent?.parent_id,name:e.name,kind:r(a||"INTERNAL"),startTimeUnixNano:1e6*e.start,endTimeUnixNano:e.end?1e6*e.end:void 0,droppedAttributesCount:0,droppedEventsCount:0,droppedLinksCount:0,attributes:t(i),status:n||{code:0},events:e.events.map((e=>({name:e.name,attributes:t(e.attributes),droppedAttributesCount:0,timeUnixNano:1e6*e.timestamp})))})}return e({resourceSpans:[{resource:{attributes:t(a),droppedAttributesCount:0},instrumentationLibrarySpans:[{instrumentationLibrary:{name:"rian",version:"0.3.0-next.1"},spans:s}]}]})};exports.exporter=n;
@@ -1 +0,0 @@
1
- var e=r=>{let n=typeof r,a={};return"string"===n?a.stringValue=r:"number"===n?Number.isInteger(r)?a.intValue=r:a.doubleValue=r:"boolean"===n?a.boolValue=r:Array.isArray(r)?a.arrayValue={values:r.map((t=>e(t)))}:r&&(a.kvlistValue={values:t(r)}),a},t=t=>{let r=[];for(let n of Object.keys(t))r.push({key:n,value:e(t[n])});return r},r=e=>{switch(e){default:case"INTERNAL":return 1;case"SERVER":return 2;case"CLIENT":return 3;case"PRODUCER":return 4;case"CONSUMER":return 5}},n=e=>(n,a)=>{let s=[];for(let e of n){let n,{kind:a,error:u,...i}=e.context;u&&(n={code:2},"message"in u&&(n.message=u.message)),s.push({traceId:e.id.trace_id,spanId:e.id.parent_id,parentSpanId:e.parent?.parent_id,name:e.name,kind:r(a||"INTERNAL"),startTimeUnixNano:1e6*e.start,endTimeUnixNano:e.end?1e6*e.end:void 0,droppedAttributesCount:0,droppedEventsCount:0,droppedLinksCount:0,attributes:t(i),status:n||{code:0},events:e.events.map((e=>({name:e.name,attributes:t(e.attributes),droppedAttributesCount:0,timeUnixNano:1e6*e.timestamp})))})}return e({resourceSpans:[{resource:{attributes:t(a),droppedAttributesCount:0},instrumentationLibrarySpans:[{instrumentationLibrary:{name:"rian",version:"0.3.0-next.1"},spans:s}]}]})};export{n as exporter};
@@ -1 +0,0 @@
1
- const { flattie:e } = require('flattie');var t=t=>(a,r)=>{let n=[];for(let t of a){let{kind:a,error:i,...s}=t.context;i&&(s.error=!("message"in i)||{name:i.name,message:i.message,stack:i.stack}),n.push({id:t.id.parent_id,traceId:t.id.trace_id,parentId:t.parent?t.parent.parent_id:void 0,name:t.name,kind:"INTERNAL"===a?void 0:a,timestamp:1e3*t.start,duration:t.end?1e3*(t.end-t.start):void 0,localEndpoint:{serviceName:r["service.name"]},tags:e({...r,...s},".",!0),annotations:t.events.map((e=>({value:`${e.name} :: ${JSON.stringify(e.attributes)}`,timestamp:1e3*e.timestamp})))})}return t(n)};exports.exporter=t;
@@ -1 +0,0 @@
1
- import{flattie as e}from"flattie";var t=t=>(a,r)=>{let n=[];for(let t of a){let{kind:a,error:i,...s}=t.context;i&&(s.error=!("message"in i)||{name:i.name,message:i.message,stack:i.stack}),n.push({id:t.id.parent_id,traceId:t.id.trace_id,parentId:t.parent?t.parent.parent_id:void 0,name:t.name,kind:"INTERNAL"===a?void 0:a,timestamp:1e3*t.start,duration:t.end?1e3*(t.end-t.start):void 0,localEndpoint:{serviceName:r["service.name"]},tags:e({...r,...s},".",!0),annotations:t.events.map((e=>({value:`${e.name} :: ${JSON.stringify(e.attributes)}`,timestamp:1e3*e.timestamp})))})}return t(n)};export{t as exporter};
package/utils/index.d.ts DELETED
@@ -1,66 +0,0 @@
1
- import type { Scope } from 'rian';
2
-
3
- export type MeasureFn =
4
- | ((...args: [...args: any[]]) => any)
5
- | ((...args: [...args: any[], scope: Scope]) => any);
6
-
7
- /**
8
- * With a passed function — will start a span, and run the function, when the function finishes
9
- * the span finishes.
10
- *
11
- * The measure method will return whatever the function is, so if it's a promise, it returns a
12
- * promise and so on. Any error is caught and re thrown, and automatically tracked in the
13
- * context under the `error` property.
14
- *
15
- * All promises are tracked, and awaited on a `tracer.end`.
16
- *
17
- * @example
18
- *
19
- * ```text
20
- * const data = await measure(scope, get_data, 'user_id_123');
21
- * ^ ^ ^ ^
22
- * | | | |
23
- * | | | the first argument to get_data
24
- * | | |
25
- * | | function to be called
26
- * | |
27
- * | the parent scope
28
- * return value from get_data
29
- * ```
30
- */
31
- export const measure: <Fn extends MeasureFn>(
32
- scope: Scope,
33
- fn: Fn, // TODO: fn doesnt see scope correctly
34
- ...args: RealMeasureFnParams<Parameters<Fn>>
35
- ) => ReturnType<Fn>;
36
-
37
- /**
38
- * Wraps any function with a measured scoped function. Useful for when defer function execution
39
- * till a later time.
40
- *
41
- * @example
42
- *
43
- * ```js
44
- * const wrapped = wrap(scope, "run something", my_function);
45
- *
46
- * // ... lots of things, where the access to `scope` is lost.
47
- *
48
- * wrapped();
49
- * ```
50
- */
51
- export const wrap: <Fn extends MeasureFn>(
52
- scope: Scope,
53
- fn: Fn, // TODO: fn doesnt see scope correctly
54
- ) => Fn;
55
-
56
- // ==> internals
57
-
58
- /** @internal */
59
- export type RealMeasureFnParams<T extends unknown[]> = T extends []
60
- ? []
61
- : T extends [...rest: infer U, scope: Scope]
62
- ? U
63
- : T;
64
-
65
- /** @internal */
66
- export const measureFn: (scope: Scope, fn: any, ...args: any[]) => any;
package/utils/index.js DELETED
@@ -1 +0,0 @@
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)=>r(t,e,...n),e=(t,e)=>function(){return r(t,e,...arguments)};exports.measure=t;exports.measureFn=r;exports.wrap=e;
package/utils/index.mjs DELETED
@@ -1 +0,0 @@
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)=>r(t,e,...n),e=(t,e)=>function(){return r(t,e,...arguments)};export{t as measure,r as measureFn,e as wrap};