spring-boot-starter-data-logging
Spring-Boot starter for reducing logging boilerplate with Spring-AOP annotations. Takes advantage of tracing and logging capabilities in Spring-Data, Spring-Cloud-Sleuth, and Lombok.
This library is a pure AOP implementation without Java reflection, which allows for compilation on GraalVM and use on high performance frameworks such as Quarkus and Micronaut.
Basic Method Wrapper
Any method on a Spring managed bean can be wrapped with logging by annotating with @Log.Around
.
@Component
public class SomeBean {
@Log.Around
public String someMethod(String param) {
return "someReturn";
}
}
2020-08-22 18:36:28,811 c.e.d.SomeBean DEBUG : before [method=someMethod, args=("someParam")]
2020-08-22 18:36:28,813 c.e.d.SomeBean DEBUG : after [method=someMethod, return="someReturn"]
Change Log Level
@Component
public class SomeBean {
// change log level with @Log.{Level}.{Pointcut}
@Log.Info.Around
public String someMethod(String param) {
return "foobar"
}
// or, on the @Log.{Pointcut} attribute
@Log.Around(withLevel = Log.Level.Info)
public String otherMethod() {
return "foobar"
}
}
2020-08-22 18:36:28,811 c.e.d.SomeBean INFO : before [method=someMethod, args=("someParam")]
2020-08-22 18:36:28,813 c.e.d.SomeBean INFO : after [method=someMethod, return="someReturn"]
2020-08-22 18:36:28,814 c.e.d.SomeBean INFO : before [method=otherMethod]
2020-08-22 18:36:28,814 c.e.d.SomeBean INFO : after [method=otherMethod, return="otherReturn"]
Additional Pointcuts
@Log.Before
// log before method is invoked
@Log.Before
public String someMethod(String param) {
return "someReturn";
}
2020-08-22 18:45:07,631 c.e.d.SomeBean DEBUG : before [method=someMethod, args=("someParam")]
@Log.After
// log after method has been invoked
@Log.After
public String otherMethod() {
return "otherReturn";
}
2020-08-22 18:45:07,634 c.e.d.SomeBean DEBUG : after [method=otherMethod]
@Log.AfterReturning
// log after method is invoked
// and include any value returned
@Log.AfterReturning
public String otherMethod() {
return "otherReturn";
}
2020-08-22 18:48:58,171 c.e.d.SomeBean DEBUG : after [method=otherMethod, return="otherReturn"]
@Log.AfterThrowing
// log after method is invoked
// and only if exception is thrown
@Log.AfterThrowing
public String someMethod(String param) {
throw new RuntimeException("some error");
}
2020-08-22 18:54:56,499 c.e.d.SomeBean ERROR : thrown [method=someMethod, exception=java.lang.RuntimeException, message=some error]
{stacktrace}
Log Arguments Using Jackson
@Log.Before(withArgs =
@Log.Args(withWriter = JacksonArgWriter.class))
@Log.AfterReturning(withReturnType =
@Log.ReturnType(withWriter = JacksonReturnTypeWriter.class))
public Foo someMethod(Foo foo) {
return foo;
}
2020-08-23 11:31:13,865 c.e.d.SomeBean DEBUG : before [method=someMethod, args=({"name":"foo","description":"foobar"})]
2020-08-23 11:31:13,870 c.e.d.SomeBean DEBUG : after [method=someMethod, return={"name":"foo","description":"foobar"}]
ArgWriter
andReturnTypeWriter
can be globally configured for all methods, see Global Configuration.
Method Configuration
@Log.Before
Log message (with method parameters) before method is invoked
Attribute | Type | Default Value | Description |
---|---|---|---|
withLevel |
@Log.Level |
@Log.Level.Debug |
Level of message when logged |
withArgs |
@Log.Args |
@Log.Args |
Configuration for writing method parameters |
@Log.After
Log message after method is invoked.
Attribute | Type | Default Value | Description |
---|---|---|---|
withLevel |
@Log.Level |
@Log.Level.Debug |
Level of message when logged |
withReturnException |
@Log.ReturnException |
@Log.ReturnException |
Configuration for handling uncaught exceptions |
@Log.AfterReturning
Log message with return value after method is invoked.
Attribute | Type | Default Value | Description |
---|---|---|---|
withLevel |
@Log.Level |
@Log.Level.Debug |
Level of message when logged |
withReturnType |
@Log.ReturnType |
@Log.ReturnType |
Configuration for writing value return from method |
withReturnException |
@Log.ReturnException |
@Log.ReturnException |
Configuration for handling uncaught exceptions |
@Log.AfterThrowing
Log message only after throwing an exception.
Attribute | Type | Default Value | Description |
---|---|---|---|
withLevel |
@Log.Level |
@Log.Level.Debug |
Level of message when logged |
withReturnException |
@Log.ReturnException |
@Log.ReturnException |
Configuration for handling uncaught exceptions |
@Log.Around
Combines other annotations to wrap a method with logging before, after, and on exception.
Attribute | Type | Default Value | Description |
---|---|---|---|
withLevel |
@Log.Level |
@Log.Level.Debug |
Level of message when logged |
withArgs |
@Log.Args |
@Log.Args |
Configuration for writing method parameters |
withReturnType |
@Log.ReturnType |
@Log.ReturnType |
Configuration for writing value return from method |
withReturnException |
@Log.ReturnException |
@Log.ReturnException |
Configuration for handling uncaught exceptions |
@Log.Args
Configuration for logging method parameters
Attribute | Type | Default Value | Description |
---|---|---|---|
enabled |
boolean |
true |
Whether to log method parameters |
withWriter |
ArgWriter |
SimpleArgWriter 1 |
Converts method parameters to String for logging |
1 See Global Configuration to change value for all logging annotations.
@Log.ReturnType
Configuration for logging any value returned from method.
Attribute | Type | Default Value | Description |
---|---|---|---|
enabled |
boolean |
true |
Whether to log return type |
withWriter |
ReturnTypeWriter |
SimpleReturnTypeWriter 1 |
Converts return type to String for logging |
1 See Global Configuration to change value for all logging annotations.
@Log.ReturnException
Configuration for logging any uncaught exception thrown by method.
Attribute | Type | Default Value | Description |
---|---|---|---|
withStacktrace |
boolean |
true |
Whether to log stacktrace on exception |
withOverride |
boolean |
true |
Whether to override withLevel with Log.Level.Error on method annotation when logging exception |
1 See Global Configuration to change value for all logging annotations.
@Log.Level
Supported log levels.
Levels |
---|
Log.Level.Trace |
Log.Level.Debug |
Log.Level.Info |
Log.Level.Warn |
Log.Level.Error |
Log.Level.Fatal |
Global Configuration
By default, SimpleArgWriter
and SimpleReturnTypeWriter
are globally wired for method annotations. These can be changed by wiring a new @Primary
@Bean
for ArgWriter
and ReturnTypeWriter
repectively.
@Configuration
public class LogConfiguration {
@Bean
@Primary
public ArgWriter argWriter(JacksonArgWriter jacksonArgWriter) {
return jacksonArgWriter;
}
@Bean
@Primary
public ReturnTypeWriter returnTypeWriter(JacksonReturnTypeWriter jacksonReturnTypeWriter) {
return jacksonReturnTypeWriter;
}
}
Alternatively, custom writers which implement
ArgWriter
orReturnTypeWriter
can also be wired.
Auto-Logging with Spring-Data-Rest
When spring-boot-starter-data-rest
is on the classpath, pointcuts are automatically applied to the default methods.
GET /employees
2020-08-23 13:28:26,064 tingRepository DEBUG : before [method=findAll, args=({"sort":{"sorted":false,"unsorted":true,"empty":true},"offset":0,"pageNumber":0,"pageSize":20,"paged":true,"unpaged":false})]
2020-08-23 13:28:26,073 tingRepository DEBUG : after [method=findAll, return={"content":[{"id":1,"name":"Alan Turing"}],"pageable":{"sort":{"sorted":false,"unsorted":true,"empty":true},"offset":0,"pageNumber":0,"pageSize":20,"paged":true,"unpaged":false},"last":true,"totalPages":1,"totalElements":1,"size":20,"number":0,"sort":{"sorted":false,"unsorted":true,"empty":true},"numberOfElements":1,"first":true,"empty":false}]
GET /employees/1
2020-08-23 13:23:51,586 CrudRepository DEBUG : before [method=findById, args=(1)]
2020-08-23 13:23:51,611 CrudRepository DEBUG : after [method=findById, return={"id":1,"name":"Alan Turing"}]
POST /employees
2020-08-23 13:22:07,634 CrudRepository DEBUG : before [method=save, args=({"name":"Alan Turing"})]
2020-08-23 13:22:07,646 CrudRepository DEBUG : after [method=save, return={"id":1,"name":"Alan Turing"}]
PATCH /employees/1
2020-08-23 13:27:02,567 CrudRepository DEBUG : before [method=save, args=({"id":1,"name":"Alan Turing"})]
2020-08-23 13:27:02,580 CrudRepository DEBUG : after [method=save, return={"id":1,"name":"Alan Turing"}]
PUT /employees/1
2020-08-23 13:26:10,042 CrudRepository DEBUG : before [method=save, args=({"id":1,"name":"Alan Turing"})]
2020-08-23 13:26:10,055 CrudRepository DEBUG : after [method=save, return={"id":1,"name":"Alan Turing"}]
DELETE /employees/1
2020-08-23 13:25:28,326 CrudRepository DEBUG : before [method=deleteById, args=(1)]
2020-08-23 13:25:28,341 CrudRepository DEBUG : after [method=deleteById]
Auto-Logging with Spring-Data-JPA
When spring-boot-starter-data-jpa
is on the classpath, the inherited methods of JpaRepository<T,ID>
are automatically applied.
@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> { }
@Service
@RequiredArgsConstructor
public class EmployeeService {
private final EmployeeRepository employeeRepository;
public Optional<Employee> findEmployeeByName(String name) {
var exampleEmployee = new Employee();
exampleEmployee.setName(name);
// findOne(..) from JpaRepository automatically pointcut for logs
return employeeRepository.findOne(Example.of(exampleEmployee));
}
}
2020-08-23 13:48:43.697 QueryByExampleExecutor DEBUG : before [method=findOne, args=({"probe":{"name":"Alan Turing"},"matcher":{"nullHandler":"IGNORE","defaultStringMatcher":"DEFAULT","propertySpecifiers":{"specifiers":[]},"ignoredPaths":[],"ignoreCaseEnabled":false,"matchMode":"ALL","allMatching":true,"anyMatching":false},"probeType":"com.example.demo.model.Employee"})]
2020-08-23 13:48:43.705 QueryByExampleExecutor DEBUG : after [method=findOne, return={"id":1,"name":"Alan Turing"}]
Auto-Logging with Spring-Cloud-Sleuth
When spring-cloud-starter-sleuth
is on the classpath with spring-boot-starter-data-rest
, spans are automatically applied to all repository methods.
POST /employees
2020-08-23 13:33:48.358 [demo,18aa9a69178dcc3f,e555838da7405451] CrudRepository DEBUG: before [method=save, args=({"name":"Alan Turing"})]
2020-08-23 13:33:48.370 [demo,18aa9a69178dcc3f,e555838da7405451] CrudRepository DEBUG : after [method=save, return={"id":1,"name":"Alan Turing"}]
Supported Dependencies
Package | Version |
---|---|
Spring-Boot-Starter | 2.3.1-RELEASE |
Spring-Boot-Starter-AOP | 2.3.1-RELEASE 1 |
Spring-Boot-Starter-Data-Rest | 2.3.1-RELEASE 1 |
Spring-Boot-Starter-Data-JPA | 2.3.1-RELEASE 1 |
Spring-Boot-Starter-Data-MongoDB | 2.3.1-RELEASE 1 |
Spring-Cloud | Hoxton.SR6 |
Spring-Cloud-Starter-Sleuth | 2.2.3.RELEASE 2 |
Lombok | 1.18.12 1 |
Jackson-Databind | 2.11.0 1 |
Java | 11 |
1 Dependency versioning inherited from Spring-Boot-Starter
.
2 Dependency versioning inherited from Spring-Cloud
.
Maven Integration
<dependency>
<groupId>io.whelk.spring.data.logging</groupId>
<artifactId>spring-boot-starter-data-logging</artifactId>
<version>${spring-boot-starter-data-logging.version}</version>
</dependency>