summaryrefslogtreecommitdiffstats
path: root/toolkit/crashreporter/client/app/src/logging.rs
blob: c3f85312f8c2710d850dea1b85519e7950f22054 (plain)
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
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

//! Application logging facilities.

use crate::std::{
    self,
    path::Path,
    sync::{Arc, Mutex},
};

/// Initialize logging and return a log target which can be used to change the destination of log
/// statements.
#[cfg_attr(mock, allow(unused))]
pub fn init() -> LogTarget {
    let log_target_inner = LogTargetInner::default();

    env_logger::builder()
        .parse_env(
            env_logger::Env::new()
                .filter("MOZ_CRASHEREPORTER")
                .write_style("MOZ_CRASHREPORTER_STYLE"),
        )
        .target(env_logger::fmt::Target::Pipe(Box::new(
            log_target_inner.clone(),
        )))
        .init();

    LogTarget {
        inner: log_target_inner,
    }
}

/// Controls the target of logging.
#[derive(Clone)]
pub struct LogTarget {
    inner: LogTargetInner,
}

impl LogTarget {
    /// Set the file to which log statements will be written.
    pub fn set_file(&self, path: &Path) {
        match std::fs::File::create(path) {
            Ok(file) => {
                if let Ok(mut guard) = self.inner.target.lock() {
                    *guard = Box::new(file);
                }
            }
            Err(e) => log::error!("failed to retarget log to {}: {e}", path.display()),
        }
    }
}

/// A private inner class implements Write, allows creation, etc. Externally the `LogTarget` only
/// supports changing the target and nothing else.
#[derive(Clone)]
struct LogTargetInner {
    target: Arc<Mutex<Box<dyn std::io::Write + Send + 'static>>>,
}

impl Default for LogTargetInner {
    fn default() -> Self {
        LogTargetInner {
            target: Arc::new(Mutex::new(Box::new(std::io::stderr()))),
        }
    }
}

impl std::io::Write for LogTargetInner {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        let Ok(mut guard) = self.target.lock() else {
            // Pretend we wrote successfully.
            return Ok(buf.len());
        };
        guard.write(buf)
    }

    fn flush(&mut self) -> std::io::Result<()> {
        let Ok(mut guard) = self.target.lock() else {
            // Pretend we flushed successfully.
            return Ok(());
        };
        guard.flush()
    }
}