scalabeginner
Structured Logging Patterns
Implement structured logging in Scala: MDC context, log levels, JSON formatting, and async logging.
scalaPress ⌘/Ctrl + Shift + C to copy
import java.time.{Instant, ZoneOffset}
import java.time.format.DateTimeFormatter
// Simple structured logger
enum Level(val value: Int, val label: String):
case DEBUG extends Level(0, "DEBUG")
case INFO extends Level(1, "INFO")
case WARN extends Level(2, "WARN")
case ERROR extends Level(3, "ERROR")
case class LogEntry(
timestamp: Instant,
level: Level,
message: String,
context: Map[String, String] = Map.empty,
error: Option[Throwable] = None
):
def toJson: String =
val base = Map(
"timestamp" -> timestamp.toString,
"level" -> level.label,
"message" -> message
)
val withCtx = if context.nonEmpty then
base ++ context.map((k, v) => s"ctx.$k" -> v)
else base
val withErr = error.map(e =>
withCtx + ("error" -> e.getMessage) + ("error.type" -> e.getClass.getSimpleName)
).getOrElse(withCtx)
withErr.map((k, v) => s""""$k":"$v"""").mkString("{", ",", "}")
def toText: String =
val ts = DateTimeFormatter.ofPattern("HH:mm:ss.SSS")
.format(timestamp.atOffset(ZoneOffset.UTC))
val ctx = if context.nonEmpty then
context.map((k, v) => s"$k=$v").mkString(" [", ", ", "]")
else ""
val err = error.map(e => s" | ${e.getClass.getSimpleName}: ${e.getMessage}").getOrElse("")
s"$ts ${level.label.padTo(5, ' ')} $message$ctx$err"
// Logger with context
class Logger(name: String, minLevel: Level = Level.DEBUG):
private var context = Map.empty[String, String]
def withContext(ctx: (String, String)*): Logger =
val l = Logger(name, minLevel)
l.context = this.context ++ ctx.toMap
l
private def log(level: Level, msg: String, error: Option[Throwable] = None): Unit =
if level.value >= minLevel.value then
val entry = LogEntry(Instant.now(), level, msg, context + ("logger" -> name), error)
println(entry.toText)
def debug(msg: String): Unit = log(Level.DEBUG, msg)
def info(msg: String): Unit = log(Level.INFO, msg)
def warn(msg: String): Unit = log(Level.WARN, msg)
def error(msg: String): Unit = log(Level.ERROR, msg)
def error(msg: String, cause: Throwable): Unit = log(Level.ERROR, msg, Some(cause))
// MDC-style context
object MDC:
private val context = ThreadLocal.withInitial[Map[String, String]](() => Map.empty)
def put(key: String, value: String): Unit =
context.set(context.get() + (key -> value))
def get(key: String): Option[String] = context.get().get(key)
def getAll: Map[String, String] = context.get()
def clear(): Unit = context.set(Map.empty)
def withContext[A](ctx: (String, String)*)(block: => A): A =
val old = context.get()
context.set(old ++ ctx.toMap)
try block
finally context.set(old)
@main def run(): Unit =
val log = Logger("App")
log.info("Application starting")
log.debug("Loading configuration")
log.warn("Config file not found, using defaults")
// With context
val reqLog = log.withContext("requestId" -> "req-123", "userId" -> "user-42")
reqLog.info("Processing request")
reqLog.debug("Fetching data")
reqLog.info("Request completed")
// Error logging
try
throw RuntimeException("Connection refused")
catch case e: RuntimeException =>
log.error("Database connection failed", e)
// MDC
MDC.withContext("traceId" -> "abc-123") {
println(s"\nMDC traceId: ${MDC.get("traceId")}")
MDC.put("spanId", "span-1")
println(s"MDC all: ${MDC.getAll}")
}
println(s"MDC after: ${MDC.getAll}") // cleaned up
// JSON format demo
val entry = LogEntry(
Instant.now(), Level.ERROR, "Something went wrong",
Map("service" -> "api", "endpoint" -> "/users"),
Some(RuntimeException("timeout"))
)
println(s"\nJSON: ${entry.toJson}")Use Cases
- Application observability
- Request tracing with context
- Structured log aggregation
Tags
Related Snippets
Similar patterns you can reuse in the same workflow.
typescriptbeginner
Structured JSON Logger
Build a structured logger with log levels, context, child loggers, and JSON output for Node.js services.
Best for: Application logging for production
#nodejs#logging
typescriptbeginner
Structured Request Logger Middleware
Express middleware that logs request/response details as structured JSON with timing information.
Best for: API monitoring
#express#logging
pythonintermediate
Structured Logging with structlog
Configure structlog for JSON-formatted structured logging with request context, timestamps, and log levels.
Best for: Application logging
#logging#structlog
pythonbeginner
Structured Logging for Data Pipelines
Use Loguru to emit structured JSON logs with contextual fields from ETL pipeline stages.
Best for: pipeline observability
#loguru#logging