-
-
Save pyricau/06c2c486d24f5f85f7f0 to your computer and use it in GitHub Desktop.
import android.util.Log; | |
import com.squareup.leakcanary.AnalysisResult; | |
import com.squareup.leakcanary.DisplayLeakService; | |
import com.squareup.leakcanary.HeapDump; | |
import retrofit.RestAdapter; | |
import retrofit.RetrofitError; | |
import retrofit.http.Multipart; | |
import retrofit.http.POST; | |
import retrofit.http.Part; | |
import retrofit.mime.TypedFile; | |
public final class LeakSlackUploadService extends DisplayLeakService { | |
/** See https://api.slack.com/ for documentation. */ | |
public interface SlackApi { | |
String TOKEN = "xoxp-SOME-USER-TOKEN"; | |
String MEMORY_LEAK_CHANNEL = "SOME-CHANNEL-TOKEN"; | |
@Multipart @POST("/api/files.upload") UploadFileResponse uploadFile(@Part("token") String token, | |
@Part("file") TypedFile file, @Part("filetype") String filetype, | |
@Part("filename") String filename, @Part("title") String title, | |
@Part("initial_comment") String initialComment, @Part("channels") String channels); | |
} | |
public static class UploadFileResponse { | |
boolean ok; | |
String error; | |
@Override public String toString() { | |
return "UploadFileResponse{" + | |
"ok=" + ok + | |
", error='" + error + '\'' + | |
'}'; | |
} | |
} | |
private static final String TAG = "LeakListenerService"; | |
private static String classSimpleName(String className) { | |
int separator = className.lastIndexOf('.'); | |
return separator == -1 ? className : className.substring(separator + 1); | |
} | |
private SlackApi slackApi; | |
@Override public void onCreate() { | |
super.onCreate(); | |
slackApi = new RestAdapter.Builder() // | |
.setEndpoint("https://slack.com") // | |
.build() // | |
.create(SlackApi.class); | |
} | |
@Override | |
protected void afterDefaultHandling(HeapDump heapDump, AnalysisResult result, String leakInfo) { | |
if (!result.leakFound || result.excludedLeak) { | |
return; | |
} | |
String name = classSimpleName(result.className); | |
if (!heapDump.referenceName.equals("")) { | |
name += "(" + heapDump.referenceName + ")"; | |
} | |
String title = name + " has leaked"; | |
String initialComment = leakInfo; | |
try { | |
slackApi.uploadFile(SlackApi.TOKEN, | |
new TypedFile("application/octet-stream", heapDump.heapDumpFile), null, | |
heapDump.heapDumpFile.getName(), title, initialComment, SlackApi.MEMORY_LEAK_CHANNEL); | |
} catch (RetrofitError e) { | |
Log.e(TAG, "Error when uploading heap dump", e); | |
} | |
} | |
} |
public class SquareDebugApplication extends SquareApplication { | |
@Override protected RefWatcher installLeakCanary() { | |
return LeakCanary.install(app, LeakSlackUploadService.class); | |
} | |
} |
Create a non-fatal issue ticket in Crashlytics for leaks:
public class CrashlyticsLeakService extends DisplayLeakService {
private static String classSimpleName(String className) {
int separator = className.lastIndexOf('.');
return separator == -1 ? className : className.substring(separator + 1);
}
protected void afterDefaultHandling(HeapDump heapDump, AnalysisResult result, String leakInfo) {
if (!result.leakFound || result.excludedLeak) {
return;
}
Crashlytics.log("*** Memory Leak ***");
for(String s : leakInfo.split("\n")) {
Crashlytics.log(s);
}
Crashlytics.log("*** End Of Leak ***");
String name = classSimpleName(result.className);
if (!heapDump.referenceName.equals("")) {
name += "(" + heapDump.referenceName + ")";
}
Crashlytics.logException(new Exception(name + " has leaked"));
}
}
Hi, I followed the code trying to upload leak traces to HipChat room, but get the retrofit.RetrofitError: 400 Bad Request, do you know how to fix this issue?
Have resolved this, because of too long Http request body.
@mullender, why are you setting up it to android:enabled="false" ?
Has anyone ever upload to flowdock or bug system (i.e. Asana, Jira)?
Hey :)
After some digging here the snippet for uploading leaks using retrofit 2:
public final class LeakSlackUploadService extends DisplayLeakService
implements Callback<LeakSlackUploadService.UploadFileResponse> {
private SlackApi slackApi;
@Override
public void onCreate() {
super.onCreate();
slackApi = new Retrofit.Builder()
.baseUrl("https://slack.com")
.addConverterFactory(GsonConverterFactory.create())
.build() //
.create(SlackApi.class);
}
@Override
protected void afterDefaultHandling(HeapDump heapDump, AnalysisResult result, String leakInfo) {
Log.e(TAG, "afterDefaultHandling " + leakInfo);
if (!result.leakFound || result.excludedLeak) {
Log.e(TAG, "!result.leakFound || result.excludedLeak");
return;
}
String name = classSimpleName(result.className);
if (!heapDump.referenceName.equals("")) {
name += "(" + heapDump.referenceName + ")";
}
String title = name + " has leaked5";
String initialComment = leakInfo.substring(0, leakInfo.indexOf("Details:", 0));
RequestBody file = RequestBody
.create(MediaType.parse("multipart/form-data"), heapDump.heapDumpFile);
MultipartBody.Part body =
MultipartBody.Part.createFormData("file", heapDump.heapDumpFile.getName(), file);
final Call<UploadFileResponse> call = slackApi.uploadFile(SlackApi.TOKEN,
body,
null,
heapDump.heapDumpFile.getName(), title, initialComment,
SlackApi.MEMORY_LEAK_CHANNEL);
call.enqueue(this);
}
@Override
public void onResponse(Call<UploadFileResponse> call,
Response<UploadFileResponse> response) {
Log.d(TAG, response.body().toString());
}
@Override
public void onFailure(Call<UploadFileResponse> call, Throwable t) {
Log.d(TAG, t.getLocalizedMessage());
}
/** See https://api.slack.com/ for documentation. */
public interface SlackApi {
String TOKEN = "YOUR_TOKEN_SLACK";
String MEMORY_LEAK_CHANNEL = "YOUR_CHANNEL_FOR_MEMORY_LEAKS";
@Multipart
@POST("/api/files.upload")
Call<UploadFileResponse> uploadFile(
@Query("token") String token,
@Part MultipartBody.Part file, @Query("filetype") String filetype,
@Query("filename") String filename, @Query("title") String title,
@Query("initial_comment") String initialComment,
@Query("channels") String channels);
}
public static class UploadFileResponse {
boolean ok;
String error;
@Override
public String toString() {
return "UploadFileResponse{" +
"ok=" + ok +
", error='" + error + '\'' +
'}';
}
}
private static final String TAG = "LeakListenerService";
private static String classSimpleName(String className) {
int separator = className.lastIndexOf('.');
return separator == -1 ? className : className.substring(separator + 1);
}
}
(Solution below) Since LeakCanary 1.5, the install method mentioned above is no longer available.
Deprecated source (below)
public static RefWatcher androidWatcher(Context context, Listener heapDumpListener, ExcludedRefs excludedRefs) {
AndroidDebuggerControl debuggerControl = new AndroidDebuggerControl();
AndroidHeapDumper heapDumper = new AndroidHeapDumper(context);
heapDumper.cleanup();
return new RefWatcher(new AndroidWatchExecutor(), debuggerControl, GcTrigger.DEFAULT, heapDumper, heapDumpListener, excludedRefs);
}
Any ideas how to refactor the solution posted above to fit v1.5?
------------ Below is the solution -----------------
AndroidRefWatcherBuilder refWatcher = LeakCanary.refWatcher(this).listenerServiceClass(LeakSlackUploadService.class);
refWatcher.buildAndInstall();
When I do LeakCanary.refWatcher(this).listenerServiceClass(LeakSlackUploadService.class);
for builds with leakcanary-android-no-op, it throws no Static method exception.
LeakCanary.refWatcher(application)
.listenerServiceClass(LeakUploadService.class)
.buildAndInstall();
I wonder if anyone created code recipe to upload leak to slack for version 2.7 and Kotlin :) @pyricau
no. Slack bot means no aggregation so it's not as helpful as say Bugsnag.
Thanks guys!