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
Throwable
types 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 Method
s and Parameter
s 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.out
and@Log.error
annotations 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.8.3</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%n
All available log levels in order from the most common TRACE
to the rarest OFF
:
OFF
deactivates logging completelyERROR
andFATAL
are identical (ERROR
is 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.6 INFO [] 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=7 DEBUG [] 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.NullPointerException java.lang.NullPointerException: null at ru.tinkoff.eclair.example.ExampleTest.filterErrors(ExampleTest.java:0) .. ERROR [] r.t.e.example.Example.filterErrors ! java.lang.Exception java.lang.Exception: null at ru.tinkoff.eclair.example.ExampleTest.filterErrors(ExampleTest.java:0) .. |
ERROR FATAL |
ERROR [] r.t.e.example.Example.filterErrors ! java.lang.Exception java.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 forTRACE
andDEBUG
levels 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
logger
attribute not defined, single candidate or@Primary
bean 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
ManualLogger
is 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.141592653589793 DEBUG [] r.t.eclair.example.Example.manual - Lazy logging: 3.141592653589793 DEBUG [] r.t.eclair.example.Example.manual < |
INFO |
INFO [] r.t.eclair.example.Example.manual - Eager logging: 3.141592653589793 |
WARN .. OFF |
- |
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.