tracing_stackdriver/
google.rs

1use serde::Serialize;
2use std::{convert::Infallible, fmt, str::FromStr};
3use tracing_core::Level;
4
5/// The severity of the event described in a log entry, expressed as standard severity levels.
6/// [See Google's LogSeverity docs here](https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#LogSeverity).
7#[cfg_attr(
8    all(tracing_unstable, feature = "valuable"),
9    derive(valuable::Valuable)
10)]
11#[derive(Debug, Default, Serialize)]
12#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
13pub enum LogSeverity {
14    /// Log entry has no assigned severity level
15    #[default]
16    Default,
17    /// Debug or trace information
18    Debug,
19    /// Routine information, such as ongoing status or performance
20    Info,
21    /// Normal but significant events, such as start up, shut down, or a configuration change
22    Notice,
23    /// Warning events might cause problems
24    Warning,
25    /// Error events are likely to cause problems
26    Error,
27    /// Critical events cause more severe problems or outages
28    Critical,
29    /// A person must take an action immediately
30    Alert,
31    /// One or more systems are unusable
32    Emergency,
33}
34
35impl fmt::Display for LogSeverity {
36    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
37        let output = match self {
38            Self::Default => "DEFAULT",
39            Self::Debug => "DEBUG",
40            Self::Info => "INFO",
41            Self::Notice => "NOTICE",
42            Self::Warning => "WARNING",
43            Self::Error => "ERROR",
44            Self::Critical => "CRITICAL",
45            Self::Alert => "ALERT",
46            Self::Emergency => "EMERGENCY",
47        };
48
49        formatter.write_str(output)
50    }
51}
52
53impl From<&Level> for LogSeverity {
54    fn from(level: &Level) -> Self {
55        match level {
56            &Level::DEBUG | &Level::TRACE => Self::Debug,
57            &Level::INFO => Self::Info,
58            &Level::WARN => Self::Warning,
59            &Level::ERROR => Self::Error,
60        }
61    }
62}
63
64impl FromStr for LogSeverity {
65    type Err = Infallible;
66
67    fn from_str(string: &str) -> Result<Self, Self::Err> {
68        let severity = match string.to_lowercase().as_str() {
69            "debug" | "trace" => Self::Debug,
70            "info" => Self::Info,
71            "notice" => Self::Notice,
72            "warn" | "warning" => Self::Warning,
73            "error" => Self::Error,
74            "critical" => Self::Critical,
75            "alert" => Self::Alert,
76            "emergency" => Self::Emergency,
77            _ => Self::Default,
78        };
79
80        Ok(severity)
81    }
82}
83
84impl From<serde_json::Value> for LogSeverity {
85    fn from(json: serde_json::Value) -> Self {
86        // handle simple string inputs
87        if let Some(str) = json.as_str() {
88            return Self::from_str(str).unwrap_or(Self::Default);
89        }
90
91        // handle wacky object encoding of Valuable enums
92        #[cfg(all(tracing_unstable, feature = "valuable"))]
93        if let Some(map) = json.as_object() {
94            if let Some(key) = map.keys().next() {
95                return Self::from_str(key).unwrap_or(Self::Default);
96            }
97        }
98
99        Self::Default
100    }
101}
102
103/// Typechecked HttpRequest structure for stucturally logging information about a request.
104/// [See Google's HttpRequest docs here](https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#HttpRequest).
105#[cfg_attr(docsrs, doc(cfg(feature = "valuable")))]
106#[cfg(any(docsrs, all(tracing_unstable, feature = "valuable")))]
107#[derive(Default)]
108pub struct HttpRequest {
109    /// Valid HTTP Method for the request (e.g. GET, POST, etc)
110    pub request_method: Option<http::Method>,
111    /// URL from the HTTP request
112    pub request_url: Option<url::Url>,
113    /// Size of the HTTP request in bytes
114    pub request_size: Option<u32>,
115    /// Size of the HTTP response in bytes
116    pub response_size: Option<u32>,
117    /// Valid HTTP StatusCode for the response
118    pub status: Option<http::StatusCode>,
119    /// User Agent string of the request
120    pub user_agent: Option<String>,
121    /// IP address of the client that issued the request
122    pub remote_ip: Option<std::net::IpAddr>,
123    /// IP address of the server that the request was sent to
124    pub server_ip: Option<std::net::IpAddr>,
125    /// Referer URL of the request, as defined in HTTP/1.1 Header Field Definitions
126    pub referer: Option<url::Url>,
127    /// Processing latency on the server, from the time the request was received until the response was sent
128    pub latency: Option<std::time::Duration>,
129    /// Whether or not a cache lookup was attempted
130    pub cache_lookup: Option<bool>,
131    /// Whether or not an entity was served from cache (with or without validation)
132    pub cache_hit: Option<bool>,
133    /// Whether or not the response was validated with the origin server before being served from cache
134    pub cache_validated_with_origin_server: Option<bool>,
135    /// Number of HTTP response bytes inserted into cache
136    pub cache_fill_bytes: Option<u32>,
137    /// Protocol used for the request (e.g. "HTTP/1.1", "HTTP/2", "websocket")
138    pub protocol: Option<String>,
139}
140
141#[cfg_attr(docsrs, doc(cfg(feature = "valuable")))]
142#[cfg(any(docsrs, all(tracing_unstable, feature = "valuable")))]
143impl HttpRequest {
144    /// Generate a new log-able HttpRequest structured log entry
145    pub fn new() -> Self {
146        Self::default()
147    }
148}
149
150#[cfg(all(tracing_unstable, feature = "valuable"))]
151static HTTP_REQUEST_FIELDS: &[valuable::NamedField<'static>] = &[
152    valuable::NamedField::new("requestMethod"),
153    valuable::NamedField::new("requestUrl"),
154    valuable::NamedField::new("requestSize"),
155    valuable::NamedField::new("responseSize"),
156    valuable::NamedField::new("status"),
157    valuable::NamedField::new("userAgent"),
158    valuable::NamedField::new("remoteIp"),
159    valuable::NamedField::new("serverIp"),
160    valuable::NamedField::new("referer"),
161    valuable::NamedField::new("latency"),
162    valuable::NamedField::new("cacheLookup"),
163    valuable::NamedField::new("cacheHit"),
164    valuable::NamedField::new("cacheValidatedWithOriginServer"),
165    valuable::NamedField::new("cacheFillBytes"),
166    valuable::NamedField::new("protocol"),
167];
168
169#[cfg_attr(docsrs, doc(cfg(feature = "valuable")))]
170#[cfg(any(docsrs, all(tracing_unstable, feature = "valuable")))]
171impl valuable::Valuable for HttpRequest {
172    fn as_value(&self) -> valuable::Value<'_> {
173        valuable::Value::Structable(self)
174    }
175
176    fn visit(&self, visit: &mut dyn valuable::Visit) {
177        let request_method = self
178            .request_method
179            .as_ref()
180            .map(|method| method.to_string());
181        let request_url = self.request_url.as_ref().map(|url| url.to_string());
182        let status = self.status.map(|status| status.as_u16());
183        let user_agent = &self.user_agent;
184        let remote_ip = self.remote_ip.map(|ip| ip.to_string());
185        let server_ip = self.server_ip.map(|ip| ip.to_string());
186        let referer = self.referer.as_ref().map(|url| url.to_string());
187        let latency = self
188            .latency
189            .map(|latency| format!("{}s", latency.as_secs_f32()));
190
191        let (fields, values): (Vec<_>, Vec<_>) = HTTP_REQUEST_FIELDS
192            .iter()
193            .zip(
194                [
195                    request_method.as_ref().map(valuable::Valuable::as_value),
196                    request_url.as_ref().map(valuable::Valuable::as_value),
197                    self.request_size.as_ref().map(valuable::Valuable::as_value),
198                    self.response_size
199                        .as_ref()
200                        .map(valuable::Valuable::as_value),
201                    status.as_ref().map(valuable::Valuable::as_value),
202                    user_agent.as_ref().map(valuable::Valuable::as_value),
203                    remote_ip.as_ref().map(valuable::Valuable::as_value),
204                    server_ip.as_ref().map(valuable::Valuable::as_value),
205                    referer.as_ref().map(valuable::Valuable::as_value),
206                    latency.as_ref().map(valuable::Valuable::as_value),
207                    self.cache_lookup.as_ref().map(valuable::Valuable::as_value),
208                    self.cache_hit.as_ref().map(valuable::Valuable::as_value),
209                    self.cache_validated_with_origin_server
210                        .as_ref()
211                        .map(valuable::Valuable::as_value),
212                    self.cache_fill_bytes
213                        .as_ref()
214                        .map(valuable::Valuable::as_value),
215                    self.protocol.as_ref().map(valuable::Valuable::as_value),
216                ]
217                .iter(),
218            )
219            .filter_map(|(field, value)| value.map(|value| (field, value)))
220            .unzip();
221
222        visit.visit_named_fields(&valuable::NamedValues::new(&fields, &values));
223    }
224}
225
226#[cfg_attr(docsrs, doc(cfg(feature = "valuable")))]
227#[cfg(any(docsrs, all(tracing_unstable, feature = "valuable")))]
228impl valuable::Structable for HttpRequest {
229    fn definition(&self) -> valuable::StructDef<'_> {
230        valuable::StructDef::new_dynamic("HttpRequest", valuable::Fields::Named(&[]))
231    }
232}
233
234/// Configuration for projects looking to use the [Cloud Trace](https://cloud.google.com/trace) integration
235/// through [trace-specific fields](https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#FIELDS.trace) in
236/// a LogEntry.
237#[cfg_attr(docsrs, doc(cfg(feature = "opentelemetry")))]
238#[cfg(any(docsrs, feature = "opentelemetry"))]
239#[derive(Clone)]
240pub struct CloudTraceConfiguration {
241    /// Google-provided [Project
242    /// ID](https://cloud.google.com/resource-manager/docs/creating-managing-projects) for
243    /// prefixing and identifying collectecd traces.
244    pub project_id: String,
245}