Skip to main content

MySQL Audit Logging Guide

Adela · Jun 3, 2026

Audit logging is a cornerstone of database security and compliance. Whether you're tracking who changed what, investigating an incident, or preparing for a SOC 2 or HIPAA audit, MySQL gives you several ways to record activity — each at a different level of detail and cost.

In this guide, we'll walk through the most practical approaches — from MySQL's built-in logging to audit plugins, binlog-based change capture, and Bytebase — to help you choose the right setup for your organization. (For the PostgreSQL equivalent, see the Postgres Audit Logging Guide.)

1. Native MySQL Logging

MySQL ships with a general query log that records every statement the server receives. It's the quickest way to get some visibility.

Key settings

Enable it in my.cnf:

[mysqld]
general_log = ON
general_log_file = /var/log/mysql/general.log
log_output = FILE          # or TABLE → mysql.general_log

Or toggle it at runtime without a restart:

SET GLOBAL general_log = 'ON';
SET GLOBAL log_output = 'TABLE';   -- query it as mysql.general_log

This records every connection and statement, including who ran it and when.

Pros

  • Built-in, no plugin needed.
  • Captures everything, including SELECTs.

Cons

  • Not a security tool — no filtering, no structured format, no tamper protection.
  • Grows extremely fast and adds noticeable overhead; most teams cannot leave it on in production.
  • Logs query text verbatim, including sensitive parameters.

The general log is fine for short debugging windows, but it was never designed as a compliance audit trail. For anything long-lived, use an audit plugin (Section 4).

2. Trigger-Based Auditing

If you need row-level changes — before and after values on UPDATE — you can use triggers.

Example

Create a table to store change history:

CREATE TABLE audit_log (
  id           BIGINT AUTO_INCREMENT PRIMARY KEY,
  table_name   VARCHAR(64),
  action       VARCHAR(10),
  changed_by   VARCHAR(128),
  changed_at   TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  old_data     JSON,
  new_data     JSON
);

Then define a trigger. Unlike PostgreSQL, MySQL needs a separate trigger per event (you can't combine INSERT OR UPDATE OR DELETE in one definition):

CREATE TRIGGER users_audit_update
AFTER UPDATE ON users
FOR EACH ROW
  INSERT INTO audit_log (table_name, action, changed_by, old_data, new_data)
  VALUES ('users', 'UPDATE', CURRENT_USER(),
          JSON_OBJECT('id', OLD.id, 'email', OLD.email),
          JSON_OBJECT('id', NEW.id, 'email', NEW.email));

Repeat with AFTER INSERT (only NEW) and AFTER DELETE (only OLD) to cover all DML.

Pros

  • Captures before/after data.
  • Fully customizable schema.

Cons

  • Must be defined per table and per event.
  • Adds write overhead on busy tables.
  • Captures the database user (CURRENT_USER()), not the real end user; can't see SELECTs or DDL.

3. Binlog-Based Auditing

MySQL's binary log records every data change. With ROW format and a full row image, it becomes a complete change-data-capture (CDC) stream without touching application code or adding triggers.

Example setup

Enable row-based binary logging in my.cnf:

[mysqld]
log_bin = mysql-bin
binlog_format = ROW
binlog_row_image = FULL
server_id = 1

Inspect changes directly:

mysqlbinlog --base64-output=DECODE-ROWS --verbose mysql-bin.000001

Or stream them to audit storage with Debezium or Maxwell, which decode the binlog into structured JSON and forward it to Kafka, Elasticsearch, or S3.

Pros

  • Captures all data changes automatically — no per-table setup.
  • Minimal performance impact — reads from infrastructure you already run for replication.
  • With binlog_row_image = FULL, you get before/after values.

Cons

  • Does not capture SELECT queries (only modifications).
  • No clean end-user attribution — the binlog records the change, not who logically requested it.
  • Binlog retention increases storage if a consumer falls behind.

This approach is ideal when you need near-real-time CDC for auditing, analytics, or event-driven systems.

4. Audit Plugins

For structured, compliance-grade audit logs, use a dedicated audit plugin. There are three common choices depending on your distribution:

  • MariaDB Audit Plugin (server_audit) — open source and widely used; it also loads into MySQL and Percona, so it's the go-to for Community Edition.
  • MySQL Enterprise Audit — part of MySQL Enterprise Edition (commercial). Structured JSON output with rich filtering by user and event class.
  • Percona Audit Log Plugin — free, bundled with Percona Server for MySQL; comparable capability.

Example — MariaDB server_audit (Community Edition)

INSTALL PLUGIN server_audit SONAME 'server_audit.so';
[mysqld]
plugin-load-add        = server_audit=server_audit.so
server_audit_logging   = ON
server_audit_events    = CONNECT,QUERY,TABLE
server_audit_file_path = /var/log/mysql/audit.log

Example — MySQL Enterprise Audit (Enterprise Edition)

[mysqld]
plugin-load-add  = audit_log.so
audit_log        = FORCE_PLUS_PERMANENT
audit_log_format = JSON
audit_log_file   = /var/log/mysql/audit.log
audit_log_policy = ALL          # or LOGINS, QUERIES

You'll get structured records of connections, queries, and table access — who, what, and when. For step-by-step enablement of each plugin — plus an application-level (API/cursor-based) auditing approach in your app code — see the reference: How to Enable Auditing in MySQL.

What to log for compliance

A defensible audit trail for SOC 2, HIPAA, or PCI DSS generally needs: authentication events (connect/disconnect, success and failure), privileged/DDL activity, access to sensitive tables, and a tamper-resistant destination. Ship the plugin output to a centralized, append-only store (SIEM) rather than leaving it on the database host.

Pros

  • Structured, filterable, compliance-grade.
  • Captures logins, DDL, and DML (and SELECTs, depending on policy).

Cons

  • MySQL Enterprise Audit requires a commercial license.
  • Still records the database user — in most apps that's a single shared account (e.g. app_user), so it can't attribute actions to a real person.
  • Needs log rotation and a SIEM/analysis pipeline.

5. Bytebase

Bytebase is a Database DevSecOps platform that provides a centralized audit trail across your MySQL environments. It records who did what, when, and why — linking SQL actions to their context (issues, approvals, and deployments) while keeping sensitive data secure.

What Bytebase Audits

  • Query access: logs who queried which data and when across SQL Editor, Admin Query, and Data Export.
  • Schema and data changes: tracks who made which changes, when they were approved, and through which workflow or Git commit.
  • Governance controls: built-in SQL review rules, approval flow, and role-based access help prevent unauthorized actions before they happen.
  • Actual end users, not just database users: This is the key advantage. In most applications every query uses the same connection user (like app_user), which makes it impossible to trace actions back to an individual with engine-native auditing or audit plugins. Because users operate through Bytebase, every action is attributed to the actual end user — not a generic database account.

Why It Matters

  • Complete visibility across read and write operations.
  • Privacy-safe auditing with no sensitive data exposure (column masking metadata travels with the record).
  • Compliance-ready logs aligned with SOC 2, ISO 27001, and GDPR.

You can also call the API to ship audit logs to a centralized sink. For the full record format, the gaps Bytebase closes versus engine-native logs, and per-column masking detail, see How Bytebase Handles Audit Logging.

Conclusion

MySQL offers multiple layers of auditing — from the basic general log to complete governance solutions.

Comparison Table

ApproachPerformance ImpactCaptures SELECTsCaptures ActorRow-Level DetailBest For
General Query LogMedium-High⚠️ DB user onlyDevelopment, debugging, short windows
TriggersMedium-High⚠️ DB user only✅ (before/after)Critical tables needing full change history
Binlog (CDC)LowReal-time CDC, event-driven systems, analytics
Audit PluginsMedium✅ (by policy)⚠️ DB user onlyCompliance requirements, structured logging
BytebaseN/A (app-level)✅ End userCentralized governance, approval workflows, team collaboration
  • Use the general query log for short-lived debugging (captures database user only).
  • Use triggers for fine-grained row-level auditing on critical tables (captures database user only).
  • Use binlog-based CDC for near-real-time change capture without application changes.
  • Use an audit plugin (Enterprise, Percona, or server_audit) for structured, compliance-grade statement logging (captures database user only).
  • Add Bytebase for centralized auditing that tracks the actual end user — not just the shared database account — linking every action to the person who performed it with full context of approvals and workflows.

By combining these layers, you gain both the visibility and control needed for secure, compliant, and well-governed MySQL operations.


Related reading:

Back to blog

Explore the standard for database development