Skip to content

Commit 8b52eb0

Browse files
committed
feat: add default analytics
1 parent 8a2a755 commit 8b52eb0

File tree

6 files changed

+90
-2
lines changed

6 files changed

+90
-2
lines changed

packages/cache/src/use-cache.ts

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import { AsyncLocalStorage } from 'node:async_hooks';
2-
import { AsyncFunction, GenericFunction } from 'commandkit';
2+
import {
3+
AsyncFunction,
4+
defer,
5+
GenericFunction,
6+
getCommandKit,
7+
} from 'commandkit';
8+
import { AnalyticsEvents } from 'commandkit/analytics';
39
import { randomUUID } from 'node:crypto';
410
import ms, { type StringValue } from 'ms';
511
import { getCacheProvider } from './cache-plugin';
@@ -90,6 +96,7 @@ function useCache<R extends any[], F extends AsyncFunction<R>>(
9096
}
9197

9298
const memo = (async (...args) => {
99+
const analytics = getCommandKit()?.analytics;
93100
const keyHash = await stableHash({
94101
fnId: metadata.id,
95102
buildId: metadata.buildId,
@@ -126,12 +133,28 @@ function useCache<R extends any[], F extends AsyncFunction<R>>(
126133
storedFn.lastAccess = Date.now();
127134
}
128135

136+
const start = performance.now();
129137
const cached = await provider.get(effectiveKey);
138+
const end = performance.now() - start;
139+
130140
if (cached && cached.value != null) {
141+
defer(() =>
142+
analytics?.track({
143+
name: AnalyticsEvents.CACHE_HIT,
144+
id: 'commandkit',
145+
data: {
146+
time: end.toFixed(2),
147+
tags: Array.from(storedFn?.tags ?? []),
148+
},
149+
}),
150+
);
151+
131152
return cached.value;
132153
}
133154

155+
const rawStart = performance.now();
134156
const result = await fn(...args);
157+
const rawEnd = performance.now() - rawStart;
135158

136159
if (result != null) {
137160
const ttl = context.params.ttl;
@@ -148,6 +171,21 @@ function useCache<R extends any[], F extends AsyncFunction<R>>(
148171
});
149172
}
150173

174+
defer(() =>
175+
analytics?.track({
176+
name: AnalyticsEvents.CACHE_MISS,
177+
id: 'commandkit',
178+
data: {
179+
time: rawEnd.toFixed(2),
180+
tags: Array.from(
181+
context.params.tags.size
182+
? context.params.tags
183+
: (storedFn?.tags ?? []),
184+
),
185+
},
186+
}),
187+
);
188+
151189
return result;
152190
},
153191
);
@@ -232,6 +270,14 @@ export async function revalidateTag(tag: string): Promise<void> {
232270

233271
// Batch delete operations for better performance
234272
await Promise.all(entries.map((entry) => provider.delete(entry.key)));
273+
274+
defer(() =>
275+
getCommandKit()?.analytics?.track({
276+
name: AnalyticsEvents.CACHE_REVALIDATED,
277+
id: 'commandkit',
278+
data: { tag },
279+
}),
280+
);
235281
}
236282

237283
/**

packages/commandkit/analytics.cjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const { getCommandKit } = require('./dist/context/async-context.js');
22
const { AnalyticsEngine } = require('./dist/analytics/analytics-engine.js');
33
const { noAnalytics } = require('./dist/analytics/utils.js');
4+
const { AnalyticsEvents } = require('./dist/analytics/constants.js');
45

56
function useAnalytics() {
67
const commandkit = getCommandKit(true);
@@ -12,6 +13,7 @@ function track(event) {
1213
}
1314

1415
module.exports = {
16+
AnalyticsEvents,
1517
AnalyticsEngine,
1618
useAnalytics,
1719
noAnalytics,

packages/commandkit/analytics.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { AnalyticsEngine } from './dist/analytics/analytics-engine';
33
export * from './dist/analytics/analytics-engine';
44
export * from './dist/analytics/analytics-provider';
55
export { noAnalytics } from './dist/analytics/utils';
6+
export { AnalyticsEvents } from './dist/analytics/constants';
67

78
export function useAnalytics(): AnalyticsEngine;
89
export function track(event: AnalyticsEvent): Promise<void>;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
11
import { randomUUID } from 'node:crypto';
22

33
export const DO_NOT_TRACK_KEY = `COMMANDKIT_ANALYTICS__DO_NOT_TRACK::${randomUUID()}`;
4+
5+
export const AnalyticsEvents = {
6+
COMMAND_EXECUTION: 'command_execution',
7+
// @commandkit/cache
8+
CACHE_HIT: 'cache_hit',
9+
CACHE_MISS: 'cache_miss',
10+
CACHE_REVALIDATED: 'cache_revalidated',
11+
} as const;
12+
13+
export type AnalyticsEvent =
14+
(typeof AnalyticsEvents)[keyof typeof AnalyticsEvents];

packages/commandkit/src/app/commands/AppCommandRunner.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
useEnvironment,
1717
} from '../../context/async-context';
1818
import { CommandKitErrorCodes, isErrorType } from '../../utils/error-codes';
19+
import { AnalyticsEvents } from '../../analytics/constants';
1920

2021
export class AppCommandRunner {
2122
public constructor(private handler: AppCommandHandler) {}
@@ -94,12 +95,14 @@ export class AppCommandRunner {
9495
);
9596
}
9697

98+
const analytics = commandkit.analytics;
99+
97100
if (fn) {
98101
try {
99102
const _executeCommand = makeContextAwareFunction(
100103
env,
101104
async () => {
102-
env.registerDeferredFunction((env) => {
105+
env.registerDeferredFunction(async (env) => {
103106
env.markEnd();
104107
const error = env.getExecutionError();
105108
const marker = env.getMarker();
@@ -110,12 +113,36 @@ export class AppCommandRunner {
110113
`[${marker} - ${time}] Error executing command: ${error.stack || error}`,
111114
);
112115

116+
await analytics.track({
117+
name: AnalyticsEvents.COMMAND_EXECUTION,
118+
id:
119+
prepared.command?.data?.command?.name ??
120+
prepared.command.command.name,
121+
data: {
122+
error: true,
123+
executionTime: env.getExecutionTime().toFixed(2),
124+
type: executionMode,
125+
},
126+
});
127+
113128
return;
114129
}
115130

116131
Logger.info(
117132
`[${marker} - ${time}] Command executed successfully`,
118133
);
134+
135+
await analytics.track({
136+
name: AnalyticsEvents.COMMAND_EXECUTION,
137+
id:
138+
prepared.command?.data?.command?.name ??
139+
prepared.command.command.name,
140+
data: {
141+
error: false,
142+
executionTime: env.getExecutionTime().toFixed(2),
143+
type: executionMode,
144+
},
145+
});
119146
});
120147

121148
return fn(ctx.clone());

packages/commandkit/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export {
2323
debounce,
2424
stopEvents,
2525
StopEventPropagationError,
26+
defer,
2627
} from './utils/utilities';
2728
export type { CommandKitHMREvent } from './utils/dev-hooks';
2829
export * from './utils/constants';

0 commit comments

Comments
 (0)