Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ exports[`otel-metrics > logs metrics when activated 1`] = `
{
"cli_version": "pre",
"exit": "ok",
"is_first_party": "false",
"job": "@shopify/app::app dev",
},
],
Expand All @@ -17,6 +18,7 @@ exports[`otel-metrics > logs metrics when activated 1`] = `
{
"cli_version": "pre",
"exit": "ok",
"is_first_party": "false",
"job": "@shopify/app::app dev",
},
],
Expand All @@ -26,6 +28,7 @@ exports[`otel-metrics > logs metrics when activated 1`] = `
{
"cli_version": "pre",
"exit": "ok",
"is_first_party": "false",
"job": "@shopify/app::app dev",
"stage": "active",
},
Expand All @@ -36,6 +39,7 @@ exports[`otel-metrics > logs metrics when activated 1`] = `
{
"cli_version": "pre",
"exit": "ok",
"is_first_party": "false",
"job": "@shopify/app::app dev",
"stage": "network",
},
Expand All @@ -46,6 +50,7 @@ exports[`otel-metrics > logs metrics when activated 1`] = `
{
"cli_version": "pre",
"exit": "ok",
"is_first_party": "false",
"job": "@shopify/app::app dev",
"stage": "prompt",
},
Expand All @@ -58,14 +63,16 @@ exports[`otel-metrics > outputs debug information when deactivated 1`] = `
"labels": {
"exit": "ok",
"job": "@shopify/app::app dev",
"cli_version": "nightly"
"cli_version": "nightly",
"is_first_party": "false"
}
}
[OTEL] record cli_commands_duration_ms histogram 10ms {
"labels": {
"exit": "ok",
"job": "@shopify/app::app dev",
"cli_version": "nightly"
"cli_version": "nightly",
"is_first_party": "false"
}
}
[OTEL] record cli_commands_wall_clock_elapsed_ms histogram stage="active" 10ms
Expand Down
40 changes: 39 additions & 1 deletion packages/cli-kit/src/private/node/otel-metrics.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import {recordMetrics} from './otel-metrics.js'
import {mockAndCaptureOutput} from '../../public/node/testing/output.js'
import {describe, expect, test, vi} from 'vitest'
import {afterEach, describe, expect, test, vi} from 'vitest'

describe('otel-metrics', () => {
afterEach(() => {
vi.unstubAllEnvs()
})

test('outputs debug information when deactivated', async () => {
const outputMock = mockAndCaptureOutput()

Expand Down Expand Up @@ -53,4 +57,38 @@ describe('otel-metrics', () => {
expect(mockOtelCreator).toHaveBeenCalledOnce()
expect(mockOtelRecorder.mock.calls).toMatchSnapshot()
})

test('labels metrics as first-party when the 1P dev path is enabled', async () => {
vi.stubEnv('SHOPIFY_CLI_1P_DEV', '1')
const mockOtelRecorder = vi.fn()
const mockOtelCreator = vi.fn()
mockOtelCreator.mockReturnValue({
type: 'otel',
otel: {
record: mockOtelRecorder,
},
})

await recordMetrics(
{
skipMetricAnalytics: false,
cliVersion: '3.49.1-pre.0',
owningPlugin: '@shopify/app',
command: 'app dev',
exitMode: 'ok',
},
{
active: 10,
network: 20,
prompt: 30,
},
mockOtelCreator,
)

const recordedLabels = mockOtelRecorder.mock.calls.map((call) => call[2])
expect(recordedLabels.length).toBeGreaterThan(0)
recordedLabels.forEach((labels) => {
expect(labels).toMatchObject({is_first_party: 'true'})
})
})
})
7 changes: 6 additions & 1 deletion packages/cli-kit/src/private/node/otel-metrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
DefaultOtelService,
DefaultOtelServiceOptions,
} from '../../public/node/vendor/otel-js/service/DefaultOtelService/DefaultOtelService.js'
import {isUnitTest, opentelemetryDomain} from '../../public/node/context/local.js'
import {firstPartyDev, isUnitTest, opentelemetryDomain} from '../../public/node/context/local.js'
import {ValueType, diag} from '@opentelemetry/api'

type MetricRecorder =
Expand All @@ -20,6 +20,7 @@ type Labels = {
exit: string
job: string
cli_version: string
is_first_party: string
}

interface Timing {
Expand Down Expand Up @@ -76,6 +77,10 @@ export async function recordMetrics(
exit: options.exitMode,
job: `${options.owningPlugin}::${options.command}`,
cli_version: regularisedCliVersion,
// Distinguishes Shopify first-party (1P) developers from third-party developers so the
// CLI guardrail dashboards can be scoped to 1P. Keyed off the 1P dev path
// (SHOPIFY_CLI_1P_DEV), the same signal that sets the X-Shopify-Cli-Employee header.
is_first_party: firstPartyDev() ? 'true' : 'false',
}

recordCommandCounter(recorder, labels)
Expand Down
Loading