LogTide

Kotlin SDK

Official Kotlin SDK for LogTide with coroutines support, automatic batching, retry logic, circuit breaker, query API, and middleware integrations for Ktor, Spring Boot, and Jakarta Servlet.

Installation

Gradle (Kotlin DSL)

dependencies {
  implementation("io.github.logtide-dev:logtide-sdk-kotlin:0.2.0")
}

Gradle (Groovy)

dependencies {
  implementation 'io.github.logtide-dev:logtide-sdk-kotlin:0.2.0'
}

Maven

<dependency>
  <groupId>io.github.logtide-dev</groupId>
  <artifactId>logtide-sdk-kotlin</artifactId>
  <version>0.2.0</version>
</dependency>

Quick Start

import dev.logtide.sdk.LogTideClient
import dev.logtide.sdk.models.LogTideClientOptions

val client = LogTideClient(
  LogTideClientOptions(
      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()
}

Features

  • ✅ 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)
  • ✅ Coroutines support for async operations
  • ✅ Ktor Plugin for automatic HTTP logging
  • ✅ Spring Boot Interceptor for automatic HTTP logging
  • ✅ Jakarta Servlet Filter for automatic HTTP logging
  • ✅ Kotlin Multiplatform ready

Configuration

val client = LogTideClient(LogTideClientOptions(
  // Required
  apiUrl = "http://localhost:8080",
  apiKey = "lp_your_api_key",
  
  // Optional - Performance
  batchSize = 100,              // Max logs per batch (default: 100)
  flushInterval = 5.seconds,    // Flush interval (default: 5s)
  maxBufferSize = 10000,        // Max logs in buffer (default: 10000)
  
  // Optional - Reliability
  maxRetries = 3,               // Max retry attempts (default: 3)
  retryDelay = 1.seconds,       // Initial retry delay (default: 1s)
  circuitBreakerThreshold = 5,  // Failures before circuit opens (default: 5)
  circuitBreakerTimeout = 60.seconds, // Circuit reset timeout (default: 60s)
  
  // Optional - Metadata
  globalMetadata = mapOf(       // Added to all logs
      "environment" to "production",
      "version" to "1.0.0"
  ),
  
  // Optional - Features
  enableMetrics = true,         // Enable metrics collection (default: true)
  debug = false                 // Enable debug logging (default: false)
))

Logging Methods

Basic Logging

// Log levels: debug, info, warn, error, critical
client.debug("service-name", "Debug message", mapOf("detail" to "value"))
client.info("api-gateway", "Request received", mapOf("method" to "GET", "path" to "/users"))
client.warn("cache", "Cache miss", mapOf("key" to "user:123"))
client.error("database", "Query failed", mapOf("query" to "SELECT *"))
client.critical("system", "Out of memory", mapOf("used" to "95%"))

Error Logging with Auto-Serialization

// Automatically serializes exception details
try {
  database.connect()
} catch (e: SQLException) {
  client.error("database", "Connection failed", e)
  // Automatically includes: exception type, message, and stack trace
}

Trace ID Context

// Manual trace ID
client.withTraceId("550e8400-e29b-41d4-a716-446655440000") {
  client.info("api", "Processing request")
  client.info("db", "Query executed")
}

// Scoped trace ID (coroutines)
coroutineScope {
  client.withTraceId("my-trace-id") {
      launch { client.info("worker-1", "Task started") }
      launch { client.info("worker-2", "Task started") }
  }
}

// Auto-generated trace ID
client.withTraceId { // generates UUID
  client.info("api", "Request processing")
}

Middleware Integration

Ktor Plugin

Automatically log HTTP requests and responses in Ktor applications.

import dev.logtide.sdk.middleware.LogTidePlugin
import io.ktor.server.application.*

fun Application.module() {
  install(LogTidePlugin) {
      apiUrl = "http://localhost:8080"
      apiKey = "lp_your_api_key_here"
      serviceName = "ktor-app"

      // Optional configuration
      logRequests = true
      logResponses = true
      logErrors = true
      skipHealthCheck = true
      skipPaths = setOf("/metrics", "/internal")

      // Client options
      batchSize = 100
      flushInterval = 5.seconds
      enableMetrics = true
      globalMetadata = mapOf("env" to "production")
  }
}

// Access client manually in routes
routing {
  get("/api/custom") {
      val client = call.application.attributes[LogTideClientKey]
      client.info("my-service", "Custom business logic executed",
          mapOf("userId" to 123, "action" to "custom_operation"))
      call.respondText("OK")
  }
}

Spring Boot Interceptor

Automatically log HTTP requests and responses in Spring Boot applications.

import dev.logtide.sdk.LogTideClient
import dev.logtide.sdk.middleware.LogTideInterceptor
import dev.logtide.sdk.models.LogTideClientOptions
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 LogTideConfig : WebMvcConfigurer {

  @Bean
  fun logTideClient() = LogTideClient(
      LogTideClientOptions(
          apiUrl = "http://localhost:8080",
          apiKey = "lp_your_api_key_here"
      )
  )

  @Bean
  fun logTideInterceptor(client: LogTideClient) = LogTideInterceptor(
      client = client,
      serviceName = "spring-boot-app",
      logRequests = true,
      logResponses = true,
      skipHealthCheck = true
  )

  override fun addInterceptors(registry: InterceptorRegistry) {
      registry.addInterceptor(logTideInterceptor(logTideClient()))
  }
}

Jakarta Servlet Filter

Automatically log HTTP requests and responses in Jakarta Servlet applications (Tomcat, Jetty, etc.).

import dev.logtide.sdk.LogTideClient
import dev.logtide.sdk.middleware.LogTideFilter
import dev.logtide.sdk.models.LogTideClientOptions

// Create client
val client = LogTideClient(
  LogTideClientOptions(
      apiUrl = "http://localhost:8080",
      apiKey = "lp_your_api_key_here"
  )
)

// Create filter
val filter = LogTideFilter(
  client = client,
  serviceName = "servlet-app",
  logRequests = true,
  logResponses = true,
  skipHealthCheck = true
)

// Add to servlet context
servletContext.addFilter("logTide", filter)

Query API

Basic Query

// Search logs
val result = client.query(
  service = "api-gateway",
  level = "error",
  from = "2025-01-15T00:00:00Z",
  to = "2025-01-15T23:59:59Z",
  limit = 100
)

println("Found ${result.total} error logs")
result.logs.forEach { log ->
  println("[${log.time}] ${log.message}")
}
val result = client.query(
  q = "timeout OR connection",  // Full-text search
  level = "error",
  limit = 50
)

Get Logs by Trace ID

val logs = client.getLogsByTraceId(
  traceId = "550e8400-e29b-41d4-a716-446655440000"
)

// Returns all logs with the same trace_id
logs.forEach { log ->
  println("[${log.service}] ${log.message}")
}

Live Streaming (SSE)

// Stream logs in real-time
client.liveTail(
  service = "api-gateway",
  level = "error"
) { log ->
  // Called for each new log
  println("[${log.time}] ${log.message}")
}

// Stream will automatically reconnect on connection loss

Metrics

// Get internal metrics
val metrics = client.getMetrics()

println("Logs sent: ${metrics.logsSent}")
println("Logs failed: ${metrics.logsFailed}")
println("Avg latency: ${metrics.avgLatencyMs}ms")
println("Circuit breaker state: ${metrics.circuitBreakerState}")

Best Practices

1. Use Middleware for HTTP Logging
Leverage built-in middleware plugins (Ktor, Spring Boot, Jakarta Servlet) for automatic HTTP request/response logging instead of manual instrumentation.
2. Use Coroutines for Async
Leverage Kotlin coroutines for non-blocking log operations in high-concurrency applications.
3. Graceful Shutdown
Always call client.close() on shutdown to ensure buffered logs are flushed before JVM terminates. The SDK also registers an automatic shutdown hook.
4. Use Global Metadata
Set global metadata (environment, version) at initialization to avoid repeating it in every log call.