Skip to content
This repository was archived by the owner on Oct 3, 2023. It is now read-only.

Commit 9d592e6

Browse files
authored
exporter/Zipkin: Add missing fields (Annotation,Status,MessageEvent etc) (#345)
* exporter/Zipkin: Add missing fields (Annotation, Status, MessageEvent etc) * Add comments * fix review comments 1. Rename messageEventTypeTranslation -> MESSAGE_EVENT_TYPE_TRANSLATION. 2. Add constant MICROS_PER_MILLI 3. Fix typo * update changelog
1 parent 3638ca5 commit 9d592e6

File tree

4 files changed

+148
-23
lines changed

4 files changed

+148
-23
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file.
55
## Unreleased
66

77
- Add optional `compressedSize` and `uncompressedSize` params to `Span.addMessageEvent`
8+
- Add support for ```tags```, ```status``` and ```annotation``` in Zipkin exporter.
89

910
## 0.0.9 - 2019-02-12
1011
- Add Metrics API.

packages/opencensus-exporter-zipkin/README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,16 @@ Instance the exporter on your application and pass the options, it must contain
2727
For javascript:
2828

2929
```javascript
30-
var tracing = require('@opencensus/nodejs');
31-
var zipkin = require('@opencensus/exporter-zipkin');
30+
const tracing = require('@opencensus/nodejs');
31+
const zipkin = require('@opencensus/exporter-zipkin');
3232

3333
// Add your zipkin url (ex http://localhost:9411/api/v2/spans)
3434
// and application name to the Zipkin options
35-
var options = {
35+
const options = {
3636
url: 'your-zipkin-url',
3737
serviceName: 'your-application-name'
3838
}
39-
var exporter = new zipkin.ZipkinTraceExporter(options);
39+
const exporter = new zipkin.ZipkinTraceExporter(options);
4040
```
4141

4242
Similarly for Typescript:
@@ -65,10 +65,13 @@ or
6565
```javascript
6666
tracing.registerExporter(exporter).start();
6767
```
68+
## Viewing your traces:
69+
Please visit the Zipkin UI endpoint http://localhost:9411
6870

6971
## Useful links
7072
- For more information on OpenCensus, visit: <https://opencensus.io/>
7173
- To checkout the OpenCensus for Node.js, visit: <https://github.com/census-instrumentation/opencensus-node>
74+
- For Zipkin project at https://zipkin.io/
7275
- For help or feedback on this project, join us on [gitter](https://gitter.im/census-instrumentation/Lobby)
7376

7477
[gitter-image]: https://badges.gitter.im/census-instrumentation/lobby.svg

packages/opencensus-exporter-zipkin/src/zipkin.ts

Lines changed: 71 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,22 @@
1414
* limitations under the License.
1515
*/
1616

17-
import {Exporter, ExporterBuffer, ExporterConfig, RootSpan, Span} from '@opencensus/core';
17+
import * as coreTypes from '@opencensus/core';
18+
import {Exporter, ExporterBuffer, ExporterConfig, RootSpan, Span, SpanKind} from '@opencensus/core';
1819
import {logger, Logger} from '@opencensus/core';
19-
import {prototype} from 'events';
2020
import * as http from 'http';
2121
import * as url from 'url';
2222

23+
const STATUS_CODE = 'census.status_code';
24+
const STATUS_DESCRIPTION = 'census.status_description';
25+
26+
const MESSAGE_EVENT_TYPE_TRANSLATION: {[k: number]: string} = {
27+
0: 'UNSPECIFIED',
28+
1: 'SENT',
29+
2: 'RECEIVED'
30+
};
31+
export const MICROS_PER_MILLI = 1000;
32+
2333
export interface ZipkinExporterOptions extends ExporterConfig {
2434
url?: string;
2535
serviceName: string;
@@ -31,11 +41,18 @@ interface TranslatedSpan {
3141
id: string;
3242
parentId?: string;
3343
kind: string;
34-
timestamp: number;
44+
timestamp: number; // in microseconds
3545
duration: number;
3646
debug: boolean;
3747
shared: boolean;
3848
localEndpoint: {serviceName: string};
49+
annotations: Annotation[];
50+
tags: {[key: string]: string};
51+
}
52+
53+
interface Annotation {
54+
timestamp?: number; // in microseconds
55+
value?: string;
3956
}
4057

4158
/** Zipkin Exporter manager class */
@@ -138,23 +155,68 @@ export class ZipkinTraceExporter implements Exporter {
138155
* @param span Span to be translated
139156
* @param rootSpan Only necessary if the span has rootSpan
140157
*/
141-
private translateSpan(span: Span|RootSpan): TranslatedSpan {
158+
translateSpan(span: Span|RootSpan): TranslatedSpan {
142159
const spanTraslated = {
143160
traceId: span.traceId,
144161
name: span.name,
145162
id: span.id,
146-
parentId: span.parentSpanId,
147-
kind: 'SERVER',
148-
timestamp: span.startTime.getTime() * 1000,
149-
duration: Math.round(span.duration * 1000),
163+
// Zipkin API for span kind only accept
164+
// (CLIENT|SERVER|PRODUCER|CONSUMER)
165+
kind: span.kind === SpanKind.CLIENT ? 'CLIENT' : 'SERVER',
166+
timestamp: span.startTime.getTime() * MICROS_PER_MILLI,
167+
duration: Math.round(span.duration * MICROS_PER_MILLI),
150168
debug: true,
151169
shared: true,
152-
localEndpoint: {serviceName: this.serviceName}
170+
localEndpoint: {serviceName: this.serviceName},
171+
tags: this.createTags(span.attributes, span.status),
172+
annotations: this.createAnnotations(span.annotations, span.messageEvents)
153173
} as TranslatedSpan;
154174

175+
if (span.parentSpanId) {
176+
spanTraslated.parentId = span.parentSpanId;
177+
}
155178
return spanTraslated;
156179
}
157180

181+
/** Converts OpenCensus Attributes ans Status to Zipkin Tags format. */
182+
private createTags(
183+
attributes: coreTypes.Attributes, status: coreTypes.Status) {
184+
const tags: {[key: string]: string} = {};
185+
for (const key of Object.keys(attributes)) {
186+
tags[key] = String(attributes[key]);
187+
}
188+
tags[STATUS_CODE] = String(status.code);
189+
if (status.message) {
190+
tags[STATUS_DESCRIPTION] = status.message;
191+
}
192+
return tags;
193+
}
194+
195+
/**
196+
* Converts OpenCensus Annotation and MessageEvent to Zipkin Annotations
197+
* format.
198+
*/
199+
private createAnnotations(
200+
annotationTimedEvents: coreTypes.Annotation[],
201+
messageEventTimedEvents: coreTypes.MessageEvent[]) {
202+
let annotations: Annotation[] = [];
203+
if (annotationTimedEvents) {
204+
annotations = annotationTimedEvents.map(
205+
(annotation) => ({
206+
timestamp: annotation.timestamp * MICROS_PER_MILLI,
207+
value: annotation.description
208+
}));
209+
}
210+
if (messageEventTimedEvents) {
211+
annotations.push(...messageEventTimedEvents.map(
212+
(messageEvent) => ({
213+
timestamp: messageEvent.timestamp * MICROS_PER_MILLI,
214+
value: MESSAGE_EVENT_TYPE_TRANSLATION[messageEvent.type]
215+
})));
216+
}
217+
return annotations;
218+
}
219+
158220
// TODO: review return of method publish from exporter interface - today is
159221
// returning void
160222
/**

packages/opencensus-exporter-zipkin/test/test-zipkin.ts

Lines changed: 69 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,11 @@
1414
* limitations under the License.
1515
*/
1616

17-
import {CoreTracer, RootSpan, SpanKind, TracerConfig} from '@opencensus/core';
17+
import {CanonicalCode, CoreTracer, MessageEventType, RootSpan, SpanKind, TracerConfig} from '@opencensus/core';
1818
import * as assert from 'assert';
19-
import * as http from 'http';
20-
import * as mocha from 'mocha';
2119
import * as nock from 'nock';
2220

23-
import {ZipkinExporterOptions, ZipkinTraceExporter} from '../src/zipkin';
24-
25-
/** Interface with request response model */
26-
interface RequestResponse {
27-
statusCode: number;
28-
statusMessage: string;
29-
}
21+
import {MICROS_PER_MILLI, ZipkinExporterOptions, ZipkinTraceExporter} from '../src/zipkin';
3022

3123
/** Zipkin host url */
3224
const zipkinHost = 'http://localhost:9411';
@@ -109,6 +101,73 @@ describe('Zipkin Exporter', function() {
109101
});
110102
});
111103

104+
describe('translateSpan()', () => {
105+
it('should translate traces to Zipkin format', () => {
106+
const exporter = new ZipkinTraceExporter(zipkinOptions);
107+
const tracer = new CoreTracer();
108+
tracer.start(defaultConfig);
109+
110+
return tracer.startRootSpan({name: 'root-test'}, (rootSpan: RootSpan) => {
111+
const span = rootSpan.startChildSpan('spanTest', SpanKind.CLIENT);
112+
span.addAttribute('my-int-attribute', 100);
113+
span.addAttribute('my-str-attribute', 'value');
114+
span.addAttribute('my-bool-attribute', true);
115+
span.setStatus(CanonicalCode.RESOURCE_EXHAUSTED, 'RESOURCE_EXHAUSTED');
116+
117+
span.addAnnotation('processing', {}, 1550213104708);
118+
span.addMessageEvent(MessageEventType.SENT, '1', 1550213104708);
119+
span.addMessageEvent(MessageEventType.RECEIVED, '2', 1550213104708);
120+
span.addMessageEvent(MessageEventType.UNSPECIFIED, '3', 1550213104708);
121+
span.addAnnotation('done', {}, 1550213104708);
122+
span.end();
123+
rootSpan.end();
124+
125+
const rootSpanTranslated = exporter.translateSpan(rootSpan);
126+
assert.deepEqual(rootSpanTranslated, {
127+
'annotations': [],
128+
'debug': true,
129+
'duration': Math.round(rootSpan.duration * MICROS_PER_MILLI),
130+
'id': rootSpan.id,
131+
'kind': 'SERVER',
132+
'localEndpoint': {'serviceName': 'opencensus-tests'},
133+
'name': 'root-test',
134+
'shared': true,
135+
'tags': {'census.status_code': '0'},
136+
'timestamp': rootSpan.startTime.getTime() * MICROS_PER_MILLI,
137+
'traceId': rootSpan.traceId
138+
});
139+
140+
const chilsSpanTranslated = exporter.translateSpan(span);
141+
assert.deepEqual(chilsSpanTranslated, {
142+
'annotations': [
143+
{'timestamp': 1550213104708000, 'value': 'processing'},
144+
{'timestamp': 1550213104708000, 'value': 'done'},
145+
{'timestamp': 1550213104708000, 'value': 'SENT'},
146+
{'timestamp': 1550213104708000, 'value': 'RECEIVED'},
147+
{'timestamp': 1550213104708000, 'value': 'UNSPECIFIED'}
148+
],
149+
'debug': true,
150+
'duration': Math.round(span.duration * MICROS_PER_MILLI),
151+
'id': span.id,
152+
'kind': 'CLIENT',
153+
'localEndpoint': {'serviceName': 'opencensus-tests'},
154+
'name': 'spanTest',
155+
'parentId': rootSpan.id,
156+
'shared': true,
157+
'tags': {
158+
'census.status_code': '8',
159+
'census.status_description': 'RESOURCE_EXHAUSTED',
160+
'my-int-attribute': '100',
161+
'my-str-attribute': 'value',
162+
'my-bool-attribute': 'true'
163+
},
164+
'timestamp': span.startTime.getTime() * MICROS_PER_MILLI,
165+
'traceId': span.traceId
166+
});
167+
});
168+
});
169+
});
170+
112171
describe('publish() with a wrong Zipkin url', () => {
113172
it('shouldn\'t send traces to Zipkin service and return an 404 error',
114173
() => {

0 commit comments

Comments
 (0)