Skip to content

Commit 67a0fc9

Browse files
buenaflorclaude
andauthored
feat(kmp): Add logs (#509)
* Implement SentryLoggerApi for Kotlin Multiplatform with platform-specific loggers for JVM and Apple. Add logging capabilities with structured message formatting and attributes. Update SentryOptions to include log enabling feature. Enhance SentryBridge to provide logger access. Include no-op logger for unsupported platforms. * Enhance logging capabilities in Kotlin Multiplatform by implementing SentryLoggerApi with structured logging support. Introduce SentryLog and SentryLogOptions for managing log entries and configurations. Update SentryOptions to include logging settings and modify SentryBridge to provide logger access. Add tests for log level conversions and SentryAttributes functionality. * Refactor logging implementation in Kotlin Multiplatform by replacing SentryLoggerApi with SentryLogger interface. Introduce structured logging capabilities with DefaultSentryLogEventBuilder and SentryLogEventBuilder. Update SentryBridge and platform-specific loggers for improved integration. Remove deprecated SentryLoggerApi and enhance no-op logger for unsupported platforms. * Implement Cocoa and JVM logging delegates in Kotlin Multiplatform, enhancing structured logging capabilities. Introduce CocoaSentryLoggerDelegate and JvmSentryLogger for platform-specific logging. Update SentryAttributes to use SentryAttributeValue for type-safe attribute handling. Refactor SentryLog and SentryLogOptions for improved log management. Add tests for log level conversions and attribute functionality. * Refactor logging implementation in Kotlin Multiplatform by introducing a unified logging architecture. Replace CocoaSentryLoggerDelegate and JvmSentryLogger with BaseSentryLogger for shared logic. Enhance SentryAttributes for type-safe attribute handling and update SentryLogBuilder for structured logging. Remove deprecated classes and add comprehensive tests for logging functionality and attribute conversions. * Enhance Kotlin Multiplatform logging by adding SentryLogger interface and SentryAttributeValue classes for structured logging. Introduce SentryLog and SentryLogOptions for improved log management across platforms. Update SentryOptions to include logging configurations and ensure compatibility with existing attributes. Refactor SentryBridge to provide logger access and improve integration with platform-specific loggers. Add tests for attribute conversions and logging functionality. * Refactor DefaultSentryLogBuilder by removing commented sections for improved readability and updating the attributes method to include the @SentryLogDsl annotation for better DSL support. * Update comment * Revert changes to plugin - accidental change * API dump * Fix test * Fix tests * Fix tests * Add structured logging support with attribute DSL functions * fix(logs): Fix Apple attribute conversion and stale logger references Apple toKmpSentryAttributes was matching raw types (String, NSNumber) instead of SentryStructuredLogAttribute objects, causing all attributes to be silently dropped. Also fix updateAttributesFrom to produce SentryStructuredLogAttribute instances instead of raw values. Both JVM and Apple logger adapters now accept a provider function instead of capturing a logger reference, preventing stale references after Sentry close/re-init cycles. Fix severityNumber update to always call setSeverityNumber, allowing users to clear it to null. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add all Sentry.logger API variants to sample app for testing Covers: simple message with varargs (Variant A), message with inline attributes lambda (Variant B), full DSL builder with message/attributes (Variant C), plain message DSL, prebuilt SentryAttributes, and merged attributes blocks. Exercises all attribute types (String, Int, Boolean, Double). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Make sample logger messages distinctive across API variants Each log call now has a unique message following a login flow theme, making it easy to identify which API variant produced each log entry. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Update tests * Update CHANGELOG * Update CHANGELOG * Update public docs for logger * Run spotlessApply * Fix NSInteger type mismatch for 32-bit Apple targets Use .convert() for Long-to-NSInteger conversion so it works on both 64-bit (Long) and 32-bit (Int) Apple targets. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix MaxLineLength detekt issue and run spotlessApply Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add missing log functions * Update sample and CHANGELOG * Update CHANGELOG --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent fa235cc commit 67a0fc9

46 files changed

Lines changed: 3582 additions & 4 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,29 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
### Features
6+
7+
- Add structured logs support ([#509](https://github.com/getsentry/sentry-kotlin-multiplatform/pull/509))
8+
9+
```kotlin
10+
// Simple API
11+
Sentry.logger.warn("Rate limit reached for %s", endpoint) {
12+
this["currentRequests"] = 120
13+
this["limit"] = 100
14+
this["retryAfterSeconds"] = 60
15+
}
16+
17+
// Full DSL
18+
Sentry.logger.fatal {
19+
message("Database connection pool exhausted for %s", dbHost)
20+
attributes {
21+
this["database"] = "users"
22+
this["activeConnections"] = 100
23+
}
24+
}
25+
```
26+
327
## 0.23.1
428

529
### Fixes

build.gradle.kts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,14 @@ val detektProjectBaseline by tasks.registering(io.gitlab.arturbosch.detekt.Detek
228228
detektExcludes()
229229
}
230230

231+
subprojects {
232+
tasks.withType<AbstractTestTask>().configureEach {
233+
if (System.getenv("CI") != "true") {
234+
filter.excludeTestsMatching("*E2E*")
235+
}
236+
}
237+
}
238+
231239
// Configure tasks so it is also run for the plugin
232240
tasks.getByName("detekt") {
233241
gradle.includedBuild("sentry-kotlin-multiplatform-gradle-plugin").task(":detekt")

config/detekt/detekt.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ complexity:
2727
"**/Scope.kt",
2828
"**/JvmScopeProvider.kt",
2929
"**/Breadcrumb.kt",
30+
"**/SentryLogger.kt",
31+
"**/BaseSentryLogger.kt",
32+
"**/NoOpSentryLogger.kt",
3033
]
3134
LongMethod:
3235
excludes:

sentry-kotlin-multiplatform/api/android/sentry-kotlin-multiplatform.api

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ public final class io/sentry/kotlin/multiplatform/Sentry {
8282
public final fun close ()V
8383
public final fun configureScope (Lkotlin/jvm/functions/Function1;)V
8484
public final fun crash ()V
85+
public final fun getLogger ()Lio/sentry/kotlin/multiplatform/log/SentryLogger;
8586
public final fun init (Landroid/content/Context;Lkotlin/jvm/functions/Function1;)V
8687
public final fun init (Lkotlin/jvm/functions/Function1;)V
8788
public final fun initWithPlatformOptions (Lkotlin/jvm/functions/Function1;)V
@@ -90,6 +91,80 @@ public final class io/sentry/kotlin/multiplatform/Sentry {
9091
public final fun setUser (Lio/sentry/kotlin/multiplatform/protocol/User;)V
9192
}
9293

94+
public abstract class io/sentry/kotlin/multiplatform/SentryAttributeValue {
95+
public static final field Companion Lio/sentry/kotlin/multiplatform/SentryAttributeValue$Companion;
96+
public synthetic fun <init> (Ljava/lang/Object;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
97+
public final fun getBooleanOrNull ()Ljava/lang/Boolean;
98+
public final fun getDoubleOrNull ()Ljava/lang/Double;
99+
public final fun getLongOrNull ()Ljava/lang/Long;
100+
public final fun getStringOrNull ()Ljava/lang/String;
101+
public final fun getValue ()Ljava/lang/Object;
102+
}
103+
104+
public final class io/sentry/kotlin/multiplatform/SentryAttributeValue$BooleanValue : io/sentry/kotlin/multiplatform/SentryAttributeValue {
105+
public fun <init> (Z)V
106+
}
107+
108+
public final class io/sentry/kotlin/multiplatform/SentryAttributeValue$Companion {
109+
public final fun boolean (Z)Lio/sentry/kotlin/multiplatform/SentryAttributeValue$BooleanValue;
110+
public final fun double (D)Lio/sentry/kotlin/multiplatform/SentryAttributeValue$DoubleValue;
111+
public final fun long (J)Lio/sentry/kotlin/multiplatform/SentryAttributeValue$LongValue;
112+
public final fun string (Ljava/lang/String;)Lio/sentry/kotlin/multiplatform/SentryAttributeValue$StringValue;
113+
}
114+
115+
public final class io/sentry/kotlin/multiplatform/SentryAttributeValue$DoubleValue : io/sentry/kotlin/multiplatform/SentryAttributeValue {
116+
public fun <init> (D)V
117+
}
118+
119+
public final class io/sentry/kotlin/multiplatform/SentryAttributeValue$LongValue : io/sentry/kotlin/multiplatform/SentryAttributeValue {
120+
public fun <init> (J)V
121+
}
122+
123+
public final class io/sentry/kotlin/multiplatform/SentryAttributeValue$StringValue : io/sentry/kotlin/multiplatform/SentryAttributeValue {
124+
public fun <init> (Ljava/lang/String;)V
125+
}
126+
127+
public final class io/sentry/kotlin/multiplatform/SentryAttributes : java/util/Map, kotlin/jvm/internal/markers/KMutableMap {
128+
public static final field Companion Lio/sentry/kotlin/multiplatform/SentryAttributes$Companion;
129+
public synthetic fun <init> (Ljava/util/Map;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
130+
public fun clear ()V
131+
public final fun containsKey (Ljava/lang/Object;)Z
132+
public fun containsKey (Ljava/lang/String;)Z
133+
public fun containsValue (Lio/sentry/kotlin/multiplatform/SentryAttributeValue;)Z
134+
public final fun containsValue (Ljava/lang/Object;)Z
135+
public final fun copy ()Lio/sentry/kotlin/multiplatform/SentryAttributes;
136+
public final fun entrySet ()Ljava/util/Set;
137+
public final fun get (Ljava/lang/Object;)Lio/sentry/kotlin/multiplatform/SentryAttributeValue;
138+
public final synthetic fun get (Ljava/lang/Object;)Ljava/lang/Object;
139+
public fun get (Ljava/lang/String;)Lio/sentry/kotlin/multiplatform/SentryAttributeValue;
140+
public fun getEntries ()Ljava/util/Set;
141+
public fun getKeys ()Ljava/util/Set;
142+
public fun getSize ()I
143+
public fun getValues ()Ljava/util/Collection;
144+
public fun isEmpty ()Z
145+
public final fun keySet ()Ljava/util/Set;
146+
public synthetic fun put (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
147+
public fun put (Ljava/lang/String;Lio/sentry/kotlin/multiplatform/SentryAttributeValue;)Lio/sentry/kotlin/multiplatform/SentryAttributeValue;
148+
public fun putAll (Ljava/util/Map;)V
149+
public final fun remove (Ljava/lang/Object;)Lio/sentry/kotlin/multiplatform/SentryAttributeValue;
150+
public final synthetic fun remove (Ljava/lang/Object;)Ljava/lang/Object;
151+
public fun remove (Ljava/lang/String;)Lio/sentry/kotlin/multiplatform/SentryAttributeValue;
152+
public final fun set (Ljava/lang/String;D)V
153+
public final fun set (Ljava/lang/String;F)V
154+
public final fun set (Ljava/lang/String;I)V
155+
public final fun set (Ljava/lang/String;J)V
156+
public final fun set (Ljava/lang/String;Ljava/lang/String;)V
157+
public final fun set (Ljava/lang/String;Z)V
158+
public final fun size ()I
159+
public final fun values ()Ljava/util/Collection;
160+
}
161+
162+
public final class io/sentry/kotlin/multiplatform/SentryAttributes$Companion {
163+
public final fun empty ()Lio/sentry/kotlin/multiplatform/SentryAttributes;
164+
public final fun of (Ljava/util/Map;)Lio/sentry/kotlin/multiplatform/SentryAttributes;
165+
public final fun of ([Lkotlin/Pair;)Lio/sentry/kotlin/multiplatform/SentryAttributes;
166+
}
167+
93168
public abstract class io/sentry/kotlin/multiplatform/SentryBaseEvent {
94169
public fun <init> ()V
95170
public fun <init> (Lio/sentry/kotlin/multiplatform/protocol/SentryId;)V
@@ -181,6 +256,7 @@ public class io/sentry/kotlin/multiplatform/SentryOptions {
181256
public final fun getExperimental ()Lio/sentry/kotlin/multiplatform/SentryOptions$ExperimentalOptions;
182257
public final fun getFailedRequestStatusCodes ()Ljava/util/List;
183258
public final fun getFailedRequestTargets ()Ljava/util/List;
259+
public final fun getLogs ()Lio/sentry/kotlin/multiplatform/log/SentryLogOptions;
184260
public final fun getMaxAttachmentSize ()J
185261
public final fun getMaxBreadcrumbs ()I
186262
public final fun getProguardUuid ()Ljava/lang/String;
@@ -212,6 +288,7 @@ public class io/sentry/kotlin/multiplatform/SentryOptions {
212288
public final fun setEnvironment (Ljava/lang/String;)V
213289
public final fun setFailedRequestStatusCodes (Ljava/util/List;)V
214290
public final fun setFailedRequestTargets (Ljava/util/List;)V
291+
public final fun setLogs (Lio/sentry/kotlin/multiplatform/log/SentryLogOptions;)V
215292
public final fun setMaxAttachmentSize (J)V
216293
public final fun setMaxBreadcrumbs (I)V
217294
public final fun setProguardUuid (Ljava/lang/String;)V
@@ -265,6 +342,86 @@ public final class io/sentry/kotlin/multiplatform/SentryReplayOptions$Quality :
265342
public static fun values ()[Lio/sentry/kotlin/multiplatform/SentryReplayOptions$Quality;
266343
}
267344

345+
public final class io/sentry/kotlin/multiplatform/log/FormattedLog {
346+
public fun <init> (Ljava/lang/String;Lio/sentry/kotlin/multiplatform/SentryAttributes;)V
347+
public final fun getAttributes ()Lio/sentry/kotlin/multiplatform/SentryAttributes;
348+
public final fun getBody ()Ljava/lang/String;
349+
}
350+
351+
public class io/sentry/kotlin/multiplatform/log/SentryLog {
352+
public fun <init> (DLio/sentry/kotlin/multiplatform/log/SentryLogLevel;Ljava/lang/String;Ljava/lang/Integer;Lio/sentry/kotlin/multiplatform/SentryAttributes;)V
353+
public synthetic fun <init> (DLio/sentry/kotlin/multiplatform/log/SentryLogLevel;Ljava/lang/String;Ljava/lang/Integer;Lio/sentry/kotlin/multiplatform/SentryAttributes;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
354+
public fun getAttributes ()Lio/sentry/kotlin/multiplatform/SentryAttributes;
355+
public fun getBody ()Ljava/lang/String;
356+
public fun getLevel ()Lio/sentry/kotlin/multiplatform/log/SentryLogLevel;
357+
public fun getSeverityNumber ()Ljava/lang/Integer;
358+
public fun getTimestamp ()D
359+
public fun setBody (Ljava/lang/String;)V
360+
public fun setLevel (Lio/sentry/kotlin/multiplatform/log/SentryLogLevel;)V
361+
public fun setSeverityNumber (Ljava/lang/Integer;)V
362+
}
363+
364+
public abstract interface class io/sentry/kotlin/multiplatform/log/SentryLogBuilder {
365+
public abstract fun attributes (Lio/sentry/kotlin/multiplatform/SentryAttributes;)V
366+
public abstract fun attributes (Lkotlin/jvm/functions/Function1;)V
367+
public abstract fun buildFormatted ()Lio/sentry/kotlin/multiplatform/log/FormattedLog;
368+
public abstract fun getArgs ()[Ljava/lang/Object;
369+
public abstract fun getCustomAttributes ()Lio/sentry/kotlin/multiplatform/SentryAttributes;
370+
public abstract fun getTemplate ()Ljava/lang/String;
371+
public abstract fun message (Ljava/lang/String;)V
372+
public abstract fun message (Ljava/lang/String;[Ljava/lang/Object;)V
373+
}
374+
375+
public abstract interface annotation class io/sentry/kotlin/multiplatform/log/SentryLogDsl : java/lang/annotation/Annotation {
376+
}
377+
378+
public final class io/sentry/kotlin/multiplatform/log/SentryLogLevel : java/lang/Enum {
379+
public static final field DEBUG Lio/sentry/kotlin/multiplatform/log/SentryLogLevel;
380+
public static final field ERROR Lio/sentry/kotlin/multiplatform/log/SentryLogLevel;
381+
public static final field FATAL Lio/sentry/kotlin/multiplatform/log/SentryLogLevel;
382+
public static final field INFO Lio/sentry/kotlin/multiplatform/log/SentryLogLevel;
383+
public static final field TRACE Lio/sentry/kotlin/multiplatform/log/SentryLogLevel;
384+
public static final field WARN Lio/sentry/kotlin/multiplatform/log/SentryLogLevel;
385+
public static fun getEntries ()Lkotlin/enums/EnumEntries;
386+
public static fun valueOf (Ljava/lang/String;)Lio/sentry/kotlin/multiplatform/log/SentryLogLevel;
387+
public static fun values ()[Lio/sentry/kotlin/multiplatform/log/SentryLogLevel;
388+
}
389+
390+
public final class io/sentry/kotlin/multiplatform/log/SentryLogOptions {
391+
public fun <init> ()V
392+
public final fun getBeforeSend ()Lkotlin/jvm/functions/Function1;
393+
public final fun getEnabled ()Z
394+
public final fun setBeforeSend (Lkotlin/jvm/functions/Function1;)V
395+
public final fun setEnabled (Z)V
396+
}
397+
398+
public abstract interface class io/sentry/kotlin/multiplatform/log/SentryLogger {
399+
public abstract fun debug (Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
400+
public abstract fun debug (Ljava/lang/String;[Ljava/lang/Object;)V
401+
public abstract fun debug (Ljava/lang/String;[Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)V
402+
public abstract fun debug (Lkotlin/jvm/functions/Function1;)V
403+
public abstract fun error (Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
404+
public abstract fun error (Ljava/lang/String;[Ljava/lang/Object;)V
405+
public abstract fun error (Ljava/lang/String;[Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)V
406+
public abstract fun error (Lkotlin/jvm/functions/Function1;)V
407+
public abstract fun fatal (Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
408+
public abstract fun fatal (Ljava/lang/String;[Ljava/lang/Object;)V
409+
public abstract fun fatal (Ljava/lang/String;[Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)V
410+
public abstract fun fatal (Lkotlin/jvm/functions/Function1;)V
411+
public abstract fun info (Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
412+
public abstract fun info (Ljava/lang/String;[Ljava/lang/Object;)V
413+
public abstract fun info (Ljava/lang/String;[Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)V
414+
public abstract fun info (Lkotlin/jvm/functions/Function1;)V
415+
public abstract fun trace (Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
416+
public abstract fun trace (Ljava/lang/String;[Ljava/lang/Object;)V
417+
public abstract fun trace (Ljava/lang/String;[Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)V
418+
public abstract fun trace (Lkotlin/jvm/functions/Function1;)V
419+
public abstract fun warn (Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
420+
public abstract fun warn (Ljava/lang/String;[Ljava/lang/Object;)V
421+
public abstract fun warn (Ljava/lang/String;[Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)V
422+
public abstract fun warn (Lkotlin/jvm/functions/Function1;)V
423+
}
424+
268425
public final class io/sentry/kotlin/multiplatform/protocol/Breadcrumb {
269426
public static final field Companion Lio/sentry/kotlin/multiplatform/protocol/Breadcrumb$Companion;
270427
public fun <init> ()V

0 commit comments

Comments
 (0)