Skip to content

Commit

Permalink
feat(opentelemetry-js): infer zipkin service name from resource (#1138)
Browse files Browse the repository at this point in the history
* feat(opentelemetry-js): infer zipkin service name from resource

* Update packages/opentelemetry-exporter-zipkin/src/zipkin.ts

making service name optional for zipkin exporter

Co-authored-by: Daniel Dyla <dyladan@users.noreply.github.com>

* fix: making serviceName optional for zipkin config

* fix: resolving type issues

* Update packages/opentelemetry-exporter-zipkin/src/zipkin.ts

making config object optional for zipkin

Co-authored-by: Daniel Dyla <dyladan@users.noreply.github.com>

* refactor: making default service name a constant

Co-authored-by: Daniel Dyla <dyladan@users.noreply.github.com>
Co-authored-by: Mayur Kale <mayurkale@google.com>
  • Loading branch information
3 people authored Jun 6, 2020
1 parent a655d2b commit a4da9de
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 18 deletions.
2 changes: 1 addition & 1 deletion packages/opentelemetry-exporter-zipkin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ npm install --save @opentelemetry/exporter-zipkin

## Usage

Install the exporter on your application and pass the options, it must contain a service name.
Install the exporter on your application and pass the options. `serviceName` is an optional string. If omitted, the exporter will first try to get the service name from the Resource. If no service name can be detected on the Resource, a fallback name of "OpenTelemetry Service" will be used.

```js
const { ZipkinExporter } = require('@opentelemetry/exporter-zipkin');
Expand Down
2 changes: 1 addition & 1 deletion packages/opentelemetry-exporter-zipkin/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import * as api from '@opentelemetry/api';
*/
export interface ExporterConfig {
logger?: api.Logger;
serviceName: string;
serviceName?: string;
url?: string;
// Optional mapping overrides for OpenTelemetry status code and description.
statusCodeTagName?: string;
Expand Down
36 changes: 20 additions & 16 deletions packages/opentelemetry-exporter-zipkin/src/zipkin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,21 @@ 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 DEFAULT_SERVICE_NAME = 'OpenTelemetry Service';
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) {
constructor(config: zipkinTypes.ExporterConfig = {}) {
const urlStr = config.url || ZipkinExporter.DEFAULT_URL;
const urlOpts = url.parse(urlStr);

Expand Down Expand Up @@ -68,12 +70,18 @@ export class ZipkinExporter implements SpanExporter {
spans: ReadableSpan[],
resultCallback: (result: ExportResult) => void
) {
if (typeof this._serviceName !== 'string') {
this._serviceName = String(
spans[0].resource.labels[SERVICE_RESOURCE.NAME] ||
this.DEFAULT_SERVICE_NAME
);
}
this._logger.debug('Zipkin exporter export');
if (this._isShutdown) {
setTimeout(() => resultCallback(ExportResult.FAILED_NOT_RETRYABLE));
return;
}
return this._sendSpans(spans, resultCallback);
return this._sendSpans(spans, this._serviceName, resultCallback);
}

/**
Expand All @@ -87,26 +95,22 @@ export class ZipkinExporter implements SpanExporter {
this._isShutdown = true;
}

/**
* Transforms an OpenTelemetry span to a Zipkin span.
*/
private _toZipkinSpan(span: ReadableSpan): zipkinTypes.Span {
return toZipkinSpan(
span,
this._serviceName,
this._statusCodeTagName,
this._statusDescriptionTagName
);
}

/**
* Transform spans and sends to Zipkin service.
*/
private _sendSpans(
spans: ReadableSpan[],
serviceName: string,
done?: (result: ExportResult) => void
) {
const zipkinSpans = spans.map(span => this._toZipkinSpan(span));
const zipkinSpans = spans.map(span =>
toZipkinSpan(
span,
serviceName,
this._statusCodeTagName,
this._statusDescriptionTagName
)
);
return this._send(zipkinSpans, (result: ExportResult) => {
if (done) {
return done(result);
Expand Down
147 changes: 147 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,152 @@ describe('ZipkinExporter', () => {
done();
});
});

it('should set serviceName to "Opentelemetry 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({});

exporter.export([span1, span2], (result: ExportResult) => {
scope.done();
assert.equal(exporter['_serviceName'], 'OpenTelemetry 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({});

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 a4da9de

Please sign in to comment.