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