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.len() > t.len() + 2 && &tag[t.len()..t.len() + 2] == "::" && tag.starts_with(t)
70        })
71    } else {
72        false
73    }
74}
75
76/// Filters by tags according to legacy LogMessage rules.
77/// Prefer filtering by moniker/selectors instead of using this function.
78pub fn filter_by_tags(log_message: &Data<Logs>, include_tags: &HashSet<String>) -> bool {
79    if include_tags.is_empty() {
80        false
81    } else if log_message.tags().map(|t| t.is_empty()).unwrap_or(true) {
82        !include_tags.contains(log_message.component_name().as_ref())
83    } else {
84        !log_message
85            .tags()
86            .map(|tags| {
87                tags.iter()
88                    .any(|tag| include_tags.contains(tag) || include_tag_prefix(tag, include_tags))
89            })
90            .unwrap_or(false)
91    }
92}
93
94/// Convert this `Message` to a FIDL representation suitable for sending to `LogListenerSafe`.
95impl From<Data<Logs>> for LogMessage {
96    fn from(data: Data<Logs>) -> LogMessage {
97        LogMessage {
98            pid: data.pid().unwrap_or(zx::sys::ZX_KOID_INVALID),
99            tid: data.tid().unwrap_or(zx::sys::ZX_KOID_INVALID),
100            time: data.metadata.timestamp,
101            severity: data.metadata.raw_severity() as i32,
102            dropped_logs: data.dropped_logs().unwrap_or(0) as _,
103            msg: format_log_message(&data),
104            tags: match data.metadata.tags {
105                Some(tags) => tags,
106                None => vec![data.component_name().to_string()],
107            },
108        }
109    }
110}
111
112#[cfg(test)]
113mod test {
114    use super::*;
115    use crate::{BuilderArgs, LogsDataBuilder, Severity, Timestamp};
116    use fidl_fuchsia_diagnostics as fdiagnostics;
117
118    const TEST_URL: &str = "fuchsia-pkg://test";
119    const TEST_MONIKER: &str = "fake-test/moniker";
120
121    macro_rules! severity_roundtrip_test {
122        ($raw:expr, $expected:expr) => {
123            let severity = Severity::from(u8::from($raw));
124            let msg = LogsDataBuilder::new(BuilderArgs {
125                timestamp: Timestamp::from_nanos(0),
126                component_url: Some(TEST_URL.into()),
127                moniker: moniker::ExtendedMoniker::parse_str(TEST_MONIKER).unwrap(),
128                severity,
129            })
130            .build();
131
132            let legacy_msg: LogMessage = (&msg).into();
133            assert_eq!(
134                legacy_msg.severity,
135                i32::from($expected),
136                "failed to round trip severity for {:?} (raw {}), intermediates: {:#?}\n{:#?}",
137                severity,
138                $raw,
139                msg,
140                legacy_msg
141            );
142        };
143    }
144
145    #[fuchsia::test]
146    fn raw_severity_roundtrip_trace() {
147        severity_roundtrip_test!(
148            fdiagnostics::Severity::Trace.into_primitive() - 1,
149            fdiagnostics::Severity::Trace.into_primitive()
150        );
151        severity_roundtrip_test!(
152            fdiagnostics::Severity::Trace.into_primitive(),
153            fdiagnostics::Severity::Trace.into_primitive()
154        );
155        severity_roundtrip_test!(
156            fdiagnostics::Severity::Trace.into_primitive() + 1,
157            fdiagnostics::Severity::Debug.into_primitive()
158        );
159    }
160
161    #[fuchsia::test]
162    fn severity_roundtrip_debug() {
163        severity_roundtrip_test!(
164            fdiagnostics::Severity::Debug.into_primitive() - 1,
165            fdiagnostics::Severity::Debug.into_primitive()
166        );
167        severity_roundtrip_test!(
168            fdiagnostics::Severity::Debug.into_primitive(),
169            fdiagnostics::Severity::Debug.into_primitive()
170        );
171        severity_roundtrip_test!(
172            fdiagnostics::Severity::Debug.into_primitive() + 1,
173            fdiagnostics::Severity::Info.into_primitive()
174        );
175    }
176
177    #[fuchsia::test]
178    fn severity_roundtrip_info() {
179        severity_roundtrip_test!(
180            fdiagnostics::Severity::Info.into_primitive() - 1,
181            fdiagnostics::Severity::Info.into_primitive()
182        );
183        severity_roundtrip_test!(
184            fdiagnostics::Severity::Info.into_primitive(),
185            fdiagnostics::Severity::Info.into_primitive()
186        );
187        severity_roundtrip_test!(
188            fdiagnostics::Severity::Info.into_primitive() + 1,
189            fdiagnostics::Severity::Warn.into_primitive()
190        );
191    }
192
193    #[fuchsia::test]
194    fn severity_roundtrip_warn() {
195        severity_roundtrip_test!(
196            fdiagnostics::Severity::Warn.into_primitive() - 1,
197            fdiagnostics::Severity::Warn.into_primitive()
198        );
199        severity_roundtrip_test!(
200            fdiagnostics::Severity::Warn.into_primitive(),
201            fdiagnostics::Severity::Warn.into_primitive()
202        );
203        severity_roundtrip_test!(
204            fdiagnostics::Severity::Warn.into_primitive() + 1,
205            fdiagnostics::Severity::Error.into_primitive()
206        );
207    }
208
209    #[fuchsia::test]
210    fn severity_roundtrip_error() {
211        severity_roundtrip_test!(
212            fdiagnostics::Severity::Error.into_primitive() - 1,
213            fdiagnostics::Severity::Error.into_primitive()
214        );
215        severity_roundtrip_test!(
216            fdiagnostics::Severity::Error.into_primitive(),
217            fdiagnostics::Severity::Error.into_primitive()
218        );
219        severity_roundtrip_test!(
220            fdiagnostics::Severity::Error.into_primitive() + 1,
221            fdiagnostics::Severity::Fatal.into_primitive()
222        );
223    }
224
225    #[fuchsia::test]
226    fn severity_roundtrip_fatal() {
227        severity_roundtrip_test!(
228            fdiagnostics::Severity::Fatal.into_primitive() - 1,
229            fdiagnostics::Severity::Fatal.into_primitive()
230        );
231        severity_roundtrip_test!(
232            fdiagnostics::Severity::Fatal.into_primitive(),
233            fdiagnostics::Severity::Fatal.into_primitive()
234        );
235        severity_roundtrip_test!(
236            fdiagnostics::Severity::Fatal.into_primitive() + 1,
237            fdiagnostics::Severity::Fatal.into_primitive()
238        );
239    }
240}