constellation/
logging.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! The constellation uses logging to perform crash reporting.
6//! The constellation receives all `warn!`, `error!` and `panic!` messages,
7//! and generates a crash report when it receives a panic.
8
9use std::borrow::ToOwned;
10use std::sync::Arc;
11use std::thread;
12
13use backtrace::Backtrace;
14use base::id::WebViewId;
15use constellation_traits::{
16    EmbedderToConstellationMessage, LogEntry, ScriptToConstellationChan,
17    ScriptToConstellationMessage,
18};
19use crossbeam_channel::Sender;
20use log::{Level, LevelFilter, Log, Metadata, Record};
21use parking_lot::ReentrantMutex;
22
23/// A logger directed at the constellation from content processes
24/// #[derive(Clone)]
25pub struct FromScriptLogger {
26    /// A channel to the constellation
27    pub script_to_constellation_chan: Arc<ReentrantMutex<ScriptToConstellationChan>>,
28}
29
30/// A logger directed at the constellation from content processes
31impl FromScriptLogger {
32    /// Create a new constellation logger.
33    pub fn new(script_to_constellation_chan: ScriptToConstellationChan) -> FromScriptLogger {
34        FromScriptLogger {
35            script_to_constellation_chan: Arc::new(ReentrantMutex::new(
36                script_to_constellation_chan,
37            )),
38        }
39    }
40
41    /// The maximum log level the constellation logger is interested in.
42    pub fn filter(&self) -> LevelFilter {
43        LevelFilter::Warn
44    }
45}
46
47impl Log for FromScriptLogger {
48    fn enabled(&self, metadata: &Metadata) -> bool {
49        metadata.level() <= Level::Warn
50    }
51
52    fn log(&self, record: &Record) {
53        if let Some(entry) = log_entry(record) {
54            let thread_name = thread::current().name().map(ToOwned::to_owned);
55            let msg = ScriptToConstellationMessage::LogEntry(thread_name, entry);
56            let chan = self.script_to_constellation_chan.lock();
57            let _ = chan.send(msg);
58        }
59    }
60
61    fn flush(&self) {}
62}
63
64/// A logger directed at the constellation from the compositor
65#[derive(Clone)]
66pub struct FromEmbedderLogger {
67    /// A channel to the constellation
68    pub constellation_chan: Arc<ReentrantMutex<Sender<EmbedderToConstellationMessage>>>,
69}
70
71impl FromEmbedderLogger {
72    /// Create a new constellation logger.
73    pub fn new(constellation_chan: Sender<EmbedderToConstellationMessage>) -> FromEmbedderLogger {
74        FromEmbedderLogger {
75            constellation_chan: Arc::new(ReentrantMutex::new(constellation_chan)),
76        }
77    }
78
79    /// The maximum log level the constellation logger is interested in.
80    pub fn filter(&self) -> LevelFilter {
81        LevelFilter::Warn
82    }
83}
84
85impl Log for FromEmbedderLogger {
86    fn enabled(&self, metadata: &Metadata) -> bool {
87        metadata.level() <= Level::Warn
88    }
89
90    fn log(&self, record: &Record) {
91        if let Some(entry) = log_entry(record) {
92            let webview_id = WebViewId::installed();
93            let thread_name = thread::current().name().map(ToOwned::to_owned);
94            let msg = EmbedderToConstellationMessage::LogEntry(webview_id, thread_name, entry);
95            let chan = self.constellation_chan.lock();
96            let _ = chan.send(msg);
97        }
98    }
99
100    fn flush(&self) {}
101}
102
103/// Rust uses `Record` for storing logging, but servo converts that to
104/// a `LogEntry`. We do this so that we can record panics as well as log
105/// messages, and because `Record` does not implement serde (de)serialization,
106/// so cannot be used over an IPC channel.
107fn log_entry(record: &Record) -> Option<LogEntry> {
108    match record.level() {
109        Level::Error if thread::panicking() => Some(LogEntry::Panic(
110            format!("{}", record.args()),
111            format!("{:?}", Backtrace::new()),
112        )),
113        Level::Error => Some(LogEntry::Error(format!("{}", record.args()))),
114        Level::Warn => Some(LogEntry::Warn(format!("{}", record.args()))),
115        _ => None,
116    }
117}