Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,35 @@ public DaoSupportLogger daoSupportLogger() {
`Unable to proxy interface-implementing method [public final void org.springframework.dao.support.DaoSupport.afterPropertiesSet() throws java.lang.IllegalArgumentException,org.springframework.beans.factory.BeanInitializationException]
because it is marked as final, consider using interface-based JDK proxies instead.`

The aspects above rely on classes either extending specific base classes or being annotated with particular annotations. `BeanLogger` has
been introduced to add logging to arbitrary beans whether they are annotated with annotations such as `@Component` or whether they are
instantiated with a factory method. Logging for arbitrary beans is enabled with the `EnableBeanLogging` annotation:

```java
@Configuration
@EnableBeanLogging(
excludedClasses = MyExcludedBean.class,
includeBasePackageClasses = {
MyLoggedBean.class
}
)
static class MyConfiguration {
@Bean
MyLoggedClass myLoggedBean() {
return new MyLoggedClass();
}

@Bean
MyExcludedBean myExcludedBean() {
return new MyExcludedBean();
}
}
```

*NOTE* `excludedClasses` only excludes the class from logging being added with `BeanLogger` and as such, logging could still be added
with one of the other aspects from this librar, such as `DaoSupportLogger` if it extends the right base class or is annotated with the
right annotation.

Building locally
================

Expand Down
3 changes: 3 additions & 0 deletions config/checkstyle/spt_checks_suppressions.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
"https://checkstyle.org/dtds/suppressions_1_2.dtd">
<suppressions>
<!-- Allow Logger aspect methods to throw Throwable -->
<suppress checks="IllegalThrows" files="src[\\/]main[\\/]java[\\/]com[\\/]spt[\\/]development[\\/]logging[\\/]spring[\\/]invocation[\\/]LoggedInvocation.java" />
<suppress checks="IllegalThrows" files="src[\\/]main[\\/]java[\\/]com[\\/]spt[\\/]development[\\/]logging[\\/]spring[\\/]invocation[\\/]ProceedingJoinPointAdapter.java" />
<suppress checks="IllegalThrows" files="src[\\/]main[\\/]java[\\/]com[\\/]spt[\\/]development[\\/]logging[\\/]spring[\\/]InvocationLogger.java" />
<suppress checks="IllegalThrows" files="src[\\/]main[\\/]java[\\/]com[\\/]spt[\\/]development[\\/]logging[\\/]spring[\\/]LoggerAspect.java" />
<suppress checks="IllegalThrows" files="src[\\/]main[\\/]java[\\/]com[\\/]spt[\\/]development[\\/]logging[\\/]spring[\\/](JmsListener|RestController|Repository|Service)Logger.java" />
</suppressions>
6 changes: 6 additions & 0 deletions config/findbugs/findbugs-exclude.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,10 @@
<Class name="com.spt.development.logging.spring.ServiceLogger" />
<Bug pattern="SLF4J_SIGN_ONLY_FORMAT" />
</Match>

<!-- No obvious workaround - not possible to take a copy of proceedingJoinPoint -->
<Match>
<Class name="com.spt.development.logging.spring.invocation.ProceedingJoinPointAdapter" />
<Bug pattern="EI_EXPOSE_REP2" />
</Match>
</FindBugsFilter>
5 changes: 5 additions & 0 deletions config/versions/version-rules.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,10 @@
<ignoreVersion type="regex">.*</ignoreVersion>
</ignoreVersions>
</rule>
<rule groupId="org.springframework" artifactId="spring-framework-bom" comparisonMethod="maven">
<ignoreVersions>
<ignoreVersion type="regex">.*</ignoreVersion>
</ignoreVersions>
</rule>
</rules>
</ruleset>
7 changes: 7 additions & 0 deletions documentation/releases/release-3.2.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
## New Features

* Added logging for public methods of any bean of types from configured packages.

## Dependencies

* Aligned dependencies with [Spring Boot 3.3.1](https://github.com/spring-projects/spring-boot/releases/tag/v3.3.1)
47 changes: 34 additions & 13 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

<groupId>com.spt-development</groupId>
<artifactId>spt-development-logging-spring</artifactId>
<version>3.1.2-SNAPSHOT</version>
<version>3.2.0-SNAPSHOT</version>

<name>logging-spring</name>
<description>A very simple library for getting/setting the current correlation ID, utilising ThreadLocal.</description>
Expand Down Expand Up @@ -41,10 +41,10 @@
<aspectj.version>1.9.22</aspectj.version>
<httpcore5.version>5.2.4</httpcore5.version>
<slf4j.version>2.0.13</slf4j.version>
<spring.version>6.1.8</spring.version>
<spring.version>6.1.10</spring.version>

<!-- Test dependency versions -->
<spt-development-test.version>3.1.7</spt-development-test.version>
<spt-development-test.version>3.1.8</spt-development-test.version>

<!-- Test dependency versions, matched to Spring Boot -->
<hamcrest.version>2.2</hamcrest.version>
Expand All @@ -55,31 +55,31 @@
<!-- Plugin versions -->
<build-helper-maven-plugin.version>3.6.0</build-helper-maven-plugin.version>
<checkstyle-maven-plugin.version>3.4.0</checkstyle-maven-plugin.version>
<dependency-check-maven.version>9.2.0</dependency-check-maven.version>
<dependency-check-maven.version>10.0.2</dependency-check-maven.version>
<jacoco-maven-plugin.version>0.8.12</jacoco-maven-plugin.version>
<license-maven-plugin.version>2.4.0</license-maven-plugin.version>
<maven-compiler-plugin.version>3.13.0</maven-compiler-plugin.version>
<maven-dependency-plugin.version>3.6.1</maven-dependency-plugin.version>
<maven-dependency-plugin.version>3.7.1</maven-dependency-plugin.version>
<maven-enforcer-plugin.version>3.5.0</maven-enforcer-plugin.version>
<maven-gpg-plugin.version>3.2.4</maven-gpg-plugin.version>
<maven-javadoc-plugin.version>3.7.0</maven-javadoc-plugin.version>
<maven-jxr-plugin.version>3.4.0</maven-jxr-plugin.version>
<maven-pmd-plugin.version>3.22.0</maven-pmd-plugin.version>
<maven-release-plugin.version>3.0.1</maven-release-plugin.version>
<maven-pmd-plugin.version>3.24.0</maven-pmd-plugin.version>
<maven-release-plugin.version>3.1.0</maven-release-plugin.version>
<maven-scm-plugin.version>2.1.0</maven-scm-plugin.version>
<maven-source-plugin.version>3.3.1</maven-source-plugin.version>
<maven-surefire-plugin.version>3.2.5</maven-surefire-plugin.version>
<maven-surefire-plugin.version>3.3.1</maven-surefire-plugin.version>
<nexus-staging-plugin.version>1.7.0</nexus-staging-plugin.version>
<pitest-maven.version>1.16.1</pitest-maven.version>
<spotbugs.version>4.8.5.0</spotbugs.version>
<versions-maven-plugin.version>2.16.2</versions-maven-plugin.version>
<spotbugs.version>4.8.6.2</spotbugs.version>
<versions-maven-plugin.version>2.17.0</versions-maven-plugin.version>

<!-- Plugin dependencies -->
<checkstyle.version>10.17.0</checkstyle.version>
<findbugs-slf4j-bug-pattern.version>1.5.0</findbugs-slf4j-bug-pattern.version>
<findbugs-sec-bug-pattern.version>1.12.0</findbugs-sec-bug-pattern.version>
<pitest-junit5-plugin.version>1.2.1</pitest-junit5-plugin.version>
<pmd.version>7.2.0</pmd.version>
<pmd.version>7.3.0</pmd.version>
</properties>

<dependencyManagement>
Expand Down Expand Up @@ -118,6 +118,21 @@

<dependencies>
<!-- Spring dependencies -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<!-- Version defined in spring framework bom, imported in dependencyManagement section -->
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<!-- Version defined in spring framework bom, imported in dependencyManagement section -->
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<!-- Version defined in spring framework bom, imported in dependencyManagement section -->
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
Expand Down Expand Up @@ -192,6 +207,12 @@
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
<!-- Version defined in spring framework bom, imported in dependencyManagement section -->
</dependency>

<!-- Test dependencies not directly related to testing -->
<dependency>
Expand Down Expand Up @@ -471,7 +492,7 @@
<limit>
<counter>LINE</counter>
<value>COVEREDRATIO</value>
<minimum>0.97</minimum>
<minimum>0.83</minimum>
</limit>
</limits>
<includes>
Expand Down Expand Up @@ -542,7 +563,7 @@
<configuration>
<mutationThreshold>100</mutationThreshold>
<excludedClasses>
<param>com.spt.development.audit.spring.security.AuthenticationAdapterFactory</param>
<param>com.spt.development.logging.spring.annotation.BeanLoggerConfiguration</param>
</excludedClasses>
</configuration>
<executions>
Expand Down
52 changes: 52 additions & 0 deletions src/main/java/com/spt/development/logging/spring/BeanLogger.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.spt.development.logging.spring;

import com.spt.development.logging.spring.invocation.MethodInvocationAdapter;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

/**
* Logs calls to methods. Intended to be combined with {@link org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor}
* to add logging to the public methods of arbitrary Spring beans.
*/
public class BeanLogger extends InvocationLogger implements MethodInterceptor {

/**
* Creates a new instance of the logger method interceptor. The log statements added by the interceptor will include the
* current correlation ID; see {@link BeanLogger#BeanLogger(boolean)} to disable this behaviour.
*/
public BeanLogger() {
this(true);
}

/**
* Creates a new instance of the logger method interceptor.
*
* @param includeCorrelationIdInLogs a flag to determine whether the correlation ID should be explicitly included
* in the log statements added by the interceptor.
*/
public BeanLogger(final boolean includeCorrelationIdInLogs) {
super(includeCorrelationIdInLogs);
}

/**
* Outputs DEBUG level logging when a public method intercepted by this interceptor is called and when it
* returns (without exception). If TRACE level logging is enabled and the method has a non-<code>void</code>
* return type, the return value will be included in the logging. For example:
*
* <pre>
* [40872057-a1b6-4fdd-bce1-7882929bbce6] MyBean.read(4)
* ...
* [40872057-a1b6-4fdd-bce1-7882929bbce6] MyBean.read Returned: MyEntity(id=4, name=test)
* </pre>
*
* @param invocation the method invocation required for implementing a {@link MethodInterceptor}.
*
* @return the value returned from the method logged.
*
* @throws Throwable thrown if the method logged throws a {@link Throwable}.
*/
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
return super.log(new MethodInvocationAdapter(invocation));
}
}
101 changes: 101 additions & 0 deletions src/main/java/com/spt/development/logging/spring/InvocationLogger.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package com.spt.development.logging.spring;

import com.spt.development.cid.CorrelationId;
import com.spt.development.logging.spring.invocation.LoggedInvocation;
import org.slf4j.LoggerFactory;
import org.slf4j.event.Level;

import java.lang.reflect.Method;
import java.util.function.BiConsumer;

import static com.spt.development.logging.spring.LoggerUtil.formatArgs;

abstract class InvocationLogger {

private final boolean includeCorrelationIdInLogs;
private final boolean isStartAndCompleteMethodLoggedAtInfo;

InvocationLogger(final boolean includeCorrelationIdInLogs) {
this(includeCorrelationIdInLogs, false);
}

InvocationLogger(final boolean includeCorrelationIdInLogs, final boolean isStartAndCompleteMethodLoggedAtInfo) {
this.includeCorrelationIdInLogs = includeCorrelationIdInLogs;
this.isStartAndCompleteMethodLoggedAtInfo = isStartAndCompleteMethodLoggedAtInfo;
}

Object log(final LoggedInvocation invocation) throws Throwable {
final Method method = invocation.getMethod();
final org.slf4j.Logger log = LoggerFactory.getLogger(invocation.getDeclaringClass());

if (log.isEnabledForLevel(isStartAndCompleteMethodLoggedAtInfo ? Level.INFO : Level.DEBUG)) {
startAndCompleteMethodLogger().accept(
log, "{}.{}({})", invocation.getDeclaringClass().getSimpleName(), method.getName(),
formatArgs(method.getParameterAnnotations(), invocation.getArgs()));
}
return proceed(invocation, log);
}

Object proceed(LoggedInvocation invocation, org.slf4j.Logger log) throws Throwable {
final Object result = invocation.proceed();

if (log.isTraceEnabled()) {
final Method method = invocation.getMethod();

if (!method.getReturnType().equals(void.class)) {
trace(log, "{}.{} Returned: {}", invocation.getDeclaringClass().getSimpleName(), method.getName(), result);

return result;
}
}

startAndCompleteMethodLogger().accept(
log, "{}.{} - complete",
invocation.getDeclaringClass().getSimpleName(),
invocation.getMethod().getName()
);
return result;
}

private LoggerConsumer startAndCompleteMethodLogger() {
return isStartAndCompleteMethodLoggedAtInfo ? this::info : this::debug;
}

void trace(org.slf4j.Logger logger, String format, Object... arguments) {
log(logger::trace, format, arguments);
}

void debug(org.slf4j.Logger logger, String format, Object... arguments) {
log(logger::debug, format, arguments);
}

void info(org.slf4j.Logger logger, String format, Object... arguments) {
log(logger::info, format, arguments);
}

void error(org.slf4j.Logger logger, String format, Object... arguments) {
log(logger::error, format, arguments);
}

private void log(BiConsumer<String, Object[]> log, String format, Object[] arguments) {
if (includeCorrelationIdInLogs) {
log.accept("[{}] " + format, addCorrelationIdToArguments(arguments));
return;
}
log.accept(format, arguments);
}

private Object[] addCorrelationIdToArguments(Object[] arguments) {
final Object[] newArguments = new Object[arguments.length + 1];
newArguments[0] = CorrelationId.get();

System.arraycopy(arguments, 0, newArguments, 1, arguments.length);

return newArguments;
}

@FunctionalInterface
public interface LoggerConsumer {
void accept(org.slf4j.Logger log, String format, Object... arguments);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.spt.development.logging.spring;

import com.spt.development.logging.spring.invocation.LoggedInvocation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
Expand Down Expand Up @@ -55,10 +56,10 @@ public Object log(final ProceedingJoinPoint point) throws Throwable {
}

@Override
Object proceed(ProceedingJoinPoint point, Logger log) throws Throwable {
final Object result = point.proceed();
Object proceed(LoggedInvocation invocation, Logger log) throws Throwable {
final Object result = invocation.proceed();

info(log, "{}.{} - complete", point.getTarget().getClass().getSimpleName(), point.getSignature().getName());
info(log, "{}.{} - complete", invocation.getDeclaringClass().getSimpleName(), invocation.getMethod().getName());

return result;
}
Expand Down
Loading