com.mastfrog:bunyan-java

Shared configuration for Giulius, Acteur and related projects

License

License

MIT
Categories

Categories

Java Languages
GroupId

GroupId

com.mastfrog
ArtifactId

ArtifactId

bunyan-java
Last Version

Last Version

2.6.13
Release Date

Release Date

Type

Type

jar
Description

Description

Shared configuration for Giulius, Acteur and related projects
Project URL

Project URL

https://github.com/timboudreau/bunyan-java
Project Organization

Project Organization

Mastfrog Technologies
Source Code Management

Source Code Management

https://github.com/timboudreau/bunyan-java.git

Download bunyan-java

How to add to project

<!-- https://jarcasting.com/artifacts/com.mastfrog/bunyan-java/ -->
<dependency>
    <groupId>com.mastfrog</groupId>
    <artifactId>bunyan-java</artifactId>
    <version>2.6.13</version>
</dependency>
// https://jarcasting.com/artifacts/com.mastfrog/bunyan-java/
implementation 'com.mastfrog:bunyan-java:2.6.13'
// https://jarcasting.com/artifacts/com.mastfrog/bunyan-java/
implementation ("com.mastfrog:bunyan-java:2.6.13")
'com.mastfrog:bunyan-java:jar:2.6.13'
<dependency org="com.mastfrog" name="bunyan-java" rev="2.6.13">
  <artifact name="bunyan-java" type="jar" />
</dependency>
@Grapes(
@Grab(group='com.mastfrog', module='bunyan-java', version='2.6.13')
)
libraryDependencies += "com.mastfrog" % "bunyan-java" % "2.6.13"
[com.mastfrog/bunyan-java "2.6.13"]

Dependencies

compile (4)

Group / Artifact Type Version
com.mastfrog : injection-reflection-indexer jar 2.6.13
com.mastfrog : giulius jar 2.6.13
com.fasterxml.jackson.core : jackson-databind jar 2.11.0
com.mastfrog : jackson jar 2.6.13

test (3)

Group / Artifact Type Version
com.mastfrog : giulius-tests jar 2.6.13
com.mastfrog : bunyan-parse jar 2.6.13
junit : junit jar 4.13

Project Modules

There are no modules declared in this project.

bunyan-java (original - obsoleted by bunyan-java-v2)

This project has been replaced with bunyan-java-v2 which simplifies its API which should be used instead

An opinionated implementation of a simple logger in Java whose output is compatible with bunyan, the excellent JSON logger for NodeJS.

Uses Guice for injection, and has a fluent interface and support for using @Named to inject named loggers. Uses AutoCloseable to enable a log record to be appended to, builder-style, and automatically written when its close() method is implicitly or explicitly called.

    class LogUser {
       private final Logger logger;
       LogUser(@Named("whatzit") Logger logger) {
          try (Log<Debug> log = logger.debug("someTask")) {
              log.add("append to the message");
              log.add(someObject);
              log.add("foo", "bar");
          }
       }
    }

File or console logging or both are available, others can be implemented.

Dependencies

Guice, Giulius, Jackson

You do not have to use Giulius, but the default LogWriter implementation expects to find an instance of Settings bound. If you want to use plain Guice, you'll need to implement LogWriter or provide a Settings.

It is also useful to include giulius-jackson's JacksonModule, which will automatically find and bind a Jackson module that adds an improved serializer for Throwables.

Integration With Logging Frameworks

Most logging frameworks have a concept of "appenders" or "handlers" which write output to some destination. Several subprojects here can be used to install bunyan-java as a logging destination.

  • bunyan-java-log4j-appender - an appender for Log4J 1.x
  • bunyan-java-util-logging - a Handler implementation for the JDK's logging library
  • bunyan-log4j2-appender - an appender for Log4J 2.x - pass -Dlog4j.configurationFactory=com.mastfrog.bunyan.log4j2.appender.BunyanConfigurationFactory to use

Each of these projects contains a Guice module which connects the logger to bunyan-java. If you use these adapters you must also install the corresponding Guice module - logging prior to bunyan-java being initialized is buffered and output to bunyan-java on initialization; if bunyan-java never gets initialized, all logging will be buffered in-memory until there is no more memory. For each framework, the module optionally (constructor argument) will attempt to reinitialize the logging framework to use bunyan-java with no configuration files needed. This is great to get the basics working, but to ensure all logging goes through bunyan-java, configure your logging framework of choice appropriately (usually system properties or files on disk or some combination thereof).

Note that java.util.logging and Log4J are text-oriented as most traditional Java logging is, meaning that their output is much less rich than you can get from using bunyan-java directly - but these adapters make it possible to unify all logging to output through bunyan.

Note you will still need to use LoggingModule to set up bunyan-java, in addition to adapter modules.

Differences from Traditional Loggers

Designed for Dependency Injection

  • Bunyan-java is designed with dependency injection in mind, and utilizes Guice internally (you don't have to) - you can, for example, during setup call loggingModule.bindLogger("request"), and then anything that needs an instance of Logger can just ask for @Named("request") Logger logger

  • Log objects, not text - most logging information is processed by machines anyway - single lines of JSON make far more sense as an output format than lines of plain text

  • A log level is an object - and it's a factory for Log instances

  • A Log instance represents one Log record you are going to write, and calling close() on it writes it - using the JDK's AutoCloseable to ensure it is:

try (Log<Debug> log = loggers.debug("saveFile")) {
   File newFile = findUnusedFile();
   log.add("to", newFile);
   // do some complicated logic here, and add different properties to the log record depending what happens
}

Usage

Logging is configured using Giulius settings API, which by default uses properties files which may be in /etc/, ~/ and/or ./. If you do not bind your own implementation of LogWriter, the default one is affected by these properties:

  • log.file - if set to a file path, log to this file instead of the console
  • log.console - also log to the console, even if a file is set
  • log.async - write log records out on a background thread, so the caller is not blocked
    • A VM shutdown hook ensures that if the VM is not brutally killed, all pending log records are flushed synchronously during shutdown
  • log.level - the level of log below which log records should be discarded

A Guice module, LoggingModule is provided, which makes it easy to use Guice's @Named to inject loggers:

	Settings s = new SettingsBuilder("app-name").addDefaultLocations().build();
            LoggingModule mod = new LoggingModule().bindLogger("mailer");

	Dependencies deps = Dependencies.builder().add(Namespace.DEFAULT, s).add(new LoggingModule());
            
	// ... then

	class Mailer {
               Mailer (@Named("mailer") Logger logger, ...) { ...

Note: log.file console logging off uses buffered NIO - log records will be buffered, so logging may appear to stutter (a shutdown hook will ensure any buffered records are written) - and can write two million records in about 10 seconds on a laptop with an SSD. log.async is useful for performance if you will log to both console and a file, but uses a different file logging implementation.

This unit test shows some usage. It results in output which, indeed, can be parsed nicely with Bunyan:

alt text

Classes

We're aiming for simplicity more than flexibility here. The following classes may be useful:

  • Loggers - A thing you get a logger from, injected by Guice, e.g. Logger logger = loggers.logger("whatever");; contains final fields for all known log levels
  • Logger - A thing which can create a Log tied to a particular level
  • Log - A thing which you add objects and key-value pairs to, which becomes one line of logging - one log record. It implements an exception-free variant of the JDK's AutoClosable - the natural way to use it is within a try-with-resources block as shown above; at the close of that block, the log record is written out

If you want to ship log records someplace special, you can implement and bind LogWriter, which has one method, write(String).

Log Levels are Objects

You may notice that log levels are actual objects - not constants. So when you get a debug logger, you get a Log<Debug>, and so forth.

You can actually also start from a log level to create a Log, e.g.

	@Inject
	MyThing(Loggers loggers) {
		try (Log<Debug> log = loggers.DEBUG.logger("stuff")) {
			log.add("This is the message");
			log.add("key", "value");
		} // log gets written out here!
	}

But What About Static Logging?

You don't need it. I said this library was opinionated, right?

MongoDB Log Sink

The experimental subproject bunyan-java-mongodb-sink provides an implementation of bunyan-java's LogSink which will route all logging to MongoDB. This can be used in the following ways:

  • To route all logging to MongoDB, period, bind LogSink to MongoDBLogSink (there will be no console or other logging, no matter what you configured bunyan-java to do)
  • To use existing logging configuration, and MongoDB, bind ComposableMongoDBSink as an eager singleton, and call LoggingModule.bindMultiLogSink() when setting up your logging

Versions

Version
2.6.13
2.6.12
2.6.11
2.6.10
2.6.9
2.6.8
2.6.7
2.6.6
2.6.5
2.6.4
2.6.3
2.6.2
2.6.1
2.6.0
2.5.0