Event log
The durable record. Append-only by the shape of its props: there is no edit, no delete and no clear.
Overview
Three pages in this system already argue from a durable record. Toast queue says a toast is safe to dismiss because what it reports is written somewhere permanent. Alert banner says a critical alert always leaves a trace. Acknowledge & escalate says an acknowledgement is terminal because it persists. Until this component existed, all three arguments were complete and had nothing underneath them.
A record is what happened, not what is currently believed. An entry is never edited and never removed. An entry that turns out to be wrong is corrected by a later entry that says so, and both stand.
Append-only
There is no onEdit, no onDelete and no onClear.
A log that can be tidied is not expressible.
Attributed
Every entry names who, and whether that who was a person or software. Those are different facts about the same action.
Honest about what it shows
The count of what is shown against what exists is required, so a shortened view cannot read as the whole record.
Anatomy
Alert activity · HARLAND, A. · MRN 44 812 907
3 of 47 · newest first
-
14:41:07 CEST Corrected the acknowledgement STEMI alert · 14:26
S. Whitfield · Registrar
Acknowledged in error while the patient list was open; the alert remains active.
-
14:32:07 CEST Acknowledged the alert STEMI alert · 14:26
S. Whitfield · Registrar
-
14:26:38 CEST Raised a critical alarm STEMI pattern — anterior leads V2–V4
AcuteLine 4.2 · automatic
Superseded by a later entry (evt-118). This entry stands as recorded.
Showing 3 of 47. The rest of the record is not on this screen.
| Element | Rule |
|---|---|
| Time | Absolute, with its zone, in a real <time> element. Never
relative: “3 hours ago” is wrong the moment a tab is left open, and a log
is read long after it is written. |
| Action | What happened, in clinical terms. “Acknowledged the STEMI alert”, not “UPDATE alerts SET ack=1”. |
| Subject | What it happened to. An entry that does not name its subject is not auditable. |
| Actor | A person with a role, or software with a version. Marked in words, because this system has no icons and because a reader must be able to tell an action somebody took from one that happened to them. |
| Correction notice | Rendered on the superseded entry, which stays. Never struck through and never hidden — a log that removes what turned out to be wrong is a log nobody can audit. |
| Shown of total | Required. A shortened view that does not say so reads as the whole record. |
What a log may never do
- Never edit an entry. Not to fix a typo, not to correct an attribution. The corrected version is a new entry that references the old one, and both are readable.
- Never delete an entry, including one written in error. “Acknowledged in error” is itself a clinically meaningful fact: it says an alert was dismissed by somebody who was not looking at it.
- Never collapse repeated entries into a count. Twelve identical alarms in four minutes is a different record from one alarm with a badge saying twelve — see Messaging.
- Never order by anything but time, and never re-order. A log read as a sequence is a claim about causality; sorting it by severity makes a claim nobody meant.
- Never show an entry without its actor. An unattributed action in a clinical record is a gap in the record, not a tidy entry.
When the actor is not a person
Most entries in a clinical event log are written by software, and a reader scanning for what a colleague did should not have to work out which is which. The actor union makes the distinction structural rather than a naming convention.
- Software carries its version. “AcuteLine 4.2” and “AcuteLine 4.3” may have raised that alarm for different reasons — see Model version & change.
- A person carries their role, because the role is what decided the action was permitted.
- An action taken by software on a person's instruction is two entries, not one. The instruction and the execution have different times and can fail independently.
- Nothing here is signed. A displayed log is a rendering of a record; the integrity of that record is your storage's problem, and it is the part that matters — see what this component enforces.
Do's and don'ts
14:41 Corrected the acknowledgement · acknowledged in error
14:32 Acknowledged the alert
Both entries stand. A reader can see that the alert was dismissed by somebody who was not looking at it, which is the fact worth having.
14:32 Acknowledged the
alert — removed
The mistaken acknowledgement is gone, and with it the only evidence that the alert was ever dismissed without being read.
Raised a critical alarm · AcuteLine 4.2 · automatic
The reader can tell software raised it, and which version. A later model update makes that version the thing they need.
Raised a critical alarm · System
“System” is not an actor. Nothing in the record says which software, at which version, on which rule.
12 alarms · 14:22, 14:22, 14:23, 14:23, 14:24 …
Twelve entries, twelve times. The rate is visible, and the rate is the finding.
Alarm raised ×12
One entry with a multiplier. An alarm system firing twelve times in four minutes is now indistinguishable from one that fired sensibly.
What the component enforces, and what it only asks for
A displayed log is a rendering. The rules that make a record trustworthy are almost all about storage, and none of them can be held by something that draws a list.
| Rule | Held by | How |
|---|---|---|
| Entries cannot be edited, removed or cleared | The component | There is no onEdit, onDelete or onClear in
the props. A log that can be tidied is not expressible.Asserted: EventLog: no entry can be edited, removed or cleared |
| Every entry names an actor | The component | actor is required and is a union, so an unattributed entry and an
ambiguous one are both unwritable.Asserted: EventLog: every entry names an actor, and says whether it was software |
| A superseded entry stays and says so | The component | supersededBy renders a notice on the entry rather than hiding or
striking it.Asserted: EventLog: a superseded entry stays, and says it was superseded |
| A shortened view says so | The component | totalCount is required and is rendered against the number shown.Asserted: EventLog: a shortened view says how much of the record it is showing |
| The order is stated | The component | orderedBy is required, and the list is an <ol>
because the order is the meaning.Asserted: EventLog: the order is stated and the list is ordered markup |
| A correction is named in the reader’s terms | The component | By the correcting entry’s time and action, or as not shown here. It printed the entry’s id — “(evt-118)” — which no reader can use. Asserted: EventLog: a superseded entry stays, and says it was superseded |
| The record itself is append-only | You | This draws a list. Whether your storage permits an UPDATE on a
recorded event is the question that actually decides whether any of this is true, and
it is entirely outside the browser. |
| The record is complete | You | totalCount is a number you pass. If your query dropped entries before
the component saw them, it will state a total with complete confidence. |
| Times are correct and in one zone | You | Preformatted strings. A log mixing zones reads as a sequence that did not happen — see Date & time picker. |
| Entries are ordered by time and not re-sorted | You | orderedBy is a string describing an array you built. Nothing here can
check the words are true. |
| The record is tamper-evident | You | Nothing here is signed or hashed. A rendering cannot make a record trustworthy; it can only avoid misrepresenting one. |
A rule stated on a component page reads as a rule the component keeps. 5 of these 10 are; 5 are obligations this page is handing to the reader. Every rule that makes a record trustworthy rather than merely well-drawn is on your side of the table, and that is not a gap this component could ever close.
Accessibility
- A real
<ol>with a name. The order is the meaning, and a screen reader announces the position and the count. - Times are in
<time datetime>, so the machine-readable value is available alongside the human one. - The actor kind is a word — “automatic” — not a glyph and not a colour. It survives greyscale and is announced.
- A correction notice is text in the entry, not a strikethrough. Struck text is announced inconsistently and read as decoration.
- Nothing here is a live region. A log that announces every arriving entry is unusable; the reader comes to it, it does not come to them.
Outcomes of use
- Effectiveness — a clinician can reconstruct what happened, who did it, and what was known at the time.
- Efficiency — the sequence is readable without cross-referencing a second system.
- Satisfaction — not having to trust a summary. Attributes believed to contribute to an outcome; the outcome is settled by observing real use, not by this page — see Usability & context of use.
Clinical safety notes
Trace these in your risk file (ISO 14971) and usability engineering file (IEC 62366-1). Which of these the component keeps and which are yours is stated in the guarantees table; a control the component does not keep is one your risk file must show you keep.
- No entry can be edited or removed through this component. (the component) Mitigates: a record that reflects what is currently believed rather than what happened.
- Every entry is attributed, and software is marked as software. (the component) Mitigates: an automatic action read as a clinical decision somebody made.
- A shortened view states the size of the record. (the component) Mitigates: a partial log read as complete, which is how an absent entry becomes evidence that nothing happened.
- Repeated events are not collapsed. (the component) Mitigates: an alarm rate — the thing IEC 60601-1-8 cares about — made invisible by a multiplier.
Implementation
$ npx shadcn@latest add https://md.notjustany.tech/r/event-log.json
<EventLog
caption="Alert activity · HARLAND, A."
orderedBy="newest first" // required: an unstated order is a claim about causality
totalCount={47} // required: a shortened view cannot read as the whole record
entries={[
{ id: "evt-118", at: "14:41:07 CEST",
actor: { kind: "person", name: "S. Whitfield", role: "Registrar" },
action: "Corrected the acknowledgement", subject: "STEMI alert · 14:26" },
{ id: "evt-092", at: "14:26:38 CEST",
actor: { kind: "system", name: "AcuteLine", version: "4.2" },
action: "Raised a critical alarm", subject: "STEMI pattern",
supersededBy: "evt-118" },
]}
/>
// There is no onEdit, no onDelete and no onClear. Correcting a record
// means appending an entry that says so; both stand.
Related
- Toast queue — the transient copy of what is written here, and why it is safe to dismiss.
- Alert banner — the alarm whose trace this holds.
- Acknowledge & escalate — what an acknowledgement is, and why it is terminal.
- Authorship & the record — who a recorded action belongs to.
- Date & time picker — why every time here carries its zone.