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
// Copyright (c) 2022, BlockProject 3D
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright notice,
//       this list of conditions and the following disclaimer in the documentation
//       and/or other materials provided with the distribution.
//     * Neither the name of BlockProject 3D nor the names of its contributors
//       may be used to endorse or promote products derived from this software
//       without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use std::any::Any;
use std::sync::atomic::{AtomicUsize, Ordering};
use bp3d_fs::dirs::App;
use tracing::subscriber::set_global_default;
use crate::core::{Tracer, TracingSystem};
use crate::logger::Logger;
use crate::profiler::Profiler;

mod core;
mod util;
mod logger;
mod profiler;

/// The guard to ensure proper termination of logging and tracing systems.
pub struct Guard(Option<Box<dyn Any>>);

impl Guard {
    /// Run the following closure then terminate logging and tracing systems.
    pub fn run<R, F: FnOnce() -> R>(self, func: F) -> R {
        func()
    }
}

fn load_system<T: 'static + Tracer + Sync + Send>(system: TracingSystem<T>) -> Guard {
    set_global_default(system.system).expect("bp3d-tracing can only be initialized once!");
    Guard(system.destructor)
}

/// Initialize the logging and tracing systems for the given application.
///
/// The function returns a guard which must be maintained for the duration of the application.
pub fn initialize<T: AsRef<str>>(app: T) -> Guard {
    {
        let app = App::new(app.as_ref());
        if let Ok(v) = app.get_documents().map(|v| v.join("environment")) {
            bp3d_env::add_override_path(&v);
        }
    }
    let profiler = bp3d_env::get_bool("PROFILER").unwrap_or(false);
    if profiler {
        Profiler::new(app.as_ref()).map(load_system).unwrap_or_else(|_| load_system(Logger::new(app.as_ref())))
    } else {
        load_system(Logger::new(app.as_ref()))
    }
}

static LOG_BUFFER_RC: AtomicUsize = AtomicUsize::new(0);

static STDOUT_DISABLE_RC: AtomicUsize = AtomicUsize::new(0);

/// A struct to automate management of the in-memory log buffer.
///
/// When a new instance of this struct is created, the log buffer is automatically enabled if not
/// already. Inversely, when all instances of this struct are dropped, the log buffer is disabled.
pub struct LogBuffer(bp3d_logger::LogBuffer);

impl LogBuffer {
    /// Creates a new access to the in-memory log buffer.
    pub fn new() -> LogBuffer {
        if LOG_BUFFER_RC.fetch_add(1, Ordering::Relaxed) == 0 {
            //If no previous buffers were created, enable the log buffer.
            bp3d_logger::enable_log_buffer();
        }
        LogBuffer(bp3d_logger::get_log_buffer())
    }

    /// Attempts to pull a message from the in-memory log buffer.
    pub fn pull(&self) -> Option<bp3d_logger::LogMsg> {
        self.0.try_recv().ok()
    }
}

impl Drop for LogBuffer {
    fn drop(&mut self) {
        if LOG_BUFFER_RC.fetch_sub(1, Ordering::Relaxed) == 1 {
            //If no more log buffers exists after this one, disable the log buffer.
            bp3d_logger::disable_log_buffer();
        }
    }
}

/// A struct to automate enabling and disabling of the stdout/stderr logger.
///
/// When a new instance of this struct is created, the stdout/stderr logger is automatically
/// disabled if not already. Inversely, when all instances of this struct are dropped, the
/// stdout/stderr logger is re-enabled.
pub struct DisableStdoutLogger;

impl DisableStdoutLogger {
    /// Temporarily disables stdout/stderr logging for the lifespan of this struct.
    pub fn new() -> DisableStdoutLogger {
        if STDOUT_DISABLE_RC.fetch_add(1, Ordering::Relaxed) == 0 {
            //If no previous instances were created, disable the stdout/stderr logger.
            //First, flush any waiting message.
            bp3d_logger::flush();
            //Then disable the backend.
            bp3d_logger::disable_stdout();
        }
        DisableStdoutLogger
    }
}

impl Drop for DisableStdoutLogger {
    fn drop(&mut self) {
        if STDOUT_DISABLE_RC.fetch_sub(1, Ordering::Relaxed) == 1 {
            //If no more instances exists after this one, re-enable the stdout/stderr logger.
            bp3d_logger::enable_stdout();
        }
    }
}