use crate::crash_info::{Metadata, TelemetryCrashUploader, UnknownValue};
use crate::CrashtrackerConfiguration;
use libdd_telemetry::data::LogLevel;
use std::sync::Arc;
#[derive(Debug)]
pub(crate) enum ReceiverIssue {
Timeout,
IoError,
ProcessLine,
AttachAdditionalFile,
IncompleteStacktrace,
UnexpectedLine,
NoData,
}
impl ReceiverIssue {
fn tag(&self) -> &'static str {
match self {
ReceiverIssue::Timeout => "receiver_issue:timeout",
ReceiverIssue::IoError => "receiver_issue:io_error",
ReceiverIssue::ProcessLine => "receiver_issue:process_line_error",
ReceiverIssue::AttachAdditionalFile => "receiver_issue:attach_additional_file_error",
ReceiverIssue::IncompleteStacktrace => "receiver_issue:incomplete_stacktrace",
ReceiverIssue::UnexpectedLine => "receiver_issue:unexpected_line",
ReceiverIssue::NoData => "receiver_issue:no_data",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
struct DebugLoggerInputs {
config: bool,
metadata: bool,
}
pub(crate) struct DebugLogger {
uploader: Option<Arc<TelemetryCrashUploader>>,
inputs: DebugLoggerInputs,
}
impl DebugLogger {
pub(crate) fn new(
config: Option<&CrashtrackerConfiguration>,
metadata: Option<&Metadata>,
) -> Self {
let inputs = DebugLoggerInputs {
config: config.is_some(),
metadata: metadata.is_some(),
};
let owned_metadata = metadata.cloned().unwrap_or_else(Metadata::unknown_value);
let endpoint = config.and_then(|config| config.endpoint().clone());
let uploader = TelemetryCrashUploader::new(&owned_metadata, &endpoint)
.ok()
.map(Arc::new);
Self { uploader, inputs }
}
pub(crate) fn update(
&mut self,
config: Option<&CrashtrackerConfiguration>,
metadata: Option<&Metadata>,
) {
let inputs = DebugLoggerInputs {
config: config.is_some(),
metadata: metadata.is_some(),
};
if inputs != self.inputs {
*self = Self::new(config, metadata);
}
}
#[cfg(test)]
pub(crate) fn disabled() -> Self {
Self {
uploader: None,
inputs: DebugLoggerInputs {
config: false,
metadata: false,
},
}
}
fn tags(issue: ReceiverIssue, crash_uuid: &str) -> String {
format!(
"{},crash_uuid:{},is_crash_debug:true",
issue.tag(),
crash_uuid
)
}
pub(crate) fn emit(
&self,
issue: ReceiverIssue,
crash_uuid: &str,
message: String,
level: LogLevel,
) {
if let Some(uploader) = self.uploader.as_ref().map(Arc::clone) {
let tags = Self::tags(issue, crash_uuid);
tokio::spawn(async move {
let _ = uploader.upload_general_log(message, tags, level).await;
});
}
}
pub(crate) async fn emit_and_wait(
&self,
issue: ReceiverIssue,
crash_uuid: &str,
message: String,
level: LogLevel,
) {
if let Some(uploader) = self.uploader.as_ref() {
let tags = Self::tags(issue, crash_uuid);
let _ = uploader.upload_general_log(message, tags, level).await;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg_attr(miri, ignore)] #[test]
fn test_debug_logger_usable_without_config_or_metadata() {
let logger = DebugLogger::new(None, None);
assert!(logger.uploader.is_some());
}
#[cfg_attr(miri, ignore)] #[test]
fn test_debug_logger_rebuilds_as_blocks_arrive() {
let mut logger = DebugLogger::new(None, None);
assert_eq!(
logger.inputs,
DebugLoggerInputs {
config: false,
metadata: false
}
);
logger.update(None, None);
assert_eq!(
logger.inputs,
DebugLoggerInputs {
config: false,
metadata: false
}
);
let metadata = Metadata::unknown_value();
logger.update(None, Some(&metadata));
assert_eq!(
logger.inputs,
DebugLoggerInputs {
config: false,
metadata: true
}
);
assert!(logger.uploader.is_some());
}
}