On this page
What audit logging is
Audit logging is the practice of recording who did what, to what, when and from where, and keeping that record so someone else can read it later. An audit log is the record itself: a time-ordered list of events, one entry per action. An audit trail is what you get by following those entries for one thing, such as the history of a single user, invoice or permission. Most people use the two terms interchangeably, and that is usually fine.
The important words are "someone else". An application log is written for the engineer fixing a bug. An audit log is written for a reader who was not there and has no reason to take your word for it: a customer's security team, an auditor, an investigator, the other side in a dispute. Almost everything that makes an audit log good follows from that reader.
What one audit event records
A useful event can be read on its own, a year later, by someone who has never seen your product. That means it answers a short list of questions without needing a lookup in another system.
Two details separate a strong entry from a weak one. The actor is a specific person, API key or service, because a record that says "admin" or "system" cannot hold anyone to account. And a change states what it changed from and to, because the value of the entry is often in the difference.
Inside one audit event
member.role.updatedOne named person, API key or service. A shared account answers nothing.
A stable name from a fixed list, so every role change can be found with one search.
The thing that changed, by ID, so the entry still means something after a rename.
In UTC, set by your server. A time from the browser is only the browser's opinion.
Where the request came from: the first thing an investigator asks.
Before and after. "Role updated" tells a reader far less than "viewer to admin".
Added on arrival, not sent by your app. Most audit logs stop one row earlier.
What to record
You will not record everything, and you do not need to. Start with the actions that change who can do what, what leaves the system, and what your customers would be upset to find out about afterwards.
| Category | Examples | Why it gets asked about |
|---|---|---|
| Access | Sign-ins, failed sign-ins, password resets, MFA changes | Shows how an account was used, and the attempts on it |
| Permissions | Role changes, invitations, API keys created or revoked | Most incidents and most reviews turn on who could do what |
| Data | Exports, bulk downloads, views of sensitive records, deletions | Shows what left the system, and who took it |
| Configuration | SSO settings, security policies, webhooks, retention | Turning a control off is an event in its own right |
| Business actions | Refunds, payouts, approvals, contract changes | Disputes start here |
| The log itself | Log exports, retention changes, who opened the viewer | Sooner or later someone asks who touched the evidence |
Audit logs, application logs and SIEMs
These get confused because they are all "logs", and because they often end up in the same tool. They do different jobs.
| Application log | Audit log | SIEM | |
|---|---|---|---|
| Question it answers | What went wrong in the system? | Who did what, and can we show it? | Is something suspicious happening now? |
| Main reader | Engineers | Auditors, customers, investigators | Security analysts |
| What it holds | Errors, traces, debug output | One structured entry per action | Logs gathered from many systems |
| Typical lifetime | Days or weeks | Months or years | Set by budget and policy |
| If an entry is lost | A harder debugging session | A hole in your evidence | A possible missed alert |
Who reads an audit log, and why
A customer's security reviewer. If you sell software to larger companies, the question appears in the security questionnaire: do you provide audit logs, can our admins see them, can we export them? A good answer can shorten a review. "We can pull that from a database if you ask" rarely helps.
An auditor. A SOC 2 or ISO 27001 auditor wants to see that a control worked throughout a period, not just that it exists today. Log entries are often how you show it.
Whoever is handling an incident. Once something has gone wrong, the first job is a timeline: which account, which actions, in what order, and what they touched. Without one, the team has to assume the worst about what was reached.
You, in a dispute. A customer says nobody on their team changed a setting, and now invoices have gone to the wrong place. If the log shows which of their users changed it and when, the conversation ends quickly. If it does not, it is your word against theirs.
What the rules actually ask for
Guides often say a framework "requires audit logs" as if they all meant the same thing. They do not. This is a summary, not advice, so confirm the detail with your auditor or counsel.
| Framework | What it asks for | How long to keep it |
|---|---|---|
| PCI DSS v4, Requirement 10 | Log access to system components and cardholder data, and protect the logs from being changed | 12 months, with the latest 3 immediately available (10.5.1) |
| HIPAA Security Rule, 45 CFR 164.312(b) | Mechanisms that record and examine activity in systems holding electronic health information | Not stated in this standard |
| SOC 2 (CC7.2 and related criteria) | No fixed log format. The auditor looks for evidence that systems were monitored across the period | Your own policy, at least the audit period |
| ISO/IEC 27001:2022, Annex A 8.15 | Produce, store, protect and analyse logs of activity and exceptions | Your own policy |
| GDPR | Does not name audit logs. They are a common way to meet accountability (Art. 5(2)) and security (Art. 32) | No longer than necessary for personal data (Art. 5(1)(e)) |
The weak point: who can change the log?
Most audit logs are rows in a database that the vendor runs. Its engineers can reach that database. So can an attacker with their credentials, and so can a migration with a bad WHERE clause. Restricting access is worth doing, but it is a promise about who will touch the log, and a reader outside your company has no way to check that promise.
That is the gap between a log you say is intact and a log that can show it. "Immutable" gets used for both. The more useful question is what would happen if someone changed a row, and who would find out.
Tamper evidence answers that with three checks, and each catches something the others miss. A signature on each event shows that event has not changed since it was signed, but says nothing about an event that has gone. A gap-free sequence number for each customer shows that an event is missing, but not that one was altered. A copy held outside the system catches both, and also the hardest case: the newest events removed and the counter rolled back to match.
Try it. In this log, someone promotes themselves to admin, creates an API key, exports the customer list and steps back down. Choose how they cover it up and see which checks notice.
Change the log, then run the checks
organization acme-prod · seq 4817 to 4822- 4817session.createduser_2207sso
- 4818member.role.updateduser_2207viewer to admin
- 4819api_key.createduser_2207key_7Hq2
- 4820customers.exporteduser_220748,210 rows
- 4821member.role.updateduser_2207admin to viewer
- 4822session.endeduser_2207sso
Signatures
verifyAuditEvent, pinned key
6 of 6 verify
Sequence scan
audit.orgs.integrity()
Contiguous through 4822
Your stream copy
events you already received
Last seq 4822, rows match
How to set it up well
Start from the questions, not the code. List the ten actions a customer's reviewer would ask about and cover those first. A short list that is complete beats a long list with holes.
Write the event on the server, in the same code path as the action. An entry sent from the browser can be blocked or faked, and one written by a job that runs later can go missing even though the action succeeded.
Pick one naming scheme and keep to it. Names like member.role.updated and billing.plan.changed stay searchable. Free-text messages do not.
Keep every event under the customer it belongs to, so one customer's admins can only ever see their own organization. Make retries safe with an idempotency key, so a redelivered request does not create a second entry.
Decide who reads it. A log nobody reads catches nothing. Alert on a small set of events, such as a new admin, MFA turned off or a bulk export, and review the rest on a schedule. Then test it: pick a real action from last month, find its entry and ask someone outside the team whether they can tell what happened.
How Invoance Audit Logs does it
Invoance Audit Logs is built for software companies whose customers want to see activity in their own accounts. Your product sends an event for each action, under the customer organization it belongs to. Invoance assigns the next sequence number for that organization, signs the event with Ed25519 and stores it in a table where deletes are blocked.
Each customer gets a read-only portal or a viewer embedded in your product, with CSV and NDJSON exports and webhook streams to their own systems. That stream is the outside copy from the demo above. Anyone holding an event can verify it offline with the SDK, with no call to Invoance. Auth0 and Clerk integrations can forward identity events for you.
It is not a SIEM, and it does not collect logs from your cloud accounts. It records what your application sends. A signature shows an event has not changed since it was signed. It cannot show the event was true, or that your application recorded an action at all: an action that never sent an event leaves no gap.
import { InvoanceClient, verifyAuditEvent } from "invoance";
const client = new InvoanceClient();
const ev = await client.audit.events.ingest({
organizationId: "org_01J8F3KQ2R7VWX9YB4ND6MCZAH",
action: "member.role.updated",
actor: { type: "user", id: "user_2207" },
});
// later, anywhere, with no network call
const event = await client.audit.events.get(ev.event_id);
console.log(verifyAuditEvent(event).valid); // true
