darkwing/
logger.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
//! Logging configuration and initialization module.
//!
//! This module provides functionality to set up application-wide logging using
//! the tracing framework, with support for different environments and panic
//! handling.

use std::{panic, thread};

use tracing::{error, level_filters::LevelFilter};
use tracing_appender::non_blocking::WorkerGuard;

use crate::config::CargoEnv;

/// Logger initialization and configuration struct.
///
/// Provides functionality to initialize the logging system with appropriate
/// settings based on the environment (development, test, or production).
pub struct Logger {}

impl Logger {
  /// Initializes the logging system with environment-specific configuration.
  ///
  /// # Arguments
  ///
  /// * `cargo_env` - The environment configuration (Development, Test, or
  ///   Production)
  /// * `with_panic_hook` - Whether to install a custom panic hook that logs
  ///   panics
  ///
  /// # Returns
  ///
  /// Returns a `WorkerGuard` that must be held for the duration of the program
  /// to ensure log messages are fully written.
  ///
  /// # Features
  ///
  /// * Configures different log levels based on environment
  /// * Development: Logs to console at DEBUG level
  /// * Test/Production: Logs to daily rotating files at DEBUG/INFO level
  /// * Optional panic hook installation for improved error reporting
  /// * Integration with Sentry for error tracking
  ///
  /// # Examples
  ///
  /// ```
  /// use darkwing::{Logger, CargoEnv};
  ///
  /// let _guard = Logger::init(CargoEnv::Development, true);
  /// // Keep guard in scope while application runs
  /// ```
  pub fn init(cargo_env: CargoEnv, with_panic_hook: bool) -> WorkerGuard {
    let file_logger = tracing_appender::rolling::daily("logs", "daily.log");
    let console_logger = std::io::stdout();

    let max_level = match cargo_env {
      CargoEnv::Development => LevelFilter::DEBUG,
      CargoEnv::Test => LevelFilter::DEBUG,
      CargoEnv::Production => LevelFilter::INFO,
    };

    let (non_blocking, guard) = match cargo_env {
      CargoEnv::Development => tracing_appender::non_blocking(console_logger),
      CargoEnv::Test => tracing_appender::non_blocking(file_logger),
      CargoEnv::Production => tracing_appender::non_blocking(file_logger),
    };

    tracing_subscriber::fmt()
      .with_writer(non_blocking)
      .with_max_level(max_level)
      .init();

    // Install custom panic hook for improved error reporting
    if with_panic_hook {
      panic::set_hook(Box::new(|info| {
        let thread = thread::current();
        let thread = thread.name().unwrap_or("unknown");

        let msg = match info.payload().downcast_ref::<&'static str>() {
          Some(s) => *s,
          None => match info.payload().downcast_ref::<String>() {
            Some(s) => &**s,
            None => "Box<Any>",
          },
        };

        let backtrace = backtrace::Backtrace::new();

        match info.location() {
          Some(location) => {
            // Log without backtrace if message starts with "notrace - "
            if msg.starts_with("notrace - ") {
              error!(
                  target: "panic", "thread '{}' panicked at '{}': {}:{}",
                  thread,
                  msg.replace("notrace - ", ""),
                  location.file(),
                  location.line()
              );
            } else {
              // Log with backtrace
              error!(
                  target: "panic", "thread '{}' panicked at '{}': {}:{}\n{:?}",
                  thread,
                  msg,
                  location.file(),
                  location.line(),
                  backtrace
              );
            }
          }
          None => {
            // Log without backtrace if message starts with "notrace - "
            if msg.starts_with("notrace - ") {
              error!(
                  target: "panic", "thread '{}' panicked at '{}'",
                  thread,
                  msg.replace("notrace - ", ""),
              );
            } else {
              // Log with backtrace
              error!(
                  target: "panic", "thread '{}' panicked at '{}'\n{:?}",
                  thread,
                  msg,
                  backtrace
              );
            }
          }
        }

        // Forward panic to Sentry
        sentry::integrations::panic::panic_handler(info);
      }));
    }

    guard
  }
}