Bug 1452200 - 1a. Add AndroidAppender for Log.jsm; r?markh draft
authorJim Chen <nchen@mozilla.com>
Sun, 15 Apr 2018 14:53:28 -0400
changeset 782396 86c45f30ee0fc1495a7af15f1081a1b2c7960f4b
parent 782271 008f977fc4ac4fc09b98580ff4355b03c14f1e19
child 782397 cbdbdc5f5607401fe402f7e51103130050a8b5e7
push id106522
push userbmo:nchen@mozilla.com
push dateSun, 15 Apr 2018 18:53:59 +0000
reviewersmarkh
bugs1452200
milestone61.0a1
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);
+  },
+};