audit
Append-only activity logging for audit trails
ActivityLogRepo is a platform port for recording user actions. Each ActivityRecord captures actor, owner, action, entity, and timestamp. Records are append-only and immutable. Query by entity, actor, or owner for activity feeds, audit reports, and history views.
1
Ports
0
Schemas
2
Hooks
1
Events
01
Ports
02
Schemas
Defines
No schemas defined
Uses
No external schemas used
03
Hooks
- before_append
- after_append
04
Events
- ActivityRecorded v1
05
Stories
06
Examples
from psp.platform.audit import ActivityRecord, EntityRef
class CreateTaskUseCase:
def __init__(self, repo: TaskRepository, log: ActivityLogRepo) -> None:
self._repo = repo
self._log = log
async def execute(self, actor_id: UUID, input: CreateTaskInput) -> Task:
task = Task.create(owner_id=actor_id, title=input.title)
await self._repo.add(task)
# Log the activity
self._log.append(ActivityRecord.create(
actor_id=actor_id,
owner_id=task.owner_id,
action="create_task",
entity_ref=EntityRef("Task", task.id),
metadata={"title": task.title},
))
return task
Record actions after successful operations.
# Get last 10 activities for a task
entity = EntityRef("Task", task_id)
history = activity_log.get_by_entity(entity, limit=10)
for record in history:
print(f"{record.occurred_at}: {record.action} by {record.actor_id}")
# 2024-01-15 10:30: create_task by user-123
# 2024-01-15 11:00: update_task by user-123
# 2024-01-15 14:22: complete_task by user-456
Get activity timeline for a specific entity.
# Get activities for data owned by a user
activities = activity_log.get_by_owner(user_id, limit=50)
# Format for UI
feed = [
{
"action": r.action,
"entity": f"{r.entity_ref.entity_type}#{r.entity_ref.entity_id}",
"actor": str(r.actor_id),
"when": r.occurred_at.isoformat(),
"details": r.metadata,
}
for r in activities
]
Show recent activities for a user's data.
# Get all activities by an actor (for audit)
user_actions = activity_log.get_by_actor(actor_id)
# Group by action type
from collections import Counter
action_counts = Counter(r.action for r in user_actions)
# Counter({'create_task': 45, 'complete_task': 32, 'delete_task': 3})
Query all actions by a specific user.
from psp.platform.audit import InMemoryActivityLogRepo
def test_task_creation_logs_activity():
log = InMemoryActivityLogRepo()
use_case = CreateTaskUseCase(repo=repo, log=log)
task = use_case.execute(actor_id, CreateTaskInput(title="Test"))
# Verify activity was logged
assert len(log.records) == 1
record = log.records[0]
assert record.action == "create_task"
assert record.entity_ref.entity_type == "Task"
assert record.actor_id == actor_id
# Or query by action type
creates = log.get_by_action("create_task")
assert len(creates) == 1
Verify activities are logged correctly.
API Reference
This component mounts routes under /v1/audit.
View OpenAPI specification