@shopify/cli-kit
Version:
A set of utilities, interfaces, and models that are common across all the platform features
137 lines • 5.32 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 { ValueType, diag } 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);
}
const COMMAND_DURATION_BOUNDARIES_MS = [
0, 100, 200, 300, 500, 750, 1000, 1500, 2000, 3000, 5000, 7500, 10000, 15000, 20000, 30000, 50000, 70000,
85000, 100000,
];
/**
* 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: COMMAND_DURATION_BOUNDARIES_MS,
},
[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: COMMAND_DURATION_BOUNDARIES_MS,
},
},
};
}
/**
* Create the metric recorder for this command.
*
* If metric logging is disabled, or we are running in a unit test, we record to the console.
*
*/
function createMetricRecorder(options) {
let recorder = 'console';
if (!(options.skipMetricAnalytics || isUnitTest())) {
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`,
});
// Suppress OTEL diagnostic output — internal export errors (e.g. retryable failures)
// should never appear in user-facing CLI output.
diag.disable();
}
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