Use a Logger instead of Log messages.

- Let the user specify if they want verbose logging.
- Set default logging to not-verbose.

Note that there is NO proguarding away of these logs;
we're leaving it up to the user to do this since we
don't want unexpected results if they explicitly allow
more verbose logs.

Change-Id: I0ea53542fae4f98e115a6ff352928d87a137ca3a
Fixes: 110104711
Test: Ran tests, looked at integration app logcat
diff --git a/work/integration-tests/testapp/src/main/java/androidx/work/integration/testapp/sherlockholmes/AnalyzeSherlockHolmesActivity.java b/work/integration-tests/testapp/src/main/java/androidx/work/integration/testapp/sherlockholmes/AnalyzeSherlockHolmesActivity.java
index 4e3d01e..1b2087f 100644
--- a/work/integration-tests/testapp/src/main/java/androidx/work/integration/testapp/sherlockholmes/AnalyzeSherlockHolmesActivity.java
+++ b/work/integration-tests/testapp/src/main/java/androidx/work/integration/testapp/sherlockholmes/AnalyzeSherlockHolmesActivity.java
@@ -71,6 +71,10 @@
     private void enqueueWork() {
         WorkManager workManager = WorkManager.getInstance();
 
+        // Cancelling all work just to make it easier to track what is happening here and make it
+        // more insulated.
+        workManager.cancelAllWork();
+
         OneTimeWorkRequest textReducingWork =
                 new OneTimeWorkRequest.Builder(TextReducingWorker.class)
                     .setInputMerger(ArrayCreatingInputMerger.class)
diff --git a/work/integration-tests/testapp/src/main/java/androidx/work/integration/testapp/sherlockholmes/TextMappingWorker.java b/work/integration-tests/testapp/src/main/java/androidx/work/integration/testapp/sherlockholmes/TextMappingWorker.java
index 2fca443..bc1b85e 100644
--- a/work/integration-tests/testapp/src/main/java/androidx/work/integration/testapp/sherlockholmes/TextMappingWorker.java
+++ b/work/integration-tests/testapp/src/main/java/androidx/work/integration/testapp/sherlockholmes/TextMappingWorker.java
@@ -18,7 +18,6 @@
 import android.content.Context;
 import android.content.res.AssetManager;
 import android.support.annotation.NonNull;
-import android.util.Log;
 
 import androidx.work.Data;
 import androidx.work.OneTimeWorkRequest;
@@ -122,7 +121,6 @@
 
         setOutputData(new Data.Builder().putString(INPUT_FILE, outputFileName).build());
 
-        Log.d("Map", "Mapping finished for " + inputFileName);
         return Result.SUCCESS;
     }
 }
diff --git a/work/integration-tests/testapp/src/main/java/androidx/work/integration/testapp/sherlockholmes/TextReducingWorker.java b/work/integration-tests/testapp/src/main/java/androidx/work/integration/testapp/sherlockholmes/TextReducingWorker.java
index 3187a46..5d5a45c 100644
--- a/work/integration-tests/testapp/src/main/java/androidx/work/integration/testapp/sherlockholmes/TextReducingWorker.java
+++ b/work/integration-tests/testapp/src/main/java/androidx/work/integration/testapp/sherlockholmes/TextReducingWorker.java
@@ -16,7 +16,6 @@
 package androidx.work.integration.testapp.sherlockholmes;
 
 import android.support.annotation.NonNull;
-import android.util.Log;
 
 import androidx.work.Data;
 import androidx.work.Worker;
@@ -101,7 +100,6 @@
             db.endTransaction();
         }
 
-        Log.d("Reduce", "Reduction finished");
         return Result.SUCCESS;
     }
 }
diff --git a/work/workmanager-firebase/src/main/java/androidx/work/impl/background/firebase/FirebaseDelayedJobAlarmReceiver.java b/work/workmanager-firebase/src/main/java/androidx/work/impl/background/firebase/FirebaseDelayedJobAlarmReceiver.java
index 5e549d2..f73d3d6 100644
--- a/work/workmanager-firebase/src/main/java/androidx/work/impl/background/firebase/FirebaseDelayedJobAlarmReceiver.java
+++ b/work/workmanager-firebase/src/main/java/androidx/work/impl/background/firebase/FirebaseDelayedJobAlarmReceiver.java
@@ -19,9 +19,9 @@
 import android.content.Context;
 import android.content.Intent;
 import android.support.annotation.RestrictTo;
-import android.util.Log;
 
 import androidx.work.Configuration;
+import androidx.work.Logger;
 import androidx.work.impl.Schedulers;
 import androidx.work.impl.WorkDatabase;
 import androidx.work.impl.WorkManagerImpl;
@@ -52,7 +52,7 @@
                 if (workSpec != null) {
                     Schedulers.schedule(configuration, database, workManagerImpl.getSchedulers());
                 } else {
-                    Log.e(TAG, "WorkSpec not found! Cannot schedule!");
+                    Logger.error(TAG, "WorkSpec not found! Cannot schedule!");
                 }
                 pendingResult.finish();
             }
diff --git a/work/workmanager-firebase/src/main/java/androidx/work/impl/background/firebase/FirebaseJobConverter.java b/work/workmanager-firebase/src/main/java/androidx/work/impl/background/firebase/FirebaseJobConverter.java
index 46b8362..23c7850 100644
--- a/work/workmanager-firebase/src/main/java/androidx/work/impl/background/firebase/FirebaseJobConverter.java
+++ b/work/workmanager-firebase/src/main/java/androidx/work/impl/background/firebase/FirebaseJobConverter.java
@@ -18,11 +18,11 @@
 
 import android.os.Build;
 import android.support.annotation.RequiresApi;
-import android.util.Log;
 
 import androidx.work.BackoffPolicy;
 import androidx.work.Constraints;
 import androidx.work.ContentUriTriggers;
+import androidx.work.Logger;
 import androidx.work.WorkRequest;
 import androidx.work.impl.model.WorkSpec;
 
@@ -131,13 +131,13 @@
         }
 
         if (constraints.requiresBatteryNotLow()) {
-            Log.w(TAG,
+            Logger.warning(TAG,
                     "Battery Not Low is not a supported constraint "
                             + "with FirebaseJobDispatcher");
         }
 
         if (constraints.requiresStorageNotLow()) {
-            Log.w(TAG, "Storage Not Low is not a supported constraint "
+            Logger.warning(TAG, "Storage Not Low is not a supported constraint "
                     + "with FirebaseJobDispatcher");
         }
 
@@ -158,14 +158,14 @@
             }
 
             case NOT_ROAMING: {
-                Log.w(TAG, "Not Roaming Network is not a supported constraint with "
+                Logger.warning(TAG, "Not Roaming Network is not a supported constraint with "
                         + "FirebaseJobDispatcher. Falling back to Any Network constraint.");
                 mConstraints.add(Constraint.ON_ANY_NETWORK);
                 break;
             }
 
             case METERED: {
-                Log.w(TAG, "Metered Network is not a supported constraint with "
+                Logger.warning(TAG, "Metered Network is not a supported constraint with "
                         + "FirebaseJobDispatcher. Falling back to Any Network constraint.");
                 mConstraints.add(Constraint.ON_ANY_NETWORK);
                 break;
diff --git a/work/workmanager-firebase/src/main/java/androidx/work/impl/background/firebase/FirebaseJobScheduler.java b/work/workmanager-firebase/src/main/java/androidx/work/impl/background/firebase/FirebaseJobScheduler.java
index d9cefb0..88a1504 100644
--- a/work/workmanager-firebase/src/main/java/androidx/work/impl/background/firebase/FirebaseJobScheduler.java
+++ b/work/workmanager-firebase/src/main/java/androidx/work/impl/background/firebase/FirebaseJobScheduler.java
@@ -23,8 +23,8 @@
 import android.os.Build;
 import android.support.annotation.NonNull;
 import android.support.annotation.RestrictTo;
-import android.util.Log;
 
+import androidx.work.Logger;
 import androidx.work.impl.Scheduler;
 import androidx.work.impl.model.WorkSpec;
 import androidx.work.impl.utils.IdGenerator;
@@ -81,10 +81,10 @@
 
     void scheduleNow(WorkSpec workSpec) {
         Job job = mJobConverter.convert(workSpec);
-        Log.d(TAG, String.format("Scheduling work now, ID: %s", workSpec.id));
+        Logger.debug(TAG, String.format("Scheduling work now, ID: %s", workSpec.id));
         int result = mDispatcher.schedule(job);
         if (result != FirebaseJobDispatcher.SCHEDULE_RESULT_SUCCESS) {
-            Log.e(TAG, String.format("Schedule failed. Result = %s", result));
+            Logger.error(TAG, String.format("Schedule failed. Result = %s", result));
         }
     }
 
@@ -95,7 +95,7 @@
         if (mIdGenerator == null) {
             mIdGenerator = new IdGenerator(mAppContext);
         }
-        Log.d(TAG, String.format("Scheduling work later, ID: %s", workSpec.id));
+        Logger.debug(TAG, String.format("Scheduling work later, ID: %s", workSpec.id));
         PendingIntent pendingIntent = createScheduleLaterPendingIntent(workSpec);
 
         // This wakes up the device at exactly the next run time.
diff --git a/work/workmanager-firebase/src/main/java/androidx/work/impl/background/firebase/FirebaseJobService.java b/work/workmanager-firebase/src/main/java/androidx/work/impl/background/firebase/FirebaseJobService.java
index f39dfb5..db1280d 100644
--- a/work/workmanager-firebase/src/main/java/androidx/work/impl/background/firebase/FirebaseJobService.java
+++ b/work/workmanager-firebase/src/main/java/androidx/work/impl/background/firebase/FirebaseJobService.java
@@ -19,8 +19,8 @@
 import android.support.annotation.NonNull;
 import android.support.annotation.RestrictTo;
 import android.text.TextUtils;
-import android.util.Log;
 
+import androidx.work.Logger;
 import androidx.work.impl.ExecutionListener;
 import androidx.work.impl.WorkManagerImpl;
 
@@ -58,11 +58,11 @@
     public boolean onStartJob(JobParameters params) {
         String workSpecId = params.getTag();
         if (TextUtils.isEmpty(workSpecId)) {
-            Log.e(TAG, "WorkSpec id not found!");
+            Logger.error(TAG, "WorkSpec id not found!");
             return false;
         }
 
-        Log.d(TAG, String.format("onStartJob for %s", workSpecId));
+        Logger.debug(TAG, String.format("onStartJob for %s", workSpecId));
         synchronized (mJobParameters) {
             mJobParameters.put(workSpecId, params);
         }
@@ -74,11 +74,11 @@
     public boolean onStopJob(JobParameters params) {
         String workSpecId = params.getTag();
         if (TextUtils.isEmpty(workSpecId)) {
-            Log.e(TAG, "WorkSpec id not found!");
+            Logger.error(TAG, "WorkSpec id not found!");
             return false;
         }
 
-        Log.d(TAG, String.format("onStopJob for %s", workSpecId));
+        Logger.debug(TAG, String.format("onStopJob for %s", workSpecId));
 
         synchronized (mJobParameters) {
             mJobParameters.remove(workSpecId);
@@ -92,7 +92,7 @@
             @NonNull String workSpecId,
             boolean isSuccessful,
             boolean needsReschedule) {
-        Log.d(TAG, String.format("%s executed on FirebaseJobDispatcher", workSpecId));
+        Logger.debug(TAG, String.format("%s executed on FirebaseJobDispatcher", workSpecId));
         JobParameters parameters;
         synchronized (mJobParameters) {
             parameters = mJobParameters.get(workSpecId);
diff --git a/work/workmanager-test/src/main/java/androidx/work/test/TestScheduler.java b/work/workmanager-test/src/main/java/androidx/work/test/TestScheduler.java
index bfec171..bbd05b3 100644
--- a/work/workmanager-test/src/main/java/androidx/work/test/TestScheduler.java
+++ b/work/workmanager-test/src/main/java/androidx/work/test/TestScheduler.java
@@ -119,8 +119,7 @@
                                     + " This will be ignored when testing.",
                             workSpec.id, workSpec.workerClassName));
                 }
-                WorkManagerImpl.getInstance()
-                        .startWork(workSpec.id);
+                WorkManagerImpl.getInstance().startWork(workSpec.id);
             }
         }
     }
diff --git a/work/workmanager/proguard-rules.pro b/work/workmanager/proguard-rules.pro
index 6040e02..f88e5e6 100644
--- a/work/workmanager/proguard-rules.pro
+++ b/work/workmanager/proguard-rules.pro
@@ -1,9 +1,3 @@
--assumenosideeffects class androidx.work.impl.logger.InternalLogger {
- public void verbose(...);
- public void debug(...);
- public void info(...);
-}
-
 -keep class * extends androidx.work.Worker
 -keep class * extends androidx.work.InputMerger
 # Worker#internalInit is marked as @Keep
diff --git a/work/workmanager/src/main/java/androidx/work/Configuration.java b/work/workmanager/src/main/java/androidx/work/Configuration.java
index 03a5a1f..9a9e449 100644
--- a/work/workmanager/src/main/java/androidx/work/Configuration.java
+++ b/work/workmanager/src/main/java/androidx/work/Configuration.java
@@ -38,7 +38,8 @@
      */
     public static final int MIN_SCHEDULER_LIMIT = 20;
 
-    private final Executor mExecutor;
+    private final @NonNull Executor mExecutor;
+    private final boolean mVerboseLoggingEnabled;
     private final int mMinJobSchedulerId;
     private final int mMaxJobSchedulerId;
     private final int mMaxSchedulerLimit;
@@ -49,6 +50,7 @@
         } else {
             mExecutor = builder.mExecutor;
         }
+        mVerboseLoggingEnabled = builder.mVerboseLoggingEnabled;
         mMinJobSchedulerId = builder.mMinJobSchedulerId;
         mMaxJobSchedulerId = builder.mMaxJobSchedulerId;
         mMaxSchedulerLimit = builder.mMaxSchedulerLimit;
@@ -62,6 +64,15 @@
     }
 
     /**
+     * @return {@code true} if verbose logging is enabled.
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    public boolean isVerboseLoggingEnabled() {
+        return mVerboseLoggingEnabled;
+    }
+
+    /**
      * @return The first valid id (inclusive) used by {@link WorkManager} when
      * creating new instances of {@link android.app.job.JobInfo}s.
      *
@@ -117,6 +128,7 @@
         int mMinJobSchedulerId = IdGenerator.INITIAL_ID;
         int mMaxJobSchedulerId = Integer.MAX_VALUE;
         int mMaxSchedulerLimit = MIN_SCHEDULER_LIMIT;
+        boolean mVerboseLoggingEnabled;
         Executor mExecutor;
 
         /**
@@ -193,6 +205,17 @@
         }
 
         /**
+         * Specifies whether to enable verbose WorkManager logging in logcat.
+         *
+         * @param verboseLoggingEnabled {@code true} to enable verbose logging
+         * @return This {@link Builder} instance
+         */
+        public @NonNull Builder setVerboseLoggingEnabled(boolean verboseLoggingEnabled) {
+            mVerboseLoggingEnabled = verboseLoggingEnabled;
+            return this;
+        }
+
+        /**
          * Builds a {@link Configuration} object.
          *
          * @return A {@link Configuration} object with this {@link Builder}'s parameters.
diff --git a/work/workmanager/src/main/java/androidx/work/InputMerger.java b/work/workmanager/src/main/java/androidx/work/InputMerger.java
index 3e45fe7..c12f0bf 100644
--- a/work/workmanager/src/main/java/androidx/work/InputMerger.java
+++ b/work/workmanager/src/main/java/androidx/work/InputMerger.java
@@ -18,7 +18,6 @@
 
 import android.support.annotation.NonNull;
 import android.support.annotation.RestrictTo;
-import android.util.Log;
 
 import java.util.List;
 
@@ -53,7 +52,7 @@
             Class<?> clazz = Class.forName(className);
             return (InputMerger) clazz.newInstance();
         } catch (Exception e) {
-            Log.e(TAG, "Trouble instantiating + " + className, e);
+            Logger.error(TAG, "Trouble instantiating + " + className, e);
         }
         return null;
     }
diff --git a/work/workmanager/src/main/java/androidx/work/Logger.java b/work/workmanager/src/main/java/androidx/work/Logger.java
new file mode 100644
index 0000000..b9ea54f
--- /dev/null
+++ b/work/workmanager/src/main/java/androidx/work/Logger.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.work;
+
+import android.support.annotation.RestrictTo;
+import android.util.Log;
+
+/**
+ * A class that handles logging to logcat.  It internally delegates to {@link Log} methods but
+ * handles library-level verbosity settings.  This class offers no threading guarantees.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public class Logger {
+
+    private static boolean sVerboseLoggingEnabled = false;
+
+    /**
+     * @param verboseLoggingEnabled {@code true} if verbose logging is enabled.
+     */
+    public static void setVerboseLoggingEnabled(boolean verboseLoggingEnabled) {
+        sVerboseLoggingEnabled = verboseLoggingEnabled;
+    }
+
+    /**
+     * Equivalent to Log.v.
+     */
+    public static void verbose(String tag, String message, Throwable... throwables)  {
+        if (sVerboseLoggingEnabled) {
+            if (throwables != null && throwables.length >= 1) {
+                Log.v(tag, message, throwables[0]);
+            } else {
+                Log.v(tag, message);
+            }
+        }
+    }
+
+    /**
+     * Equivalent to Log.d.
+     */
+    public static void debug(String tag, String message, Throwable... throwables)  {
+        if (sVerboseLoggingEnabled) {
+            if (throwables != null && throwables.length >= 1) {
+                Log.d(tag, message, throwables[0]);
+            } else {
+                Log.d(tag, message);
+            }
+        }
+    }
+
+    /**
+     * Equivalent to Log.i.
+     */
+    public static void info(String tag, String message, Throwable... throwables)  {
+        if (sVerboseLoggingEnabled) {
+            if (throwables != null && throwables.length >= 1) {
+                Log.i(tag, message, throwables[0]);
+            } else {
+                Log.i(tag, message);
+            }
+        }
+    }
+
+    /**
+     * Equivalent to Log.w.
+     */
+    public static void warning(String tag, String message, Throwable... throwables)  {
+        if (throwables != null && throwables.length >= 1) {
+            Log.w(tag, message, throwables[0]);
+        } else {
+            Log.w(tag, message);
+        }
+    }
+
+    /**
+     * Equivalent to Log.e.
+     */
+    public static void error(String tag, String message, Throwable... throwables)  {
+        if (throwables != null && throwables.length >= 1) {
+            Log.e(tag, message, throwables[0]);
+        } else {
+            Log.e(tag, message);
+        }
+    }
+
+    private Logger() {
+    }
+}
diff --git a/work/workmanager/src/main/java/androidx/work/impl/Processor.java b/work/workmanager/src/main/java/androidx/work/impl/Processor.java
index 86b7edf..2290823 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/Processor.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/Processor.java
@@ -18,9 +18,9 @@
 import android.content.Context;
 import android.support.annotation.NonNull;
 import android.support.annotation.RestrictTo;
-import android.util.Log;
 
 import androidx.work.Configuration;
+import androidx.work.Logger;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -87,7 +87,7 @@
         // Work may get triggered multiple times if they have passing constraints and new work with
         // those constraints are added.
         if (mEnqueuedWorkMap.containsKey(id)) {
-            Log.d(TAG, String.format("Work %s is already enqueued for processing", id));
+            Logger.debug(TAG, String.format("Work %s is already enqueued for processing", id));
             return false;
         }
 
@@ -99,7 +99,7 @@
                         .build();
         mEnqueuedWorkMap.put(id, workWrapper);
         mExecutor.execute(workWrapper);
-        Log.d(TAG, String.format("%s: processing %s", getClass().getSimpleName(), id));
+        Logger.debug(TAG, String.format("%s: processing %s", getClass().getSimpleName(), id));
         return true;
     }
 
@@ -110,14 +110,14 @@
      * @return {@code true} if the work was stopped successfully
      */
     public synchronized boolean stopWork(String id) {
-        Log.d(TAG, String.format("Processor stopping %s", id));
+        Logger.debug(TAG, String.format("Processor stopping %s", id));
         WorkerWrapper wrapper = mEnqueuedWorkMap.remove(id);
         if (wrapper != null) {
             wrapper.interrupt(false);
-            Log.d(TAG, String.format("WorkerWrapper stopped for %s", id));
+            Logger.debug(TAG, String.format("WorkerWrapper stopped for %s", id));
             return true;
         }
-        Log.d(TAG, String.format("WorkerWrapper could not be found for %s", id));
+        Logger.debug(TAG, String.format("WorkerWrapper could not be found for %s", id));
         return false;
     }
 
@@ -128,15 +128,15 @@
      * @return {@code true} if the work was stopped successfully
      */
     public synchronized boolean stopAndCancelWork(String id) {
-        Log.d(TAG, String.format("Processor cancelling %s", id));
+        Logger.debug(TAG, String.format("Processor cancelling %s", id));
         mCancelledIds.add(id);
         WorkerWrapper wrapper = mEnqueuedWorkMap.remove(id);
         if (wrapper != null) {
             wrapper.interrupt(true);
-            Log.d(TAG, String.format("WorkerWrapper cancelled for %s", id));
+            Logger.debug(TAG, String.format("WorkerWrapper cancelled for %s", id));
             return true;
         }
-        Log.d(TAG, String.format("WorkerWrapper could not be found for %s", id));
+        Logger.debug(TAG, String.format("WorkerWrapper could not be found for %s", id));
         return false;
     }
 
@@ -192,7 +192,7 @@
             boolean needsReschedule) {
 
         mEnqueuedWorkMap.remove(workSpecId);
-        Log.d(TAG, String.format("%s %s executed; isSuccessful = %s, reschedule = %s",
+        Logger.debug(TAG, String.format("%s %s executed; isSuccessful = %s, reschedule = %s",
                 getClass().getSimpleName(), workSpecId, isSuccessful, needsReschedule));
 
         // TODO(sumir): Let's get some synchronization guarantees here.
diff --git a/work/workmanager/src/main/java/androidx/work/impl/Schedulers.java b/work/workmanager/src/main/java/androidx/work/impl/Schedulers.java
index 041f788..22244b1 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/Schedulers.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/Schedulers.java
@@ -23,9 +23,9 @@
 import android.support.annotation.NonNull;
 import android.support.annotation.RestrictTo;
 import android.support.annotation.VisibleForTesting;
-import android.util.Log;
 
 import androidx.work.Configuration;
+import androidx.work.Logger;
 import androidx.work.impl.background.systemalarm.SystemAlarmScheduler;
 import androidx.work.impl.background.systemalarm.SystemAlarmService;
 import androidx.work.impl.background.systemjob.SystemJobScheduler;
@@ -111,17 +111,17 @@
         if (Build.VERSION.SDK_INT >= WorkManagerImpl.MIN_JOB_SCHEDULER_API_LEVEL) {
             scheduler = new SystemJobScheduler(context, workManager);
             setComponentEnabled(context, SystemJobService.class, true);
-            Log.d(TAG, "Created SystemJobScheduler and enabled SystemJobService");
+            Logger.debug(TAG, "Created SystemJobScheduler and enabled SystemJobService");
         } else {
             try {
                 scheduler = tryCreateFirebaseJobScheduler(context);
                 enableFirebaseJobService = true;
-                Log.d(TAG, "Created FirebaseJobScheduler");
+                Logger.debug(TAG, "Created FirebaseJobScheduler");
             } catch (Exception e) {
                 // Also catches the exception thrown if Play Services was not found on the device.
                 scheduler = new SystemAlarmScheduler(context);
                 enableSystemAlarmService = true;
-                Log.d(TAG, "Created SystemAlarmScheduler");
+                Logger.debug(TAG, "Created SystemAlarmScheduler");
             }
         }
 
diff --git a/work/workmanager/src/main/java/androidx/work/impl/WorkContinuationImpl.java b/work/workmanager/src/main/java/androidx/work/impl/WorkContinuationImpl.java
index 6fdc186..3d33b1c 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/WorkContinuationImpl.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/WorkContinuationImpl.java
@@ -23,10 +23,10 @@
 import android.support.annotation.RestrictTo;
 import android.support.annotation.WorkerThread;
 import android.text.TextUtils;
-import android.util.Log;
 
 import androidx.work.ArrayCreatingInputMerger;
 import androidx.work.ExistingWorkPolicy;
+import androidx.work.Logger;
 import androidx.work.OneTimeWorkRequest;
 import androidx.work.SynchronousWorkContinuation;
 import androidx.work.WorkContinuation;
@@ -180,7 +180,8 @@
             mWorkManagerImpl.getTaskExecutor()
                     .executeOnBackgroundThread(new EnqueueRunnable(this));
         } else {
-            Log.w(TAG, String.format("Already enqueued work ids (%s)", TextUtils.join(", ", mIds)));
+            Logger.warning(TAG,
+                    String.format("Already enqueued work ids (%s)", TextUtils.join(", ", mIds)));
         }
     }
 
@@ -196,7 +197,8 @@
             // and marks them enqueued using the markEnqueued() method, parent first.
             new EnqueueRunnable(this).run();
         } else {
-            Log.w(TAG, String.format("Already enqueued work ids (%s)", TextUtils.join(", ", mIds)));
+            Logger.warning(TAG,
+                    String.format("Already enqueued work ids (%s)", TextUtils.join(", ", mIds)));
         }
     }
 
diff --git a/work/workmanager/src/main/java/androidx/work/impl/WorkManagerImpl.java b/work/workmanager/src/main/java/androidx/work/impl/WorkManagerImpl.java
index 763a454..ab8a2fa 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/WorkManagerImpl.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/WorkManagerImpl.java
@@ -30,6 +30,7 @@
 import androidx.work.Configuration;
 import androidx.work.ExistingPeriodicWorkPolicy;
 import androidx.work.ExistingWorkPolicy;
+import androidx.work.Logger;
 import androidx.work.OneTimeWorkRequest;
 import androidx.work.PeriodicWorkRequest;
 import androidx.work.R;
@@ -179,6 +180,8 @@
         mPreferences = new Preferences(mContext);
         mForceStopRunnableCompleted = false;
 
+        Logger.setVerboseLoggingEnabled(mConfiguration.isVerboseLoggingEnabled());
+
         // Checks for app force stops.
         mTaskExecutor.executeOnBackgroundThread(new ForceStopRunnable(context, this));
     }
diff --git a/work/workmanager/src/main/java/androidx/work/impl/WorkerWrapper.java b/work/workmanager/src/main/java/androidx/work/impl/WorkerWrapper.java
index d510e30..1970786 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/WorkerWrapper.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/WorkerWrapper.java
@@ -29,11 +29,11 @@
 import android.support.annotation.RestrictTo;
 import android.support.annotation.VisibleForTesting;
 import android.support.annotation.WorkerThread;
-import android.util.Log;
 
 import androidx.work.Configuration;
 import androidx.work.Data;
 import androidx.work.InputMerger;
+import androidx.work.Logger;
 import androidx.work.State;
 import androidx.work.Worker;
 import androidx.work.impl.model.DependencyDao;
@@ -101,7 +101,7 @@
         try {
             mWorkSpec = mWorkSpecDao.getWorkSpec(mWorkSpecId);
             if (mWorkSpec == null) {
-                Log.e(TAG, String.format("Didn't find WorkSpec for id %s", mWorkSpecId));
+                Logger.error(TAG, String.format("Didn't find WorkSpec for id %s", mWorkSpecId));
                 notifyListener(false, false);
                 return;
             }
@@ -129,7 +129,7 @@
         } else {
             InputMerger inputMerger = InputMerger.fromClassName(mWorkSpec.inputMergerClassName);
             if (inputMerger == null) {
-                Log.e(TAG, String.format("Could not create Input Merger %s",
+                Logger.error(TAG, String.format("Could not create Input Merger %s",
                         mWorkSpec.inputMergerClassName));
                 setFailedAndNotify();
                 return;
@@ -153,7 +153,8 @@
         }
 
         if (mWorker == null) {
-            Log.e(TAG, String.format("Could for create Worker %s", mWorkSpec.workerClassName));
+            Logger.error(TAG,
+                    String.format("Could for create Worker %s", mWorkSpec.workerClassName));
             setFailedAndNotify();
             return;
         }
@@ -170,9 +171,8 @@
                 result = mWorker.doWork();
             } catch (Exception | Error e) {
                 result = Worker.Result.FAILURE;
-                Log.e(TAG,
-                        String.format(
-                                "Worker %s failed because it threw an exception/error",
+                Logger.error(TAG,
+                        String.format("Worker %s failed because it threw an exception/error",
                                 mWorkSpecId),
                         e);
             }
@@ -217,11 +217,11 @@
     private void notifyIncorrectStatus() {
         State status = mWorkSpecDao.getState(mWorkSpecId);
         if (status == RUNNING) {
-            Log.d(TAG, String.format("Status for %s is RUNNING;"
+            Logger.debug(TAG, String.format("Status for %s is RUNNING;"
                     + "not doing any work and rescheduling for later execution", mWorkSpecId));
             notifyListener(false, true);
         } else {
-            Log.e(TAG,
+            Logger.debug(TAG,
                     String.format("Status for %s is %s; not doing any work", mWorkSpecId, status));
             notifyListener(false, false);
         }
@@ -229,7 +229,7 @@
 
     private boolean tryCheckForInterruptionAndNotify() {
         if (mInterrupted) {
-            Log.d(TAG, String.format("Work interrupted for %s", mWorkSpecId));
+            Logger.debug(TAG, String.format("Work interrupted for %s", mWorkSpecId));
             State currentState = mWorkSpecDao.getState(mWorkSpecId);
             if (currentState == null) {
                 // This can happen because of a beginUniqueWork(..., REPLACE, ...).  Notify the
@@ -258,7 +258,7 @@
     private void handleResult(Worker.Result result) {
         switch (result) {
             case SUCCESS: {
-                Log.d(TAG, String.format("Worker result SUCCESS for %s", mWorkSpecId));
+                Logger.debug(TAG, String.format("Worker result SUCCESS for %s", mWorkSpecId));
                 if (mWorkSpec.isPeriodic()) {
                     resetPeriodicAndNotify(true);
                 } else {
@@ -268,14 +268,14 @@
             }
 
             case RETRY: {
-                Log.d(TAG, String.format("Worker result RETRY for %s", mWorkSpecId));
+                Logger.debug(TAG, String.format("Worker result RETRY for %s", mWorkSpecId));
                 rescheduleAndNotify();
                 break;
             }
 
             case FAILURE:
             default: {
-                Log.d(TAG, String.format("Worker result FAILURE for %s", mWorkSpecId));
+                Logger.debug(TAG, String.format("Worker result FAILURE for %s", mWorkSpecId));
                 if (mWorkSpec.isPeriodic()) {
                     resetPeriodicAndNotify(false);
                 } else {
@@ -387,7 +387,8 @@
             List<String> dependentWorkIds = mDependencyDao.getDependentWorkIds(mWorkSpecId);
             for (String dependentWorkId : dependentWorkIds) {
                 if (mDependencyDao.hasCompletedAllPrerequisites(dependentWorkId)) {
-                    Log.d(TAG, String.format("Setting status to enqueued for %s", dependentWorkId));
+                    Logger.debug(TAG,
+                            String.format("Setting status to enqueued for %s", dependentWorkId));
                     mWorkSpecDao.setState(ENQUEUED, dependentWorkId);
                     mWorkSpecDao.setPeriodStartTime(dependentWorkId, currentTimeMillis);
                 }
@@ -450,7 +451,7 @@
                     extras);
             return worker;
         } catch (Exception e) {
-            Log.e(TAG, "Trouble instantiating " + workerClassName, e);
+            Logger.error(TAG, "Trouble instantiating " + workerClassName, e);
         }
         return null;
     }
diff --git a/work/workmanager/src/main/java/androidx/work/impl/background/greedy/GreedyScheduler.java b/work/workmanager/src/main/java/androidx/work/impl/background/greedy/GreedyScheduler.java
index d5f6f4c..ebd885a 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/background/greedy/GreedyScheduler.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/background/greedy/GreedyScheduler.java
@@ -21,8 +21,8 @@
 import android.support.annotation.NonNull;
 import android.support.annotation.RestrictTo;
 import android.support.annotation.VisibleForTesting;
-import android.util.Log;
 
+import androidx.work.Logger;
 import androidx.work.State;
 import androidx.work.impl.ExecutionListener;
 import androidx.work.impl.Scheduler;
@@ -76,7 +76,7 @@
                     // background scheduler should take care of them.
                     if (Build.VERSION.SDK_INT < 24
                             || !workSpec.constraints.hasContentUriTriggers()) {
-                        Log.d(TAG, String.format("Starting tracking for %s", workSpec.id));
+                        Logger.debug(TAG, String.format("Starting tracking for %s", workSpec.id));
                         mConstrainedWorkSpecs.add(workSpec);
                     }
                 } else {
@@ -92,7 +92,7 @@
 
     @Override
     public synchronized void cancel(@NonNull String workSpecId) {
-        Log.d(TAG, String.format("Cancelling work ID %s", workSpecId));
+        Logger.debug(TAG, String.format("Cancelling work ID %s", workSpecId));
         mWorkManagerImpl.stopWork(workSpecId);
         removeConstraintTrackingFor(workSpecId);
     }
@@ -100,7 +100,7 @@
     @Override
     public synchronized void onAllConstraintsMet(@NonNull List<String> workSpecIds) {
         for (String workSpecId : workSpecIds) {
-            Log.d(TAG, String.format("Constraints met: Scheduling work ID %s", workSpecId));
+            Logger.debug(TAG, String.format("Constraints met: Scheduling work ID %s", workSpecId));
             mWorkManagerImpl.startWork(workSpecId);
         }
     }
@@ -108,7 +108,8 @@
     @Override
     public synchronized void onAllConstraintsNotMet(@NonNull List<String> workSpecIds) {
         for (String workSpecId : workSpecIds) {
-            Log.d(TAG, String.format("Constraints not met: Cancelling work ID %s", workSpecId));
+            Logger.debug(TAG,
+                    String.format("Constraints not met: Cancelling work ID %s", workSpecId));
             mWorkManagerImpl.stopWork(workSpecId);
         }
     }
@@ -123,7 +124,7 @@
     private synchronized void removeConstraintTrackingFor(@NonNull String workSpecId) {
         for (int i = 0, size = mConstrainedWorkSpecs.size(); i < size; ++i) {
             if (mConstrainedWorkSpecs.get(i).id.equals(workSpecId)) {
-                Log.d(TAG, String.format("Stopping tracking for %s", workSpecId));
+                Logger.debug(TAG, String.format("Stopping tracking for %s", workSpecId));
                 mConstrainedWorkSpecs.remove(i);
                 mWorkConstraintsTracker.replace(mConstrainedWorkSpecs);
                 break;
diff --git a/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/Alarms.java b/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/Alarms.java
index e46d95a..d85c1d9 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/Alarms.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/Alarms.java
@@ -25,8 +25,8 @@
 import android.os.Build;
 import android.support.annotation.NonNull;
 import android.support.annotation.RestrictTo;
-import android.util.Log;
 
+import androidx.work.Logger;
 import androidx.work.impl.WorkDatabase;
 import androidx.work.impl.WorkManagerImpl;
 import androidx.work.impl.model.SystemIdInfo;
@@ -89,7 +89,8 @@
         SystemIdInfo systemIdInfo = systemIdInfoDao.getSystemIdInfo(workSpecId);
         if (systemIdInfo != null) {
             cancelExactAlarm(context, workSpecId, systemIdInfo.systemId);
-            Log.d(TAG, String.format("Removing SystemIdInfo for workSpecId (%s)", workSpecId));
+            Logger.debug(TAG,
+                    String.format("Removing SystemIdInfo for workSpecId (%s)", workSpecId));
             systemIdInfoDao.removeSystemIdInfo(workSpecId);
         }
     }
@@ -104,7 +105,7 @@
         PendingIntent pendingIntent = PendingIntent.getService(
                 context, alarmId, delayMet, PendingIntent.FLAG_NO_CREATE);
         if (pendingIntent != null && alarmManager != null) {
-            Log.d(TAG, String.format(
+            Logger.debug(TAG, String.format(
                     "Cancelling existing alarm with (workSpecId, systemId) (%s, %s)",
                     workSpecId,
                     alarmId));
diff --git a/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/CommandHandler.java b/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/CommandHandler.java
index 302486a..d651629 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/CommandHandler.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/CommandHandler.java
@@ -23,8 +23,8 @@
 import android.support.annotation.Nullable;
 import android.support.annotation.RestrictTo;
 import android.support.annotation.WorkerThread;
-import android.util.Log;
 
+import androidx.work.Logger;
 import androidx.work.impl.ExecutionListener;
 import androidx.work.impl.WorkDatabase;
 import androidx.work.impl.WorkManagerImpl;
@@ -167,8 +167,10 @@
         } else {
             Bundle extras = intent.getExtras();
             if (!hasKeys(extras, KEY_WORKSPEC_ID)) {
-                Log.e(TAG, String.format(
-                        "Invalid request for %s, requires %s.", action, KEY_WORKSPEC_ID));
+                Logger.error(TAG,
+                        String.format("Invalid request for %s, requires %s.",
+                                action,
+                                KEY_WORKSPEC_ID));
             } else {
                 if (ACTION_SCHEDULE_WORK.equals(action)) {
                     handleScheduleWorkIntent(intent, startId, dispatcher);
@@ -179,7 +181,7 @@
                 } else if (ACTION_EXECUTION_COMPLETED.equals(action)) {
                     handleExecutionCompleted(intent, startId, dispatcher);
                 } else {
-                    Log.w(TAG, String.format("Ignoring intent %s", intent));
+                    Logger.warning(TAG, String.format("Ignoring intent %s", intent));
                 }
             }
         }
@@ -192,7 +194,7 @@
 
         Bundle extras = intent.getExtras();
         String workSpecId = extras.getString(KEY_WORKSPEC_ID);
-        Log.d(TAG, String.format("Handling schedule work for %s", workSpecId));
+        Logger.debug(TAG, String.format("Handling schedule work for %s", workSpecId));
 
         WorkManagerImpl workManager = dispatcher.getWorkManager();
         WorkDatabase workDatabase = workManager.getWorkDatabase();
@@ -202,11 +204,12 @@
         long triggerAt = workSpec.calculateNextRunTime();
 
         if (!workSpec.hasConstraints()) {
-            Log.d(TAG, String.format("Setting up Alarms for %s", workSpecId));
+            Logger.debug(TAG, String.format("Setting up Alarms for %s", workSpecId));
             Alarms.setAlarm(mContext, dispatcher.getWorkManager(), workSpecId, triggerAt);
         } else {
             // Schedule an alarm irrespective of whether all constraints matched.
-            Log.d(TAG, String.format("Opportunistically setting an alarm for %s", workSpecId));
+            Logger.debug(TAG,
+                    String.format("Opportunistically setting an alarm for %s", workSpecId));
             Alarms.setAlarm(
                     mContext,
                     dispatcher.getWorkManager(),
@@ -232,7 +235,7 @@
         Bundle extras = intent.getExtras();
         synchronized (mLock) {
             String workSpecId = extras.getString(KEY_WORKSPEC_ID);
-            Log.d(TAG, String.format("Handing delay met for %s", workSpecId));
+            Logger.debug(TAG, String.format("Handing delay met for %s", workSpecId));
             DelayMetCommandHandler delayMetCommandHandler =
                     new DelayMetCommandHandler(mContext, startId, workSpecId, dispatcher);
             mPendingDelayMet.put(workSpecId, delayMetCommandHandler);
@@ -246,7 +249,7 @@
 
         Bundle extras = intent.getExtras();
         String workSpecId = extras.getString(KEY_WORKSPEC_ID);
-        Log.d(TAG, String.format("Handing stopWork work for %s", workSpecId));
+        Logger.debug(TAG, String.format("Handing stopWork work for %s", workSpecId));
 
         dispatcher.getWorkManager().stopWork(workSpecId);
         Alarms.cancelAlarm(mContext, dispatcher.getWorkManager(), workSpecId);
@@ -259,7 +262,7 @@
             @NonNull Intent intent, int startId,
             @NonNull SystemAlarmDispatcher dispatcher) {
 
-        Log.d(TAG, String.format("Handling constraints changed %s", intent));
+        Logger.debug(TAG, String.format("Handling constraints changed %s", intent));
         // Constraints changed command handler is synchronous. No cleanup
         // is necessary.
         ConstraintsCommandHandler changedCommandHandler =
@@ -272,7 +275,7 @@
             int startId,
             @NonNull SystemAlarmDispatcher dispatcher) {
 
-        Log.d(TAG, String.format("Handling reschedule %s, %s", intent, startId));
+        Logger.debug(TAG, String.format("Handling reschedule %s, %s", intent, startId));
         dispatcher.getWorkManager().rescheduleEligibleWork();
     }
 
@@ -285,7 +288,7 @@
         String workSpecId = extras.getString(KEY_WORKSPEC_ID);
         boolean isSuccessful = extras.getBoolean(KEY_IS_SUCCESSFUL);
         boolean needsReschedule = extras.getBoolean(KEY_NEEDS_RESCHEDULE);
-        Log.d(TAG, String.format("Handling onExecutionCompleted %s, %s", intent, startId));
+        Logger.debug(TAG, String.format("Handling onExecutionCompleted %s, %s", intent, startId));
         // Delegate onExecuted() to the command handler.
         onExecuted(workSpecId, isSuccessful, needsReschedule);
         // Check if we need to stop service.
diff --git a/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/ConstraintProxy.java b/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/ConstraintProxy.java
index 69e6140..a5dc676 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/ConstraintProxy.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/ConstraintProxy.java
@@ -20,9 +20,9 @@
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.util.Log;
 
 import androidx.work.Constraints;
+import androidx.work.Logger;
 import androidx.work.impl.model.WorkSpec;
 
 import java.util.List;
@@ -32,7 +32,7 @@
 
     @Override
     public void onReceive(Context context, Intent intent) {
-        Log.d(TAG, String.format("onReceive : %s", intent));
+        Logger.debug(TAG, String.format("onReceive : %s", intent));
         Intent constraintChangedIntent = CommandHandler.createConstraintsChangedIntent(context);
         context.startService(constraintChangedIntent);
     }
diff --git a/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/ConstraintProxyUpdateReceiver.java b/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/ConstraintProxyUpdateReceiver.java
index 8046fdb..1197d8f 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/ConstraintProxyUpdateReceiver.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/ConstraintProxyUpdateReceiver.java
@@ -19,8 +19,8 @@
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.util.Log;
 
+import androidx.work.Logger;
 import androidx.work.impl.background.systemalarm.ConstraintProxy.BatteryChargingProxy;
 import androidx.work.impl.background.systemalarm.ConstraintProxy.BatteryNotLowProxy;
 import androidx.work.impl.background.systemalarm.ConstraintProxy.NetworkStateProxy;
@@ -72,7 +72,7 @@
     public void onReceive(Context context, Intent intent) {
         String action = intent != null ? intent.getAction() : null;
         if (!ACTION.equals(action)) {
-            Log.d(TAG, String.format("Ignoring unknown action %s", action));
+            Logger.debug(TAG, String.format("Ignoring unknown action %s", action));
         } else {
             boolean batteryNotLowProxyEnabled = intent.getBooleanExtra(
                     KEY_BATTERY_NOT_LOW_PROXY_ENABLED, false);
@@ -83,7 +83,7 @@
             boolean networkStateProxyEnabled = intent.getBooleanExtra(
                     KEY_NETWORK_STATE_PROXY_ENABLED, false);
 
-            Log.d(TAG, String.format("Updating proxies: BatteryNotLowProxy enabled (%s), "
+            Logger.debug(TAG, String.format("Updating proxies: BatteryNotLowProxy enabled (%s), "
                             + "BatteryChargingProxy enabled (%s), "
                             + "StorageNotLowProxy (%s), "
                             + "NetworkStateProxy enabled (%s)", batteryNotLowProxyEnabled,
diff --git a/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/ConstraintsCommandHandler.java b/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/ConstraintsCommandHandler.java
index 6dbd752..9a014cb 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/ConstraintsCommandHandler.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/ConstraintsCommandHandler.java
@@ -21,8 +21,8 @@
 import android.support.annotation.NonNull;
 import android.support.annotation.RestrictTo;
 import android.support.annotation.WorkerThread;
-import android.util.Log;
 
+import androidx.work.Logger;
 import androidx.work.impl.constraints.WorkConstraintsTracker;
 import androidx.work.impl.model.WorkSpec;
 
@@ -95,7 +95,7 @@
         for (WorkSpec workSpec : mEligibleWorkSpecs) {
             String workSpecId = workSpec.id;
             Intent intent = CommandHandler.createDelayMetIntent(mContext, workSpecId);
-            Log.d(TAG, String.format(
+            Logger.debug(TAG, String.format(
                     "Creating a delay_met command for workSpec with id (%s)", workSpecId));
             mDispatcher.postOnMainThread(
                     new SystemAlarmDispatcher.AddRunnable(mDispatcher, intent, mStartId));
diff --git a/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/DelayMetCommandHandler.java b/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/DelayMetCommandHandler.java
index e3bb4f0..90ceb08 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/DelayMetCommandHandler.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/DelayMetCommandHandler.java
@@ -23,8 +23,8 @@
 import android.support.annotation.Nullable;
 import android.support.annotation.RestrictTo;
 import android.support.annotation.WorkerThread;
-import android.util.Log;
 
+import androidx.work.Logger;
 import androidx.work.impl.ExecutionListener;
 import androidx.work.impl.constraints.WorkConstraintsCallback;
 import androidx.work.impl.constraints.WorkConstraintsTracker;
@@ -77,7 +77,7 @@
 
     @Override
     public void onAllConstraintsMet(@NonNull List<String> ignored) {
-        Log.d(TAG, String.format("onAllConstraintsMet for %s", mWorkSpecId));
+        Logger.debug(TAG, String.format("onAllConstraintsMet for %s", mWorkSpecId));
         // Constraints met, schedule execution
 
         // Not using WorkManagerImpl#startWork() here because we need to know if the processor
@@ -104,7 +104,7 @@
             boolean isSuccessful,
             boolean needsReschedule) {
 
-        Log.d(TAG, String.format(
+        Logger.debug(TAG, String.format(
                 "onExecuted %s, %s, %s", workSpecId, isSuccessful, needsReschedule));
 
         cleanUp();
@@ -121,7 +121,7 @@
 
     @Override
     public void onTimeLimitExceeded(@NonNull String workSpecId) {
-        Log.d(TAG, String.format("Exceeded time limits on execution for %s", workSpecId));
+        Logger.debug(TAG, String.format("Exceeded time limits on execution for %s", workSpecId));
         stopWork();
     }
 
@@ -135,7 +135,8 @@
         mWakeLock = WakeLocks.newWakeLock(
                 mContext,
                 String.format("%s (%s)", mWorkSpecId, mStartId));
-        Log.d(TAG, String.format("Acquiring wakelock %s for WorkSpec %s", mWakeLock, mWorkSpecId));
+        Logger.debug(TAG,
+                String.format("Acquiring wakelock %s for WorkSpec %s", mWakeLock, mWorkSpecId));
         mWakeLock.acquire();
 
         WorkSpec workSpec = mDispatcher.getWorkManager()
@@ -148,7 +149,7 @@
         mHasConstraints = workSpec.hasConstraints();
 
         if (!mHasConstraints) {
-            Log.d(TAG, String.format("No constraints for %s", mWorkSpecId));
+            Logger.debug(TAG, String.format("No constraints for %s", mWorkSpecId));
             onAllConstraintsMet(Collections.singletonList(mWorkSpecId));
         } else {
             // Allow tracker to report constraint changes
@@ -165,7 +166,7 @@
         // WorkTimer thread as well as the command executor service in SystemAlarmDispatcher.
         synchronized (mLock) {
             if (!mHasPendingStopWorkCommand) {
-                Log.d(TAG, String.format("Stopping work for workspec %s", mWorkSpecId));
+                Logger.debug(TAG, String.format("Stopping work for workspec %s", mWorkSpecId));
                 Intent stopWork = CommandHandler.createStopWorkIntent(mContext, mWorkSpecId);
                 mDispatcher.postOnMainThread(
                         new SystemAlarmDispatcher.AddRunnable(mDispatcher, stopWork, mStartId));
@@ -174,20 +175,21 @@
                 // reschedule should not happen. For e.g. DELAY_MET when constraints are not met,
                 // should not result in a reschedule.
                 if (mDispatcher.getProcessor().isEnqueued(mWorkSpecId)) {
-                    Log.d(TAG, String.format("WorkSpec %s needs to be rescheduled", mWorkSpecId));
+                    Logger.debug(TAG,
+                            String.format("WorkSpec %s needs to be rescheduled", mWorkSpecId));
                     Intent reschedule = CommandHandler.createScheduleWorkIntent(mContext,
                             mWorkSpecId);
                     mDispatcher.postOnMainThread(
                             new SystemAlarmDispatcher.AddRunnable(mDispatcher, reschedule,
                                     mStartId));
                 } else {
-                    Log.d(TAG, String.format(
+                    Logger.debug(TAG, String.format(
                             "Processor does not have WorkSpec %s. No need to reschedule ",
                             mWorkSpecId));
                 }
                 mHasPendingStopWorkCommand = true;
             } else {
-                Log.d(TAG, String.format("Already stopped work for %s", mWorkSpecId));
+                Logger.debug(TAG, String.format("Already stopped work for %s", mWorkSpecId));
             }
         }
     }
@@ -205,7 +207,7 @@
 
             // release wake locks
             if (mWakeLock != null && mWakeLock.isHeld()) {
-                Log.d(TAG, String.format(
+                Logger.debug(TAG, String.format(
                         "Releasing wakelock %s for WorkSpec %s", mWakeLock, mWorkSpecId));
                 mWakeLock.release();
             }
diff --git a/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/RescheduleReceiver.java b/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/RescheduleReceiver.java
index b8adec2..797b730 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/RescheduleReceiver.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/RescheduleReceiver.java
@@ -20,8 +20,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.Build;
-import android.util.Log;
 
+import androidx.work.Logger;
 import androidx.work.impl.WorkManagerImpl;
 
 /**
@@ -37,7 +37,7 @@
             WorkManagerImpl workManager = WorkManagerImpl.getInstance();
             if (workManager == null) {
                 // WorkManager has not already been initialized.
-                Log.e(TAG,
+                Logger.error(TAG,
                         "Cannot reschedule jobs. WorkManager needs to be initialized via a "
                                 + "ContentProvider#onCreate() or an Application#onCreate().");
             } else {
diff --git a/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/SystemAlarmDispatcher.java b/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/SystemAlarmDispatcher.java
index f36f2cf..f82e9f8 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/SystemAlarmDispatcher.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/SystemAlarmDispatcher.java
@@ -27,8 +27,8 @@
 import android.support.annotation.RestrictTo;
 import android.support.annotation.VisibleForTesting;
 import android.text.TextUtils;
-import android.util.Log;
 
+import androidx.work.Logger;
 import androidx.work.impl.ExecutionListener;
 import androidx.work.impl.Processor;
 import androidx.work.impl.WorkManagerImpl;
@@ -130,7 +130,7 @@
         assertMainThread();
         String action = intent.getAction();
         if (TextUtils.isEmpty(action)) {
-            Log.w(TAG, "Unknown command. Ignoring");
+            Logger.warning(TAG, "Unknown command. Ignoring");
             return false;
         }
 
@@ -152,7 +152,7 @@
 
     void setCompletedListener(@NonNull CommandsCompletedListener listener) {
         if (mCompletedListener != null) {
-            Log.e(TAG, "A completion listener for SystemAlarmDispatcher already exists.");
+            Logger.error(TAG, "A completion listener for SystemAlarmDispatcher already exists.");
             return;
         }
         mCompletedListener = listener;
@@ -182,7 +182,7 @@
         // has no more pending commands, stop the service.
         synchronized (mIntents) {
             if (!mCommandHandler.hasPendingCommands() && mIntents.isEmpty()) {
-                Log.d(TAG, "No more commands & intents.");
+                Logger.debug(TAG, "No more commands & intents.");
                 if (mCompletedListener != null) {
                     mCompletedListener.onAllCommandsCompleted();
                 }
@@ -211,12 +211,13 @@
                     if (intent != null) {
                         final String action = intent.getAction();
                         final int startId = intent.getIntExtra(KEY_START_ID, DEFAULT_START_ID);
-                        Log.d(TAG, String.format("Processing command %s, %s", intent, startId));
+                        Logger.debug(TAG,
+                                String.format("Processing command %s, %s", intent, startId));
                         final PowerManager.WakeLock wakeLock = WakeLocks.newWakeLock(
                                 mContext,
                                 String.format("%s (%s)", action, startId));
                         try {
-                            Log.d(TAG, String.format(
+                            Logger.debug(TAG, String.format(
                                     "Acquiring operation wake lock (%s) %s",
                                     action,
                                     wakeLock));
@@ -246,7 +247,7 @@
                                 mIntents.remove(0);
                             }
 
-                            Log.d(TAG, String.format(
+                            Logger.debug(TAG, String.format(
                                     "Releasing operation wake lock (%s) %s",
                                     action,
                                     wakeLock));
diff --git a/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/SystemAlarmScheduler.java b/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/SystemAlarmScheduler.java
index 916269f..115e5868 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/SystemAlarmScheduler.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/SystemAlarmScheduler.java
@@ -20,8 +20,8 @@
 import android.content.Intent;
 import android.support.annotation.NonNull;
 import android.support.annotation.RestrictTo;
-import android.util.Log;
 
+import androidx.work.Logger;
 import androidx.work.impl.Scheduler;
 import androidx.work.impl.model.WorkSpec;
 
@@ -59,7 +59,7 @@
      * times to drift to guarantee that the interval duration always elapses between alarms.
      */
     private void scheduleWorkSpec(@NonNull WorkSpec workSpec) {
-        Log.d(TAG, String.format("Scheduling work with workSpecId %s", workSpec.id));
+        Logger.debug(TAG, String.format("Scheduling work with workSpecId %s", workSpec.id));
         Intent scheduleIntent = CommandHandler.createScheduleWorkIntent(mContext, workSpec.id);
         mContext.startService(scheduleIntent);
     }
diff --git a/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/SystemAlarmService.java b/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/SystemAlarmService.java
index 1bfec63..48b3c6d 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/SystemAlarmService.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/SystemAlarmService.java
@@ -21,7 +21,8 @@
 import android.content.Intent;
 import android.support.annotation.MainThread;
 import android.support.annotation.RestrictTo;
-import android.util.Log;
+
+import androidx.work.Logger;
 
 /**
  * Service invoked by {@link android.app.AlarmManager} to run work tasks.
@@ -61,7 +62,7 @@
     @MainThread
     @Override
     public void onAllCommandsCompleted() {
-        Log.d(TAG, "All commands completed in dispatcher");
+        Logger.debug(TAG, "All commands completed in dispatcher");
         stopSelf();
     }
 }
diff --git a/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/WorkTimer.java b/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/WorkTimer.java
index e6ef15f..f0f0912 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/WorkTimer.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/WorkTimer.java
@@ -19,8 +19,8 @@
 import android.support.annotation.NonNull;
 import android.support.annotation.RestrictTo;
 import android.support.annotation.VisibleForTesting;
-import android.util.Log;
 
+import androidx.work.Logger;
 import androidx.work.WorkRequest;
 
 import java.util.HashMap;
@@ -59,7 +59,7 @@
             @NonNull TimeLimitExceededListener listener) {
 
         synchronized (mLock) {
-            Log.d(TAG, String.format("Starting timer for %s", workSpecId));
+            Logger.debug(TAG, String.format("Starting timer for %s", workSpecId));
             // clear existing timer's first
             stopTimer(workSpecId);
             WorkTimerRunnable runnable = new WorkTimerRunnable(this, workSpecId);
@@ -72,7 +72,7 @@
     void stopTimer(@NonNull final String workSpecId) {
         synchronized (mLock) {
             if (mTimerMap.containsKey(workSpecId)) {
-                Log.d(TAG, String.format("Stopping timer for %s", workSpecId));
+                Logger.debug(TAG, String.format("Stopping timer for %s", workSpecId));
                 mTimerMap.remove(workSpecId);
                 mListeners.remove(workSpecId);
             }
@@ -114,7 +114,7 @@
                         listener.onTimeLimitExceeded(mWorkSpecId);
                     }
                 } else {
-                    Log.d(TAG, String.format(
+                    Logger.debug(TAG, String.format(
                             "Timer with %s is already marked as complete.", mWorkSpecId));
                 }
             }
diff --git a/work/workmanager/src/main/java/androidx/work/impl/background/systemjob/SystemJobInfoConverter.java b/work/workmanager/src/main/java/androidx/work/impl/background/systemjob/SystemJobInfoConverter.java
index 22c5559..7f55201 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/background/systemjob/SystemJobInfoConverter.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/background/systemjob/SystemJobInfoConverter.java
@@ -27,11 +27,11 @@
 import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.support.annotation.VisibleForTesting;
-import android.util.Log;
 
 import androidx.work.BackoffPolicy;
 import androidx.work.Constraints;
 import androidx.work.ContentUriTriggers;
+import androidx.work.Logger;
 import androidx.work.NetworkType;
 import androidx.work.impl.WorkManagerImpl;
 import androidx.work.impl.model.WorkSpec;
@@ -90,7 +90,7 @@
             if (Build.VERSION.SDK_INT >= 24) {
                 builder.setPeriodic(workSpec.intervalDuration, workSpec.flexDuration);
             } else {
-                Log.d(TAG,
+                Logger.debug(TAG,
                         "Flex duration is currently not supported before API 24. Ignoring.");
                 builder.setPeriodic(workSpec.intervalDuration);
             }
@@ -150,7 +150,7 @@
                 }
                 break;
         }
-        Log.d(TAG, String.format(
+        Logger.debug(TAG, String.format(
                 "API version too low. Cannot convert network type value %s", networkType));
         return JobInfo.NETWORK_TYPE_ANY;
     }
diff --git a/work/workmanager/src/main/java/androidx/work/impl/background/systemjob/SystemJobScheduler.java b/work/workmanager/src/main/java/androidx/work/impl/background/systemjob/SystemJobScheduler.java
index 933e0d8..fddf8af 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/background/systemjob/SystemJobScheduler.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/background/systemjob/SystemJobScheduler.java
@@ -24,8 +24,8 @@
 import android.support.annotation.NonNull;
 import android.support.annotation.RestrictTo;
 import android.support.annotation.VisibleForTesting;
-import android.util.Log;
 
+import androidx.work.Logger;
 import androidx.work.impl.Scheduler;
 import androidx.work.impl.WorkDatabase;
 import androidx.work.impl.WorkManagerImpl;
@@ -122,7 +122,7 @@
     @VisibleForTesting
     public void scheduleInternal(WorkSpec workSpec, int jobId) {
         JobInfo jobInfo = mSystemJobInfoConverter.convert(workSpec, jobId);
-        Log.d(TAG, String.format("Scheduling work ID %s Job ID %s", workSpec.id, jobId));
+        Logger.debug(TAG, String.format("Scheduling work ID %s Job ID %s", workSpec.id, jobId));
         mJobScheduler.schedule(jobInfo);
     }
 
diff --git a/work/workmanager/src/main/java/androidx/work/impl/background/systemjob/SystemJobService.java b/work/workmanager/src/main/java/androidx/work/impl/background/systemjob/SystemJobService.java
index aaea3a1..15ea84c 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/background/systemjob/SystemJobService.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/background/systemjob/SystemJobService.java
@@ -25,8 +25,8 @@
 import android.support.annotation.NonNull;
 import android.support.annotation.RestrictTo;
 import android.text.TextUtils;
-import android.util.Log;
 
+import androidx.work.Logger;
 import androidx.work.impl.ExecutionListener;
 import androidx.work.impl.Extras;
 import androidx.work.impl.WorkManagerImpl;
@@ -67,7 +67,7 @@
                 throw new IllegalStateException("WorkManager needs to be initialized via a "
                         + "ContentProvider#onCreate() or an Application#onCreate().");
             }
-            Log.w(TAG, "Could not find WorkManager instance; this may be because an "
+            Logger.warning(TAG, "Could not find WorkManager instance; this may be because an "
                     + "auto-backup is in progress. Ignoring JobScheduler commands for now. Please "
                     + "make sure that you are initializing WorkManager if you have manually "
                     + "disabled WorkManagerInitializer.");
@@ -87,7 +87,7 @@
     @Override
     public boolean onStartJob(JobParameters params) {
         if (mWorkManagerImpl == null) {
-            Log.d(TAG, "WorkManager is not initialized; requesting retry.");
+            Logger.debug(TAG, "WorkManager is not initialized; requesting retry.");
             jobFinished(params, true);
             return false;
         }
@@ -95,7 +95,7 @@
         PersistableBundle extras = params.getExtras();
         String workSpecId = extras.getString(SystemJobInfoConverter.EXTRA_WORK_SPEC_ID);
         if (TextUtils.isEmpty(workSpecId)) {
-            Log.e(TAG, "WorkSpec id not found!");
+            Logger.error(TAG, "WorkSpec id not found!");
             return false;
         }
 
@@ -103,20 +103,20 @@
             if (mJobParameters.containsKey(workSpecId)) {
                 // This condition may happen due to our workaround for an undesired behavior in API
                 // 23.  See the documentation in {@link SystemJobScheduler#schedule}.
-                Log.d(TAG, String.format(
+                Logger.debug(TAG, String.format(
                         "Job is already being executed by SystemJobService: %s", workSpecId));
                 return false;
             }
 
             boolean isPeriodic = extras.getBoolean(SystemJobInfoConverter.EXTRA_IS_PERIODIC, false);
             if (isPeriodic && params.isOverrideDeadlineExpired()) {
-                Log.d(TAG, String.format(
+                Logger.debug(TAG, String.format(
                         "Override deadline expired for id %s. Retry requested", workSpecId));
                 jobFinished(params, true);
                 return false;
             }
 
-            Log.d(TAG, String.format("onStartJob for %s", workSpecId));
+            Logger.debug(TAG, String.format("onStartJob for %s", workSpecId));
             mJobParameters.put(workSpecId, params);
         }
 
@@ -142,17 +142,17 @@
     @Override
     public boolean onStopJob(JobParameters params) {
         if (mWorkManagerImpl == null) {
-            Log.d(TAG, "WorkManager is not initialized; requesting retry.");
+            Logger.debug(TAG, "WorkManager is not initialized; requesting retry.");
             return true;
         }
 
         String workSpecId = params.getExtras().getString(SystemJobInfoConverter.EXTRA_WORK_SPEC_ID);
         if (TextUtils.isEmpty(workSpecId)) {
-            Log.e(TAG, "WorkSpec id not found!");
+            Logger.error(TAG, "WorkSpec id not found!");
             return false;
         }
 
-        Log.d(TAG, String.format("onStopJob for %s", workSpecId));
+        Logger.debug(TAG, String.format("onStopJob for %s", workSpecId));
 
         synchronized (mJobParameters) {
             mJobParameters.remove(workSpecId);
@@ -166,7 +166,7 @@
             @NonNull String workSpecId,
             boolean isSuccessful,
             boolean needsReschedule) {
-        Log.d(TAG, String.format("%s executed on JobScheduler", workSpecId));
+        Logger.debug(TAG, String.format("%s executed on JobScheduler", workSpecId));
         JobParameters parameters;
         synchronized (mJobParameters) {
             parameters = mJobParameters.get(workSpecId);
diff --git a/work/workmanager/src/main/java/androidx/work/impl/constraints/WorkConstraintsTracker.java b/work/workmanager/src/main/java/androidx/work/impl/constraints/WorkConstraintsTracker.java
index d28e86b..3d4dca7 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/constraints/WorkConstraintsTracker.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/constraints/WorkConstraintsTracker.java
@@ -19,9 +19,9 @@
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.annotation.VisibleForTesting;
-import android.util.Log;
 
 import androidx.work.Constraints;
+import androidx.work.Logger;
 import androidx.work.impl.constraints.controllers.BatteryChargingController;
 import androidx.work.impl.constraints.controllers.BatteryNotLowController;
 import androidx.work.impl.constraints.controllers.ConstraintController;
@@ -106,7 +106,7 @@
     public boolean areAllConstraintsMet(@NonNull String workSpecId) {
         for (ConstraintController constraintController : mConstraintControllers) {
             if (constraintController.isWorkSpecConstrained(workSpecId)) {
-                Log.d(TAG, String.format("Work %s constrained by %s", workSpecId,
+                Logger.debug(TAG, String.format("Work %s constrained by %s", workSpecId,
                         constraintController.getClass().getSimpleName()));
                 return false;
             }
@@ -119,7 +119,7 @@
         List<String> unconstrainedWorkSpecIds = new ArrayList<>();
         for (String workSpecId : workSpecIds) {
             if (areAllConstraintsMet(workSpecId)) {
-                Log.d(TAG, String.format("Constraints met for %s", workSpecId));
+                Logger.debug(TAG, String.format("Constraints met for %s", workSpecId));
                 unconstrainedWorkSpecIds.add(workSpecId);
             }
         }
diff --git a/work/workmanager/src/main/java/androidx/work/impl/constraints/controllers/NetworkMeteredController.java b/work/workmanager/src/main/java/androidx/work/impl/constraints/controllers/NetworkMeteredController.java
index 7c2cf26..fd954e8 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/constraints/controllers/NetworkMeteredController.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/constraints/controllers/NetworkMeteredController.java
@@ -21,8 +21,8 @@
 import android.content.Context;
 import android.os.Build;
 import android.support.annotation.NonNull;
-import android.util.Log;
 
+import androidx.work.Logger;
 import androidx.work.impl.constraints.NetworkState;
 import androidx.work.impl.constraints.trackers.Trackers;
 import androidx.work.impl.model.WorkSpec;
@@ -50,7 +50,7 @@
     @Override
     boolean isConstrained(@NonNull NetworkState state) {
         if (Build.VERSION.SDK_INT < 26) {
-            Log.d(TAG, "Metered network constraint is not supported before API 26, "
+            Logger.debug(TAG, "Metered network constraint is not supported before API 26, "
                     + "only checking for connected state.");
             return !state.isConnected();
         }
diff --git a/work/workmanager/src/main/java/androidx/work/impl/constraints/controllers/NetworkNotRoamingController.java b/work/workmanager/src/main/java/androidx/work/impl/constraints/controllers/NetworkNotRoamingController.java
index 93f530e..e392941 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/constraints/controllers/NetworkNotRoamingController.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/constraints/controllers/NetworkNotRoamingController.java
@@ -21,8 +21,8 @@
 import android.content.Context;
 import android.os.Build;
 import android.support.annotation.NonNull;
-import android.util.Log;
 
+import androidx.work.Logger;
 import androidx.work.impl.constraints.NetworkState;
 import androidx.work.impl.constraints.trackers.Trackers;
 import androidx.work.impl.model.WorkSpec;
@@ -50,7 +50,7 @@
     @Override
     boolean isConstrained(@NonNull NetworkState state) {
         if (Build.VERSION.SDK_INT < 24) {
-            Log.d(TAG, "Not-roaming network constraint is not supported before API 24, "
+            Logger.debug(TAG, "Not-roaming network constraint is not supported before API 24, "
                     + "only checking for connected state.");
             return !state.isConnected();
         }
diff --git a/work/workmanager/src/main/java/androidx/work/impl/constraints/trackers/BatteryChargingTracker.java b/work/workmanager/src/main/java/androidx/work/impl/constraints/trackers/BatteryChargingTracker.java
index be32e60..e9dcb34 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/constraints/trackers/BatteryChargingTracker.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/constraints/trackers/BatteryChargingTracker.java
@@ -22,7 +22,8 @@
 import android.os.Build;
 import android.support.annotation.NonNull;
 import android.support.annotation.RestrictTo;
-import android.util.Log;
+
+import androidx.work.Logger;
 
 /**
  * Tracks whether or not the device's battery is charging.
@@ -48,7 +49,7 @@
         IntentFilter intentFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
         Intent intent = mAppContext.registerReceiver(null, intentFilter);
         if (intent == null) {
-            Log.e(TAG, "getInitialState - null intent received");
+            Logger.error(TAG, "getInitialState - null intent received");
             return null;
         }
         return isBatteryChangedIntentCharging(intent);
@@ -74,7 +75,7 @@
             return;
         }
 
-        Log.d(TAG, String.format("Received %s", action));
+        Logger.debug(TAG, String.format("Received %s", action));
         switch (action) {
             case BatteryManager.ACTION_CHARGING:
                 setState(true);
diff --git a/work/workmanager/src/main/java/androidx/work/impl/constraints/trackers/BatteryNotLowTracker.java b/work/workmanager/src/main/java/androidx/work/impl/constraints/trackers/BatteryNotLowTracker.java
index 7d504da..d86c281 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/constraints/trackers/BatteryNotLowTracker.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/constraints/trackers/BatteryNotLowTracker.java
@@ -21,7 +21,8 @@
 import android.os.BatteryManager;
 import android.support.annotation.NonNull;
 import android.support.annotation.RestrictTo;
-import android.util.Log;
+
+import androidx.work.Logger;
 
 /**
  * Tracks whether or not the device's battery level is low.
@@ -61,7 +62,7 @@
         IntentFilter intentFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
         Intent intent = mAppContext.registerReceiver(null, intentFilter);
         if (intent == null) {
-            Log.e(TAG, "getInitialState - null intent received");
+            Logger.error(TAG, "getInitialState - null intent received");
             return null;
         }
         int plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, BATTERY_PLUGGED_NONE);
@@ -89,7 +90,7 @@
             return;
         }
 
-        Log.d(TAG, String.format("Received %s", intent.getAction()));
+        Logger.debug(TAG, String.format("Received %s", intent.getAction()));
 
         switch (intent.getAction()) {
             case Intent.ACTION_BATTERY_OKAY:
diff --git a/work/workmanager/src/main/java/androidx/work/impl/constraints/trackers/BroadcastReceiverConstraintTracker.java b/work/workmanager/src/main/java/androidx/work/impl/constraints/trackers/BroadcastReceiverConstraintTracker.java
index cc3b649..c81e954 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/constraints/trackers/BroadcastReceiverConstraintTracker.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/constraints/trackers/BroadcastReceiverConstraintTracker.java
@@ -22,7 +22,8 @@
 import android.content.IntentFilter;
 import android.support.annotation.NonNull;
 import android.support.annotation.RestrictTo;
-import android.util.Log;
+
+import androidx.work.Logger;
 
 /**
  * A {@link ConstraintTracker} with a {@link BroadcastReceiver} for monitoring constraint changes.
@@ -63,13 +64,13 @@
 
     @Override
     public void startTracking() {
-        Log.d(TAG, String.format("%s: registering receiver", getClass().getSimpleName()));
+        Logger.debug(TAG, String.format("%s: registering receiver", getClass().getSimpleName()));
         mAppContext.registerReceiver(mBroadcastReceiver, getIntentFilter());
     }
 
     @Override
     public void stopTracking() {
-        Log.d(TAG, String.format("%s: unregistering receiver", getClass().getSimpleName()));
+        Logger.debug(TAG, String.format("%s: unregistering receiver", getClass().getSimpleName()));
         mAppContext.unregisterReceiver(mBroadcastReceiver);
     }
 }
diff --git a/work/workmanager/src/main/java/androidx/work/impl/constraints/trackers/ConstraintTracker.java b/work/workmanager/src/main/java/androidx/work/impl/constraints/trackers/ConstraintTracker.java
index cef1ae4..29523ce 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/constraints/trackers/ConstraintTracker.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/constraints/trackers/ConstraintTracker.java
@@ -17,8 +17,8 @@
 
 import android.content.Context;
 import android.support.annotation.RestrictTo;
-import android.util.Log;
 
+import androidx.work.Logger;
 import androidx.work.impl.constraints.ConstraintListener;
 
 import java.util.LinkedHashSet;
@@ -54,7 +54,8 @@
         if (mListeners.add(listener)) {
             if (mListeners.size() == 1) {
                 mCurrentState = getInitialState();
-                Log.d(TAG, String.format("%s: initial state = %s", getClass().getSimpleName(),
+                Logger.debug(TAG, String.format("%s: initial state = %s",
+                        getClass().getSimpleName(),
                         mCurrentState));
                 startTracking();
             }
diff --git a/work/workmanager/src/main/java/androidx/work/impl/constraints/trackers/NetworkStateTracker.java b/work/workmanager/src/main/java/androidx/work/impl/constraints/trackers/NetworkStateTracker.java
index dcacc02..399ce24 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/constraints/trackers/NetworkStateTracker.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/constraints/trackers/NetworkStateTracker.java
@@ -28,8 +28,8 @@
 import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.support.v4.net.ConnectivityManagerCompat;
-import android.util.Log;
 
+import androidx.work.Logger;
 import androidx.work.impl.constraints.NetworkState;
 
 /**
@@ -78,10 +78,10 @@
     @Override
     public void startTracking() {
         if (isNetworkCallbackSupported()) {
-            Log.d(TAG, "Registering network callback");
+            Logger.debug(TAG, "Registering network callback");
             mConnectivityManager.registerDefaultNetworkCallback(mNetworkCallback);
         } else {
-            Log.d(TAG, "Registering broadcast receiver");
+            Logger.debug(TAG, "Registering broadcast receiver");
             mAppContext.registerReceiver(mBroadcastReceiver,
                     new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
         }
@@ -90,10 +90,10 @@
     @Override
     public void stopTracking() {
         if (isNetworkCallbackSupported()) {
-            Log.d(TAG, "Unregistering network callback");
+            Logger.debug(TAG, "Unregistering network callback");
             mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
         } else {
-            Log.d(TAG, "Unregistering broadcast receiver");
+            Logger.debug(TAG, "Unregistering broadcast receiver");
             mAppContext.unregisterReceiver(mBroadcastReceiver);
         }
     }
@@ -132,13 +132,13 @@
         @Override
         public void onCapabilitiesChanged(Network network, NetworkCapabilities capabilities) {
             // The Network parameter is unreliable when a VPN app is running - use active network.
-            Log.d(TAG, String.format("Network capabilities changed: %s", capabilities));
+            Logger.debug(TAG, String.format("Network capabilities changed: %s", capabilities));
             setState(getActiveNetworkState());
         }
 
         @Override
         public void onLost(Network network) {
-            Log.d(TAG, "Network connection lost");
+            Logger.debug(TAG, "Network connection lost");
             setState(getActiveNetworkState());
         }
     }
@@ -153,7 +153,7 @@
                 return;
             }
             if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
-                Log.d(TAG, "Network broadcast received");
+                Logger.debug(TAG, "Network broadcast received");
                 setState(getActiveNetworkState());
             }
         }
diff --git a/work/workmanager/src/main/java/androidx/work/impl/constraints/trackers/StorageNotLowTracker.java b/work/workmanager/src/main/java/androidx/work/impl/constraints/trackers/StorageNotLowTracker.java
index ff0622c..b4fc7c5 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/constraints/trackers/StorageNotLowTracker.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/constraints/trackers/StorageNotLowTracker.java
@@ -20,7 +20,8 @@
 import android.content.IntentFilter;
 import android.support.annotation.NonNull;
 import android.support.annotation.RestrictTo;
-import android.util.Log;
+
+import androidx.work.Logger;
 
 /**
  * Tracks whether or not the device's storage is low.
@@ -80,7 +81,7 @@
             return; // Should never happen since the IntentFilter was configured.
         }
 
-        Log.d(TAG, String.format("Received %s", intent.getAction()));
+        Logger.debug(TAG, String.format("Received %s", intent.getAction()));
 
         switch (intent.getAction()) {
             case Intent.ACTION_DEVICE_STORAGE_OK:
diff --git a/work/workmanager/src/main/java/androidx/work/impl/model/WorkSpec.java b/work/workmanager/src/main/java/androidx/work/impl/model/WorkSpec.java
index 6f5120d..510c7b0 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/model/WorkSpec.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/model/WorkSpec.java
@@ -31,11 +31,11 @@
 import android.arch.persistence.room.Relation;
 import android.support.annotation.NonNull;
 import android.support.annotation.RestrictTo;
-import android.util.Log;
 
 import androidx.work.BackoffPolicy;
 import androidx.work.Constraints;
 import androidx.work.Data;
+import androidx.work.Logger;
 import androidx.work.State;
 import androidx.work.WorkRequest;
 import androidx.work.WorkStatus;
@@ -127,11 +127,11 @@
      */
     public void setBackoffDelayDuration(long backoffDelayDuration) {
         if (backoffDelayDuration > MAX_BACKOFF_MILLIS) {
-            Log.w(TAG, "Backoff delay duration exceeds maximum value");
+            Logger.warning(TAG, "Backoff delay duration exceeds maximum value");
             backoffDelayDuration = MAX_BACKOFF_MILLIS;
         }
         if (backoffDelayDuration < MIN_BACKOFF_MILLIS) {
-            Log.w(TAG, "Backoff delay duration less than minimum value");
+            Logger.warning(TAG, "Backoff delay duration less than minimum value");
             backoffDelayDuration = MIN_BACKOFF_MILLIS;
         }
         this.backoffDelayDuration = backoffDelayDuration;
@@ -153,7 +153,7 @@
      */
     public void setPeriodic(long intervalDuration) {
         if (intervalDuration < MIN_PERIODIC_INTERVAL_MILLIS) {
-            Log.w(TAG, String.format(
+            Logger.warning(TAG, String.format(
                     "Interval duration lesser than minimum allowed value; Changed to %s",
                     MIN_PERIODIC_INTERVAL_MILLIS));
             intervalDuration = MIN_PERIODIC_INTERVAL_MILLIS;
@@ -169,19 +169,20 @@
      */
     public void setPeriodic(long intervalDuration, long flexDuration) {
         if (intervalDuration < MIN_PERIODIC_INTERVAL_MILLIS) {
-            Log.w(TAG, String.format(
+            Logger.warning(TAG, String.format(
                     "Interval duration lesser than minimum allowed value; Changed to %s",
                     MIN_PERIODIC_INTERVAL_MILLIS));
             intervalDuration = MIN_PERIODIC_INTERVAL_MILLIS;
         }
         if (flexDuration < MIN_PERIODIC_FLEX_MILLIS) {
-            Log.w(TAG,
+            Logger.warning(TAG,
                     String.format("Flex duration lesser than minimum allowed value; Changed to %s",
                             MIN_PERIODIC_FLEX_MILLIS));
             flexDuration = MIN_PERIODIC_FLEX_MILLIS;
         }
         if (flexDuration > intervalDuration) {
-            Log.w(TAG, String.format("Flex duration greater than interval duration; Changed to %s",
+            Logger.warning(TAG,
+                    String.format("Flex duration greater than interval duration; Changed to %s",
                     intervalDuration));
             flexDuration = intervalDuration;
         }
diff --git a/work/workmanager/src/main/java/androidx/work/impl/utils/EnqueueRunnable.java b/work/workmanager/src/main/java/androidx/work/impl/utils/EnqueueRunnable.java
index 00d7b8f..eb6fa56 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/utils/EnqueueRunnable.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/utils/EnqueueRunnable.java
@@ -31,11 +31,11 @@
 import android.support.annotation.RestrictTo;
 import android.support.annotation.VisibleForTesting;
 import android.text.TextUtils;
-import android.util.Log;
 
 import androidx.work.Constraints;
 import androidx.work.Data;
 import androidx.work.ExistingWorkPolicy;
+import androidx.work.Logger;
 import androidx.work.State;
 import androidx.work.WorkRequest;
 import androidx.work.impl.Schedulers;
@@ -122,7 +122,7 @@
                 if (!parent.isEnqueued()) {
                     needsScheduling |= processContinuation(parent);
                 } else {
-                    Log.w(TAG, String.format("Already enqueued work ids (%s).",
+                    Logger.warning(TAG, String.format("Already enqueued work ids (%s).",
                             TextUtils.join(", ", parent.getIds())));
                 }
             }
@@ -172,7 +172,8 @@
             for (String id : prerequisiteIds) {
                 WorkSpec prerequisiteWorkSpec = workDatabase.workSpecDao().getWorkSpec(id);
                 if (prerequisiteWorkSpec == null) {
-                    Log.e(TAG, String.format("Prerequisite %s doesn't exist; not enqueuing", id));
+                    Logger.error(TAG,
+                            String.format("Prerequisite %s doesn't exist; not enqueuing", id));
                     return false;
                 }
 
diff --git a/work/workmanager/src/main/java/androidx/work/impl/utils/ForceStopRunnable.java b/work/workmanager/src/main/java/androidx/work/impl/utils/ForceStopRunnable.java
index c01e4fd..8c8e8e8 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/utils/ForceStopRunnable.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/utils/ForceStopRunnable.java
@@ -29,8 +29,8 @@
 import android.support.annotation.NonNull;
 import android.support.annotation.RestrictTo;
 import android.support.annotation.VisibleForTesting;
-import android.util.Log;
 
+import androidx.work.Logger;
 import androidx.work.impl.WorkManagerImpl;
 
 import java.util.concurrent.TimeUnit;
@@ -65,12 +65,12 @@
     @Override
     public void run() {
         if (shouldRescheduleWorkers()) {
-            Log.d(TAG, "Rescheduling Workers.");
+            Logger.debug(TAG, "Rescheduling Workers.");
             mWorkManager.rescheduleEligibleWork();
             // Mark the jobs as migrated.
             mWorkManager.getPreferences().setNeedsReschedule(false);
         } else if (isForceStopped()) {
-            Log.d(TAG, "Application was force-stopped, rescheduling.");
+            Logger.debug(TAG, "Application was force-stopped, rescheduling.");
             mWorkManager.rescheduleEligibleWork();
         }
         mWorkManager.onForceStopRunnableCompleted();
@@ -153,7 +153,7 @@
             if (intent != null) {
                 String action = intent.getAction();
                 if (ACTION_FORCE_STOP_RESCHEDULE.equals(action)) {
-                    Log.v(TAG, "Rescheduling alarm that keeps track of force-stops.");
+                    Logger.verbose(TAG, "Rescheduling alarm that keeps track of force-stops.");
                     WorkManagerImpl workManager = WorkManagerImpl.getInstance();
                     ForceStopRunnable runnable = new ForceStopRunnable(context, workManager);
                     runnable.setAlarm(ForceStopRunnable.ALARM_ID);
diff --git a/work/workmanager/src/main/java/androidx/work/impl/utils/PackageManagerHelper.java b/work/workmanager/src/main/java/androidx/work/impl/utils/PackageManagerHelper.java
index c912048..c6fd45e 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/utils/PackageManagerHelper.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/utils/PackageManagerHelper.java
@@ -19,7 +19,8 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.support.annotation.NonNull;
-import android.util.Log;
+
+import androidx.work.Logger;
 
 /**
  * Helper class for common {@link PackageManager} functions
@@ -51,9 +52,10 @@
                             : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
                     PackageManager.DONT_KILL_APP);
 
-            Log.d(TAG, String.format("%s %s", klazz.getName(), (enabled ? "enabled" : "disabled")));
+            Logger.debug(TAG,
+                    String.format("%s %s", klazz.getName(), (enabled ? "enabled" : "disabled")));
         } catch (Exception exception) {
-            Log.d(TAG, String.format("%s could not be %s", klazz.getName(),
+            Logger.debug(TAG, String.format("%s could not be %s", klazz.getName(),
                     (enabled ? "enabled" : "disabled")), exception);
         }
     }
diff --git a/work/workmanager/src/main/java/androidx/work/impl/utils/StartWorkRunnable.java b/work/workmanager/src/main/java/androidx/work/impl/utils/StartWorkRunnable.java
index 1f51067..246b2be 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/utils/StartWorkRunnable.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/utils/StartWorkRunnable.java
@@ -29,8 +29,6 @@
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 public class StartWorkRunnable implements Runnable {
 
-    private static final String TAG = "StartWorkRunnable";
-
     private WorkManagerImpl mWorkManagerImpl;
     private String mWorkSpecId;
     private Extras.RuntimeExtras mRuntimeExtras;
diff --git a/work/workmanager/src/main/java/androidx/work/impl/utils/StopWorkRunnable.java b/work/workmanager/src/main/java/androidx/work/impl/utils/StopWorkRunnable.java
index a1ed222..a86ecad 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/utils/StopWorkRunnable.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/utils/StopWorkRunnable.java
@@ -17,8 +17,8 @@
 package androidx.work.impl.utils;
 
 import android.support.annotation.RestrictTo;
-import android.util.Log;
 
+import androidx.work.Logger;
 import androidx.work.State;
 import androidx.work.impl.WorkDatabase;
 import androidx.work.impl.WorkManagerImpl;
@@ -52,7 +52,7 @@
                 workSpecDao.setState(State.ENQUEUED, mWorkSpecId);
             }
             boolean isStopped = mWorkManagerImpl.getProcessor().stopWork(mWorkSpecId);
-            Log.d(
+            Logger.debug(
                     TAG,
                     String.format(
                             "StopWorkRunnable for %s; Processor.stopWork = %s",
diff --git a/work/workmanager/src/main/java/androidx/work/impl/workers/ConstraintTrackingWorker.java b/work/workmanager/src/main/java/androidx/work/impl/workers/ConstraintTrackingWorker.java
index e023fed..f3373f98 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/workers/ConstraintTrackingWorker.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/workers/ConstraintTrackingWorker.java
@@ -21,8 +21,8 @@
 import android.support.annotation.RestrictTo;
 import android.support.annotation.VisibleForTesting;
 import android.text.TextUtils;
-import android.util.Log;
 
+import androidx.work.Logger;
 import androidx.work.Worker;
 import androidx.work.impl.WorkDatabase;
 import androidx.work.impl.WorkManagerImpl;
@@ -66,7 +66,7 @@
     public @NonNull Result doWork() {
         String className = getInputData().getString(ARGUMENT_CLASS_NAME);
         if (TextUtils.isEmpty(className)) {
-            Log.d(TAG, "No worker to delegate to.");
+            Logger.debug(TAG, "No worker to delegate to.");
             return Result.FAILURE;
         }
         // Instantiate the delegated worker. Use the same workSpecId, and the same Data
@@ -78,7 +78,7 @@
                 getExtras());
 
         if (mDelegate == null) {
-            Log.d(TAG, "No worker to delegate to.");
+            Logger.debug(TAG, "No worker to delegate to.");
             return Result.FAILURE;
         }
 
@@ -96,7 +96,7 @@
         workConstraintsTracker.replace(Collections.singletonList(workSpec));
 
         if (workConstraintsTracker.areAllConstraintsMet(getId().toString())) {
-            Log.d(TAG, String.format("Constraints met for delegate %s", className));
+            Logger.debug(TAG, String.format("Constraints met for delegate %s", className));
 
             // Wrapping the call to mDelegate#doWork() in a try catch, because
             // changes in constraints can cause the worker to throw RuntimeExceptions, and
@@ -112,11 +112,11 @@
                     }
                 }
             } catch (Throwable exception) {
-                Log.d(TAG, String.format(
+                Logger.debug(TAG, String.format(
                         "Delegated worker %s threw a runtime exception.", className), exception);
                 synchronized (mLock) {
                     if (mAreConstraintsUnmet) {
-                        Log.d(TAG, "Constraints were unmet, Retrying.");
+                        Logger.debug(TAG, "Constraints were unmet, Retrying.");
                         return Result.RETRY;
                     } else {
                         return Result.FAILURE;
@@ -124,7 +124,7 @@
                 }
             }
         } else {
-            Log.d(TAG, String.format(
+            Logger.debug(TAG, String.format(
                     "Constraints not met for delegate %s. Requesting retry.", className));
             return Result.RETRY;
         }
@@ -147,7 +147,7 @@
     @Override
     public void onAllConstraintsNotMet(@NonNull List<String> workSpecIds) {
         // If at any point, constraints are not met mark it so we can retry the work.
-        Log.d(TAG, String.format("Constraints changed for %s", workSpecIds));
+        Logger.debug(TAG, String.format("Constraints changed for %s", workSpecIds));
         synchronized (mLock) {
             mAreConstraintsUnmet = true;
         }