[android] Change the recommended log tag format to "cr_foo"

This is to ensure that log tags are not elided when retrieving
logcat output from crash reports. The log function has been
modified to make the transition easier and avoid specifying
the prefix everywhere.

This patch also moves some eliding testing from instrumentation
tests to junit tests, and adds a test to ensure that the new
tag format is not elided.

BUG=533072

Review URL: https://codereview.chromium.org/1354723004

Cr-Commit-Position: refs/heads/master@{#349733}
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index eb06a2a..c777fb8 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -1353,8 +1353,7 @@
 def _CheckAndroidCrLogUsage(input_api, output_api):
   """Checks that new logs using org.chromium.base.Log:
     - Are using 'TAG' as variable name for the tags (warn)
-    - Are using the suggested name format for the tags: "cr.<PackageTag>" (warn)
-    - Are using a tag that is shorter than 23 characters (error)
+    - Are using a tag that is shorter than 20 characters (error)
   """
   cr_log_import_pattern = input_api.re.compile(
       r'^import org\.chromium\.base\.Log;$', input_api.re.MULTILINE)
@@ -1365,9 +1364,8 @@
   # Extract the tag from lines like `Log.d(TAG, "*");` or `Log.d("TAG", "*");`
   log_call_pattern = input_api.re.compile(r'^\s*Log\.\w\((?P<tag>\"?\w+\"?)\,')
   log_decl_pattern = input_api.re.compile(
-      r'^\s*private static final String TAG = "(?P<name>(.*)")',
+      r'^\s*private static final String TAG = "(?P<name>(.*))";',
       input_api.re.MULTILINE)
-  log_name_pattern = input_api.re.compile(r'^cr[.\w]*')
 
   REF_MSG = ('See docs/android_logging.md '
             'or contact [email protected] for more info.')
@@ -1376,6 +1374,7 @@
   tag_decl_errors = []
   tag_length_errors = []
   tag_errors = []
+  tag_with_dot_errors = []
   util_log_errors = []
 
   for f in input_api.AffectedSourceFiles(sources):
@@ -1407,23 +1406,26 @@
     if has_modified_logs:
       # Make sure the tag is using the "cr" prefix and is not too long
       match = log_decl_pattern.search(file_content)
-      tag_name = match.group('name') if match else ''
-      if not log_name_pattern.search(tag_name ):
+      tag_name = match.group('name') if match else None
+      if not tag_name:
         tag_decl_errors.append(f.LocalPath())
-      if len(tag_name) > 23:
+      elif len(tag_name) > 20:
         tag_length_errors.append(f.LocalPath())
+      elif '.' in tag_name:
+        tag_with_dot_errors.append(f.LocalPath())
 
   results = []
   if tag_decl_errors:
     results.append(output_api.PresubmitPromptWarning(
         'Please define your tags using the suggested format: .\n'
-        '"private static final String TAG = "cr.<package tag>".\n' + REF_MSG,
+        '"private static final String TAG = "<package tag>".\n'
+        'They will be prepended with "cr_" automatically.\n' + REF_MSG,
         tag_decl_errors))
 
   if tag_length_errors:
     results.append(output_api.PresubmitError(
         'The tag length is restricted by the system to be at most '
-        '23 characters.\n' + REF_MSG,
+        '20 characters.\n' + REF_MSG,
         tag_length_errors))
 
   if tag_errors:
@@ -1436,6 +1438,11 @@
         'Please use org.chromium.base.Log for new logs.\n' + REF_MSG,
         util_log_errors))
 
+  if tag_with_dot_errors:
+    results.append(output_api.PresubmitPromptWarning(
+        'Dot in log tags cause them to be elided in crash reports.\n' + REF_MSG,
+        tag_with_dot_errors))
+
   return results
 
 
diff --git a/PRESUBMIT_test.py b/PRESUBMIT_test.py
index a864f1f..4d40d8f 100755
--- a/PRESUBMIT_test.py
+++ b/PRESUBMIT_test.py
@@ -463,7 +463,7 @@
       '"+chrome/plugin/chrome_content_plugin_client.h",',
       '"+chrome/utility/chrome_content_utility_client.h",',
       '"+chromeos/chromeos_paths.h",',
-      '"+components/crash",',
+      '"+components/crash/content",',
       '"+components/nacl/common",',
       '"+content/public/browser/render_process_host.h",',
       '"+jni/fooblat.h",',
@@ -840,32 +840,38 @@
       ]),
       MockAffectedFile('IsInBasePackage.java', [
         'package org.chromium.base;',
-        'private static final String TAG = "cr.Foo";',
+        'private static final String TAG = "cr_Foo";',
         'Log.d(TAG, "foo");',
       ]),
       MockAffectedFile('IsInBasePackageButImportsLog.java', [
         'package org.chromium.base;',
         'import android.util.Log;',
-        'private static final String TAG = "cr.Foo";',
+        'private static final String TAG = "cr_Foo";',
         'Log.d(TAG, "foo");',
       ]),
       MockAffectedFile('HasBothLog.java', [
         'import org.chromium.base.Log;',
         'some random stuff',
-        'private static final String TAG = "cr.Foo";',
+        'private static final String TAG = "cr_Foo";',
         'Log.d(TAG, "foo");',
         'android.util.Log.d("TAG", "foo");',
       ]),
       MockAffectedFile('HasCorrectTag.java', [
         'import org.chromium.base.Log;',
         'some random stuff',
+        'private static final String TAG = "cr_Foo";',
+        'Log.d(TAG, "foo");',
+      ]),
+      MockAffectedFile('HasOldTag.java', [
+        'import org.chromium.base.Log;',
+        'some random stuff',
         'private static final String TAG = "cr.Foo";',
         'Log.d(TAG, "foo");',
       ]),
-      MockAffectedFile('HasShortCorrectTag.java', [
+      MockAffectedFile('HasDottedTag.java', [
         'import org.chromium.base.Log;',
         'some random stuff',
-        'private static final String TAG = "cr";',
+        'private static final String TAG = "cr_foo.bar";',
         'Log.d(TAG, "foo");',
       ]),
       MockAffectedFile('HasNoTagDecl.java', [
@@ -875,17 +881,17 @@
       ]),
       MockAffectedFile('HasIncorrectTagDecl.java', [
         'import org.chromium.base.Log;',
-        'private static final String TAHG = "cr.Foo";',
+        'private static final String TAHG = "cr_Foo";',
         'some random stuff',
         'Log.d(TAG, "foo");',
       ]),
       MockAffectedFile('HasInlineTag.java', [
         'import org.chromium.base.Log;',
         'some random stuff',
-        'private static final String TAG = "cr.Foo";',
+        'private static final String TAG = "cr_Foo";',
         'Log.d("TAG", "foo");',
       ]),
-      MockAffectedFile('HasIncorrectTag.java', [
+      MockAffectedFile('HasUnprefixedTag.java', [
         'import org.chromium.base.Log;',
         'some random stuff',
         'private static final String TAG = "rubbish";',
@@ -894,7 +900,7 @@
       MockAffectedFile('HasTooLongTag.java', [
         'import org.chromium.base.Log;',
         'some random stuff',
-        'private static final String TAG = "cr.24_charachers_long___";',
+        'private static final String TAG = "21_charachers_long___";',
         'Log.d(TAG, "foo");',
       ]),
     ]
@@ -902,27 +908,42 @@
     msgs = PRESUBMIT._CheckAndroidCrLogUsage(
         mock_input_api, mock_output_api)
 
-    self.assertEqual(4, len(msgs))
+    self.assertEqual(5, len(msgs),
+                     'Expected %d items, found %d: %s' % (5, len(msgs), msgs))
 
     # Declaration format
-    self.assertEqual(3, len(msgs[0].items))
+    nb = len(msgs[0].items)
+    self.assertEqual(2, nb,
+                     'Expected %d items, found %d: %s' % (2, nb, msgs[0].items))
     self.assertTrue('HasNoTagDecl.java' in msgs[0].items)
     self.assertTrue('HasIncorrectTagDecl.java' in msgs[0].items)
-    self.assertTrue('HasIncorrectTag.java' in msgs[0].items)
 
     # Tag length
-    self.assertEqual(1, len(msgs[1].items))
+    nb = len(msgs[1].items)
+    self.assertEqual(1, nb,
+                     'Expected %d items, found %d: %s' % (1, nb, msgs[1].items))
     self.assertTrue('HasTooLongTag.java' in msgs[1].items)
 
     # Tag must be a variable named TAG
-    self.assertEqual(1, len(msgs[2].items))
+    nb = len(msgs[2].items)
+    self.assertEqual(1, nb,
+                     'Expected %d items, found %d: %s' % (1, nb, msgs[2].items))
     self.assertTrue('HasInlineTag.java:4' in msgs[2].items)
 
     # Util Log usage
-    self.assertEqual(2, len(msgs[3].items))
+    nb = len(msgs[3].items)
+    self.assertEqual(2, nb,
+                     'Expected %d items, found %d: %s' % (2, nb, msgs[3].items))
     self.assertTrue('HasAndroidLog.java:3' in msgs[3].items)
     self.assertTrue('IsInBasePackageButImportsLog.java:4' in msgs[3].items)
 
+    # Tag must not contain
+    nb = len(msgs[4].items)
+    self.assertEqual(2, nb,
+                     'Expected %d items, found %d: %s' % (2, nb, msgs[4].items))
+    self.assertTrue('HasDottedTag.java' in msgs[4].items)
+    self.assertTrue('HasOldTag.java' in msgs[4].items)
+
 
 if __name__ == '__main__':
   unittest.main()
diff --git a/base/android/java/src/org/chromium/base/Log.java b/base/android/java/src/org/chromium/base/Log.java
index 2a77211..7160f0e4 100644
--- a/base/android/java/src/org/chromium/base/Log.java
+++ b/base/android/java/src/org/chromium/base/Log.java
@@ -39,6 +39,9 @@
     /** Convenience property, same as {@link android.util.Log#WARN}. */
     public static final int WARN = android.util.Log.WARN;
 
+    private static final String sTagPrefix = "cr_";
+    private static final String sDeprecatedTagPrefix = "cr.";
+
     private Log() {
         // Static only access
     }
@@ -53,6 +56,24 @@
     }
 
     /**
+     * Returns a normalized tag that will be in the form: "cr_foo". This function is called by the
+     * various Log overrides. If using {@link #isLoggable(String, int)}, you might want to call it
+     * to get the tag that will actually be used.
+     * @see #sTagPrefix
+     */
+    public static String normalizeTag(String tag) {
+        if (tag.startsWith(sTagPrefix)) return tag;
+
+        // TODO(dgn) simplify this once 'cr.' is out of the repo (http://crbug.com/533072)
+        int unprefixedTagStart = 0;
+        if (tag.startsWith(sDeprecatedTagPrefix)) {
+            unprefixedTagStart = sDeprecatedTagPrefix.length();
+        }
+
+        return sTagPrefix + tag.substring(unprefixedTagStart, tag.length());
+    }
+
+    /**
      * Returns a formatted log message, using the supplied format and arguments.
      * The message will be prepended with the filename and line number of the call.
      */
@@ -77,7 +98,8 @@
      * than 7 parameters, consider building your log message using a function annotated with
      * {@link RemovableInRelease}.
      *
-     * @param tag Used to identify the source of a log message.
+     * @param tag Used to identify the source of a log message. Might be modified in the output
+     *            (see {@link #normalizeTag(String)})
      * @param messageTemplate The message you would like logged. It is to be specified as a format
      *                        string.
      * @param args Arguments referenced by the format specifiers in the format string. If the last
@@ -87,9 +109,9 @@
         String message = formatLogWithStack(messageTemplate, args);
         Throwable tr = getThrowableToLog(args);
         if (tr != null) {
-            android.util.Log.v(tag, message, tr);
+            android.util.Log.v(normalizeTag(tag), message, tr);
         } else {
-            android.util.Log.v(tag, message);
+            android.util.Log.v(normalizeTag(tag), message);
         }
     }
 
@@ -161,7 +183,8 @@
      * than 7 parameters, consider building your log message using a function annotated with
      * {@link RemovableInRelease}.
      *
-     * @param tag Used to identify the source of a log message.
+     * @param tag Used to identify the source of a log message. Might be modified in the output
+     *            (see {@link #normalizeTag(String)})
      * @param messageTemplate The message you would like logged. It is to be specified as a format
      *                        string.
      * @param args Arguments referenced by the format specifiers in the format string. If the last
@@ -171,9 +194,9 @@
         String message = formatLogWithStack(messageTemplate, args);
         Throwable tr = getThrowableToLog(args);
         if (tr != null) {
-            android.util.Log.d(tag, message, tr);
+            android.util.Log.d(normalizeTag(tag), message, tr);
         } else {
-            android.util.Log.d(tag, message);
+            android.util.Log.d(normalizeTag(tag), message);
         }
     }
 
@@ -239,7 +262,8 @@
     /**
      * Sends an {@link android.util.Log#INFO} log message.
      *
-     * @param tag Used to identify the source of a log message.
+     * @param tag Used to identify the source of a log message. Might be modified in the output
+     *            (see {@link #normalizeTag(String)})
      * @param messageTemplate The message you would like logged. It is to be specified as a format
      *                        string.
      * @param args Arguments referenced by the format specifiers in the format string. If the last
@@ -250,16 +274,17 @@
         String message = formatLog(messageTemplate, args);
         Throwable tr = getThrowableToLog(args);
         if (tr != null) {
-            android.util.Log.i(tag, message, tr);
+            android.util.Log.i(normalizeTag(tag), message, tr);
         } else {
-            android.util.Log.i(tag, message);
+            android.util.Log.i(normalizeTag(tag), message);
         }
     }
 
     /**
      * Sends a {@link android.util.Log#WARN} log message.
      *
-     * @param tag Used to identify the source of a log message.
+     * @param tag Used to identify the source of a log message. Might be modified in the output
+     *            (see {@link #normalizeTag(String)})
      * @param messageTemplate The message you would like logged. It is to be specified as a format
      *                        string.
      * @param args Arguments referenced by the format specifiers in the format string. If the last
@@ -270,16 +295,17 @@
         String message = formatLog(messageTemplate, args);
         Throwable tr = getThrowableToLog(args);
         if (tr != null) {
-            android.util.Log.w(tag, message, tr);
+            android.util.Log.w(normalizeTag(tag), message, tr);
         } else {
-            android.util.Log.w(tag, message);
+            android.util.Log.w(normalizeTag(tag), message);
         }
     }
 
     /**
      * Sends an {@link android.util.Log#ERROR} log message.
      *
-     * @param tag Used to identify the source of a log message.
+     * @param tag Used to identify the source of a log message. Might be modified in the output
+     *            (see {@link #normalizeTag(String)})
      * @param messageTemplate The message you would like logged. It is to be specified as a format
      *                        string.
      * @param args Arguments referenced by the format specifiers in the format string. If the last
@@ -290,9 +316,9 @@
         String message = formatLog(messageTemplate, args);
         Throwable tr = getThrowableToLog(args);
         if (tr != null) {
-            android.util.Log.e(tag, message, tr);
+            android.util.Log.e(normalizeTag(tag), message, tr);
         } else {
-            android.util.Log.e(tag, message);
+            android.util.Log.e(normalizeTag(tag), message);
         }
     }
 
@@ -303,7 +329,8 @@
      *
      * @see android.util.Log#wtf(String, String, Throwable)
      *
-     * @param tag Used to identify the source of a log message.
+     * @param tag Used to identify the source of a log message. Might be modified in the output
+     *            (see {@link #normalizeTag(String)})
      * @param messageTemplate The message you would like logged. It is to be specified as a format
      *                        string.
      * @param args Arguments referenced by the format specifiers in the format string. If the last
@@ -314,9 +341,9 @@
         String message = formatLog(messageTemplate, args);
         Throwable tr = getThrowableToLog(args);
         if (tr != null) {
-            android.util.Log.wtf(tag, message, tr);
+            android.util.Log.wtf(normalizeTag(tag), message, tr);
         } else {
-            android.util.Log.wtf(tag, message);
+            android.util.Log.wtf(normalizeTag(tag), message);
         }
     }
 
diff --git a/base/android/junit/src/org/chromium/base/LogTest.java b/base/android/junit/src/org/chromium/base/LogTest.java
index 6f4e4063..745ac8c 100644
--- a/base/android/junit/src/org/chromium/base/LogTest.java
+++ b/base/android/junit/src/org/chromium/base/LogTest.java
@@ -32,6 +32,14 @@
                 logs.get(0).msg.matches("\\[LogTest.java:\\d+\\].*"));
     }
 
+    @Test
+    public void normalizeTagTest() {
+        assertEquals("cr_foo", Log.normalizeTag("cr.foo"));
+        assertEquals("cr_foo", Log.normalizeTag("cr_foo"));
+        assertEquals("cr_foo", Log.normalizeTag("foo"));
+        assertEquals("cr_ab_foo", Log.normalizeTag("ab_foo"));
+    }
+
     /** Tests that exceptions provided to the log functions are properly recognized and printed. */
     @Test
     public void exceptionLoggingTest() {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/crash/LogcatExtractionCallableTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/crash/LogcatExtractionCallableTest.java
index 9cf435be..861235f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/crash/LogcatExtractionCallableTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/crash/LogcatExtractionCallableTest.java
@@ -4,17 +4,10 @@
 
 package org.chromium.chrome.browser.crash;
 
-import static org.chromium.chrome.browser.crash.LogcatExtractionCallable.BEGIN_MICRODUMP;
-import static org.chromium.chrome.browser.crash.LogcatExtractionCallable.END_MICRODUMP;
-
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.test.MoreAsserts;
 import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import android.text.TextUtils;
 
 import org.chromium.base.test.util.AdvancedMockContext;
 
@@ -22,11 +15,7 @@
 import java.io.File;
 import java.io.FileReader;
 import java.io.IOException;
-import java.io.StringReader;
-
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -36,192 +25,11 @@
 public class LogcatExtractionCallableTest extends CrashTestCase {
     private File mCrashDir;
 
-    private static final int MAX_LINES = 5;
-
     protected void setUp() throws Exception {
         super.setUp();
         mCrashDir = new CrashFileManager(mCacheDir).getCrashDirectory();
     }
 
-    @SmallTest
-    public void testElideEmail() {
-        String original = "email me at [email protected]";
-        String expected = "email me at [email protected]";
-        assertEquals(expected, LogcatExtractionCallable.elideEmail(original));
-    }
-
-    @SmallTest
-    public void testElideUrl() {
-        String original = "file bugs at crbug.com";
-        String expected = "file bugs at HTTP://WEBADDRESS.ELIDED";
-        assertEquals(expected, LogcatExtractionCallable.elideUrl(original));
-    }
-
-    @SmallTest
-    public void testElideUrl2() {
-        String original =
-                "exception at org.chromium.chrome.browser.crash.LogcatExtractionCallableTest";
-        assertEquals(original, LogcatExtractionCallable.elideUrl(original));
-    }
-
-    @SmallTest
-    public void testElideUrl3() {
-        String original = "file bugs at crbug.com or code.google.com";
-        String expected = "file bugs at HTTP://WEBADDRESS.ELIDED or HTTP://WEBADDRESS.ELIDED";
-        assertEquals(expected, LogcatExtractionCallable.elideUrl(original));
-    }
-
-    @SmallTest
-    public void testElideUrl4() {
-        String original = "test shorturl.com !!!";
-        String expected = "test HTTP://WEBADDRESS.ELIDED !!!";
-        assertEquals(expected, LogcatExtractionCallable.elideUrl(original));
-    }
-
-    @SmallTest
-    public void testElideUrl5() {
-        String original = "test just.the.perfect.len.url !!!";
-        String expected = "test HTTP://WEBADDRESS.ELIDED !!!";
-        assertEquals(expected, LogcatExtractionCallable.elideUrl(original));
-    }
-
-    @SmallTest
-    public void testElideUrl6() {
-        String original = "test a.very.very.very.very.very.long.url !!!";
-        String expected = "test HTTP://WEBADDRESS.ELIDED !!!";
-        assertEquals(expected, LogcatExtractionCallable.elideUrl(original));
-    }
-
-    @SmallTest
-    public void testElideUrl7() {
-        String original = " at android.content.Intent \n at java.util.ArrayList";
-        assertEquals(original, LogcatExtractionCallable.elideUrl(original));
-    }
-
-    @SmallTest
-    public void testElideIp() {
-        String original = "traceroute 127.0.0.1";
-        String expected = "traceroute 1.2.3.4";
-        assertEquals(expected, LogcatExtractionCallable.elideIp(original));
-    }
-
-    @SmallTest
-    public void testElideMac1() {
-        String original = "MAC: AB-AB-AB-AB-AB-AB";
-        String expected = "MAC: 01:23:45:67:89:AB";
-        assertEquals(expected, LogcatExtractionCallable.elideMac(original));
-    }
-
-    @SmallTest
-    public void testElideMac2() {
-        String original = "MAC: AB:AB:AB:AB:AB:AB";
-        String expected = "MAC: 01:23:45:67:89:AB";
-        assertEquals(expected, LogcatExtractionCallable.elideMac(original));
-    }
-
-    @SmallTest
-    public void testElideConsole() {
-        String original = "I/chromium(123): [INFO:CONSOLE(2)] hello!";
-        String expected = "I/chromium(123): [ELIDED:CONSOLE(0)] ELIDED CONSOLE MESSAGE";
-        assertEquals(expected, LogcatExtractionCallable.elideConsole(original));
-    }
-
-    @SmallTest
-    public void testLogcatEmpty() {
-        final String original = "";
-        List<String> expected = new LinkedList<>();
-        List<String> logcat = null;
-        try {
-            logcat = LogcatExtractionCallable.extractLogcatFromReader(
-                    new BufferedReader(new StringReader(original)), MAX_LINES);
-        } catch (Exception e) {
-            fail(e.toString());
-        }
-        MoreAsserts.assertEquals(expected.toArray(), logcat.toArray());
-    }
-
-    @SmallTest
-    public void testLogcatWithoutBeginOrEnd_smallLogcat() {
-        final List<String> original = Arrays.asList("Line 1", "Line 2", "Line 3", "Line 4",
-                "Line 5");
-        assertLogcatLists(original, original);
-    }
-
-    @SmallTest
-    public void testLogcatWithoutBeginOrEnd_largeLogcat() {
-        final List<String> original = Arrays.asList("Line 1", "Line 2", "Line 3", "Line 4",
-                "Line 5", "Redundant Line 1", "Redundant Line 2");
-        final List<String> expected = Arrays.asList("Line 1", "Line 2", "Line 3", "Line 4",
-                "Line 5");
-        assertLogcatLists(expected, original);
-    }
-
-    @SmallTest
-    public void testLogcatBeginsWithBegin() {
-        final List<String> original = Arrays.asList(BEGIN_MICRODUMP, "a", "b", "c", "d", "e");
-        assertLogcatLists(new LinkedList<String>(), original);
-    }
-
-    @SmallTest
-    public void testLogcatWithBegin() {
-        final List<String> original = Arrays.asList("Line 1", "Line 2", BEGIN_MICRODUMP, "a",
-                "b", "c", "d", "e");
-        final List<String> expected = Arrays.asList("Line 1", "Line 2");
-        assertLogcatLists(expected, original);
-    }
-
-    @SmallTest
-    public void testLogcatWithEnd() {
-        final List<String> original = Arrays.asList("Line 1", "Line 2", END_MICRODUMP);
-        assertLogcatLists(new LinkedList<String>(), original);
-    }
-
-    @SmallTest
-    public void testLogcatWithBeginAndEnd_smallLogcat() {
-        final List<String> original = Arrays.asList("Line 1", "Line 2", BEGIN_MICRODUMP, "a", "b",
-                "c", "d", "e", END_MICRODUMP);
-        final List<String> expected = Arrays.asList("Line 1", "Line 2");
-        assertLogcatLists(expected, original);
-    }
-
-    @SmallTest
-    public void testLogcatWithBeginAndEnd_largeLogcat() {
-        final List<String> original = Arrays.asList("Line 1", "Line 2", BEGIN_MICRODUMP, "a", "b",
-                "c", "d", "e", END_MICRODUMP, "Line 3", "Line 4");
-        final List<String> expected = Arrays.asList("Line 1", "Line 2", "Line 3", "Line 4");
-        assertLogcatLists(expected, original);
-    }
-
-    @SmallTest
-    public void testLogcatWithEndAndBegin_smallLogcat() {
-        final List<String> original = Arrays.asList(END_MICRODUMP, "Line 1", "Line 2",
-                BEGIN_MICRODUMP, "a", "b", "c", "d", "e");
-        final List<String> expected = Arrays.asList("Line 1", "Line 2");
-        assertLogcatLists(expected, original);
-    }
-
-    @SmallTest
-    public void testLogcatWithEndAndBegin_largeLogcat() {
-        final List<String> original = Arrays.asList(END_MICRODUMP, "Line 1", "Line 2",
-                BEGIN_MICRODUMP, "a", "b", "c", "d", "e", END_MICRODUMP, "Line 3", "Line 4");
-        final List<String> expected = Arrays.asList("Line 1", "Line 2", "Line 3", "Line 4");
-        assertLogcatLists(expected, original);
-    }
-
-    private void assertLogcatLists(List<String> expected, List<String> original) {
-        List<String> actualLogcat = null;
-        String combinedLogcat = TextUtils.join("\n", original);
-        try {
-            //simulate a file reader to test whether the extraction process
-            //successfully strips microdump from logcat
-            actualLogcat = LogcatExtractionCallable.extractLogcatFromReader(
-                    new BufferedReader(new StringReader(combinedLogcat)), MAX_LINES);
-        } catch (Exception e) {
-            fail(e.toString());
-        }
-        MoreAsserts.assertEquals(expected.toArray(), actualLogcat.toArray());
-    }
-
     @MediumTest
     public void testExtractToFile() {
         final AtomicInteger numServiceStarts = new AtomicInteger(0);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/crash/LogcatExtractionCallableTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/crash/LogcatExtractionCallableTest.java
new file mode 100644
index 0000000..9c74e30
--- /dev/null
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/crash/LogcatExtractionCallableTest.java
@@ -0,0 +1,218 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.crash;
+
+import static org.chromium.chrome.browser.crash.LogcatExtractionCallable.BEGIN_MICRODUMP;
+import static org.chromium.chrome.browser.crash.LogcatExtractionCallable.END_MICRODUMP;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.text.TextUtils;
+
+import org.chromium.testing.local.LocalRobolectricTestRunner;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+import java.io.BufferedReader;
+import java.io.StringReader;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * junit tests for {@link LogcatExtractionCallable}.
+ */
+@RunWith(LocalRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class LogcatExtractionCallableTest {
+    private static final int MAX_LINES = 5;
+
+    @Test
+    public void testElideEmail() {
+        String original = "email me at [email protected]";
+        String expected = "email me at [email protected]";
+        assertEquals(expected, LogcatExtractionCallable.elideEmail(original));
+    }
+
+    @Test
+    public void testElideUrl() {
+        String original = "file bugs at crbug.com";
+        String expected = "file bugs at HTTP://WEBADDRESS.ELIDED";
+        assertEquals(expected, LogcatExtractionCallable.elideUrl(original));
+    }
+
+    @Test
+    public void testElideUrl2() {
+        String original =
+                "exception at org.chromium.chrome.browser.crash.LogcatExtractionCallableTest";
+        assertEquals(original, LogcatExtractionCallable.elideUrl(original));
+    }
+
+    @Test
+    public void testElideUrl3() {
+        String original = "file bugs at crbug.com or code.google.com";
+        String expected = "file bugs at HTTP://WEBADDRESS.ELIDED or HTTP://WEBADDRESS.ELIDED";
+        assertEquals(expected, LogcatExtractionCallable.elideUrl(original));
+    }
+
+    @Test
+    public void testElideUrl4() {
+        String original = "test shorturl.com !!!";
+        String expected = "test HTTP://WEBADDRESS.ELIDED !!!";
+        assertEquals(expected, LogcatExtractionCallable.elideUrl(original));
+    }
+
+    @Test
+    public void testElideUrl5() {
+        String original = "test just.the.perfect.len.url !!!";
+        String expected = "test HTTP://WEBADDRESS.ELIDED !!!";
+        assertEquals(expected, LogcatExtractionCallable.elideUrl(original));
+    }
+
+    @Test
+    public void testElideUrl6() {
+        String original = "test a.very.very.very.very.very.long.url !!!";
+        String expected = "test HTTP://WEBADDRESS.ELIDED !!!";
+        assertEquals(expected, LogcatExtractionCallable.elideUrl(original));
+    }
+
+    @Test
+    public void testElideUrl7() {
+        String original = " at android.content.Intent \n at java.util.ArrayList";
+        assertEquals(original, LogcatExtractionCallable.elideUrl(original));
+    }
+
+    @Test
+    public void testElideIp() {
+        String original = "traceroute 127.0.0.1";
+        String expected = "traceroute 1.2.3.4";
+        assertEquals(expected, LogcatExtractionCallable.elideIp(original));
+    }
+
+    @Test
+    public void testElideMac1() {
+        String original = "MAC: AB-AB-AB-AB-AB-AB";
+        String expected = "MAC: 01:23:45:67:89:AB";
+        assertEquals(expected, LogcatExtractionCallable.elideMac(original));
+    }
+
+    @Test
+    public void testElideMac2() {
+        String original = "MAC: AB:AB:AB:AB:AB:AB";
+        String expected = "MAC: 01:23:45:67:89:AB";
+        assertEquals(expected, LogcatExtractionCallable.elideMac(original));
+    }
+
+    @Test
+    public void testElideConsole() {
+        String original = "I/chromium(123): [INFO:CONSOLE(2)] hello!";
+        String expected = "I/chromium(123): [ELIDED:CONSOLE(0)] ELIDED CONSOLE MESSAGE";
+        assertEquals(expected, LogcatExtractionCallable.elideConsole(original));
+    }
+
+    @Test
+    public void testLogTagNotElided() {
+        List<String> original = Arrays.asList(new String[] {"I/cr_FooBar(123): Some message"});
+        assertEquals(original, LogcatExtractionCallable.processLogcat(original));
+    }
+
+    @Test
+    public void testLogcatEmpty() {
+        final String original = "";
+        List<String> expected = new LinkedList<>();
+        List<String> logcat = null;
+        try {
+            logcat = LogcatExtractionCallable.extractLogcatFromReader(
+                    new BufferedReader(new StringReader(original)), MAX_LINES);
+        } catch (Exception e) {
+            fail(e.toString());
+        }
+        assertArrayEquals(expected.toArray(), logcat.toArray());
+    }
+
+    @Test
+    public void testLogcatWithoutBeginOrEnd_smallLogcat() {
+        final List<String> original = Arrays.asList("Line 1", "Line 2", "Line 3", "Line 4",
+                "Line 5");
+        assertLogcatLists(original, original);
+    }
+
+    @Test
+    public void testLogcatWithoutBeginOrEnd_largeLogcat() {
+        final List<String> original = Arrays.asList("Line 1", "Line 2", "Line 3", "Line 4",
+                "Line 5", "Redundant Line 1", "Redundant Line 2");
+        final List<String> expected = Arrays.asList("Line 1", "Line 2", "Line 3", "Line 4",
+                "Line 5");
+        assertLogcatLists(expected, original);
+    }
+
+    @Test
+    public void testLogcatBeginsWithBegin() {
+        final List<String> original = Arrays.asList(BEGIN_MICRODUMP, "a", "b", "c", "d", "e");
+        assertLogcatLists(new LinkedList<String>(), original);
+    }
+
+    @Test
+    public void testLogcatWithBegin() {
+        final List<String> original = Arrays.asList("Line 1", "Line 2", BEGIN_MICRODUMP, "a",
+                "b", "c", "d", "e");
+        final List<String> expected = Arrays.asList("Line 1", "Line 2");
+        assertLogcatLists(expected, original);
+    }
+
+    @Test
+    public void testLogcatWithEnd() {
+        final List<String> original = Arrays.asList("Line 1", "Line 2", END_MICRODUMP);
+        assertLogcatLists(new LinkedList<String>(), original);
+    }
+
+    @Test
+    public void testLogcatWithBeginAndEnd_smallLogcat() {
+        final List<String> original = Arrays.asList("Line 1", "Line 2", BEGIN_MICRODUMP, "a", "b",
+                "c", "d", "e", END_MICRODUMP);
+        final List<String> expected = Arrays.asList("Line 1", "Line 2");
+        assertLogcatLists(expected, original);
+    }
+
+    @Test
+    public void testLogcatWithBeginAndEnd_largeLogcat() {
+        final List<String> original = Arrays.asList("Line 1", "Line 2", BEGIN_MICRODUMP, "a", "b",
+                "c", "d", "e", END_MICRODUMP, "Line 3", "Line 4");
+        final List<String> expected = Arrays.asList("Line 1", "Line 2", "Line 3", "Line 4");
+        assertLogcatLists(expected, original);
+    }
+
+    @Test
+    public void testLogcatWithEndAndBegin_smallLogcat() {
+        final List<String> original = Arrays.asList(END_MICRODUMP, "Line 1", "Line 2",
+                BEGIN_MICRODUMP, "a", "b", "c", "d", "e");
+        final List<String> expected = Arrays.asList("Line 1", "Line 2");
+        assertLogcatLists(expected, original);
+    }
+
+    @Test
+    public void testLogcatWithEndAndBegin_largeLogcat() {
+        final List<String> original = Arrays.asList(END_MICRODUMP, "Line 1", "Line 2",
+                BEGIN_MICRODUMP, "a", "b", "c", "d", "e", END_MICRODUMP, "Line 3", "Line 4");
+        final List<String> expected = Arrays.asList("Line 1", "Line 2", "Line 3", "Line 4");
+        assertLogcatLists(expected, original);
+    }
+
+    private void assertLogcatLists(List<String> expected, List<String> original) {
+        List<String> actualLogcat = null;
+        String combinedLogcat = TextUtils.join("\n", original);
+        try {
+            //simulate a file reader to test whether the extraction process
+            //successfully strips microdump from logcat
+            actualLogcat = LogcatExtractionCallable.extractLogcatFromReader(
+                    new BufferedReader(new StringReader(combinedLogcat)), MAX_LINES);
+        } catch (Exception e) {
+            fail(e.toString());
+        }
+        assertArrayEquals(expected.toArray(), actualLogcat.toArray());
+    }
+}
diff --git a/docs/android_logging.md b/docs/android_logging.md
index 3068dc5..d7d248f 100644
--- a/docs/android_logging.md
+++ b/docs/android_logging.md
@@ -15,7 +15,7 @@
 Usage:
 
 ```java
-private static final String TAG = "cr.YourModuleTag";
+private static final String TAG = "YourModuleTag";
 ...
 Log.i(TAG, "Logged INFO message.");
 Log.d(TAG, "Some DEBUG info: %s", data);
@@ -24,12 +24,13 @@
 Output:
 
 ```
-I/cr.YourModuleTag: ( 999): Logged INFO message
-D/cr.YourModuleTag: ( 999): [MyClass.java:42] Some DEBUG info: data.toString
+I/cr_YourModuleTag: ( 999): Logged INFO message
+D/cr_YourModuleTag: ( 999): [MyClass.java:42] Some DEBUG info: data.toString
 ```
 
 Here, **TAG** will be a feature or package name, "MediaRemote" or "NFC" for
-example. In most cases, the class name is not needed.
+example. In most cases, the class name is not needed. It will be prepended by
+the "cr_" prefix to make obvious which logs are coming from Chrome.
 
 ### Verbose and Debug logs have special handling ###
 
@@ -50,10 +51,10 @@
 ```
 
 ```
-I/cr.YourModuleTag: ( 999): An error happened: This is the exception's message
-I/cr.YourModuleTag: ( 999): java.lang.Exception: This is the exception's message
-I/cr.YourModuleTag: ( 999):     at foo.bar.MyClass.test(MyClass.java:42)
-I/cr.YourModuleTag: ( 999):     ...
+I/cr_YourModuleTag: ( 999): An error happened: This is the exception's message
+I/cr_YourModuleTag: ( 999): java.lang.Exception: This is the exception's message
+I/cr_YourModuleTag: ( 999):     at foo.bar.MyClass.test(MyClass.java:42)
+I/cr_YourModuleTag: ( 999):     ...
 ```
 
 Having the exception as last parameter doesn't prevent it from being used for
@@ -187,16 +188,16 @@
 
 ```shell
 adb logcat [TAG_EXPR:LEVEL]...
-adb logcat cr.YourModuleTag:D *:S
+adb logcat cr_YourModuleTag:D *:S
 ```
 
 This shows only logs having a level higher or equal to DEBUG for
-`cr.YourModuleTag`, and SILENT (nothing is logged at this level or higher, so it
+`cr_YourModuleTag`, and SILENT (nothing is logged at this level or higher, so it
 silences the tags) for everything else. You can persist a filter by setting an
 environment variable:
 
 ```shell
-export ANDROID_LOG_TAGS="cr.YourModuleTag:D *:S"
+export ANDROID_LOG_TAGS="cr_YourModuleTag:D *:S"
 ```
 
 For more, see the [related page on developer.android.com]