Skip to main content

httpgenerator_cli/telemetry/
recorder.rs

1use 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}