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
/* 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/. */

use std::borrow::ToOwned;
use std::sync::Arc;
use std::thread;

use backtrace::Backtrace;
use base::id::TopLevelBrowsingContextId;
use compositing_traits::ConstellationMsg as FromCompositorMsg;
use crossbeam_channel::Sender;
use log::{Level, LevelFilter, Log, Metadata, Record};
use parking_lot::ReentrantMutex;
use script_traits::{LogEntry, ScriptMsg as FromScriptMsg, ScriptToConstellationChan};

/// The constellation uses logging to perform crash reporting.
/// The constellation receives all `warn!`, `error!` and `panic!` messages,
/// and generates a crash report when it receives a panic.

/// A logger directed at the constellation from content processes
/// #[derive(Clone)]
pub struct FromScriptLogger {
    /// A channel to the constellation
    pub script_to_constellation_chan: Arc<ReentrantMutex<ScriptToConstellationChan>>,
}

/// The constellation uses logging to perform crash reporting.
/// The constellation receives all `warn!`, `error!` and `panic!` messages,
/// and generates a crash report when it receives a panic.

/// A logger directed at the constellation from content processes
impl FromScriptLogger {
    /// Create a new constellation logger.
    pub fn new(script_to_constellation_chan: ScriptToConstellationChan) -> FromScriptLogger {
        FromScriptLogger {
            script_to_constellation_chan: Arc::new(ReentrantMutex::new(
                script_to_constellation_chan,
            )),
        }
    }

    /// The maximum log level the constellation logger is interested in.
    pub fn filter(&self) -> LevelFilter {
        LevelFilter::Warn
    }
}

impl Log for FromScriptLogger {
    fn enabled(&self, metadata: &Metadata) -> bool {
        metadata.level() <= Level::Warn
    }

    fn log(&self, record: &Record) {
        if let Some(entry) = log_entry(record) {
            let thread_name = thread::current().name().map(ToOwned::to_owned);
            let msg = FromScriptMsg::LogEntry(thread_name, entry);
            let chan = self.script_to_constellation_chan.lock();
            let _ = chan.send(msg);
        }
    }

    fn flush(&self) {}
}

/// A logger directed at the constellation from the compositor
#[derive(Clone)]
pub struct FromCompositorLogger {
    /// A channel to the constellation
    pub constellation_chan: Arc<ReentrantMutex<Sender<FromCompositorMsg>>>,
}

impl FromCompositorLogger {
    /// Create a new constellation logger.
    pub fn new(constellation_chan: Sender<FromCompositorMsg>) -> FromCompositorLogger {
        FromCompositorLogger {
            constellation_chan: Arc::new(ReentrantMutex::new(constellation_chan)),
        }
    }

    /// The maximum log level the constellation logger is interested in.
    pub fn filter(&self) -> LevelFilter {
        LevelFilter::Warn
    }
}

impl Log for FromCompositorLogger {
    fn enabled(&self, metadata: &Metadata) -> bool {
        metadata.level() <= Level::Warn
    }

    fn log(&self, record: &Record) {
        if let Some(entry) = log_entry(record) {
            let top_level_id = TopLevelBrowsingContextId::installed();
            let thread_name = thread::current().name().map(ToOwned::to_owned);
            let msg = FromCompositorMsg::LogEntry(top_level_id, thread_name, entry);
            let chan = self.constellation_chan.lock();
            let _ = chan.send(msg);
        }
    }

    fn flush(&self) {}
}

/// Rust uses `Record` for storing logging, but servo converts that to
/// a `LogEntry`. We do this so that we can record panics as well as log
/// messages, and because `Record` does not implement serde (de)serialization,
/// so cannot be used over an IPC channel.
fn log_entry(record: &Record) -> Option<LogEntry> {
    match record.level() {
        Level::Error if thread::panicking() => Some(LogEntry::Panic(
            format!("{}", record.args()),
            format!("{:?}", Backtrace::new()),
        )),
        Level::Error => Some(LogEntry::Error(format!("{}", record.args()))),
        Level::Warn => Some(LogEntry::Warn(format!("{}", record.args()))),
        _ => None,
    }
}