99import io .sentry .InitPriority ;
1010import io .sentry .ScopesAdapter ;
1111import io .sentry .Sentry ;
12+ import io .sentry .SentryAttribute ;
13+ import io .sentry .SentryAttributes ;
1214import io .sentry .SentryEvent ;
1315import io .sentry .SentryIntegrationPackageStorage ;
1416import io .sentry .SentryLevel ;
17+ import io .sentry .SentryLogLevel ;
1518import io .sentry .SentryOptions ;
1619import io .sentry .exception .ExceptionMechanismException ;
20+ import io .sentry .logger .SentryLogParameters ;
1721import io .sentry .protocol .Mechanism ;
1822import io .sentry .protocol .Message ;
1923import io .sentry .protocol .SdkVersion ;
@@ -50,6 +54,7 @@ public class SentryHandler extends Handler {
5054
5155 private @ NotNull Level minimumBreadcrumbLevel = Level .INFO ;
5256 private @ NotNull Level minimumEventLevel = Level .SEVERE ;
57+ private @ NotNull Level minimumLevel = Level .INFO ;
5358
5459 static {
5560 SentryIntegrationPackageStorage .getInstance ()
@@ -106,6 +111,9 @@ public void publish(final @NotNull LogRecord record) {
106111 return ;
107112 }
108113 try {
114+ if (record .getLevel ().intValue () >= minimumLevel .intValue ()) {
115+ captureLog (record );
116+ }
109117 if (record .getLevel ().intValue () >= minimumEventLevel .intValue ()) {
110118 final Hint hint = new Hint ();
111119 hint .set (SENTRY_SYNTHETIC_EXCEPTION , record );
@@ -126,6 +134,46 @@ public void publish(final @NotNull LogRecord record) {
126134 }
127135 }
128136
137+ /**
138+ * Captures a Sentry log from JULs {@link LogRecord}.
139+ *
140+ * @param loggingEvent the JUL log record
141+ */
142+ // for the Android compatibility we must use old Java Date class
143+ @ SuppressWarnings ("JdkObsolete" )
144+ protected void captureLog (@ NotNull LogRecord loggingEvent ) {
145+ final @ NotNull SentryLogLevel sentryLevel = toSentryLogLevel (loggingEvent .getLevel ());
146+
147+ final @ Nullable Object [] arguments = loggingEvent .getParameters ();
148+ final @ NotNull SentryAttributes attributes = SentryAttributes .of ();
149+
150+ @ NotNull String message = loggingEvent .getMessage ();
151+ if (loggingEvent .getResourceBundle () != null
152+ && loggingEvent .getResourceBundle ().containsKey (loggingEvent .getMessage ())) {
153+ message = loggingEvent .getResourceBundle ().getString (loggingEvent .getMessage ());
154+ }
155+
156+ attributes .add (SentryAttribute .stringAttribute ("sentry.message.template" , message ));
157+
158+ final @ NotNull String formattedMessage = maybeFormatted (arguments , message );
159+ final @ NotNull SentryLogParameters params = SentryLogParameters .create (attributes );
160+
161+ Sentry .logger ().log (sentryLevel , params , formattedMessage , arguments );
162+ }
163+
164+ private @ NotNull String maybeFormatted (
165+ final @ NotNull Object [] arguments , final @ NotNull String message ) {
166+ if (arguments != null ) {
167+ try {
168+ return formatMessage (message , arguments );
169+ } catch (RuntimeException e ) {
170+ // local formatting failed, sending raw message instead of formatted message
171+ }
172+ }
173+
174+ return message ;
175+ }
176+
129177 /** Retrieves the properties of the logger. */
130178 private void retrieveProperties () {
131179 final LogManager manager = LogManager .getLogManager ();
@@ -141,6 +189,10 @@ private void retrieveProperties() {
141189 if (minimumEventLevel != null ) {
142190 setMinimumEventLevel (parseLevelOrDefault (minimumEventLevel ));
143191 }
192+ final String minimumLevel = manager .getProperty (className + ".minimumLevel" );
193+ if (minimumLevel != null ) {
194+ setMinimumLevel (parseLevelOrDefault (minimumLevel ));
195+ }
144196 }
145197
146198 /**
@@ -163,6 +215,26 @@ private void retrieveProperties() {
163215 }
164216 }
165217
218+ /**
219+ * Transforms a {@link Level} into an {@link SentryLogLevel}.
220+ *
221+ * @param level original level as defined in JUL.
222+ * @return log level used within sentry logs.
223+ */
224+ private static @ NotNull SentryLogLevel toSentryLogLevel (final @ NotNull Level level ) {
225+ if (level .intValue () >= Level .SEVERE .intValue ()) {
226+ return SentryLogLevel .ERROR ;
227+ } else if (level .intValue () >= Level .WARNING .intValue ()) {
228+ return SentryLogLevel .WARN ;
229+ } else if (level .intValue () >= Level .INFO .intValue ()) {
230+ return SentryLogLevel .INFO ;
231+ } else if (level .intValue () >= Level .FINE .intValue ()) {
232+ return SentryLogLevel .DEBUG ;
233+ } else {
234+ return SentryLogLevel .TRACE ;
235+ }
236+ }
237+
166238 private @ NotNull Level parseLevelOrDefault (final @ NotNull String levelName ) {
167239 try {
168240 return Level .parse (levelName .trim ());
@@ -339,6 +411,16 @@ public void setMinimumEventLevel(final @Nullable Level minimumEventLevel) {
339411 return minimumEventLevel ;
340412 }
341413
414+ public void setMinimumLevel (final @ Nullable Level minimumLevel ) {
415+ if (minimumLevel != null ) {
416+ this .minimumLevel = minimumLevel ;
417+ }
418+ }
419+
420+ public @ NotNull Level getMinimumLevel () {
421+ return minimumLevel ;
422+ }
423+
342424 public boolean isPrintfStyle () {
343425 return printfStyle ;
344426 }
0 commit comments