1
1
use std:: { collections:: HashMap , env} ;
2
2
3
3
use but_settings:: AppSettings ;
4
+ use posthog_rs:: Client ;
4
5
use serde:: { Deserialize , Serialize } ;
5
6
7
+ use crate :: args:: MetricsCommandName ;
8
+
6
9
#[ derive( Debug , Clone ) ]
7
10
pub struct Metrics {
8
11
sender : Option < tokio:: sync:: mpsc:: UnboundedSender < Event > > ,
@@ -13,7 +16,29 @@ pub struct Metrics {
13
16
pub enum EventKind {
14
17
Mcp ,
15
18
McpInternal ,
19
+ CliLog ,
20
+ CliStatus ,
21
+ CliRub ,
22
+ ClaudePreTool ,
23
+ ClaudePostTool ,
24
+ ClaudeStop ,
25
+ Unknown ,
26
+ }
27
+
28
+ impl From < MetricsCommandName > for EventKind {
29
+ fn from ( command_name : MetricsCommandName ) -> Self {
30
+ match command_name {
31
+ MetricsCommandName :: Log => EventKind :: CliLog ,
32
+ MetricsCommandName :: Status => EventKind :: CliStatus ,
33
+ MetricsCommandName :: Rub => EventKind :: CliRub ,
34
+ MetricsCommandName :: ClaudePreTool => EventKind :: ClaudePreTool ,
35
+ MetricsCommandName :: ClaudePostTool => EventKind :: ClaudePostTool ,
36
+ MetricsCommandName :: ClaudeStop => EventKind :: ClaudeStop ,
37
+ _ => EventKind :: Unknown ,
38
+ }
39
+ }
16
40
}
41
+
17
42
#[ derive( Debug , Clone ) ]
18
43
pub struct Event {
19
44
event_name : EventKind ,
@@ -55,18 +80,7 @@ impl Metrics {
55
80
pub fn new_with_background_handling ( app_settings : & AppSettings ) -> Self {
56
81
let metrics_permitted = app_settings. telemetry . app_metrics_enabled ;
57
82
// Only create client and sender if metrics are permitted
58
- let client = if metrics_permitted {
59
- option_env ! ( "POSTHOG_API_KEY" ) . and_then ( |api_key| {
60
- let options = posthog_rs:: ClientOptionsBuilder :: default ( )
61
- . api_key ( api_key. to_string ( ) )
62
- . api_endpoint ( "https://eu.i.posthog.com/i/v0/e/" . to_string ( ) )
63
- . build ( )
64
- . ok ( ) ?;
65
- Some ( posthog_rs:: client ( options) )
66
- } )
67
- } else {
68
- None
69
- } ;
83
+ let client = posthog_client ( app_settings. clone ( ) ) ;
70
84
let ( sender, receiver) = tokio:: sync:: mpsc:: unbounded_channel ( ) ;
71
85
let sender = if metrics_permitted {
72
86
Some ( sender)
@@ -77,19 +91,11 @@ impl Metrics {
77
91
78
92
if let Some ( client_future) = client {
79
93
let mut receiver = receiver;
80
- let distinct_id = app_settings. telemetry . app_distinct_id . clone ( ) ;
94
+ let app_settings = app_settings. clone ( ) ;
81
95
tokio:: task:: spawn ( async move {
82
96
let client = client_future. await ;
83
97
while let Some ( event) = receiver. recv ( ) . await {
84
- let mut posthog_event = if let Some ( id) = & distinct_id {
85
- posthog_rs:: Event :: new ( event. event_name . to_string ( ) , id. clone ( ) )
86
- } else {
87
- posthog_rs:: Event :: new_anon ( event. event_name . to_string ( ) )
88
- } ;
89
- for ( key, prop) in event. props {
90
- let _ = posthog_event. insert_prop ( key, prop) ;
91
- }
92
- let _ = client. capture ( posthog_event) . await ;
98
+ do_capture ( & client, event, & app_settings) . await . ok ( ) ;
93
99
}
94
100
} ) ;
95
101
}
@@ -102,4 +108,44 @@ impl Metrics {
102
108
let _ = sender. send ( event. clone ( ) ) ;
103
109
}
104
110
}
111
+
112
+ pub async fn capture_blocking ( app_settings : & AppSettings , event : Event ) {
113
+ if let Some ( client) = posthog_client ( app_settings. clone ( ) ) {
114
+ do_capture ( & client. await , event, app_settings) . await . ok ( ) ;
115
+ }
116
+ }
117
+ }
118
+
119
+ fn do_capture (
120
+ client : & Client ,
121
+ event : Event ,
122
+ app_settings : & AppSettings ,
123
+ ) -> impl Future < Output = Result < ( ) , posthog_rs:: Error > > {
124
+ let mut posthog_event = if let Some ( id) = & app_settings. telemetry . app_distinct_id . clone ( ) {
125
+ posthog_rs:: Event :: new ( event. event_name . to_string ( ) , id. clone ( ) )
126
+ } else {
127
+ posthog_rs:: Event :: new_anon ( event. event_name . to_string ( ) )
128
+ } ;
129
+ for ( key, prop) in event. props {
130
+ let _ = posthog_event. insert_prop ( key, prop) ;
131
+ }
132
+ client. capture ( posthog_event)
133
+ }
134
+
135
+ /// Creates a PostHog client if metrics are enabled and the API key is set.
136
+ fn posthog_client ( app_settings : AppSettings ) -> Option < impl Future < Output = posthog_rs:: Client > > {
137
+ if app_settings. telemetry . app_metrics_enabled {
138
+ if let Some ( api_key) = option_env ! ( "POSTHOG_API_KEY" ) {
139
+ let options = posthog_rs:: ClientOptionsBuilder :: default ( )
140
+ . api_key ( api_key. to_string ( ) )
141
+ . api_endpoint ( "https://eu.i.posthog.com/i/v0/e/" . to_string ( ) )
142
+ . build ( )
143
+ . ok ( ) ?;
144
+ Some ( posthog_rs:: client ( options) )
145
+ } else {
146
+ None
147
+ }
148
+ } else {
149
+ None
150
+ }
105
151
}
0 commit comments