node_launchpad/
utils.rs

1// Copyright 2024 MaidSafe.net limited.
2//
3// This SAFE Network Software is licensed to you under The General Public License (GPL), version 3.
4// Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed
5// under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
6// KIND, either express or implied. Please review the Licences for the specific language governing
7// permissions and limitations relating to use of the SAFE Network Software.
8
9use crate::config::get_launchpad_data_dir_path;
10use color_eyre::eyre::{Context, Result};
11use tracing::error;
12use tracing_error::ErrorLayer;
13use tracing_subscriber::{
14    self, Layer, prelude::__tracing_subscriber_SubscriberExt, util::SubscriberInitExt,
15};
16
17pub fn initialize_panic_handler() -> Result<()> {
18    let (panic_hook, eyre_hook) = color_eyre::config::HookBuilder::default()
19        .panic_section(format!(
20            "This is a bug. Consider reporting it at {}",
21            env!("CARGO_PKG_REPOSITORY")
22        ))
23        .capture_span_trace_by_default(false)
24        .display_location_section(false)
25        .display_env_section(false)
26        .into_hooks();
27    eyre_hook.install()?;
28    std::panic::set_hook(Box::new(move |panic_info| {
29        if let Ok(mut t) = crate::tui::Tui::new()
30            && let Err(r) = t.exit()
31        {
32            error!("Unable to exit Terminal: {:?}", r);
33        }
34
35        #[cfg(not(debug_assertions))]
36        {
37            use human_panic::{Metadata, handle_dump, print_msg};
38            let meta = Metadata {
39                version: env!("CARGO_PKG_VERSION").into(),
40                name: env!("CARGO_PKG_NAME").into(),
41                authors: env!("CARGO_PKG_AUTHORS").replace(':', ", ").into(),
42                homepage: "https://autonomi.com/".into(),
43            };
44
45            let file_path = handle_dump(&meta, panic_info);
46            // prints human-panic message
47            print_msg(file_path, &meta)
48                .expect("human-panic: printing error message to console failed");
49            eprintln!("{}", panic_hook.panic_report(panic_info)); // prints color-eyre stack trace to stderr
50        }
51        let msg = format!("{}", panic_hook.panic_report(panic_info));
52        log::error!("Error: {}", strip_ansi_escapes::strip_str(msg));
53
54        #[cfg(debug_assertions)]
55        {
56            // Better Panic stacktrace that is only enabled when debugging.
57            better_panic::Settings::auto()
58                .most_recent_first(false)
59                .lineno_suffix(true)
60                .verbosity(better_panic::Verbosity::Full)
61                .create_panic_handler()(panic_info);
62        }
63
64        std::process::exit(libc::EXIT_FAILURE);
65    }));
66    Ok(())
67}
68
69// Gets the current logging path
70pub fn get_logging_path() -> Result<std::path::PathBuf> {
71    let log_path = get_launchpad_data_dir_path()?.join("logs");
72    Ok(log_path)
73}
74
75// TODO: use ant_logging
76pub fn initialize_logging() -> Result<()> {
77    let timestamp = chrono::Local::now().format("%Y-%m-%d_%H-%M-%S").to_string();
78    let log_path = get_logging_path()?;
79    std::fs::create_dir_all(&log_path)?;
80    let log_file = std::fs::File::create(log_path.join(format!("launchpad_{timestamp}.log")))
81        .context(format!("Failed to create file {log_path:?}"))?;
82
83    // SAFETY: This is called during application initialization before any other threads
84    // are spawned, so there's no risk of data races. Setting RUST_LOG is necessary
85    // to configure logging levels for the application.
86    #[allow(unsafe_code)]
87    unsafe {
88        std::env::set_var(
89            "RUST_LOG",
90            std::env::var("RUST_LOG").unwrap_or_else(|_| {
91                format!(
92                    "{}=trace,ant_node_manager=trace,ant_service_management=trace,ant_bootstrap=debug",
93                    env!("CARGO_CRATE_NAME")
94                )
95            }),
96        );
97    }
98    let file_subscriber = tracing_subscriber::fmt::layer()
99        .with_file(true)
100        .with_line_number(true)
101        .with_writer(log_file)
102        .with_target(false)
103        .with_ansi(false)
104        .with_filter(tracing_subscriber::filter::EnvFilter::from_default_env());
105    tracing_subscriber::registry()
106        .with(file_subscriber)
107        .with(ErrorLayer::default())
108        .init();
109    Ok(())
110}
111
112/// Similar to the `std::dbg!` macro, but generates `tracing` events rather
113/// than printing to stdout.
114///
115/// By default, the verbosity level for the generated events is `DEBUG`, but
116/// this can be customized.
117#[macro_export]
118macro_rules! trace_dbg {
119    (target: $target:expr, level: $level:expr, $ex:expr) => {{
120        match $ex {
121            value => {
122                tracing::event!(target: $target, $level, ?value, stringify!($ex));
123                value
124            }
125        }
126    }};
127    (level: $level:expr, $ex:expr) => {
128        trace_dbg!(target: module_path!(), level: $level, $ex)
129    };
130    (target: $target:expr, $ex:expr) => {
131        trace_dbg!(target: $target, level: tracing::Level::DEBUG, $ex)
132    };
133    ($ex:expr) => {
134        trace_dbg!(level: tracing::Level::DEBUG, $ex)
135    };
136}