API Reference
Complete API documentation for Admin Audit Trail functions and hooks.
Core Functions
admin_audit_trail_insert()
Inserts a log record into the admin audit trail database.
Function signature:
admin_audit_trail_insert(array &$log)
Parameters:
- $log (array, required) - The log record to be saved. Passed by reference.
Log array structure:
[
// Required fields
'type' => 'string', // Event type (e.g., 'node', 'user', 'taxonomy_term')
'operation' => 'string', // Operation type (e.g., 'insert', 'update', 'delete')
'description' => 'string', // Human-readable description of the event
// Optional fields (auto-filled if empty)
'uid' => 1, // User ID (defaults to current user)
'created' => 1234567890, // Unix timestamp (defaults to current time)
'ip' => '192.168.1.1', // IP address (defaults to client IP)
'path' => '/node/123/edit', // Current path (defaults to current route)
// Optional reference fields
'ref_numeric' => 123, // Numeric reference (e.g., entity ID)
'ref_char' => 'string', // Character reference (e.g., bundle name)
]
Return value: None (void)
Behavior:
1. Auto-fills missing optional fields (uid, created, ip, path)
2. Invokes hook_admin_audit_trail_log_alter() to allow modifications
3. Inserts the log into the database
4. Ignores CLI requests (PHP_SAPI == 'cli')
Example usage:
$log = [
'type' => 'node',
'operation' => 'publish',
'description' => t('Published article "@title"', ['@title' => $node->getTitle()]),
'ref_numeric' => $node->id(),
];
admin_audit_trail_insert($log);
Notes:
- Always provide type, operation, and description
- Description supports HTML (stored in TEXT field)
- Function is ignored when run via CLI/Drush
- Log can be altered by other modules via alter hook
admin_audit_trail_get_event_handlers()
Returns all registered event handlers from enabled modules.
Function signature:
admin_audit_trail_get_event_handlers()
Parameters: None
Return value: Array of event handlers
Return structure:
[
'handler_id' => [
'title' => 'Handler Title',
'form_ids' => ['form_id_1', 'form_id_2'],
'form_ids_regexp' => ['/pattern_1/', '/pattern_2/'],
'form_submit_callback' => 'callback_function_name',
],
// ... more handlers
]
Example usage:
$handlers = admin_audit_trail_get_event_handlers();
foreach ($handlers as $handler_id => $handler) {
print "Handler: {$handler['title']}\n";
}
Notes:
- Uses drupal_static() for caching
- Invokes hook_admin_audit_trail_handlers()
- Allows alterations via hook_admin_audit_trail_handlers_alter()
- Cached per request
admin_audit_trail_form_submit()
Internal form submission callback handler.
Function signature:
admin_audit_trail_form_submit(&$form, FormStateInterface $form_state)
Parameters:
- $form (array) - The form array
- $form_state (FormStateInterface) - The form state object
Return value: None (void)
Behavior: 1. Prevents duplicate logging for forms submitted multiple times 2. Identifies the form ID 3. Matches against registered handlers 4. Calls appropriate handler callbacks
Notes:
- Internal function - typically not called directly
- Attached to forms via hook_form_alter()
- Prevents duplicate logs using form state flag
Hooks
hook_admin_audit_trail_handlers()
Define event handlers for tracking specific events.
Function signature:
function hook_admin_audit_trail_handlers()
Return value: Array of event handlers
Return structure:
return [
'handler_id' => [
'title' => t('Handler Title'),
// Match exact form IDs
'form_ids' => [
'node_article_form',
'node_page_form',
],
// OR match form IDs with regular expressions
'form_ids_regexp' => [
'/^node_.*_form$/',
'/^user_.*_form$/',
],
// Callback function to execute
'form_submit_callback' => 'my_module_log_callback',
],
];
Example implementation:
/**
* Implements hook_admin_audit_trail_handlers().
*/
function my_module_admin_audit_trail_handlers() {
$handlers = [];
// Track contact form submissions.
$handlers['contact_forms'] = [
'title' => t('Contact Form Submissions'),
'form_ids' => [
'contact_message_feedback_form',
'contact_message_personal_form',
],
'form_submit_callback' => 'my_module_log_contact_submission',
];
// Track all webform submissions using regex.
$handlers['webforms'] = [
'title' => t('Webform Submissions'),
'form_ids_regexp' => ['/^webform_submission_.*_form$/'],
'form_submit_callback' => 'my_module_log_webform_submission',
];
return $handlers;
}
/**
* Callback for contact form submissions.
*/
function my_module_log_contact_submission($form, FormStateInterface $form_state) {
$log = [
'type' => 'contact_form',
'operation' => 'submit',
'description' => t('Contact form submitted: @subject', [
'@subject' => $form_state->getValue('subject'),
]),
];
admin_audit_trail_insert($log);
}
Handler callback signature:
function callback_name(&$form, FormStateInterface $form_state)
hook_admin_audit_trail_handlers_alter()
Modify event handlers defined by other modules.
Function signature:
function hook_admin_audit_trail_handlers_alter(array &$handlers)
Parameters:
- $handlers (array) - Array of event handlers
Return value: None (void)
Example implementation:
/**
* Implements hook_admin_audit_trail_handlers_alter().
*/
function my_module_admin_audit_trail_handlers_alter(array &$handlers) {
// Disable a handler from another module.
unset($handlers['unwanted_handler']);
// Modify an existing handler.
if (isset($handlers['node_forms'])) {
$handlers['node_forms']['title'] = t('Modified Title');
// Add additional form IDs.
$handlers['node_forms']['form_ids'][] = 'custom_node_form';
}
// Replace a callback.
if (isset($handlers['user_forms'])) {
$handlers['user_forms']['form_submit_callback'] = 'my_module_custom_callback';
}
}
hook_admin_audit_trail_log_alter()
Alter log entries before they are saved to the database.
Function signature:
function hook_admin_audit_trail_log_alter(array &$log)
Parameters:
- $log (array) - The log record to be altered
Return value: None (void)
Example implementation:
/**
* Implements hook_admin_audit_trail_log_alter().
*/
function my_module_admin_audit_trail_log_alter(array &$log) {
// Add custom reference for node events.
if ($log['type'] === 'node' && !empty($log['ref_numeric'])) {
$node = \Drupal::entityTypeManager()
->getStorage('node')
->load($log['ref_numeric']);
if ($node && $node->hasField('field_department')) {
$log['ref_char'] = 'dept_' . $node->get('field_department')->target_id;
}
}
// Sanitize sensitive information.
if (stripos($log['description'], 'password') !== FALSE) {
$log['description'] = preg_replace(
'/password[:\s]+\S+/i',
'password: [REDACTED]',
$log['description']
);
}
// Add contextual metadata.
if ($log['type'] === 'commerce_order') {
$order = \Drupal::entityTypeManager()
->getStorage('commerce_order')
->load($log['ref_numeric']);
if ($order) {
$log['description'] .= ' [Total: ' . $order->getTotalPrice()->getNumber() . ']';
}
}
// Prevent logging of specific events.
// Note: Set $log to empty array to prevent logging.
if ($log['type'] === 'ignored_type') {
$log = [];
}
}
Use cases: - Add additional reference data - Sanitize sensitive information - Enhance descriptions with context - Prevent specific events from being logged - Add custom fields (requires schema modification)
Entity Hooks for Tracking
hook_entity_insert()
Track entity creation events.
Example:
/**
* Implements hook_entity_insert().
*/
function my_module_entity_insert(EntityInterface $entity) {
// Track specific entity types.
if ($entity->getEntityTypeId() === 'my_custom_entity') {
$log = [
'type' => $entity->getEntityTypeId(),
'operation' => 'insert',
'description' => t('Created @type: @label', [
'@type' => $entity->getEntityType()->getLabel(),
'@label' => $entity->label(),
]),
'ref_numeric' => $entity->id(),
];
admin_audit_trail_insert($log);
}
}
hook_entity_update()
Track entity update events.
Example:
/**
* Implements hook_entity_update().
*/
function my_module_entity_update(EntityInterface $entity) {
if ($entity->getEntityTypeId() === 'node') {
// Detect field changes.
$changed_fields = [];
if (isset($entity->original)) {
foreach ($entity->getFields() as $field_name => $field) {
if ($entity->get($field_name)->getValue() !== $entity->original->get($field_name)->getValue()) {
$changed_fields[] = $field_name;
}
}
}
$log = [
'type' => 'node',
'operation' => 'update',
'description' => t('Updated @bundle: @title - Changed: @fields', [
'@bundle' => $entity->bundle(),
'@title' => $entity->label(),
'@fields' => implode(', ', $changed_fields),
]),
'ref_numeric' => $entity->id(),
'ref_char' => $entity->bundle(),
];
admin_audit_trail_insert($log);
}
}
hook_entity_delete()
Track entity deletion events.
Example:
/**
* Implements hook_entity_delete().
*/
function my_module_entity_delete(EntityInterface $entity) {
if ($entity->getEntityTypeId() === 'taxonomy_term') {
$log = [
'type' => 'taxonomy_term',
'operation' => 'delete',
'description' => t('Deleted taxonomy term: @name (ID: @id)', [
'@name' => $entity->label(),
'@id' => $entity->id(),
]),
'ref_numeric' => $entity->id(),
'ref_char' => $entity->bundle(),
];
admin_audit_trail_insert($log);
}
}
Database Query Examples
Retrieve Recent Logs
$database = \Drupal::database();
$logs = $database->select('admin_audit_trail', 'a')
->fields('a')
->orderBy('created', 'DESC')
->range(0, 50)
->execute()
->fetchAll();
Filter by Type and Operation
$logs = $database->select('admin_audit_trail', 'a')
->fields('a')
->condition('type', 'node')
->condition('operation', 'delete')
->orderBy('created', 'DESC')
->execute()
->fetchAll();
Filter by Date Range
$start_date = strtotime('2024-01-01');
$end_date = strtotime('2024-01-31 23:59:59');
$logs = $database->select('admin_audit_trail', 'a')
->fields('a')
->condition('created', $start_date, '>=')
->condition('created', $end_date, '<=')
->orderBy('created', 'DESC')
->execute()
->fetchAll();
Filter by User
$logs = $database->select('admin_audit_trail', 'a')
->fields('a')
->condition('uid', 3)
->orderBy('created', 'DESC')
->execute()
->fetchAll();
Join with User Table
$query = $database->select('admin_audit_trail', 'a');
$query->leftJoin('users_field_data', 'u', 'a.uid = u.uid');
$query->fields('a');
$query->addField('u', 'name', 'username');
$query->orderBy('a.created', 'DESC');
$logs = $query->execute()->fetchAll();
Count Logs by Type
$counts = $database->select('admin_audit_trail', 'a')
->fields('a', ['type'])
->groupBy('type')
->addExpression('COUNT(*)', 'count')
->orderBy('count', 'DESC')
->execute()
->fetchAllKeyed();
Services
Accessing via Dependency Injection
namespace Drupal\my_module\Controller;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Database\Connection;
use Symfony\Component\DependencyInjection\ContainerInterface;
class MyController extends ControllerBase {
protected $database;
public function __construct(Connection $database) {
$this->database = $database;
}
public static function create(ContainerInterface $container) {
return new static(
$container->get('database')
);
}
public function viewLogs() {
$logs = $this->database->select('admin_audit_trail', 'a')
->fields('a')
->orderBy('created', 'DESC')
->range(0, 20)
->execute()
->fetchAll();
return [
'#theme' => 'my_logs_template',
'#logs' => $logs,
];
}
}
Configuration API
Get Configuration
$config = \Drupal::config('admin_audit_trail.settings');
$filter_expanded = $config->get('filter_expanded');
$row_limit = $config->get('admin_audit_trail_row_limit');
Set Configuration
$config = \Drupal::configFactory()->getEditable('admin_audit_trail.settings');
$config->set('filter_expanded', TRUE);
$config->set('admin_audit_trail_row_limit', 10000);
$config->save();
Constants and Defaults
Default Configuration
'filter_expanded' => FALSE // Filters collapsed by default
'admin_audit_trail_row_limit' => 0 // Unlimited retention
Common Operation Types
insert- Entity createdupdate- Entity updateddelete- Entity deletedlogin- User logged inlogout- User logged outlogin_failed- Failed login attemptpassword_reset- Password reset requestedstate_change- Workflow state changedrole_add- Role added to userrole_remove- Role removed from user