diff --git a/docs/frameworks/log4j2.md b/docs/frameworks/log4j2.md index 42d1b6a9..c4626192 100644 --- a/docs/frameworks/log4j2.md +++ b/docs/frameworks/log4j2.md @@ -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 baseLogger = LoggerFactory.getLogger(); +Logger baseLogger = LoggerFactory.getLogger(); Log4JCoreLogger core = (Log4JCoreLogger) baseLogger.core(); org.apache.logging.log4j.Logger log4jLogger = core.logger(); ``` diff --git a/docs/frameworks/logback.md b/docs/frameworks/logback.md index 1d2eaf89..46f6153b 100644 --- a/docs/frameworks/logback.md +++ b/docs/frameworks/logback.md @@ -15,7 +15,7 @@ LogstashCoreLogger core = (LogstashCoreLogger) CoreLoggerFactory.getLogger(); The `LogstashCoreLogger` has a `withMarkers` method that takes an SLF4J marker: ```java -Logger logger = LoggerFactory.getLogger( +Logger logger = LoggerFactory.getLogger( core.withMarkers(MarkerFactory.getMarker("SECURITY")), FieldBuilder.instance); ``` diff --git a/docs/usage/context.md b/docs/usage/context.md index 3eafc875..eccdc390 100644 --- a/docs/usage/context.md +++ b/docs/usage/context.md @@ -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 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 { diff --git a/docs/usage/filters.md b/docs/usage/filters.md index 3298cbd4..8e795950 100644 --- a/docs/usage/filters.md +++ b/docs/usage/filters.md @@ -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. @@ -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()); } } ``` @@ -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]), // diff --git a/docs/usage/logger.md b/docs/usage/logger.md index 5c407897..41e8f813 100644 --- a/docs/usage/logger.md +++ b/docs/usage/logger.md @@ -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` 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: @@ -23,46 +20,51 @@ Gradle: implementation "com.tersesystems.echopraxia:simple:" ``` -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. \ No newline at end of file diff --git a/docs/usage/scripting.md b/docs/usage/scripting.md index e08eecde..c123a808 100644 --- a/docs/usage/scripting.md +++ b/docs/usage/scripting.md @@ -30,8 +30,7 @@ implementation "com.tersesystems.echopraxia:scripting:" ## 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")`. diff --git a/simple/src/test/java/echopraxia/MyLogger.java b/simple/src/test/java/echopraxia/MyLogger.java index 2cef628b..4ee553c3 100644 --- a/simple/src/test/java/echopraxia/MyLogger.java +++ b/simple/src/test/java/echopraxia/MyLogger.java @@ -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); } } }