Skip to content

Commit 1cfcc58

Browse files
authored
feat: metadata reading and logging (getsentry/sentry-android#4)
1 parent 4d5c50a commit 1cfcc58

18 files changed

Lines changed: 573 additions & 4 deletions
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package io.sentry.android;
2+
3+
import android.util.Log;
4+
import io.sentry.ILogger;
5+
import io.sentry.SentryLevel;
6+
7+
class AndroidLogger implements ILogger {
8+
9+
private static final String tag = "Sentry";
10+
11+
@Override
12+
public void log(SentryLevel level, String message, Object... args) {
13+
Log.println(toLogcatLevel(level), tag, String.format(message, args));
14+
}
15+
16+
@Override
17+
public void log(SentryLevel level, String message, Throwable throwable) {
18+
19+
switch (level) {
20+
case Debug:
21+
Log.d(tag, message, throwable);
22+
break;
23+
case Info:
24+
Log.i(tag, message, throwable);
25+
break;
26+
case Warning:
27+
Log.w(tag, message, throwable);
28+
break;
29+
case Error:
30+
Log.e(tag, message, throwable);
31+
break;
32+
case Fatal:
33+
Log.wtf(tag, message, throwable);
34+
break;
35+
}
36+
}
37+
38+
SentryLevel toSentryLevel(int logcatLevel) {
39+
switch (logcatLevel) {
40+
case Log.VERBOSE:
41+
case Log.DEBUG:
42+
return SentryLevel.Debug;
43+
case Log.INFO:
44+
return SentryLevel.Info;
45+
case Log.WARN:
46+
return SentryLevel.Warning;
47+
case Log.ERROR:
48+
return SentryLevel.Error;
49+
case Log.ASSERT:
50+
default:
51+
return SentryLevel.Fatal;
52+
}
53+
}
54+
55+
int toLogcatLevel(SentryLevel sentryLevel) {
56+
switch (sentryLevel) {
57+
case Debug:
58+
return Log.DEBUG;
59+
case Info:
60+
return Log.INFO;
61+
case Warning:
62+
return Log.WARN;
63+
case Fatal:
64+
return Log.ASSERT;
65+
case Error:
66+
default:
67+
return Log.ERROR;
68+
}
69+
}
70+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package io.sentry.android;
2+
3+
import android.content.Context;
4+
import io.sentry.SentryOptions;
5+
6+
class AndroidOptionsInitializer {
7+
static void init(SentryOptions options, Context context) {
8+
// Firstly set the logger, if `debug=true` configured, logging can start asap.
9+
options.setLogger(new AndroidLogger());
10+
ManifestMetadataReader.applyMetadata(context, options);
11+
options.addEventProcessor(new DefaultAndroidEventProcessor(context));
12+
}
13+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package io.sentry.android;
2+
3+
import android.content.Context;
4+
import android.content.pm.ApplicationInfo;
5+
import android.content.pm.PackageManager;
6+
import android.os.Bundle;
7+
import io.sentry.SentryLevel;
8+
import io.sentry.SentryOptions;
9+
10+
class ManifestMetadataReader {
11+
12+
private static final String DSN_KEY = "io.sentry.dsn";
13+
private static final String DEBUG_KEY = "io.sentry.debug";
14+
15+
public static void applyMetadata(Context context, SentryOptions options) {
16+
try {
17+
ApplicationInfo app =
18+
context
19+
.getPackageManager()
20+
.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
21+
Bundle metadata = app.metaData;
22+
23+
options.setDebug(metadata.getBoolean(DEBUG_KEY, options.isDebug()));
24+
options
25+
.getLogger()
26+
.log(SentryLevel.Info, "Retrieving configuration from AndroidManifest.xml");
27+
28+
String dsn = metadata.getString(DSN_KEY, null);
29+
if (dsn != null) {
30+
options.getLogger().log(SentryLevel.Debug, "DSN read: %s", dsn);
31+
options.setDsn(dsn);
32+
}
33+
} catch (Exception e) {
34+
options
35+
.getLogger()
36+
.log(
37+
SentryLevel.Error, "Failed to read configuration from android manifest metadata.", e);
38+
}
39+
}
40+
}

sentry-android/src/main/java/io/sentry/android/SentryInitProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
public class SentryInitProvider extends ContentProvider {
1212
@Override
1313
public boolean onCreate() {
14-
Sentry.init(o -> o.AddEventProcessor(new DefaultAndroidEventProcessor(getContext())));
14+
Sentry.init(o -> AndroidOptionsInitializer.init(o, getContext()));
1515
return true;
1616
}
1717

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package io.sentry.android
2+
3+
import com.nhaarman.mockitokotlin2.mock
4+
import io.sentry.SentryOptions
5+
import kotlin.test.Test
6+
import kotlin.test.assertEquals
7+
import kotlin.test.assertNotNull
8+
9+
class AndroidOptionsInitializerTest {
10+
@Test
11+
fun `logger set to AndroidLogger`() {
12+
val sentryOptions = SentryOptions()
13+
AndroidOptionsInitializer.init(sentryOptions, mock())
14+
val logger = sentryOptions.javaClass.declaredFields.first { it.name == "logger" }
15+
logger.isAccessible = true
16+
val loggerField = logger.get(sentryOptions)
17+
val innerLogger = loggerField.javaClass.declaredFields.first { it.name == "logger" }
18+
innerLogger.isAccessible = true
19+
assertEquals(AndroidLogger::class, innerLogger.get(loggerField)::class)
20+
}
21+
22+
@Test
23+
fun `AndroidEventProcessor added to processors list`() {
24+
val sentryOptions = SentryOptions()
25+
AndroidOptionsInitializer.init(sentryOptions, mock())
26+
val actual = sentryOptions.eventProcessors.firstOrNull { it::class == DefaultAndroidEventProcessor::class }
27+
assertNotNull(actual)
28+
}
29+
}

sentry-core/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ plugins {
77
dependencies {
88
testImplementation(kotlin(Config.kotlinStdLib))
99
testImplementation(Config.TestLibs.kotlinTestJunit)
10+
testImplementation(Config.TestLibs.mockitoKotlin)
1011
}
1112

1213
configure<SourceSetContainer> {
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package io.sentry;
2+
3+
import io.sentry.util.Nullable;
4+
5+
/** Sentry SDK internal diagnostic logger. */
6+
class DiagnosticLogger implements ILogger {
7+
private SentryOptions options;
8+
private ILogger logger;
9+
10+
/**
11+
* Creates a new instance of DiagnosticLogger with the wrapped ILogger.
12+
*
13+
* @param options
14+
* @param logger
15+
*/
16+
DiagnosticLogger(SentryOptions options, @Nullable ILogger logger) {
17+
if (options == null) {
18+
throw new IllegalArgumentException("SentryOptions is required.");
19+
}
20+
this.options = options;
21+
this.logger = logger;
22+
}
23+
24+
/**
25+
* Whether this logger is enabled for the specified SentryLevel.
26+
*
27+
* @param level The SentryLevel to test against.
28+
* @return True if a log message would be recorded for the level. Otherwise false.
29+
*/
30+
public boolean isEnabled(SentryLevel level) {
31+
SentryLevel diagLevel = options.getDiagnosticLevel();
32+
if (level == null || diagLevel == null) {
33+
return false;
34+
}
35+
return options.isDebug() && level.ordinal() >= diagLevel.ordinal();
36+
}
37+
38+
/**
39+
* Logs a message with the specified level, message and optional arguments.
40+
*
41+
* @param level The SentryLevel.
42+
* @param message The message.
43+
* @param args The optional arguments to format the message.
44+
*/
45+
@Override
46+
public void log(SentryLevel level, String message, Object... args) {
47+
if (logger != null && isEnabled(level)) {
48+
logger.log(level, message, args);
49+
}
50+
}
51+
52+
/**
53+
* Logs a message with the specified level, message and throwable.
54+
*
55+
* @param level The SentryLevel.
56+
* @param message The message.
57+
* @param throwable The throwable to log.
58+
*/
59+
@Override
60+
public void log(SentryLevel level, String message, Throwable throwable) {
61+
if (logger != null && isEnabled(level)) {
62+
logger.log(level, message, throwable);
63+
}
64+
}
65+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package io.sentry;
2+
3+
/** Sentry SDK internal logging interface. */
4+
public interface ILogger {
5+
/**
6+
* Logs a message with the specified level, message and optional arguments.
7+
*
8+
* @param level The SentryLevel.
9+
* @param message The message.
10+
* @param args The optional arguments to format the message.
11+
*/
12+
void log(SentryLevel level, String message, Object... args);
13+
14+
/**
15+
* Logs a message with the specified level, message and optional arguments.
16+
*
17+
* @param level The SentryLevel.
18+
* @param message The message.
19+
* @param throwable The throwable to log.
20+
*/
21+
void log(SentryLevel level, String message, Throwable throwable);
22+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package io.sentry;
2+
3+
/** No-op implementation of ILogger */
4+
class NoOpLogger implements ILogger {
5+
6+
private static NoOpLogger instance = new NoOpLogger();
7+
8+
public static NoOpLogger getInstance() {
9+
return instance;
10+
}
11+
12+
NoOpLogger() {}
13+
14+
@Override
15+
public void log(SentryLevel level, String message, Object... args) {}
16+
17+
@Override
18+
public void log(SentryLevel level, String message, Throwable throwable) {}
19+
}

sentry-core/src/main/java/io/sentry/Sentry.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ public static void init(OptionsConfiguration optionsConfiguration) {
2121
}
2222

2323
static synchronized void init(SentryOptions options) {
24+
ILogger logger = options.getLogger();
25+
if (logger != null) {
26+
logger.log(SentryLevel.Info, "Initializing SDK with DSN: '%d'", options.getDsn());
27+
}
2428
currentClient.close();
2529
currentClient = new SentryClient(options);
2630
}

0 commit comments

Comments
 (0)