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.
- 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
Eclair logs annotated Methods and Parameters only.
Implementation is based on standard Spring proxying with all its consequences and limitations.
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
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. |
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>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%nAll 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
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.
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 |
- |
@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 |
- |
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.
@Log.error(level = WARN, ofType = {NullPointerException.class, IndexOutOfBoundsException.class})
@Log.error(exclude = Error.class)
void filterErrors(Throwable throwable) throws Throwable {
throw throwable;
}filterErrors(new NullPointerException());
filterErrors(new Exception());
filterErrors(new Error());| 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 |
- |
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 |
- |
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() {
}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.
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() {
}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 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) {
}DEBUG [length=8, dto=Dto{i=12, s='password'}] r.t.e.example.Example.mdcByArgument > dto=Dto{i=12, s='password'}
Inject ManualLogger implementation for manual logging.
If execution time is important to you, manual logging with
ManualLoggeris not recommended.
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 |
- |
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
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.