Bug 1452200 - 1a. Add AndroidAppender for Log.jsm; r=markh
authorJim Chen <nchen@mozilla.com>
Sun, 15 Apr 2018 14:53:28 -0400
changeset 466965 aeb280ada8cb17576e755223deafad0a439238bd
parent 466964 2b7cbea0291d31ab704b238f7b86de376660dbaf
child 466966 0f1f1f69f5d68328f48a9cea997db7037651d1a4
push id9165
push userasasaki@mozilla.com
push dateThu, 26 Apr 2018 21:04:54 +0000
treeherdermozilla-beta@064c3804de2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmarkh
bugs1452200
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1452200 - 1a. Add AndroidAppender for Log.jsm; r=markh Add an AndroidAppender that lets Log.jsm output to the Android logs, using AndroidLog.jsm. Because the Android logging system keeps track of the log metadata (time/level/name) separately from the log message, the patch also adds a separate AndroidFormatter that does not prepend the metadata to the log message itself. MozReview-Commit-ID: C9oBbgVQOEc
toolkit/modules/Log.jsm
--- a/toolkit/modules/Log.jsm
+++ b/toolkit/modules/Log.jsm
@@ -9,22 +9,22 @@ var EXPORTED_SYMBOLS = ["Log"];
 const ONE_BYTE = 1;
 const ONE_KILOBYTE = 1024 * ONE_BYTE;
 const ONE_MEGABYTE = 1024 * ONE_KILOBYTE;
 
 const STREAM_SEGMENT_SIZE = 4096;
 const PR_UINT32_MAX = 0xffffffff;
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-ChromeUtils.defineModuleGetter(this, "OS",
-                               "resource://gre/modules/osfile.jsm");
-ChromeUtils.defineModuleGetter(this, "Task",
-                               "resource://gre/modules/Task.jsm");
-ChromeUtils.defineModuleGetter(this, "Services",
-                               "resource://gre/modules/Services.jsm");
+XPCOMUtils.defineLazyModuleGetters(this, {
+  AndroidLog: "resource://gre/modules/AndroidLog.jsm", // Only used on Android.
+  OS: "resource://gre/modules/osfile.jsm",
+  Services: "resource://gre/modules/Services.jsm",
+  Task: "resource://gre/modules/Task.jsm",
+});
 const INTERNAL_FIELDS = new Set(["_level", "_message", "_time", "_namespace"]);
 
 
 /*
  * Dump a message everywhere we can if we have a failure.
  */
 function dumpError(text) {
   dump(text + "\n");
@@ -81,16 +81,17 @@ var Log = {
   BasicFormatter,
   MessageOnlyFormatter,
   StructuredFormatter,
 
   Appender,
   DumpAppender,
   ConsoleAppender,
   StorageStreamAppender,
+  AndroidAppender,
 
   FileAppender,
   BoundedFileAppender,
 
   ParameterFormatter,
   // Logging helper:
   // let logger = Log.repository.getLogger("foo");
   // logger.info(Log.enumerateInterfaces(someObject).join(","));
@@ -672,16 +673,31 @@ StructuredFormatter.prototype = {
       output._message = logMessage.message;
     }
 
     return JSON.stringify(output);
   }
 };
 
 /**
+ * A formatter that does not prepend time/name/level information to messages,
+ * because those fields are logged separately when using the Android logger.
+ */
+function AndroidFormatter() {
+  BasicFormatter.call(this);
+}
+AndroidFormatter.prototype = Object.freeze({
+  __proto__: BasicFormatter.prototype,
+
+  format(message) {
+    return this.formatText(message);
+  },
+});
+
+/**
  * Test an object to see if it is a Mozilla JS Error.
  */
 function isError(aObj) {
   return (aObj && typeof(aObj) == "object" && "name" in aObj && "message" in aObj &&
           "fileName" in aObj && "lineNumber" in aObj && "stack" in aObj);
 }
 
 /*
@@ -1002,8 +1018,43 @@ BoundedFileAppender.prototype = {
 
     return fileClosePromise.then(_ => {
       this._size = 0;
       this._file = null;
       return OS.File.remove(this._path);
     });
   }
 };
+
+/*
+ * AndroidAppender
+ * Logs to Android logcat using AndroidLog.jsm
+ */
+function AndroidAppender(aFormatter) {
+  Appender.call(this, aFormatter || new AndroidFormatter());
+  this._name = "AndroidAppender";
+}
+AndroidAppender.prototype = {
+  __proto__: Appender.prototype,
+
+  // Map log level to AndroidLog.foo method.
+  _mapping: {
+    [Log.Level.Fatal]:  "e",
+    [Log.Level.Error]:  "e",
+    [Log.Level.Warn]:   "w",
+    [Log.Level.Info]:   "i",
+    [Log.Level.Config]: "d",
+    [Log.Level.Debug]:  "d",
+    [Log.Level.Trace]:  "v",
+  },
+
+  append(aMessage) {
+    if (!aMessage) {
+      return;
+    }
+
+    // AndroidLog.jsm always prepends "Gecko" to the tag, so we strip any
+    // leading "Gecko" here. Also strip dots to save space.
+    const tag = aMessage.loggerName.replace(/^Gecko|\./g, "");
+    const msg = this._formatter.format(aMessage);
+    AndroidLog[this._mapping[aMessage.level]](tag, msg);
+  },
+};