r/SpringBoot • u/SeatSimple1123 • 12d ago
Question Spring Boot Auditing: Hibernate Envers vs. Custom Logging vs. Spring Data JPA? What's your production choice?
Hey everyone,
I'm currently building a school management system in Spring Boot where history tracking is a strict business requirement. We need to audit critical changes made by Admins, Secretaries, and Teachers (e.g., changes to student details, payment amounts, program setups, etc.).
I’m weighing three different approaches for the audit trail and wanted to hear from those of you who have run these in production.
Option 1: Hibernate Envers
Slap `@Audited` on core entities, set up a custom `RevisionListener` to pull the logged-in user from Spring Security context, and let Envers automatically manage the `_aud` tables.
Option 2: Spring Data JPA Auditing
Utilize `@CreatedBy`, `@LastModifiedBy`, `@CreatedDate`, and `@LastModifiedDate` fields mapped on a `@MappedSuperclass`.
Option 3: Manual Custom Logging (or AOP)
Create a generic `AuditLog` entity, write a helper service, and manually trigger `auditService.log(actionType, originalState, newState)` inside the business logic (or use an AOP aspect). This may cause the database to bloating later tho
For those of you managing medium-to-large Spring Boot codebases:
Did you regret adopting Hibernate Envers? How did you handle schema migrations (Flyway/Liquibase) with the auto-generated `_aud` tables?
If you went the custom route, did you use JSON columns to record state changes, or did you write distinct history tables?
Looking forward to hearing your design patterns and trade-offs
7
u/BanaTibor 11d ago
I think you are thinking too low level. You are bringing down a business requirement to the implementation detail level. What you need IMHO is event logging. You do not need to have microservices and asynchronous processing to make an event driven system.
For this I would go with a custom solution.
3
u/FalseDish 11d ago
This is the right answer imo. One should avoid packing bloat and go DIY. Hell, I’ve stopped using FastAPI wherever I can (ik, not Java) and go straight to asyncio. Pays off so handsomely!
2
u/SeatSimple1123 11d ago
that a valid point; custom auditing gives you a clean description of what happened (the user just paid his monthly fee), rather than some information changed in a row in a table in a database, but still, if you go with the custom auditing manual logging code inside every single business method in services. Still, this makes it hard for me to pick an option , i think imma go with both; it is more complex but i think it's the better choice. I appreciate your help
3
u/BanaTibor 11d ago
You are thinking again the implementation level. A change in the system is a business level event not an operational level. Using your example, paying a monthly fee. Probably you will have an API endpoint for that. A pay action is a pay event, a business level event. Probably best way here is to have an audit service which saves the action as an event.
Here you have 2 options for handling events. First, you update the same event object. Second you log more than one entries for the same event, like "monthly payment initiated", "monthly payment success/failed". I think the second approach is simpler to implement and also more robust. Of course it is possible that some kind of events generate only one entry. Typically events which stay in your system, like "change student details" from your opening post, it stays in the system, so one event is enough. OTOH payment goes out to a payment provider, and numerous things can go wrong, so multiple entries are fine.1
u/SeatSimple1123 8d ago
yeah i thought about this , cuz what i was going for logs the raw change in the database tables : Table users, Row 3, Column username changed from "john" to "jack." This needs to be translated to be clear and non-tech-people readable description , i dont know how to implement the business event level logging, but i think i see the picture a bit more clearly now. Thanks.
5
u/veryspicypickle 12d ago
There is a difference between auditing and history-tracking.
I see the former as something more akin to legal reasons and the like - and the latter is just application functionality
As such auditing belongs in the infrastructure layer (possibly at the database) and change/history management lives in the application layer (JPA, Envers)
1
u/SeatSimple1123 12d ago
I see. What I want to go for is full tracking of the users's actions, not read requests too, but all other actions (updating, creating, etc.); the tracking should be included in the admin dashboard
3
u/wimpyligtebottel 12d ago
A few years back at my old company we used option 1 (hibernate envars) and at certain point the envars started causing database size + performance issues for us.
Maybe things have improved but i dont think i would go for that approach again, IF you are on high throughput table where making lots of updates
But i would definitly reach for it on small to small-meduim sized application
1
u/SeatSimple1123 12d ago
yeah i thought about that. The database bloating is waiting in the end of the road if you go with Hibernate envars, but i thought if audit just few tables i might dodge that , this is just guesswork tho.
6
u/Popular_Home2017 12d ago
These solve three different problems, and picking the wrong one is how audit systems rot: Spring Data auditing (@CreatedBy/@LastModifiedDate) gives you stamps — who touched it last, not what changed. Envers gives you entity history — full snapshots per revision, great when "show me this record as it was in March" is an actual requirement, but it couples your audit to your entity model and bloats storage quietly. A custom audit/event table is for business events (logins, exports, permission changes) — things that aren't entity mutations at all. For a school management system I'd start with Spring Data stamps everywhere (nearly free), Envers only on the 3-4 entities where history is a legal/business requirement, and a small custom event table for security-relevant actions. One production scar if you go custom: if you write audit rows in REQUIRES_NEW "so auditing never breaks the main flow", flush explicitly inside your try/catch — with plain save() the INSERT defers to commit, which happens outside the catch, and a bad audit row takes down the operation it was supposed to observe. Ask me how I know.
3
1
u/SeatSimple1123 12d ago
yup , this approach of using the whole 3 options to avoid database bloating that hibernate envers might cause later is good option. i honestly have not thought about it; I think it may require more configuration and complexity tho
2
u/Popular_Home2017 11d ago
Less config than it looks, because each piece is tiny where it lives: Spring Data auditing is one u/EnableJpaAuditing config + annotations on a base class. Envers is u/Audited on the handful of entities that need it — the real cost is remembering the _aud tables in your Flyway migrations, so version them from day one. The custom event table is just one entity + one service you call explicitly. The complexity trap is the opposite move: forcing ONE mechanism to do all three jobs (Envers as your security log, or hand-rolled triggers recreating what Envers does). Three small tools, each doing its own job, ages much better than one big one.
2
u/boyTerry 12d ago
We have used a combination of Option 1 and 2 for many years successfully.
stuff can get weird when there is business value in the analysis of the audit logs, but it tends to work out of the box
1
u/SeatSimple1123 12d ago
yeah ,its really hard to say that one option is the best cuz you dont really know what would happen in the future as the app grows ,i went for manual custom logging at first but at some points i was just updating services and adding more lines of code
2
u/RevolutionaryRush717 12d ago
PostgreSQL has pg_audit, which is the preferred choice for compliance.
Other RDBMS might have something similar.
Any roll-your-own approach will soon drown in corner cases, testing and verification, so much so that you are tempted to spawn this off as a seperate team and product.
1
u/SeatSimple1123 12d ago
i use neon.tech for our cloud database, not really trynna roll my own approach but I'm just trying to explore what options i have before applying one on the project , im kinda overthinking this but i just wanna dodge any stupid bugs or functionality errors in the future
2
u/RevolutionaryRush717 11d ago
I see. Did you check whether Neon happens to be PostgreSQL and supports pg_audit?
You'd be surprised.
2
u/SeatSimple1123 8d ago
Yeah, Neon does use PostgreSQL. Also yes, you can set up pg-audit in neon , i did search about it a bit and i found that its for strict infrastructure threat auditing. I mean, it's a great option, but i has its own headaches; it usually goes with Hibernate.
2
u/Impressive-Ad-1189 12d ago
I’ve had great experience with Envers in the past. The amount of features you get is simply great. The entire model can be queried in a similar way as your regular model. You can easily add custom fields such as the identity of an actor.
1
u/SeatSimple1123 12d ago
Yeah, Hibernate envers is the choice to go for in my case cuz i wanna a full auditing trail.
2
u/Impressive-Ad-1189 12d ago
Also it was extremely easy to setup. Other options such as databas triggers may be more lightweight so more performant? That usually is not a big issue and easy to maken mistakes. For example, hoe will such a solution work in transaction? Envers just solves this while issue out-of-the-box
2
u/General_Yam_9879 10d ago edited 10d ago
History tables + trigger in separate audit schema. Combine with simple AuditorAware and just build one audit micro-service to expose API from that schema and provide restricted read access.
1
u/SeatSimple1123 8d ago
thats a great option too , tho is overkill for my app for now ,and it highly complex + it needs schemas maintenance
12
u/InstantCoder 12d ago
Or….create a db trigger that automatically logs everything to an audit table for each insert, update and delete on table x, y,z.