summaryrefslogtreecommitdiffstats
path: root/services/common/app_services_logger
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--services/common/app_services_logger/AppServicesLoggerComponents.h38
-rw-r--r--services/common/app_services_logger/Cargo.toml16
-rw-r--r--services/common/app_services_logger/components.conf15
-rw-r--r--services/common/app_services_logger/src/lib.rs139
4 files changed, 208 insertions, 0 deletions
diff --git a/services/common/app_services_logger/AppServicesLoggerComponents.h b/services/common/app_services_logger/AppServicesLoggerComponents.h
new file mode 100644
index 0000000000..015eae230b
--- /dev/null
+++ b/services/common/app_services_logger/AppServicesLoggerComponents.h
@@ -0,0 +1,38 @@
+/* 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 http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_services_AppServicesLoggerComponents_h_
+#define mozilla_services_AppServicesLoggerComponents_h_
+
+#include "mozIAppServicesLogger.h"
+#include "nsCOMPtr.h"
+
+extern "C" {
+
+// Implemented in Rust, in the `app_services_logger` crate.
+nsresult NS_NewAppServicesLogger(mozIAppServicesLogger** aResult);
+
+} // extern "C"
+
+namespace mozilla {
+namespace appservices {
+
+// The C++ constructor for a `services.appServicesLogger` service. This wrapper
+// exists because `components.conf` requires a component class constructor to
+// return an `already_AddRefed<T>`, but Rust doesn't have such a type. So we
+// call the Rust constructor using a `nsCOMPtr` (which is compatible with Rust's
+// `xpcom::RefPtr`) out param, and return that.
+already_AddRefed<mozIAppServicesLogger> NewLogService() {
+ nsCOMPtr<mozIAppServicesLogger> logger;
+ nsresult rv = NS_NewAppServicesLogger(getter_AddRefs(logger));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return nullptr;
+ }
+ return logger.forget();
+}
+
+} // namespace appservices
+} // namespace mozilla
+
+#endif // mozilla_services_AppServicesLoggerComponents_h_
diff --git a/services/common/app_services_logger/Cargo.toml b/services/common/app_services_logger/Cargo.toml
new file mode 100644
index 0000000000..5db41ddcdb
--- /dev/null
+++ b/services/common/app_services_logger/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "app_services_logger"
+version = "0.1.0"
+authors = ["lougeniac64 <lougeniaC64@users.noreply.github.com>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+cstr = "0.2"
+golden_gate = { path = "../../../services/sync/golden_gate" }
+log = "0.4"
+once_cell = "1.4.0"
+nserror = { path = "../../../xpcom/rust/nserror" }
+nsstring = { path = "../../../xpcom/rust/nsstring" }
+xpcom = { path = "../../../xpcom/rust/xpcom" }
diff --git a/services/common/app_services_logger/components.conf b/services/common/app_services_logger/components.conf
new file mode 100644
index 0000000000..cdea60a04b
--- /dev/null
+++ b/services/common/app_services_logger/components.conf
@@ -0,0 +1,15 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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 http://mozilla.org/MPL/2.0/.
+
+Classes = [
+ {
+ 'cid': '{d2716568-f5fa-4989-91dd-e11599e932a1}',
+ 'contract_ids': ['@mozilla.org/appservices/logger;1'],
+ 'type': 'mozIAppServicesLogger',
+ 'headers': ['mozilla/appservices/AppServicesLoggerComponents.h'],
+ 'constructor': 'mozilla::appservices::NewLogService',
+ },
+]
diff --git a/services/common/app_services_logger/src/lib.rs b/services/common/app_services_logger/src/lib.rs
new file mode 100644
index 0000000000..18873e8ba9
--- /dev/null
+++ b/services/common/app_services_logger/src/lib.rs
@@ -0,0 +1,139 @@
+/* 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 XPCOM service to send app services logs to the desktop
+
+#[macro_use]
+extern crate cstr;
+
+#[macro_use]
+extern crate xpcom;
+
+use golden_gate::log::LogSink;
+use log;
+use nserror::{nsresult, NS_OK};
+use nsstring::nsAString;
+use once_cell::sync::Lazy;
+use std::os::raw::c_char;
+use std::{
+ cmp,
+ collections::HashMap,
+ sync::{
+ atomic::{AtomicBool, Ordering},
+ RwLock,
+ },
+};
+use xpcom::{
+ interfaces::{mozIAppServicesLogger, mozIServicesLogSink, nsISupports},
+ RefPtr,
+};
+
+/// A flag that's set after we register our observer to clear the map of loggers
+/// on shutdown.
+static SHUTDOWN_OBSERVED: AtomicBool = AtomicBool::new(false);
+
+#[derive(xpcom)]
+#[xpimplements(mozIAppServicesLogger)]
+#[refcnt = "nonatomic"]
+pub struct InitAppServicesLogger {}
+
+pub static LOGGERS_BY_TARGET: Lazy<RwLock<HashMap<String, LogSink>>> = Lazy::new(|| {
+ let h: HashMap<String, LogSink> = HashMap::new();
+ let m = RwLock::new(h);
+ m
+});
+
+impl AppServicesLogger {
+ xpcom_method!(register => Register(target: *const nsAString, logger: *const mozIServicesLogSink));
+ fn register(&self, target: &nsAString, logger: &mozIServicesLogSink) -> Result<(), nsresult> {
+ let log_sink_logger = LogSink::with_logger(Some(logger))?;
+ let max_level = cmp::max(log::max_level(), log_sink_logger.max_level);
+
+ // Note: This will only work if the max_level is lower than the compile-time
+ // max_level_* filter.
+ log::set_max_level(max_level);
+
+ ensure_observing_shutdown();
+
+ LOGGERS_BY_TARGET
+ .write()
+ .unwrap()
+ .insert(target.to_string(), log_sink_logger);
+ Ok(())
+ }
+
+ pub fn is_app_services_logger_registered(target: String) -> bool {
+ match LOGGERS_BY_TARGET.read() {
+ Ok(loggers_by_target) => loggers_by_target.contains_key(&target),
+ Err(_e) => false,
+ }
+ }
+}
+
+// Import the `NS_IsMainThread` symbol from Gecko...
+extern "C" {
+ fn NS_IsMainThread() -> bool;
+}
+
+/// Registers an observer to clear the loggers map on `xpcom-shutdown`. This
+/// function must be called from the main thread, because the observer service
+//// is main thread-only.
+fn ensure_observing_shutdown() {
+ assert!(unsafe { NS_IsMainThread() });
+ // If we've already registered our observer, bail. Relaxed ordering is safe
+ // here and below, because we've asserted we're only called from the main
+ // thread, and only check the flag here.
+ if SHUTDOWN_OBSERVED.load(Ordering::Relaxed) {
+ return;
+ }
+ if let Some(service) = xpcom::services::get_ObserverService() {
+ let observer = ShutdownObserver::allocate(InitShutdownObserver {});
+ let rv = unsafe {
+ service.AddObserver(observer.coerce(), cstr!("xpcom-shutdown").as_ptr(), false)
+ };
+ // If we fail to register the observer now, or fail to get the observer
+ // service, the flag will remain `false`, and we'll try again on the
+ // next call to `ensure_observing_shutdown`.
+ SHUTDOWN_OBSERVED.store(rv.succeeded(), Ordering::Relaxed);
+ }
+}
+
+#[derive(xpcom)]
+#[xpimplements(nsIObserver)]
+#[refcnt = "nonatomic"]
+struct InitShutdownObserver {}
+
+impl ShutdownObserver {
+ xpcom_method!(observe => Observe(_subject: *const nsISupports, topic: *const c_char, _data: *const i16));
+ /// Remove our shutdown observer and clear the map.
+ fn observe(
+ &self,
+ _subject: &nsISupports,
+ topic: *const c_char,
+ _data: *const i16,
+ ) -> Result<(), nsresult> {
+ LOGGERS_BY_TARGET.write().unwrap().clear();
+ if let Some(service) = xpcom::services::get_ObserverService() {
+ // Ignore errors, since we're already shutting down.
+ let _ = unsafe { service.RemoveObserver(self.coerce(), topic) };
+ }
+ Ok(())
+ }
+}
+
+/// The constructor for an `AppServicesLogger` service. This uses C linkage so that it
+/// can be called from C++. See `AppServicesLoggerComponents.h` for the C++
+/// constructor that's passed to the component manager.
+///
+/// # Safety
+///
+/// This function is unsafe because it dereferences `result`.
+#[no_mangle]
+pub unsafe extern "C" fn NS_NewAppServicesLogger(
+ result: *mut *const mozIAppServicesLogger,
+) -> nsresult {
+ let logger = AppServicesLogger::allocate(InitAppServicesLogger {});
+ RefPtr::new(logger.coerce::<mozIAppServicesLogger>()).forget(&mut *result);
+ NS_OK
+}