-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Expand file tree
/
Copy pathclearcut-logger.ts
More file actions
125 lines (109 loc) · 3.14 KB
/
clearcut-logger.ts
File metadata and controls
125 lines (109 loc) · 3.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import process from 'node:process';
import {logger} from '../logger.js';
import type {LocalState, Persistence} from './persistence.js';
import {FilePersistence} from './persistence.js';
import {type FlagUsage, WatchdogMessageType, OsType} from './types.js';
import {WatchdogClient} from './watchdog-client.js';
const MS_PER_DAY = 24 * 60 * 60 * 1000;
function detectOsType(): OsType {
switch (process.platform) {
case 'win32':
return OsType.OS_TYPE_WINDOWS;
case 'darwin':
return OsType.OS_TYPE_MACOS;
case 'linux':
return OsType.OS_TYPE_LINUX;
default:
return OsType.OS_TYPE_UNSPECIFIED;
}
}
export class ClearcutLogger {
#persistence: Persistence;
#watchdog: WatchdogClient;
constructor(options: {
appVersion: string;
logFile?: string;
persistence?: Persistence;
watchdogClient?: WatchdogClient;
}) {
this.#persistence = options.persistence ?? new FilePersistence();
this.#watchdog =
options.watchdogClient ??
new WatchdogClient({
parentPid: process.pid,
appVersion: options.appVersion,
osType: detectOsType(),
logFile: options.logFile,
});
}
async logToolInvocation(args: {
toolName: string;
success: boolean;
latencyMs: number;
}): Promise<void> {
this.#watchdog.send({
type: WatchdogMessageType.LOG_EVENT,
payload: {
tool_invocation: {
tool_name: args.toolName,
success: args.success,
latency_ms: args.latencyMs,
},
},
});
}
async logServerStart(flagUsage: FlagUsage): Promise<void> {
this.#watchdog.send({
type: WatchdogMessageType.LOG_EVENT,
payload: {
server_start: {
flag_usage: flagUsage,
},
},
});
}
async logDailyActiveIfNeeded(): Promise<void> {
try {
const state = await this.#persistence.loadState();
if (this.#shouldLogDailyActive(state)) {
let daysSince = -1;
if (state.lastActive) {
const lastActiveDate = new Date(state.lastActive);
const now = new Date();
const diffTime = Math.abs(now.getTime() - lastActiveDate.getTime());
daysSince = Math.ceil(diffTime / MS_PER_DAY);
}
this.#watchdog.send({
type: WatchdogMessageType.LOG_EVENT,
payload: {
daily_active: {
days_since_last_active: daysSince,
},
},
});
state.lastActive = new Date().toISOString();
await this.#persistence.saveState(state);
}
} catch (err) {
logger('Error in logDailyActiveIfNeeded:', err);
}
}
#shouldLogDailyActive(state: LocalState): boolean {
if (!state.lastActive) {
return true;
}
const lastActiveDate = new Date(state.lastActive);
const now = new Date();
// Compare UTC dates
const isSameDay =
lastActiveDate.getUTCFullYear() === now.getUTCFullYear() &&
lastActiveDate.getUTCMonth() === now.getUTCMonth() &&
lastActiveDate.getUTCDate() === now.getUTCDate();
return !isSameDay;
}
}