apostrophe
Version:
The Apostrophe Content Management System.
146 lines (133 loc) • 4.65 kB
JavaScript
// Initialize OpenTelemetry tracer singleton and export
// the most useful API members, the tracer and some helper functions.
//
// The library shouldn't be used directly (although it's not fatal),
// it is registered as self.apos.telemetry pseudo module (see index.js).
const api = require('@opentelemetry/api');
const util = require('util');
const version = require('../package.json').version;
/** @type {api.Tracer} */
let tracer;
const Attributes = {
SCENE: 'apos.scene',
TEMPLATE: 'apos.template',
EVENT_MODULE: 'apos.event.module',
EVENT_NAME: 'apos.event.name',
// The module and method targeted by the current operation (see doc module)
TARGET_NAMESPACE: 'apos.target.namespace',
TARGET_FUNCTION: 'apos.target.function',
ARGV: 'apos.argv'
};
module.exports = function (options = {}) {
if (!tracer) {
tracer = api.trace.getTracer('apostrophe', version);
// This shouldn't be needed, but having it doesn't hurt
if (options.openTelemetryProvider) {
api.trace.setGlobalTracerProvider(options.openTelemetryProvider);
}
}
/**
* Start and return a new span. Optionally provide a parent span or allow the
* parent span to be auto-detected.
* Use this when the current code block does the tracing and it doesn't expect
* more tracing to happen down the line.
*
* @param {String} name span name
* @param {api.Span|Boolean} [parentSpan] optional parent span
* @param {api.SpanOptions} [options] span options
* @returns {api.Span}
* @example
* // Auto-merge with the currently active span context
* const span = self.apos.telemetry.startSpan('event:someEvent');
* // Provide a parent span
* const span = self.apos.telemetry.startSpan('event:someEvent', parent);
* // Do not merge with any parent, start as a root
* const span = self.apos.telemetry.startSpan('event:someEvent', false);
* // Provide options for the newly created span
* const span = self.apos.telemetry.startSpan('event:someEvent', true, {
* attributes: [
* [SemanticAttributes.HTTP_METHOD]: "GET",
* [SemanticAttributes.HTTP_FLAVOR]: "1.1",
* [SemanticAttributes.HTTP_URL]: req.url
* ]
* });
*/
function startSpan(name, parentSpan, options) {
if ((!parentSpan || parentSpan === true) && parentSpan !== false) {
parentSpan = api.trace.getSpan(api.context.active());
}
if (parentSpan) {
const ctx = api.trace.setSpan(api.context.active(), parentSpan);
return tracer.startSpan(name, options || undefined, ctx);
}
return tracer.startSpan(name, options || undefined);
}
/**
* Start span and make it active for all the nested spans.
* Use this when the current code block does the tracing, but it also expects
* more tracing to happen down the line (async calls).
*
* @param {String} name span name
* @param {Function} fn handler function
* @param {api.Span|Boolean} [parentSpan] optional parent span
* @param {api.SpanOptions} [options] span options
* @returns {api.Span|any} the return value of the handler or the newly created span
* @example
* // Activate span, return some value
* const value = await self.apos.telemetry.startActiveSpan(spanName, async
* (span) => {
* // Use the span, do work, end span, return any value
* span.end();
* return value;
* });
* // Activate span, using the context of parent span, return the active span
* const span = self.apos.telemetry.startActiveSpan(spanName, async (span) => {
* // Use the span, do work, return the span
* return span;
* });
*/
function startActiveSpan(name, fn, parentSpan, options) {
if (parentSpan) {
const ctx = api.trace.setSpan(api.context.active(), parentSpan);
return tracer.startActiveSpan(name, options || undefined, ctx, fn);
}
return tracer.startActiveSpan(name, options || undefined, fn);
}
/**
* Handle errors helper (catch block).
*
* @param {api.Span} span
* @param {Error|String} err
*/
function handleError(span, err) {
span.recordException(err);
span.setStatus({
code: api.SpanStatusCode.ERROR,
message: err
? typeof err === 'string' ? err : err.message
: undefined
});
}
/**
* Stringify object - helper to properly set
* object as a span attribute value.
*
* @param {Object} obj
* @returns {String}
*/
function stringify(obj) {
return util.inspect(obj, {
depth: 20,
compact: false
});
}
return {
api,
tracer,
Attributes,
stringify,
handleError,
startSpan,
startActiveSpan
};
};