@cran/gql.koa
Version:
Cran/GraphQL Koa Server
108 lines (107 loc) • 3.94 kB
JavaScript
/* eslint-disable max-lines */
import { SpanStatusCode } from "@opentelemetry/api";
import { isatty } from "tty";
import { snakeCase } from "lodash";
import { Counter, Histogram, Summary, collectDefaultMetrics } from "prom-client";
import { ExportResultCode, hrTimeToMilliseconds } from "@opentelemetry/core";
export class MetricSpanExporter {
metrics = {};
config;
constructor(config) {
collectDefaultMetrics({ prefix: config.prefix, });
this.config = {
prefix: "",
trace: MetricSpanExporter.defaultTracer,
...config,
relabel: {
...MetricSpanExporter.defaultRelabel,
...config.relabel,
},
};
}
export(spans, done) {
return void this.send(spans, done);
}
async shutdown() {
this.send([]);
}
format(span) {
return {
ok: span.status.code !== SpanStatusCode.ERROR,
name: span.name,
timestamp: hrTimeToMilliseconds(span.startTime),
duration: hrTimeToMilliseconds(span.duration),
attributes: span.attributes,
};
}
send(spans, done) {
for (const span of spans) {
const result = this.format(span);
this.config.trace(result);
this.metric(result.name, result.duration, span);
}
if (done) {
return void done({ code: ExportResultCode.SUCCESS, });
}
}
metric(name, value, span) {
const config = { library: span.instrumentationLibrary.name, name, };
const relabel = this.config.relabel[config.name] ||
this.config.relabel[config.library];
if (!relabel) {
return;
}
const labels = relabel(span.attributes, config);
if (!labels) {
return;
}
const metric = this.metrics[name] || (this.metrics[name] = this.newMetric(config, Object.keys(labels)));
metric.counter.inc(labels);
metric.histogram.observe(labels, value);
metric.summary.observe(labels, value);
}
newMetric({ library, name, }, labelNames) {
const metricName = this.config.prefix + snakeCase(name);
const help = `${name} (${library})`;
return {
counter: new Counter({ name: `${metricName}_counter`, help, labelNames, }),
summary: new Summary({ name: `${metricName}_summary`, help, labelNames, }),
histogram: new Histogram({
name: `${metricName}_histogram`,
help,
labelNames,
buckets: [0.1, 0.25, 0.5, 1, 2.5, 5, 7,],
}),
};
}
}
(function (MetricSpanExporter) {
MetricSpanExporter.ttyPrint = isatty(process.stdout.fd)
? function identity(value) { return value; }
: JSON.stringify.bind(JSON);
function defaultTracer(value) {
// eslint-disable-next-line no-console
console.log(MetricSpanExporter.ttyPrint(value));
}
MetricSpanExporter.defaultTracer = defaultTracer;
function relabelAll(attributes) {
return Object.entries(attributes).reduce(function reduceLabel(acc, [key, val,]) {
acc[snakeCase(key)] = String(val);
return acc;
}, {});
}
MetricSpanExporter.relabelAll = relabelAll;
MetricSpanExporter.defaultRelabel = {
"@opentelemetry/instrumentation-koa": relabelAll,
"middleware - allowedMethods"() { return null; },
"middleware - "() { return null; },
"@opentelemetry/instrumentation-http"(attributes) {
return {
route: attributes["http.route"],
status_code: attributes["http.status_code"],
};
},
"@opentelemetry/instrumentation-graphql"() { return {}; },
"graphql.resolve"() { return null; },
};
})(MetricSpanExporter || (MetricSpanExporter = {}));