1use std::time::Duration;
6use tracing::{debug, error, info, warn};
7
8#[derive(Debug, Clone)]
10pub enum WorktreeEvent {
11 CreateStart {
13 worktree_id: String,
14 branch_name: String,
15 },
16 CreateComplete {
18 worktree_id: String,
19 duration: Duration,
20 },
21 ExecuteStart {
23 worktree_id: String,
24 agent_type: String,
25 },
26 ExecuteComplete {
28 worktree_id: String,
29 duration: Duration,
30 success: bool,
31 },
32 CleanupStart { worktree_id: String },
34 CleanupComplete {
36 worktree_id: String,
37 duration: Duration,
38 },
39 Error {
41 worktree_id: String,
42 error_message: String,
43 },
44}
45
46pub struct TelemetryCollector {
48 events: Vec<WorktreeEvent>,
49}
50
51impl TelemetryCollector {
52 pub fn new() -> Self {
54 Self { events: Vec::new() }
55 }
56
57 pub fn record(&mut self, event: WorktreeEvent) {
59 match &event {
61 WorktreeEvent::CreateStart {
62 worktree_id,
63 branch_name,
64 } => {
65 info!(
66 worktree_id = %worktree_id,
67 branch = %branch_name,
68 "Worktree作成開始"
69 );
70 },
71 WorktreeEvent::CreateComplete {
72 worktree_id,
73 duration,
74 } => {
75 info!(
76 worktree_id = %worktree_id,
77 duration_ms = duration.as_millis(),
78 "Worktree作成完了"
79 );
80 },
81 WorktreeEvent::ExecuteStart {
82 worktree_id,
83 agent_type,
84 } => {
85 info!(
86 worktree_id = %worktree_id,
87 agent = %agent_type,
88 "Agent実行開始"
89 );
90 },
91 WorktreeEvent::ExecuteComplete {
92 worktree_id,
93 duration,
94 success,
95 } => {
96 if *success {
97 info!(
98 worktree_id = %worktree_id,
99 duration_ms = duration.as_millis(),
100 "Agent実行成功"
101 );
102 } else {
103 warn!(
104 worktree_id = %worktree_id,
105 duration_ms = duration.as_millis(),
106 "Agent実行失敗"
107 );
108 }
109 },
110 WorktreeEvent::CleanupStart { worktree_id } => {
111 debug!(worktree_id = %worktree_id, "クリーンアップ開始");
112 },
113 WorktreeEvent::CleanupComplete {
114 worktree_id,
115 duration,
116 } => {
117 debug!(
118 worktree_id = %worktree_id,
119 duration_ms = duration.as_millis(),
120 "クリーンアップ完了"
121 );
122 },
123 WorktreeEvent::Error {
124 worktree_id,
125 error_message,
126 } => {
127 error!(
128 worktree_id = %worktree_id,
129 error = %error_message,
130 "Worktreeエラー"
131 );
132 },
133 }
134
135 self.events.push(event);
136 }
137
138 pub fn events(&self) -> &[WorktreeEvent] {
140 &self.events
141 }
142
143 pub fn generate_stats(&self) -> TelemetryStats {
145 let mut stats = TelemetryStats::default();
146
147 for event in &self.events {
148 match event {
149 WorktreeEvent::CreateComplete { .. } => stats.creates += 1,
150 WorktreeEvent::ExecuteComplete {
151 success, duration, ..
152 } => {
153 stats.executions += 1;
154 if *success {
155 stats.successful_executions += 1;
156 } else {
157 stats.failed_executions += 1;
158 }
159 stats.total_execution_time += *duration;
160 },
161 WorktreeEvent::CleanupComplete { .. } => stats.cleanups += 1,
162 WorktreeEvent::Error { .. } => stats.errors += 1,
163 _ => {},
164 }
165 }
166
167 stats
168 }
169
170 pub fn generate_report(&self) -> String {
172 let stats = self.generate_stats();
173 format!(
174 "📊 Worktree実行レポート\n\
175 - 作成: {}回\n\
176 - 実行: {}回(成功: {}, 失敗: {})\n\
177 - クリーンアップ: {}回\n\
178 - エラー: {}回\n\
179 - 平均実行時間: {:.2}秒\n\
180 - 成功率: {:.1}%",
181 stats.creates,
182 stats.executions,
183 stats.successful_executions,
184 stats.failed_executions,
185 stats.cleanups,
186 stats.errors,
187 stats.average_execution_time().as_secs_f64(),
188 stats.success_rate()
189 )
190 }
191}
192
193impl Default for TelemetryCollector {
194 fn default() -> Self {
195 Self::new()
196 }
197}
198
199#[derive(Debug, Clone, Default)]
201pub struct TelemetryStats {
202 pub creates: usize,
204 pub executions: usize,
205 pub successful_executions: usize,
207 pub failed_executions: usize,
208 pub cleanups: usize,
210 pub errors: usize,
211 pub total_execution_time: Duration,
213}
214
215impl TelemetryStats {
216 pub fn average_execution_time(&self) -> Duration {
218 if self.executions == 0 {
219 Duration::ZERO
220 } else {
221 self.total_execution_time / self.executions as u32
222 }
223 }
224
225 pub fn success_rate(&self) -> f64 {
227 if self.executions == 0 {
228 0.0
229 } else {
230 (self.successful_executions as f64 / self.executions as f64) * 100.0
231 }
232 }
233}
234
235#[cfg(test)]
236mod tests {
237 use super::*;
238
239 #[test]
240 fn test_telemetry_collector_creation() {
241 let collector = TelemetryCollector::new();
242 assert_eq!(collector.events().len(), 0);
243 }
244
245 #[test]
246 fn test_record_event() {
247 let mut collector = TelemetryCollector::new();
248 collector.record(WorktreeEvent::CreateStart {
249 worktree_id: "test-1".to_string(),
250 branch_name: "feature/test".to_string(),
251 });
252 assert_eq!(collector.events().len(), 1);
253 }
254
255 #[test]
256 fn test_generate_stats() {
257 let mut collector = TelemetryCollector::new();
258
259 collector.record(WorktreeEvent::CreateComplete {
260 worktree_id: "test-1".to_string(),
261 duration: Duration::from_secs(1),
262 });
263
264 collector.record(WorktreeEvent::ExecuteComplete {
265 worktree_id: "test-1".to_string(),
266 duration: Duration::from_secs(2),
267 success: true,
268 });
269
270 let stats = collector.generate_stats();
271 assert_eq!(stats.creates, 1);
272 assert_eq!(stats.executions, 1);
273 assert_eq!(stats.successful_executions, 1);
274 assert_eq!(stats.success_rate(), 100.0);
275 }
276
277 #[test]
278 fn test_generate_report() {
279 let mut collector = TelemetryCollector::new();
280
281 collector.record(WorktreeEvent::CreateComplete {
282 worktree_id: "test-1".to_string(),
283 duration: Duration::from_secs(1),
284 });
285
286 let report = collector.generate_report();
287 assert!(report.contains("作成: 1回"));
288 }
289
290 #[test]
291 fn test_telemetry_stats_average() {
292 let stats = TelemetryStats {
293 executions: 2,
294 total_execution_time: Duration::from_secs(10),
295 ..Default::default()
296 };
297
298 assert_eq!(stats.average_execution_time(), Duration::from_secs(5));
299 }
300}