Skip to content

Commit

Permalink
feat(opentelemetry-js): infer zipkin service name from resource
Browse files Browse the repository at this point in the history
  • Loading branch information
rezakrimi committed Jun 3, 2020
1 parent f08b2a0 commit 6c113d7
Show file tree
Hide file tree
Showing 2 changed files with 168 additions and 1 deletion.
14 changes: 13 additions & 1 deletion packages/opentelemetry-exporter-zipkin/src/zipkin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,17 @@ import {
statusDescriptionTagName,
} from './transform';
import { OT_REQUEST_HEADER } from './utils';
import { SERVICE_RESOURCE } from '@opentelemetry/resources';
/**
* Zipkin Exporter
*/
export class ZipkinExporter implements SpanExporter {
static readonly DEFAULT_URL = 'http://localhost:9411/api/v2/spans';
private readonly _logger: api.Logger;
private readonly _serviceName: string;
private readonly _statusCodeTagName: string;
private readonly _statusDescriptionTagName: string;
private readonly _reqOpts: http.RequestOptions;
private _serviceName: string;
private _isShutdown: boolean;

constructor(config: zipkinTypes.ExporterConfig) {
Expand Down Expand Up @@ -68,6 +69,17 @@ export class ZipkinExporter implements SpanExporter {
spans: ReadableSpan[],
resultCallback: (result: ExportResult) => void
) {
if (typeof this._serviceName !== 'string') {
if (
typeof spans[0].resource.labels[SERVICE_RESOURCE.NAME] !== 'undefined'
) {
this._serviceName = spans[0].resource.labels[
SERVICE_RESOURCE.NAME
].toString();
} else {
this._serviceName = 'Unnamed Service';
}
}
this._logger.debug('Zipkin exporter export');
if (this._isShutdown) {
setTimeout(() => resultCallback(ExportResult.FAILED_NOT_RETRYABLE));
Expand Down
155 changes: 155 additions & 0 deletions packages/opentelemetry-exporter-zipkin/test/zipkin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { ZipkinExporter } from '../src';
import * as zipkinTypes from '../src/types';
import { OT_REQUEST_HEADER } from '../src/utils';
import { TraceFlags } from '@opentelemetry/api';
import { SERVICE_RESOURCE } from '@opentelemetry/resources';

const MICROS_PER_SECS = 1e6;

Expand Down Expand Up @@ -331,6 +332,160 @@ describe('ZipkinExporter', () => {
done();
});
});

it('should set serviceName to "Unnamed service" by default', () => {
const scope = nock('http://localhost:9411')
.post('/api/v2/spans')
.replyWithError(new Error('My Socket Error'));

const parentSpanId = '5c1c63257de34c67';
const startTime = 1566156729709;
const duration = 2000;

const span1: ReadableSpan = {
name: 'my-span',
kind: api.SpanKind.INTERNAL,
parentSpanId,
spanContext: {
traceId: 'd4cda95b652f4a1592b449d5929fda1b',
spanId: '6e0c63257de34c92',
traceFlags: TraceFlags.NONE,
},
startTime: [startTime, 0],
endTime: [startTime + duration, 0],
ended: true,
duration: [duration, 0],
status: {
code: api.CanonicalCode.OK,
},
attributes: {
key1: 'value1',
key2: 'value2',
},
links: [],
events: [
{
name: 'my-event',
time: [startTime + 10, 0],
attributes: { key3: 'value3' },
},
],
resource: Resource.empty(),
};
const span2: ReadableSpan = {
name: 'my-span',
kind: api.SpanKind.SERVER,
spanContext: {
traceId: 'd4cda95b652f4a1592b449d5929fda1b',
spanId: '6e0c63257de34c92',
traceFlags: TraceFlags.NONE,
},
startTime: [startTime, 0],
endTime: [startTime + duration, 0],
ended: true,
duration: [duration, 0],
status: {
code: api.CanonicalCode.OK,
},
attributes: {},
links: [],
events: [],
resource: Resource.empty(),
};

const exporter = new ZipkinExporter({
serviceName: 'my-service',
});

delete exporter['_serviceName'];

exporter.export([span1, span2], (result: ExportResult) => {
scope.done();
assert.equal(exporter['_serviceName'], 'Unnamed Service');
});
});

it('should set serviceName if resource has one', () => {
const resource_service_name = 'resource_service_name';

const scope = nock('http://localhost:9411')
.post('/api/v2/spans')
.replyWithError(new Error('My Socket Error'));

const parentSpanId = '5c1c63257de34c67';
const startTime = 1566156729709;
const duration = 2000;

const span1: ReadableSpan = {
name: 'my-span',
kind: api.SpanKind.INTERNAL,
parentSpanId,
spanContext: {
traceId: 'd4cda95b652f4a1592b449d5929fda1b',
spanId: '6e0c63257de34c92',
traceFlags: TraceFlags.NONE,
},
startTime: [startTime, 0],
endTime: [startTime + duration, 0],
ended: true,
duration: [duration, 0],
status: {
code: api.CanonicalCode.OK,
},
attributes: {
key1: 'value1',
key2: 'value2',
},
links: [],
events: [
{
name: 'my-event',
time: [startTime + 10, 0],
attributes: { key3: 'value3' },
},
],
resource: new Resource({
[SERVICE_RESOURCE.NAME]: resource_service_name,
}),
};
const span2: ReadableSpan = {
name: 'my-span',
kind: api.SpanKind.SERVER,
spanContext: {
traceId: 'd4cda95b652f4a1592b449d5929fda1b',
spanId: '6e0c63257de34c92',
traceFlags: TraceFlags.NONE,
},
startTime: [startTime, 0],
endTime: [startTime + duration, 0],
ended: true,
duration: [duration, 0],
status: {
code: api.CanonicalCode.OK,
},
attributes: {},
links: [],
events: [],
resource: Resource.empty(),
};

const exporter = new ZipkinExporter({
serviceName: 'my-service',
});

delete exporter['_serviceName'];

exporter.export([span1, span2], (result: ExportResult) => {
scope.done();
assert.equal(exporter['_serviceName'], resource_service_name);

// checking if service name remains consistent in further exports
exporter.export([span2], (result: ExportResult) => {
scope.done();
assert.equal(exporter['_serviceName'], resource_service_name);
});
});
});
});

describe('shutdown', () => {
Expand Down

0 comments on commit 6c113d7

Please sign in to comment.