|
|
||
|---|---|---|
| .. | ||
| controllers | ||
| helpers | ||
| migrations | ||
| models | ||
| views | ||
| README.md | ||
| bootstrap.php | ||
| helpers.php | ||
| plugin.json | ||
README.md
Logger plugin
Overview
The Logger plugin (located in plugins/logs/) provides a modular, pluggable logging
system for the application. It records both user and system events in the log
table and exposes retrieval utilities plus a built-in UI at ?page=logs.
The plugin uses the callable dispatcher pattern with PluginRouteRegistry for routing
and follows the App API pattern for service access.
Features
- Log entry management
- PSR-3-style
log()method with level + context payloads - Core helper
app_log()for simplified access with NullLogger fallback
- PSR-3-style
- Filtering & pagination
- Query by scope, user, time range, message text, or specific user IDs
- Pagination-ready result sets with newest-first sorting
- User awareness
- Stores username via joins for auditing
- Captures current user IP via plugin bootstrap
- Auto-migration
logs_ensure_tables()function creates thelogtable on demand- Called automatically via
logger.system_inithook
- UI integration
- Adds a "Logs" entry to the top menu
- Provides list/detail views with tabs for user vs system scopes
- Uses callable dispatcher for route handling
Installation
- Copy the
logsfolder into the project'splugins/directory. - Enable the plugin via the admin plugin management interface (stored in
settingstable). - The plugin bootstrap automatically:
- Registers the
logsroute prefix with a callable dispatcher - Sets up the
logs_ensure_tables()migration function - Initializes the logger via the
logger.system_inithook
- Registers the
Database Schema
The plugin defines the following table (auto-created):
CREATE TABLE IF NOT EXISTS `log` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`scope` SET('user','system') NOT NULL,
`message` VARCHAR(255) NOT NULL,
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`),
CONSTRAINT `log_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
Routing & Dispatcher
The plugin registers its route using PluginRouteRegistry:
register_plugin_route_prefix('logs', [
'dispatcher' => function($action, array $context = []) {
require_once PLUGIN_LOGS_PATH . 'controllers/logs.php';
if (function_exists('logs_plugin_handle')) {
return logs_plugin_handle($action, $context);
}
return false;
},
'access' => 'private',
'defaults' => ['action' => 'list'],
'plugin' => 'logs',
]);
Hook + Loader API
Core must fire the initialization hook after the database connection is ready:
do_hook('logger.system_init', ['db' => $db]);
The plugin listener:
- calls
logs_ensure_tables()to create thelogtable if needed - resolves the current user IP
- exposes
$GLOBALS['logObject'](Loginstance) and$GLOBALS['user_IP']
When $logObject is not available, use app_log($level, $message, $context) which falls back to NullLogger.
PHP API
Log lives in plugins/logs/models/Log.php and receives the database connector.
Methods
Log::log(string $level, string $message, array $context = []): void
Log::readLog(int $userId, string $scope, int $offset = 0, int $itemsPerPage = 0, array $filters = []): array
Supported log levels
emergency, alert, critical, error, warning, notice, info, debug
Supported filters
from_time:YYYY-MM-DDlower bound (inclusive)until_time:YYYY-MM-DDupper bound (inclusive)message: substring match across message textid: explicit user ID (system scope only)
Typical usage
app_log('info', 'User updated profile', [
'user_id' => $userId,
'scope' => 'user',
]);
$entries = $logObject->readLog(
$userId,
$scope,
$offset,
$itemsPerPage,
['message' => 'profile']
);
Usage guidelines
- When to log
- User actions, authentication events, configuration changes
- System events, background job outcomes, and security anomalies
- Message hygiene
- Keep messages concise, include essential metadata, avoid sensitive data
- Data integrity
- Validate user input before logging to avoid malformed queries
- Wrap bulk insertions in transactions when necessary
- Performance
- Prefer pagination for large result sets
- Index columns used by custom filters if extending the schema
- Retention
- Schedule archival/log rotation via cron if the table grows quickly
File Structure
plugins/logs/
├─ bootstrap.php # registers route, migration function, hooks & menu
├─ plugin.json # plugin metadata
├─ README.md # this documentation
├─ controllers/
│ └─ logs.php # procedural handler functions for callable dispatcher
├─ models/
│ ├─ Log.php # main Log class
│ └─ LoggerFactory.php # migration + factory
├─ helpers/
│ ├─ logs_view_helper.php
├─ helpers.php # plugin helper wrapper
└─ migrations/
└─ create_log_table.sql
Controller Architecture
The controller uses procedural functions instead of classes:
logs_plugin_handle($action, $context)- main dispatcher functionlogs_plugin_render_list($logObject, $db, $userId, $validSession, $app_root)- renders log list with filters and pagination
The callable dispatcher pattern provides:
- Clean separation of concerns
- Access to request context (user_id, db, app_root, valid_session)
- Consistent error handling and layout rendering
Admin Plugin Check
The plugin provides logs_ensure_tables() for the admin plugin management interface:
- Owned tables:
log(will be removed on purge) - Referenced tables:
user(dependency, not removed)
Uninstall / Disable
To disable the plugin:
- Use the admin plugin management interface to disable it (updates the
settingstable), or - Delete the
plugins/logs/folder entirely
When disabled, the app_log() helper automatically falls back to NullLogger, so logging calls remain safe and won't cause errors. To remove plugin data, use the admin plugin management interface to purge the log table.