diagnostics_data/
logs_legacy.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::{Data, Logs};
6#[cfg(fuchsia_api_level_at_least = "PLATFORM")]
7use fidl_fuchsia_logger::LogMessage;
8use std::collections::HashSet;
9use std::fmt::Write;
10
11/// Convert this `Message` to a FIDL representation suitable for sending to `LogListenerSafe`.
12#[cfg(fuchsia_api_level_at_least = "PLATFORM")]
13impl From<&Data<Logs>> for LogMessage {
14    fn from(data: &Data<Logs>) -> LogMessage {
15        let mut msg = data.msg().unwrap_or("").to_string();
16
17        if let Some(payload) = data.payload_keys() {
18            for property in payload.properties.iter() {
19                write!(&mut msg, " {property}").expect("allocations have to fail for this to fail");
20            }
21        }
22        let file = data.metadata.file.as_ref();
23        let line = data.metadata.line.as_ref();
24        if let (Some(file), Some(line)) = (file, line) {
25            msg = format!("[{}({})] {}", file, line, msg);
26        }
27
28        let tags = match &data.metadata.tags {
29            None => vec![data.component_name().to_string()],
30            Some(tags) if tags.is_empty() => vec![data.component_name().to_string()],
31            Some(tags) => tags.clone(),
32        };
33
34        LogMessage {
35            pid: data.pid().unwrap_or(zx::sys::ZX_KOID_INVALID),
36            tid: data.tid().unwrap_or(zx::sys::ZX_KOID_INVALID),
37            time: data.metadata.timestamp,
38            severity: data.metadata.raw_severity() as i32,
39            dropped_logs: data.dropped_logs().unwrap_or(0) as _,
40            tags,
41            msg,
42        }
43    }
44}
45
46/// Applies legacy formatting to a log message, matching the formatting
47/// used for a legacy LogMessage.
48/// Prefer using LogTextPresenter if possible instead of this function.
49pub fn format_log_message(data: &Data<Logs>) -> String {
50    let mut msg = data.msg().unwrap_or("").to_string();
51
52    if let Some(payload) = data.payload_keys() {
53        for property in payload.properties.iter() {
54            write!(&mut msg, " {property}").expect("allocations have to fail for this to fail");
55        }
56    }
57    let file = data.metadata.file.as_ref();
58    let line = data.metadata.line.as_ref();
59    if let (Some(file), Some(line)) = (file, line) {
60        msg = format!("[{}({})] {}", file, line, msg);
61    }
62    msg
63}
64
65// Rust uses tags of the form "<foo>::<bar>" so if we have a filter for "<foo>" we should
66// include messages that have "<foo>" as a prefix.
67fn include_tag_prefix(tag: &str, tags: &HashSet<String>) -> bool {
68    if tag.contains("::") {
69        tags.iter().any(|t| {
70            tag.len() > t.len() + 2 && &tag[t.len()..t.len() + 2] == "::" && tag.starts_with(t)
71        })
72    } else {
73        false
74    }
75}
76
77/// Filters by tags according to legacy LogMessage rules.
78/// Prefer filtering by moniker/selectors instead of using this function.
79pub fn filter_by_tags(log_message: &Data<Logs>, include_tags: &HashSet<String>) -> bool {
80    let reject_tags = if include_tags.is_empty() {
81        false
82    } else if log_message.tags().map(|t| t.is_empty()).unwrap_or(true) {
83        !include_tags.contains(log_message.component_name().as_ref())
84    } else {
85        !log_message
86            .tags()
87            .map(|tags| {
88                tags.iter()
89                    .any(|tag| include_tags.contains(tag) || include_tag_prefix(tag, include_tags))
90            })
91            .unwrap_or(false)
92    };
93    reject_tags
94}
95
96/// Convert this `Message` to a FIDL representation suitable for sending to `LogListenerSafe`.
97#[cfg(fuchsia_api_level_at_least = "PLATFORM")]
98impl From<Data<Logs>> for LogMessage {
99    fn from(data: Data<Logs>) -> LogMessage {
100        LogMessage {
101            pid: data.pid().unwrap_or(zx::sys::ZX_KOID_INVALID),
102            tid: data.tid().unwrap_or(zx::sys::ZX_KOID_INVALID),
103            time: data.metadata.timestamp,
104            severity: data.metadata.raw_severity() as i32,
105            dropped_logs: data.dropped_logs().unwrap_or(0) as _,
106            msg: format_log_message(&data),
107            tags: match data.metadata.tags {
108                Some(tags) => tags,
109                None => vec![data.component_name().to_string()],
110            },
111        }
112    }
113}
114
115#[cfg(test)]
116mod test {
117    use super::*;
118    use crate::{BuilderArgs, LogsDataBuilder, Severity, Timestamp};
119    use fidl_fuchsia_diagnostics as fdiagnostics;
120
121    const TEST_URL: &str = "fuchsia-pkg://test";
122    const TEST_MONIKER: &str = "fake-test/moniker";
123
124    macro_rules! severity_roundtrip_test {
125        ($raw:expr, $expected:expr) => {
126            let severity = Severity::from(u8::from($raw));
127            let msg = LogsDataBuilder::new(BuilderArgs {
128                timestamp: Timestamp::from_nanos(0),
129                component_url: Some(TEST_URL.into()),
130                moniker: moniker::ExtendedMoniker::parse_str(TEST_MONIKER).unwrap(),
131                severity,
132            })
133            .build();
134
135            let legacy_msg: LogMessage = (&msg).into();
136            assert_eq!(
137                legacy_msg.severity,
138                i32::from($expected),
139                "failed to round trip severity for {:?} (raw {}), intermediates: {:#?}\n{:#?}",
140                severity,
141                $raw,
142                msg,
143                legacy_msg
144            );
145        };
146    }
147
148    #[fuchsia::test]
149    fn raw_severity_roundtrip_trace() {
150        severity_roundtrip_test!(
151            fdiagnostics::Severity::Trace.into_primitive() - 1,
152            fdiagnostics::Severity::Trace.into_primitive()
153        );
154        severity_roundtrip_test!(
155            fdiagnostics::Severity::Trace.into_primitive(),
156            fdiagnostics::Severity::Trace.into_primitive()
157        );
158        severity_roundtrip_test!(
159            fdiagnostics::Severity::Trace.into_primitive() + 1,
160            fdiagnostics::Severity::Debug.into_primitive()
161        );
162    }
163
164    #[fuchsia::test]
165    fn severity_roundtrip_debug() {
166        severity_roundtrip_test!(
167            fdiagnostics::Severity::Debug.into_primitive() - 1,
168            fdiagnostics::Severity::Debug.into_primitive()
169        );
170        severity_roundtrip_test!(
171            fdiagnostics::Severity::Debug.into_primitive(),
172            fdiagnostics::Severity::Debug.into_primitive()
173        );
174        severity_roundtrip_test!(
175            fdiagnostics::Severity::Debug.into_primitive() + 1,
176            fdiagnostics::Severity::Info.into_primitive()
177        );
178    }
179
180    #[fuchsia::test]
181    fn severity_roundtrip_info() {
182        severity_roundtrip_test!(
183            fdiagnostics::Severity::Info.into_primitive() - 1,
184            fdiagnostics::Severity::Info.into_primitive()
185        );
186        severity_roundtrip_test!(
187            fdiagnostics::Severity::Info.into_primitive(),
188            fdiagnostics::Severity::Info.into_primitive()
189        );
190        severity_roundtrip_test!(
191            fdiagnostics::Severity::Info.into_primitive() + 1,
192            fdiagnostics::Severity::Warn.into_primitive()
193        );
194    }
195
196    #[fuchsia::test]
197    fn severity_roundtrip_warn() {
198        severity_roundtrip_test!(
199            fdiagnostics::Severity::Warn.into_primitive() - 1,
200            fdiagnostics::Severity::Warn.into_primitive()
201        );
202        severity_roundtrip_test!(
203            fdiagnostics::Severity::Warn.into_primitive(),
204            fdiagnostics::Severity::Warn.into_primitive()
205        );
206        severity_roundtrip_test!(
207            fdiagnostics::Severity::Warn.into_primitive() + 1,
208            fdiagnostics::Severity::Error.into_primitive()
209        );
210    }
211
212    #[fuchsia::test]
213    fn severity_roundtrip_error() {
214        severity_roundtrip_test!(
215            fdiagnostics::Severity::Error.into_primitive() - 1,
216            fdiagnostics::Severity::Error.into_primitive()
217        );
218        severity_roundtrip_test!(
219            fdiagnostics::Severity::Error.into_primitive(),
220            fdiagnostics::Severity::Error.into_primitive()
221        );
222        severity_roundtrip_test!(
223            fdiagnostics::Severity::Error.into_primitive() + 1,
224            fdiagnostics::Severity::Fatal.into_primitive()
225        );
226    }
227
228    #[fuchsia::test]
229    fn severity_roundtrip_fatal() {
230        severity_roundtrip_test!(
231            fdiagnostics::Severity::Fatal.into_primitive() - 1,
232            fdiagnostics::Severity::Fatal.into_primitive()
233        );
234        severity_roundtrip_test!(
235            fdiagnostics::Severity::Fatal.into_primitive(),
236            fdiagnostics::Severity::Fatal.into_primitive()
237        );
238        severity_roundtrip_test!(
239            fdiagnostics::Severity::Fatal.into_primitive() + 1,
240            fdiagnostics::Severity::Fatal.into_primitive()
241        );
242    }
243}