#![cfg(target_os = "fuchsia")]
use crate::{Data, Logs, Severity};
use fidl_fuchsia_diagnostics as fdiagnostics;
use fidl_fuchsia_logger::{LogLevelFilter, LogMessage};
use fuchsia_zircon as zx;
use std::{convert::TryFrom, fmt::Write, os::raw::c_int};
use thiserror::Error;
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
#[repr(i8)]
pub enum LegacySeverity {
Trace,
Debug,
Verbose(i8),
Info,
Warn,
Error,
Fatal,
}
impl LegacySeverity {
pub fn for_structured(self) -> (Severity, Option<i8>) {
match self {
LegacySeverity::Trace => (Severity::Trace, None),
LegacySeverity::Debug => (Severity::Debug, None),
LegacySeverity::Info => (Severity::Info, None),
LegacySeverity::Warn => (Severity::Warn, None),
LegacySeverity::Error => (Severity::Error, None),
LegacySeverity::Fatal => (Severity::Fatal, None),
LegacySeverity::Verbose(v) => (Severity::Debug, Some(v)),
}
}
}
impl From<Severity> for LegacySeverity {
fn from(severity: Severity) -> Self {
match severity {
Severity::Trace => Self::Trace,
Severity::Debug => Self::Debug,
Severity::Info => Self::Info,
Severity::Warn => Self::Warn,
Severity::Error => Self::Error,
Severity::Fatal => Self::Fatal,
}
}
}
impl From<fdiagnostics::Severity> for LegacySeverity {
fn from(fidl_severity: fdiagnostics::Severity) -> Self {
match fidl_severity {
fdiagnostics::Severity::Trace => Self::Trace,
fdiagnostics::Severity::Debug => Self::Debug,
fdiagnostics::Severity::Info => Self::Info,
fdiagnostics::Severity::Warn => Self::Warn,
fdiagnostics::Severity::Error => Self::Error,
fdiagnostics::Severity::Fatal => Self::Fatal,
}
}
}
impl From<LegacySeverity> for c_int {
fn from(severity: LegacySeverity) -> c_int {
match severity {
LegacySeverity::Trace => LogLevelFilter::Trace as _,
LegacySeverity::Debug => LogLevelFilter::Debug as _,
LegacySeverity::Info => LogLevelFilter::Info as _,
LegacySeverity::Warn => LogLevelFilter::Warn as _,
LegacySeverity::Error => LogLevelFilter::Error as _,
LegacySeverity::Fatal => LogLevelFilter::Fatal as _,
LegacySeverity::Verbose(v) => (LogLevelFilter::Info as i8 - v) as _,
}
}
}
#[derive(Clone, Debug, Eq, Error, PartialEq)]
pub enum SeverityError {
#[error("invalid or corrupt severity received: {provided}")]
Invalid { provided: c_int },
}
impl TryFrom<c_int> for LegacySeverity {
type Error = SeverityError;
fn try_from(raw: c_int) -> Result<LegacySeverity, SeverityError> {
if -10 <= raw && raw <= -3 {
Ok(LegacySeverity::Verbose(-raw as i8))
} else if raw == -2 {
Ok(LegacySeverity::Trace)
} else if raw == -1 {
Ok(LegacySeverity::Debug)
} else if raw == 0 {
Ok(LegacySeverity::Info)
} else if raw == 1 {
Ok(LegacySeverity::Warn)
} else if raw == 2 {
Ok(LegacySeverity::Error)
} else if raw == 3 {
Ok(LegacySeverity::Fatal)
} else if raw < LogLevelFilter::Info as i32 && raw > LogLevelFilter::Debug as i32 {
Ok(LegacySeverity::Verbose(LogLevelFilter::Info as i8 - raw as i8))
} else if let Some(level) = LogLevelFilter::from_primitive(raw as i8) {
match level {
LogLevelFilter::Trace => Ok(LegacySeverity::Trace),
LogLevelFilter::Debug => Ok(LegacySeverity::Debug),
LogLevelFilter::Info => Ok(LegacySeverity::Info),
LogLevelFilter::Warn => Ok(LegacySeverity::Warn),
LogLevelFilter::Error => Ok(LegacySeverity::Error),
LogLevelFilter::Fatal => Ok(LegacySeverity::Fatal),
_ => Err(SeverityError::Invalid { provided: raw }),
}
} else {
Err(SeverityError::Invalid { provided: raw })
}
}
}
impl Into<LogMessage> for &Data<Logs> {
fn into(self) -> LogMessage {
let mut msg = self.msg().unwrap_or("").to_string();
for property in self.non_legacy_contents() {
match property {
other => {
write!(&mut msg, " {}", other)
.expect("allocations have to fail for this to fail");
}
}
}
let file = self.metadata.file.as_ref();
let line = self.metadata.line.as_ref();
if let (Some(file), Some(line)) = (file, line) {
msg = format!("[{}({})] {}", file, line, msg);
}
let tags = match &self.metadata.tags {
None => vec![self.component_name().to_string()],
Some(tags) if tags.is_empty() => vec![self.component_name().to_string()],
Some(tags) => tags.clone(),
};
LogMessage {
pid: self.pid().unwrap_or(zx::sys::ZX_KOID_INVALID),
tid: self.tid().unwrap_or(zx::sys::ZX_KOID_INVALID),
time: self.metadata.timestamp.into(),
severity: self.legacy_severity().into(),
dropped_logs: self.dropped_logs().unwrap_or(0) as _,
tags,
msg,
}
}
}
impl Into<LogMessage> for Data<Logs> {
fn into(self) -> LogMessage {
let mut msg = self.msg().unwrap_or("").to_string();
for property in self.non_legacy_contents() {
match property {
other => {
write!(&mut msg, " {}", other)
.expect("allocations have to fail for this to fail");
}
}
}
let file = self.metadata.file.as_ref();
let line = self.metadata.line.as_ref();
if let (Some(file), Some(line)) = (file, line) {
msg = format!("[{}({})] {}", file, line, msg);
}
LogMessage {
pid: self.pid().unwrap_or(zx::sys::ZX_KOID_INVALID),
tid: self.tid().unwrap_or(zx::sys::ZX_KOID_INVALID),
time: self.metadata.timestamp.into(),
severity: self.legacy_severity().into(),
dropped_logs: self.dropped_logs().unwrap_or(0) as _,
tags: match self.metadata.tags {
Some(tags) => tags,
None => vec![self.component_name().to_string()],
},
msg,
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::{BuilderArgs, LogsDataBuilder};
use std::convert::TryFrom;
const TEST_URL: &'static str = "fuchsia-pkg://test";
const TEST_MONIKER: &'static str = "fake-test/moniker";
macro_rules! severity_roundtrip_test {
($raw:expr, $expected:expr) => {
let legacy = LegacySeverity::try_from(i32::from($raw)).unwrap();
let (severity, verbosity) = legacy.for_structured();
let mut msg = LogsDataBuilder::new(BuilderArgs {
timestamp_nanos: 0i64.into(),
component_url: Some(TEST_URL.to_string()),
moniker: TEST_MONIKER.to_string(),
severity,
})
.build();
if let Some(v) = verbosity {
msg.set_legacy_verbosity(v);
}
let legacy_msg: LogMessage = (&msg).into();
assert_eq!(
legacy_msg.severity,
i32::from($expected),
"failed to round trip severity for {:?} (raw {}), intermediates: {:#?}\n{:#?}",
legacy,
$raw,
msg,
legacy_msg
);
};
}
#[fuchsia::test]
fn verbosity_roundtrip_legacy_v10() {
severity_roundtrip_test!(-10, fdiagnostics::Severity::Info.into_primitive() - 10);
}
#[fuchsia::test]
fn verbosity_roundtrip_legacy_v5() {
severity_roundtrip_test!(-5, fdiagnostics::Severity::Info.into_primitive() - 5);
}
#[fuchsia::test]
fn verbosity_roundtrip_legacy_v4() {
severity_roundtrip_test!(-4, fdiagnostics::Severity::Info.into_primitive() - 4);
}
#[fuchsia::test]
fn verbosity_roundtrip_legacy_v3() {
severity_roundtrip_test!(-3, fdiagnostics::Severity::Info.into_primitive() - 3);
}
#[fuchsia::test]
fn verbosity_roundtrip_legacy_v2() {
severity_roundtrip_test!(-2, fdiagnostics::Severity::Trace.into_primitive());
}
#[fuchsia::test]
fn severity_roundtrip_legacy_v1() {
severity_roundtrip_test!(-1, fdiagnostics::Severity::Debug.into_primitive());
}
#[fuchsia::test]
fn verbosity_roundtrip_legacy_v0() {
severity_roundtrip_test!(0, fdiagnostics::Severity::Info.into_primitive());
}
#[fuchsia::test]
fn severity_roundtrip_legacy_warn() {
severity_roundtrip_test!(1, fdiagnostics::Severity::Warn.into_primitive());
}
#[fuchsia::test]
fn verbosity_roundtrip_legacy_error() {
severity_roundtrip_test!(2, fdiagnostics::Severity::Error.into_primitive());
}
#[fuchsia::test]
fn severity_roundtrip_trace() {
severity_roundtrip_test!(
fdiagnostics::Severity::Trace.into_primitive(),
fdiagnostics::Severity::Trace.into_primitive()
);
}
#[fuchsia::test]
fn severity_roundtrip_debug() {
severity_roundtrip_test!(
fdiagnostics::Severity::Debug.into_primitive(),
fdiagnostics::Severity::Debug.into_primitive()
);
}
#[fuchsia::test]
fn severity_roundtrip_info() {
severity_roundtrip_test!(
fdiagnostics::Severity::Info.into_primitive(),
fdiagnostics::Severity::Info.into_primitive()
);
}
#[fuchsia::test]
fn severity_roundtrip_warn() {
severity_roundtrip_test!(
fdiagnostics::Severity::Warn.into_primitive(),
fdiagnostics::Severity::Warn.into_primitive()
);
}
#[fuchsia::test]
fn severity_roundtrip_error() {
severity_roundtrip_test!(
fdiagnostics::Severity::Error.into_primitive(),
fdiagnostics::Severity::Error.into_primitive()
);
}
}