UNPKG

@chankamlam/express-jaeger

Version:

Jaeger middleware to request tracing for express application

292 lines (248 loc) 7.41 kB
const { initTracer } = require("jaeger-client"); const { FORMAT_HTTP_HEADERS, Tags } = require("opentracing"); const axios = require("axios") const process = require("process") const TAGS = { ...Tags, "PROTOCAL": "protocal", "TRACING_TAG": "tracing-tag", } const UNKNOW = 'Unknow' const STRING = 'string' const DEFAULT_SAMPLER = { type: "const", param: 1 }; const DEFAULT_REPORTER = { collectorEndpoint: "http://localhost:14268/api/traces", }; const DEFAULT_LOGGER = { info: msg => { console.log("JAEGER INFO ", msg); }, error: msg => { console.log("JAEGER ERROR", msg); } }; const DEFAULT_OPTIONS = { logger: DEFAULT_LOGGER }; const DAFAULT_CONFIG = { serviceName: UNKNOW, reporter: DEFAULT_REPORTER, sampler: DEFAULT_SAMPLER }; // tracer instance var tracer = null // master span instance var span = null // tracing tag form request and will send to remote call var tracing_tag = {} /** * Inject tracing tag to headers for remote call * @param {*} opts */ const injectSpan = (opts = undefined) => { var headers = {} headers[TAGS.TRACING_TAG] = JSON.stringify(tracing_tag) tracer.inject(span, FORMAT_HTTP_HEADERS, headers); if (opts == undefined) { opts = { headers } } else if (typeof (opts) == STRING) { opts = { headers, url: opts } } else { opts.headers = { ...opts.headers, ...headers } } return opts } /** * print log * @param {string} title * @param {string} msg */ const printMsg = function (title, msg) { console.log(`=>${title}:`) console.log(msg) } /** * create a jaeger instance */ const createJaegerInstance = () => { return { // span instance span, // tracer instance tracer, // TAGS of opentracing tags: TAGS, // use for remote call axios: ((opts = undefined) => { return (function () { let obj = function (opts) { // handle tracing header var options = injectSpan(opts) return axios(options) } for (const key in axios) { if (axios.hasOwnProperty(key)) { const element = axios[key]; obj[key] = function () { if (arguments.length == 0) { arguments = injectSpan({}) } else if (arguments.length == 1 && typeof (arguments[0]) == STRING) { arguments[1] = injectSpan(arguments[0]) } else if (arguments.length == 1 && typeof (arguments[0]) != STRING) { arguments[0] = injectSpan(arguments[0]) } else if (arguments.length == 2) { arguments[1].url = arguments[0] arguments[1] = injectSpan(arguments[1]) } else if (arguments.length == 3) { arguments[2].data = arguments[1] arguments[2].url = arguments[0] arguments[2] = injectSpan(arguments[2]) } return element.apply(null, Array.prototype.slice.call(arguments)) } } } return obj })() })(), // log log: (name, content) => { if (!span) return span.logEvent(name, content) }, // setup tag setTag: (tag, val) => { if (!span) return span.setTag(tag, val) }, // setup mutiple TAGS addTags: (obj) => { if (!span || !obj) return span.addTags(obj) }, // setup tracing tag which can pass through all remote call by using Jaeger.request setTracingTag: (tag, val) => { if (!span) return span.setTag(tag, val) tracing_tag[tag] = val }, // finish span job finish: () => { if (!span) return span.finish() }, // create new span under master span createSpan: (name, parent) => { const parentObj = parent ? { childOf: parent } : { childOf: this.span }; if (!tracer) return return tracer.startSpan(name, parentObj) } } } /** * init tracer */ const initiateTracer = (cfg = {}, opt = {}) => { if (!tracer) { const config = { ...DAFAULT_CONFIG, ...cfg } const options = { ...DEFAULT_OPTIONS, ...opt } tracer = initTracer(config, options); } } var Jaeger = (cfg = {}, opt = {}, cb = undefined) => { // initiateTracer(cfg, opt) //////////////////////////////////////////////////////////////////// /// /// SPECIAL CASE HANDLE /// /////////////////////////////////////////////////////////////////// // pass for CI if (process.env && process.env["CI_RUNNER_ID"]) { return (req, res, next) => { next() } } // for using in directive if (!cfg || !opt || Object.keys(cfg) < 1 || Object.keys(opt) < 1) { return createJaegerInstance() } //////////////////////////////////////////////////////////////////// // for using in express return (req, res, next) => { initiateTracer(cfg, opt) // check exclude array if (opt && opt["excludePath"] && opt["excludePath"] instanceof Array) { let arr = opt["excludePath"] let isPass = false for (let i = 0; i < arr.length; i++) { const path = arr[i]; const regex = new RegExp(path) if (regex.test(req.path)) { isPass = true break } } if (isPass) { next() return } } //////////////////////////////////////////////////// // extract parent span from headers of request const extractSpan = () => { if (!tracer) { throw new Error(`[*]Initiate tracer... [FAILED]`) } var parent = tracer.extract(FORMAT_HTTP_HEADERS, req.headers); parent = parent ? { childOf: parent } : {}; span = tracer.startSpan(req.path, parent); } extractSpan() //////////////////////////////////////////////////////////////// // extract tracing tag from headers of request const extractTracingTag = () => { if (!span) { throw new Error(`[*]Initiate master span... [FAILED]`) } if (req.headers && req.headers[TAGS.TRACING_TAG]) { tracing_tag = JSON.parse(req.headers[TAGS.TRACING_TAG]) } for (const key in tracing_tag) { const val = tracing_tag[key]; span.setTag(key, val); } } extractTracingTag() /////////////////////////////////////////////// // binding jaeger instance to req req.jaeger = createJaegerInstance() // handle callback function which open to programer if (cb) { cb(req, res) } /////////////////////////////////////////////////// // mark default tag of request req.jaeger.setTag("request.ip", req.ip || UNKNOW) req.jaeger.setTag("request.method", req.method || UNKNOW) req.jaeger.setTag("request.headers", req.headers || UNKNOW) req.jaeger.setTag("request.path", req.path || UNKNOW) req.jaeger.setTag("request.body", req.body || UNKNOW) req.jaeger.setTag("request.query", req.query || UNKNOW) ///////////////////////////////////////////////////// next(); // auto finish for master span res.once("finish", () => { //mark default tag of response req.jaeger.setTag("response.state", res.statusCode || UNKNOW); req.jaeger.setTag("response.result", res.statusMessage || UNKNOW); req.jaeger.finish() }); ///////////////////////////////////////////////////// } } module.exports = Jaeger