hawkly
Version:
An OpenTracing compatible tracer for hawkly.io
308 lines • 24.4 kB
JavaScript
import * as opentracing from 'opentracing';
import { Context } from './Context';
import { Span } from './Span';
const COLLECTOR_HOSTNAME = 'https://collector.hawkly.io';
const CARRIER_TRACER_STATE_PREFIX = 'ot-tracer-';
const CARRIER_BAGGAGE_PREFIX = 'ot-baggage-';
const CARRIER_FIELD_NAME_TRACE_ID = 'traceId';
const CARRIER_FIELD_NAME_SPAN_ID = 'spanId';
const CARRIER_FIELD_NAME_PARENT_ID = 'parentId';
const CARRIER_FIELD_NAME_REFERENCE_TYPE = 'referenceType';
const CARRIER_FIELD_NAME_SAMPLED = 'sampled';
const CARRIER_FIELD_COUNT = 5;
export class Tracer extends opentracing.Tracer {
constructor(options) {
super();
this.internalEvents = [];
this.collectorHostname = COLLECTOR_HOSTNAME;
this._spans = [];
// Check that we have an accessToken that is a string
if (typeof options.accessToken === undefined
|| options.accessToken === undefined) {
throw new Error('You need to set your accessToken for the hawkly tracer');
}
else if (typeof options.accessToken !== 'string') {
throw new Error('The accessToken must be a string');
}
else {
this.accessToken = options.accessToken;
}
// Check that we have a componentName that is a string
if (typeof options.componentName === undefined
|| options.componentName === undefined) {
throw new Error('You need to set a componentName to identify where these traces are coming from');
}
else if (typeof options.componentName !== 'string') {
throw new Error('The componentName must be a string');
}
else {
this.componentName = options.componentName;
}
// Check that the recordCallback is the correct type if one is set, and then add it to our object.
if (typeof options.recordCallback !== undefined) {
if (typeof options.recordCallback === 'function') {
this.recordCallback = options.recordCallback;
}
else {
throw new Error('recordCallback must be a function');
}
}
else {
this.recordCallback = undefined;
}
this.beginMs = Date.now();
this.endMs = 0;
this.opentracing = opentracing;
// Allow the user to use their own version of opentracing
if (options.opentracingModule) {
this.opentracing = options.opentracingModule;
this.recordInternalEvent('using external opentracing module');
}
}
_startSpan(name, fields) {
let parentContext = undefined;
let traceId;
let parentId;
let spanId;
let referenceType = undefined;
fields = fields || {};
if (fields.followsFrom) {
// Convert from a Span or a SpanContext into a Reference.
const followsFrom = this.opentracing.followsFrom(fields.followsFrom);
if (fields.references) {
fields.references.push(followsFrom);
}
else {
fields.references = [followsFrom];
}
delete (fields.followsFrom);
}
// If there are any references we need to process them
if (fields.references) {
// Loop through until we find them
for (const i = 0; i < fields.references.length; i + 1) {
const ref = fields.references[i];
const refType = ref.type();
if (refType === this.opentracing.REFERENCE_CHILD_OF ||
refType === this.opentracing.REFERENCE_FOLLOWS_FROM) {
referenceType = refType;
const context = ref.referencedContext();
if (!context) {
this.recordInternalEvent('Span reference has an invalid context', context);
continue;
}
parentContext = context;
break;
}
}
}
// If there is a traceId from the parent span use that, or create a new one
if (parentContext) {
traceId = parentContext.traceId;
parentId = parentContext.spanId;
spanId = this.generateUUID();
}
else {
// if this is the root span, parentId, traceId, spanId are all the same
parentId = traceId = spanId = this.generateUUID();
}
// check some of the optional fields are of the right type
if (fields.startTime) {
if (typeof fields.startTime !== 'number') {
throw new Error('startTime must be a timestamp of type number');
}
}
if (fields.tags) {
if (typeof fields.tags !== 'object' || Array.isArray(fields.tags)) {
throw new Error('tags must be an object');
}
}
// create the span, and pass in it's Context
const span = new Span(this, name, new Context(spanId, parentId, traceId, referenceType), {
startTime: fields.startTime ? fields.startTime : Date.now(),
tags: fields.tags ? fields.tags : {},
});
this._spans.push(span);
return span;
}
_inject(spanContext, format, carrier) {
switch (format) {
case this.opentracing.FORMAT_TEXT_MAP:
this._injectToTextMap(spanContext, carrier);
break;
case this.opentracing.FORMAT_BINARY:
this.recordInternalEvent(`Unsupported format: ${format}`);
break;
default:
this.recordInternalEvent(`Unknown format: ${format}`);
break;
}
}
_injectToTextMap(context, carrier) {
if (!carrier) {
this.recordInternalEvent('Unexpected null FORMAT_TEXT_MAP carrier in call to inject');
return;
}
if (typeof carrier !== 'object') {
this.recordInternalEvent(`Unexpected '${typeof carrier}' FORMAT_TEXT_MAP carrier in call to inject`);
return;
}
carrier[`${CARRIER_TRACER_STATE_PREFIX}${CARRIER_FIELD_NAME_SPAN_ID}`] = context.spanId;
carrier[`${CARRIER_TRACER_STATE_PREFIX}${CARRIER_FIELD_NAME_PARENT_ID}`] = context.parentId;
carrier[`${CARRIER_TRACER_STATE_PREFIX}${CARRIER_FIELD_NAME_TRACE_ID}`] = context.traceId;
carrier[`${CARRIER_TRACER_STATE_PREFIX}${CARRIER_FIELD_NAME_REFERENCE_TYPE}`] = context.referenceType;
// Baggage currently not implemented
// context.forEachBaggageItem((key: string, value: any) => {
// carrier[`${CARRIER_BAGGAGE_PREFIX}${key}`] = value;
// });
carrier[`${CARRIER_TRACER_STATE_PREFIX}${CARRIER_FIELD_NAME_SAMPLED}`] = context.sampled;
return carrier;
}
_extract(format, carrier) {
switch (format) {
case this.opentracing.FORMAT_HTTP_HEADERS:
case this.opentracing.FORMAT_TEXT_MAP:
return this._extractTextMap(carrier);
case this.opentracing.FORMAT_BINARY:
this.recordInternalEvent(`Unsupported format: ${format}`);
return undefined;
default:
this.recordInternalEvent(`Unsupported format: ${format}`);
return undefined;
}
}
/**
* Create a new Context from a carrier JSON object
*/
_extractTextMap(carrier) {
// Begin with the empty SpanContextImp
const fields = {
baggage: [],
};
let count = 0;
Object.keys(carrier).forEach((field) => {
if (field === CARRIER_TRACER_STATE_PREFIX + CARRIER_FIELD_NAME_TRACE_ID) {
fields.traceId = carrier[field];
count += 1;
}
else if (field === CARRIER_TRACER_STATE_PREFIX + CARRIER_FIELD_NAME_SPAN_ID) {
fields.spanId = carrier[field];
count += 1;
}
else if (field === CARRIER_TRACER_STATE_PREFIX + CARRIER_FIELD_NAME_PARENT_ID) {
fields.parentId = carrier[field];
count += 1;
}
else if (field === CARRIER_TRACER_STATE_PREFIX + CARRIER_FIELD_NAME_REFERENCE_TYPE) {
fields.referenceType = carrier[field];
count += 1;
}
else if (field === CARRIER_TRACER_STATE_PREFIX + CARRIER_FIELD_NAME_SAMPLED) {
if (carrier[field] !== 'true' &&
carrier[field] !== 'false' &&
carrier[field] !== true &&
carrier[field] !== false) {
throw new Error('Trace corrupted, sampled should be type ' +
`Boolean, got ${carrier[field]}`);
}
else {
fields.sampled = Boolean(carrier[field]);
}
count += 1;
}
else if (field.indexOf(CARRIER_BAGGAGE_PREFIX) === 0) {
fields.baggage[field.slice(CARRIER_BAGGAGE_PREFIX.length)] =
carrier[field];
}
});
if (count !== CARRIER_FIELD_COUNT) {
throw new Error('Trace corrupted, ' +
'require traceId, spanId and sampled');
}
return new Context(fields.spanId, fields.parentId, fields.traceId);
}
isSampled() {
// return this.sampleInstance.isSampled(span, parent);
return true;
}
/**
* Discard any buffered data.
*/
clear() {
this._spans = [];
}
/**
* Generate a uuid.v4
*
* Based on this gist https://gist.github.com/jed/982883
*
*/
generateUUID() {
// tslint:disable
function generate(a) {
return a // if the placeholder was passed, return
? (a ^
Math.random() * 16 // in which case
>> a / 4 // 8 to 11
).toString(16) // in hexadecimal
: ("" +
1e7 +
-1e3 +
-4e3 +
-8e3 +
-1e11 // -100000000000,
).replace(// replacing
/[018]/g, // zeroes, ones, and eights with
generate);
}
return generate();
// tslint:enable
}
/**
* Create a new span from a carrier
* by default uses the text_map
*
*/
join(operationName, carrier, format = 'text_map') {
const context = this.extract(format, carrier);
return this.startSpan(operationName, { childOf: context });
}
recordInternalEvent(msg, payload) {
this.internalEvents.push({
msg,
payload,
});
}
/**
* Return the buffered data in a format convenient for making unit test
* assertions.
*/
record(span) {
const report = {
operationName: span._operationName,
startTime: span._startMs,
finishMs: span._finishMs,
duration: span.durationMs(),
tags: span._tags,
logs: span._logs,
traceId: span.context().traceId,
spanId: span.context().spanId,
// sampled: span.sampled,
baggage: span.context().baggage,
referenceType: span.context().referenceType,
};
if (typeof this.recordCallback === undefined) {
// curl
}
else {
this.recordCallback(report);
}
}
}
// TODO: work out how to use this without breaking the tests
// export interface TracerConstructorOptions {
// accessToken: string;
// componentName: string;
// recordCallback: any;
// }
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiVHJhY2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3RyYWNlci9UcmFjZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLFdBQVcsTUFBTSxhQUFhLENBQUM7QUFFM0MsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUNwQyxPQUFPLEVBQUUsSUFBSSxFQUFFLE1BQU0sUUFBUSxDQUFDO0FBRTlCLE1BQU0sa0JBQWtCLEdBQVcsNkJBQTZCLENBQUM7QUFDakUsTUFBTSwyQkFBMkIsR0FBVyxZQUFZLENBQUM7QUFDekQsTUFBTSxzQkFBc0IsR0FBVyxhQUFhLENBQUM7QUFDckQsTUFBTSwyQkFBMkIsR0FBVyxTQUFTLENBQUM7QUFDdEQsTUFBTSwwQkFBMEIsR0FBVyxRQUFRLENBQUM7QUFDcEQsTUFBTSw0QkFBNEIsR0FBVyxVQUFVLENBQUM7QUFDeEQsTUFBTSxpQ0FBaUMsR0FBVyxlQUFlLENBQUM7QUFDbEUsTUFBTSwwQkFBMEIsR0FBVyxTQUFTLENBQUM7QUFDckQsTUFBTSxtQkFBbUIsR0FBVyxDQUFDLENBQUM7QUFFdEMsTUFBTSxhQUFjLFNBQVEsV0FBVyxDQUFDLE1BQU07SUFtTjVDLFlBQVksT0FBWTtRQUN0QixLQUFLLEVBQUUsQ0FBQztRQTdNSCxtQkFBYyxHQUFVLEVBQUUsQ0FBQztRQVkzQixzQkFBaUIsR0FBVyxrQkFBa0IsQ0FBQztRQWtNcEQsSUFBSSxDQUFDLE1BQU0sR0FBRyxFQUFFLENBQUM7UUFFakIscURBQXFEO1FBQ3JELEVBQUUsQ0FBQyxDQUFDLE9BQU8sT0FBTyxDQUFDLFdBQVcsS0FBSyxTQUFTO2VBQ3ZDLE9BQU8sQ0FBQyxXQUFXLEtBQUssU0FBUyxDQUFDLENBQUMsQ0FBQztZQUN2QyxNQUFNLElBQUksS0FBSyxDQUFDLHdEQUF3RCxDQUFDLENBQUM7UUFDNUUsQ0FBQztRQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQyxPQUFPLE9B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