- Manage Logback configuration and integration with other logging APIs.
- Provide reference implementation of the standard logging format
- Provide Appender implementations for OpenTable logging infrastructure
- Optionally provide a 'nicer' api than any of
SLF4J
,commons-logging
, orjava.util.logging
provide.
The Core module provides definitions for the OpenTable common log format.
The CommonLogFields interface defines the fields that are sent for all logging events. The HttpLogFields interface defines the fields that are sent for HTTP access log events. The ApplicationLogEvent class wraps a Logback logging event and exposes the common log format.
It also provides a Logback encoder JsonLogEncoder which serializes the above event types into a format suitable to submit to the OpenTable logging infrastructure through an arbitrary Appender.
The common log format defines a "servicetype" field which should be filled with the name of the service.
As the logger does not know the name of the service it is running in, this must be provided by the application.
Absent of a better way of doing this, the otj-serverinfo component
provides the application name. For builds that are built on top of the otj-server
StandaloneServer
this will automatically be filled in. Other applications should explicitly initialize this early on during
application initialization:
ServerInfo.add(ServerInfo.SERVER_TYPE, "my-cool-server");
The Redis module provides an Appender that submits logging events to a Redis queue. Example configuration:
<?xml version="1.0" encoding="UTF-8" ?>
<!--
Example configuration that logs text to the console and JSON to Redis.
-->
<configuration threshold="TRACE">
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<appender name="REDIS" class="com.opentable.logging.RedisAppender">
<encoder class="com.opentable.logging.JsonLogEncoder"/>
<host>localhost</host>
<port>12345</port>
<key>logs</key>
<clientName>test-client</clientName>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="REDIS" />
</root>
</configuration>
The Kafka module provides an Appender that submits logging events to a Kafka queue. Example configuration:
<?xml version="1.0" encoding="UTF-8" ?>
<!--
Example configuration that logs text to the console and JSON to Kafka
-->
<configuration threshold="TRACE">
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<appender name="KAFKA" class="com.opentable.logging.KafkaAppender">
<encoder class="com.opentable.logging.JsonLogEncoder"/>
<brokerList>localhost:12345</brokerList>
<topic>logs</topic>
<clientId>my-application-name</clientId>
</appender>
<!-- Default is DEBUG for our stuff, INFO for everything else -->
<logger name="com.opentable" level="DEBUG" />
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="KAFKA" />
</root>
</configuration>
The main module ties together all of the above modules. In addition, it provides adapters to pull together other common logging frameworks:
java.util.logging
viajul-to-slf4j
commons-logging
viajcl-over-slf4j
log4j
vialog4j-over-slf4j
Finally, it provides an alternative facade to SLF4J
with a nicer API.
It is almost offensive this day and age to create Yet Another Logging Facade.
otj-logging
dramatically improves the interface that developers use to log, which reduces
friction in a very crucial piece of the platform. So far, it has been worth the effort.
SLF4J
void info(String format, Object... args);
void info(String message, Throwable t);
The two overloads are not compatible with each other! Depending on whether you are handling an exception or not changes the semantics of the first argument - with a Throwable it is a plain String message, and without it is a format string and will get interpolated.
This so close to being good but misses the mark on usability. We have a much better one:
otj-logging
void info(String format, Object... args);
void info(Throwable t, String format, Object... args);
Now you may confidently use the same message strings with or without a Throwable to report.
Note that Log
uses String.format
so make sure to use %s
instead of {}
.
The SLF4J API is still available and works fine, just be very conscious about which one you use
because the message formats are not the same. This is arguably a bug, and if there is sufficient
interest maybe Log
should switch to using the same MessageFormat
.
Usage is very simple:
class MyLoggingClass {
private static final Log LOG = Log.findLog();
MyLoggingClass() {
LOG.info("Hello constructor from %s!", this);
}
public void handleError(Throwable t) {
LOG.error(t, "Something went wrong! I am: %s", this);
}
}
Copyright (C) 2014 OpenTable, Inc.