Skip to content

Installation

Echopraxia is divided into two sections: the user logger APIs (logger, fluent, semantic) and an underlying CoreLogger implementation which is tied to the logging framework.

You will need to install both, although in 99% of cases you will want logger: Maven:

<dependency>
  <groupId>com.tersesystems.echopraxia</groupId>
  <artifactId>logger</artifactId>
  <version>VERSION</version>
</dependency>

Gradle:

implementation "com.tersesystems.echopraxia:logger:<VERSION>" 

There are core loggers for Logback, Log4J2, and JUL.

Logstash Core Logger

There is a Logback implementation based around logstash-logback-encoder. This library does not provide a front end logger API, so you must pick (or create) one yourself, i.e. normal, async, fluent, or semantic.

Maven:

<dependency>
  <groupId>com.tersesystems.echopraxia</groupId>
  <artifactId>logstash</artifactId>
  <version><VERSION></version>
</dependency>

Gradle:

implementation "com.tersesystems.echopraxia:logstash:<VERSION>"

Because Logback 1.2 is compiled with SLF4J 1.7.x and Logback 1.3 uses SLF4J 2.x, Echopraxia does not include the transitive dependencies. Instead, you will need to select the appropriate Logback implementation:

For SLF4J 1.7.x:

implementation "ch.qos.logback:logback-classic:1.2.12"
implementation 'net.logstash.logback:logstash-logback-encoder:7.3'

For SLF4J 2.0.x and for logstash-logback-encoder from 7.4:

implementation "ch.qos.logback:logback-classic:1.4.6"
implementation 'net.logstash.logback:logstash-logback-encoder:7.4'

Log4J Core Logger

There is a Log4J implementation that works with the JSON Template Layout. This provides a core logger implementation but does not provide a user visible logging API.

Maven:

<dependency>
  <groupId>com.tersesystems.echopraxia</groupId>
  <artifactId>log4j</artifactId>
  <version><VERSION></version>
</dependency>

Gradle:

implementation "com.tersesystems.echopraxia:log4j:<VERSION>" 

You should explicitly define the Log4J dependencies as well:

implementation "org.apache.logging.log4j:log4j-core:$log4j2Version"
implementation "org.apache.logging.log4j:log4j-api:$log4j2Version"
implementation "org.apache.logging.log4j:log4j-layout-template-json:$log4j2Version"

You will need to integrate the com.tersesystems.echopraxia.log4j.layout package into your log4j2.xml file, e.g. by using the packages attribute, and add an EventTemplateAdditionalField element:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" packages="com.tersesystems.echopraxia.log4j.layout">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT" follow="true">
            <JsonTemplateLayout eventTemplateUri="classpath:LogstashJsonEventLayoutV1.json">
                <EventTemplateAdditionalField
                        key="fields"
                        format="JSON"
                        value='{"$resolver": "echopraxiaFields"}'/>
            </JsonTemplateLayout>
        </Console>
    </Appenders>
    <Loggers>
        <Root level="info">
            <AppenderRef ref="Console" />
        </Root>
    </Loggers>
</Configuration>

If you want to separate the context fields from the argument fields, you can define them separately:

<JsonTemplateLayout eventTemplateUri="classpath:LogstashJsonEventLayoutV1.json">
    <EventTemplateAdditionalField
            key="arguments"
            format="JSON"
            value='{"$resolver": "echopraxiaArgumentFields"}'/>
    <EventTemplateAdditionalField
            key="context"
            format="JSON"
            value='{"$resolver": "echopraxiaContextFields"}'/>
</JsonTemplateLayout>

Unfortunately, I don't know of a way to "flatten" fields so that they show up on the root object instead of under an additional field. If you know how to do this, let me know!

JUL (java.util.logging) Core Logger

There is a JUL implementation.

Maven:

<dependency>
  <groupId>com.tersesystems.echopraxia</groupId>
  <artifactId>jul</artifactId>
  <version><VERSION></version>
</dependency>

Gradle:

implementation "com.tersesystems.echopraxia:jul:<VERSION>" 

You will probably want to configure JUL by calling logManager.readConfiguration:

InputStream is;
try {
  is = getClass().getClassLoader().getResourceAsStream("logging.properties");
  LogManager manager = LogManager.getLogManager();
  manager.reset();
  manager.readConfiguration(is);
} finally {
  if (is != null) is.close();
}

JSON output is managed using a custom formatter com.tersesystems.echopraxia.jul.JULJSONFormatter:

handlers=java.util.logging.ConsoleHandler,java.util.logging.FileHandler

.level=FINEST

com.tersesystems.echopraxia.jul.JULJSONFormatter.use_slf4j_level_names=true

java.util.logging.FileHandler.formatter=com.tersesystems.echopraxia.jul.JULJSONFormatter
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter

The use_slf4j_level_names property will map from JUL's levels to SLF4J, mapping FINE and FINER to DEBUG and FINEST to TRACE.

JUL's default class/method inference is disabled as it is not useful here and needlessly slows down logging.