From 7c08e5c75c7c6e397702e3af8c1838a1fb2d27ef Mon Sep 17 00:00:00 2001 From: Pierre-Yves Ricau Date: Fri, 28 Aug 2015 17:57:06 -0400 Subject: [PATCH] Add logger API Fixes #201 --- CHANGELOG.md | 1 + .../leakcanary/AndroidHeapDumper.java | 15 ++--- .../com/squareup/leakcanary/CanaryLog.java | 59 +++++++++++++++++++ .../leakcanary/DisplayLeakService.java | 21 ++----- .../internal/DisplayLeakActivity.java | 6 +- .../internal/HeapAnalyzerService.java | 5 +- .../internal/LeakCanaryInternals.java | 9 ++- 7 files changed, 80 insertions(+), 36 deletions(-) create mode 100644 leakcanary-android/src/main/java/com/squareup/leakcanary/CanaryLog.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 463778efcb..3cf18f0b60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ * 2 new ignored Android SDK leaks: [#26](https://github.com/square/leakcanary/issues/26) [#62](https://github.com/square/leakcanary/issues/62). 1 Android SDK leak updated: [#133](https://github.com/square/leakcanary/issues/133). * Added excluded leaks to text report [#119](https://github.com/square/leakcanary/issues/119). * Added LeakCanary SHA to text report [#120](https://github.com/square/leakcanary/issues/120). +* Added CanaryLog API to replace the logger: [#201](https://github.com/square/leakcanary/issues/201). * Renamed all resources to begin with `leak_canary_` instead of `__leak_canary`[#161](https://github.com/square/leakcanary/pull/161) * No crash when heap dump fails [#226](https://github.com/square/leakcanary/issues/226). diff --git a/leakcanary-android/src/main/java/com/squareup/leakcanary/AndroidHeapDumper.java b/leakcanary-android/src/main/java/com/squareup/leakcanary/AndroidHeapDumper.java index 943355616a..7f32c56648 100644 --- a/leakcanary-android/src/main/java/com/squareup/leakcanary/AndroidHeapDumper.java +++ b/leakcanary-android/src/main/java/com/squareup/leakcanary/AndroidHeapDumper.java @@ -20,7 +20,6 @@ import android.os.Handler; import android.os.Looper; import android.os.MessageQueue; -import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; import android.widget.Toast; @@ -34,8 +33,6 @@ public final class AndroidHeapDumper implements HeapDumper { - private static final String TAG = "AndroidHeapDumper"; - private final Context context; private final Handler mainHandler; @@ -46,11 +43,11 @@ public AndroidHeapDumper(Context context) { @Override public File dumpHeap() { if (!isExternalStorageWritable()) { - Log.d(TAG, "Could not dump heap, external storage not mounted."); + CanaryLog.d("Could not dump heap, external storage not mounted."); } File heapDumpFile = getHeapDumpFile(); if (heapDumpFile.exists()) { - Log.d(TAG, "Could not dump heap, previous analysis still is in progress."); + CanaryLog.d("Could not dump heap, previous analysis still is in progress."); // Heap analysis in progress, let's not put too much pressure on the device. return NO_DUMP; } @@ -59,7 +56,7 @@ public AndroidHeapDumper(Context context) { showToast(waitingForToast); if (!waitingForToast.wait(5, SECONDS)) { - Log.d(TAG, "Did not dump heap, too much time waiting for Toast."); + CanaryLog.d("Did not dump heap, too much time waiting for Toast."); return NO_DUMP; } @@ -70,7 +67,7 @@ public AndroidHeapDumper(Context context) { return heapDumpFile; } catch (Exception e) { cleanup(); - Log.e(TAG, "Could not perform heap dump", e); + CanaryLog.d(e, "Could not perform heap dump"); // Abort heap dump return NO_DUMP; } @@ -84,11 +81,11 @@ public void cleanup() { LeakCanaryInternals.executeOnFileIoThread(new Runnable() { @Override public void run() { if (isExternalStorageWritable()) { - Log.d(TAG, "Could not attempt cleanup, external storage not mounted."); + CanaryLog.d("Could not attempt cleanup, external storage not mounted."); } File heapDumpFile = getHeapDumpFile(); if (heapDumpFile.exists()) { - Log.d(TAG, "Previous analysis did not complete correctly, cleaning: " + heapDumpFile); + CanaryLog.d("Previous analysis did not complete correctly, cleaning: %s", heapDumpFile); heapDumpFile.delete(); } } diff --git a/leakcanary-android/src/main/java/com/squareup/leakcanary/CanaryLog.java b/leakcanary-android/src/main/java/com/squareup/leakcanary/CanaryLog.java new file mode 100644 index 0000000000..500cbcd8db --- /dev/null +++ b/leakcanary-android/src/main/java/com/squareup/leakcanary/CanaryLog.java @@ -0,0 +1,59 @@ +package com.squareup.leakcanary; + +import android.util.Log; + +public final class CanaryLog { + + private static volatile Logger logger = new DefaultLogger(); + + public interface Logger { + void d(String message, Object... args); + + void d(Throwable throwable, String message, Object... args); + } + + private static class DefaultLogger implements Logger { + + @Override public void d(String message, Object... args) { + String formatted = String.format(message, args); + if (formatted.length() < 4000) { + Log.d("LeakCanary", formatted); + } else { + String[] lines = formatted.split("\n"); + for (String line : lines) { + Log.d("LeakCanary", line); + } + } + } + + @Override public void d(Throwable throwable, String message, Object... args) { + d(String.format(message, args) + '\n' + Log.getStackTraceString(throwable)); + } + } + + public static void setLogger(Logger logger) { + CanaryLog.logger = logger; + } + + public static void d(String message, Object... args) { + // Local variable to prevent the ref from becoming null after the null check. + Logger logger = CanaryLog.logger; + if (logger == null) { + return; + } + logger.d(message, args); + } + + public static void d(Throwable throwable, String message, Object... args) { + // Local variable to prevent the ref from becoming null after the null check. + Logger logger = CanaryLog.logger; + if (logger == null) { + return; + } + logger.d(throwable, message, args); + } + + private CanaryLog() { + throw new AssertionError(); + } +} diff --git a/leakcanary-android/src/main/java/com/squareup/leakcanary/DisplayLeakService.java b/leakcanary-android/src/main/java/com/squareup/leakcanary/DisplayLeakService.java index 479b09e020..f16ffb830e 100644 --- a/leakcanary-android/src/main/java/com/squareup/leakcanary/DisplayLeakService.java +++ b/leakcanary-android/src/main/java/com/squareup/leakcanary/DisplayLeakService.java @@ -20,7 +20,6 @@ import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; -import android.util.Log; import com.squareup.leakcanary.internal.DisplayLeakActivity; import java.io.File; import java.io.FileOutputStream; @@ -44,17 +43,9 @@ */ public class DisplayLeakService extends AbstractAnalysisResultService { - @Override - protected final void onHeapAnalyzed(HeapDump heapDump, AnalysisResult result) { + @Override protected final void onHeapAnalyzed(HeapDump heapDump, AnalysisResult result) { String leakInfo = leakInfo(this, heapDump, result, true); - if (leakInfo.length() < 4000) { - Log.d("LeakCanary", leakInfo); - } else { - String[] lines = leakInfo.split("\n"); - for (String line : lines) { - Log.d("LeakCanary", line); - } - } + CanaryLog.d(leakInfo); if (result.failure == null && (!result.leakFound || result.excludedLeak)) { if (result.excludedLeak) { @@ -73,8 +64,7 @@ protected final void onHeapAnalyzed(HeapDump heapDump, AnalysisResult result) { if (renamedFile == null) { // No file available. - Log.e("LeakCanary", - "Leak result dropped because we already store " + maxStoredLeaks + " leak traces."); + CanaryLog.d("Leak result dropped because we already store %d leak traces.", maxStoredLeaks); afterDefaultHandling(heapDump, result, leakInfo); return; } @@ -89,7 +79,7 @@ protected final void onHeapAnalyzed(HeapDump heapDump, AnalysisResult result) { oos.writeObject(heapDump); oos.writeObject(result); } catch (IOException e) { - Log.e("LeakCanary", "Could not save leak analysis result to disk", e); + CanaryLog.d(e, "Could not save leak analysis result to disk"); afterDefaultHandling(heapDump, result, leakInfo); return; } finally { @@ -118,8 +108,7 @@ protected final void onHeapAnalyzed(HeapDump heapDump, AnalysisResult result) { } @TargetApi(HONEYCOMB) - private void notify(String contentTitle, String contentText, - PendingIntent pendingIntent) { + private void notify(String contentTitle, String contentText, PendingIntent pendingIntent) { NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); diff --git a/leakcanary-android/src/main/java/com/squareup/leakcanary/internal/DisplayLeakActivity.java b/leakcanary-android/src/main/java/com/squareup/leakcanary/internal/DisplayLeakActivity.java index d39a8376a0..20584cc31f 100644 --- a/leakcanary-android/src/main/java/com/squareup/leakcanary/internal/DisplayLeakActivity.java +++ b/leakcanary-android/src/main/java/com/squareup/leakcanary/internal/DisplayLeakActivity.java @@ -39,6 +39,7 @@ import android.widget.ListView; import android.widget.TextView; import com.squareup.leakcanary.AnalysisResult; +import com.squareup.leakcanary.CanaryLog; import com.squareup.leakcanary.HeapDump; import com.squareup.leakcanary.R; import java.io.File; @@ -65,7 +66,6 @@ @SuppressWarnings("ConstantConditions") @TargetApi(Build.VERSION_CODES.HONEYCOMB) public final class DisplayLeakActivity extends Activity { - private static final String TAG = "DisplayLeakActivity"; private static final String SHOW_LEAK_EXTRA = "show_latest"; public static PendingIntent createPendingIntent(Context context) { @@ -413,8 +413,8 @@ static void forgetActivity() { // Let's remove the files, we can't read them anymore. heapDumpFile.delete(); resultFile.delete(); - Log.e(TAG, "Could not read result file, deleted result and heap dump:" + heapDumpFile, - e); + CanaryLog.d(e, "Could not read result file, deleted result and heap dump: %s", + heapDumpFile); } finally { if (fis != null) { try { diff --git a/leakcanary-android/src/main/java/com/squareup/leakcanary/internal/HeapAnalyzerService.java b/leakcanary-android/src/main/java/com/squareup/leakcanary/internal/HeapAnalyzerService.java index 0bcaf9e004..d03dad9470 100644 --- a/leakcanary-android/src/main/java/com/squareup/leakcanary/internal/HeapAnalyzerService.java +++ b/leakcanary-android/src/main/java/com/squareup/leakcanary/internal/HeapAnalyzerService.java @@ -18,9 +18,9 @@ import android.app.IntentService; import android.content.Context; import android.content.Intent; -import android.util.Log; import com.squareup.leakcanary.AbstractAnalysisResultService; import com.squareup.leakcanary.AnalysisResult; +import com.squareup.leakcanary.CanaryLog; import com.squareup.leakcanary.HeapAnalyzer; import com.squareup.leakcanary.HeapDump; @@ -32,7 +32,6 @@ public final class HeapAnalyzerService extends IntentService { private static final String LISTENER_CLASS_EXTRA = "listener_class_extra"; private static final String HEAPDUMP_EXTRA = "heapdump_extra"; - public static final String TAG = "HeapAnalyzerService"; public static void runAnalysis(Context context, HeapDump heapDump, Class listenerServiceClass) { @@ -48,7 +47,7 @@ public HeapAnalyzerService() { @Override protected void onHandleIntent(Intent intent) { if (intent == null) { - Log.d(TAG, "HeapAnalyzerService received a null intent, ignoring."); + CanaryLog.d("HeapAnalyzerService received a null intent, ignoring."); return; } String listenerClassName = intent.getStringExtra(LISTENER_CLASS_EXTRA); diff --git a/leakcanary-android/src/main/java/com/squareup/leakcanary/internal/LeakCanaryInternals.java b/leakcanary-android/src/main/java/com/squareup/leakcanary/internal/LeakCanaryInternals.java index ac8ecba785..a66e34ca9a 100644 --- a/leakcanary-android/src/main/java/com/squareup/leakcanary/internal/LeakCanaryInternals.java +++ b/leakcanary-android/src/main/java/com/squareup/leakcanary/internal/LeakCanaryInternals.java @@ -23,7 +23,7 @@ import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; import android.os.Environment; -import android.util.Log; +import com.squareup.leakcanary.CanaryLog; import java.io.File; import java.util.concurrent.Executor; import java.util.concurrent.Executors; @@ -113,7 +113,7 @@ public static boolean isInServiceProcess(Context context, Class