Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: add jaeger exporter * feat: add transform tests * feat: add jaege tests * fix: transform span.status to tags * fix: use NoopLogger * fix: use named constant * fix: transform event->attributes to fields * fix: transform links to ThriftReference * fix: make _sender readonly * fix: add status.name * fix: add @todos
- Loading branch information
1 parent
c8a37be
commit c020efe
Showing
8 changed files
with
662 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
/** | ||
* Copyright 2019, OpenTelemetry Authors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
import { | ||
SpanExporter, | ||
ReadableSpan, | ||
ExportResult, | ||
} from '@opentelemetry/basic-tracer'; | ||
import * as jaegerTypes from './types'; | ||
import { NoopLogger } from '@opentelemetry/core'; | ||
import * as types from '@opentelemetry/types'; | ||
import { spanToThrift } from './transform'; | ||
|
||
/** | ||
* Format and sends span information to Jaeger Exporter. | ||
*/ | ||
export class JaegerExporter implements SpanExporter { | ||
private readonly _logger: types.Logger; | ||
private readonly _process: jaegerTypes.ThriftProcess; | ||
private readonly _sender: typeof jaegerTypes.UDPSender; | ||
|
||
constructor(config: jaegerTypes.ExporterConfig) { | ||
this._logger = config.logger || new NoopLogger(); | ||
const tags: jaegerTypes.Tag[] = config.tags || []; | ||
|
||
this._sender = new jaegerTypes.UDPSender(config); | ||
this._process = { | ||
serviceName: config.serviceName, | ||
tags: jaegerTypes.ThriftUtils.getThriftTags(tags), | ||
}; | ||
this._sender.setProcess(this._process); | ||
} | ||
|
||
/** Exports a list of spans to Jaeger. */ | ||
export( | ||
spans: ReadableSpan[], | ||
resultCallback: (result: ExportResult) => void | ||
): void { | ||
this._logger.debug('Jaeger exporter export'); | ||
return this._sendSpans(spans, resultCallback); | ||
} | ||
|
||
/** Shutdown exporter. */ | ||
shutdown(): void { | ||
this._sender.close(); | ||
} | ||
|
||
/** Transform spans and sends to Jaeger service. */ | ||
private _sendSpans( | ||
spans: ReadableSpan[], | ||
done?: (result: ExportResult) => void | ||
) { | ||
const thriftSpan = spans.map(span => spanToThrift(span)); | ||
for (const span of thriftSpan) { | ||
this._sender.append(span, (numSpans: number, err?: string) => { | ||
if (err) { | ||
// @todo: decide whether to break out the loop on first error. | ||
this._logger.error(`failed to append span: ${err}`); | ||
if (done) return done(ExportResult.FailedNonRetryable); | ||
} | ||
}); | ||
} | ||
// @todo: We should wait for all the callbacks of the append calls to | ||
// complete before it calls done with success. | ||
this._logger.debug('successful append for : %s', thriftSpan.length); | ||
if (done) return done(ExportResult.Success); | ||
} | ||
} |
121 changes: 121 additions & 0 deletions
121
packages/opentelemetry-exporter-jaeger/src/transform.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
/** | ||
* Copyright 2019, OpenTelemetry Authors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
import { Link, CanonicalCode } from '@opentelemetry/types'; | ||
import { ReadableSpan } from '@opentelemetry/basic-tracer'; | ||
import { | ||
ThriftSpan, | ||
Tag, | ||
Log, | ||
ThriftTag, | ||
ThriftLog, | ||
ThriftUtils, | ||
Utils, | ||
ThriftReference, | ||
TagValue, | ||
ThriftReferenceType, | ||
} from './types'; | ||
|
||
const MICROS_PER_MILLI = 1000; | ||
const DEFAULT_FLAGS = 0x1; | ||
|
||
/** | ||
* Translate OpenTelemetry ReadableSpan to Jaeger Thrift Span | ||
* @param span Span to be translated | ||
*/ | ||
export function spanToThrift(span: ReadableSpan): ThriftSpan { | ||
const traceIdHigh = span.spanContext.traceId.slice(0, 16); | ||
const traceIdLow = span.spanContext.traceId.slice(16); | ||
const parentSpan = span.parentSpanId | ||
? Utils.encodeInt64(span.parentSpanId) | ||
: ThriftUtils.emptyBuffer; | ||
|
||
const tags = Object.keys(span.attributes).map( | ||
(name): Tag => ({ key: name, value: toTagValue(span.attributes[name]) }) | ||
); | ||
tags.push({ key: 'status.code', value: span.status.code }); | ||
tags.push({ key: 'status.name', value: CanonicalCode[span.status.code] }); | ||
if (span.status.message) { | ||
tags.push({ key: 'status.message', value: span.status.message }); | ||
} | ||
// Ensure that if Status.Code is not OK, that we set the "error" tag on the | ||
// Jaeger span. | ||
if (span.status.code !== CanonicalCode.OK) { | ||
tags.push({ key: 'error', value: true }); | ||
} | ||
const spanTags: ThriftTag[] = ThriftUtils.getThriftTags(tags); | ||
|
||
const logs = span.events.map( | ||
(event): Log => { | ||
const fields: Tag[] = [{ key: 'message.id', value: event.name }]; | ||
const attrs = event.attributes; | ||
if (attrs) { | ||
Object.keys(attrs).forEach(attr => | ||
fields.push({ key: attr, value: toTagValue(attrs[attr]) }) | ||
); | ||
} | ||
return { timestamp: event.time, fields }; | ||
} | ||
); | ||
const spanLogs: ThriftLog[] = ThriftUtils.getThriftLogs(logs); | ||
|
||
return { | ||
traceIdLow: Utils.encodeInt64(traceIdLow), | ||
traceIdHigh: Utils.encodeInt64(traceIdHigh), | ||
spanId: Utils.encodeInt64(span.spanContext.spanId), | ||
parentSpanId: parentSpan, | ||
operationName: span.name, | ||
references: spanLinksToThriftRefs(span.links, span.parentSpanId), | ||
flags: span.spanContext.traceOptions || DEFAULT_FLAGS, | ||
startTime: Utils.encodeInt64(span.startTime * MICROS_PER_MILLI), | ||
duration: Utils.encodeInt64( | ||
Math.round((span.endTime - span.startTime) * MICROS_PER_MILLI) | ||
), | ||
tags: spanTags, | ||
logs: spanLogs, | ||
}; | ||
} | ||
|
||
/** Translate OpenTelemetry {@link Link}s to Jaeger ThriftReference. */ | ||
function spanLinksToThriftRefs( | ||
links: Link[], | ||
parentSpanId?: string | ||
): ThriftReference[] { | ||
return links | ||
.map((link): ThriftReference | null => { | ||
if (link.spanContext.spanId === parentSpanId) { | ||
const refType = ThriftReferenceType.CHILD_OF; | ||
const traceId = link.spanContext.traceId; | ||
const traceIdHigh = Utils.encodeInt64(traceId.slice(0, 16)); | ||
const traceIdLow = Utils.encodeInt64(traceId.slice(16)); | ||
const spanId = Utils.encodeInt64(link.spanContext.spanId); | ||
return { traceIdLow, traceIdHigh, spanId, refType }; | ||
} | ||
return null; | ||
}) | ||
.filter(ref => !!ref) as ThriftReference[]; | ||
} | ||
|
||
/** Translate OpenTelemetry attribute value to Jaeger TagValue. */ | ||
function toTagValue(value: unknown): TagValue { | ||
const valueType = typeof value; | ||
if (valueType === 'boolean') { | ||
return value as boolean; | ||
} else if (valueType === 'number') { | ||
return value as number; | ||
} | ||
return String(value); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
/** | ||
* Copyright 2019, OpenTelemetry Authors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
import * as types from '@opentelemetry/types'; | ||
|
||
/** | ||
* Options for Jaeger configuration | ||
*/ | ||
export interface ExporterConfig { | ||
logger?: types.Logger; | ||
serviceName: string; | ||
tags?: Tag[]; | ||
host?: string; // default: 'localhost' | ||
port?: number; // default: 6832 | ||
maxPacketSize?: number; // default: 65000 | ||
} | ||
|
||
// Below require is needed as jaeger-client types does not expose the thrift, | ||
// udp_sender, util etc. modules. | ||
|
||
// tslint:disable-next-line:variable-name | ||
export const UDPSender = require('jaeger-client/dist/src/reporters/udp_sender') | ||
.default; | ||
// tslint:disable-next-line:variable-name | ||
export const Utils = require('jaeger-client/dist/src/util').default; | ||
// tslint:disable-next-line:variable-name | ||
export const ThriftUtils = require('jaeger-client/dist/src/thrift').default; | ||
|
||
export type TagValue = string | number | boolean; | ||
|
||
export interface Tag { | ||
key: string; | ||
value: TagValue; | ||
} | ||
|
||
export interface Log { | ||
timestamp: number; | ||
fields: Tag[]; | ||
} | ||
|
||
export type SenderCallback = (numSpans: number, err?: string) => void; | ||
|
||
export interface ThriftProcess { | ||
serviceName: string; | ||
tags: ThriftTag[]; | ||
} | ||
|
||
export interface ThriftTag { | ||
key: string; | ||
vType: string; | ||
vStr: string; | ||
vDouble: number; | ||
vBool: boolean; | ||
} | ||
|
||
export interface ThriftLog { | ||
timestamp: number; | ||
fields: ThriftTag[]; | ||
} | ||
|
||
export enum ThriftReferenceType { | ||
CHILD_OF = 'CHILD_OF', | ||
FOLLOWS_FROM = 'FOLLOWS_FROM', | ||
} | ||
|
||
export interface ThriftReference { | ||
traceIdLow: Buffer; | ||
traceIdHigh: Buffer; | ||
spanId: Buffer; | ||
refType: ThriftReferenceType; | ||
} | ||
|
||
export interface ThriftSpan { | ||
traceIdLow: Buffer; | ||
traceIdHigh: Buffer; | ||
spanId: Buffer; | ||
parentSpanId: string | Buffer; | ||
operationName: string; | ||
references: ThriftReference[]; | ||
flags: number; | ||
startTime: number; // milliseconds | ||
duration: number; // milliseconds | ||
tags: ThriftTag[]; | ||
logs: ThriftLog[]; | ||
} |
Oops, something went wrong.