rian 0.0.2-alpha.10 → 0.0.2-alpha.11
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.
- package/exporter.otel.http.d.ts +8 -0
- package/exporter.otel.http.js +108 -0
- package/exporter.otel.http.mjs +106 -0
- package/exporter.zipkin.d.ts +8 -0
- package/exporter.zipkin.js +38 -0
- package/exporter.zipkin.mjs +36 -0
- package/index.d.ts +121 -16
- package/index.js +72 -46
- package/index.mjs +72 -46
- package/package.json +10 -1
@@ -0,0 +1,108 @@
|
|
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 = (config) => (spans, context) => {
|
58
|
+
const otel_spans = [];
|
59
|
+
for (let span of spans) {
|
60
|
+
const kind = span.context.kind;
|
61
|
+
delete span.context.kind;
|
62
|
+
let status;
|
63
|
+
if ('error' in span.context) {
|
64
|
+
status = {
|
65
|
+
code: SpanStatusCode_ERROR,
|
66
|
+
};
|
67
|
+
if ('message' in span.context.error) {
|
68
|
+
status.message = span.context.error.message;
|
69
|
+
}
|
70
|
+
delete span.context.error;
|
71
|
+
}
|
72
|
+
otel_spans.push({
|
73
|
+
traceId: span.id.trace_id,
|
74
|
+
spanId: span.id.parent_id,
|
75
|
+
parentSpanId: span.parent?.parent_id,
|
76
|
+
name: span.name,
|
77
|
+
kind: map_kind(kind || 'INTERNAL'),
|
78
|
+
startTimeUnixNano: span.start * 1000000,
|
79
|
+
endTimeUnixNano: span.end ? span.end * 1000000 : undefined,
|
80
|
+
droppedAttributesCount: 0,
|
81
|
+
droppedEventsCount: 0,
|
82
|
+
droppedLinksCount: 0,
|
83
|
+
attributes: convert_object_to_kv(span.context),
|
84
|
+
status: status || { code: SpanStatusCode_UNSET },
|
85
|
+
});
|
86
|
+
}
|
87
|
+
return config.onRequest({
|
88
|
+
resourceSpans: [
|
89
|
+
{
|
90
|
+
resource: {
|
91
|
+
attributes: convert_object_to_kv(context),
|
92
|
+
droppedAttributesCount: 0,
|
93
|
+
},
|
94
|
+
instrumentationLibrarySpans: [
|
95
|
+
{
|
96
|
+
instrumentationLibrary: {
|
97
|
+
name: package_json.name,
|
98
|
+
version: package_json.version,
|
99
|
+
},
|
100
|
+
spans: otel_spans,
|
101
|
+
},
|
102
|
+
],
|
103
|
+
},
|
104
|
+
],
|
105
|
+
});
|
106
|
+
};
|
107
|
+
|
108
|
+
exports.exporter = exporter;
|
@@ -0,0 +1,106 @@
|
|
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 = (config) => (spans, context) => {
|
56
|
+
const otel_spans = [];
|
57
|
+
for (let span of spans) {
|
58
|
+
const kind = span.context.kind;
|
59
|
+
delete span.context.kind;
|
60
|
+
let status;
|
61
|
+
if ('error' in span.context) {
|
62
|
+
status = {
|
63
|
+
code: SpanStatusCode_ERROR,
|
64
|
+
};
|
65
|
+
if ('message' in span.context.error) {
|
66
|
+
status.message = span.context.error.message;
|
67
|
+
}
|
68
|
+
delete span.context.error;
|
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.context),
|
82
|
+
status: status || { code: SpanStatusCode_UNSET },
|
83
|
+
});
|
84
|
+
}
|
85
|
+
return config.onRequest({
|
86
|
+
resourceSpans: [
|
87
|
+
{
|
88
|
+
resource: {
|
89
|
+
attributes: convert_object_to_kv(context),
|
90
|
+
droppedAttributesCount: 0,
|
91
|
+
},
|
92
|
+
instrumentationLibrarySpans: [
|
93
|
+
{
|
94
|
+
instrumentationLibrary: {
|
95
|
+
name: name,
|
96
|
+
version: version,
|
97
|
+
},
|
98
|
+
spans: otel_spans,
|
99
|
+
},
|
100
|
+
],
|
101
|
+
},
|
102
|
+
],
|
103
|
+
});
|
104
|
+
};
|
105
|
+
|
106
|
+
export { exporter };
|
@@ -0,0 +1,38 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
const flattie = require('flattie');
|
4
|
+
|
5
|
+
const exporter = (config) => (spans, context) => {
|
6
|
+
const zipkin = [];
|
7
|
+
for (let span of spans) {
|
8
|
+
const kind = span.context.kind;
|
9
|
+
delete span.context.kind;
|
10
|
+
if ('error' in span.context) {
|
11
|
+
const error = span.context.error;
|
12
|
+
if ('message' in error) {
|
13
|
+
span.context.error = {
|
14
|
+
name: error.name,
|
15
|
+
message: error.message,
|
16
|
+
stack: error.stack,
|
17
|
+
};
|
18
|
+
}
|
19
|
+
else {
|
20
|
+
span.context.error = true;
|
21
|
+
}
|
22
|
+
}
|
23
|
+
zipkin.push({
|
24
|
+
id: span.id.parent_id,
|
25
|
+
traceId: span.id.trace_id,
|
26
|
+
parentId: span.parent?.parent_id,
|
27
|
+
name: span.name,
|
28
|
+
kind: kind === 'INTERNAL' ? undefined : kind,
|
29
|
+
timestamp: span.start * 1000,
|
30
|
+
duration: span.end ? (span.end - span.start) * 1000 : undefined,
|
31
|
+
localEndpoint: context.localEndpoint,
|
32
|
+
tags: flattie.flattie(Object.assign({}, context, span.context), '.', true),
|
33
|
+
});
|
34
|
+
}
|
35
|
+
return config.onRequest(zipkin);
|
36
|
+
};
|
37
|
+
|
38
|
+
exports.exporter = exporter;
|
@@ -0,0 +1,36 @@
|
|
1
|
+
import { flattie } from 'flattie';
|
2
|
+
|
3
|
+
const exporter = (config) => (spans, context) => {
|
4
|
+
const zipkin = [];
|
5
|
+
for (let span of spans) {
|
6
|
+
const kind = span.context.kind;
|
7
|
+
delete span.context.kind;
|
8
|
+
if ('error' in span.context) {
|
9
|
+
const error = span.context.error;
|
10
|
+
if ('message' in error) {
|
11
|
+
span.context.error = {
|
12
|
+
name: error.name,
|
13
|
+
message: error.message,
|
14
|
+
stack: error.stack,
|
15
|
+
};
|
16
|
+
}
|
17
|
+
else {
|
18
|
+
span.context.error = true;
|
19
|
+
}
|
20
|
+
}
|
21
|
+
zipkin.push({
|
22
|
+
id: span.id.parent_id,
|
23
|
+
traceId: span.id.trace_id,
|
24
|
+
parentId: span.parent?.parent_id,
|
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(Object.assign({}, context, span.context), '.', true),
|
31
|
+
});
|
32
|
+
}
|
33
|
+
return config.onRequest(zipkin);
|
34
|
+
};
|
35
|
+
|
36
|
+
export { exporter };
|
package/index.d.ts
CHANGED
@@ -1,37 +1,142 @@
|
|
1
1
|
import { Traceparent } from 'tctx';
|
2
|
+
import { Scope as Scope$1 } from 'rian';
|
2
3
|
|
3
|
-
declare type
|
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
|
-
|
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
|
+
*/
|
9
76
|
context: Context;
|
10
|
-
}
|
11
|
-
|
12
|
-
|
77
|
+
}
|
78
|
+
/**
|
79
|
+
* An exporter is a method called when the parent scope ends, gets given a Set of all spans traced
|
80
|
+
* during this execution.
|
81
|
+
*/
|
82
|
+
declare type Exporter = (spans: ReadonlySet<Span>, context: Context) => any;
|
83
|
+
/**
|
84
|
+
* @borrows {@link Span.context}
|
85
|
+
*/
|
86
|
+
interface Context {
|
13
87
|
[property: string]: any;
|
14
|
-
}
|
15
|
-
|
16
|
-
|
88
|
+
}
|
89
|
+
/**
|
90
|
+
* Should return true when you want to sample the span, this is ran before the span is traced — so
|
91
|
+
* decisions is made preemptively.
|
92
|
+
*
|
93
|
+
* The Span itself will still be included in the {@link Options.exporter|exporter}, and can be
|
94
|
+
* filtered out there.
|
95
|
+
*
|
96
|
+
* Sampling does impact the traceparent, for injection — and is encoded there.
|
97
|
+
*/
|
98
|
+
declare type Sampler = (name: string, parentId?: Traceparent, context?: Context) => boolean;
|
99
|
+
interface Options {
|
100
|
+
/**
|
101
|
+
* @borrows {@link Exporter}
|
102
|
+
*/
|
103
|
+
exporter: Exporter;
|
104
|
+
/**
|
105
|
+
* @borrows {@link Sampler}
|
106
|
+
*/
|
107
|
+
sampler?: Sampler;
|
108
|
+
context?: Context;
|
109
|
+
/**
|
110
|
+
* A root, or extracted w3c traceparent stringed header.
|
111
|
+
*
|
112
|
+
* If the id is malformed, the {@link create} method will throw an exception. If no root is
|
113
|
+
* provided then one will be created obeying the {@link Options.sampler|sampling} rules.
|
114
|
+
*/
|
17
115
|
traceparent?: string;
|
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;
|
116
|
+
}
|
20
117
|
interface CallableScope extends Scope {
|
21
118
|
(cb: (scope: Omit<Scope, 'end'>) => void): ReturnType<typeof cb>;
|
22
119
|
}
|
23
120
|
interface Scope {
|
24
121
|
traceparent: Traceparent;
|
25
|
-
fork(name: string
|
26
|
-
measure<Fn extends (
|
27
|
-
...args:
|
122
|
+
fork(name: string): CallableScope;
|
123
|
+
measure<Fn extends MeasureFn>(name: string, fn: Fn, // TODO: fn doesnt see scope correctly
|
124
|
+
...args: RealMeasureFnParams<Parameters<Fn>>): ReturnType<Fn>;
|
28
125
|
set_context(contextFn: (context: Context) => Context): void;
|
29
126
|
set_context(context: Context): void;
|
30
127
|
end(): void;
|
31
128
|
}
|
32
|
-
interface Tracer
|
33
|
-
|
129
|
+
interface Tracer {
|
130
|
+
/**
|
131
|
+
* @borrows {@link Scope.fork}
|
132
|
+
*/
|
133
|
+
span: Scope['fork'];
|
134
|
+
/**
|
135
|
+
* @borrows {@link Scope.measure}
|
136
|
+
*/
|
137
|
+
measure: Scope['measure'];
|
138
|
+
end(): ReturnType<Exporter>;
|
34
139
|
}
|
35
140
|
declare const create: (name: string, options: Options) => Tracer;
|
36
141
|
|
37
|
-
export {
|
142
|
+
export { Context, Exporter, Options, Sampler, Scope, Span, Tracer, create };
|
package/index.js
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
'use strict';
|
2
2
|
|
3
|
+
const package_json = require('rian/package.json');
|
3
4
|
const tctx = require('tctx');
|
4
5
|
|
5
6
|
function _interopNamespace(e) {
|
@@ -22,72 +23,97 @@ function _interopNamespace(e) {
|
|
22
23
|
|
23
24
|
const tctx__namespace = /*#__PURE__*/_interopNamespace(tctx);
|
24
25
|
|
25
|
-
const
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
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) => {
|
33
|
+
try {
|
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()));
|
39
|
+
return r;
|
40
|
+
}
|
41
|
+
catch (e) {
|
42
|
+
set_error(scope, e);
|
43
|
+
throw e;
|
44
|
+
}
|
45
|
+
finally {
|
46
|
+
if (is_promise !== true)
|
47
|
+
scope.end();
|
48
|
+
}
|
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,
|
47
70
|
};
|
48
71
|
const create = (name, options) => {
|
49
72
|
const spans = new Set();
|
50
73
|
const promises = [];
|
51
74
|
const span = (name, parent) => {
|
52
|
-
const
|
53
|
-
|
75
|
+
const should_sample = (options.sampler || defaultSampler)(name, parent, options.context);
|
76
|
+
const id = parent
|
77
|
+
? parent.child(should_sample)
|
78
|
+
: tctx__namespace.make(should_sample);
|
54
79
|
const start = Date.now();
|
55
|
-
|
56
|
-
|
57
|
-
|
80
|
+
const span_obj = {
|
81
|
+
id,
|
82
|
+
parent,
|
83
|
+
start,
|
84
|
+
name,
|
85
|
+
context: {},
|
86
|
+
};
|
87
|
+
if (should_sample)
|
88
|
+
spans.add(span_obj);
|
89
|
+
const $ = (cb) => measure(cb, $, promises);
|
58
90
|
$.traceparent = id;
|
59
91
|
$.fork = (name) => span(name, id);
|
60
|
-
$.measure = (name, cb, ...args) => measure(cb, span(name, id), promises
|
92
|
+
$.measure = (name, cb, ...args) => measure(cb, span(name, id), promises, ...args);
|
61
93
|
$.set_context = (ctx) => {
|
62
94
|
if (typeof ctx === 'function')
|
63
|
-
return void (context = ctx(context));
|
64
|
-
Object.assign(context, ctx);
|
95
|
+
return void (span_obj.context = ctx(span_obj.context));
|
96
|
+
Object.assign(span_obj.context, ctx);
|
65
97
|
};
|
66
98
|
$.end = () => {
|
67
|
-
if (
|
99
|
+
if (span_obj.end)
|
68
100
|
return void 0;
|
69
|
-
|
70
|
-
id,
|
71
|
-
parent,
|
72
|
-
start,
|
73
|
-
end: Date.now(),
|
74
|
-
name,
|
75
|
-
context,
|
76
|
-
});
|
77
|
-
ended = true;
|
101
|
+
span_obj.end = Date.now();
|
78
102
|
};
|
79
103
|
return $;
|
80
104
|
};
|
81
105
|
const root = span(name, typeof options.traceparent === 'string'
|
82
106
|
? tctx__namespace.parse(options.traceparent)
|
83
107
|
: undefined);
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
108
|
+
return {
|
109
|
+
span: root.fork.bind(root),
|
110
|
+
measure: root.measure.bind(root),
|
111
|
+
async end() {
|
112
|
+
root.end();
|
113
|
+
await Promise.all(promises);
|
114
|
+
return options.exporter(spans, Object.assign(options.context || {}, sdk_object));
|
115
|
+
},
|
89
116
|
};
|
90
|
-
return root;
|
91
117
|
};
|
92
118
|
|
93
119
|
exports.create = create;
|
package/index.mjs
CHANGED
@@ -1,71 +1,97 @@
|
|
1
|
+
import { name, version } from 'rian/package.json';
|
1
2
|
import * as tctx from 'tctx';
|
2
3
|
|
3
|
-
const
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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) => {
|
11
|
+
try {
|
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()));
|
17
|
+
return r;
|
18
|
+
}
|
19
|
+
catch (e) {
|
20
|
+
set_error(scope, e);
|
21
|
+
throw e;
|
22
|
+
}
|
23
|
+
finally {
|
24
|
+
if (is_promise !== true)
|
25
|
+
scope.end();
|
26
|
+
}
|
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,
|
25
48
|
};
|
26
49
|
const create = (name, options) => {
|
27
50
|
const spans = new Set();
|
28
51
|
const promises = [];
|
29
52
|
const span = (name, parent) => {
|
30
|
-
const
|
31
|
-
|
53
|
+
const should_sample = (options.sampler || defaultSampler)(name, parent, options.context);
|
54
|
+
const id = parent
|
55
|
+
? parent.child(should_sample)
|
56
|
+
: tctx.make(should_sample);
|
32
57
|
const start = Date.now();
|
33
|
-
|
34
|
-
|
35
|
-
|
58
|
+
const span_obj = {
|
59
|
+
id,
|
60
|
+
parent,
|
61
|
+
start,
|
62
|
+
name,
|
63
|
+
context: {},
|
64
|
+
};
|
65
|
+
if (should_sample)
|
66
|
+
spans.add(span_obj);
|
67
|
+
const $ = (cb) => measure(cb, $, promises);
|
36
68
|
$.traceparent = id;
|
37
69
|
$.fork = (name) => span(name, id);
|
38
|
-
$.measure = (name, cb, ...args) => measure(cb, span(name, id), promises
|
70
|
+
$.measure = (name, cb, ...args) => measure(cb, span(name, id), promises, ...args);
|
39
71
|
$.set_context = (ctx) => {
|
40
72
|
if (typeof ctx === 'function')
|
41
|
-
return void (context = ctx(context));
|
42
|
-
Object.assign(context, ctx);
|
73
|
+
return void (span_obj.context = ctx(span_obj.context));
|
74
|
+
Object.assign(span_obj.context, ctx);
|
43
75
|
};
|
44
76
|
$.end = () => {
|
45
|
-
if (
|
77
|
+
if (span_obj.end)
|
46
78
|
return void 0;
|
47
|
-
|
48
|
-
id,
|
49
|
-
parent,
|
50
|
-
start,
|
51
|
-
end: Date.now(),
|
52
|
-
name,
|
53
|
-
context,
|
54
|
-
});
|
55
|
-
ended = true;
|
79
|
+
span_obj.end = Date.now();
|
56
80
|
};
|
57
81
|
return $;
|
58
82
|
};
|
59
83
|
const root = span(name, typeof options.traceparent === 'string'
|
60
84
|
? tctx.parse(options.traceparent)
|
61
85
|
: undefined);
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
86
|
+
return {
|
87
|
+
span: root.fork.bind(root),
|
88
|
+
measure: root.measure.bind(root),
|
89
|
+
async end() {
|
90
|
+
root.end();
|
91
|
+
await Promise.all(promises);
|
92
|
+
return options.exporter(spans, Object.assign(options.context || {}, sdk_object));
|
93
|
+
},
|
67
94
|
};
|
68
|
-
return root;
|
69
95
|
};
|
70
96
|
|
71
97
|
export { create };
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "rian",
|
3
|
-
"version": "0.0.2-alpha.
|
3
|
+
"version": "0.0.2-alpha.11",
|
4
4
|
"description": "A tracer for the edge",
|
5
5
|
"keywords": [
|
6
6
|
"TODO"
|
@@ -19,6 +19,14 @@
|
|
19
19
|
"import": "./index.mjs",
|
20
20
|
"require": "./index.js"
|
21
21
|
},
|
22
|
+
"./exporter.otel.http": {
|
23
|
+
"import": "./exporter.otel.http.mjs",
|
24
|
+
"require": "./exporter.otel.http.js"
|
25
|
+
},
|
26
|
+
"./exporter.zipkin": {
|
27
|
+
"import": "./exporter.zipkin.mjs",
|
28
|
+
"require": "./exporter.zipkin.js"
|
29
|
+
},
|
22
30
|
"./package.json": "./package.json"
|
23
31
|
},
|
24
32
|
"main": "./index.js",
|
@@ -30,6 +38,7 @@
|
|
30
38
|
"*.d.ts"
|
31
39
|
],
|
32
40
|
"dependencies": {
|
41
|
+
"flattie": "^1.1.0",
|
33
42
|
"tctx": "^0.0.10"
|
34
43
|
}
|
35
44
|
}
|