一键导入
create-logger
Guides creation of custom loggers for Simple History. Use when building a new logger class to log WordPress events.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Guides creation of custom loggers for Simple History. Use when building a new logger class to log WordPress events.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Adds changelog entries to readme.txt following keepachangelog format. Use when updating the Unreleased section or documenting changes for a release.
Reproducibly capture in-product UI screenshots (admin popovers, settings teasers, dashboard widgets) used as embedded images in premium upsell teasers. Use whenever a teaser image needs refreshing after the underlying UI changes.
Guides implementation of structured action links on log events. Use when adding get_action_links() to a logger or migrating from get_log_row_details_output().
Enforces active voice for logger messages and the Event Details API. Use when writing a new logger class or modifying message arrays in getInfo().
How to design and regenerate marketing screenshots for the wordpress.org plugin page (banner-1544x500.png, screenshot-1.png, etc). Covers the event mix that converts, the reproducible WordPress Playground pipeline that bakes it, and every non-obvious gotcha from previous shoots. Use when refreshing screenshot-1.png, banners, or any image showing the Simple History event log.
Guidance for writing and running tests in Simple History. Covers which framework to use, how to run existing tests, and how to create new ones (including the codegen recording workflow).
| name | create-logger |
| description | Guides creation of custom loggers for Simple History. Use when building a new logger class to log WordPress events. |
| allowed-tools | Read, Grep, Glob, Bash, Edit, Write |
Step-by-step guide for building a logger that hooks into WordPress actions/filters and logs events to Simple History.
WordPress hook fires
-> Logger::loaded() registers callback
-> Callback builds context array
-> $this->info_message('message_key', $context)
-> Stored in DB with logger slug + message key
-> get_log_row_details_output() formats for display
Create a PHP file in loggers/ following the naming convention class-{name}-logger.php.
<?php
namespace Simple_History\Loggers;
use Simple_History\Log_Initiators;
/**
* Logs [description of what this logger tracks].
*/
class My_Feature_Logger extends Logger {
/** @var string Logger slug, max 30 characters, stored in DB. */
public $slug = 'MyFeatureLogger';
/**
* Return logger info.
*
* @return array
*/
public function get_info() {
return array(
'name' => _x( 'My Feature Logger', 'MyFeatureLogger', 'simple-history' ),
'description' => __( 'Logs changes to my feature', 'simple-history' ),
'capability' => 'manage_options',
'messages' => array(
'feature_created' => __( 'Created feature "{feature_name}"', 'simple-history' ),
'feature_updated' => __( 'Updated feature "{feature_name}"', 'simple-history' ),
'feature_deleted' => __( 'Deleted feature "{feature_name}"', 'simple-history' ),
),
'labels' => array(
'search' => array(
'label' => _x( 'My Feature', 'My Feature logger: search', 'simple-history' ),
'label_all' => _x( 'All my feature changes', 'My Feature logger: search', 'simple-history' ),
'options' => array(
_x( 'Created', 'My Feature logger: search', 'simple-history' ) => array(
'feature_created',
),
_x( 'Updated', 'My Feature logger: search', 'simple-history' ) => array(
'feature_updated',
),
_x( 'Deleted', 'My Feature logger: search', 'simple-history' ) => array(
'feature_deleted',
),
),
),
),
);
}
/**
* Called when logger is loaded. Hook into WordPress here.
*/
public function loaded() {
add_action( 'save_post', array( $this, 'on_save_post' ), 10, 3 );
}
/**
* Handle the WordPress hook.
*
* @param int $post_id Post ID.
* @param \WP_Post $post Post object.
* @param bool $update Whether this is an update.
*/
public function on_save_post( $post_id, $post, $update ) {
$context = array(
'feature_name' => $post->post_title,
'feature_id' => $post_id,
);
if ( $update ) {
$this->info_message( 'feature_updated', $context );
} else {
$this->info_message( 'feature_created', $context );
}
}
}
$slug PropertyPluginLogger, SiteHealthLogger).get_info() MethodRequired keys:
| Key | Type | Description |
|---|---|---|
name | string | Human-readable logger name (translated) |
description | string | What this logger tracks (translated) |
messages | array | Message key => template string pairs |
capability | string | Required capability to view logs (default: manage_options) |
labels | array | Search/filter labels for the GUI |
Optional keys:
| Key | Type | Description |
|---|---|---|
type | string | 'core' for built-in loggers, omit for custom |
loaded() MethodThis is where you hook into WordPress actions and filters. Called once when Simple History loads the logger.
Use {context_key} placeholders in message strings. They are automatically replaced with values from the context array.
'messages' => array(
// {plugin_name} is replaced with $context['plugin_name']
'plugin_activated' => __( 'Activated plugin "{plugin_name}"', 'simple-history' ),
),
Message keys are used as RFC 5424 MSGIDs and must be globally unique across all loggers. Use descriptive prefixes:
// Good - specific to this logger.
'feature_created', 'feature_updated', 'feature_deleted'
// Bad - too generic, may collide with other loggers.
'created', 'updated', 'deleted'
Two approaches for logging:
Reference a key from the messages array in get_info(). The untranslated string is stored in DB; the translated version is shown in the GUI.
$this->info_message( 'feature_created', $context );
$this->warning_message( 'feature_deleted', $context );
$this->notice_message( 'feature_updated', $context );
Pass the message string directly. Less common, used for dynamic messages.
$this->info( 'Something happened', $context );
$this->warning( 'Something bad happened', $context );
| Method | When to use |
|---|---|
emergency() | System is unusable |
alert() | Action must be taken immediately |
critical() | Critical conditions |
error() | Runtime errors |
warning() | Destructive actions (deletes, security events) |
notice() | Normal but noteworthy (setting changes) |
info() | Routine events (logins, creates, updates) |
debug() | Detailed debug information |
Most logger events use info or notice. Use warning for destructive or security-relevant actions.
The context array stores metadata about the event. It is saved to the contexts table as key-value pairs.
Prefix all context keys with the entity name to avoid collisions:
$context = array(
// Good - prefixed with entity.
'plugin_name' => 'Akismet',
'plugin_current_version' => '5.3',
'plugin_new_version' => '5.4',
// Bad - too generic.
'name' => 'Akismet',
'version' => '5.3',
);
Store previous and new values with _prev and _new suffixes. The Event Details API auto-detects these for diff display.
$context = array(
'setting_value_prev' => $old_value,
'setting_value_new' => $new_value,
);
Keys starting with _ have special meaning and are handled by Simple History:
| Key | Purpose |
|---|---|
_initiator | Override who initiated the event (see below) |
_user_id | Auto-set to current user ID |
_user_login | Auto-set to current user login |
_user_email | Auto-set to current user email |
By default, the initiator is the current logged-in user. Override with _initiator in context:
use Simple_History\Log_Initiators;
$context = array(
'_initiator' => Log_Initiators::WORDPRESS, // 'wp' - automated/cron
// Other options:
// Log_Initiators::WP_USER - 'wp_user' (default when user logged in)
// Log_Initiators::WEB_USER - 'web_user' (anonymous visitor)
// Log_Initiators::WP_CLI - 'wp_cli' (terminal command)
// Log_Initiators::OTHER - 'other' (unknown source)
);
Override get_log_row_details_output() to show additional details below the log message. Use the Event Details API -- never build raw HTML.
use Simple_History\Event_Details\Event_Details_Group;
use Simple_History\Event_Details\Event_Details_Group_Table_Formatter;
use Simple_History\Event_Details\Event_Details_Item;
public function get_log_row_details_output( $row ) {
$group = new Event_Details_Group();
$group->set_formatter( new Event_Details_Group_Table_Formatter() );
// Reads 'feature_status' from context automatically.
$group->add_item(
new Event_Details_Item( 'feature_status', __( 'Status', 'simple-history' ) )
);
// Reads 'setting_value_new' and 'setting_value_prev' automatically.
$group->add_item(
new Event_Details_Item( array( 'setting_value' ), __( 'Value', 'simple-history' ) )
);
return $group;
}
| Formatter | Use case |
|---|---|
Event_Details_Group_Table_Formatter | Key-value table (most common) |
Event_Details_Group_Diff_Table_Formatter | Before/after with diffs |
Event_Details_Group_Inline_Formatter | Compact inline text |
When context keys don't follow conventions:
( new Event_Details_Item( null, __( 'Label', 'simple-history' ) ) )
->set_new_value( $computed_value )
See the logger-messages skill for full Event Details API reference, RAW formatters, and migration patterns.
Add navigational links below log events. See the action-links skill for details.
public function get_action_links( $row ) {
if ( ! current_user_can( 'manage_options' ) ) {
return [];
}
return [
[
'url' => admin_url( 'admin.php?page=my-feature' ),
'label' => __( 'View feature', 'simple-history' ),
'action' => 'view',
],
];
}
Add the class to the loader array in inc/services/class-loggers-loader.php.
Use the simple_history/add_custom_logger hook:
add_action(
'simple_history/add_custom_logger',
function ( $simple_history ) {
require_once __DIR__ . '/class-my-feature-logger.php';
$simple_history->register_logger( My_Feature_Logger::class );
}
);
# View latest events (run from docker-compose directory)
docker compose run --rm wpcli_mariadb simple-history list
# Trigger the WordPress hook your logger listens to, then check the log
Create a test in tests/wpunit/loggers/ that:
Follow active voice. See the logger-messages skill.
Created feature (not "Feature was created")
Updated settings (not "Settings have been updated")
Deleted attachment (not "Attachment has been deleted")
Before submitting a new logger:
$slug is unique, PascalCase, max 30 charactersmanage_options)simple-history for core loggers| File | Purpose |
|---|---|
loggers/class-logger.php | Base class with all available methods |
loggers/class-site-health-logger.php | Clean, simple real-world example |
loggers/class-plugin-logger.php | Complex example with many events |
inc/class-log-initiators.php | Initiator constants |
inc/class-log-levels.php | Log level constants |
inc/services/class-loggers-loader.php | Core logger registration |
docs/architecture/event-details.md | Full Event Details API reference |
tests/_data/mu-plugins/mu-plugin.php | External logger registration example |