Library Name: Audit Library
Purpose: Lightweight, annotation-driven audit logging for Spring Boot applications using AspectJ AOP
This library provides automatic method-level auditing through a simple @Audit annotation.
When a method is annotated with @Audit, the following data is captured and persisted in a dedicated database:
- Execution timestamp
- Current user (resolved via
AuditResolver) - Application/module identifier (resolved via
AuditResolver) - Event type (custom value or default
"METHOD_EXECUTED") - Method name
- Input parameters (JSON-serialized, with safe handling of
MultipartFile) - Method result / response body (JSON-serialized)
- HTTP status code (when method returns
ResponseEntity)
Auditing uses a separate DataSource, EntityManagerFactory and TransactionManager — isolated from the main application database.
- Zero-configuration auditing via annotation
- JSON serialization of arguments and results
- Special handling for file uploads (
MultipartFile→ metadata only) - Pluggable user & application context via
AuditResolver - Automatic detection of
ResponseEntityresponses - Dedicated audit datasource (
audit.datasource.*properties) - Uses
hibernate.hbm2ddl.auto=update(development-friendly)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Audit {
String value() default "";
}@Audit("PATIENT_CREATED")
public ResponseEntity<PatientDto> createPatient(@RequestBody PatientCreateDto dto) { … }
@Audit
public List<Appointment> findAppointmentsByPatient(Long patientId) { … } // → "METHOD_EXECUTED"
@Audit("REPORT_DOWNLOADED")
public ResponseEntity<Resource> downloadMedicalReport(String reportId) { … }public interface AuditResolver {
@Nullable String resolveCurrentUser();
@Nullable String resolveApplicationIdentifier();
}@Configuration
public class AuditConfig {
@Value("${audit.applicationName}")
private String applicationName;
@Bean
@Primary
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
@Bean
public AuditResolver auditUserResolver() {
return new AuditResolver() {
@Override
public String resolveCurrentUser() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (!(authentication instanceof JwtAuthenticationToken)
|| !authentication.isAuthenticated()) {
return null;
}
Jwt jwt = (Jwt) authentication.getPrincipal();
return jwt.getClaim("name");
}
@Override
public String resolveApplicationIdentifier() {
return applicationName;
}
};
}
}@Entity
@Table(name = "audit_events", indexes = {
@Index(name = "idx_audit_timestamp", columnList = "timestamp"),
@Index(name = "idx_audit_user", columnList = "userName"),
@Index(name = "idx_audit_event_type", columnList = "eventType"),
@Index(name = "idx_audit_method_name", columnList = "methodName"),
@Index(name = "idx_audit_app_identification", columnList = "appIdentification")
})
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class AuditEvent {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private LocalDateTime timestamp;
private String userName;
private String appIdentification;
private String eventType;
private String methodName;
@Column(columnDefinition = "TEXT")
private String methodResult;
private String methodResultStatus;
@Column(columnDefinition = "TEXT")
private String parameters;
}The following indexes are defined to improve query performance:
| Index Name | Column | Purpose |
|---|---|---|
idx_audit_timestamp |
timestamp |
Speeds up time-based queries (e.g., logs within a date range) |
idx_audit_user |
userName |
Optimizes filtering by user |
idx_audit_event_type |
eventType |
Improves queries by event category |
idx_audit_method_name |
methodName |
Helps trace specific method executions |
| Field Name | Type | Description |
|---|---|---|
id |
Long |
Primary key (auto-generated) |
timestamp |
LocalDateTime |
Time when the event occurred |
userName |
String |
User who triggered the event |
appIdentification |
String |
Identifier of the application |
eventType |
String |
Type/category of the event |
methodName |
String |
Name of the executed method |
methodResult |
String (TEXT) |
Result/output of the method |
methodResultStatus |
String |
Status of execution (e.g., 200, 401) |
parameters |
String (TEXT) |
Input parameters passed to the method |
audit:
datasource:
url: [DB_URL]
username: [DB_USERNAME]
password: [DB_PASSWORD]** Audit Library**
Current date reference: March 2026
The library is deliberately lightweight and tries to reuse Spring Boot starters where possible.
<!-- Spring Boot starters -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId> <!-- brings AspectJ -->
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jackson</artifactId>
</dependency>
<!-- Jackson (used for serialization of params & results) -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency># Clean & build (including tests if you have them)
mvn clean package
# Skip tests (common in CI when no integration tests yet)
mvn clean package -DskipTests
# Install to local .m2 repository (useful for local testing)
mvn clean install
# Deploy to your company's GitLab / Nexus / Artifactory
mvn clean deploy
<distributionManagement>
<repository>
<id>repo-name</id>
<url>repo-url>
</repository>
</distributionManagement>
<dependency>
<groupId>audit</groupId>
<artifactId>audit</artifactId>
<version>1.0.0</version> <!-- or latest version -->
</dependency>
<repositories>
<repository>
<id>repo-name</id>
<url>repo-url>
</repository>
</repositories>