Hi everyone
This is the full write-up of the audit trail work I have been doing with OpenMRS β what got built, where the code lives now, and every blog post from along the way in one place. My earlier thread has the introduction; this one is the complete picture.
The problem
OpenMRS already records a detailed audit trail. The auditlogweb module, built on Hibernate Envers, has been quietly capturing revisions in the background for a while. The gap was that all of that history lived in the database and in raw API responses, and there was no native way to read it inside OpenMRS 3.
So the goal was to build that place β natively in the O3 UI β and answer the question that matters most in a medical record system: who changed what, and when.
The work has two homes, because there are two very different people asking that question. A compliance officer wants to sweep across the whole system. A clinician wants to know what happened to the patient in front of them.
1. Admin Audit Log Dashboard
A system-wide view, reachable as an Audit Logs card on the System Administration page.
- Search first by design. An audit log can hold an enormous number of revisions, so the dashboard never dumps everything at once. It opens clean and asks you to filter β by entity type, username, or date range β and results appear only then. On a real server this is the difference between a usable tool and one that falls over on the first big query.
- Filters live in the URL. Narrow to one user on one day and that view has its own address β bookmark it, paste it in a chat, refresh and land right back where you were.
- Colour-coded events β Created, Updated, Deleted β so the event type reads at a glance.
- Field-level diffs. Expand any row to see every changed field with its old value and its new value side by side.
- Server-side pagination, so large logs stay fast.
2. Patient Audit History tab
The same trail, scoped to one patient, as a new Audit History tab in the patient chart.
- Every audited revision that touched that patient, newest first.
- Deliberately the same reading experience as the dashboard β same colour-coded events, same expandable diffs. If you can read one, you can read the other.
- βChanged in the same saveβ β a single save often touches a name, an address and an attribute at once, so related records are grouped under the revision that caused them. You see the full ripple of one edit rather than a misleadingly narrow view.
- Fails gracefully. If the audit backend is not installed, the tab steps aside quietly rather than breaking the chart. A feature that can hurt the page it lives in is not a finished feature, and the patient chart is far too important to risk for a nice-to-have.
Where the code lives
Both features now live together in a single standalone app with its own repo:
- Repo: GitHub - openmrs/openmrs-esm-audit-log-app Β· GitHub
- PR (in review): Add audit log app: admin dashboard and patient audit history tab by RajPrakash681 Β· Pull Request #1 Β· openmrs/openmrs-esm-audit-log-app Β· GitHub
For most of the project the two halves grew up in separate monorepos. They share nearly all of their data fetching, formatting and diff rendering, so that meant two places to build, two places to test and two places to file an issue. After talking it through with my mentors, they moved into one app with two entry points. These two PRs are closed in favour of the new repo:
- openmrs/openmrs-esm-admin-tools#284 β admin dashboard
- openmrs/openmrs-esm-patient-chart#3408 β patient audit history tab
Worth noting: nothing in admin-tools or patient-chart had to change to make room for it. The app registers itself purely through routes.json β the card into the System Administration slot, the tab into the patient chart.
Built the O3 way: React + TypeScript, the OpenMRS ESM framework, Carbon Design System, SWR for data fetching, and translatable strings throughout.
Backend work
Merged into auditlogweb: Return display values and ISO-8601 dates in audit field diffs by RajPrakash681 Β· Pull Request #55 Β· openmrs/openmrs-module-auditlogweb Β· GitHub
Field diffs now return display values and ISO-8601 dates. This one is easy to overlook but it is the change I am happiest with. The backend could hand me a revision saying a field went from a raw id or a timestamp to null β technically complete and humanly useless. Nobody thinks in field names and nulls. Now a diff reads as a name and a date a person can actually parse, and the frontend can localise it.
CSV export
I have also built a CSV export for the dashboard as an additional feature beyond the original scope β one click exports the whole filtered result set, not just the page on screen, generated server-side. It is working locally at the moment and I will share more details on it soon.
Demo
- Walkthrough video: Here
- Project presentation: https://gsoc-2026-audit-trail.vercel.app
Project Wiki: Here
The whole journey, week by week
If you want the long version, the weekly posts are the real diary β the wins and the walls both.
- Community bonding β Before the Code Comes the Community
- Week 1 β When the Blank Screen Finally Blinked Back
- Week 2 β Validating, Learning, and Fixing Things Up
- Week 3 β Wrapping One Thing, Starting Another
- Week 4 β From a Plan to Action
- Week 5 β The Week of Small Things That Add Up
- Week 6 β Making the Audit Trail Readable
- Week 7 β Where the Trail Goes Quiet
- Week 8 β A Home of Its Own
- Full project write-up: Here
Feedback is very welcome β on the UX, the component design, naming, accessibility, or anything I could do better. @dennis @manojll @jayasanka @ibacher, and anyone else who has thoughts, please do take a look at the PR.
A big thank you to my mentors @olewandowski and @manojll, and to the wider OpenMRS community for all the guidance.
Regards Raj