0
# Loggers
1
2
Logger interface and platform-specific implementations for outputting HTTP logging information across different platforms and environments.
3
4
## Capabilities
5
6
### Logger Interface
7
8
The core logging interface that all logger implementations must implement.
9
10
```kotlin { .api }
11
/**
12
* HttpClient Logger interface
13
*/
14
interface Logger {
15
/**
16
* Add message to log
17
* @param message The message to log
18
*/
19
fun log(message: String)
20
21
companion object
22
}
23
```
24
25
**Usage Examples:**
26
27
```kotlin
28
// Custom logger implementation
29
class MyCustomLogger : Logger {
30
override fun log(message: String) {
31
// Write to file, database, remote service, etc.
32
println("HTTP: $message")
33
}
34
}
35
36
// Use custom logger
37
install(Logging) {
38
logger = MyCustomLogger()
39
}
40
```
41
42
### Built-in Logger Implementations
43
44
Platform-specific logger implementations provided by the library.
45
46
```kotlin { .api }
47
/**
48
* Default logger to use (platform-specific implementation)
49
* - JVM: Uses SLF4J LoggerFactory with HttpClient class as logger name
50
* - JS/WASM: Uses Logger.SIMPLE
51
* - iOS/Native: Uses Logger.SIMPLE
52
*/
53
expect val Logger.Companion.DEFAULT: Logger
54
55
/**
56
* Logger using println for console output
57
*/
58
val Logger.Companion.SIMPLE: Logger
59
60
/**
61
* Empty logger for testing purposes (no output)
62
*/
63
val Logger.Companion.EMPTY: Logger
64
```
65
66
**Usage Examples:**
67
68
```kotlin
69
install(Logging) {
70
logger = Logger.DEFAULT // Platform-appropriate default
71
logger = Logger.SIMPLE // Console output on all platforms
72
logger = Logger.EMPTY // No output (useful for tests)
73
}
74
```
75
76
### JVM-Specific Loggers
77
78
Additional logger implementations available only on JVM platforms.
79
80
```kotlin { .api }
81
/**
82
* Android Logger: Logs to the Logcat on Android if the SLF4J provider isn't found.
83
* Otherwise, uses the Logger.DEFAULT.
84
* Breaks up long log messages that would be truncated by Android's max log
85
* length of 4068 characters.
86
*/
87
val Logger.Companion.ANDROID: Logger
88
89
/**
90
* A Logger that breaks up log messages into multiple logs no longer than maxLength
91
* @param maxLength max length allowed for a log message (default: 4000)
92
* @param minLength if log message is longer than maxLength, attempt to break the log
93
* message at a new line between minLength and maxLength if one exists (default: 3000)
94
* @param delegate underlying logger to use (default: Logger.DEFAULT)
95
*/
96
class MessageLengthLimitingLogger(
97
private val maxLength: Int = 4000,
98
private val minLength: Int = 3000,
99
private val delegate: Logger = Logger.DEFAULT
100
) : Logger {
101
override fun log(message: String)
102
}
103
```
104
105
**Usage Examples:**
106
107
```kotlin
108
// Android-optimized logging
109
install(Logging) {
110
logger = Logger.ANDROID
111
}
112
113
// Custom message length limiting
114
install(Logging) {
115
logger = MessageLengthLimitingLogger(
116
maxLength = 2000,
117
minLength = 1500,
118
delegate = Logger.SIMPLE
119
)
120
}
121
122
// Chain loggers for complex scenarios
123
class MultiLogger(private val loggers: List<Logger>) : Logger {
124
override fun log(message: String) {
125
loggers.forEach { it.log(message) }
126
}
127
}
128
129
install(Logging) {
130
logger = MultiLogger(listOf(
131
Logger.ANDROID,
132
MessageLengthLimitingLogger(delegate = Logger.SIMPLE)
133
))
134
}
135
```
136
137
## Platform-Specific Behavior
138
139
### JVM Platform
140
141
On JVM, `Logger.DEFAULT` uses SLF4J integration:
142
143
```kotlin
144
// Automatic SLF4J integration
145
val client = HttpClient(CIO) {
146
install(Logging) {
147
logger = Logger.DEFAULT // Uses SLF4J LoggerFactory
148
}
149
}
150
```
151
152
Dependencies for SLF4J support:
153
```kotlin
154
dependencies {
155
implementation("org.slf4j:slf4j-api:1.7.36")
156
implementation("ch.qos.logback:logback-classic:1.2.11") // or other SLF4J implementation
157
}
158
```
159
160
### Android Platform
161
162
On Android, `Logger.ANDROID` provides smart Logcat integration:
163
164
```kotlin
165
val client = HttpClient(CIO) {
166
install(Logging) {
167
logger = Logger.ANDROID // Automatically detects Android environment
168
}
169
}
170
```
171
172
Features:
173
- Automatically uses Logcat when available
174
- Falls back to SLF4J if configured
175
- Breaks up long messages to avoid Android's 4068-character limit
176
- Uses "Ktor Client" as the log tag
177
178
### JavaScript/WASM Platform
179
180
```kotlin
181
val client = HttpClient(JS) {
182
install(Logging) {
183
logger = Logger.DEFAULT // Uses console.log equivalent
184
}
185
}
186
```
187
188
### Native/iOS Platform
189
190
```kotlin
191
val client = HttpClient(Darwin) {
192
install(Logging) {
193
logger = Logger.DEFAULT // Uses platform-appropriate logging
194
}
195
}
196
```
197
198
## Custom Logger Examples
199
200
### File Logger
201
202
```kotlin
203
class FileLogger(private val filePath: String) : Logger {
204
override fun log(message: String) {
205
File(filePath).appendText("${System.currentTimeMillis()}: $message\n")
206
}
207
}
208
209
install(Logging) {
210
logger = FileLogger("/tmp/http.log")
211
}
212
```
213
214
### Structured JSON Logger
215
216
```kotlin
217
import kotlinx.serialization.json.*
218
219
class JsonLogger : Logger {
220
private val json = Json { prettyPrint = true }
221
222
override fun log(message: String) {
223
val logEntry = buildJsonObject {
224
put("timestamp", System.currentTimeMillis())
225
put("level", "HTTP")
226
put("message", message)
227
}
228
println(json.encodeToString(logEntry))
229
}
230
}
231
232
install(Logging) {
233
logger = JsonLogger()
234
}
235
```
236
237
### Conditional Logger
238
239
```kotlin
240
class ConditionalLogger(
241
private val condition: () -> Boolean,
242
private val delegate: Logger = Logger.DEFAULT
243
) : Logger {
244
override fun log(message: String) {
245
if (condition()) {
246
delegate.log(message)
247
}
248
}
249
}
250
251
install(Logging) {
252
logger = ConditionalLogger(
253
condition = { BuildConfig.DEBUG }, // Only log in debug builds
254
delegate = Logger.SIMPLE
255
)
256
}
257
```
258
259
### Remote Logger
260
261
```kotlin
262
class RemoteLogger(
263
private val endpoint: String,
264
private val httpClient: HttpClient
265
) : Logger {
266
override fun log(message: String) {
267
// Note: This creates a logging loop risk - use carefully
268
runBlocking {
269
try {
270
httpClient.post(endpoint) {
271
setBody(message)
272
}
273
} catch (e: Exception) {
274
// Fallback to console to avoid infinite loops
275
println("Failed to send log: $message")
276
}
277
}
278
}
279
}
280
```
281
282
## Logger Performance Considerations
283
284
### Asynchronous Logging
285
286
```kotlin
287
class AsyncLogger(private val delegate: Logger) : Logger {
288
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
289
290
override fun log(message: String) {
291
scope.launch {
292
delegate.log(message)
293
}
294
}
295
}
296
297
install(Logging) {
298
logger = AsyncLogger(Logger.DEFAULT)
299
}
300
```
301
302
### Buffered Logging
303
304
```kotlin
305
class BufferedLogger(
306
private val delegate: Logger,
307
private val bufferSize: Int = 100
308
) : Logger {
309
private val buffer = mutableListOf<String>()
310
311
@Synchronized
312
override fun log(message: String) {
313
buffer.add(message)
314
if (buffer.size >= bufferSize) {
315
flush()
316
}
317
}
318
319
@Synchronized
320
private fun flush() {
321
if (buffer.isNotEmpty()) {
322
delegate.log(buffer.joinToString("\n"))
323
buffer.clear()
324
}
325
}
326
}
327
```