Skip to content

Commit 71fff26

Browse files
committed
Initial implementation of recording a custom key value pair in recordException
1 parent f7acdac commit 71fff26

File tree

5 files changed

+69
-25
lines changed

5 files changed

+69
-25
lines changed

firebase-crashlytics/src/main/java/com/google/firebase/crashlytics/FirebaseCrashlytics.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import com.google.firebase.remoteconfig.interop.FirebaseRemoteConfigInterop;
4444
import com.google.firebase.sessions.api.FirebaseSessionsDependencies;
4545
import java.util.List;
46+
import java.util.Map;
4647
import java.util.concurrent.ExecutorService;
4748

4849
/**
@@ -202,11 +203,26 @@ public static FirebaseCrashlytics getInstance() {
202203
* @param throwable a {@link Throwable} to be recorded as a non-fatal event.
203204
*/
204205
public void recordException(@NonNull Throwable throwable) {
206+
recordException(throwable, Map.of());
207+
}
208+
209+
/**
210+
* Records a non-fatal report to send to Crashlytics.
211+
*
212+
* @param throwable a {@link Throwable} to be recorded as a non-fatal event.
213+
* @param userInfo a {@link Map} to add key value pairs to be recorded with the non fatal exception.
214+
*/
215+
public void recordException(@NonNull Throwable throwable, @NonNull Map<String, String> userInfo) {
205216
if (throwable == null) { // Users could call this with null despite the annotation.
206217
Logger.getLogger().w("A null value was passed to recordException. Ignoring.");
207218
return;
208219
}
209-
core.logException(throwable);
220+
221+
if (userInfo == null) { // It's possible to set this to null even with the annotation.
222+
userInfo = Map.of();
223+
}
224+
225+
core.logException(throwable, userInfo);
210226
}
211227

212228
/**

firebase-crashlytics/src/main/java/com/google/firebase/crashlytics/internal/common/CrashlyticsController.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import com.google.firebase.crashlytics.internal.analytics.AnalyticsEventLogger;
3636
import com.google.firebase.crashlytics.internal.concurrency.CrashlyticsTasks;
3737
import com.google.firebase.crashlytics.internal.concurrency.CrashlyticsWorkers;
38+
import com.google.firebase.crashlytics.internal.metadata.EventMetadata;
3839
import com.google.firebase.crashlytics.internal.metadata.LogFileManager;
3940
import com.google.firebase.crashlytics.internal.metadata.UserMetadata;
4041
import com.google.firebase.crashlytics.internal.model.CrashlyticsReport;
@@ -405,7 +406,10 @@ void writeToLog(final long timestamp, final String msg) {
405406
}
406407

407408
/** Log a caught exception - write out Throwable as event section of protobuf */
408-
void writeNonFatalException(@NonNull final Thread thread, @NonNull final Throwable ex) {
409+
void writeNonFatalException(
410+
@NonNull final Thread thread,
411+
@NonNull final Throwable ex,
412+
@NonNull Map<String, String> userInfo) {
409413
// Capture and close over the current time, so that we get the exact call time,
410414
// rather than the time at which the task executes.
411415
final long timestampMillis = System.currentTimeMillis();
@@ -417,7 +421,8 @@ void writeNonFatalException(@NonNull final Thread thread, @NonNull final Throwab
417421
Logger.getLogger().w("Tried to write a non-fatal exception while no session was open.");
418422
return;
419423
}
420-
reportingCoordinator.persistNonFatalEvent(ex, thread, currentSessionId, timestampSeconds);
424+
EventMetadata eventMetadata = new EventMetadata(currentSessionId, timestampSeconds, userInfo);
425+
reportingCoordinator.persistNonFatalEvent(ex, thread, eventMetadata);
421426
}
422427
}
423428

firebase-crashlytics/src/main/java/com/google/firebase/crashlytics/internal/common/CrashlyticsCore.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -313,9 +313,9 @@ public static String getVersion() {
313313
* Throwable was thrown. The Throwable will always be processed on a background thread, so it is
314314
* safe to invoke this method from the main thread.
315315
*/
316-
public void logException(@NonNull Throwable throwable) {
316+
public void logException(@NonNull Throwable throwable, @NonNull Map<String, String> userInfo) {
317317
crashlyticsWorkers.common.submit(
318-
() -> controller.writeNonFatalException(Thread.currentThread(), throwable));
318+
() -> controller.writeNonFatalException(Thread.currentThread(), throwable, userInfo));
319319
}
320320

321321
/**

firebase-crashlytics/src/main/java/com/google/firebase/crashlytics/internal/common/SessionReportingCoordinator.java

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import com.google.android.gms.tasks.Tasks;
2626
import com.google.firebase.crashlytics.internal.Logger;
2727
import com.google.firebase.crashlytics.internal.concurrency.CrashlyticsWorkers;
28+
import com.google.firebase.crashlytics.internal.metadata.EventMetadata;
2829
import com.google.firebase.crashlytics.internal.metadata.LogFileManager;
2930
import com.google.firebase.crashlytics.internal.metadata.UserMetadata;
3031
import com.google.firebase.crashlytics.internal.model.CrashlyticsReport;
@@ -42,6 +43,7 @@
4243
import java.nio.charset.StandardCharsets;
4344
import java.util.ArrayList;
4445
import java.util.Collections;
46+
import java.util.HashMap;
4547
import java.util.List;
4648
import java.util.Map;
4749
import java.util.SortedSet;
@@ -124,13 +126,14 @@ public void onBeginSession(@NonNull String sessionId, long timestampSeconds) {
124126
public void persistFatalEvent(
125127
@NonNull Throwable event, @NonNull Thread thread, @NonNull String sessionId, long timestamp) {
126128
Logger.getLogger().v("Persisting fatal event for session " + sessionId);
127-
persistEvent(event, thread, sessionId, EVENT_TYPE_CRASH, timestamp, true);
129+
EventMetadata eventMetadata = new EventMetadata(sessionId, timestamp, Map.of());
130+
persistEvent(event, thread, EVENT_TYPE_CRASH, eventMetadata, true);
128131
}
129132

130133
public void persistNonFatalEvent(
131-
@NonNull Throwable event, @NonNull Thread thread, @NonNull String sessionId, long timestamp) {
132-
Logger.getLogger().v("Persisting non-fatal event for session " + sessionId);
133-
persistEvent(event, thread, sessionId, EVENT_TYPE_LOGGED, timestamp, false);
134+
@NonNull Throwable event, @NonNull Thread thread, @NonNull EventMetadata eventMetadata) {
135+
Logger.getLogger().v("Persisting non-fatal event for session " + eventMetadata.getSessionId());
136+
persistEvent(event, thread, EVENT_TYPE_LOGGED, eventMetadata, false);
134137
}
135138

136139
@RequiresApi(api = Build.VERSION_CODES.R)
@@ -253,23 +256,20 @@ private CrashlyticsReportWithSessionId ensureHasFid(CrashlyticsReportWithSession
253256
}
254257

255258
private CrashlyticsReport.Session.Event addMetaDataToEvent(
256-
CrashlyticsReport.Session.Event capturedEvent) {
259+
CrashlyticsReport.Session.Event capturedEvent, Map<String, String> eventCustomKeys) {
257260
CrashlyticsReport.Session.Event eventWithLogsAndCustomKeys =
258-
addLogsAndCustomKeysToEvent(capturedEvent, logFileManager, reportMetadata);
261+
addLogsCustomKeysAndEventKeysToEvent(
262+
capturedEvent, logFileManager, reportMetadata, eventCustomKeys);
259263
CrashlyticsReport.Session.Event eventWithRollouts =
260264
addRolloutsStateToEvent(eventWithLogsAndCustomKeys, reportMetadata);
261265
return eventWithRollouts;
262266
}
263267

264-
private CrashlyticsReport.Session.Event addLogsAndCustomKeysToEvent(
265-
CrashlyticsReport.Session.Event capturedEvent) {
266-
return addLogsAndCustomKeysToEvent(capturedEvent, logFileManager, reportMetadata);
267-
}
268-
269-
private CrashlyticsReport.Session.Event addLogsAndCustomKeysToEvent(
268+
private CrashlyticsReport.Session.Event addLogsCustomKeysAndEventKeysToEvent(
270269
CrashlyticsReport.Session.Event capturedEvent,
271270
LogFileManager logFileManager,
272-
UserMetadata reportMetadata) {
271+
UserMetadata reportMetadata,
272+
Map<String, String> eventKeys) {
273273
final CrashlyticsReport.Session.Event.Builder eventBuilder = capturedEvent.toBuilder();
274274
final String content = logFileManager.getLogString();
275275

@@ -283,12 +283,18 @@ private CrashlyticsReport.Session.Event addLogsAndCustomKeysToEvent(
283283
// TODO: Put this back once support for reports endpoint is removed.
284284
// logFileManager.clearLog(); // Clear log to prepare for next event.
285285

286+
// Merges the app level custom keys, with the event specific custom keys.
287+
Map<String, String> customKeysMergedWithEventKeys =
288+
new HashMap<>(reportMetadata.getCustomKeys());
289+
customKeysMergedWithEventKeys.putAll(eventKeys);
290+
286291
final List<CustomAttribute> sortedCustomAttributes =
287-
getSortedCustomAttributes(reportMetadata.getCustomKeys());
292+
getSortedCustomAttributes(customKeysMergedWithEventKeys);
288293
final List<CustomAttribute> sortedInternalKeys =
289294
getSortedCustomAttributes(reportMetadata.getInternalKeys());
290295

291296
if (!sortedCustomAttributes.isEmpty() || !sortedInternalKeys.isEmpty()) {
297+
// TODO(b/380072776): Change implementation to *not* override existing custom attributes.
292298
eventBuilder.setApp(
293299
capturedEvent.getApp().toBuilder()
294300
.setCustomAttributes(sortedCustomAttributes)
@@ -299,6 +305,14 @@ private CrashlyticsReport.Session.Event addLogsAndCustomKeysToEvent(
299305
return eventBuilder.build();
300306
}
301307

308+
private CrashlyticsReport.Session.Event addLogsAndCustomKeysToEvent(
309+
CrashlyticsReport.Session.Event capturedEvent,
310+
LogFileManager logFileManager,
311+
UserMetadata reportMetadata) {
312+
return addLogsCustomKeysAndEventKeysToEvent(
313+
capturedEvent, logFileManager, reportMetadata, Map.of());
314+
}
315+
302316
private CrashlyticsReport.Session.Event addRolloutsStateToEvent(
303317
CrashlyticsReport.Session.Event capturedEvent, UserMetadata reportMetadata) {
304318
List<CrashlyticsReport.Session.Event.RolloutAssignment> reportRolloutAssignments =
@@ -319,9 +333,8 @@ private CrashlyticsReport.Session.Event addRolloutsStateToEvent(
319333
private void persistEvent(
320334
@NonNull Throwable event,
321335
@NonNull Thread thread,
322-
@NonNull String sessionId,
323336
@NonNull String eventType,
324-
long timestamp,
337+
@NonNull EventMetadata eventMetadata,
325338
boolean isFatal) {
326339

327340
final boolean isHighPriority = eventType.equals(EVENT_TYPE_CRASH);
@@ -331,23 +344,25 @@ private void persistEvent(
331344
event,
332345
thread,
333346
eventType,
334-
timestamp,
347+
eventMetadata.getTimestamp(),
335348
EVENT_THREAD_IMPORTANCE,
336349
MAX_CHAINED_EXCEPTION_DEPTH,
337350
isFatal);
338-
CrashlyticsReport.Session.Event finallizedEvent = addMetaDataToEvent(capturedEvent);
351+
CrashlyticsReport.Session.Event finallizedEvent =
352+
addMetaDataToEvent(capturedEvent, eventMetadata.getUserInfo());
339353

340354
// Non-fatal, persistence write task we move to diskWriteWorker
341355
if (!isFatal) {
342356
crashlyticsWorkers.diskWrite.submit(
343357
() -> {
344358
Logger.getLogger().d("disk worker: log non-fatal event to persistence");
345-
reportPersistence.persistEvent(finallizedEvent, sessionId, isHighPriority);
359+
reportPersistence.persistEvent(
360+
finallizedEvent, eventMetadata.getSessionId(), isHighPriority);
346361
});
347362
return;
348363
}
349364

350-
reportPersistence.persistEvent(finallizedEvent, sessionId, isHighPriority);
365+
reportPersistence.persistEvent(finallizedEvent, eventMetadata.getSessionId(), isHighPriority);
351366
}
352367

353368
private boolean onReportSendComplete(@NonNull Task<CrashlyticsReportWithSessionId> task) {
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.google.firebase.crashlytics.internal.metadata
2+
3+
/** A class that represents information to attach to a specific event. */
4+
data class EventMetadata(
5+
val sessionId: String,
6+
val timestamp: Long,
7+
val userInfo: Map<String, String> = mapOf()
8+
)

0 commit comments

Comments
 (0)