Skip to main content

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