Skip to main content
Scalple
Scalple
MySQL · MariaDB · GDPR Art. 32

MySQL audit logging
for GDPR compliance

Answer

MySQL Community Edition has no per-user audit logging. general_log captures every query but with no filtering, no JSON output, and significant performance overhead — it is not suitable for production. GDPR Article 32 compliance requires MySQL Enterprise Edition with the audit_log plugin, or MariaDB with server_audit. Neither provides structural tamper-evidence or human identity attribution if a shared user is in use — two gaps that no MySQL-native solution closes.

Why general_log is not a compliance solution

MySQL's general_log logs every query executed by every user. It is the simplest audit mechanism but has three properties that make it unsuitable for GDPR compliance.

Performance

20-40% throughput reduction on production workloads. MySQL documents it as unsuitable for production.

Volume

Logs every internal query — framework introspection, health checks, ORM bookkeeping. Noise ratio makes the log operationally useless.

No filtering

Cannot filter by user, database, table, or event type. Every query from every connection is logged identically.

general_log entry

2024-01-15T14:23:47.123456Z                42 Query	SELECT id, email FROM users WHERE id = 4892

Connection 42 ran this query. There is no user name in the line, no database, and no filtering. To know which MySQL user connection 42 belongs to, you must correlate with the CONNECT event earlier in the log — if it was not rotated.

MySQL Enterprise audit_log plugin

MySQL Enterprise Edition includes the audit_log plugin, which provides per-user filtering, JSON output, and asynchronous writing. It is the correct MySQL-native tool for GDPR compliance — with the caveat that it requires a commercial license.

my.cnf — load the plugin

# my.cnf — MySQL Enterprise
[mysqld]
plugin-load-add = audit_log.so
audit_log_format = NEW           # JSON output (recommended)
audit_log_rotate_on_size = 104857600  # rotate at 100 MB

Rule-based filtering — per user (MySQL 8.0.34+, recommended)

The modern approach uses mysql.audit_log_filter_* procedures. Do not mix with the legacy audit_log_policy variable — they are mutually exclusive modes.

-- Create a filter that captures table access events
CALL mysql.audit_log_filter_set_filter(
  'log_table_access',
'{ "filter": { "class": { "name": "table_access" } } }'
);

-- Assign the filter to specific users
CALL mysql.audit_log_filter_set_user('engineer_alice', 'log_table_access');
CALL mysql.audit_log_filter_set_user('engineer_bob', 'log_table_access');

audit_log JSON format (NEW format)

{
  "timestamp": "2024-01-15T14:23:47Z",
  "id": 1,
  "class": "table_access",
  "event": "read",
  "connection_id": 42,
  "account": {
    // MySQL user — NOT the human operator if shared
    "user": "app_user",
    "host": "10.0.1.5"
  },
  "table_access": {
    "database": "production",
    "table": "users",
    "query": "SELECT id, email FROM users WHERE id = 4892"
  }
}

MariaDB server_audit (free alternative)

# my.cnf (MariaDB)
[mariadb]
plugin_load_add = server_audit
server_audit_logging = ON
server_audit_events = QUERY_DML,QUERY_DDL,CONNECT
server_audit_incl_users = engineer_alice,engineer_bob

# MariaDB server_audit log line format:
# timestamp,server,user,host,connection_id,query_id,op,db,object,retcode
20240115 14:23:47,db01,app_user,10.0.1.5,42,157,QUERY,production,
'SELECT id, email FROM users WHERE id = 4892',0

What MySQL native logging cannot provide

Structural tamper evidence. The audit_log file is written to the filesystem. A system administrator can delete or replace it. MySQL Enterprise's read-only log mode prevents the MySQL process from modifying the file, but filesystem-level deletion remains possible.

Human identity attribution. The log records the MySQL user (account.user). If your application connects with a shared user, every audit line shows that shared user. MySQL has no native concept of a human identity separate from a database user.

Closing both gaps requires routing all database access through an access layer that authenticates humans and writes to an INSERT-only audit store — not to the MySQL log file. Scalple provides this for MySQL connections: human identity from SSO, audit entries written before query execution, structurally unmodifiable even by Scalple administrators.

MySQL audit logging — frequently asked questions

Does MySQL Community Edition support per-user audit logging?

No. MySQL Community Edition does not include the audit_log plugin. The general_log captures all queries regardless of user, with no per-user filtering, no table-level filtering, and no JSON output format. It generates enormous log volume on any production system and degrades performance significantly. Per-user audit logging in MySQL requires either MySQL Enterprise Edition (commercial license), a third-party plugin like McAfee MySQL Audit Plugin (open source, archived), or MariaDB with its built-in server_audit plugin.

What does the MySQL audit_log plugin log format look like?

MySQL Enterprise audit_log outputs structured JSON by default (NEW format) or XML (TRADITIONAL format). A JSON log entry looks like: {"timestamp":"2024-01-15T14:23:47Z","id":1,"class":"table_access","event":"read","connection_id":42,"account":{"user":"app_user","host":"10.0.1.5"},"table_access":{"database":"production","table":"users","query":"SELECT id, email FROM users WHERE id = 4892"}}. The account.user field shows the MySQL user — not the human operator if a shared account is used.

Is MariaDB server_audit free to use?

Yes. MariaDB's server_audit plugin is included in MariaDB Community Server at no cost. It supports per-user filtering, event filtering (CONNECT, QUERY, TABLE), and writes audit events to a file or syslog. It does not require a commercial license. The log format is CSV-like: timestamp, servername, username, host, connection_id, query_id, operation, database, object, retcode. If you are on MySQL Community and need audit logging without a commercial license, migrating to MariaDB or using a self-hosted access layer is the most practical path.

Is MySQL audit_log GDPR Article 32 compliant on its own?

No, for the same two structural reasons that apply to all file-based audit logs. First, the audit_log file is writable by a system administrator — it is not structurally tamper-evident. MySQL Enterprise provides a read-only audit log mode that prevents the server from modifying the file, but a filesystem administrator can still delete or replace it. Second, if your application connects via a shared MySQL user, the log records that user rather than the human operator. GDPR requires per-individual attribution, not per-database-role attribution. These gaps require additional infrastructure to close.

What is the performance impact of MySQL general_log?

MySQL general_log logs every query synchronously before execution. On a production system handling thousands of queries per second, this typically causes 20-40% throughput reduction and increases latency on every query. It is explicitly documented as 'not recommended for production use' in the MySQL manual. The audit_log plugin (Enterprise) and MariaDB server_audit use asynchronous writing and filtering, reducing overhead significantly. Even so, any file-based logging with full query capture adds measurable overhead. For compliance purposes, this is usually acceptable — but general_log is not the right mechanism.

GDPR-compliant MySQL access without an Enterprise license

Scalple provides human-attributed, INSERT-only audit logging for MySQL connections — on Community Edition, without the audit_log plugin. Deploy on your EU infrastructure.