moleculer
Version:
Fast & powerful microservices framework for Node.JS
166 lines (143 loc) • 4.09 kB
JavaScript
"use strict";
const _ = require("lodash");
const BaseTraceExporter = require("./base");
const { isFunction } = require("../../utils");
/**
* Import types
*
* @typedef {import("./datadog-simple")} DatadogSimpleTraceExporterClass
* @typedef {import("./datadog-simple").DatadogSimpleTraceExporterOptions} DatadogSimpleTraceExporterOptions
* @typedef {import("../tracer")} Tracer
* @typedef {import("../span")} Span
*/
/*
docker run -d --name dd-agent --restart unless-stopped -v /var/run/docker.sock:/var/run/docker.sock:ro -v /proc/:/host/proc/:ro -v /sys/fs/cgroup/:/host/sys/fs/cgroup:ro -e DD_API_KEY=123456 -e DD_APM_ENABLED=true -e DD_APM_NON_LOCAL_TRAFFIC=true -p 8126:8126 datadog/agent:latest
*/
/**
* Datadog Trace Exporter.
*
* @class DatadogSimpleTraceExporter
* @implements {DatadogSimpleTraceExporterClass}
*/
/* istanbul ignore next */
class DatadogSimpleTraceExporter extends BaseTraceExporter {
/**
* Creates an instance of DatadogSimpleTraceExporter.
* @param {DatadogSimpleTraceExporterOptions?} opts
* @memberof DatadogSimpleTraceExporter
*/
constructor(opts) {
super(opts);
/** @type {DatadogSimpleTraceExporterOptions} */
this.opts = _.defaultsDeep(this.opts, {
agentUrl: process.env.DD_AGENT_URL || "http://localhost:8126/v0.4/traces",
interval: 5,
defaultTags: null
});
this.queue = [];
}
/**
* Initialize Trace Exporter.
*
* @param {Tracer} tracer
* @memberof DatadogSimpleTraceExporter
*/
init(tracer) {
super.init(tracer);
if (this.opts.interval > 0) {
this.timer = setInterval(() => this.flush(), this.opts.interval * 1000);
this.timer.unref();
}
this.defaultTags = isFunction(this.opts.defaultTags)
? this.opts.defaultTags.call(this, tracer)
: this.opts.defaultTags;
if (this.defaultTags) {
this.defaultTags = this.flattenTags(this.defaultTags, true);
}
}
/**
* Span is finished.
*
* @param {Span} span
* @memberof DatadogSimpleTraceExporter
*/
spanFinished(span) {
this.queue.push(span);
}
/**
* Flush tracing data to Datadog server
*
* @memberof DatadogSimpleTraceExporter
*/
flush() {
if (this.queue.length === 0) return;
const data = this.generateDatadogTracingData();
this.queue.length = 0;
fetch(this.opts.agentUrl, {
method: "post",
body: JSON.stringify(data),
headers: {
"Content-Type": "application/json"
}
})
.then(res => {
this.logger.info(
`Tracing spans (${data.length} traces) are uploaded to Datadog. Status: ${res.statusText}`
);
})
.catch(err => {
this.logger.warn(
"Unable to upload tracing spans to Datadog. Error:" + err.message,
err
);
});
}
/**
* Convert traceID & spanID to number for Datadog Agent.
* @param {String} str
* @returns {Number}
*/
convertIDToNumber(str) {
if (str == null) return null;
try {
return parseInt(str.substring(0, 8), 16);
} catch {
this.logger.warn(`Unable to convert '${str}' to number.`);
return null;
}
}
/**
* Generate tracing data for Datadog
*
* @returns {Array<Object>}
* @memberof DatadogSimpleTraceExporter
*/
generateDatadogTracingData() {
const traces = this.queue.reduce((store, span) => {
const traceID = span.traceID;
const ddSpan = {
trace_id: this.convertIDToNumber(traceID),
span_id: this.convertIDToNumber(span.id),
parent_id: this.convertIDToNumber(span.parentID),
name: span.name,
resource: span.tags.action ? span.tags.action.name : null,
service: span.service ? span.service.fullName : null,
type: "custom",
start: Math.round(span.startTime * 1e6),
duration: Math.round(span.duration * 1e6),
error: span.error ? 1 : 0,
meta: Object.assign(
{},
this.defaultTags || {},
this.flattenTags(span.tags, true),
this.flattenTags(this.errorToObject(span.error), true, "error") || {}
)
};
if (!store[traceID]) store[traceID] = [ddSpan];
else store[traceID].push(ddSpan);
return store;
}, {});
return Object.values(traces);
}
}
module.exports = DatadogSimpleTraceExporter;