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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
use cfg_if::cfg_if;
use vector_common::request_metadata::GroupedCountByteSize;
use vector_config::configurable_component;

cfg_if! {
    // The telemetry code assumes a process wide singleton. When running `cargo test`,
    // multiple threads might try to read/write this global.
    if #[cfg(any(test, feature = "test"))] {
        use std::sync::{Arc, Mutex};

        thread_local! {
            static TELEMETRY: Arc<Mutex<Option<Telemetry>>> = Arc::new(Mutex::new(None));
        }

        /// Test implementation.
        ///
        /// # Panics
        ///
        /// If deny is set, will panic if telemetry has already been set.
        /// Also, panics if the lock is poisoned.
        pub fn init_telemetry(telemetry: Telemetry, deny_if_set: bool) {
            TELEMETRY.with(|tl| {
                let mut tl = tl.lock().expect("telemetry lock poisoned");
                assert!(!(tl.is_some() && deny_if_set), "Couldn't set telemetry");
                *tl = Some(telemetry);
            });
        }

        /// Test implementation.
        ///
        /// # Panics
        ///
        /// If the lock is poisoned.
         pub fn telemetry() -> Telemetry {
            TELEMETRY.with(|tl| {
               let mut tl = tl.lock().expect("telemetry lock poisoned");
                // For non-test code we return `TELEMETRY_DEFAULT`.
                // For test code, we will instantiate a default instance per thread.
                if tl.is_none() {
                    *tl = Some(Telemetry::default());
                }
                tl.clone().unwrap()
            })
        }
    }
    else {
        use std::sync::{LazyLock, OnceLock};

        static TELEMETRY: OnceLock<Telemetry> = OnceLock::new();
        static TELEMETRY_DEFAULT: LazyLock<Telemetry> = LazyLock::new(Telemetry::default);

        /// Loads the telemetry options from configurations and sets the global options.
        /// Once this is done, configurations can be correctly loaded using configured
        /// log schema defaults.
        ///
        /// # Errors
        ///
        /// This function will fail if the `builder` fails.
        ///
        /// # Panics
        ///
        /// If deny is set, will panic if telemetry has already been set.
        pub fn init_telemetry(telemetry: Telemetry, deny_if_set: bool) {
            assert!(
                !(TELEMETRY.set(telemetry).is_err() && deny_if_set),
                "Couldn't set telemetry"
            );
        }

        /// Returns the telemetry configuration options.
        pub fn telemetry() -> &'static Telemetry {
            TELEMETRY.get().unwrap_or(&TELEMETRY_DEFAULT)
        }
    }
}

/// Sets options for the telemetry that Vector emits.
#[configurable_component]
#[derive(Clone, Debug, Eq, PartialEq, Default)]
#[serde(default)]
pub struct Telemetry {
    #[configurable(derived)]
    pub tags: Tags,
}

impl Telemetry {
    /// Merge two `Telemetry` instances together.
    pub fn merge(&mut self, other: &Telemetry) {
        self.tags.emit_service = self.tags.emit_service || other.tags.emit_service;
        self.tags.emit_source = self.tags.emit_source || other.tags.emit_source;
    }

    /// Returns true if any of the tag options are true.
    pub fn has_tags(&self) -> bool {
        self.tags.emit_service || self.tags.emit_source
    }

    pub fn tags(&self) -> &Tags {
        &self.tags
    }

    /// The variant of `GroupedCountByteSize`
    pub fn create_request_count_byte_size(&self) -> GroupedCountByteSize {
        if self.has_tags() {
            GroupedCountByteSize::new_tagged()
        } else {
            GroupedCountByteSize::new_untagged()
        }
    }
}

/// Configures whether to emit certain tags
#[configurable_component]
#[derive(Clone, Debug, Eq, PartialEq, Default)]
#[serde(default)]
pub struct Tags {
    /// True if the `service` tag should be emitted
    /// in the `component_received_*` and `component_sent_*`
    /// telemetry.
    pub emit_service: bool,

    /// True if the `source` tag should be emitted
    /// in the `component_received_*` and `component_sent_*`
    /// telemetry.
    pub emit_source: bool,
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn partial_telemetry() {
        let toml = r"
            emit_source = true
        ";
        toml::from_str::<Telemetry>(toml).unwrap();
    }
}