Logging in Scala

Here’s the translation of the Go logging example to Scala, formatted in Markdown suitable for Hugo:

import java.util.logging._
import java.io.ByteArrayOutputStream
import java.time.Instant

object LoggingExample {
  def main(args: Array[String]): Unit = {
    // Simply invoking methods like info from the Logger class uses the
    // standard logger, which is already pre-configured for reasonable
    // logging output to System.err.
    val logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME)
    logger.info("standard logger")

    // Loggers can be configured with handlers and formatters to set
    // their output format. We can change its format to emit time with
    // millisecond accuracy, for example.
    val handler = new ConsoleHandler()
    val formatter = new SimpleFormatter() {
      override def format(record: LogRecord): String = {
        val instant = Instant.ofEpochMilli(record.getMillis)
        s"${instant} ${record.getLevel}: ${record.getMessage}\n"
      }
    }
    handler.setFormatter(formatter)
    logger.setUseParentHandlers(false)
    logger.addHandler(handler)
    logger.info("with milliseconds")

    // It may be useful to create a custom logger and
    // pass it around. When creating a new logger, we
    // can set a name to distinguish its output from other loggers.
    val myLogger = Logger.getLogger("MyLogger")
    myLogger.info("from myLogger")

    // We can set the name on existing loggers as well.
    myLogger.setName("OhMyLogger")
    myLogger.info("from renamed myLogger")

    // Loggers can have custom output targets;
    // any OutputStream works.
    val baos = new ByteArrayOutputStream()
    val customHandler = new StreamHandler(baos, new SimpleFormatter())
    val bufLogger = Logger.getLogger("BufferLogger")
    bufLogger.setUseParentHandlers(false)
    bufLogger.addHandler(customHandler)

    // This call writes the log output into baos.
    bufLogger.info("hello")
    customHandler.flush()

    // This will actually show it on standard output.
    println(s"from bufLogger: ${baos.toString}")

    // Scala doesn't have a direct equivalent to Go's slog package for structured logging.
    // However, we can use a third-party library like Logback with SLF4J for more advanced logging features.
    // Here's a simple example of how you might log with additional context:
    logger.info("hello again with context: key=val, age=25")
  }
}

This Scala code demonstrates various logging techniques, mirroring the functionality of the original Go example as closely as possible. Here’s a breakdown of the main points:

  1. We use Java’s built-in java.util.logging package, which is readily available in Scala.
  2. The standard logger is accessed using Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).
  3. We demonstrate how to customize the log format using a custom SimpleFormatter.
  4. Creating and renaming loggers is shown.
  5. Custom output targets are demonstrated using a ByteArrayOutputStream.
  6. While Scala doesn’t have a direct equivalent to Go’s slog package for structured logging, we show how to log with additional context using a simple string format. For more advanced structured logging, you would typically use a third-party library like Logback with SLF4J.

To run this program, save it as LoggingExample.scala and use the Scala compiler:

$ scalac LoggingExample.scala
$ scala LoggingExample

The output will be similar to the original Go example, with timestamps and log levels included in the output.

Note that for more advanced logging features, especially structured logging, Scala projects often use external libraries like Logback, Log4j, or scala-logging, which provide more sophisticated logging capabilities.