Skip to content

Commit 4177c84

Browse files
committed
Add initial context passing + tracedFetch
1 parent 53d3697 commit 4177c84

File tree

4 files changed

+93
-11
lines changed

4 files changed

+93
-11
lines changed

src/exporters/exporter.ts

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { Span, Trace } from 'src/tracing';
2+
import { SpanContext } from 'src/types';
3+
4+
export class Exporter {
5+
6+
/**
7+
* Transform a Trace into a JSON object which will be sent to the collector.
8+
*
9+
* @param trace The Trace which needs to be transformed
10+
* @returns The transformed Trace
11+
*/
12+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
13+
transform(trace: Trace): object {
14+
throw new Error('Transformer has not implemented `transform()` function');
15+
}
16+
17+
/**
18+
* Get the headers containing the trace context, these will be passed on to other services
19+
* so that they can continue the trace.
20+
*/
21+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
22+
injectContextHeaders(span: Span): Record<string, string> {
23+
return {};
24+
}
25+
26+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
27+
readContextHeaders(headers: Headers): SpanContext | null {
28+
return null;
29+
}
30+
31+
collectSpans(span: Span): Span[] {
32+
const spans = [];
33+
34+
spans.push(span);
35+
// Go through children and collect them all
36+
for (const childSpan of span.getChildSpans()) {
37+
spans.push(...this.collectSpans(childSpan));
38+
}
39+
40+
return spans;
41+
}
42+
}

src/trace.ts

+9
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,12 @@ export function traceFn<T>(
4949
span.end();
5050
return value;
5151
}
52+
53+
export function tracedFetch(
54+
parent: Span,
55+
request: string | Request,
56+
requestInit?: RequestInit | Request,
57+
spanOpts?: SpanCreationOptions,
58+
): Promise<Response> {
59+
return parent.tracedFetch(request, requestInit, spanOpts);
60+
}

src/tracing.ts

+33-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { OtlpTransformer } from './transformers/otlp';
2-
import { ATTRIBUTE_NAME } from './utils/constants';
1+
import { OtlpTransformer } from './exporters/otlp';
2+
import { ATTRIBUTE_NAME, SPAN_NAME } from './utils/constants';
33
import { generateSpanId, generateTraceId } from './utils/rand';
44
import { traceFn } from './trace';
55
import { SpanBuilder } from './builder';
@@ -33,11 +33,19 @@ export function getDefaultAttributes(opts: TracerOptions): Attributes {
3333
}
3434

3535
export class Span {
36-
36+
37+
#trace: Trace;
3738
#span: SpanData;
3839
#childSpans: Span[];
3940

4041
constructor(traceId: string, name: string, spanOptions?: SpanCreationOptions) {
42+
// TODO: Figure out how I want to do this
43+
const trace = this instanceof Trace ? this : spanOptions?.trace;
44+
if (!trace) {
45+
throw new Error('No Trace provided for Span');
46+
}
47+
48+
this.#trace = trace;
4149
this.#span = {
4250
traceId: traceId,
4351
name,
@@ -75,16 +83,37 @@ export class Span {
7583

7684
startSpan(name: string, spanOptions?: SpanCreationOptions): Span {
7785
const span = new Span(this.#span.traceId, name, spanOptions);
86+
span.#trace = this.#trace; // TODO: I hate this, fix.
7887
span.#span.parentId = this.getSpanId();
7988
this.#childSpans.push(span);
8089

8190
return span;
8291
}
8392

93+
injectPropagation(req: Request) {
94+
// TODO: Figure out a better way to get the exporter in these situations
95+
const exporter = this.#trace.getTracerOptions().collector.exporter ?? new OtlpTransformer();
96+
97+
for (const [name, value] of Object.entries(exporter.injectContextHeaders(this))) {
98+
req.headers.append(name, value);
99+
}
100+
}
101+
84102
trace<T>(name: string, fn: TracedFn<T>, opts?: SpanCreationOptions): T {
85103
return traceFn(this, name, fn, opts);
86104
}
87105

106+
tracedFetch(
107+
request: string | Request,
108+
requestInit?: RequestInit | Request,
109+
spanOpts?: SpanCreationOptions,
110+
): Promise<Response> {
111+
const tracedRequest = new Request(request, requestInit);
112+
this.injectPropagation(tracedRequest);
113+
114+
return traceFn(this, SPAN_NAME.FETCH, () => fetch(tracedRequest), spanOpts);
115+
}
116+
88117
buildSpan(name: string) {
89118
return new SpanBuilder(this, name);
90119
}
@@ -114,7 +143,7 @@ export class Trace extends Span {
114143
) {
115144
super(
116145
tracerOptions.traceContext?.traceId ?? generateTraceId(),
117-
'Request (fetch event)',
146+
'Request',
118147
{
119148
parentId: tracerOptions.traceContext?.spanId,
120149
...spanOptions,
@@ -146,10 +175,6 @@ export class Trace extends Span {
146175
return this.#tracerOptions;
147176
}
148177

149-
injectPropagation(req: Request) {
150-
151-
}
152-
153178
async send() {
154179
// We need to end the trace here
155180
this.end();

src/types.ts

+9-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { StatusCode } from './tracing';
2-
import { TraceTransformer } from './transformers/transformer';
1+
import { StatusCode, Trace } from './tracing';
2+
import { Exporter } from './exporters/exporter';
33

44
export interface TraceData {
55
// 8 bit field currently only used to indicate sampling
@@ -66,6 +66,8 @@ export interface SpanCreationOptions {
6666
// https://www.w3.org/TR/trace-context/#parent-id
6767
parentId?: string;
6868

69+
trace?: Trace;
70+
6971
// The creation time of the span, epoch unix timestamp
7072
// https://opentelemetry.io/docs/reference/specification/trace/api/#timestamp
7173
timestamp?: number;
@@ -126,7 +128,11 @@ export interface TracerOptions {
126128
export interface CollectorOptions {
127129
url: string;
128130
headers?: HeadersInit;
129-
transformer?: TraceTransformer;
131+
/**
132+
* @deprecated Use `exporter` instead
133+
*/
134+
transformer?: Exporter;
135+
exporter?: Exporter;
130136
}
131137

132138
export interface ResourceOptions {

0 commit comments

Comments
 (0)