@shopify/cli-kit
Version:
A set of utilities, interfaces, and models that are common across all the platform features
131 lines • 5.12 kB
JavaScript
import { MetricInstrumentType } from '../../public/node/vendor/otel-js/service/types.js';
import { outputContent, outputDebug, outputToken } from '../../public/node/output.js';
import { DefaultOtelService, } from '../../public/node/vendor/otel-js/service/DefaultOtelService/DefaultOtelService.js';
import { isUnitTest, opentelemetryDomain } from '../../public/node/context/local.js';
import { isSpinEnvironment } from '../../public/node/context/spin.js';
import { ValueType } from '@opentelemetry/api';
var Name;
(function (Name) {
Name["Counter"] = "cli_commands_total";
Name["Duration"] = "cli_commands_duration_ms";
Name["Elapsed"] = "cli_commands_wall_clock_elapsed_ms";
})(Name || (Name = {}));
/**
* Record reliability metrics.
*/
export async function recordMetrics(options, timing, recorderFactory = createMetricRecorder) {
const recorder = recorderFactory({
skipMetricAnalytics: options.skipMetricAnalytics,
otelOptions: defaultOtelOptions(),
});
let regularisedCliVersion = options.cliVersion;
if (options.cliVersion.includes('nightly')) {
regularisedCliVersion = 'nightly';
}
else if (options.cliVersion.includes('pre')) {
regularisedCliVersion = 'pre';
}
const labels = {
exit: options.exitMode,
job: `${options.owningPlugin}::${options.command}`,
cli_version: regularisedCliVersion,
};
recordCommandCounter(recorder, labels);
recordCommandTiming(recorder, labels, timing);
}
/**
* Get the default options for the OTEL service. These are the same across environments.
*/
function defaultOtelOptions() {
return {
serviceName: 'shopify-cli',
throttleLimit: 1000,
prefixMetric: false,
metrics: {
[Name.Counter]: {
type: MetricInstrumentType.Counter,
description: 'Total number of CLI commands executed',
valueType: ValueType.INT,
},
[Name.Duration]: {
type: MetricInstrumentType.Histogram,
description: 'Total time spent in execution of CLI commands. Does not include time spent waiting for network, prompts, etc.',
valueType: ValueType.INT,
boundaries: [0, 100, 250, 500, 1000, 2000, 5000, 10000, 20000, 50000],
},
[Name.Elapsed]: {
type: MetricInstrumentType.Histogram,
description: 'Total time elapsed from start to finish of CLI commands. Includes time spent waiting for network, prompts, etc.',
valueType: ValueType.INT,
boundaries: [0, 100, 250, 500, 1000, 2000, 5000, 10000, 20000, 50000],
},
},
};
}
/**
* Create the metric recorder for this command.
*
* If metric logging is disabled, or we are running in a unit test or Spin, we record to the console.
*
*/
function createMetricRecorder(options) {
let recorder = 'console';
if (!(options.skipMetricAnalytics || isUnitTest() || isSpinEnvironment())) {
recorder = {
type: 'otel',
otel: globalOtelService(options),
};
}
return recorder;
}
let _otelService;
/**
* OTEL service singleton.
*
* The service is a singleton as it uses a global diagnostic logger that assumes its the only one in the process.
*/
function globalOtelService(options) {
if (!_otelService) {
_otelService = new DefaultOtelService({
...options.otelOptions,
env: undefined,
otelEndpoint: `${opentelemetryDomain()}/v1/metrics`,
});
}
return _otelService;
}
/**
* Log command counter metrics.
*/
function recordCommandCounter(recorder, labels) {
if (recorder === 'console') {
outputDebug(outputContent `[OTEL] record ${Name.Counter} counter ${outputToken.json({ labels })}`);
return;
}
recorder.otel.record(Name.Counter, 1, labels);
}
/**
* Log command timing metrics.
*/
function recordCommandTiming(recorder, labels, timing) {
if (recorder === 'console') {
outputDebug(outputContent `[OTEL] record ${Name.Duration} histogram ${timing.active.toString()}ms ${outputToken.json({
labels,
})}`);
outputDebug(outputContent `[OTEL] record ${Name.Elapsed} histogram stage="active" ${timing.active.toString()}ms`);
outputDebug(outputContent `[OTEL] record ${Name.Elapsed} histogram stage="network" ${timing.network.toString()}ms`);
outputDebug(outputContent `[OTEL] record ${Name.Elapsed} histogram stage="prompt" ${timing.prompt.toString()}ms`);
return;
}
if (timing.active > 0) {
recorder.otel.record(Name.Duration, timing.active, labels);
recorder.otel.record(Name.Elapsed, timing.active, { ...labels, stage: 'active' });
}
if (timing.network > 0) {
recorder.otel.record(Name.Elapsed, timing.network, { ...labels, stage: 'network' });
}
if (timing.prompt > 0) {
recorder.otel.record(Name.Elapsed, timing.prompt, { ...labels, stage: 'prompt' });
}
}
//# sourceMappingURL=otel-metrics.js.map