UNPKG

vulcain-corejs

Version:
255 lines 10.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const requestContext_1 = require("../pipeline/requestContext"); const system_1 = require("../globals/system"); const annotations_1 = require("../di/annotations"); const common_1 = require("./../pipeline/common"); const common_2 = require("./common"); const os = require("os"); // Metrics use the RED method https://www.weave.works/blog/prometheus-and-kubernetes-monitoring-your-applications/ class Span { constructor(context, kind, name, parentId) { this.context = context; this.kind = kind; this.name = name; this.tags = {}; this.commandType = "Custom"; this._logger = context.container.get(annotations_1.DefaultServiceNames.Logger); this.startTime = Date.now(); this.startTick = process.hrtime(); this._id = { correlationId: parentId.correlationId, spanId: !parentId.spanId ? parentId.correlationId : this.randomTraceId(), parentId: parentId.spanId }; this.metrics = context.container.get(annotations_1.DefaultServiceNames.Metrics); this.addTag("name", name); this.addTag("domain", system_1.Service.domainName); this.addTag("host", os.hostname()); this.addTag("correlationId", parentId.correlationId); this.convertKind(); } get id() { return this._id; } get tracker() { return this._tracker; } static createRequestTracker(context, parentId) { return new Span(context, common_2.SpanKind.Request, system_1.Service.fullServiceName, parentId); } createCommandTracker(context, commandName) { return new Span(context, common_2.SpanKind.Command, commandName, this._id); } createCustomTracker(context, name, tags) { let span = new Span(context, common_2.SpanKind.Custom, name, this._id); span.trackAction(name, tags); return span; } trackAction(action, tags) { if (!action || action.startsWith('_')) return; this.action = action; let trackerFactory = this.context.container.get(annotations_1.DefaultServiceNames.RequestTracker, true); if (trackerFactory) { this._tracker = trackerFactory.startSpan(this, this.name, this.action); } this.addTag("action", action); tags && this.addTags(tags); this.logAction("Log", `...Action : ${action}, ${(tags && JSON.stringify(tags)) || ""}`); if (this.kind === common_2.SpanKind.Command) { this.addTag("span.kind", "client"); this.addTag("type", "Command"); this.logAction("BC", `Command: ${this.name}`); } else if (this.kind === common_2.SpanKind.Request) { this.addTag("span.kind", "server"); this.addTag("type", "Service"); this.logAction("RR", `Request: ${this.name}`); } else if (this.kind === common_2.SpanKind.Task) { this.addTag("span.kind", "server"); this.addTag("type", "Task"); this.logAction("RT", `Async task: ${this.name}`); } else if (this.kind === common_2.SpanKind.Event) { this.addTag("span.kind", "consumer"); this.addTag("type", "Event"); this.logAction("RE", `Event: ${this.name}`); } } convertKind() { if (this.kind === common_2.SpanKind.Command) return; if (this.context.pipeline === common_1.Pipeline.AsyncTask) { this.kind = common_2.SpanKind.Task; this.name = "Async:" + this.name; } else if (this.context.pipeline === common_1.Pipeline.Event) { this.kind = common_2.SpanKind.Event; this.name = "Event:" + this.name; } } injectHeaders(headers) { headers(requestContext_1.VulcainHeaderNames.X_VULCAIN_PARENT_ID, this._id.spanId); headers(requestContext_1.VulcainHeaderNames.X_VULCAIN_CORRELATION_ID, this._id.correlationId); // TODO move this code if (this.context.request.headers[requestContext_1.VulcainHeaderNames.X_VULCAIN_REGISTER_STUB]) { headers(requestContext_1.VulcainHeaderNames.X_VULCAIN_REGISTER_STUB, this.context.request.headers[requestContext_1.VulcainHeaderNames.X_VULCAIN_REGISTER_STUB]); } if (this.context.request.headers[requestContext_1.VulcainHeaderNames.X_VULCAIN_USE_STUB]) { headers(requestContext_1.VulcainHeaderNames.X_VULCAIN_USE_STUB, this.context.request.headers[requestContext_1.VulcainHeaderNames.X_VULCAIN_USE_STUB]); } } dispose() { this.addTag("tenant", this.context.user.tenant); if (this.kind === common_2.SpanKind.Command) this.endCommand(); else this.endRequest(); if (this._tracker) { Object.keys(this.tags).forEach(key => this._tracker.addTag(key, this.tags[key])); this._tracker.finish(); this._tracker = null; } this.context = null; this._logger = null; } endCommand() { if (this.action) { this.addTag("error", this.error ? this.error.toString() : "false"); let metricsName = `vulcain_${this.commandType.toLowerCase()}command_duration_ms`; this.metrics.timing(metricsName, this.durationInMs, this.tags); } // End Command trace this._logger && this._logger.logAction(this.context, "EC", `Command: ${this.name} completed with ${this.error ? this.error.message : 'success'}`); } endRequest() { if (this.action) { if (!this.error && this.kind === common_2.SpanKind.Request && this.context.response && this.context.response.statusCode && this.context.response.statusCode >= 400) { this.error = new Error("Http error " + this.context.response.statusCode); } this.addTag("error", this.error ? this.error.toString() : "false"); this.metrics.timing("vulcain_service_duration_ms", this.durationInMs, this.tags); } if (this.kind === common_2.SpanKind.Request) { this.logAction("ER", `End request status: ${(this.context.response && this.context.response.statusCode) || 200}`); } else if (this.kind === common_2.SpanKind.Task) { this.logAction("ET", `Async task: ${this.name} completed with ${this.error ? this.error.message : 'success'}`); } else if (this.kind === common_2.SpanKind.Event) { this.logAction("EE", `Event ${this.name} completed with ${this.error ? this.error.message : 'success'}`); } } addHttpRequestTags(uri, verb) { this.commandType = "Http"; this.addTag("http.url", uri); this.addTag("http.method", verb); // http.status_code } addProviderCommandTags(address, schema, tenant) { this.commandType = "Database"; this.addTag("db.instance", system_1.Service.removePasswordFromUrl(address)); this.addTag("db.schema", schema); this.addTag("db.tenant", tenant); } addServiceCommandTags(serviceName, serviceVersion) { this.commandType = "Service"; this.addTag("peer.address", system_1.Service.createContainerEndpoint(serviceName, serviceVersion)); this.addTag("peer.service", "vulcain"); this.addTag("peer.service_version", serviceVersion); this.addTag("peer.service_name", serviceName); } addCustomCommandTags(commandType, tags) { this.commandType = commandType; this.addTags(tags); } addTag(name, value) { if (name && value) { try { if (typeof value === "object") { value = JSON.stringify(value); } this.tags[name] = value; } catch (e) { this.context.logError(e); // then ignore } } } addTags(tags) { if (!tags) return; Object.keys(tags) .forEach(key => this.addTag(key, tags[key])); } logAction(action, message) { this._logger.logAction(this.context, action, message); } /** * Log an error * * @param {Error} error Error instance * @param {string} [msg] Additional message * */ logError(error, msg) { if (!this.error) { this.error = error; // Catch only first error if (this._tracker) { this._tracker.trackError(error, msg && msg()); } } this._logger.error(this.context, error, msg); } /** * Log a message info * * @param {string} msg Message format (can include %s, %j ...) * @param {...Array<string>} params Message parameters * */ logInfo(msg) { this._logger.info(this.context, msg); if (this._tracker) { this._tracker.log(msg()); } } /** * Log a verbose message. Verbose message are enable by service configuration property : enableVerboseLog * * @param {any} context Current context * @param {string} msg Message format (can include %s, %j ...) * @param {...Array<string>} params Message parameters * */ logVerbose(msg) { const ok = this._logger.verbose(this.context, msg); if (ok && this._tracker) { this._tracker.log(msg()); } } get now() { return this.startTime + this.durationInMs; } get durationInMs() { const endTime = process.hrtime(); const secondDiff = endTime[0] - this.startTick[0]; const nanoSecondDiff = endTime[1] - this.startTick[1]; const diffInNanoSecond = secondDiff * 1e9 + nanoSecondDiff; return diffInNanoSecond / 1e6; } randomTraceId() { const digits = '0123456789abcdef'; let n = ''; for (let i = 0; i < 16; i++) { const rand = Math.floor(Math.random() * 16); n += digits[rand]; } return n; } } exports.Span = Span; //# sourceMappingURL=span.js.map