rian 0.0.2-alpha.7 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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]: any;
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 Tracer 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
186
  declare const create: (name: string, options: Options) => Tracer;
34
187
 
35
- export { Attributes, Collector, Options, Scope, Span, Tracer, 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
- const tracecontext = require('rian/tracecontext');
3
+ const package_json = require('rian/package.json');
4
+ const tctx = require('tctx');
4
5
 
5
- const measure = (cb, scope, promises) => {
6
- const set_error = (error) => {
7
- scope.set_attributes({
8
- error,
9
- });
10
- };
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) => {
11
33
  try {
12
- const r = cb();
13
- if (r instanceof Promise)
14
- 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()));
15
39
  return r;
16
40
  }
17
41
  catch (e) {
18
- set_error(e);
42
+ set_error(scope, e);
19
43
  throw e;
20
44
  }
21
45
  finally {
22
- scope.end();
46
+ if (is_promise !== true)
47
+ scope.end();
23
48
  }
24
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
+ };
25
71
  const create = (name, options) => {
26
72
  const spans = new Set();
27
73
  const promises = [];
28
- const scope = (name, parent) => {
29
- const me = parent ? parent.child() : tracecontext.make_traceparent();
30
- const attributes = {};
31
- const start = Date.now();
32
- let ended = false;
33
- const $ = {
34
- get traceparent() {
35
- return me;
36
- },
37
- fork(name) {
38
- return scope(name, me);
39
- },
40
- measure(name, cb, ...args) {
41
- const scope = this.fork(name);
42
- return measure(() => cb(...args, scope), scope, promises);
43
- },
44
- set_attributes(attr) {
45
- Object.assign(attributes, attr);
46
- },
47
- end() {
48
- if (ended)
49
- return void 0;
50
- spans.add({
51
- id: me,
52
- parent,
53
- start,
54
- end: Date.now(),
55
- name,
56
- attributes,
57
- });
58
- ended = true;
59
- },
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);
60
98
  };
61
- return Object.setPrototypeOf((cb) => measure(cb, $, promises), $);
99
+ $.add_event = (name, attributes) => {
100
+ span_obj.events.push({
101
+ name,
102
+ timestamp: Date.now(),
103
+ attributes: attributes || {},
104
+ });
105
+ };
106
+ $.end = () => {
107
+ if (span_obj.end)
108
+ return void 0;
109
+ span_obj.end = Date.now();
110
+ };
111
+ return $;
62
112
  };
63
- const me = scope(name, typeof options.traceparent === 'string'
64
- ? tracecontext.parse_traceparent(options.traceparent)
65
- : options.traceparent);
66
- const meEnd = me.end.bind(me);
67
- 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();
68
119
  await Promise.all(promises);
69
- meEnd();
70
- return options.collector(spans);
120
+ return options.exporter(spans, {
121
+ ...(options.context || {}),
122
+ ...sdk_object,
123
+ });
71
124
  };
72
- return me;
125
+ return root;
73
126
  };
74
127
 
75
128
  exports.create = create;
package/index.mjs CHANGED
@@ -1,73 +1,106 @@
1
- import { parse_traceparent, make_traceparent } from 'rian/tracecontext';
1
+ import { name, version } from 'rian/package.json';
2
+ import * as tctx from 'tctx';
2
3
 
3
- const measure = (cb, scope, promises) => {
4
- const set_error = (error) => {
5
- scope.set_attributes({
6
- error,
7
- });
8
- };
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) => {
9
11
  try {
10
- const r = cb();
11
- if (r instanceof Promise)
12
- 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()));
13
17
  return r;
14
18
  }
15
19
  catch (e) {
16
- set_error(e);
20
+ set_error(scope, e);
17
21
  throw e;
18
22
  }
19
23
  finally {
20
- scope.end();
24
+ if (is_promise !== true)
25
+ scope.end();
21
26
  }
22
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
+ };
23
49
  const create = (name, options) => {
24
50
  const spans = new Set();
25
51
  const promises = [];
26
- const scope = (name, parent) => {
27
- const me = parent ? parent.child() : make_traceparent();
28
- const attributes = {};
29
- const start = Date.now();
30
- let ended = false;
31
- const $ = {
32
- get traceparent() {
33
- return me;
34
- },
35
- fork(name) {
36
- return scope(name, me);
37
- },
38
- measure(name, cb, ...args) {
39
- const scope = this.fork(name);
40
- return measure(() => cb(...args, scope), scope, promises);
41
- },
42
- set_attributes(attr) {
43
- Object.assign(attributes, attr);
44
- },
45
- end() {
46
- if (ended)
47
- return void 0;
48
- spans.add({
49
- id: me,
50
- parent,
51
- start,
52
- end: Date.now(),
53
- name,
54
- attributes,
55
- });
56
- ended = true;
57
- },
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: {},
65
+ };
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
+ });
58
83
  };
59
- return Object.setPrototypeOf((cb) => measure(cb, $, promises), $);
84
+ $.end = () => {
85
+ if (span_obj.end)
86
+ return void 0;
87
+ span_obj.end = Date.now();
88
+ };
89
+ return $;
60
90
  };
61
- const me = scope(name, typeof options.traceparent === 'string'
62
- ? parse_traceparent(options.traceparent)
63
- : options.traceparent);
64
- const meEnd = me.end.bind(me);
65
- 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();
66
97
  await Promise.all(promises);
67
- meEnd();
68
- return options.collector(spans);
98
+ return options.exporter(spans, {
99
+ ...(options.context || {}),
100
+ ...sdk_object,
101
+ });
69
102
  };
70
- return me;
103
+ return root;
71
104
  };
72
105
 
73
106
  export { create };
package/package.json CHANGED
@@ -1,9 +1,19 @@
1
1
  {
2
2
  "name": "rian",
3
- "version": "0.0.2-alpha.7",
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.9"
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,14 +0,0 @@
1
- 'use strict';
2
-
3
- const tctx = require('tctx');
4
-
5
-
6
-
7
- Object.defineProperty(exports, 'make_traceparent', {
8
- enumerable: true,
9
- get: function () { return tctx.make; }
10
- });
11
- Object.defineProperty(exports, 'parse_traceparent', {
12
- enumerable: true,
13
- get: function () { return tctx.parse; }
14
- });
package/tracecontext.mjs DELETED
@@ -1 +0,0 @@
1
- export { make as make_traceparent, parse as parse_traceparent } from 'tctx';