Official Kotlin SDK for LogWard with advanced features: automatic batching, retry logic, circuit breaker, query API, live streaming, and middleware support.
- âś… Automatic batching with configurable size and interval
- âś… Retry logic with exponential backoff
- âś… Circuit breaker pattern for fault tolerance
- âś… Max buffer size with drop policy to prevent memory leaks
- âś… Query API for searching and filtering logs
- âś… Live tail with Server-Sent Events (SSE)
- âś… Trace ID context for distributed tracing
- âś… Global metadata added to all logs
- âś… Structured error serialization
- âś… Internal metrics (logs sent, errors, latency, etc.)
- âś… Spring Boot, Ktor middleware for auto-logging HTTP requests
- âś… Full Kotlin coroutines support with suspend functions
- JVM 11 or higher
- Kotlin 1.9+ (or Java 11+ for Java interop)
- Gradle or Maven
dependencies {
implementation("io.github.logward-dev:logward-sdk-kotlin:0.2.0")
}dependencies {
implementation 'io.github.logward-dev:logward-sdk-kotlin:0.2.0'
}<dependency>
<groupId>io.github.logward-dev</groupId>
<artifactId>logward-sdk-kotlin</artifactId>
<version>0.2.0</version>
</dependency>import dev.logward.sdk.LogWardClient
import dev.logward.sdk.models.LogWardClientOptions
val client = LogWardClient(
LogWardClientOptions(
apiUrl = "http://localhost:8080",
apiKey = "lp_your_api_key_here"
)
)
// Send logs
client.info("api-gateway", "Server started", mapOf("port" to 3000))
client.error("database", "Connection failed", RuntimeException("Timeout"))
// Graceful shutdown (also automatic on JVM shutdown)
runBlocking {
client.close()
}| Option | Type | Default | Description |
|---|---|---|---|
apiUrl |
String |
required | Base URL of your LogWard instance |
apiKey |
String |
required | Project API key (starts with lp_) |
batchSize |
Int |
100 |
Number of logs to batch before sending |
flushInterval |
Duration |
5.seconds |
Interval to auto-flush logs |
| Option | Type | Default | Description |
|---|---|---|---|
maxBufferSize |
Int |
10000 |
Max logs in buffer (prevents memory leak) |
maxRetries |
Int |
3 |
Max retry attempts on failure |
retryDelay |
Duration |
1.seconds |
Initial retry delay (exponential backoff) |
circuitBreakerThreshold |
Int |
5 |
Failures before opening circuit |
circuitBreakerReset |
Duration |
30.seconds |
Time before retrying after circuit opens |
enableMetrics |
Boolean |
true |
Track internal metrics |
debug |
Boolean |
false |
Enable debug logging to console |
globalMetadata |
Map<String, Any> |
emptyMap() |
Metadata added to all logs |
autoTraceId |
Boolean |
false |
Auto-generate trace IDs for logs |
import kotlin.time.Duration.Companion.seconds
import kotlin.time.Duration.Companion.milliseconds
val client = LogWardClient(
LogWardClientOptions(
apiUrl = "http://localhost:8080",
apiKey = "lp_your_api_key_here",
// Batching
batchSize = 100,
flushInterval = 5.seconds,
// Buffer management
maxBufferSize = 10000,
// Retry with exponential backoff (1s → 2s → 4s)
maxRetries = 3,
retryDelay = 1.seconds,
// Circuit breaker
circuitBreakerThreshold = 5,
circuitBreakerReset = 30.seconds,
// Metrics & debugging
enableMetrics = true,
debug = true,
// Global context
globalMetadata = mapOf(
"env" to System.getenv("APP_ENV"),
"version" to "1.0.0",
"hostname" to System.getenv("HOSTNAME")
),
// Auto trace IDs
autoTraceId = false
)
)client.debug("service-name", "Debug message")
client.info("service-name", "Info message", mapOf("userId" to 123))
client.warn("service-name", "Warning message")
client.error("service-name", "Error message", mapOf("custom" to "data"))
client.critical("service-name", "Critical message")The SDK automatically serializes Throwable objects:
try {
throw RuntimeException("Database timeout")
} catch (e: Exception) {
// Automatically serializes error with stack trace
client.error("database", "Query failed", e)
}Generated log metadata:
{
"error": {
"name": "RuntimeException",
"message": "Database timeout",
"stack": "..."
}
}Track requests across services with trace IDs.
client.setTraceId("request-123")
client.info("api", "Request received")
client.info("database", "Querying users")
client.info("api", "Response sent")
client.setTraceId(null) // Clear contextclient.withTraceId("request-456") {
client.info("api", "Processing in context")
client.warn("cache", "Cache miss")
}
// Trace ID automatically restored after blockclient.withNewTraceId {
client.info("worker", "Background job started")
client.info("worker", "Job completed")
}Search and retrieve logs programmatically.
import dev.logward.sdk.models.QueryOptions
import dev.logward.sdk.enums.LogLevel
import java.time.Instant
import java.time.temporal.ChronoUnit
val result = client.query(
QueryOptions(
service = "api-gateway",
level = LogLevel.ERROR,
from = Instant.now().minus(24, ChronoUnit.HOURS),
to = Instant.now(),
limit = 100,
offset = 0
)
)
println("Found ${result.total} logs")
result.logs.forEach { log ->
println(log)
}val result = client.query(
QueryOptions(
q = "timeout",
limit = 50
)
)val logs = client.getByTraceId("trace-123")
println("Trace has ${logs.size} logs")Stream logs in real-time using Server-Sent Events.
val cleanup = client.stream(
onLog = { log ->
println("[${log.time}] ${log.level}: ${log.message}")
},
onError = { error ->
println("Stream error: ${error.message}")
},
filters = mapOf(
"service" to "api-gateway",
"level" to "error"
)
)
// Stop streaming when done
Thread.sleep(60000)
cleanup()Track SDK performance and health.
val metrics = client.getMetrics()
println("Logs sent: ${metrics.logsSent}")
println("Logs dropped: ${metrics.logsDropped}")
println("Errors: ${metrics.errors}")
println("Retries: ${metrics.retries}")
println("Avg latency: ${metrics.avgLatencyMs}ms")
println("Circuit breaker trips: ${metrics.circuitBreakerTrips}")
// Get circuit breaker state
println(client.getCircuitBreakerState()) // CLOSED, OPEN, or HALF_OPEN
// Reset metrics
client.resetMetrics()LogWard provides ready-to-use middleware for popular frameworks.
Automatically log HTTP requests and responses in Ktor applications.
import dev.logward.sdk.middleware.LogWardPlugin
import io.ktor.server.application.*
fun Application.module() {
install(LogWardPlugin) {
apiUrl = "http://localhost:8080"
apiKey = "lp_your_api_key_here"
serviceName = "ktor-app"
// Optional configuration
logErrors = true
skipHealthCheck = true
skipPaths = setOf("/metrics", "/internal")
// Client options
batchSize = 100
flushInterval = kotlin.time.Duration.parse("5s")
enableMetrics = true
globalMetadata = mapOf("env" to "production")
// Enable request/response logging
logRequests = true // Log incoming requests, e.g., method, path, headers
logResponses = true // Log outgoing responses stats, e.g., status code, duration
// Customize metadata extraction from calls, e.g., add user ID, session info, etc.
extractMetadataFromIncomingCall = { call, traceId ->
mapOf(
"method" to call.request.httpMethod.value,
"path" to call.request.uri,
"remoteHost" to call.request.local.remoteHost,
"traceId" to traceId
)
}
// Customize metadata extraction from responses, e.g., status code, time elapsed, etc.
extractMetadataFromOutgoingContent = { call, traceId, duration ->
val statusValue = call.response.status()?.value
val metadata = mutableMapOf(
"method" to call.request.httpMethod.value,
"path" to call.request.uri,
"status" to (statusValue ?: 0),
"duration" to (duration ?: 0L),
"traceId" to traceId
)
metadata
}
// Extract trace ID from incoming requests (if any)
// useful for using in combination with the CallId plugin
extractTraceIdFromCall = { call ->
call.request.headers["X-Trace-ID"]
}
// Whether to use the default interceptor to propagate trace IDs in call context
// if you plan to access the client manually in routes
useDefaultInterceptor = true
}
}See full example: examples/middleware/ktor/KtorExample.kt
You can access the LogWard client directly in your routes for custom logging:
import dev.logward.sdk.middleware.LogWardClientKey
routing {
get("/api/custom") {
// Get the client from application attributes
val client = call.application.attributes[LogWardClientKey]
// Log custom messages
client.info(
"my-service",
"Custom business logic executed",
mapOf("userId" to 123, "action" to "custom_operation")
)
call.respondText("OK")
}
}Automatically log HTTP requests and responses in Spring Boot applications.
import dev.logward.sdk.LogWardClient
import dev.logward.sdk.middleware.LogWardInterceptor
import dev.logward.sdk.models.LogWardClientOptions
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.web.servlet.config.annotation.InterceptorRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
@Configuration
class LogWardConfig : WebMvcConfigurer {
@Bean
fun logWardClient() = LogWardClient(
LogWardClientOptions(
apiUrl = "http://localhost:8080",
apiKey = "lp_your_api_key_here"
)
)
@Bean
fun logWardInterceptor(client: LogWardClient) = LogWardInterceptor(
client = client,
serviceName = "spring-boot-app",
logRequests = true,
logResponses = true,
skipHealthCheck = true
)
override fun addInterceptors(registry: InterceptorRegistry) {
registry.addInterceptor(logWardInterceptor(logWardClient()))
}
}See full example: examples/middleware/spring-boot/SpringBootExample.kt
Automatically log HTTP requests and responses in Jakarta Servlet applications (Tomcat, Jetty, etc.).
import dev.logward.sdk.LogWardClient
import dev.logward.sdk.middleware.LogWardFilter
import dev.logward.sdk.models.LogWardClientOptions
// Create client
val client = LogWardClient(
LogWardClientOptions(
apiUrl = "http://localhost:8080",
apiKey = "lp_your_api_key_here"
)
)
// Create filter
val filter = LogWardFilter(
client = client,
serviceName = "servlet-app",
logRequests = true,
logResponses = true,
skipHealthCheck = true
)
// Add to servlet context
servletContext.addFilter("logWard", filter)Or via web.xml:
<filter>
<filter-name>LogWardFilter</filter-name>
<filter-class>dev.logward.sdk.middleware.LogWardFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>LogWardFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>See full example: examples/middleware/jakarta-servlet/JakartaServletExample.kt
MIT
Contributions are welcome! Please open an issue or PR on GitHub.
- Documentation: https://logward.dev/docs
- Issues: GitHub Issues