eclair
eclair copied to clipboard
Java Spring library for declarative logging
Eclair
Eclair - Java Spring library for AOP logging.
Provides annotations for declarative logging of annotated method execution. Includes abstractions for annotations processing, simple implementation and Spring Boot starter with auto-configuration.
Features
- events logging detected by Spring AOP: beginning, ending or emergency ending of method execution
- flexible filtering
Throwabletypes for logging - configurable verbosity based on the enabled log level
- pre-defined printers to log arguments or method return value in different formats:
JSON(by Jackson)XML(by JAXB)
- declarative defining (with SpEL) and erasing of Mapped Diagnostic Context (MDC) based on scopes
- multiple logger definition
- annotations validation during application context start
- ability to use meta-annotations (applied to other annotations) and annotated method overriding
- manual logging with invoker class detection is also available
Annotations
Eclair logs annotated Methods and Parameters only.
Implementation is based on standard Spring proxying with all its consequences and limitations.
@Log
Annotated Method is able to log beginning and ending (except the emergency ending) of execution.
Works the same as both @Log.in and @Log.out annotations with all matching attribute values.
Note: emergency ending of the method execution should be specified separately by @Log.error annotation.
Can be defined on Parameter and specify logging settings for it.
Should have unique logger value per annotated element.
Parameter-level annotation has higher priority settings than Method-level with same logger value.
| Attribute | Description |
|---|---|
level |
Expected level to log beginning and ending of method execution. |
ifEnabled |
Enables logging with level only if specified here level is enabled for the current logger too.Ignored by default. |
verbose |
If specified log-level is enabled for the current logger activates detailed logging.For annotated Method verbose log includes argument/return values.For annotated Parameter verbose log includes argument name.Note: it is assumed that OFF deactivates verbose logging of annotated element for any level. |
printer |
Determines Printer implementation by specified bean name (or alias).The printer will be used to convert argument/return values from raw type to String.Note: if not specified highest priority compatible printer or PrinterResolver#defaultPrinter will be used. |
logger |
Determines EclairLogger implementation by specified bean name (or alias) which should process this annotation.Note: if not specified single candidate or Primary bean will be used for processing. |
See also
@Log.in,@Log.outand@Log.errorannotations and their specific attributes
@Mdc
Defines MDC (Mapped Diagnostic Context) entry. MDC is level-insensitive.
Before method execution beginning, @Mdc will be processed first and after ending cleared last.
So annotations @Log / @Log.in / @Log.out of the same method will be processed inside @Mdc processing.
| Attribute | Description |
|---|---|
key |
Key of the MDC entry. If empty, it will be synthesized by code meta-data: annotated method or parameter name. Note: It is not always possible to obtain information about parameter names at runtime. In that case, MDC keys will contain method name and parameter index. |
value |
Value of the MDC entry. Can contain SpEL (Spring Expression Language) and invoke static methods or beans by id from the ApplicationContext.If empty, it will be synthesized by code meta-data: annotated parameter value (or each parameter of annotated method). |
global |
Key/value pair defined by this annotation automatically cleared after exit from the method by default.global MDC is available within ThreadLocal scope. |
Getting started
Eclair compatible with Java 8, Spring Boot 1.5.0+.
Add this to your POM:
<dependency>
<groupId>ru.tinkoff</groupId>
<artifactId>eclair-spring-boot-starter</artifactId>
<version>0.9.0</version>
</dependency>
Usage examples
The examples assume that you are using a standard SimpleLogger and that you have the following configuration property:
logging.pattern.console: %d{yyyy-MM-dd HH:mm:ss.SSS UTC} [%thread] %-5level [%X] %logger{35} %msg%n
All available log levels in order from the most common TRACE to the rarest OFF:
OFFdeactivates logging completelyERRORandFATALare identical (ERRORis used everywhere)
TRACE > DEBUG > INFO > WARN > ERROR = FATAL > OFF
Used Spring Boot log levels enum:
org.springframework.boot.logging.LogLevel
Declarative logging
The left table column shows the configured available logging level for the current method.
The right column shows a log sample or the specified level.
Simplest
DEBUG level by default.
@Log
void simple() {
}
| Enabled level | Log sample |
|---|---|
TRACE DEBUG |
DEBUG [] r.t.eclair.example.Example.simple >DEBUG [] r.t.eclair.example.Example.simple < |
INFO .. OFF |
- |
Influence of configured level to verbosity
@Log(INFO)
boolean verbose(String s, Integer i, Double d) {
return false;
}
| Enabled level | Log sample |
|---|---|
TRACE DEBUG |
INFO [] r.t.eclair.example.Example.verbose > s="s", i=4, d=5.6INFO [] r.t.eclair.example.Example.verbose < false |
INFO |
INFO [] r.t.eclair.example.Example.verbose >INFO [] r.t.eclair.example.Example.verbose < |
WARN .. OFF |
- |
Try to print arguments by Jaxb2Printer as XML
You can specify printer's bean name or alias.
@Log(printer = "jaxb2Printer")
void verboseXml(Dto dto, Integer i) {
}
| Enabled level | Log sample |
|---|---|
TRACE DEBUG |
DEBUG [] r.t.eclair.example.Example.xml > dto=<dto><i>4</i><s>k</s></dto>, i=7DEBUG [] r.t.eclair.example.Example.xml < |
INFO .. OFF |
- |
Filter errors by type
Errors can be filtered multiple times by ofType and exclude attributes.
By default ofType contains Throwable and includes all subtypes.
If the thrown exception matches any of @Log.error filters it will be logged according to the settings of the corresponding annotation.
Annotated method
@Log.error(level = WARN, ofType = {NullPointerException.class, IndexOutOfBoundsException.class})
@Log.error(exclude = Error.class)
void filterErrors(Throwable throwable) throws Throwable {
throw throwable;
}
Invocation statement
filterErrors(new NullPointerException());
filterErrors(new Exception());
filterErrors(new Error());
Result log
| Enabled level | Log sample |
|---|---|
TRACE .. WARN |
WARN [] r.t.e.example.Example.filterErrors ! java.lang.NullPointerExceptionjava.lang.NullPointerException: null at ru.tinkoff.eclair.example.ExampleTest.filterErrors(ExampleTest.java:0).. ERROR [] r.t.e.example.Example.filterErrors ! java.lang.Exceptionjava.lang.Exception: null at ru.tinkoff.eclair.example.ExampleTest.filterErrors(ExampleTest.java:0).. |
ERROR FATAL |
ERROR [] r.t.e.example.Example.filterErrors ! java.lang.Exceptionjava.lang.Exception: null at ru.tinkoff.eclair.example.ExampleTest.filterErrors(ExampleTest.java:0).. |
OFF |
- |
Log annotated argument only
Note: If method is not annotated, log string will have the highest level among annotated parameters.
Note: Parameter name printed forTRACEandDEBUGlevels by default.
void parameter(@Log(INFO) Dto dto, String s, Integer i) {
}
| Enabled level | Log sample |
|---|---|
TRACE DEBUG |
INFO [] r.t.e.example.Example.parameter > dto=Dto{i=0, s='u'} |
INFO |
INFO [] r.t.e.example.Example.parameter > Dto{i=0, s='u'} |
WARN .. OFF |
- |
Multiple loggers in application
You can have several EclairLogger implementations in your application context.
This can be useful for logging various slices of information to different targets.
If
loggerattribute not defined, single candidate or@Primarybean will be used.
@Log
@Log(logger = "auditLogger")
void twoLoggers() {
}
Declarative MDC management
MDC - Mapped Diagnostic Context
Key/value pair defined by annotation automatically cleared after exit from the method.
global MDC is available within ThreadLocal scope.
value attribute can contain SpEL expression and invoke static methods or beans by id from the application context.
Note: MDC is level-insensitive and printed every time.
Note: MDC does not guarantee order of elements when printing.
Common usage
Before method execution beginning, @Mdc annotations will be processed first and after ending cleared last.
So annotations @Log / @Log.in / @Log.out of the same method will be processed inside @Mdc processing.
@Log
void outer() {
self.mdc();
}
@Mdc(key = "static", value = "string")
@Mdc(key = "sum", value = "1 + 1", global = true)
@Mdc(key = "beanReference", value = "@jacksonPrinter.print(new ru.tinkoff.eclair.example.Dto())")
@Mdc(key = "staticMethod", value = "T(java.util.UUID).randomUUID()")
@Log
void mdc() {
self.inner();
}
@Log.in
void inner() {
}
Log sample
DEBUG [] r.t.eclair.example.Example.outer >
DEBUG [beanReference={"i":0,"s":null}, sum=2, static=string, staticMethod=01234567-89ab-cdef-ghij-klmnopqrstuv] r.t.eclair.example.Example.mdc >
DEBUG [beanReference={"i":0,"s":null}, sum=2, static=string, staticMethod=01234567-89ab-cdef-ghij-klmnopqrstuv] r.t.eclair.example.Example.inner >
DEBUG [beanReference={"i":0,"s":null}, sum=2, static=string, staticMethod=01234567-89ab-cdef-ghij-klmnopqrstuv] r.t.eclair.example.Example.mdc <
DEBUG [sum=2] r.t.eclair.example.Example.outer <
MDC defined by argument
MDC can get access to annotated parameter value with SpEL as root object of evaluation context.
@Log.in
void mdcByArgument(@Mdc(key = "dto", value = "#this")
@Mdc(key = "length", value = "s.length()") Dto dto) {
}
Log sample
DEBUG [length=8, dto=Dto{i=12, s='password'}] r.t.e.example.Example.mdcByArgument > dto=Dto{i=12, s='password'}
Manual logging
Inject ManualLogger implementation for manual logging.
If execution time is important to you, manual logging with
ManualLoggeris not recommended.
Manual
Expensive calculations may be wrapped into Supplier for lazy initialization.
@Autowired
private ManualLogger logger;
@Log
void manual() {
logger.info("Eager logging: {}", Math.PI);
logger.debug("Lazy logging: {}", (Supplier) () -> Math.PI);
}
| Enabled level | Log sample |
|---|---|
TRACE DEBUG |
DEBUG [] r.t.eclair.example.Example.manual >INFO [] r.t.eclair.example.Example.manual - Eager logging: 3.141592653589793DEBUG [] r.t.eclair.example.Example.manual - Lazy logging: 3.141592653589793DEBUG [] r.t.eclair.example.Example.manual < |
INFO |
INFO [] r.t.eclair.example.Example.manual - Eager logging: 3.141592653589793 |
WARN .. OFF |
- |
Release History
19.05.2023 - 0.9.0 SpringBoot 3.0 support
21.01.2022 - 0.8.4 Make SimpleLogger more extendable
07.05.2018 - 0.8.3 Corrected optional classes usage in auto-configuration
06.05.2018 - 0.8.2 Published on Maven Central Repository
25.04.2018 - 0.8.1 Removed Lombok dependency
24.04.2018 - 0.8.0 Basic features
License
Copyright 2018 Tinkoff Bank
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.