hawkly
Version:
An OpenTracing compatible tracer for hawkly.io
311 lines • 24.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const opentracing = require("opentracing");
const Context_1 = require("./Context");
const Span_1 = require("./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;
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_1.Span(this, name, new Context_1.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_1.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);
}
}
}
exports.Tracer = Tracer;
// 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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiVHJhY2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3RyYWNlci9UcmFjZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQSwyQ0FBMkM7QUFFM0MsdUNBQW9DO0FBQ3BDLGlDQUE4QjtBQUU5QixNQUFNLGtCQUFrQixHQUFXLDZCQUE2QixDQUFDO0FBQ2pFLE1BQU0sMkJBQTJCLEdBQVcsWUFBWSxDQUFDO0FBQ3pELE1BQU0sc0JBQXNCLEdBQVcsYUFBYSxDQUFDO0FBQ3JELE1BQU0sMkJBQTJCLEdBQVcsU0FBUyxDQUFDO0FBQ3RELE1BQU0sMEJBQTBCLEdBQVcsUUFBUSxDQUFDO0FBQ3BELE1BQU0sNEJBQTRCLEdBQVcsVUFBVSxDQUFDO0FBQ3hELE1BQU0saUNBQWlDLEdBQVcsZUFBZSxDQUFDO0FBQ2xFLE1BQU0sMEJBQTBCLEdBQVcsU0FBUyxDQUFDO0FBQ3JELE1BQU0sbUJBQW1CLEdBQVcsQ0FBQyxDQUFDO0FBRXRDLFlBQW9CLFNBQVEsV0FBVyxDQUFDLE1BQU07SUFtTjVDLFlBQVksT0FBWTtRQUN0QixLQUFLLEVBQUUsQ0FBQztRQTdNSCxtQkFBYyxHQUFVLEVBQUUsQ0FBQztRQVkzQixzQkFBaUIsR0FBVyxrQkFBa0IsQ0FBQztRQWtNcEQsSUFBSSxDQUFDLE1BQU0sR0FBRyxFQUFFLENBQUM7UUFFakIscURBQXFEO1FBQ3JELEVBQUUsQ0FBQyxDQUFDLE9BQU8sT0FBTyxDQUFDLFdBQVcsS0FBSyxTQUFTO2VBQ3ZDLE9BQU8sQ0FBQyxXQUFXLEtBQUssU0FBUyxDQUFDLENBQUMsQ0FBQztZQUN2QyxNQUFNLElBQUksS0FBSyxDQUFDLHdEQUF3RCxDQUFDLENBQUM7UUFDNUUsQ0FBQztRQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQyxPQUFPLE9BQU8sQ0FBQyxXQUFXLEtBQUssUUFBUSxDQUFDLENBQUMsQ0FBQztZQUNuRCxNQUFNLElBQUksS0FBSyxDQUFDLGtDQUFrQyxDQUFDLENBQUM7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