Skip to content

Commit 61ea8c0

Browse files
committed
Missing Android logger
1 parent 457aec4 commit 61ea8c0

File tree

1 file changed

+96
-0
lines changed

1 file changed

+96
-0
lines changed
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package io.sentry.react;
2+
3+
import com.facebook.react.bridge.Arguments;
4+
import com.facebook.react.bridge.ReactApplicationContext;
5+
import com.facebook.react.bridge.WritableMap;
6+
import com.facebook.react.modules.core.DeviceEventManagerModule;
7+
import io.sentry.ILogger;
8+
import io.sentry.SentryLevel;
9+
import io.sentry.android.core.AndroidLogger;
10+
import java.lang.ref.WeakReference;
11+
import org.jetbrains.annotations.NotNull;
12+
import org.jetbrains.annotations.Nullable;
13+
14+
/**
15+
* Custom ILogger implementation that wraps AndroidLogger and forwards log messages to React Native.
16+
* This allows native SDK logs to appear in the Metro console when debug mode is enabled.
17+
*/
18+
public class RNSentryLogger implements ILogger {
19+
private static final String TAG = "Sentry";
20+
private static final String EVENT_NAME = "SentryNativeLog";
21+
22+
private final AndroidLogger androidLogger;
23+
private WeakReference<ReactApplicationContext> reactContextRef;
24+
25+
public RNSentryLogger() {
26+
this.androidLogger = new AndroidLogger(TAG);
27+
}
28+
29+
public void setReactContext(@Nullable ReactApplicationContext context) {
30+
this.reactContextRef = context != null ? new WeakReference<>(context) : null;
31+
}
32+
33+
@Override
34+
public void log(@NotNull SentryLevel level, @NotNull String message, @Nullable Object... args) {
35+
// Always log to Logcat (default behavior)
36+
androidLogger.log(level, message, args);
37+
38+
// Forward to JS
39+
String formattedMessage =
40+
(args == null || args.length == 0) ? message : String.format(message, args);
41+
forwardToJS(level, formattedMessage);
42+
}
43+
44+
@Override
45+
public void log(
46+
@NotNull SentryLevel level, @NotNull String message, @Nullable Throwable throwable) {
47+
androidLogger.log(level, message, throwable);
48+
49+
String fullMessage = throwable != null ? message + ": " + throwable.getMessage() : message;
50+
forwardToJS(level, fullMessage);
51+
}
52+
53+
@Override
54+
public void log(
55+
@NotNull SentryLevel level,
56+
@Nullable Throwable throwable,
57+
@NotNull String message,
58+
@Nullable Object... args) {
59+
androidLogger.log(level, throwable, message, args);
60+
61+
String formattedMessage =
62+
(args == null || args.length == 0) ? message : String.format(message, args);
63+
if (throwable != null) {
64+
formattedMessage += ": " + throwable.getMessage();
65+
}
66+
forwardToJS(level, formattedMessage);
67+
}
68+
69+
@Override
70+
public boolean isEnabled(@Nullable SentryLevel level) {
71+
return androidLogger.isEnabled(level);
72+
}
73+
74+
private void forwardToJS(@NotNull SentryLevel level, @NotNull String message) {
75+
ReactApplicationContext context = reactContextRef != null ? reactContextRef.get() : null;
76+
if (context == null || !context.hasActiveReactInstance()) {
77+
return;
78+
}
79+
80+
try {
81+
WritableMap params = Arguments.createMap();
82+
params.putString("level", level.name().toLowerCase());
83+
params.putString("component", "Sentry");
84+
params.putString("message", message);
85+
86+
context
87+
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
88+
.emit(EVENT_NAME, params);
89+
} catch (Exception e) {
90+
// Silently ignore - don't cause issues if JS bridge isn't ready
91+
// We intentionally swallow this exception to avoid disrupting the app
92+
// when the React Native bridge is not yet initialized or has been torn down
93+
androidLogger.log(SentryLevel.DEBUG, "Failed to forward log to JS: " + e.getMessage());
94+
}
95+
}
96+
}

0 commit comments

Comments
 (0)