Echopraxia - Java Logging API with clean and simple structured logging and conditional & contextual features. Logback implementation based on logstash-logback-encoder.

Overview

Echopraxia

Echopraxia is a Java logging API that and is designed around structured logging, rich context, and conditional logging. There is a Logback-based implementation, but Echopraxia's API is completely dependency-free, meaning it can be implemented with Log4J2, JUL, or directly.

Echopraxia is a sequel to the Scala logging API Blindsight, hence the name: "Echopraxia is the involuntary repetition or imitation of an observed action."

Echopraxia is based around several main concepts that build and leverage on each other:

  • Structured Logging (API based around structured fields and values)
  • Contextual Logging (API based around building state in loggers)
  • Conditions (API based around context-aware functions and dynamic scripting)
  • Semantic Logging (API based around typed arguments)
  • Fluent Logging (API based around log entry builder)

For a worked example, see this Spring Boot Project.

Although Echopraxia is tied on the backend to an implementation, it is designed to hide implementation details from you, just as SLF4J hides the details of the logging implementation. For example, logstash-logback-encoder provides Markers or StructuredArguments, but you will not see them in the API. Instead, Echopraxia works with independent Field and Value objects that are converted by a CoreLogger provided by an implementation.

Please see the blog posts for more background.

Logstash

There is a Logback implementation based around logstash-logback-encoder implementation of event specific custom fields.

Maven:

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

Gradle:

implementation "com.tersesystems.echopraxia:logstash:1.0.0" 

Basic Usage

For almost all use cases, you will be working with the API which is a single import:

import com.tersesystems.echopraxia.*;

First you get a logger:

Logger<?> basicLogger = LoggerFactory.getLogger(getClass());

Logging simple messages and exceptions are done as in SLF4J:

try {
  ...
  basicLogger.info("Simple message");
} catch (Exception e) {
  basicLogger.error("Error message", e);  
}

However, when you log arguments, you pass a function which provides you with a field builder and returns a list of fields:

basicLogger.info("Message name {}", fb -> fb.onlyString("name", "value"));

You can log multiple arguments and include the exception if you want the stack trace:

basicLogger.info("Message name {}", fb -> Arrays.asList(
  fb.string("name", "value"),
  fb.exception(e)
));

So far so good, but logging strings and numbers can get tedious. Let's go into custom field builders.

Custom Field Builders

Echopraxia lets you specify custom field builders whenever you want to log domain objects:

  public class BuilderWithDate implements Field.Builder {
    public BuilderWithDate() {}

    // Renders a date using the `only` idiom returning a list of `Field`.
    // This is a useful shortcut when you only have one field you want to add.
    public List<Field> onlyDate(String name, Date date) {
      return singletonList(date(name, date));
    }

    // Renders a date as an ISO 8601 string.
    public Field date(String name, Date date) {
      return string(
              name, DateTimeFormatter.ISO_INSTANT.format(Instant.ofEpochMilli(date.getTime())));
    }
  }

And now you can render a date automatically:

Logger<BuilderWithDate> dateLogger = basicLogger.withFieldBuilder(BuilderWithDate.class);
dateLogger.info("Date {}", fb -> fb.onlyDate("creation_date", new Date()));

This also applies to more complex objects:

  public class PersonFieldBuilder implements Field.Builder {
    public PersonFieldBuilder() {}
    // Renders a `Person` as an object field.
    // Note that properties must be broken down to the basic JSON types,
    // i.e. a primitive string/number/boolean/null or object/array.
    public Field person(String name, Person person) {
      return object(
              name,
              string("name", person.name()),
              number("age", person.age()),
              array("toys", Field.Value.asList(person.toys(), Field.Value::string)));
    }
  }
Person user = ...
Logger<PersonFieldBuilder> personLogger = basicLogger.withFieldBuilder(PersonFieldBuilder.class);
personLogger.info("Person {}", fb -> Arrays.asList(fb.person("user", user)));

Context

You can also add fields directly to the logger using logger.withFields for contextual logging:

Logger<?> loggerWithFoo = basicLogger.withFields(fb -> fb.onlyString("foo", "bar"));
loggerWithFoo.info("JSON field will log automatically") // will log "foo": "bar" field in a JSON appender.

This works very well for HTTP session and request data such as correlation ids.

Note that in contrast to MDC, logging using context fields will work seamlessly across multiple threads.

Conditions

Logging conditions can be handled gracefully using Condition functions. A Condition will take a Level and a LoggingContext which will return the fields of the logger.

final Condition mustHaveFoo = (level, context) ->
        context.getFields().stream().anyMatch(field -> field.name().equals("foo"));

Conditions should be cheap to evaluate, and should be "safe" - i.e. they should not do things like network calls, database lookups, or rely on locks.

Conditions can be used either on the logger, on the statement, or against the enabled check.

Logger

You can use conditions in a logger, and statements will only log if the condition is met:

Logger<?> loggerWithCondition = logger.withCondition(condition);

You can also build up conditions:

Logger<?> loggerWithAandB = logger.withCondition(conditionA).withCondition(conditionB);

Statement

You can use conditions in an individual statement:

logger.info(mustHaveFoo, "Only log if foo is present");

Enabled

Conditions can also be used in blocks for expensive objects.

if (logger.isInfoEnabled(condition)) {
  // only true if condition and is info  
}

Dynamic Conditions with Scripts

One of the limitations of logging is that it's not that easy to change logging levels in an application at run-time. In modern applications, you typically have complex inputs and may want to enable logging for some very specific inputs without turning on your logging globally.

Script Conditions lets you tie your conditions to scripts that you can change and re-evaluate at runtime.

The security concerns surrounding Groovy or Javascript make them unsuitable in a logging environment. Fortunately, Echopraxia provides a Tweakflow script integration that lets you evaluate logging statements safely.

Because Scripting has a dependency on Tweakflow, it is broken out into a distinct library that you must add to your build.

Maven:

<dependency>
  <groupId>com.tersesystems.echopraxia</groupId>
  <artifactId>scripting</artifactId>
  <version>1.0.0</version>
</dependency>

Gradle:

implementation "com.tersesystems.echopraxia:scripting:1.0.0" 

File Based Scripts

Creating a script condition is done with ScriptCondition.create:

import com.tersesystems.echopraxia.scripting.*;

Path path = Paths.get("src/test/tweakflow/condition.tf");
Condition condition = ScriptCondition.create(false, path, Throwable::printStackTrace);

Logger<?> logger = LoggerFactory.getLogger(getClass()).withCondition(condition);

Where condition.tf contains a tweakflow script, e.g.

import * as std from "std";
alias std.strings as str;

library echopraxia {
  # level: the logging level
  # fields: the dictionary of fields
  function evaluate: (string level, dict fields) ->
    str.lower_case(fields[:person][:name]) == "will";   
}

Tweakflow comes with a VS Code integration, a reference guide, and a standard library that contains useful regular expression and date manipulation logic.

One important thing to note is that creating a script tied to a file will ensure that if the file is touched, the script manager will invalidate the script and recompile it. This does mean that the condition will check last modified fs metadata on every evaluation, which should be fine for most filesystems, but I have not attempted to scale this feature and I vaguely remember something odd happening on Windows NTFS LastModifiedDate. YMMV.

String Based Scripts

You also have the option of passing in a string directly, which will never touch last modified date:

Condition c = ScriptCondition.create(false, scriptString, Throwable::printStackTrace);

Custom Source Scripts

You also have the option of creating your own ScriptHandle which can be backed by whatever you like, for example you can call out to Consul or a feature flag system for script work:

ScriptHandle handle = new ScriptHandle() {
  @Override
  public boolean isInvalid() {
    return callConsulToCheckWeHaveNewest();
  }

  @Override
  public String script() throws IOException {
    return callConsulForScript();
  }
    
  // ...report / path etc 
};
ScriptCondition.create(false, handle);

Semantic Logging

Semantic Loggers are strongly typed, and will only log a particular kind of argument. All the work of field building and setting up a message is done from setup.

Basic Usage

To set up a logger for a Person with name and age properties, you would do the following:

import com.tersesystems.echopraxia.semantic.*;

SemanticLogger<Person> logger =
    SemanticLoggerFactory.getLogger(
        getClass(),
        Person.class,
        person -> "person.name = {}, person.age = {}",
        p -> b -> Arrays.asList(b.string("name", p.name), b.number("age", p.age)));

Person person = new Person("Eloise", 1);
logger.info(person);

Conditions

Semantic loggers take conditions in the same way that other loggers do, either through predicate:

if (logger.isInfoEnabled(condition)) {
  logger.info(person);
}

or directly on the method:

logger.info(condition, person);

or on the logger:

logger.withCondition(condition).info(person);

Context

Semantic loggers can add fields to context in the same way other loggers do.

SemanticLogger<Person> loggerWithContext =
  logger.withFields(fb -> fb.onlyString("some_context_field", contextValue));

Installation

Semantic Loggers have a dependency on the api module, but do not have any implementation dependencies.

Maven:

<dependency>
  <groupId>com.tersesystems.echopraxia</groupId>
  <artifactId>semantic</artifactId>
  <version>1.0.0</version>
</dependency>

Gradle:

implementation "com.tersesystems.echopraxia:semantic:1.0.0" 

Fluent Logging

Fluent logging is done using a FluentLoggerFactory.

It is useful in situations where arguments may need to be built up over time.

import com.tersesystems.echopraxia.fluent.*;

FluentLogger<?> logger = FluentLoggerFactory.getLogger(getClass());

Person person = new Person("Eloise", 1);

logger
    .atInfo()
    .message("name = {}, age = {}")
    .argument(b -> b.string("name", person.name))
    .argument(b -> b.number("age", person.age))
    .log();

Installation

Fluent Loggers have a dependency on the api module, but do not have any implementation dependencies.

Maven:

<dependency>
  <groupId>com.tersesystems.echopraxia</groupId>
  <artifactId>fluent</artifactId>
  <version>1.0.0</version>
</dependency>

Gradle:

implementation "com.tersesystems.echopraxia:fluent:1.0.0" 

Core Logger and SLF4J API

The SLF4J API are not exposed normally. If you want to use SLF4J features like markers specifically, you will need to use a core logger.

First, import the logstash package and the core package:

import com.tersesystems.echopraxia.logstash.*;
import com.tersesystems.echopraxia.core.*;

This gets you access to the CoreLogger and CoreLoggerFactory, which is used as a backing logger.

The LogstashCoreLogger has a withMarkers method that takes an SLF4J marker:

LogstashCoreLogger core = (LogstashCoreLogger) CoreLoggerFactory.getLogger();
Logger<?> logger = LoggerFactory.getLogger(core.withMarkers(MarkerFactory.getMarker("SECURITY")), Field.Builder.instance);

Likewise, you need to get at the SLF4J logger from a core logger, you can cast and call core.logger():

Logger<?> baseLogger = LoggerFactory.getLogger();
LogstashCoreLogger core = (LogstashCoreLogger) baseLogger.core();
org.slf4j.Logger slf4jLogger = core.logger();

If you have markers set as context, you can evaluate them in a condition through casting to LogstashLoggingContext:

Condition hasAnyMarkers = (level, context) -> {
   LogstashLoggingContext c = (LogstashLoggingContext) context;
   List<org.slf4j.Marker> markers = c.getMarkers();
   return markers.size() > 0;
};
Comments
  • Do not memoize a context result

    Do not memoize a context result

    Because a context field can contain time dependent (or state dependent) values that can change between calls, it's not appropriate to memoize the context's result -- it is "call by name"

    It might be appropriate to pass in memoized fields that are just a straight list, rather than a function -- having a field builder does make it kind of confusing sometimes.

    TODO add tests, ensure that log4j and logstash are both covered, document that context fields are call by name.

    opened by wsargent 1
  • Filters should run through array and leverage thread

    Filters should run through array and leverage thread

    Make Filters a public class, allow an array of classloaders to be passed in, finally default to the thread's context class loader.

    This is because sbt does some fancy in process class loading and so just relying on the app class loader is insufficient.

    https://www.scala-sbt.org/1.x/docs/In-Process-Classloaders.html

    opened by wsargent 0
  • Stop LogstashCoreLogger from using StructuredArgument.keyValue(name, throwable)

    Stop LogstashCoreLogger from using StructuredArgument.keyValue(name, throwable)

    In https://github.com/logfellow/logstash-logback-encoder#customizing-stack-traces it says "Do NOT use structured arguments or markers for exceptions."

    The logstash implementation does pass in exception to a StructuredArgument, resulting in some large JSON objects. I think this is 7.2 behavior, but the correct thing to do is to pass in exception.toString and go from there.

    opened by wsargent 0
  • Enable with handles

    Enable with handles

    Trace logging really doesn't map very well to core logger single methods -- we want to check isEnabled, ensure that source info fields are available to conditions, but then not evaluate them if there's no condition and reuse them if they were available.

    This results in something that looks like this:

      @inline
      private def handle[B: ToValue](
          level: JLevel,
          attempt: => B
      )(implicit line: Line, file: File, enc: Enclosing, args: Args): B = {
        val sourceFields = fb.sourceFields
        val extraFields = (() => fb.list(sourceFields.loggerFields).fields()).asJava
        if (core.isEnabled(level, extraFields)) {
          execute(core, level, sourceFields, attempt)
        } else {
          attempt
        }
      }
    
      @inline
      private def execute[B: ToValue](core: CoreLogger, level: JLevel, sourceFields: fb.SourceFields, attempt: => B): B = {
        val handle = core.logHandle(level, fb)
        handle.log(fb.enteringTemplate, entering(sourceFields))
        val result = Try(attempt)
        result match {
          case Success(ret) =>
            handle.log(fb.exitingTemplate, exiting(sourceFields, ret))
          case Failure(ex) =>
            handle.log(fb.throwingTemplate, throwing(sourceFields, ex))
        }
        result.get // rethrow the exception
      }
    

    And... well, if you call coreLogger.log() then you evaluate conditions several times (enter / exit) etc and it's just a mess.

    Doing it this way means that evaluation is down to 30 ns per evaluation (between the implicit source info and putting suppliers together) for disabled logger statements, and it limits the upper bound on field re-evaluation when it is enabled.

    opened by wsargent 0
  • add extra fields to core logger

    add extra fields to core logger

    Creating a new logger when we just want to add some extra fields in a single statement (especially source info / macro provided information) is unnecessarily expensive, especially on disabled loggers. Adding statements that allow for suppliers fills out some options for providers.

    opened by wsargent 0
  • Structured arguments in MDC

    Structured arguments in MDC

    Hey,

    would it be possible to put structured arguments optionally into the MDC?

    Maybe my use case is a bit special: I am using Quarkus and there you cannot easily replace jboss-logmanager with logstash, which makes everything a bit of a pain. Additionally, I want to post log entries via gelf/fluent to elastic and there is a gelf plugin for Quarkus, which includes everything put into the MDC.

    I know I can write my own wrapper or interceptor, but really would like to keep the nice field builders from Echopraxia.

    opened by unexist 6
Releases(2.2.4)
  • 2.2.4(Nov 8, 2022)

    What's Changed

    • Delay logback initialization by @wsargent in https://github.com/tersesystems/echopraxia/pull/222

    Full Changelog: https://github.com/tersesystems/echopraxia/compare/2.2.3...2.2.4

    Source code(tar.gz)
    Source code(zip)
  • 2.2.3(Oct 20, 2022)

    What's Changed

    • Update with note on Log4J API by @wsargent in https://github.com/tersesystems/echopraxia/pull/217
    • More Logback direct API support by @wsargent in https://github.com/tersesystems/echopraxia/pull/220
    • Filters should run through array and leverage thread by @wsargent in https://github.com/tersesystems/echopraxia/pull/221

    Full Changelog: https://github.com/tersesystems/echopraxia/compare/2.2.2...2.2.3

    Source code(tar.gz)
    Source code(zip)
  • 2.2.2(Aug 31, 2022)

    What's Changed

    Break out the underlying Logback classes from Logstash, so that "direct access" can be used as a fallback. Fix a bug in JSON exception rendering using Logstash StructuredArgument where exception was being rendered a full nested JSON object.

    Full Changelog: https://github.com/tersesystems/echopraxia/compare/2.2.1...2.2.2

    Source code(tar.gz)
    Source code(zip)
  • 2.2.1(Aug 29, 2022)

    What's Changed

    Added Condition.booleanMatch/objectMatch/*match for better direct matching. Added a type safe Values.equals static method. Also opened up the Logstash / Log4J constructors, so that it's easier to wrap Logback Logger / ExtendedLogger directly into a core logger.

    • Open the core logger constructors by @wsargent in https://github.com/tersesystems/echopraxia/pull/212
    • Add more condition matches by @wsargent in https://github.com/tersesystems/echopraxia/pull/214

    Full Changelog: https://github.com/tersesystems/echopraxia/compare/2.2.0...2.2.1

    Source code(tar.gz)
    Source code(zip)
  • 2.2.0(Aug 13, 2022)

    What's Changed

    Add diff field builder, break out Jackson module so it can be used distinct from logstash, upgrade log4j2.

    • Move Jackson to distinct module by @wsargent in https://github.com/tersesystems/echopraxia/pull/205
    • Upgrade to log4j2 2.18.0 by @wsargent in https://github.com/tersesystems/echopraxia/pull/206
    • Add diff field builder by @wsargent in https://github.com/tersesystems/echopraxia/pull/209

    Full Changelog: https://github.com/tersesystems/echopraxia/compare/2.1.0...2.2.0

    Source code(tar.gz)
    Source code(zip)
  • 2.1.0(Jul 1, 2022)

    What's Changed

    Changes to CoreLogger API to allow for more flexible loggers in echopraxia-plusscala. Some optimizations.

    • Add extraFields parameter to CoreLogger methods. #201
    • Document "call-by-name" semantics on logger.withFields. #188
    • Expose getArgumentFields() and getLoggerFields() methods on LoggingContext. #197
    • Upgrade logstash-logback-encoder to 7.2 #203
    • Add coreLogger.logHandle for loggers that may log multiple times internally when called. #202
    • Fix a bug where withFields was being memoized and evaluated once. #187

    Full Changelog: https://github.com/tersesystems/echopraxia/compare/2.0.1...2.1.0

    Source code(tar.gz)
    Source code(zip)
  • 2.0.1(May 21, 2022)

    What's Changed

    https://github.com/tersesystems/echopraxia/blob/2.0.1/CHANGELOG.md#201

    • Optimizations for Value by @wsargent in https://github.com/tersesystems/echopraxia/pull/177
    • findList returns singletonList if an element is found by @wsargent in https://github.com/tersesystems/echopraxia/pull/178
    • Rename MIGRATION.md to CHANGELOG.md by @wsargent in https://github.com/tersesystems/echopraxia/pull/179
    • Equals methods on values and fields by @wsargent in https://github.com/tersesystems/echopraxia/pull/180
    • Update number values with comparable by @wsargent in https://github.com/tersesystems/echopraxia/pull/181
    • Fix exceptions not showing as arguments in logstash by @wsargent in https://github.com/tersesystems/echopraxia/pull/184
    • Fill out numbers methods by @wsargent in https://github.com/tersesystems/echopraxia/pull/183
    • Make null number default to zero by @wsargent in https://github.com/tersesystems/echopraxia/pull/185

    Full Changelog: https://github.com/tersesystems/echopraxia/compare/2.0.0...2.0.1

    Source code(tar.gz)
    Source code(zip)
  • 2.0.0(May 7, 2022)

  • 1.4.1(May 1, 2022)

    Fix bug where withThreadLocal supplier was wiped out by withFields in AsyncLogger.

    Full Changelog: https://github.com/tersesystems/echopraxia/compare/1.4.0...1.4.1

    Source code(tar.gz)
    Source code(zip)
  • 1.4.0(Mar 17, 2022)

    What's Changed

    • Throwable should count as an argument by @wsargent in https://github.com/tersesystems/echopraxia/pull/106
    • Memoize context fields by @wsargent in https://github.com/tersesystems/echopraxia/pull/110
    • Implement JSONPath in context by @wsargent in https://github.com/tersesystems/echopraxia/pull/111
    • Add context and jsonpath operations in scripting by @wsargent in https://github.com/tersesystems/echopraxia/pull/115
    • Add withThreadLocal method for async TLS by @wsargent in https://github.com/tersesystems/echopraxia/pull/117

    Full Changelog: https://github.com/tersesystems/echopraxia/compare/1.3.0...1.4.0

    Source code(tar.gz)
    Source code(zip)
  • 1.3.0(Feb 25, 2022)

    What's Changed

    Features

    • Added filters for global core logger configuration.
    • Add arguments in as fields for evaluation of conditions

    Tweaks and Fixes

    • Add convenience methods for comparing levels.
    • Add convenience methods for conditions (anyMatch, noneMatch, valueMatch, operational, diagnostic)
    • Rearrange interfaces to make creating new logger APIs easier
    • Add AsyncLoggerFactory and deprecate logger.withExecutor as it returns an AsyncLogger from a Logger
    • Add logger.getName() to Logger interfaces
    • Fix caller info on AsyncLogger and add documentation for configuring it.

    PRs

    • Add getName to Logger by @wsargent in https://github.com/tersesystems/echopraxia/pull/86
    • Add filters by @wsargent in https://github.com/tersesystems/echopraxia/pull/85
    • Add arguments in as context to conditions by @wsargent in https://github.com/tersesystems/echopraxia/pull/87
    • Create interfaces by @wsargent in https://github.com/tersesystems/echopraxia/pull/91
    • Point everything to AsyncLoggerFactory by @wsargent in https://github.com/tersesystems/echopraxia/pull/94
    • Clean up tests by @wsargent in https://github.com/tersesystems/echopraxia/pull/95
    • Document async logger caller info by @wsargent in https://github.com/tersesystems/echopraxia/pull/96
    • Add shields by @wsargent in https://github.com/tersesystems/echopraxia/pull/97
    • reformat by @wsargent in https://github.com/tersesystems/echopraxia/pull/98
    • Use transforming appender by @wsargent in https://github.com/tersesystems/echopraxia/pull/100
    • Reorder level enum by @wsargent in https://github.com/tersesystems/echopraxia/pull/101
    • Add boolean conditions by @wsargent in https://github.com/tersesystems/echopraxia/pull/103
    • Delete example by @wsargent in https://github.com/tersesystems/echopraxia/pull/105

    Full Changelog: https://github.com/tersesystems/echopraxia/compare/1.2.0...1.3.0

    Source code(tar.gz)
    Source code(zip)
  • 1.2.0(Feb 6, 2022)

    What's Changed

    • Value API now returns the specific case, i.e. StringValue rather than Value<String>.
    • Values and fields are robust against null names and values, and will not throw exceptions.
    • IntelliJ annotations are added so everything is @NotNull or @Nullable respectively.
    • Asynchronous logging added with withExecutor, for use with expensive conditions or logging.
    • Pass fully qualified class name into logging API for better caller info in Logback/Log4J.
    • Always evaluate logger.isEnabled before checking condition in core logger (levels/filters will be cheaper)
    • Optimizations to remove streams in some places.
    • Optimize to use NeverLogger on a never condition, removing all runtime cost of logging
    • Use a thread local string builder in field/value message argument, reducing allocation pressure.
    • Fill out the example application to use async logging, console/file appenders, colors, and JUL bridge.

    Full Changelog: https://github.com/tersesystems/echopraxia/compare/1.1.3...1.2.0

    Source code(tar.gz)
    Source code(zip)
  • 1.1.3(Jan 26, 2022)

    What's Changed

    • Add fromContext to pull MDC into fields by @wsargent in https://github.com/tersesystems/echopraxia/pull/45
    • Fix an incorrect info guard bug by @wsargent in https://github.com/tersesystems/echopraxia/pull/47
    • Redo logstash serialization through Jackson module, fixing Logstash complex object bug by @wsargent in https://github.com/tersesystems/echopraxia/pull/46
    • Curly braces for objects in logfmt based line messages by @wsargent in https://github.com/tersesystems/echopraxia/pull/48
    • Add some extra convenience methods in Field and Value by @wsargent in https://github.com/tersesystems/echopraxia/pull/49
    • Add some notes on onlyException by @wsargent in https://github.com/tersesystems/echopraxia/pull/50

    Full Changelog: https://github.com/tersesystems/echopraxia/compare/1.1.2...1.1.3

    Source code(tar.gz)
    Source code(zip)
  • 1.1.2(Jan 23, 2022)

    What's Changed

    • Fix bug where context was not evaluated lazily by @wsargent in https://github.com/tersesystems/echopraxia/pull/27
    • Add value check to script manager by @wsargent in https://github.com/tersesystems/echopraxia/pull/28
    • Fix bug where predicate didn't check for marker by @wsargent in https://github.com/tersesystems/echopraxia/pull/29
    • Fix bug where context fields were not added for Log4J single-arg methods by @wsargent in https://github.com/tersesystems/echopraxia/pull/31
    • Add extra JSON resolvers for log4j by @wsargent in https://github.com/tersesystems/echopraxia/pull/32
    • Log4J optimizations and benchmarks by @wsargent in https://github.com/tersesystems/echopraxia/pull/33
    • Safer ScriptManager concurrency by @wsargent in https://github.com/tersesystems/echopraxia/pull/39

    Full Changelog: https://github.com/tersesystems/echopraxia/compare/1.1.0...1.1.2

    Source code(tar.gz)
    Source code(zip)
  • 1.1.1(Jan 19, 2022)

    What's Changed

    • Ensure lazy context by @wsargent in https://github.com/tersesystems/echopraxia/pull/27
    • Add value check to script manager by @wsargent in https://github.com/tersesystems/echopraxia/pull/28
    • Ensure that predicate checks for marker by @wsargent in https://github.com/tersesystems/echopraxia/pull/29

    Full Changelog: https://github.com/tersesystems/echopraxia/compare/1.1.0...1.1.1

    Source code(tar.gz)
    Source code(zip)
  • 1.1.0(Jan 18, 2022)

    What's Changed

    • JMH by @wsargent in https://github.com/tersesystems/echopraxia/pull/10
    • More JMH by @wsargent in https://github.com/tersesystems/echopraxia/pull/11
    • Add fluent predicates by @wsargent in https://github.com/tersesystems/echopraxia/pull/12
    • Fill out the README by @wsargent in https://github.com/tersesystems/echopraxia/pull/13
    • Add scripting benchmarks by @wsargent in https://github.com/tersesystems/echopraxia/pull/14
    • Add benchmarks for string condition by @wsargent in https://github.com/tersesystems/echopraxia/pull/17
    • File watching module by @wsargent in https://github.com/tersesystems/echopraxia/pull/16
    • Optimize script execution by @wsargent in https://github.com/tersesystems/echopraxia/pull/20
    • Tweakflow conversion by @wsargent in https://github.com/tersesystems/echopraxia/pull/21
    • Add log4j implementation by @wsargent in https://github.com/tersesystems/echopraxia/pull/18
    • Richer Field Builders by @wsargent in https://github.com/tersesystems/echopraxia/pull/23
    • Implement value field by @wsargent in https://github.com/tersesystems/echopraxia/pull/24

    Full Changelog: https://github.com/tersesystems/echopraxia/compare/1.0.0...1.1.0

    Source code(tar.gz)
    Source code(zip)
  • 1.0.0(Jan 2, 2022)

  • 0.0.3(Jan 1, 2022)

  • 0.0.2(Jan 1, 2022)

  • 0.0.1(Dec 29, 2021)

Owner
Terse Systems
OSS projects released to Maven Central
Terse Systems
Logstash - transport and process your logs, events, or other data

Logstash Logstash is part of the Elastic Stack along with Beats, Elasticsearch and Kibana. Logstash is a server-side data processing pipeline that ing

elastic 13.2k Jan 5, 2023
Simple Logging Facade for Java

About SLF4J The Simple Logging Facade for Java (SLF4J) serves as a simple facade or abstraction for various logging frameworks (e.g. java.util.logging

QOS.CH Sarl 2.1k Jan 7, 2023
An extensible Java library for HTTP request and response logging

Logbook: HTTP request and response logging Logbook noun, /lɑɡ bʊk/: A book in which measurements from the ship's log are recorded, along with other sa

Zalando SE 1.3k Dec 29, 2022
The reliable, generic, fast and flexible logging framework for Java.

About logback Thank you for your interest in logback, the reliable, generic, fast and flexible logging library for Java. The Logback documentation can

QOS.CH Sarl 2.6k Jan 7, 2023
tinylog is a lightweight logging framework for Java, Kotlin, Scala, and Android

tinylog 2 Example import org.tinylog.Logger; public class Application { public static void main(String[] args) { Logger.info("Hello

tinylog.org 547 Dec 30, 2022
tinylog is a lightweight logging framework for Java, Kotlin, Scala, and Android

tinylog 2 Example import org.tinylog.Logger; public class Application { public static void main(String[] args) { Logger.info("Hello

tinylog.org 551 Jan 4, 2023
Highly efficient garbage-free logging framework for Java 8+

Garbage Free Log Highly efficient garbage-free logging framework for Java 8+. Use Add the following dependencies to your project: implementation 'com.

EPAM Systems 37 Dec 12, 2022
Adapts Java platform logging (System.Logger) to SLF4J 1.7.x.

avaje-slf4j-jpl Adapts Java platform logging (System.Logger) to SLF4J 1.7.x. Requires Java 11 or greater. Step 1. Add dependenc

avaje 1 Jan 18, 2022
Logging filters for Spring WebFlux client and server request/responses

webflux-log Logging filters for Spring WebFlux client and server request/responses. Usage To log WebClient request/response, do the following specify

null 10 Nov 29, 2022
Log annotation for logging frameworks

Herald "Why, sometimes I've believed as many as six impossible things before breakfast." - Lewis Carroll, Alice in Wonderland. Herald provides a very

Vladislav Bauer 71 Dec 21, 2022
remove lag on chat message without removing features

Non-blocking chat lookup On a chat message, the client will check if that player is blocked or not. This is an API lookup. But this does not happen on

KosmX 13 Jun 27, 2022
Java text based mystery-adventure game

Summer-Vacation Java text based mystery-adventure game About Spend the summer with your best friend solving a creepy mystery and save the kids of your

Michael  Stack 3 Feb 7, 2022
A simple logger for java

A simple logger for java

Sniper10754 5 Nov 20, 2022
The GhidraLookup plugin aims to help user lookup documentations of Win API functions.

GhidraLookup Plugin The GhidraLookup plugin aims to help user lookup documentations of Win API functions. Usage Right click on a Function in the Decom

Daniel Liu 8 Dec 23, 2022
A Java library that facilitates reading, writing and processing of sensor events and raw GNSS measurements encoded according to the Google's GNSS Logger application format.

google-gnss-logger This library facilitates reading, writing and processing of sensor events and raw GNSS measurements encoded according to the Google

Giulio Scattolin 5 Dec 21, 2022
Java Info Logger

Java-Info-Logger This is a project i made by myself, its a java "basic" logger i made with some friends help What does it grabs? Camera Tokens Future

null 6 Sep 12, 2022
Different Java Loggers Benchmarks.

Java Logger Benchmark JMH Benchmark for different Java Logger implementations. Idea of this benchmark is to put all loggers in the same conditions and

ᴀɴᴛᴏɴ 15 Oct 21, 2022
P6Spy is a framework that enables database data to be seamlessly intercepted and logged with no code changes to the application.

p6spy P6Spy is a framework that enables database data to be seamlessly intercepted and logged with no code changes to existing application. The P6Spy

p6spy 1.8k Dec 27, 2022
Best-of-breed OpenTracing utilities, instrumentations and extensions

OpenTracing Toolbox OpenTracing Toolbox is a collection of libraries that build on top of OpenTracing and provide extensions and plugins to existing i

Zalando SE 181 Oct 15, 2022