UNPKG

hawkly

Version:
308 lines 24.4 kB
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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