Como registrar logs de actividad de usuario con hooks en PHP en WordPress

Contents

Introducción

Registrar logs de actividad de usuario en WordPress es fundamental para auditoría, seguridad, diagnóstico de errores y cumplimiento normativo. Usando los hooks nativos de WordPress (actions y filters) podemos interceptar eventos relevantes —inicios de sesión, ediciones de contenidos, cambios de perfil, intentos fallidos de login, borrados— y persistir esa información en un repositorio controlado. Este tutorial explica, con ejemplos completos en PHP, cómo diseñar, implementar y consumir un sistema de logging de actividad basado en hooks.

Conceptos básicos: hooks en WordPress

Los hooks permiten ejecutar código en puntos concretos del ciclo de vida de WordPress. Hay dos tipos principales:

  • Actions: Permiten ejecutar funciones cuando ocurre un evento (ej. wp_login, wp_logout, profile_update).
  • Filters: Permiten modificar datos pasados entre funciones (se usan menos para logging, salvo casos concretos).

Para registrar actividades normalmente nos apoyamos en actions: nos suscribimos a una action y dentro de nuestra función recopilamos datos y los persistimos.

Decisiones de diseño

  • Qué registrar: usuario (ID, login), acción (tipo), objeto afectado (post ID, tipo), timestamp, IP, user agent, metadatos opcionales (motivo, estado previo, cambios old->new).
  • Dónde almacenar: tabla personalizada en la base de datos (recomendado), custom post type (posible pero menos óptimo para grandes volúmenes), logs externos (SIEM, Sentry, ELK), o ficheros.
  • Retención y privacidad: evitar almacenar datos sensibles innecesarios, enmascarar PII, definir políticas de retención y purgado automático.
  • Performance: considerar inserciones asíncronas (queues, Action Scheduler), índice en columnas frecuentes (user_id, action, created_at).

Esquema de la tabla de logs

Columna Tipo Descripción
id BIGINT UNSIGNED AUTO_INCREMENT PK
user_id BIGINT UNSIGNED ID del usuario que realiza la acción (0 si anónimo)
action VARCHAR(100) Nombre de la acción (ej. login, logout, publish_post)
object_id BIGINT UNSIGNED NULL ID del recurso afectado (post_id, term_id…)
object_type VARCHAR(50) NULL Tipo del recurso (post, user, comment)
meta TEXT NULL JSON con información adicional
ip_address VARCHAR(45) NULL IP del usuario
user_agent TEXT NULL User agent del navegador
created_at DATETIME Timestamp del evento

Crear la tabla en la activación del plugin

Ejemplo mínimo de plugin que crea la tabla mediante dbDelta. Guardar como archivo PHP en wp-content/plugins/mi-activity-logger/mi-activity-logger.php

prefix . user_activity_logs
    charset_collate = wpdb->get_charset_collate()

    sql = CREATE TABLE {table_name} (
      id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
      user_id BIGINT UNSIGNED NOT NULL DEFAULT 0,
      action VARCHAR(100) NOT NULL,
      object_id BIGINT UNSIGNED NULL,
      object_type VARCHAR(50) NULL,
      meta TEXT NULL,
      ip_address VARCHAR(45) NULL,
      user_agent TEXT NULL,
      created_at DATETIME NOT NULL,
      PRIMARY KEY  (id),
      KEY user_id (user_id),
      KEY action (action),
      KEY created_at (created_at)
    ) {charset_collate}

    require_once(ABSPATH . wp-admin/includes/upgrade.php)
    dbDelta(sql)
}

Helper para insertar logs

Centralizamos la inserción para facilitar cambios futuros (por ejemplo, enviar a un endpoint externo). Usamos wpdb->insert para evitar inyecciones.

prefix . user_activity_logs

    data = array(
        user_id    => absint(user_id),
        action     => sanitize_text_field(action),
        object_id  => object_id ? absint(object_id) : null,
        object_type=> object_type ? sanitize_text_field(object_type) : null,
        meta       => ! empty(meta) ? wp_json_encode(meta) : null,
        ip_address => mal_get_client_ip(),
        user_agent => isset(_SERVER[HTTP_USER_AGENT]) ? sanitize_text_field(_SERVER[HTTP_USER_AGENT]) : null,
        created_at => current_time(mysql),
    )

    format = array(%d, %s, %d, %s, %s, %s, %s)

    // Ajustar formato según presencia de valores nulos
    wpdb->insert(table, data, format)
}

Ejemplos de hooks comunes

1) Inicio de sesión

Usamos la action wp_login que recibe el nombre de usuario y el objeto WP_User puede obtenerse si es necesario.

ID : 0
    mal_log_activity(login, user_id, null, user, array(login => user_login))
}

2) Logout


3) Intento de login fallido

 username))
}

4) Publicación o actualización de post

Para capturar cuando un post se crea o actualiza conviene usar transition_post_status o save_post. Aquí un ejemplo que diferencia publish y update.

ID, post->post_type, array(title => post->post_title))
    } elseif (new_status === publish  old_status === publish) {
        mal_log_activity(update_post, get_current_user_id(), post->ID, post->post_type, array(title => post->post_title))
    }
}

5) Borrado de post

post_type, array(title => post->post_title))
    }
}

6) Actualización de perfil

 isset(old_user_data->user_email) ? old_user_data->user_email : ,
        new_user_email => isset(new_user->user_email) ? new_user->user_email : ,
    ))
}

Interfaz administrativa simple para ver logs

Un ejemplo básico que añade un menú y muestra las últimas entradas. En producción recomendamos usar clases y paginación.

prefix . user_activity_logs
    rows = wpdb->get_results(SELECT  FROM {table} ORDER BY created_at DESC LIMIT 200)

    echo lth2gtÚltimas entradas de Activity Logslt/h2gt
    echo lttable class=widefat fixedgt
    echo lttheadgtlttrgtltthgtIDlt/thgtltthgtUsuariolt/thgtltthgtAcciónlt/thgtltthgtObjetolt/thgtltthgtMetalt/thgtltthgtIPlt/thgtltthgtFechalt/thgtlt/trgtlt/theadgt
    echo lttbodygt
    foreach (rows as r) {
        user_display = r->user_id ? esc_html(get_userdata(r->user_id) ? get_userdata(r->user_id)->user_login : r->user_id) : anon
        meta = r->meta ? esc_html(r->meta) : 
        echo lttrgt
        echo lttdgt . esc_html(r->id) . lt/tdgt
        echo lttdgt . user_display . lt/tdgt
        echo lttdgt . esc_html(r->action) . lt/tdgt
        echo lttdgt . esc_html(r->object_type .   . r->object_id) . lt/tdgt
        echo lttdgt . meta . lt/tdgt
        echo lttdgt . esc_html(r->ip_address) . lt/tdgt
        echo lttdgt . esc_html(r->created_at) . lt/tdgt
        echo lt/trgt
    }
    echo lt/tbodygt
    echo lt/tablegt
}

Enviar logs a un servicio externo (opcional)

Si se prefiere centralizar en un sistema externo, se puede enviar cada evento con wp_remote_post o acumular y enviar por lotes.

 wp_json_encode(payload),
        headers => array(Content-Type => application/json),
        timeout => 5,
    )
    resp = wp_remote_post(endpoint, args)
    // manejar errores si es necesario
}
  
// Dentro de mal_log_activity, se podría añadir:
payload = array_merge(data, array(site => get_bloginfo(url)))
mal_send_log_to_external(payload)

Consideraciones de seguridad y privacidad

  • No loguear contraseñas ni tokens.
  • Enmascarar o truncar datos personales cuando sea posible.
  • Restringir acceso al admin de logs mediante capabilities (ej. manage_options).
  • Encriptar datos sensibles si deben almacenarse (revisar legislación como GDPR).
  • Validar y escapar todo lo mostrado en el panel (esc_html, esc_attr).

Performance y escalabilidad

  • Si el sitio genera muchos eventos, evitar hacer wp_remote_post o consultas pesadas de forma síncrona.
  • Valorar Action Scheduler o un sistema de colas para procesar logs en background.
  • Crear índices en columnas consultadas frecuentemente (user_id, action, created_at).
  • Archivar o purgar logs antiguos automáticamente con un cron o WP-CLI.

Pruebas y depuración

  1. Probar cada hook manualmente: iniciar/cerrar sesión, crear/editar/borrar posts, actualizar perfil.
  2. Revisar la tabla en la base de datos para comprobar integridad y tipos.
  3. Comprobar headers HTTP y proxies para la IP real si usas X-Forwarded-For.
  4. Habilitar logs temporales (error_log) para detectar fallos en el insert.

Buenas prácticas finales

  • Centralizar la lógica de logging en funciones reutilizables.
  • Documentar las acciones registradas y el significado de cada action y meta.
  • Implementar rotación/purgado de logs y mecanismos de backup.
  • Probar impacto en rendimiento en staging antes de desplegar en producción.

Recursos

Conclusión

Usando hooks en WordPress y una tabla personalizada se puede implementar un sistema de logging de actividad robusto y flexible. El ejemplo mostrado cubre creación de tabla, función central de inserción, hooks para eventos habituales y una interfaz administrativa básica. Antes de producir, ajusta retención, privacidad y rendimiento según las necesidades del proyecto.



Acepto donaciones de BAT's mediante el navegador Brave 🙂



Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *