#![allow(dead_code, unused_imports)]
use std::{
fs::File,
io::Write,
path::Path,
sync::{atomic::Ordering, Arc},
time::Duration,
};
use color_eyre::eyre::{bail, eyre, Report, Result, WrapErr};
use function_name::named;
use serde::{de::DeserializeOwned, Deserializer};
use tokio::{runtime::Runtime, time::sleep};
use tracing::{error, instrument, subscriber::set_global_default, warn, Subscriber};
use tracing_bunyan_formatter::{BunyanFormattingLayer, JsonStorageLayer, Type};
use tracing_error::ErrorLayer;
use tracing_log::LogTracer;
use tracing_subscriber::{
fmt::{self, MakeWriter},
layer::SubscriberExt,
prelude::*,
util::SubscriberInitExt,
EnvFilter, Registry,
};
use crate::{MAX_CONNECTION_FAILURES, MUT_CURRENT_CONNECTION_FAILURES};
pub fn init_subscriber(log_path: Option<Box<Path>>) {
let setup = |make_writer: Box<dyn Fn() -> Box<dyn Write> + Send + Sync>| {
let formatting_layer = tracing_subscriber::fmt::layer().json().pretty().with_writer(make_writer).with_file(true).with_line_number(true);
let env_filter = tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or(tracing_subscriber::EnvFilter::new("info"));
let error_layer = ErrorLayer::default();
let console_layer = console_subscriber::spawn::<Registry>();
tracing_subscriber::registry()
.with(console_layer)
.with(env_filter)
.with(formatting_layer)
.with(error_layer)
.init();
};
match log_path {
Some(path) => {
let path = path.to_owned();
{
let _ = std::fs::OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(&path)
.expect("Failed to truncate log file");
}
setup(Box::new(move || {
let file = std::fs::OpenOptions::new().create(true).append(true).open(&path).expect("Failed to open log file");
Box::new(file) as Box<dyn Write>
}));
}
None => {
setup(Box::new(|| Box::new(std::io::stdout())));
}
};
}
pub fn format_eyre_chain_for_user(e: eyre::Report) -> String {
let chain = e.chain().rev().collect::<Vec<_>>();
let mut s = String::new();
for (i, e) in chain.into_iter().enumerate() {
if i > 0 {
s.push('\n');
}
s.push_str("-> ");
s.push_str(&e.to_string());
}
s
}
fn deser_reqwest_core<T: DeserializeOwned>(text: String) -> Result<T> {
match serde_json::from_str::<T>(&text) {
Ok(deserialized) => Ok(deserialized),
Err(e) => {
let mut error_msg = e.to_string();
if let Ok(json_value) = serde_json::from_str::<serde_json::Value>(&text) {
let _ = std::panic::catch_unwind(|| {
dbg!(&json_value["symbols"][0]);
});
let mut jd = serde_json::Deserializer::from_str(&text);
let r: Result<T, _> = serde_path_to_error::deserialize(&mut jd);
if let Err(e) = r {
error_msg = e.path().to_string();
}
}
Err(unexpected_response_str(&text)).wrap_err_with(|| error_msg)
}
}
}
pub async fn report_connection_problem(e: Report) -> bool {
let failures = MUT_CURRENT_CONNECTION_FAILURES.fetch_add(1, Ordering::Relaxed);
warn!("Likely connection problem: {:?}", e);
if failures + 1 >= MAX_CONNECTION_FAILURES {
error!("Reached max current connection failures ({MAX_CONNECTION_FAILURES})");
MUT_CURRENT_CONNECTION_FAILURES.store(0, Ordering::Relaxed);
return true;
}
false
}
pub async fn deser_reqwest<T: DeserializeOwned>(r: reqwest::Response) -> Result<T> {
let text = r.text().await?;
deser_reqwest_core(text)
}
pub fn deser_reqwest_blocking<T: DeserializeOwned>(r: reqwest::blocking::Response) -> Result<T> {
let text = r.text()?;
deser_reqwest_core(text)
}
pub fn unexpected_response_str(s: &str) -> eyre::Report {
let s = match serde_json::from_str::<serde_json::Value>(s) {
Ok(v) => serde_json::to_string_pretty(&v).unwrap(),
Err(_) => s.to_owned(),
};
let report = report_msg(s);
report.wrap_err("Unexpected API response")
}
#[track_caller]
#[named]
pub fn report_msg(s: String) -> eyre::Report {
let lines: Vec<&str> = s.lines().collect();
let total_lines = lines.len();
let truncated_message = if total_lines > 50 {
let first_25 = &lines[..25];
let last_25 = &lines[total_lines - 25..];
let truncation_message = format!("------------------------- // truncated at {} by `{}`\n", std::panic::Location::caller(), function_name!());
let concat_message = format!("{}\n{truncation_message}{}", first_25.join("\n"), last_25.join("\n"));
tracing::trace!("Had to concat an error message\n:{s}");
concat_message
} else {
s
};
eyre::Report::msg(truncated_message)
}