httpgenerator_cli/telemetry/
recorder.rs1use httpgenerator_core::{anonymous_identity, support_key_from_anonymous_identity};
2use std::ffi::OsString;
3
4use crate::args::CliArgs;
5
6use super::{
7 ErrorEvent, FeatureUsageEvent, TelemetryContext, TelemetryEvent,
8 redaction::{feature_usage_names, redacted_command_line, redacted_settings},
9 sink::TelemetrySink,
10};
11
12pub struct TelemetryRecorder<S> {
13 context: Option<TelemetryContext>,
14 sink: S,
15}
16
17impl<S> TelemetryRecorder<S>
18where
19 S: TelemetrySink,
20{
21 pub fn from_cli_args(raw_args: &[OsString], args: &CliArgs, sink: S) -> Self {
22 let context = (!args.no_logging).then(|| {
23 let anonymous_identity = anonymous_identity();
24 let support_key = support_key_from_anonymous_identity(&anonymous_identity);
25
26 TelemetryContext {
27 support_key,
28 anonymous_identity,
29 command_line: redacted_command_line(raw_args),
30 }
31 });
32
33 Self { context, sink }
34 }
35
36 pub fn record_feature_usage(&mut self, args: &CliArgs) {
37 let Some(context) = &self.context else {
38 return;
39 };
40
41 for feature_name in feature_usage_names(args) {
42 self.sink
43 .emit(TelemetryEvent::FeatureUsage(FeatureUsageEvent {
44 feature_name,
45 support_key: context.support_key.clone(),
46 anonymous_identity: context.anonymous_identity.clone(),
47 }));
48 }
49 }
50
51 pub fn record_error(&mut self, args: &CliArgs, error_type: &str, message: &str) {
52 let Some(context) = &self.context else {
53 return;
54 };
55
56 let settings = redacted_settings(args);
57 let settings_json = serde_json::Value::Object(settings.clone()).to_string();
58
59 self.sink.emit(TelemetryEvent::Error(ErrorEvent {
60 error_type: error_type.to_string(),
61 message: message.to_string(),
62 support_key: context.support_key.clone(),
63 anonymous_identity: context.anonymous_identity.clone(),
64 command_line: context.command_line.clone(),
65 settings_json,
66 settings,
67 }));
68 }
69
70 pub fn into_sink(self) -> S {
71 self.sink
72 }
73}