Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  lib.rs   Sprache: unbekannt

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

//! This provides a way to direct rust logging into the gecko logger.

#[macro_use]
extern crate lazy_static;

use app_services_logger::{AppServicesLogger, LOGGERS_BY_TARGET};
use log::Log;
use log::{Level, LevelFilter};
use std::collections::HashMap;
use std::ffi::{CStr, CString};
use std::os::raw::c_char;
use std::os::raw::c_int;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::RwLock;
use std::{cmp, env};

extern "C" {
    fn ExternMozLog(tag: *const c_char, prio: c_int, text: *const c_char);
    fn gfx_critical_note(msg: *const c_char);
    #[cfg(target_os = "android")]
    fn __android_log_write(prio: c_int, tag: *const c_char, text: *const c_char) -> c_int;
}

lazy_static! {
    // This could be a proper static once [1] is fixed or parking_lot's const fn
    // support is not nightly-only.
    //
    // [1]: https://github.com/rust-lang/rust/issues/73714
    static ref LOG_MODULE_MAP: RwLock<HashMap<String, (LevelFilter, bool)>> = RwLock::new(HashMap::new());
}

/// This tells us whether LOG_MODULE_MAP is possibly non-empty.
static LOGGING_ACTIVE: AtomicBool = AtomicBool::new(false);

/// This function searches for the module's name in the hashmap. If that is not
/// found, it proceeds to search for the parent modules.
/// It returns a tuple containing the matched string, if the matched module
/// was a pattern match, and the level we found in the hashmap.
/// If none is found, it will return the module's name and LevelFilter::Off
fn get_level_for_module<'a>(
    map: &HashMap<String, (LevelFilter, bool)>,
    key: &'a str,
) -> (&'a str, bool, LevelFilter) {
    if let Some((level, is_pattern_match)) = map.get(key) {
        return (key, *is_pattern_match, level.clone());
    }

    let mut mod_name = &key[..];
    while let Some(pos) = mod_name.rfind("::") {
        mod_name = &mod_name[..pos];
        if let Some((level, is_pattern_match)) = map.get(mod_name) {
            return (mod_name, *is_pattern_match, level.clone());
        }
    }

    return (key, false, LevelFilter::Off);
}

/// This function takes a record to maybe log to Gecko.
/// It returns true if the record was handled by Gecko's logging, and false
/// otherwise.
pub fn log_to_gecko(record: &log::Record) -> bool {
    if !LOGGING_ACTIVE.load(Ordering::Relaxed) {
        return false;
    }

    let key = match record.module_path() {
        Some(key) => key,
        None => return false,
    };

    let (mod_name, is_pattern_match, level) = {
        let map = LOG_MODULE_MAP.read().unwrap();
        get_level_for_module(&map, &key)
    };

    if level == LevelFilter::Off {
        return false;
    }

    if level < record.metadata().level() {
        return false;
    }

    // Map the log::Level to mozilla::LogLevel.
    let moz_log_level = match record.metadata().level() {
        Level::Error => 1, // Error
        Level::Warn => 2,  // Warning
        Level::Info => 3,  // Info
        Level::Debug => 4, // Debug
        Level::Trace => 5, // Verbose
    };

    // If it was a pattern match, we need to append ::* to the matched string.
    let (tag, msg) = if is_pattern_match {
        (
            CString::new(format!("{}::*", mod_name)).unwrap(),
            CString::new(format!("[{}] {}", key, record.args())).unwrap(),
        )
    } else {
        (
            CString::new(key).unwrap(),
            CString::new(format!("{}", record.args())).unwrap(),
        )
    };

    unsafe {
        ExternMozLog(tag.as_ptr(), moz_log_level, msg.as_ptr());
    }

    return true;
}

#[no_mangle]
pub unsafe extern "C" fn set_rust_log_level(module: *const c_char, level: u8) {
    // Convert the Gecko level to a rust LevelFilter.
    let rust_level = match level {
        1 => LevelFilter::Error,
        2 => LevelFilter::Warn,
        3 => LevelFilter::Info,
        4 => LevelFilter::Debug,
        5 => LevelFilter::Trace,
        _ => LevelFilter::Off,
    };

    // This is the name of the rust module that we're trying to log in Gecko.
    let mut mod_name = CStr::from_ptr(module).to_string_lossy().into_owned();

    let is_pattern_match = mod_name.ends_with("::*");

    // If this is a pattern, remove the last "::*" from it so we can search it
    // in the map.
    if is_pattern_match {
        let len = mod_name.len() - 3;
        mod_name.truncate(len);
    }

    LOGGING_ACTIVE.store(true, Ordering::Relaxed);
    let mut map = LOG_MODULE_MAP.write().unwrap();
    map.insert(mod_name, (rust_level, is_pattern_match));

    // Figure out the max level of all the modules.
    let max = map
        .values()
        .map(|(lvl, _)| lvl)
        .max()
        .unwrap_or(&LevelFilter::Off);
    log::set_max_level(*max);
}

pub struct GeckoLogger {
    logger: env_logger::Logger,
}

impl GeckoLogger {
    pub fn new() -> GeckoLogger {
        let mut builder = env_logger::Builder::new();
        let default_level = if cfg!(debug_assertions) {
            "warn"
        } else {
            "error"
        };
        let logger = match env::var("RUST_LOG") {
            Ok(v) => builder.parse_filters(&v).build(),
            _ => builder.parse_filters(default_level).build(),
        };

        GeckoLogger { logger }
    }

    pub fn init() -> Result<(), log::SetLoggerError> {
        let gecko_logger = Self::new();

        // The max level may have already been set by gecko_logger. Don't
        // set it to a lower level.
        let level = cmp::max(log::max_level(), gecko_logger.logger.filter());
        log::set_max_level(level);
        log::set_boxed_logger(Box::new(gecko_logger))
    }

    fn should_log_to_app_services(target: &str) -> bool {
        return AppServicesLogger::is_app_services_logger_registered(target.into());
    }

    fn maybe_log_to_app_services(&self, record: &log::Record) {
        if Self::should_log_to_app_services(record.target()) {
            if let Some(l) = LOGGERS_BY_TARGET.read().unwrap().get(record.target()) {
                l.log(record);
            }
        }
    }

    fn should_log_to_gfx_critical_note(record: &log::Record) -> bool {
        record.level() == log::Level::Error && record.target().contains("webrender")
    }

    fn maybe_log_to_gfx_critical_note(&self, record: &log::Record) {
        if Self::should_log_to_gfx_critical_note(record) {
            let msg = CString::new(format!("{}", record.args())).unwrap();
            unsafe {
                gfx_critical_note(msg.as_ptr());
            }
        }
    }

    #[cfg(not(target_os = "android"))]
    fn log_out(&self, record: &log::Record) {
        // If the log wasn't handled by the gecko platform logger, just pass it
        // to the env_logger.
        if !log_to_gecko(record) {
            self.logger.log(record);
        }
    }

    #[cfg(target_os = "android")]
    fn log_out(&self, record: &log::Record) {
        if !self.logger.matches(record) {
            return;
        }

        let msg = CString::new(format!("{}", record.args())).unwrap();
        let tag = CString::new(record.module_path().unwrap()).unwrap();
        let prio = match record.metadata().level() {
            Level::Error => 6, /* ERROR */
            Level::Warn => 5,  /* WARN */
            Level::Info => 4,  /* INFO */
            Level::Debug => 3, /* DEBUG */
            Level::Trace => 2, /* VERBOSE */
        };
        // Output log directly to android log, since env_logger can output log
        // only to stderr or stdout.
        unsafe {
            __android_log_write(prio, tag.as_ptr(), msg.as_ptr());
        }
    }
}

impl log::Log for GeckoLogger {
    fn enabled(&self, metadata: &log::Metadata) -> bool {
        self.logger.enabled(metadata) || GeckoLogger::should_log_to_app_services(metadata.target())
    }

    fn log(&self, record: &log::Record) {
        // Forward log to gfxCriticalNote, if the log should be in gfx crash log.
        self.maybe_log_to_gfx_critical_note(record);
        self.maybe_log_to_app_services(record);
        self.log_out(record);
    }

    fn flush(&self) {}
}

[ Dauer der Verarbeitung: 0.25 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge