Skip to content

Commit d28ddaa

Browse files
vreynoldsdyladan
andauthored
feat(shim-opentracing): update logging based on new spec (#2282)
Co-authored-by: Daniel Dyla <[email protected]>
1 parent b09ee6f commit d28ddaa

File tree

7 files changed

+153
-33
lines changed

7 files changed

+153
-33
lines changed

examples/opentracing-shim/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
"@opentelemetry/exporter-zipkin": "0.22.0",
3434
"@opentelemetry/instrumentation": "0.22.0",
3535
"@opentelemetry/node": "0.22.0",
36+
"@opentelemetry/resources": "0.22.0",
37+
"@opentelemetry/semantic-conventions": "0.22.0",
3638
"@opentelemetry/shim-opentracing": "0.22.0",
3739
"@opentelemetry/tracing": "0.22.0",
3840
"opentracing": "^0.14.4"

examples/opentracing-shim/server.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ async function handleRequest(req, res) {
3737

3838
res.writeHead(200, { 'Content-Type': 'application/json' });
3939
res.write(
40-
JSON.stringify({ status: 'OK', traceId: span.spanContext().toTraceId() }),
40+
JSON.stringify({ status: 'OK', traceId: span.context().toTraceId() }),
4141
);
4242

4343
res.end();

examples/opentracing-shim/shim.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,33 @@
11
'use strict';
22

3-
const { registerInstrumentations } = require('@opentelemetry/instrumentation');
3+
const { ResourceAttributes } = require('@opentelemetry/semantic-conventions');
4+
const { Resource } = require('@opentelemetry/resources');
45
const { NodeTracerProvider } = require('@opentelemetry/node');
56
const { SimpleSpanProcessor } = require('@opentelemetry/tracing');
67
const { JaegerExporter } = require('@opentelemetry/exporter-jaeger');
78
const { ZipkinExporter } = require('@opentelemetry/exporter-zipkin');
89
const { TracerShim } = require('@opentelemetry/shim-opentracing');
910

1011
function shim(serviceName) {
11-
const provider = new NodeTracerProvider();
12+
const provider = new NodeTracerProvider({
13+
resource: new Resource({ [ResourceAttributes.SERVICE_NAME]: serviceName }),
14+
});
1215

1316
provider.addSpanProcessor(new SimpleSpanProcessor(getExporter(serviceName)));
1417
// Initialize the OpenTelemetry APIs to use the NodeTracerProvider bindings
1518
provider.register();
1619

17-
registerInstrumentations({
18-
});
19-
2020
return new TracerShim(provider.getTracer('opentracing-shim'));
2121
}
2222

23-
function getExporter(serviceName) {
23+
function getExporter() {
2424
const type = process.env.EXPORTER.toLowerCase() || 'jaeger';
2525

2626
if (type.startsWith('z')) {
27-
return new ZipkinExporter({ serviceName });
27+
return new ZipkinExporter();
2828
}
2929

30-
return new JaegerExporter({ serviceName, flushInterval: 100 });
30+
return new JaegerExporter();
3131
}
3232

3333
exports.shim = shim;

packages/opentelemetry-shim-opentracing/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
},
5858
"dependencies": {
5959
"@opentelemetry/core": "0.22.0",
60+
"@opentelemetry/semantic-conventions": "0.22.0",
6061
"opentracing": "^0.14.4"
6162
}
6263
}

packages/opentelemetry-shim-opentracing/src/shim.ts

Lines changed: 52 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,9 @@
1515
*/
1616

1717
import * as api from '@opentelemetry/api';
18+
import { SpanAttributes, SpanAttributeValue, SpanStatusCode, TextMapPropagator } from '@opentelemetry/api';
1819
import * as opentracing from 'opentracing';
19-
import {
20-
SpanAttributes,
21-
SpanAttributeValue,
22-
SpanStatusCode,
23-
TextMapPropagator,
24-
} from '@opentelemetry/api';
20+
import { SemanticAttributes } from "@opentelemetry/semantic-conventions";
2521

2622
function translateReferences(references: opentracing.Reference[]): api.Link[] {
2723
const links: api.Link[] = [];
@@ -96,14 +92,14 @@ export class SpanContextShim extends opentracing.SpanContext {
9692
/**
9793
* Returns the trace ID as a string.
9894
*/
99-
override toTraceId(): string {
95+
override toTraceId(): string {
10096
return this._spanContext.traceId;
10197
}
10298

10399
/**
104100
* Returns the span ID as a string.
105101
*/
106-
override toSpanId(): string {
102+
override toSpanId(): string {
107103
return this._spanContext.spanId;
108104
}
109105

@@ -281,19 +277,62 @@ export class SpanShim extends opentracing.Span {
281277
* @param payload an arbitrary object to be attached to the event.
282278
*/
283279
override logEvent(eventName: string, payload?: SpanAttributes): void {
284-
this._span.addEvent(eventName, payload);
280+
this._logInternal(eventName, payload);
285281
}
286282

287283
/**
288284
* Logs a set of key value pairs. Since OpenTelemetry only supports events,
289-
* the KV pairs are used as attributes on an event named "log".
285+
* the KV pairs are used as attributes on a Span event.
286+
* @param keyValuePairs a set of key-value pairs to be used as event attributes
287+
* @param timestamp optional timestamp for the event
290288
*/
291-
override log(keyValuePairs: SpanAttributes, _timestamp?: number): this {
292-
// @todo: Handle timestamp
293-
this._span.addEvent('log', keyValuePairs);
289+
override log(keyValuePairs: SpanAttributes, timestamp?: number): this {
290+
const entries = Object.entries(keyValuePairs);
291+
const eventEntry = entries.find(([key, _]) => key === 'event');
292+
const eventName = eventEntry?.[1] || 'log';
293+
const name = eventName.toString();
294+
295+
this._logInternal(name, keyValuePairs, timestamp);
294296
return this;
295297
}
296298

299+
private _logInternal(eventName: string, attributes: SpanAttributes | undefined, timestamp?: number): void {
300+
if (attributes && eventName === 'error') {
301+
const entries = Object.entries(attributes);
302+
const errorEntry = entries.find(([key]) => key === 'error.object');
303+
const error = errorEntry?.[1];
304+
if (typeof error === "string") {
305+
this._span.recordException(error, timestamp);
306+
return;
307+
}
308+
309+
const mappedAttributes: api.SpanAttributes = {};
310+
for (const [k, v] of entries) {
311+
switch (k) {
312+
case "error.kind": {
313+
mappedAttributes[SemanticAttributes.EXCEPTION_TYPE] = v;
314+
break;
315+
}
316+
case "message": {
317+
mappedAttributes[SemanticAttributes.EXCEPTION_MESSAGE] = v;
318+
break;
319+
}
320+
case "stack": {
321+
mappedAttributes[SemanticAttributes.EXCEPTION_STACKTRACE] = v;
322+
break;
323+
}
324+
default: {
325+
mappedAttributes[k] = v;
326+
break;
327+
}
328+
}
329+
}
330+
this._span.addEvent('exception', mappedAttributes, timestamp);
331+
return;
332+
}
333+
this._span.addEvent(eventName, attributes, timestamp);
334+
}
335+
297336
/**
298337
* Adds a set of tags to the span.
299338
* @param keyValueMap set of KV pairs representing tags

packages/opentelemetry-shim-opentracing/test/Shim.test.ts

Lines changed: 86 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import {
3636
import { performance } from 'perf_hooks';
3737
import { B3Propagator } from '@opentelemetry/propagator-b3';
3838
import { JaegerPropagator } from '@opentelemetry/propagator-jaeger';
39+
import { SemanticAttributes } from "@opentelemetry/semantic-conventions";
3940

4041
describe('OpenTracing Shim', () => {
4142
const compositePropagator = new CompositePropagator({
@@ -355,18 +356,92 @@ describe('OpenTracing Shim', () => {
355356
});
356357
});
357358

358-
it('logs KV pairs', () => {
359-
const kvLogs = { key: 'value', error: 'not a valid span' };
360-
span.log(kvLogs);
361-
assert.strictEqual(otSpan.events[0].name, 'log');
362-
assert.strictEqual(otSpan.events[0].attributes, kvLogs);
363-
});
359+
describe('logging', () => {
360+
describe('event with payload', () => {
361+
it('logs an event with a payload', () => {
362+
const payload = { user: 'payload', request: 1 };
363+
span.logEvent('some log', payload);
364+
assert.strictEqual(otSpan.events[0].name, 'some log');
365+
assert.deepStrictEqual(otSpan.events[0].attributes, payload);
366+
});
367+
368+
it('records an exception', () => {
369+
const payload = {
370+
'error.object': 'boom', fault: 'meow'
371+
};
372+
span.logEvent('error', payload);
373+
assert.strictEqual(otSpan.events[0].name, 'exception');
374+
const expectedAttributes = {
375+
[SemanticAttributes.EXCEPTION_MESSAGE]: 'boom',
376+
};
377+
assert.deepStrictEqual(otSpan.events[0].attributes, expectedAttributes);
378+
});
379+
380+
it('maps to exception semantic conventions', () => {
381+
const payload = {
382+
fault: 'meow', 'error.kind': 'boom', message: 'oh no!', stack: 'pancakes'
383+
};
384+
span.logEvent('error', payload);
385+
assert.strictEqual(otSpan.events[0].name, 'exception');
386+
const expectedAttributes = {
387+
fault: 'meow',
388+
[SemanticAttributes.EXCEPTION_TYPE]: 'boom',
389+
[SemanticAttributes.EXCEPTION_MESSAGE]: 'oh no!',
390+
[SemanticAttributes.EXCEPTION_STACKTRACE]: 'pancakes'
391+
};
392+
assert.deepStrictEqual(otSpan.events[0].attributes, expectedAttributes);
393+
});
394+
});
395+
396+
describe('key-value pairs', () => {
397+
const tomorrow = new Date().setDate(new Date().getDate() + 1);
364398

365-
it('logs an event with a payload', () => {
366-
const payload = { user: 'payload', request: 1 };
367-
span.logEvent('some log', payload);
368-
assert.strictEqual(otSpan.events[0].name, 'some log');
369-
assert.deepStrictEqual(otSpan.events[0].attributes, payload);
399+
it('names event after event attribute', () => {
400+
const kvLogs = { event: 'fun-time', user: 'meow', value: 123 };
401+
span.log(kvLogs, tomorrow);
402+
assert.strictEqual(otSpan.events[0].name, 'fun-time');
403+
assert.strictEqual(otSpan.events[0].time[0], Math.trunc(tomorrow / 1000));
404+
assert.strictEqual(otSpan.events[0].attributes, kvLogs);
405+
});
406+
407+
it('names event log, as a fallback', () => {
408+
const kvLogs = { user: 'meow', value: 123 };
409+
span.log(kvLogs, tomorrow);
410+
assert.strictEqual(otSpan.events[0].name, 'log');
411+
assert.strictEqual(otSpan.events[0].time[0], Math.trunc(tomorrow / 1000));
412+
assert.strictEqual(otSpan.events[0].attributes, kvLogs);
413+
});
414+
415+
it('records an exception', () => {
416+
const kvLogs = {
417+
event: 'error', 'error.object': 'boom', fault: 'meow'
418+
};
419+
span.log(kvLogs, tomorrow);
420+
assert.strictEqual(otSpan.events[0].name, 'exception');
421+
assert.strictEqual(otSpan.events[0].time[0], Math.trunc(tomorrow / 1000));
422+
const expectedAttributes = {
423+
[SemanticAttributes.EXCEPTION_MESSAGE]: 'boom',
424+
};
425+
assert.deepStrictEqual(otSpan.events[0].attributes, expectedAttributes);
426+
});
427+
428+
it('maps to exception semantic conventions', () => {
429+
const kvLogs = {
430+
event: 'error', fault: 'meow', 'error.kind': 'boom', message: 'oh no!', stack: 'pancakes'
431+
};
432+
span.log(kvLogs, tomorrow);
433+
assert.strictEqual(otSpan.events[0].name, 'exception');
434+
assert.strictEqual(otSpan.events[0].time[0], Math.trunc(tomorrow / 1000));
435+
const expectedAttributes = {
436+
event: 'error',
437+
fault: 'meow',
438+
[SemanticAttributes.EXCEPTION_TYPE]: 'boom',
439+
[SemanticAttributes.EXCEPTION_MESSAGE]: 'oh no!',
440+
[SemanticAttributes.EXCEPTION_STACKTRACE]: 'pancakes'
441+
};
442+
assert.deepStrictEqual(otSpan.events[0].attributes, expectedAttributes);
443+
});
444+
});
370445
});
371446

372447
it('updates the name', () => {

packages/opentelemetry-shim-opentracing/tsconfig.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
{
1919
"path": "../opentelemetry-propagator-jaeger"
2020
},
21+
{
22+
"path": "../opentelemetry-semantic-conventions"
23+
},
2124
{
2225
"path": "../opentelemetry-tracing"
2326
}

0 commit comments

Comments
 (0)