Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/frameworks/log4j2.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Condition hasAnyMarkers = (level, context) -> {
If you need to get the Log4j logger from a core logger, you can cast and call `core.logger()`:

```java
Logger<FieldBuilder> baseLogger = LoggerFactory.getLogger();
Logger baseLogger = LoggerFactory.getLogger();
Log4JCoreLogger core = (Log4JCoreLogger) baseLogger.core();
org.apache.logging.log4j.Logger log4jLogger = core.logger();
```
Expand Down
2 changes: 1 addition & 1 deletion docs/frameworks/logback.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ LogstashCoreLogger core = (LogstashCoreLogger) CoreLoggerFactory.getLogger();
The `LogstashCoreLogger` has a `withMarkers` method that takes an SLF4J marker:

```java
Logger<FieldBuilder> logger = LoggerFactory.getLogger(
Logger logger = LoggerFactory.getLogger(
core.withMarkers(MarkerFactory.getMarker("SECURITY")), FieldBuilder.instance);
```

Expand Down
2 changes: 1 addition & 1 deletion docs/usage/context.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ loggerWithFoo.info("JSON field will log automatically")

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

One thing to be aware of that the popular idiom of using `public static final Logger<MyFieldBuilder> logger` can be limiting in cases where you want to include context data. For example, if you have a number of objects with their own internal state, it may be more appropriate to create a logger field on the object.
One thing to be aware of that the popular idiom of using `public static final Logger logger` can be limiting in cases where you want to include context data. For example, if you have a number of objects with their own internal state, it may be more appropriate to create a logger field on the object.

```java
public class PlayerData {
Expand Down
7 changes: 3 additions & 4 deletions docs/usage/filters.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Filters

There are times when you want to add a field or a condition to all loggers. Although you can wrap individual loggers or create your own wrapper around `LoggerFactory`, this can be a labor-intensive process that requires lots of code modification, and must be handled for fluent, semantic, async, and regular loggers.
There are times when you want to add a field or a condition to all loggers.

Echopraxia includes filters that wrap around the `CoreLogger` returned by `CoreLoggerFactory` that provides the ability to modify the core logger from a single pipeline in the code.

Expand All @@ -12,8 +12,7 @@ package example;
public class ExampleFilter implements CoreLoggerFilter {
@Override
public CoreLogger apply(CoreLogger coreLogger) {
var fb = FieldBuilder.instance();
return coreLogger.withFields(fb.bool("uses_filter", true));
return coreLogger.withFields(fb -> fb.bool("uses_filter", true), FieldBuilder.instance());
}
}
```
Expand Down Expand Up @@ -48,7 +47,7 @@ public class SystemInfoFilter implements CoreLoggerFilter {

// Now you can add conditions based on these fields, and conditionally
// enable logging based on your load and memory!

var fb = FieldBuilder.instance();
Field loadField = fb.object("load_average", //
fb.number("1min", loadAverage[0]), //
fb.number("5min", loadAverage[1]), //
Expand Down
78 changes: 40 additions & 38 deletions docs/usage/logger.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@

# Custom Logger

If you are using a particular set of field builders for your domain and want them available by default, it's easy to create your own logger with your own field builder, using the support classes and interfaces.

Creating your own logger will also remove the type parameter from your code, so you don't have to type `Logger<PersonFieldBuilder>` everywhere, and allow you to create custom methods that leverage field builders.
If you want to create a custom `Logger` class that has its own methods, you can do so easily.

If you want to make sure your logger is the only one available, you should import only the API:
First add the `simple` module:

Maven:

Expand All @@ -23,46 +20,51 @@ Gradle:
implementation "com.tersesystems.echopraxia:simple:<VERSION>"
```

And then continuing on from the [custom field builder example](https://github.com/tersesystems/echopraxia-examples/blob/main/custom-field-builder/README.md), you can build a `PersonLogger`:

```

```

and a custom logger factory:
And then add a custom logger factory:

```java
public final class PersonLoggerFactory {

private static final PersonFieldBuilder myFieldBuilder = PersonFieldBuilder.instance;

// the class containing the error/warn/info/debug/trace methods
private static final String FQCN = DefaultLoggerMethods.class.getName();

public static PersonLogger getLogger(Class<?> clazz) {
return getLogger(CoreLoggerFactory.getLogger(FQCN, clazz.getName()));
}

public static PersonLogger getLogger(String name) {
return getLogger(CoreLoggerFactory.getLogger(FQCN, name));
}

public static PersonLogger getLogger() {
return getLogger(CoreLoggerFactory.getLogger(FQCN, Caller.resolveClassName()));
}

public static PersonLogger getLogger(@NotNull CoreLogger core) {
return new PersonLogger(core, myFieldBuilder, PersonLogger.class);
}
class MyLoggerFactory {
public static class MyFieldBuilder implements FieldBuilder {
// Add your own field builder methods in here
}

public static final MyFieldBuilder FIELD_BUILDER = new MyFieldBuilder();

public static MyLogger getLogger(Class<?> clazz) {
final CoreLogger core = CoreLoggerFactory.getLogger(Logger.class.getName(), clazz);
return new MyLogger(core);
}

public static final class MyLogger extends Logger {
public static final String FQCN = MyLogger.class.getName();

public MyLogger(CoreLogger logger) {
super(logger);
}

public void notice(String message) {
// the caller is MyLogger specifically, so we need to let the logging framework know how to
// address it.
core().withFQCN(FQCN)
.withFields(fb -> fb.bool("notice", true), FIELD_BUILDER)
.log(Level.INFO, message);
}
}
}

```

and then you can log a person as a raw parameter:
and then you can log with a `notice` method:

```java
PersonLogger logger = PersonLoggerFactory.getLogger();
Person abe = ...
logger.info("Best person: {}", abe);
class Main {
private static final MyLoggerFactory.MyLogger logger = MyLoggerFactory.getLogger(Main.class);

public static void main(String[] args) {
logger.notice("this has a notice field added");
}
}
```

Generally loggers should be final, and any common functionality should be moved out to interfaces you can share. This is because subclassing can have an impact on JVM optimizations, and can make returning specific types from `with*` methods more complicated.
There is no cache associated with logging, but adding a cache with `ConcurrentHashMap.computeIfAbsent` is straightforward.
3 changes: 1 addition & 2 deletions docs/usage/scripting.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@ implementation "com.tersesystems.echopraxia:scripting:<VERSION>"

## Script Syntax

The call site for a script is the function `evaluate` inside a library called `echopraxia`. The level and context are
passed through as `(string level, dict ctx)`, where `ctx` is a dictionary of functions that connect back to the logging context.
The call site for a script is the function `evaluate` inside a library called `echopraxia`. The level and context are passed through as `(string level, dict ctx)`, where `ctx` is a dictionary of functions that connect back to the logging context.

Methods in the context are snake case, separated by underscores. For example, to call the equivalent of `ctx.findString("$.person.name")`, you would call `ctx[:find_string]("$.person.name")`.

Expand Down
5 changes: 4 additions & 1 deletion simple/src/test/java/echopraxia/MyLogger.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ public MyLogger(CoreLogger logger) {
public void notice(String message) {
// the caller is MyLogger specifically, so we need to let the logging framework know how to
// address it.
core().withFQCN(FQCN).log(Level.INFO, message, fb -> fb.bool("notice", true), FIELD_BUILDER);
core()
.withFQCN(FQCN)
.withFields(fb -> fb.bool("notice", true), FIELD_BUILDER)
.log(Level.INFO, message);
}
}
}
Expand Down
Loading