UNPKG

@atomist/automation-client

Version:

Atomist API for software low-level client

245 lines • 9.46 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const cluster = require("cluster"); const Heavy = require("heavy"); const trace = require("stack-trace"); const shutdown_1 = require("../../internal/util/shutdown"); const AutomationEventListener_1 = require("../../server/AutomationEventListener"); const logger_1 = require("../../util/logger"); const statsdClient_1 = require("./statsdClient"); const GcTypes = { 0: "unknown", 1: "scavenge", 2: "mark_sweep_compact", 3: "scavenge_and_mark_sweep_compact", 4: "incremental_marking", 8: "weak_phantom", 15: "all", }; class StatsdAutomationEventListener extends AutomationEventListener_1.AutomationEventListenerSupport { constructor(configuration) { super(); this.configuration = configuration; this.registrationName = `${this.configuration.name}/${this.configuration.version}`; this.initStatsd(); this.initGc(); } registrationSuccessful(handler) { this.increment("counter.registration"); this.event("event.registration", `New registration for ${this.registrationName}`); } contextCreated(ctx) { const context = ctx.context; const graphClient = ctx.graphClient; // On the cluster master we don't have a GraphClient if (graphClient) { const tags = [ `atomist_operation:${context.operation}`, `atomist_operation_type:command`, ]; ctx.graphClient = { endpoint: graphClient.endpoint, mutate: (optionsOrName) => { const start = Date.now(); const options = (typeof optionsOrName === "string") ? { name: optionsOrName } : optionsOrName; options.moduleDir = trace.get()[1].getFileName(); return graphClient.mutate(options) .then(result => { this.statsd.increment("counter.graphql.mutation.success", 1, 1, tags, this.callback); this.statsd.timing("timer.graphql.mutation", Date.now() - start, 1, tags, this.callback); return result; }) .catch(err => { this.statsd.increment("counter.graphql.mutation.failure", 1, 1, tags, this.callback); this.statsd.timing("timer.graphql.mutation", Date.now() - start, 1, tags, this.callback); return err; }); }, query: (optionsOrName) => { const start = Date.now(); const options = (typeof optionsOrName === "string") ? { name: optionsOrName } : optionsOrName; options.moduleDir = trace.get()[1].getFileName(); return graphClient.query(options) .then(result => { this.statsd.increment("counter.graphql.query.success", 1, 1, tags, this.callback); this.statsd.timing("timer.graphql.query", Date.now() - start, 1, tags, this.callback); return result; }) .catch(err => { this.statsd.increment("counter.graphql.query.failure", 1, 1, tags, this.callback); this.statsd.timing("timer.graphql.query", Date.now() - start, 1, tags, this.callback); return err; }); }, }; } } commandIncoming(payload) { const tags = [ `atomist_operation_type:command`, ]; this.increment("counter.operation", tags); } commandSuccessful(payload, ctx, result) { const tags = [ `atomist_operation_type:command`, ]; this.increment("counter.operation.success", tags); this.timing("timer.operation", ctx, tags); return Promise.resolve(); } commandFailed(payload, ctx, err) { const tags = [ `atomist_operation:${payload.name}`, `atomist_operation_type:command`, ]; this.increment("counter.operation.failure", tags); this.timing("timer.operation", ctx, tags); this.event("event.operation.failure", "Unsuccessfully invoked command", tags); return Promise.resolve(); } eventIncoming(payload) { const tags = [ `atomist_operation_type:event`, ]; this.increment("counter.operation", tags); } eventSuccessful(payload, ctx, result) { const tags = [ `atomist_operation_type:event`, ]; this.increment("counter.operation.success", tags); this.timing("timer.operation", ctx, tags); return Promise.resolve(); } eventFailed(payload, ctx, err) { const tags = [ `atomist_operation:${payload.extensions.operationName}`, `atomist_operation_type:event`, ]; this.increment("counter.operation.failure", tags); this.timing("timer.operation", ctx, tags); this.event("event.operation.failure", "Unsuccessfully invoked event", tags); return Promise.resolve(); } messageSent(message, dests, options, ctx) { let type; const destinations = Array.isArray(dests) ? dests : [dests]; destinations.forEach(d => { if (d.userAgent === "slack") { const sd = d; if (sd.users && sd.users.length > 0) { type = "slack_users"; } else if (sd.channels && sd.channels.length > 0) { type = "slack_channels"; } else { type = "slack_response"; } } }); this.increment("counter.message", [ `atomist_message_type:${type}`, ]); return Promise.resolve(); } /** Do-nothing callback */ callback(err) { return; } increment(stat, tags) { if (!this.statsd) { return; } if (cluster.isMaster) { this.statsd.increment(stat, 1, 1, tags, this.callback); } } event(title, text, tags) { if (!this.statsd) { return; } if (cluster.isMaster && !!this.statsd.event) { this.statsd.event(`automation_client.${title}`, text, {}, tags, this.callback); } } timing(stat, ctx, tags) { if (!this.statsd) { return; } if (cluster.isMaster && ctx && ctx.context && ctx.context.ts) { const context = ctx.context; this.statsd.timing(stat, Date.now() - context.ts, 1, tags, this.callback); } } initStatsd() { const statsdClientDefaultOptions = statsdClient_1.defaultStatsDClientOptions(this.configuration); this.statsd = this.configuration.statsd.client.factory.create(statsdClientDefaultOptions); this.timer = setInterval(() => { this.submitHeapStats(); this.submitEventLoopDelay(); }, 2500); this.timer.unref(); this.heavy = new Heavy({ sampleInterval: 1000, }); this.heavy.start(); // Register orderly shutdown shutdown_1.registerShutdownHook(() => { this.close(); return Promise.resolve(0); }, 5000, "close statsd"); this.configuration.statsd.__instance = this.statsd; } close() { this.event("event.shutdown", `Shutting down client ${this.registrationName}`); this.statsd.close(() => { logger_1.logger.debug("Closing StatsD connection"); }); clearInterval(this.timer); this.heavy.stop(); this.statsd = undefined; } submitHeapStats() { if (!this.statsd) { return; } const heap = process.memoryUsage(); this.statsd.gauge("heap.rss", heap.rss, 1, [], this.callback); this.statsd.gauge("heap.total", heap.heapTotal, 1, [], this.callback); this.statsd.gauge("heap.used", heap.heapUsed, 1, [], this.callback); } submitEventLoopDelay() { if (this.heavy && this.heavy.load && !!this.statsd) { this.statsd.timing("event_loop.delay", this.heavy.load.eventLoopDelay, 1, [], this.callback); } } initGc() { try { const gc = require("gc-stats"); gc().on("stats", stats => { if (!this.statsd) { return; } const gcType = GcTypes[stats.gctype]; const tags = [ `atomist_gc_type:${gcType}`, ]; this.statsd.increment("gc", 1, 1, tags, this.callback); this.statsd.timing("gc.duration", stats.pause / 1e9 * 1000, 1, tags, this.callback); if (stats.diff.usedHeapSize < 0) { this.statsd.gauge("gc.heap.reclaimed", stats.diff.usedHeapSize * -1, 1, tags, this.callback); } }); } catch (err) { // Ignore missing gc-stats package } } } exports.StatsdAutomationEventListener = StatsdAutomationEventListener; //# sourceMappingURL=statsd.js.map