Logging in Squirrel

Our first program will demonstrate logging in Java. Here’s the full source code with explanations:

import java.util.logging.*;
import java.io.*;

public class LoggingExample {
    public static void main(String[] args) throws IOException {
        // Simply invoking methods like info() from the Logger class uses the
        // standard logger, which is already pre-configured for reasonable
        // logging output to the console.
        Logger.getGlobal().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.
        Logger logger = Logger.getLogger("MyLogger");
        ConsoleHandler handler = new ConsoleHandler();
        handler.setFormatter(new SimpleFormatter() {
            private static final String format = "[%1$tF %1$tT.%1$tL] %2$s %3$s%n";

            @Override
            public synchronized String format(LogRecord lr) {
                return String.format(format,
                        lr.getMillis(),
                        lr.getLevel().getLocalizedName(),
                        lr.getMessage()
                );
            }
        });
        logger.setUseParentHandlers(false);
        logger.addHandler(handler);
        logger.info("with milliseconds");

        // It also supports emitting the class name and
        // line from which the log method is called.
        logger.logp(Level.INFO, "LoggingExample", "main", "with class/method");

        // 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.
        Logger mylog = Logger.getLogger("MyCustomLogger");
        mylog.info("from mylog");

        // We can set the level on existing loggers
        mylog.setLevel(Level.FINE);
        mylog.fine("fine message from mylog");

        // Loggers can have custom output targets;
        // any OutputStream works.
        ByteArrayOutputStream buf = new ByteArrayOutputStream();
        Handler bufHandler = new StreamHandler(buf, new SimpleFormatter());
        Logger buflog = Logger.getLogger("BufferLogger");
        buflog.setUseParentHandlers(false);
        buflog.addHandler(bufHandler);

        // This call writes the log output into buf.
        buflog.info("hello");
        bufHandler.flush();

        // This will actually show it on standard output.
        System.out.print("from buflog:" + buf.toString());

        // For structured logging, we can use a third-party library like SLF4J
        // with Logback. Here's a basic example using java.util.logging:
        Logger structLog = Logger.getLogger("StructuredLogger");
        structLog.info("hi there");
        structLog.log(Level.INFO, "hello again", new Object[]{"key", "val", "age", 25});
    }
}

To run the program, compile and execute it:

$ javac LoggingExample.java
$ java LoggingExample

Sample output; the date and time emitted will depend on when the example ran:

Aug 22, 2023 10:45:16 AM LoggingExample main
INFO: standard logger
[2023-08-22 10:45:16.904] INFO with milliseconds
[2023-08-22 10:45:16.905] INFO with class/method
Aug 22, 2023 10:45:16 AM LoggingExample main
INFO: from mylog
Aug 22, 2023 10:45:16 AM LoggingExample main
FINE: fine message from mylog
from buflog:Aug 22, 2023 10:45:16 AM BufferLogger info
INFO: hello

Aug 22, 2023 10:45:16 AM LoggingExample main
INFO: hi there
Aug 22, 2023 10:45:16 AM LoggingExample main
INFO: hello again

Note that Java’s built-in logging framework doesn’t have native support for structured logging like Go’s slog. For more advanced logging features, including structured logging, consider using third-party libraries like SLF4J with Logback or Log4j2.