Skip to content

Commit 356f346

Browse files
committed
Improve log flushing and add test
1 parent 2f05276 commit 356f346

File tree

3 files changed

+104
-1
lines changed

3 files changed

+104
-1
lines changed

sentry-logback/src/test/kotlin/io/sentry/logback/SentryAppenderTest.kt

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ import io.sentry.ITransportFactory
1414
import io.sentry.InitPriority
1515
import io.sentry.Sentry
1616
import io.sentry.SentryLevel
17+
import io.sentry.SentryLogLevel
1718
import io.sentry.SentryOptions
1819
import io.sentry.checkEvent
20+
import io.sentry.checkLogs
1921
import io.sentry.test.initForTest
2022
import io.sentry.transport.ITransport
2123
import org.mockito.kotlin.any
@@ -40,7 +42,7 @@ import kotlin.test.assertNull
4042
import kotlin.test.assertTrue
4143

4244
class SentryAppenderTest {
43-
private class Fixture(dsn: String? = "http://key@localhost/proj", minimumBreadcrumbLevel: Level? = null, minimumEventLevel: Level? = null, contextTags: List<String>? = null, encoder: Encoder<ILoggingEvent>? = null, sendDefaultPii: Boolean = false, options: SentryOptions = SentryOptions(), startLater: Boolean = false) {
45+
private class Fixture(dsn: String? = "http://key@localhost/proj", minimumBreadcrumbLevel: Level? = null, minimumEventLevel: Level? = null, minimumLevel: Level? = null, contextTags: List<String>? = null, encoder: Encoder<ILoggingEvent>? = null, sendDefaultPii: Boolean = false, enableLogs: Boolean = false, options: SentryOptions = SentryOptions(), startLater: Boolean = false) {
4446
val logger: Logger = LoggerFactory.getLogger(SentryAppenderTest::class.java)
4547
val loggerContext = LoggerFactory.getILoggerFactory() as LoggerContext
4648
val transportFactory = mock<ITransportFactory>()
@@ -54,10 +56,12 @@ class SentryAppenderTest {
5456
this.encoder = encoder
5557
options.dsn = dsn
5658
options.isSendDefaultPii = sendDefaultPii
59+
options.logs.isEnabled = enableLogs
5760
contextTags?.forEach { options.addContextTag(it) }
5861
appender.setOptions(options)
5962
appender.setMinimumBreadcrumbLevel(minimumBreadcrumbLevel)
6063
appender.setMinimumEventLevel(minimumEventLevel)
64+
appender.setMinimumLevel(minimumLevel)
6165
appender.context = loggerContext
6266
appender.setTransportFactory(transportFactory)
6367
encoder?.context = loggerContext
@@ -312,6 +316,76 @@ class SentryAppenderTest {
312316
)
313317
}
314318

319+
@Test
320+
fun `converts trace log level to Sentry log level`() {
321+
fixture = Fixture(minimumLevel = Level.TRACE, enableLogs = true)
322+
fixture.logger.trace("testing trace level")
323+
324+
Sentry.flush(1000)
325+
326+
verify(fixture.transport).send(
327+
checkLogs { logs ->
328+
assertEquals(SentryLogLevel.TRACE, logs.items.first().level)
329+
}
330+
)
331+
}
332+
333+
@Test
334+
fun `converts debug log level to Sentry log level`() {
335+
fixture = Fixture(minimumLevel = Level.DEBUG, enableLogs = true)
336+
fixture.logger.debug("testing debug level")
337+
338+
Sentry.flush(1000)
339+
340+
verify(fixture.transport).send(
341+
checkLogs { logs ->
342+
assertEquals(SentryLogLevel.DEBUG, logs.items.first().level)
343+
}
344+
)
345+
}
346+
347+
@Test
348+
fun `converts info log level to Sentry log level`() {
349+
fixture = Fixture(minimumLevel = Level.INFO, enableLogs = true)
350+
fixture.logger.info("testing info level")
351+
352+
Sentry.flush(1000)
353+
354+
verify(fixture.transport).send(
355+
checkLogs { logs ->
356+
assertEquals(SentryLogLevel.INFO, logs.items.first().level)
357+
}
358+
)
359+
}
360+
361+
@Test
362+
fun `converts warn log level to Sentry log level`() {
363+
fixture = Fixture(minimumLevel = Level.WARN, enableLogs = true)
364+
fixture.logger.warn("testing warn level")
365+
366+
Sentry.flush(1000)
367+
368+
verify(fixture.transport).send(
369+
checkLogs { logs ->
370+
assertEquals(SentryLogLevel.WARN, logs.items.first().level)
371+
}
372+
)
373+
}
374+
375+
@Test
376+
fun `converts error log level to Sentry log level`() {
377+
fixture = Fixture(minimumLevel = Level.ERROR, enableLogs = true)
378+
fixture.logger.error("testing error level")
379+
380+
Sentry.flush(1000)
381+
382+
verify(fixture.transport).send(
383+
checkLogs { logs ->
384+
assertEquals(SentryLogLevel.ERROR, logs.items.first().level)
385+
}
386+
)
387+
}
388+
315389
@Test
316390
fun `attaches thread information`() {
317391
fixture = Fixture(minimumEventLevel = Level.WARN)

sentry-test-support/src/main/kotlin/io/sentry/Assertions.kt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,20 @@ fun checkEvent(predicate: (SentryEvent) -> Unit): SentryEnvelope {
1818
}
1919
}
2020

21+
/**
22+
* Verifies is [SentryEnvelope] contains log events matching a predicate.
23+
*/
24+
fun checkLogs(predicate: (SentryLogEvents) -> Unit): SentryEnvelope {
25+
return check {
26+
val events: SentryLogEvents? = it.items.first().getLogs(JsonSerializer(SentryOptions.empty()))
27+
if (events != null) {
28+
predicate(events)
29+
} else {
30+
throw SkipError("event is null")
31+
}
32+
}
33+
}
34+
2135
fun checkTransaction(predicate: (SentryTransaction) -> Unit): SentryEnvelope {
2236
return check {
2337
val transaction = it.items.first().getTransaction(JsonSerializer(SentryOptions.empty()))

sentry/src/main/java/io/sentry/logger/LoggerBatchProcessor.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,18 @@
44
import io.sentry.ISentryExecutorService;
55
import io.sentry.ISentryLifecycleToken;
66
import io.sentry.SentryExecutorService;
7+
import io.sentry.SentryLevel;
78
import io.sentry.SentryLogEvent;
89
import io.sentry.SentryLogEvents;
910
import io.sentry.SentryOptions;
11+
import io.sentry.transport.ReusableCountLatch;
1012
import io.sentry.util.AutoClosableReentrantLock;
1113
import java.util.ArrayList;
1214
import java.util.List;
1315
import java.util.Queue;
1416
import java.util.concurrent.ConcurrentLinkedQueue;
1517
import java.util.concurrent.Future;
18+
import java.util.concurrent.TimeUnit;
1619
import org.jetbrains.annotations.NotNull;
1720
import org.jetbrains.annotations.Nullable;
1821

@@ -30,6 +33,8 @@ public final class LoggerBatchProcessor implements ILoggerBatchProcessor {
3033
new AutoClosableReentrantLock();
3134
private volatile boolean hasScheduled = false;
3235

36+
private final @NotNull ReusableCountLatch pendingCount = new ReusableCountLatch();
37+
3338
public LoggerBatchProcessor(
3439
final @NotNull SentryOptions options, final @NotNull ISentryClient client) {
3540
this.options = options;
@@ -40,6 +45,7 @@ public LoggerBatchProcessor(
4045

4146
@Override
4247
public void add(final @NotNull SentryLogEvent logEvent) {
48+
pendingCount.increment();
4349
queue.offer(logEvent);
4450
maybeSchedule(false, false);
4551
}
@@ -78,6 +84,12 @@ private void maybeSchedule(boolean forceSchedule, boolean immediately) {
7884
@Override
7985
public void flush(long timeoutMillis) {
8086
maybeSchedule(true, true);
87+
try {
88+
pendingCount.waitTillZero(timeoutMillis, TimeUnit.MILLISECONDS);
89+
} catch (InterruptedException e) {
90+
options.getLogger().log(SentryLevel.ERROR, "Failed to flush log events", e);
91+
Thread.currentThread().interrupt();
92+
}
8193
}
8294

8395
private void flush() {
@@ -108,6 +120,9 @@ private void flushBatch() {
108120

109121
if (!logEvents.isEmpty()) {
110122
client.captureBatchedLogEvents(new SentryLogEvents(logEvents));
123+
for (int i = 0; i < logEvents.size(); i++) {
124+
pendingCount.decrement();
125+
}
111126
}
112127
}
113128

0 commit comments

Comments
 (0)