@atomist/automation-client
Version:
Atomist API for software low-level client
245 lines • 9.46 kB
JavaScript
"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