tetthys / activity-log
Framework-agnostic activity log core with Enum/Attribute definitions and high-performance counter-backed stats. Optional Laravel 12 integration included.
Installs: 4
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/tetthys/activity-log
Requires
- php: ^8.1
Requires (Dev)
- illuminate/cache: ^12.0
- illuminate/database: ^12.0
- illuminate/support: ^12.0
Suggests
- illuminate/cache: Required for cache-backed idempotency store
- illuminate/database: Required for DatabaseActivityWriter and DatabaseCounterStore
- illuminate/support: Required for Laravel integration (ServiceProvider, Str, Carbon)
README
A query-first activity logging system for PHP 8.x, built around Enums and Attributes.
This package provides a clean alternative to the “one table per action” pattern by combining:
- a single append-only activity log
- attribute-driven validation and policy
- counter-backed fast reads for analytics
The core is framework-agnostic, with an optional Laravel 12 integration.
Why this exists
Most applications eventually need to answer questions like:
- How many times did a user perform an action?
- How often does this happen per day?
- How many times did a user interact with a specific resource?
Traditional logging approaches make these questions expensive by forcing full table scans or ad-hoc aggregation queries.
tetthys/activity-log is designed so that:
- actions are explicitly defined
- counting rules are declared up front
- reads are fast by default
Core Ideas (Quick Overview)
1. Actions are Enums
Instead of logging arbitrary strings, every activity is a typed enum case.
Each case declares:
- its public name
- whether an actor is required
- whether a target object is required
- what metadata is allowed
- how the action should be counted
The enum becomes the single source of truth.
2. Two data layers
| Layer | Purpose |
|---|---|
activity_logs |
Append-only event log (auditing, debugging, history) |
activity_counters |
Pre-aggregated counters for fast queries |
The log table is never scanned for common queries.
3. Queryability is built in
Actions explicitly declare what should be counted:
- per actor
- per object
- per actor + object
- per time bucket (day, hour)
This makes common analytics O(1) or small range reads.
Defining Actions (Generic Example)
<?php use Tetthys\ActivityLog\Attributes\ActivityAction; use Tetthys\ActivityLog\Attributes\Actor; use Tetthys\ActivityLog\Attributes\ObjectRef; use Tetthys\ActivityLog\Attributes\MetaSchema; use Tetthys\ActivityLog\Attributes\CountBy; use Tetthys\ActivityLog\Attributes\Uniq; enum AppAction { #[ActivityAction( name: 'resource.view', category: 'interaction', description: 'A resource was viewed', version: 1 )] #[Actor(type: 'user', required: true)] #[ObjectRef(type: 'resource', required: true)] #[MetaSchema(key: 'source', type: 'string', maxLen: 32)] #[CountBy(dimension: 'actor', bucket: 'all')] #[CountBy(dimension: 'actor', bucket: 'day')] #[CountBy(dimension: 'actor_object', bucket: 'all')] #[Uniq(mode: 'trace', ttlSeconds: 300)] case ResourceView; }
What this definition gives you automatically:
- actor and object validation
- metadata schema enforcement
- idempotent logging (same request won’t double-count)
- per-user totals
- per-user daily counts
- per-user-per-resource counts
No additional query logic is required.
Laravel 12 Usage
1. Install
composer require tetthys/activity-log
Laravel auto-discovers the service provider.
2. Publish config and migrations
php artisan vendor:publish --tag=activity-log-config php artisan vendor:publish --tag=activity-log-migrations php artisan migrate
This creates:
activity_logsactivity_counters
3. Logging an action
use Tetthys\ActivityLog\Contracts\ActivityLogger; use Tetthys\ActivityLog\Model\ActorRef; use Tetthys\ActivityLog\Model\ObjectReference; $logger->log( action: AppAction::ResourceView, actor: new ActorRef('user', (string) $user->id), object: new ObjectReference('resource', (string) $resourceId), metadata: ['source' => 'web'], );
That single call:
- validates the action definition
- sanitizes metadata
- inserts into
activity_logs - updates all declared counters atomically
Fast Queries (What makes this useful)
Laravel binds a counter-backed stats service:
use Tetthys\ActivityLog\Contracts\ActivityStats;
Total count per user
$count = $stats->countForActor( AppAction::ResourceView, new ActorRef('user', (string) $user->id), );
Daily time series
$series = $stats->seriesForActor( AppAction::ResourceView, new ActorRef('user', (string) $user->id), bucket: 'day', from: now()->subDays(6)->startOfDay()->toImmutable(), to: now()->startOfDay()->toImmutable(), );
Per user + per resource count
$count = $stats->countForActorObject( AppAction::ResourceView, new ActorRef('user', (string) $user->id), new ObjectReference('resource', (string) $resourceId), );
All of these queries read from activity_counters, not the raw log table.
Optional: Request Context Automation
The Laravel adapter includes middleware that captures:
- IP address
- User agent
- request trace ID
Register the middleware:
->withMiddleware(function ($middleware) { $middleware->web([ \Tetthys\ActivityLog\Integration\Laravel\Http\Middleware\ActivityRequestContextMiddleware::class, ]); });
Then use the contextual logger:
$ctxLogger->log( AppAction::ResourceView, actor: new ActorRef('user', (string) $user->id), object: new ObjectReference('resource', (string) $resourceId), );
Design Principles
- Actions are contracts, not strings
- Logging rules are explicit and declarative
- Writes are correct and idempotent
- Reads are fast by default
- Analytics should never require log scans
This package is intentionally opinionated toward systems that need reliable usage analytics.
License
MIT