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.

License

License

Categories

Categories

Spring Boot Container Microservices Data Logging Application Layer Libs
GroupId

GroupId

io.whelk.spring.data.logging
ArtifactId

ArtifactId

spring-boot-starter-data-logging
Last Version

Last Version

0.1.3
Release Date

Release Date

Type

Type

jar
Description

Description

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.
Project URL

Project URL

https://github.com/whelk-io/spring-boot-starter-data-logging
Source Code Management

Source Code Management

https://github.com/whelk-io/spring-boot-starter-data-logging.git

Download spring-boot-starter-data-logging

How to add to project

<!-- https://jarcasting.com/artifacts/io.whelk.spring.data.logging/spring-boot-starter-data-logging/ -->
<dependency>
    <groupId>io.whelk.spring.data.logging</groupId>
    <artifactId>spring-boot-starter-data-logging</artifactId>
    <version>0.1.3</version>
</dependency>
// https://jarcasting.com/artifacts/io.whelk.spring.data.logging/spring-boot-starter-data-logging/
implementation 'io.whelk.spring.data.logging:spring-boot-starter-data-logging:0.1.3'
// https://jarcasting.com/artifacts/io.whelk.spring.data.logging/spring-boot-starter-data-logging/
implementation ("io.whelk.spring.data.logging:spring-boot-starter-data-logging:0.1.3")
'io.whelk.spring.data.logging:spring-boot-starter-data-logging:jar:0.1.3'
<dependency org="io.whelk.spring.data.logging" name="spring-boot-starter-data-logging" rev="0.1.3">
  <artifact name="spring-boot-starter-data-logging" type="jar" />
</dependency>
@Grapes(
@Grab(group='io.whelk.spring.data.logging', module='spring-boot-starter-data-logging', version='0.1.3')
)
libraryDependencies += "io.whelk.spring.data.logging" % "spring-boot-starter-data-logging" % "0.1.3"
[io.whelk.spring.data.logging/spring-boot-starter-data-logging "0.1.3"]

Dependencies

compile (3)

Group / Artifact Type Version
org.springframework.boot : spring-boot-starter jar
org.springframework.boot : spring-boot-starter-aop jar
org.projectlombok : lombok Optional jar

provided (5)

Group / Artifact Type Version
org.springframework.cloud : spring-cloud-starter-sleuth jar
org.springframework.boot : spring-boot-starter-data-rest jar
org.springframework.boot : spring-boot-starter-data-jpa jar
org.springframework.boot : spring-boot-starter-data-mongodb jar
com.fasterxml.jackson.core : jackson-databind jar

test (1)

Group / Artifact Type Version
org.springframework.boot : spring-boot-starter-test jar

Project Modules

There are no modules declared in this project.

spring-boot-starter-data-logging

CodeFactor release

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 and ReturnTypeWriter 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 SimpleArgWriter1 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 SimpleReturnTypeWriter1 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 or ReturnTypeWriter 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>
io.whelk.spring.data.logging

whelk.io

OSS libraries

Versions

Version
0.1.3
0.1.2
0.1.1