or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration.mdindex.mdloggers.mdutilities.md

loggers.mddocs/

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

```