From a1d9d5de77734c439a913288886c8a162518d68a Mon Sep 17 00:00:00 2001 From: melekr Date: Tue, 18 Mar 2025 15:44:37 -0400 Subject: [PATCH 1/3] Add pure java Logger --- .../backtraceio/coroner/CoronerClient.java | 10 +++++++- .../coroner/CoronerHttpClient.java | 11 +++++++- .../coroner/common/AndroidLogDelegate.java | 6 +++++ .../backtraceio/coroner/common/Logger.java | 25 +++++++++++++++++++ .../coroner/serialization/GsonWrapper.java | 4 +++ example-app/build.gradle | 2 +- .../backtraceio/CoronerLogDelegate.java | 12 +++++++++ .../backtraceio/ExampleInstrumentedTest.java | 24 ++++++++++++++++-- 8 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 coroner-client/src/main/java/backtraceio/coroner/common/AndroidLogDelegate.java create mode 100644 coroner-client/src/main/java/backtraceio/coroner/common/Logger.java create mode 100644 example-app/src/androidTest/java/backtraceio/backtraceio/CoronerLogDelegate.java diff --git a/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java b/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java index 3f5ce9cc..6920c737 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java +++ b/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java @@ -48,7 +48,11 @@ public CoronerResponse rxIdFilter(final String rxId, final List customAt public CoronerResponse errorTypeTimestampFilter(final String errorType, final String timestampLeast, final String timestampMost, final List customAttributes) throws CoronerResponseException, IOException, CoronerHttpException { final List attributes = concatAttributes(customAttributes); + backtraceio.coroner.common.Logger.d("CoronerHttpClient errorTypeTimestampFilter started attributes", String.valueOf(attributes)); + + final JsonObject coronerQuery = this.coronerQueries.filterByErrorTypeAndTimestamp(errorType, timestampLeast, timestampMost, attributes); + backtraceio.coroner.common.Logger.d("CoronerHttpClient errorTypeTimestampFilter coronerQuery JsonObject", String.valueOf(coronerQuery)); return makeRequest(coronerQuery); } @@ -60,8 +64,12 @@ private List concatAttributes(final List customAttributes) { } private CoronerResponse makeRequest(final JsonObject coronerQuery) throws CoronerResponseException, IOException, CoronerHttpException { + backtraceio.coroner.common.Logger.d("CoronerHttpClient makeRequest started", "started"); + + final CoronerApiResponse response = this.coronerHttpClient.get(coronerQuery.toString()); - + backtraceio.coroner.common.Logger.d("CoronerHttpClient CoronerResponse makeRequest", response.toString()); + if (response.error != null) { throw new CoronerResponseException(response.getError()); } diff --git a/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java b/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java index 1f269002..c56e001a 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java +++ b/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java @@ -17,6 +17,8 @@ import backtraceio.coroner.serialization.CoronerResponseGroupDeserializer; import backtraceio.coroner.serialization.GsonWrapper; +import backtraceio.coroner.common.AndroidLogDelegate; + class CoronerHttpClient implements HttpClient { private static final Logger LOGGER = Logger.getLogger(CoronerResponseGroupDeserializer.class.getName()); private final String apiUrl; @@ -29,9 +31,13 @@ public CoronerHttpClient(final String apiUrl, final String coronerToken) { public CoronerApiResponse get(final String requestJson) throws CoronerHttpException, IOException { final HttpURLConnection urlConnection = prepareHttpRequest(requestJson); final int statusCode = urlConnection.getResponseCode(); - + backtraceio.coroner.common.Logger.d("CoronerHttpClient", "invoked"); + backtraceio.coroner.common.Logger.d("CoronerHttpClient statusCode", String.valueOf(statusCode)); if (statusCode != HttpURLConnection.HTTP_OK) { String message = getResponseMessage(urlConnection); + + backtraceio.coroner.common.Logger.d("CoronerHttpClient getResponseMessage message", message); + message = (Common.isNullOrEmpty(message)) ? urlConnection.getResponseMessage() : message; throw new CoronerHttpException(statusCode, String.format("%s: %s", statusCode, message)); @@ -39,6 +45,9 @@ public CoronerApiResponse get(final String requestJson) throws CoronerHttpExcept final String resultJson = getResponseMessage(urlConnection); + backtraceio.coroner.common.Logger.d("CoronerHttpClient resultJson", resultJson); + + return GsonWrapper.fromJson( resultJson, CoronerApiResponse.class); diff --git a/coroner-client/src/main/java/backtraceio/coroner/common/AndroidLogDelegate.java b/coroner-client/src/main/java/backtraceio/coroner/common/AndroidLogDelegate.java new file mode 100644 index 00000000..f8bb5b68 --- /dev/null +++ b/coroner-client/src/main/java/backtraceio/coroner/common/AndroidLogDelegate.java @@ -0,0 +1,6 @@ +package backtraceio.coroner.common; + +// Interface that the instrumentation test can implement to call android.util.Log +public interface AndroidLogDelegate { + void d(String tag, String message); +} diff --git a/coroner-client/src/main/java/backtraceio/coroner/common/Logger.java b/coroner-client/src/main/java/backtraceio/coroner/common/Logger.java new file mode 100644 index 00000000..daa6de45 --- /dev/null +++ b/coroner-client/src/main/java/backtraceio/coroner/common/Logger.java @@ -0,0 +1,25 @@ +package backtraceio.coroner.common; + +public final class Logger { + private static AndroidLogDelegate delegate; + + private Logger() { + // no instances + } + + /** + * Called from the Android test code to register a delegate that can log to Logcat. + */ + public static synchronized void setDelegate(AndroidLogDelegate logDelegate) { + delegate = logDelegate; + } + + /** + * Pure Java code can call this method instead of System.out.println(). + */ + public static synchronized void d(String tag, String message) { + if (delegate != null) { + delegate.d(tag, message); + } + } +} diff --git a/coroner-client/src/main/java/backtraceio/coroner/serialization/GsonWrapper.java b/coroner-client/src/main/java/backtraceio/coroner/serialization/GsonWrapper.java index 3b805f48..ce6e1041 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/serialization/GsonWrapper.java +++ b/coroner-client/src/main/java/backtraceio/coroner/serialization/GsonWrapper.java @@ -9,10 +9,14 @@ public class GsonWrapper { public static T fromJson(final String json, final Class type) { + backtraceio.coroner.common.Logger.d("CoronerHttpClient GsonWrapper fromJson", "started"); + + Gson gson = new GsonBuilder() .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES) .registerTypeAdapter(CoronerResponseGroup.class, new CoronerResponseGroupDeserializer()) .create(); + backtraceio.coroner.common.Logger.d("CoronerHttpClient GsonWrapper", gson.toString()); return gson.fromJson(json, type); } diff --git a/example-app/build.gradle b/example-app/build.gradle index e595acb0..3fba7081 100644 --- a/example-app/build.gradle +++ b/example-app/build.gradle @@ -67,7 +67,7 @@ dependencies { testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.2.1' androidTestImplementation 'androidx.test:rules:1.6.1' - androidTestImplementation 'net.jodah:concurrentunit:0.4.4' + androidTestImplementation 'net.jodah:concurrentunit:0.4.6' androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1' implementation project(':backtrace-library') androidTestImplementation project(path: ':coroner-client') diff --git a/example-app/src/androidTest/java/backtraceio/backtraceio/CoronerLogDelegate.java b/example-app/src/androidTest/java/backtraceio/backtraceio/CoronerLogDelegate.java new file mode 100644 index 00000000..4f4c60ea --- /dev/null +++ b/example-app/src/androidTest/java/backtraceio/backtraceio/CoronerLogDelegate.java @@ -0,0 +1,12 @@ +package backtraceio.backtraceio; + +import backtraceio.coroner.common.AndroidLogDelegate; +import android.util.Log; + +// Implementation that calls the real android.util.Log +public class CoronerLogDelegate implements AndroidLogDelegate { + @Override + public void d(String tag, String message) { + Log.d(tag, message); + } +} \ No newline at end of file diff --git a/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java b/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java index e9bacb4a..c8486d08 100644 --- a/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java +++ b/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java @@ -20,9 +20,11 @@ import org.junit.runner.RunWith; import java.util.Arrays; +import java.util.Collections; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import backtraceio.coroner.common.Logger; import backtraceio.coroner.response.CoronerResponse; import backtraceio.coroner.response.CoronerResponseProcessingException; @@ -44,6 +46,11 @@ public void enableMetricsAndBreadcrumbs() { onView(withId(R.id.enableBreadcrumbs)).perform(click()); } + @Before + public void setUp() { + Logger.setDelegate(new CoronerLogDelegate()); + } + @Test public void useAppContext() { // Context of the app under test. @@ -70,8 +77,13 @@ public void handledException() throws TimeoutException, CoronerResponseProcessin // THEN CoronerResponse response = null; + // Test + backtraceio.coroner.common.Logger.d("CoronerHttpClient", "handledException"); + try { response = this.getCoronerClient().rxIdFilter(rxId[0], Arrays.asList("error.message")); + backtraceio.coroner.common.Logger.d("CoronerHttpClient handledException response", response.toString()); + } catch (Exception ex) { Assert.fail(ex.getMessage()); } @@ -96,13 +108,21 @@ public void dumpWithoutCrash() throws CoronerResponseProcessingException, Interr onView(withId(R.id.dumpWithoutCrash)).perform(click()); // UI action Thread.sleep(THREAD_SLEEP_TIME_MS * 10); + // Test + Logger.d("CoronerHttpClient", "Hello from pure Java code!"); + // THEN try { response = this.getCoronerClient().errorTypeTimestampFilter("Crash", Long.toString(timestampStart), Long.toString(this.getSecondsTimestampNowGMT()), - Arrays.asList("error.message")); + Collections.singletonList("error.message")); + + backtraceio.coroner.common.Logger.d("CoronerHttpClient dumpWithoutCrash response", response.toString()); + } catch (Exception ex) { + backtraceio.coroner.common.Logger.d("CoronerHttpClient dumpWithoutCrash Exception", ex.getMessage()); + Assert.fail(ex.getMessage()); } @@ -132,7 +152,7 @@ public void unhandledException() throws CoronerResponseProcessingException, Inte response = this.getCoronerClient().errorTypeTimestampFilter("Crash", Long.toString(timestampStart), Long.toString(this.getSecondsTimestampNowGMT()), - Arrays.asList("error.message")); + Collections.singletonList("error.message")); } catch (Exception ex) { Assert.fail(ex.getMessage()); } From 806d2c72728096c299cd0458f5b99b3ff08c5a19 Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Tue, 18 Mar 2025 22:30:07 +0100 Subject: [PATCH 2/3] Print json --- .../coroner/serialization/GsonWrapper.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/coroner-client/src/main/java/backtraceio/coroner/serialization/GsonWrapper.java b/coroner-client/src/main/java/backtraceio/coroner/serialization/GsonWrapper.java index ce6e1041..30e28a2c 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/serialization/GsonWrapper.java +++ b/coroner-client/src/main/java/backtraceio/coroner/serialization/GsonWrapper.java @@ -18,7 +18,16 @@ public static T fromJson(final String json, final Class type) { .create(); backtraceio.coroner.common.Logger.d("CoronerHttpClient GsonWrapper", gson.toString()); - return gson.fromJson(json, type); + + backtraceio.coroner.common.Logger.d("CoronerHttpClient GsonWrapper fromJson", "json"); + backtraceio.coroner.common.Logger.d("CoronerHttpClient GsonWrapper fromJson", json); + + backtraceio.coroner.common.Logger.d("CoronerHttpClient GsonWrapper fromJson", "started"); + T result = gson.fromJson(json, type); + backtraceio.coroner.common.Logger.d("CoronerHttpClient GsonWrapper fromJson", "result"); + backtraceio.coroner.common.Logger.d("CoronerHttpClient GsonWrapper fromJson", result.toString()); + + return result; } } From 1e9d50b29e3965d0204b85a903eec5de38b6b6a8 Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Tue, 18 Mar 2025 22:36:18 +0100 Subject: [PATCH 3/3] Log api url --- .../src/main/java/backtraceio/coroner/CoronerHttpClient.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java b/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java index c56e001a..f18e0ebc 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java +++ b/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java @@ -17,8 +17,6 @@ import backtraceio.coroner.serialization.CoronerResponseGroupDeserializer; import backtraceio.coroner.serialization.GsonWrapper; -import backtraceio.coroner.common.AndroidLogDelegate; - class CoronerHttpClient implements HttpClient { private static final Logger LOGGER = Logger.getLogger(CoronerResponseGroupDeserializer.class.getName()); private final String apiUrl; @@ -32,6 +30,8 @@ public CoronerApiResponse get(final String requestJson) throws CoronerHttpExcept final HttpURLConnection urlConnection = prepareHttpRequest(requestJson); final int statusCode = urlConnection.getResponseCode(); backtraceio.coroner.common.Logger.d("CoronerHttpClient", "invoked"); + backtraceio.coroner.common.Logger.d("CoronerHttpClient api url", apiUrl); + backtraceio.coroner.common.Logger.d("CoronerHttpClient request json", requestJson); backtraceio.coroner.common.Logger.d("CoronerHttpClient statusCode", String.valueOf(statusCode)); if (statusCode != HttpURLConnection.HTTP_OK) { String message = getResponseMessage(urlConnection);