rian 0.0.2 → 0.0.3

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.
@@ -0,0 +1,5 @@
1
+ import * as rian from 'rian';
2
+
3
+ declare const exporter: (request: (payload: any) => any) => rian.Exporter;
4
+
5
+ export { exporter };
@@ -0,0 +1,112 @@
1
+ 'use strict';
2
+
3
+ const package_json = require('rian/package.json');
4
+
5
+ const SpanStatusCode_UNSET = 0;
6
+ const SpanStatusCode_ERROR = 2;
7
+ const convert_value_to_anyvalue = (value) => {
8
+ let type = typeof value, any_value = {};
9
+ if (type === 'string')
10
+ any_value.stringValue = value;
11
+ else if (type === 'number')
12
+ if (Number.isInteger(value))
13
+ any_value.intValue = value;
14
+ else
15
+ any_value.doubleValue = value;
16
+ else if (type === 'boolean')
17
+ any_value.boolValue = value;
18
+ else if (Array.isArray(value))
19
+ any_value.arrayValue = {
20
+ values: value.map((i) => convert_value_to_anyvalue(i)),
21
+ };
22
+ else
23
+ any_value.kvlistValue = { values: convert_object_to_kv(value) };
24
+ return any_value;
25
+ };
26
+ const convert_object_to_kv = (input) => {
27
+ const value = [];
28
+ for (let key of Object.keys(input)) {
29
+ value.push({
30
+ key,
31
+ value: convert_value_to_anyvalue(input[key]),
32
+ });
33
+ }
34
+ return value;
35
+ };
36
+ // https://github.com/open-telemetry/opentelemetry-proto/blob/b43e9b18b76abf3ee040164b55b9c355217151f3/opentelemetry/proto/trace/v1/trace.proto#L127-L155
37
+ const map_kind = (kind) => {
38
+ switch (kind) {
39
+ default:
40
+ case 'INTERNAL': {
41
+ return 1;
42
+ }
43
+ case 'SERVER': {
44
+ return 2;
45
+ }
46
+ case 'CLIENT': {
47
+ return 3;
48
+ }
49
+ case 'PRODUCER': {
50
+ return 4;
51
+ }
52
+ case 'CONSUMER': {
53
+ return 5;
54
+ }
55
+ }
56
+ };
57
+ const exporter = (request) => (spans, context) => {
58
+ const otel_spans = [];
59
+ for (let span of spans) {
60
+ const { kind, error, ...span_ctx } = span.context;
61
+ let status;
62
+ if (error) {
63
+ status = {
64
+ code: SpanStatusCode_ERROR,
65
+ };
66
+ if ('message' in error) {
67
+ status.message = error.message;
68
+ }
69
+ }
70
+ otel_spans.push({
71
+ traceId: span.id.trace_id,
72
+ spanId: span.id.parent_id,
73
+ parentSpanId: span.parent?.parent_id,
74
+ name: span.name,
75
+ kind: map_kind(kind || 'INTERNAL'),
76
+ startTimeUnixNano: span.start * 1000000,
77
+ endTimeUnixNano: span.end ? span.end * 1000000 : undefined,
78
+ droppedAttributesCount: 0,
79
+ droppedEventsCount: 0,
80
+ droppedLinksCount: 0,
81
+ attributes: convert_object_to_kv(span_ctx),
82
+ status: status || { code: SpanStatusCode_UNSET },
83
+ events: span.events.map((i) => ({
84
+ name: i.name,
85
+ attributes: convert_object_to_kv(i.attributes),
86
+ droppedAttributesCount: 0,
87
+ timeUnixNano: i.timestamp * 1000000,
88
+ })),
89
+ });
90
+ }
91
+ return request({
92
+ resourceSpans: [
93
+ {
94
+ resource: {
95
+ attributes: convert_object_to_kv(context),
96
+ droppedAttributesCount: 0,
97
+ },
98
+ instrumentationLibrarySpans: [
99
+ {
100
+ instrumentationLibrary: {
101
+ name: package_json.name,
102
+ version: package_json.version,
103
+ },
104
+ spans: otel_spans,
105
+ },
106
+ ],
107
+ },
108
+ ],
109
+ });
110
+ };
111
+
112
+ exports.exporter = exporter;
@@ -0,0 +1,110 @@
1
+ import { name, version } from 'rian/package.json';
2
+
3
+ const SpanStatusCode_UNSET = 0;
4
+ const SpanStatusCode_ERROR = 2;
5
+ const convert_value_to_anyvalue = (value) => {
6
+ let type = typeof value, any_value = {};
7
+ if (type === 'string')
8
+ any_value.stringValue = value;
9
+ else if (type === 'number')
10
+ if (Number.isInteger(value))
11
+ any_value.intValue = value;
12
+ else
13
+ any_value.doubleValue = value;
14
+ else if (type === 'boolean')
15
+ any_value.boolValue = value;
16
+ else if (Array.isArray(value))
17
+ any_value.arrayValue = {
18
+ values: value.map((i) => convert_value_to_anyvalue(i)),
19
+ };
20
+ else
21
+ any_value.kvlistValue = { values: convert_object_to_kv(value) };
22
+ return any_value;
23
+ };
24
+ const convert_object_to_kv = (input) => {
25
+ const value = [];
26
+ for (let key of Object.keys(input)) {
27
+ value.push({
28
+ key,
29
+ value: convert_value_to_anyvalue(input[key]),
30
+ });
31
+ }
32
+ return value;
33
+ };
34
+ // https://github.com/open-telemetry/opentelemetry-proto/blob/b43e9b18b76abf3ee040164b55b9c355217151f3/opentelemetry/proto/trace/v1/trace.proto#L127-L155
35
+ const map_kind = (kind) => {
36
+ switch (kind) {
37
+ default:
38
+ case 'INTERNAL': {
39
+ return 1;
40
+ }
41
+ case 'SERVER': {
42
+ return 2;
43
+ }
44
+ case 'CLIENT': {
45
+ return 3;
46
+ }
47
+ case 'PRODUCER': {
48
+ return 4;
49
+ }
50
+ case 'CONSUMER': {
51
+ return 5;
52
+ }
53
+ }
54
+ };
55
+ const exporter = (request) => (spans, context) => {
56
+ const otel_spans = [];
57
+ for (let span of spans) {
58
+ const { kind, error, ...span_ctx } = span.context;
59
+ let status;
60
+ if (error) {
61
+ status = {
62
+ code: SpanStatusCode_ERROR,
63
+ };
64
+ if ('message' in error) {
65
+ status.message = error.message;
66
+ }
67
+ }
68
+ otel_spans.push({
69
+ traceId: span.id.trace_id,
70
+ spanId: span.id.parent_id,
71
+ parentSpanId: span.parent?.parent_id,
72
+ name: span.name,
73
+ kind: map_kind(kind || 'INTERNAL'),
74
+ startTimeUnixNano: span.start * 1000000,
75
+ endTimeUnixNano: span.end ? span.end * 1000000 : undefined,
76
+ droppedAttributesCount: 0,
77
+ droppedEventsCount: 0,
78
+ droppedLinksCount: 0,
79
+ attributes: convert_object_to_kv(span_ctx),
80
+ status: status || { code: SpanStatusCode_UNSET },
81
+ events: span.events.map((i) => ({
82
+ name: i.name,
83
+ attributes: convert_object_to_kv(i.attributes),
84
+ droppedAttributesCount: 0,
85
+ timeUnixNano: i.timestamp * 1000000,
86
+ })),
87
+ });
88
+ }
89
+ return request({
90
+ resourceSpans: [
91
+ {
92
+ resource: {
93
+ attributes: convert_object_to_kv(context),
94
+ droppedAttributesCount: 0,
95
+ },
96
+ instrumentationLibrarySpans: [
97
+ {
98
+ instrumentationLibrary: {
99
+ name: name,
100
+ version: version,
101
+ },
102
+ spans: otel_spans,
103
+ },
104
+ ],
105
+ },
106
+ ],
107
+ });
108
+ };
109
+
110
+ export { exporter };
@@ -0,0 +1,5 @@
1
+ import * as rian from 'rian';
2
+
3
+ declare const exporter: (request: (payload: any) => any) => rian.Exporter;
4
+
5
+ export { exporter };
@@ -0,0 +1,40 @@
1
+ 'use strict';
2
+
3
+ const flattie = require('flattie');
4
+
5
+ const exporter = (request) => (spans, context) => {
6
+ const zipkin = [];
7
+ for (let span of spans) {
8
+ const { kind, error, ...span_ctx } = span.context;
9
+ if (error) {
10
+ if ('message' in error) {
11
+ span_ctx.error = {
12
+ name: error.name,
13
+ message: error.message,
14
+ stack: error.stack,
15
+ };
16
+ }
17
+ else {
18
+ span_ctx.error = true;
19
+ }
20
+ }
21
+ zipkin.push({
22
+ id: span.id.parent_id,
23
+ traceId: span.id.trace_id,
24
+ parentId: span.parent ? span.parent.parent_id : undefined,
25
+ name: span.name,
26
+ kind: kind === 'INTERNAL' ? undefined : kind,
27
+ timestamp: span.start * 1000,
28
+ duration: span.end ? (span.end - span.start) * 1000 : undefined,
29
+ localEndpoint: context.localEndpoint,
30
+ tags: flattie.flattie(Object.assign({}, context, span_ctx), '.', true),
31
+ annotations: span.events.map((i) => ({
32
+ value: `${i.name} :: ${JSON.stringify(i.attributes)}`,
33
+ timestamp: i.timestamp * 1000,
34
+ })),
35
+ });
36
+ }
37
+ return request(zipkin);
38
+ };
39
+
40
+ exports.exporter = exporter;
@@ -0,0 +1,38 @@
1
+ import { flattie } from 'flattie';
2
+
3
+ const exporter = (request) => (spans, context) => {
4
+ const zipkin = [];
5
+ for (let span of spans) {
6
+ const { kind, error, ...span_ctx } = span.context;
7
+ if (error) {
8
+ if ('message' in error) {
9
+ span_ctx.error = {
10
+ name: error.name,
11
+ message: error.message,
12
+ stack: error.stack,
13
+ };
14
+ }
15
+ else {
16
+ span_ctx.error = true;
17
+ }
18
+ }
19
+ zipkin.push({
20
+ id: span.id.parent_id,
21
+ traceId: span.id.trace_id,
22
+ parentId: span.parent ? span.parent.parent_id : undefined,
23
+ name: span.name,
24
+ kind: kind === 'INTERNAL' ? undefined : kind,
25
+ timestamp: span.start * 1000,
26
+ duration: span.end ? (span.end - span.start) * 1000 : undefined,
27
+ localEndpoint: context.localEndpoint,
28
+ tags: flattie(Object.assign({}, context, span_ctx), '.', true),
29
+ annotations: span.events.map((i) => ({
30
+ value: `${i.name} :: ${JSON.stringify(i.attributes)}`,
31
+ timestamp: i.timestamp * 1000,
32
+ })),
33
+ });
34
+ }
35
+ return request(zipkin);
36
+ };
37
+
38
+ export { exporter };
package/index.d.ts CHANGED
@@ -1,35 +1,188 @@
1
- import { Traceparent } from 'rian/tracecontext';
1
+ import { Traceparent } from 'tctx';
2
+ import { Scope as Scope$1 } from 'rian';
2
3
 
3
- declare type Span = {
4
+ declare type MeasureFn = ((...args: [...args: any[]]) => any) | ((...args: [...args: any[], scope: Scope$1]) => any);
5
+ declare type RealMeasureFnParams<T extends unknown[]> = T extends [] ? [] : T extends [...rest: infer U, scope: Scope$1] ? U : T;
6
+
7
+ /**
8
+ * Spans are units within a distributed trace. Spans encapsulate mainly 3 pieces of information, a
9
+ * {@link Span.name|name}, and a {@link Span.start|start} and {@link Span.end|end} time.
10
+ *
11
+ * Each span should be named, not too vague, and not too precise. For example, "resolve_user_ids"
12
+ * and not "resolver_user_ids[1,2,3]" nor "resolve".
13
+ *
14
+ * A span forms part of a wider trace, and can be visualized like:
15
+ *
16
+ * ```plain
17
+ * [Span A················································(2ms)]
18
+ * [Span B·········································(1.7ms)]
19
+ * [Span D···············(0.8ms)] [Span C......(0.6ms)]
20
+ * ```
21
+ *
22
+ * ---
23
+ *
24
+ * Spans are aimed to interoperate with
25
+ * {@link https://github.com/opentracing/specification/blob/master/specification.md|OpenTracing's Spans}, albeit not entirely api compatible — they do share principles.
26
+ */
27
+ interface Span {
28
+ /**
29
+ * A human-readable name for this span. For example the function name, the name of a subtask,
30
+ * or stage of the larger stack.
31
+ *
32
+ * @example
33
+ * "resolve_user_ids"
34
+ * "[POST] /api"
35
+ */
4
36
  name: string;
37
+ /**
38
+ * A w3c trace context compatible id for this span. Will .toString() into an injectable header.
39
+ *
40
+ * @see https://www.w3.org/TR/trace-context/#traceparent-header
41
+ * @see https://github.com/maraisr/tctx
42
+ */
5
43
  id: Traceparent;
44
+ /**
45
+ * Is the id of rhe parent if this is not the parent {@link Span}.
46
+ *
47
+ * @see {@link Span.id}
48
+ */
6
49
  parent?: Traceparent;
50
+ /**
51
+ * The time represented as a UNIX epoch timestamp in milliseconds when this span was created.
52
+ * Typically, via
53
+ * {@link Scope.fork|tracer.fork()}.
54
+ */
7
55
  start: number;
8
- end: number;
9
- attributes: Attributes;
10
- };
11
- declare type Collector = (spans: ReadonlySet<Span>) => any;
12
- declare type Attributes = {
13
- [property: string]: string | number | boolean | undefined | Error;
14
- };
15
- declare type Options = {
16
- collector: Collector;
17
- traceparent?: Traceparent;
18
- };
19
- declare type OmitScopeParam<T extends unknown[]> = T extends [] ? [] : T extends [infer H, ...infer R] ? H extends Scope ? OmitScopeParam<R> : [H, ...OmitScopeParam<R>] : T;
20
- interface CallableScope extends Scope {
21
- (cb: (scope: Omit<Scope, 'end'>) => void): ReturnType<typeof cb>;
56
+ /**
57
+ * The UNIX epoch timestamp in milliseconds when the span ended, or undefined if ending was not
58
+ * captured during the current trace. Time should then be assumed as current time.
59
+ */
60
+ end?: number;
61
+ /**
62
+ * An arbitrary context object useful for storing information during a trace.
63
+ *
64
+ * Usually following a convention such as `tag.*`, `http.*` or any of the
65
+ * {@link https://github.com/opentracing/specification/blob/master/semantic_conventions.md|Semantic Conventions outlined by OpenTracing}.
66
+ *
67
+ * ### Note!
68
+ *
69
+ * There are a few keys with "powers"
70
+ *
71
+ * - `kind` when set will coerce into the exports scheme, aka INTERNAL in zipkin will be
72
+ * `"INTERNAL"`, or `1` in otel
73
+ * - `error` when set, will be assumed to be an `Error` instance, and thus its `.message` wil
74
+ * exist as `error.message` in zipkin, and `status: 2` in otel.
75
+ */
76
+ context: Context;
77
+ /**
78
+ * Events are user-defined timestamped annotations of "events" that happened during the
79
+ * lifetime of a span. Consisting of a textual message, and optional attributes.
80
+ *
81
+ * As a rule-of-thumb use events to attach verbose information about a span, than an entirely
82
+ * new span.
83
+ */
84
+ events: {
85
+ name: string;
86
+ timestamp: number;
87
+ attributes: Context;
88
+ }[];
22
89
  }
23
90
  interface Scope {
91
+ /**
92
+ * A W3C traceparent. One can .toString() this if you want to cross a network.
93
+ */
24
94
  traceparent: Traceparent;
25
- fork(name: string, traceparent?: Traceparent): CallableScope;
26
- measure<Fn extends (...args: any[]) => any, Params extends Parameters<Fn>>(name: string, fn: Fn, ...args: OmitScopeParam<Params>): ReturnType<Fn>;
27
- set_attributes(attributes: Attributes): void;
95
+ /**
96
+ * Forks the span into a new child span.
97
+ */
98
+ fork(name: string): CallableScope;
99
+ /**
100
+ * With a passed function — will start a span, and run the function, when the function finishes
101
+ * the span finishes.
102
+ *
103
+ * The measure method will return whatever the function is, so if it's a promise, it returns a
104
+ * promise and so on. Any error is caught and re thrown, and automatically tracked in the
105
+ * context under the `error` property.
106
+ *
107
+ * All promises are tracked, and awaited on a `tracer.end`.
108
+ *
109
+ * @example
110
+ *
111
+ * ```text
112
+ * const data = await scope.measure('name', get_data, 'user_id_123');
113
+ * ^ ^ ^ ^
114
+ * | | | |
115
+ * | | | the first argument to get_data
116
+ * | | function to be called
117
+ * | the name of the sub scope
118
+ * return value from get_data
119
+ * ```
120
+ */
121
+ measure<Fn extends MeasureFn>(name: string, fn: Fn, // TODO: fn doesnt see scope correctly
122
+ ...args: RealMeasureFnParams<Parameters<Fn>>): ReturnType<Fn>;
123
+ /**
124
+ * Allows the span's context to be set. Passing an object will be `Object.assign`ed into the
125
+ * current context.
126
+ *
127
+ * Passing a function will be available to return a new context.
128
+ */
129
+ set_context(contextFn: Context | ((context: Context) => Context)): void;
130
+ /**
131
+ * Adds a new event to the span. As a rule-of-thumb use events to attach verbose information
132
+ * about a span, than an entirely new span.
133
+ */
134
+ add_event(name: string, attributes?: Record<string, any>): void;
135
+ /**
136
+ * Ends the current span — setting its `end` timestamp. Not calling this, will have its `end`
137
+ * timestamp nulled out — when the tracer ends.
138
+ */
28
139
  end(): void;
29
140
  }
30
- interface ParentScope extends Scope {
31
- end(): ReturnType<Collector>;
141
+ interface Tracer extends Omit<Scope, 'end'> {
142
+ end(): ReturnType<Exporter>;
143
+ }
144
+ /**
145
+ * An exporter is a method called when the parent scope ends, gets given a Set of all spans traced
146
+ * during this execution.
147
+ */
148
+ declare type Exporter = (spans: ReadonlySet<Readonly<Span>>, context: Context) => any;
149
+ /**
150
+ * @borrows {@link Span.context}
151
+ */
152
+ interface Context {
153
+ [property: string]: any;
154
+ }
155
+ /**
156
+ * Should return true when you want to sample the span, this is ran before the span is traced — so
157
+ * decisions is made preemptively.
158
+ *
159
+ * The Span itself will still be included in the {@link Options.exporter|exporter}, and can be
160
+ * filtered out there.
161
+ *
162
+ * Sampling does impact the traceparent, for injection — and is encoded there.
163
+ */
164
+ declare type Sampler = (name: string, parentId?: Traceparent, context?: Context) => boolean;
165
+ interface Options {
166
+ /**
167
+ * @borrows {@link Exporter}
168
+ */
169
+ exporter: Exporter;
170
+ /**
171
+ * @borrows {@link Sampler}
172
+ */
173
+ sampler?: Sampler;
174
+ context?: Context;
175
+ /**
176
+ * A root, or extracted w3c traceparent stringed header.
177
+ *
178
+ * If the id is malformed, the {@link create} method will throw an exception. If no root is
179
+ * provided then one will be created obeying the {@link Options.sampler|sampling} rules.
180
+ */
181
+ traceparent?: string;
182
+ }
183
+ interface CallableScope extends Scope {
184
+ (cb: (scope: Omit<Scope, 'end'>) => void): ReturnType<typeof cb>;
32
185
  }
33
- declare const create: (name: string, options: Options) => ParentScope;
186
+ declare const create: (name: string, options: Options) => Tracer;
34
187
 
35
- export { Attributes, Collector, Options, Scope, Span, create };
188
+ export { Context, Exporter, Options, Sampler, Scope, Span, Tracer, create };
package/index.js CHANGED
@@ -1,75 +1,128 @@
1
1
  'use strict';
2
2
 
3
- Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.create = void 0;
5
- const tracecontext_1 = require("rian/tracecontext");
6
- const measure = (cb, scope, promises) => {
7
- const set_error = (error) => {
8
- scope.set_attributes({
9
- error,
10
- });
11
- };
3
+ const package_json = require('rian/package.json');
4
+ const tctx = require('tctx');
5
+
6
+ function _interopNamespace(e) {
7
+ if (e && e.__esModule) return e;
8
+ const n = Object.create(null);
9
+ if (e) {
10
+ for (const k in e) {
11
+ if (k !== 'default') {
12
+ const d = Object.getOwnPropertyDescriptor(e, k);
13
+ Object.defineProperty(n, k, d.get ? d : {
14
+ enumerable: true,
15
+ get: function () { return e[k]; }
16
+ });
17
+ }
18
+ }
19
+ }
20
+ n["default"] = e;
21
+ return n;
22
+ }
23
+
24
+ const tctx__namespace = /*#__PURE__*/_interopNamespace(tctx);
25
+
26
+ const set_error = (scope, error) => {
27
+ scope.set_context({
28
+ error,
29
+ });
30
+ };
31
+ const measure = (fn, // TODO: fn doesnt see scope correctly
32
+ scope, promises, ...args) => {
12
33
  try {
13
- const r = cb();
14
- if (r instanceof Promise)
15
- promises.push(r.catch(set_error).finally(() => scope.end()));
34
+ var r = fn(...args, scope), is_promise = r instanceof Promise;
35
+ if (is_promise)
36
+ promises.push(r
37
+ .catch((e) => void set_error(scope, e))
38
+ .finally(() => scope.end()));
16
39
  return r;
17
40
  }
18
41
  catch (e) {
19
- set_error(e);
42
+ set_error(scope, e);
20
43
  throw e;
21
44
  }
22
45
  finally {
23
- scope.end();
46
+ if (is_promise !== true)
47
+ scope.end();
24
48
  }
25
49
  };
50
+
51
+ // ==> impl
52
+ /**
53
+ * The default sampler;
54
+ *
55
+ * If no parent
56
+ * ~> sample
57
+ * if parent was off
58
+ * ~> never sample
59
+ * if parent was on
60
+ * ~> always sample
61
+ */
62
+ const defaultSampler = (_name, parentId) => {
63
+ if (!parentId)
64
+ return true;
65
+ return tctx__namespace.is_sampled(parentId);
66
+ };
67
+ const sdk_object = {
68
+ 'telemetry.sdk.name': package_json.name,
69
+ 'telemetry.sdk.version': package_json.version,
70
+ };
26
71
  const create = (name, options) => {
27
72
  const spans = new Set();
28
73
  const promises = [];
29
- const scope = (name, parent) => {
30
- const me = parent ? parent.child() : (0, tracecontext_1.make_traceparent)();
31
- const attributes = {};
32
- const start = performance.now();
33
- let ended = false;
34
- const $ = {
35
- get traceparent() {
36
- return me;
37
- },
38
- fork(name) {
39
- return scope(name, me);
40
- },
41
- measure(name, cb, ...args) {
42
- const scope = this.fork(name);
43
- return measure(() => cb(...args, scope), scope, promises);
44
- },
45
- set_attributes(attr) {
46
- Object.assign(attributes, attr);
47
- },
48
- end() {
49
- if (ended)
50
- return void 0;
51
- spans.add({
52
- id: me,
53
- parent,
54
- start,
55
- end: performance.now(),
56
- name,
57
- attributes,
58
- });
59
- ended = true;
60
- },
74
+ const sampler = options.sampler || defaultSampler;
75
+ const span = (name, parent) => {
76
+ const should_sample = sampler(name, parent, options.context);
77
+ const id = parent
78
+ ? parent.child(should_sample)
79
+ : tctx__namespace.make(should_sample);
80
+ const span_obj = {
81
+ id,
82
+ parent,
83
+ start: Date.now(),
84
+ name,
85
+ events: [],
86
+ context: {},
87
+ };
88
+ if (should_sample)
89
+ spans.add(span_obj);
90
+ const $ = (cb) => measure(cb, $, promises);
91
+ $.traceparent = id;
92
+ $.fork = (name) => span(name, id);
93
+ $.measure = (name, cb, ...args) => measure(cb, span(name, id), promises, ...args);
94
+ $.set_context = (ctx) => {
95
+ if (typeof ctx === 'function')
96
+ return void (span_obj.context = ctx(span_obj.context));
97
+ Object.assign(span_obj.context, ctx);
98
+ };
99
+ $.add_event = (name, attributes) => {
100
+ span_obj.events.push({
101
+ name,
102
+ timestamp: Date.now(),
103
+ attributes: attributes || {},
104
+ });
61
105
  };
62
- return Object.setPrototypeOf((cb) => measure(cb, $, promises), $);
106
+ $.end = () => {
107
+ if (span_obj.end)
108
+ return void 0;
109
+ span_obj.end = Date.now();
110
+ };
111
+ return $;
63
112
  };
64
- const me = scope(name, typeof options.traceparent === 'string'
65
- ? (0, tracecontext_1.parse_traceparent)(options.traceparent)
66
- : options.traceparent);
67
- const meEnd = me.end.bind(me);
68
- me.end = async () => {
113
+ const root = span(name, typeof options.traceparent === 'string'
114
+ ? tctx__namespace.parse(options.traceparent)
115
+ : undefined);
116
+ const endRoot = root.end.bind(root);
117
+ root.end = async () => {
118
+ endRoot();
69
119
  await Promise.all(promises);
70
- meEnd();
71
- return options.collector(spans);
120
+ return options.exporter(spans, {
121
+ ...(options.context || {}),
122
+ ...sdk_object,
123
+ });
72
124
  };
73
- return me;
125
+ return root;
74
126
  };
127
+
75
128
  exports.create = create;
package/index.mjs CHANGED
@@ -1,73 +1,106 @@
1
- Object.defineProperty(exports, "__esModule", { value: true });
2
- exports.create = void 0;
3
- const tracecontext_1 = require("rian/tracecontext");
4
- const measure = (cb, scope, promises) => {
5
- const set_error = (error) => {
6
- scope.set_attributes({
7
- error,
8
- });
9
- };
1
+ import { name, version } from 'rian/package.json';
2
+ import * as tctx from 'tctx';
3
+
4
+ const set_error = (scope, error) => {
5
+ scope.set_context({
6
+ error,
7
+ });
8
+ };
9
+ const measure = (fn, // TODO: fn doesnt see scope correctly
10
+ scope, promises, ...args) => {
10
11
  try {
11
- const r = cb();
12
- if (r instanceof Promise)
13
- promises.push(r.catch(set_error).finally(() => scope.end()));
12
+ var r = fn(...args, scope), is_promise = r instanceof Promise;
13
+ if (is_promise)
14
+ promises.push(r
15
+ .catch((e) => void set_error(scope, e))
16
+ .finally(() => scope.end()));
14
17
  return r;
15
18
  }
16
19
  catch (e) {
17
- set_error(e);
20
+ set_error(scope, e);
18
21
  throw e;
19
22
  }
20
23
  finally {
21
- scope.end();
24
+ if (is_promise !== true)
25
+ scope.end();
22
26
  }
23
27
  };
28
+
29
+ // ==> impl
30
+ /**
31
+ * The default sampler;
32
+ *
33
+ * If no parent
34
+ * ~> sample
35
+ * if parent was off
36
+ * ~> never sample
37
+ * if parent was on
38
+ * ~> always sample
39
+ */
40
+ const defaultSampler = (_name, parentId) => {
41
+ if (!parentId)
42
+ return true;
43
+ return tctx.is_sampled(parentId);
44
+ };
45
+ const sdk_object = {
46
+ 'telemetry.sdk.name': name,
47
+ 'telemetry.sdk.version': version,
48
+ };
24
49
  const create = (name, options) => {
25
50
  const spans = new Set();
26
51
  const promises = [];
27
- const scope = (name, parent) => {
28
- const me = parent ? parent.child() : (0, tracecontext_1.make_traceparent)();
29
- const attributes = {};
30
- const start = performance.now();
31
- let ended = false;
32
- const $ = {
33
- get traceparent() {
34
- return me;
35
- },
36
- fork(name) {
37
- return scope(name, me);
38
- },
39
- measure(name, cb, ...args) {
40
- const scope = this.fork(name);
41
- return measure(() => cb(...args, scope), scope, promises);
42
- },
43
- set_attributes(attr) {
44
- Object.assign(attributes, attr);
45
- },
46
- end() {
47
- if (ended)
48
- return void 0;
49
- spans.add({
50
- id: me,
51
- parent,
52
- start,
53
- end: performance.now(),
54
- name,
55
- attributes,
56
- });
57
- ended = true;
58
- },
52
+ const sampler = options.sampler || defaultSampler;
53
+ const span = (name, parent) => {
54
+ const should_sample = sampler(name, parent, options.context);
55
+ const id = parent
56
+ ? parent.child(should_sample)
57
+ : tctx.make(should_sample);
58
+ const span_obj = {
59
+ id,
60
+ parent,
61
+ start: Date.now(),
62
+ name,
63
+ events: [],
64
+ context: {},
59
65
  };
60
- return Object.setPrototypeOf((cb) => measure(cb, $, promises), $);
66
+ if (should_sample)
67
+ spans.add(span_obj);
68
+ const $ = (cb) => measure(cb, $, promises);
69
+ $.traceparent = id;
70
+ $.fork = (name) => span(name, id);
71
+ $.measure = (name, cb, ...args) => measure(cb, span(name, id), promises, ...args);
72
+ $.set_context = (ctx) => {
73
+ if (typeof ctx === 'function')
74
+ return void (span_obj.context = ctx(span_obj.context));
75
+ Object.assign(span_obj.context, ctx);
76
+ };
77
+ $.add_event = (name, attributes) => {
78
+ span_obj.events.push({
79
+ name,
80
+ timestamp: Date.now(),
81
+ attributes: attributes || {},
82
+ });
83
+ };
84
+ $.end = () => {
85
+ if (span_obj.end)
86
+ return void 0;
87
+ span_obj.end = Date.now();
88
+ };
89
+ return $;
61
90
  };
62
- const me = scope(name, typeof options.traceparent === 'string'
63
- ? (0, tracecontext_1.parse_traceparent)(options.traceparent)
64
- : options.traceparent);
65
- const meEnd = me.end.bind(me);
66
- me.end = async () => {
91
+ const root = span(name, typeof options.traceparent === 'string'
92
+ ? tctx.parse(options.traceparent)
93
+ : undefined);
94
+ const endRoot = root.end.bind(root);
95
+ root.end = async () => {
96
+ endRoot();
67
97
  await Promise.all(promises);
68
- meEnd();
69
- return options.collector(spans);
98
+ return options.exporter(spans, {
99
+ ...(options.context || {}),
100
+ ...sdk_object,
101
+ });
70
102
  };
71
- return me;
103
+ return root;
72
104
  };
73
- exports.create = create;
105
+
106
+ export { create };
package/package.json CHANGED
@@ -1,9 +1,19 @@
1
1
  {
2
2
  "name": "rian",
3
- "version": "0.0.2",
4
- "description": "A tracer for the edge",
3
+ "version": "0.0.3",
4
+ "description": "Effective tracing for the edge and origins",
5
5
  "keywords": [
6
- "TODO"
6
+ "opentelemetry",
7
+ "trace-context",
8
+ "opentracing",
9
+ "traceparent",
10
+ "measure",
11
+ "monitor",
12
+ "observe",
13
+ "tracing",
14
+ "zipkin",
15
+ "tracer",
16
+ "trace"
7
17
  ],
8
18
  "repository": "maraisr/rian",
9
19
  "license": "MIT",
@@ -19,9 +29,13 @@
19
29
  "import": "./index.mjs",
20
30
  "require": "./index.js"
21
31
  },
22
- "./tracecontext": {
23
- "import": "./tracecontext.mjs",
24
- "require": "./tracecontext.js"
32
+ "./exporter.otel.http": {
33
+ "import": "./exporter.otel.http.mjs",
34
+ "require": "./exporter.otel.http.js"
35
+ },
36
+ "./exporter.zipkin": {
37
+ "import": "./exporter.zipkin.mjs",
38
+ "require": "./exporter.zipkin.js"
25
39
  },
26
40
  "./package.json": "./package.json"
27
41
  },
@@ -34,6 +48,7 @@
34
48
  "*.d.ts"
35
49
  ],
36
50
  "dependencies": {
37
- "tctx": "^0.0.6"
51
+ "flattie": "^1.1.0",
52
+ "tctx": "^0.0.10"
38
53
  }
39
54
  }
package/tracecontext.d.ts DELETED
@@ -1 +0,0 @@
1
- export { Traceparent, make as make_traceparent, parse as parse_traceparent } from 'tctx';
package/tracecontext.js DELETED
@@ -1,7 +0,0 @@
1
- 'use strict';
2
-
3
- Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.parse_traceparent = exports.make_traceparent = void 0;
5
- var tctx_1 = require("tctx");
6
- Object.defineProperty(exports, "make_traceparent", { enumerable: true, get: function () { return tctx_1.make; } });
7
- Object.defineProperty(exports, "parse_traceparent", { enumerable: true, get: function () { return tctx_1.parse; } });
package/tracecontext.mjs DELETED
@@ -1,5 +0,0 @@
1
- Object.defineProperty(exports, "__esModule", { value: true });
2
- exports.parse_traceparent = exports.make_traceparent = void 0;
3
- var tctx_1 = require("tctx");
4
- Object.defineProperty(exports, "make_traceparent", { enumerable: true, get: function () { return tctx_1.make; } });
5
- Object.defineProperty(exports, "parse_traceparent", { enumerable: true, get: function () { return tctx_1.parse; } });