-## :fontawesome-solid-gears: Audit Log Filter plugin { .title }
+## :fontawesome-solid-gears: Audit Log Filter component { .title }
-Learn about the Audit Log Filter plugin that allows you to monitor, log, and block a connection or query actively executed on the selected server.
+Learn about the Audit Log Filter component that allows you to monitor, log, and block a connection or query actively executed on the selected server.
[Audit Log Filter](audit-log-filter-overview.md){ .md-button}
diff --git a/docs/install-audit-log-filter.md b/docs/install-audit-log-filter.md
index 0e6a4c9ac57..9cf262ae54b 100644
--- a/docs/install-audit-log-filter.md
+++ b/docs/install-audit-log-filter.md
@@ -2,27 +2,27 @@
## Installation script
-The recommended way to install the component is to use the `audit_log_filter_linux_install.sql` script, located in the `share` directory, which creates the required tables before installing the component.
+Run `audit_log_filter_linux_install.sql` from the server `share` directory. The script creates the audit tables and then installs the component.
### Prerequisites
-The `plugin_dir` system variable defines the component library location. If needed, set the `plugin_dir` variable at server startup.
+`plugin_dir` locates the component library. Set the variable at startup when the default path is wrong.
### Database selection
-The script determines the target database using the following priority:
+The script picks the target database in this order:
-* If the component is already loaded, the script uses the database name from the `audit_log_filter.database` variable
+* When the component is already loaded, the script uses the database name from the `audit_log_filter.database` variable.
-* If the component is not loaded, but you pass the `-D db_name` option to the mysql client when running the script, it uses the specified `db_name`
+* When the component is not loaded and you pass the `-D db_name` option to the `mysql` client, the script uses the specified `db_name`.
-* If the component is not loaded and no `-D` option is provided, you must specify the `mysql` database when running the script
+* When the component is not loaded and you pass no `-D` option, you must specify the `mysql` database when running the script.
-You can also designate a different database with the `audit_log_filter.database` system variable. The database name cannot be NULL or exceed 64 characters. If the database name is invalid, the audit log filter tables are not found.
+Point the component at another database through `audit_log_filter.database`. The name must be non-`NULL`, at most 64 characters, and valid. An invalid name prevents the server from opening the audit log filter tables.
### Install the component
-To install the component using the script, you must specify the `mysql` database. You can do this in two ways:
+The script must run against the `mysql` database. Use either approach:
* Option 1: Run the script from the command line with the `-D mysql` option:
@@ -30,18 +30,18 @@ To install the component using the script, you must specify the `mysql` database
mysql -u root -p -D mysql < /path/to/mysql/share/audit_log_filter_linux_install.sql
```
-* Option 2: Connect to `mysql` database and run the script interactively:
+* Option 2: Connect to the `mysql` database and run the script interactively:
```sql
use mysql;
source /path/to/mysql/share/audit_log_filter_linux_install.sql;
```
- Replace `/path/to/mysql/share/` with the actual path to your MySQL installation's share directory.
+ Replace `/path/to/mysql/share/` with the path to the server installation's `share` directory.
### Verify installation
-After running the script, verify that the required tables are created:
+Confirm the audit tables exist:
```sql
show tables in mysql like 'aud%';
@@ -61,15 +61,15 @@ show tables in mysql like 'aud%';
## Alternative: INSTALL COMPONENT method
-You can also install the component using the `INSTALL COMPONENT` command, but this method does not create the required tables and will cause filter operations to fail.
+`INSTALL COMPONENT` loads the binary only. The statement does not create tables, so filter UDFs fail until you run the install script.
```mysql
-INSTALL COMPONENT 'file://audit_log_filter'
+INSTALL COMPONENT 'file://audit_log_filter';
```
### Verify component installation
-Check that the component is properly installed:
+Confirm the component row exists:
```sql
select * from mysql.component;
@@ -89,7 +89,7 @@ select * from mysql.component;
### Test filter functionality
-Test that the audit log filter is working correctly:
+Exercise a filter UDF:
```sql
SELECT audit_log_filter_set_filter('log_all', '{"filter": {"log": true}}');
@@ -108,11 +108,11 @@ SELECT audit_log_filter_set_filter('log_all', '{"filter": {"log": true}}');
!!! note
- This error occurs when the component is installed without the required tables. Using the SQL script prevents this issue.
+ The error appears when the component loads without the tables. Run `audit_log_filter_linux_install.sql` first.
### Fix missing tables
-If you have already installed the audit log component but are missing the required tables, you can run the `audit_log_filter_linux_install.sql` script to create the audit tables in the `mysql` database:
+When the component is installed but tables are missing, run the install script against `mysql`:
```shell
mysql -u root -p -D mysql < /path/to/mysql/share/audit_log_filter_linux_install.sql
@@ -125,12 +125,24 @@ use mysql;
source /path/to/mysql/share/audit_log_filter_linux_install.sql;
```
-This operation creates the missing tables without reinstalling the component.
+The script adds the tables and does not reinstall the component.
## Additional information
-
+When you are replacing an existing audit plugin (the legacy `audit_log` plugin or the transitional `audit_log_filter` plugin) with the component, see [Migrate to the audit log filter component](migrate-to-audit-log-filter-component.md) for the variable mapping, policy translation, and a worked example. The general plugin-to-component framing appears in [Upgrade from plugins to components](upgrade-components.md).
-To upgrade from `audit_log_filter` plugin in Percona Server 8.4 to `component_audit_log_filter` component in Percona Server {{vers}}, do the [manual upgrade](upgrade-components.md).
+## Additional reading
+
+* [Audit Log Filter overview](audit-log-filter-overview.md)
+
+* [Audit Log Filter quickstart](audit-log-filter-quickstart.md)
+
+* [Audit log filter functions, options, and variables](audit-log-filter-variables.md)
+
+* [Uninstall Audit Log Filter](uninstall-audit-log-filter.md)
+
+* [Upgrade components](upgrade-components.md)
+
+* [Upgrade Percona Server for MySQL](upgrade.md)
--8<--- "get-help-snip.md"
diff --git a/docs/manage-audit-log-filter.md b/docs/manage-audit-log-filter.md
index 533111a464d..132035bf6c7 100644
--- a/docs/manage-audit-log-filter.md
+++ b/docs/manage-audit-log-filter.md
@@ -1,20 +1,33 @@
# Manage the Audit Log Filter files
-The Audit Log Filter files have the following potential results:
+Audit log files can fill disks and grow without bound unless you cap them.
-* Consume a large amount of disk space
-* Grow large
+Use rotation to rename the active file and start a fresh file with the original name. Rotate manually with a UDF or automatically by size.
-You can manage the space by using log file rotation. This operation renames and then rotates the current log file and then uses the original name on a new current log file. You can rotate the file either manually or automatically.
-
-If automatic rotation is enabled, you can prune the log file. This pruning operation can be based on either the log file age or combined log file size.
+With automatic rotation enabled, prune old files by age, total size, or both.
## Manual log rotation
-The default setting for [`audit_log_filter.rotate_on_size`](audit-log-filter-variables.md#audit_log_filterrotate_on_size) is 1GB. If this option is set to `0`, the audit log filter component does not do an automatic rotation of the log file. You must do the rotation manually with this setting.
+[`audit_log_filter.rotate_on_size`](audit-log-filter-variables.md#audit_log_filterrotate_on_size) defaults to 1 GB. Set the variable to `0` to disable automatic rotation. You must then rotate by hand.
+
+Run `SELECT audit_log_rotate();` to rotate immediately. The call requires `AUDIT_ADMIN`.
+
+Rotation finishes immediately in the `ASYNCHRONOUS` and `PERFORMANCE` strategies instead of waiting on the background flush thread. Several rotations in the same second add a numeric suffix, for example `audit_filter.20250401T120000-1.log`, so that files are not overwritten.
+
+Pruning runs when `audit_log_filter.max_size` or `audit_log_filter.prune_seconds` is greater than zero and `audit_log_filter.rotate_on_size` is greater than zero.
+
+After rotation, delete archived files you no longer need. [`audit_log_read()`](audit-log-filter-variables.md#audit_log_read) can read renamed files only when the file names still match the active naming pattern.
+
+## Additional reading
+
+* [Audit log filter functions, options, and variables](audit-log-filter-variables.md) — rotation, size, and pruning options
+
+* [Audit Log Filter naming conventions](audit-log-filter-naming.md)
+
+* [Reading Audit Log Filter files](reading-audit-log-filter-files.md)
-The `SELECT audit_log_rotate()` command renames the file and creates a new audit log filter file with the original name. You must have the `AUDIT_ADMIN` privilege.
+* [Audit Log Filter compression and encryption](audit-log-filter-compression-encryption.md)
-The files are pruned if either `audit_log_filter.max_size` or `audit_log_filter.prune_seconds` have a value greater than 0 (zero) and `audit_log_filter.rotate_on_size` > 0.
+* [Audit Log Filter security](audit-log-filter-security.md)
-After the files have been renamed, you must manually remove any archived audit log filter files. The renamed audit log filter files can be read by `audit_log_read()`. The `audit_log_read()` does not find the logs if the name pattern differs from the current pattern.
+* [Audit Log Filter overview](audit-log-filter-overview.md)
diff --git a/docs/migrate-to-audit-log-filter-component.md b/docs/migrate-to-audit-log-filter-component.md
new file mode 100644
index 00000000000..6bd9eb40ff9
--- /dev/null
+++ b/docs/migrate-to-audit-log-filter-component.md
@@ -0,0 +1,313 @@
+# Migrate to the audit log filter component
+
+Percona Server for MySQL {{vers}} replaces two legacy audit sources with `component_audit_log_filter`. The legacy sources are the `audit_log` plugin and the transitional `audit_log_filter` plugin.
+
+This page covers migration from either source. The page maps plugin configuration to the component's system variables and filter JSON, and walks through a safe cutover.
+
+Before you start, read:
+
+* [Upgrade from plugins to components](upgrade-components.md) — timing, general procedure, and which plugins transition before or after the server upgrade.
+* [Audit Log Filter overview](audit-log-filter-overview.md) — what the component is and why it replaces the plugin.
+* [Install the audit log filter](install-audit-log-filter.md) — install script, component URN, and the `mysql.audit_log_filter` / `mysql.audit_log_user` tables.
+
+!!! note
+
+ The component and the plugin use different system variables and a different on-disk format. Do not enable both at the same time, and do not set `audit_log_*` plugin variables on a server running the component. See the [deprecation notice](audit-log-plugin.md) on the plugin page.
+
+## Which source are you migrating from?
+
+Two legacy sources exist. The target is `component_audit_log_filter` in both cases. The starting point differs:
+
+| Source | Recommended path |
+|---|---|
+| `audit_log` plugin (pre-8.4 installs, still available in {{vers}} as a deprecated plugin) | Upgrade to {{vers}}, install the component, translate `audit_log_*` variables to filter JSON, validate in parallel, then uninstall the plugin. |
+| `audit_log_filter` plugin (transitional, 8.0 and early 8.4 builds) | Upgrade to {{vers}} first, then transition to the component per [Upgrade from plugins to components → Transition after upgrade](upgrade-components.md#transition-timing). |
+
+The following detailed mapping targets the `audit_log` plugin because the plugin's configuration model — global `audit_log_*` variables and policy presets — differs most from the component. If you are migrating from the `audit_log_filter` plugin, the filter JSON you already wrote continues to work unchanged; the migration reduces to a shorter path:
+
+1. Upgrade the server to {{vers}}.
+2. Uninstall the plugin.
+3. Run the component install script. See [Install the audit log filter](install-audit-log-filter.md). The script creates `mysql.audit_log_filter` and `mysql.audit_log_user` and registers the component.
+4. Re-apply each filter with [`audit_log_filter_set_filter()`](audit-log-filter-variables.md#audit_log_filter_set_filterfilter_name-definition) and re-assign accounts with [`audit_log_filter_set_user()`](audit-log-filter-variables.md#audit_log_filter_set_userusername-filter_name). When you exported the plugin's filter and user tables, those rows can be re-inserted directly.
+5. Move non-filter settings (file path, format, rotation, syslog) from `audit_log_filter_*` plugin variables to the `audit_log_filter.*` component variables listed in the [variable mapping](#option-and-variable-mapping).
+
+See also [Upgrade from plugins to components → Transition after upgrade](upgrade-components.md#transition-timing).
+
+## What changes, at a glance
+
+* Configuration moves from global `audit_log_*` system variables into JSON filter definitions stored in `mysql.audit_log_filter`, plus per-account assignments in `mysql.audit_log_user`.
+
+* Scope moves from a single global include/exclude list to per-account assignments. `admin@%` and `app@%` can use different filters on the same server.
+
+* Rule changes require no restart or plugin reinstall. Update the JSON with `audit_log_filter_set_filter()`, and the next session applies the change.
+
+* Log format names shift. The plugin's `OLD`, `NEW`, `JSON`, and `CSV` map to the component's `JSONL` (default), `JSON`, and `NEW`. The component does not emit `OLD` XML or `CSV`.
+
+* The `audit_log_handler = SYSLOG` path becomes `audit_log_filter.handler = SYSLOG`. The syslog sub-variables use the same values under renamed component names.
+
+## Migration steps
+
+1. **Inventory the current configuration.** On the plugin-enabled server, capture the legacy settings:
+
+ ```sql
+ SHOW VARIABLES LIKE 'audit_log_%';
+ ```
+
+ Save the output. Translate each non-default value into a component variable, a filter JSON rule, or an `audit_log_filter_set_user()` call.
+
+2. **Upgrade the server to {{vers}}** by following [Upgrade procedures](upgrade-procedures.md). The `audit_log` plugin remains loadable in {{vers}}, so the old log keeps flowing during the transition.
+
+3. **Install the component** per [Install the audit log filter](install-audit-log-filter.md). The install script creates `mysql.audit_log_filter` and `mysql.audit_log_user`, then runs `INSTALL COMPONENT`.
+
+4. **Translate configuration** using the two mapping tables that follow. Apply non-filter settings (file path, format, rotation, syslog) as component variables. Apply scope settings (policy, include/exclude lists) as a filter definition plus user assignments.
+
+5. **Run in parallel (optional, recommended).** Load both the plugin and the component. Verify that the events you care about appear in the component's log. Compare record types, SQL text, and redactions.
+
+6. **Cut over.** Uninstall the plugin (`UNINSTALL PLUGIN audit_log;`), remove `audit_log_*` entries from `my.cnf`, and leave the component as the sole audit writer. When you also had the transitional `audit_log_filter` plugin loaded, uninstall that plugin as well.
+
+7. **Verify.** Log in as a subject account, execute a representative statement, and read the new log with [`audit_log_read()`](audit-log-filter-variables.md#audit_log_read) or by opening the file.
+
+## Option and variable mapping
+
+The following table maps plugin system variables (left) to component system variables (right). Variables not listed have no direct equivalent because the filter JSON grammar subsumes them.
+
+| Plugin variable | Component equivalent | Notes |
+|---|---|---|
+| `audit_log_file` | [`audit_log_filter.file`](audit-log-filter-variables.md#audit_log_filterfile) | Default file name and data-directory placement differ; see [Log file naming](audit-log-filter-naming.md). |
+| `audit_log_format` (`OLD`/`NEW`/`JSON`/`CSV`) | [`audit_log_filter.format`](audit-log-filter-variables.md#audit_log_filterformat) (`JSONL`/`JSON`/`NEW`) | Component default is `JSONL`. The component does not emit `OLD` XML or `CSV`; pick `NEW` to stay on XML or use `JSONL`/`JSON` (see [JSON and JSONL](audit-log-filter-json.md)). |
+| `audit_log_strategy` | [`audit_log_filter.strategy`](audit-log-filter-variables.md#audit_log_filterstrategy) | Same `ASYNCHRONOUS` / `PERFORMANCE` / `SEMISYNCHRONOUS` / `SYNCHRONOUS` trade-offs. |
+| `audit_log_buffer_size` | [`audit_log_filter.buffer_size`](audit-log-filter-variables.md#audit_log_filterbuffer_size) | Applies to `ASYNCHRONOUS` / `PERFORMANCE`. |
+| `audit_log_rotate_on_size` | [`audit_log_filter.rotate_on_size`](audit-log-filter-variables.md#audit_log_filterrotate_on_size) | Size-based rotation. |
+| `audit_log_rotations` | [`audit_log_filter.prune_seconds`](audit-log-filter-variables.md#audit_log_filterprune_seconds) + [`audit_log_filter.max_size`](audit-log-filter-variables.md#audit_log_filtermax_size) | The component prunes by age and total size instead of a fixed file count. Convert "keep N files" to an age or total-size budget. |
+| `audit_log_flush` | [`audit_log_rotate()`](audit-log-filter-variables.md#audit_log_rotate) | Manual rotation uses a UDF call instead of a variable toggle. |
+| `audit_log_handler` (`FILE` / `SYSLOG`) | [`audit_log_filter.handler`](audit-log-filter-variables.md#audit_log_filterhandler) | Same two values. |
+| `audit_log_syslog_ident` | [`audit_log_filter.syslog_tag`](audit-log-filter-variables.md#audit_log_filtersyslog_tag) | Renamed. |
+| `audit_log_syslog_facility` | [`audit_log_filter.syslog_facility`](audit-log-filter-variables.md#audit_log_filtersyslog_facility) | — |
+| `audit_log_syslog_priority` | [`audit_log_filter.syslog_priority`](audit-log-filter-variables.md#audit_log_filtersyslog_priority) | — |
+| `audit_log_policy` | *(filter JSON)* | Translate to class selection — see [Translating `audit_log_policy`](#translating-audit_log_policy). |
+| `audit_log_include_accounts` / `audit_log_exclude_accounts` | *(filter JSON + `audit_log_filter_set_user()`)* | Per-account assignment replaces global lists — see [Translating include / exclude account lists](#translating-include--exclude-account-lists). |
+| `audit_log_include_commands` / `audit_log_exclude_commands` | *(filter JSON)* | Use `log` conditions that test `general_sql_command.str`. |
+| `audit_log_include_databases` / `audit_log_exclude_databases` | *(filter JSON)* | Use `log` conditions that test `table_database.str` in `table_access`. |
+
+Component-only features with no plugin counterpart include:
+
+* Block-on-match with `abort`
+
+* Field redaction with `print` and `replace`
+
+* Predefined variables and functions inside conditions
+
+* Dynamic filter swapping with `activate` and `ref`
+
+See [Write filter definitions](write-filter-definitions.md) and the sub-pages.
+
+## Translating `audit_log_policy` to filter JSON
+
+`audit_log_policy` gated what the plugin recorded. The component expresses the same four choices as filter definitions:
+
+| Plugin policy | Equivalent filter definition |
+|---|---|
+| `ALL` | Log everything the component sees (respects [`audit_log_filter.event_mode`](audit-log-filter-variables.md#audit_log_filterevent_mode)): `{ "filter": { "log": true } }` |
+| `LOGINS` | `{ "filter": { "class": { "name": "connection" } } }` |
+| `QUERIES` | `{ "filter": { "class": [ { "name": "general" }, { "name": "table_access" } ] } }` |
+| `NONE` | Either do not assign a filter to the account, or bind an empty filter: `{ "filter": {} }` |
+
+Install any of these with [`audit_log_filter_set_filter()`](audit-log-filter-variables.md#audit_log_filter_set_filterfilter_name-definition) and assign them with [`audit_log_filter_set_user()`](audit-log-filter-variables.md#audit_log_filter_set_userusername-filter_name). For example:
+
+```sql
+SELECT audit_log_filter_set_filter('log_all', '{ "filter": { "log": true } }');
+SELECT audit_log_filter_set_user('%', 'log_all');
+```
+
+## Translating include/exclude lists
+
+The plugin filtered globally. The component filters per account through `audit_log_user` and per event through `log` conditions that compare event fields. Most migrations combine both.
+
+### Accounts
+
+Plugin:
+
+```sql
+SET GLOBAL audit_log_include_accounts = 'app@%,admin@localhost';
+```
+
+Component — assign a logging filter to the two accounts and leave the default `%` with no filter (or an empty filter):
+
+```sql
+SELECT audit_log_filter_set_filter('log_all', '{ "filter": { "log": true } }');
+SELECT audit_log_filter_set_user('app@%', 'log_all');
+SELECT audit_log_filter_set_user('admin@localhost','log_all');
+```
+
+Plugin exclude list, inverted:
+
+```sql
+SET GLOBAL audit_log_exclude_accounts = 'monitor@%';
+```
+
+Component — keep the default `%` assigned to a logging filter and assign `monitor@%` to an empty filter (or leave it unassigned if `%` has no filter):
+
+```sql
+SELECT audit_log_filter_set_filter('no_log', '{ "filter": {} }');
+SELECT audit_log_filter_set_user('monitor@%', 'no_log');
+```
+
+### Commands
+
+Plugin:
+
+```sql
+SET GLOBAL audit_log_include_commands = 'select,insert,update,delete';
+```
+
+Component — narrow the `general` class with a `log` condition that tests `general_sql_command.str`:
+
+```sql
+SELECT audit_log_filter_set_filter('log_dml', '{
+ "filter": {
+ "class": {
+ "name": "general",
+ "log": {
+ "field": {
+ "name": "general_sql_command.str",
+ "value": ["select", "insert", "update", "delete"]
+ }
+ }
+ }
+ }
+}');
+```
+
+For exclusion, wrap the condition in `not`.
+
+### Databases
+
+Plugin:
+
+```sql
+SET GLOBAL audit_log_include_databases = 'app,reports';
+```
+
+Component — narrow `table_access` by `table_database.str`:
+
+```sql
+SELECT audit_log_filter_set_filter('log_app_reports', '{
+ "filter": {
+ "class": {
+ "name": "table_access",
+ "log": {
+ "field": {
+ "name": "table_database.str",
+ "value": ["app", "reports"]
+ }
+ }
+ }
+ }
+}');
+```
+
+For a complete grammar reference (field names, logical operators, variables, functions), see [Write filter definitions](write-filter-definitions.md) and [Definition fields reference](audit-log-filter-definition-fields.md).
+
+## Worked example
+
+Starting plugin configuration in `my.cnf`:
+
+```ini
+[mysqld]
+plugin-load-add = audit_log.so
+audit_log_format = JSON
+audit_log_policy = ALL
+audit_log_include_accounts = app@%,admin@localhost
+audit_log_exclude_commands = set_option
+audit_log_rotate_on_size = 104857600
+audit_log_rotations = 10
+```
+
+After upgrading to {{vers}} and running `audit_log_filter_linux_install.sql`, replace it with:
+
+```ini
+[mysqld]
+audit_log_filter.format = JSON
+audit_log_filter.rotate_on_size = 104857600
+audit_log_filter.prune_seconds = 2592000
+```
+
+Then define the filter and assign accounts:
+
+```sql
+SELECT audit_log_filter_set_filter('log_all_except_set_option', '{
+ "filter": {
+ "class": [
+ { "name": "connection" },
+ { "name": "table_access" },
+ {
+ "name": "general",
+ "log": {
+ "not": {
+ "field": { "name": "general_sql_command.str", "value": "set_option" }
+ }
+ }
+ }
+ ]
+ }
+}');
+
+SELECT audit_log_filter_set_user('app@%', 'log_all_except_set_option');
+SELECT audit_log_filter_set_user('admin@localhost', 'log_all_except_set_option');
+```
+
+Accounts that match neither `app@%` nor `admin@localhost`, and that have no explicit assignment to the default `%` filter, are not audited. The setup reproduces the plugin's include-list behavior.
+
+## Cutover and verification
+
+1. Confirm the component is live:
+
+ ```sql
+ SELECT * FROM mysql.component WHERE component_urn = 'file://component_audit_log_filter';
+ ```
+
+2. Log in as a subject account, run a representative statement, and read the new log:
+
+ ```sql
+ SELECT audit_log_read('{}');
+ ```
+
+ See [Read log files](reading-audit-log-filter-files.md).
+
+3. Uninstall the legacy plugin and clear its settings from `my.cnf`:
+
+ ```sql
+ UNINSTALL PLUGIN audit_log;
+ ```
+
+4. Rotate once so new writes go to a fresh file under the component's naming scheme:
+
+ ```sql
+ SELECT audit_log_rotate();
+ ```
+
+5. Archive any pre-existing plugin log files you want to keep. The component does not ingest those files. You can run [`filter_audit_log_filter_files`](filter-audit-log-filter-files.md) to post-process component logs.
+
+## Known caveats
+
+* Log content changes slightly even when you keep the same format. Some statements that the 8.0 plugin did not record appear in current logs because the server sends additional events. The plugin page describes the `SELECT $$` example. Percona does not plan to backport format changes to match 8.0 output.
+
+* The component stores state in `mysql.audit_log_filter` and `mysql.audit_log_user`. Back up those tables before making bulk changes, and include the tables in standard `mysql` database backups.
+
+* Lifecycle events — `server_startup`, `server_shutdown`, and the `audit` class itself — are not valid filter targets. See [Definition fields reference](audit-log-filter-definition-fields.md#class-audit).
+
+* `audit_log_filter.event_mode` defaults to `REDUCED`. When you relied on plugin logging for classes beyond `connection`, `general`, `table_access`, and `message`, set `event_mode = FULL` to match the broader class set.
+
+## Additional reading
+
+* [Upgrade from plugins to components](upgrade-components.md)
+
+* [Audit Log Filter overview](audit-log-filter-overview.md)
+
+* [Install the audit log filter](install-audit-log-filter.md)
+
+* [Write filter definitions](write-filter-definitions.md)
+
+* [Functions, options, and variables](audit-log-filter-variables.md)
+
+* [Audit log plugin](audit-log-plugin.md) — legacy reference, with the deprecation notice
diff --git a/docs/reading-audit-log-filter-files.md b/docs/reading-audit-log-filter-files.md
index bd3da91b3b9..81721d861b3 100644
--- a/docs/reading-audit-log-filter-files.md
+++ b/docs/reading-audit-log-filter-files.md
@@ -1,28 +1,71 @@
# Reading Audit Log Filter files
-The Audit Log Filter functions can provide a SQL interface to read JSON-format audit log files. The functions cannot read log files in other formats. Configuring the component for JSON logging lets the functions use the directory that contains the current audit log filter file and search in that location for readable files. The value of the `audit_log_filter.file` system variable provides the file location, base name, and the suffix and then searches for names that match the pattern.
+Audit Log Filter exposes a SQL API to read audit files in JSON or JSONL only. Layout and the JSONL option appear in [Audit Log Filter format - JSON and JSONL](audit-log-filter-json.md) and [Audit Log Filter file format overview](audit-log-filter-formats.md).
-If the file is renamed and no longer fits the pattern, the file is ignored.
+Set `audit_log_filter.format` to match. [`audit_log_filter.file`](audit-log-filter-variables.md#audit_log_filterfile) defines the path, base name, and suffix that readers use to locate files.
+
+When a file no longer matches the pattern, readers ignore the file.
## Functions used for reading the files
-The following functions read the files in the JSON-format:
+The following functions read JSON or JSONL audit files:
-* [`audit_log_read`](audit-log-filter-variables.md#audit_log_read) - reads audit log filter events
+* [`audit_log_read`](audit-log-filter-variables.md#audit_log_read) — returns audit events from the log.
-* [`audit_log_read_bookmark`](audit-log-filter-variables.md#audit_log_read_bookmark) - for the most recently read event, returns a bookmark. This bookmark can be passed to `audit_log_read()`.
+* [`audit_log_read_bookmark`](audit-log-filter-variables.md#audit_log_read_bookmark) — returns a bookmark for the last read position. Pass the bookmark into `audit_log_read()` to resume.
-Initialize a read sequence by using a bookmark or an argument that specifies the start position:
+Start a read with a bookmark or an explicit start position:
```sql
SELECT audit_log_read(audit_log_read_bookmark());
```
-The following example continues reading from the current position:
+Continue from the current cursor:
```sql
SELECT audit_log_read();
```
-Reading a file is closed when the session ends or calling `audit_log_read()` with another argument.
+The read sequence ends when the session ends or when you call `audit_log_read('null')`.
+
+## Common starting points
+
+The `audit_log_read()` argument is a JSON object. Pick one of the following starting forms.
+
+Start at a specific timestamp:
+
+```sql
+SELECT audit_log_read('{"start": {"timestamp": "2026-05-20 12:28:10"}}');
+```
+
+Start at a date (the time defaults to `00:00:00`):
+
+```sql
+SELECT audit_log_read('{"start": {"timestamp": "2026-05-20"}}');
+```
+
+Cap how many events the call returns with `max_array_length`:
+
+```sql
+SELECT audit_log_read('{"start": {"timestamp": "2026-05-20 12:28:10"}, "max_array_length": 3}');
+```
+
+Resume from an explicit bookmark. Pass both `timestamp` and `id` at the top level, with no `start` wrapper:
+
+```sql
+SELECT audit_log_read('{"timestamp": "2026-05-20 12:28:10", "id": 1561422}');
+```
+
+The `start` form and the bookmark form (`timestamp` + `id`) are mutually exclusive. See [`audit_log_read()`](audit-log-filter-variables.md#audit_log_read) for the full parameter reference, including constraints on re-seeding a read sequence in flight.
+
+## Additional reading
+
+* [Audit Log Filter format - JSON and JSONL](audit-log-filter-json.md)
+
+* [Audit Log Filter file format overview](audit-log-filter-formats.md)
+
+* [Audit log filter functions, options, and variables](audit-log-filter-variables.md)
+
+* [Manage the Audit Log Filter files](manage-audit-log-filter.md)
+* [Audit Log Filter overview](audit-log-filter-overview.md)
diff --git a/docs/redact-audit-log-fields.md b/docs/redact-audit-log-fields.md
new file mode 100644
index 00000000000..9dfb55e29a0
--- /dev/null
+++ b/docs/redact-audit-log-fields.md
@@ -0,0 +1,224 @@
+# Redact audit log fields
+
+Audit events that carry SQL statement text can leak sensitive values into the audit log. Examples include credentials, personal data, and secret literals. The Audit Log Filter can rewrite that text as a **statement digest** before the event is written. The log records *what kind of statement ran* without capturing the literal values.
+
+This page covers the `print` and `replace` mechanism. For base filter authoring, see [Write audit_log_filter definitions](write-filter-definitions.md).
+
+## What can be replaced
+
+Only the statement-text fields are replaceable, and only with the `query_digest` function:
+
+| Event class | Replaceable field | Event mode required |
+|---|---|---|
+| `general` | `general_query.str` | `REDUCED` or `FULL` |
+| `table_access` | `query.str` | `REDUCED` or `FULL` |
+| `query` | `query.str` | `FULL` |
+| `parse` | `query.str` | `FULL` |
+
+The `general` and `table_access` classes are processed in both event modes. The `query` and `parse` classes are processed only when [`audit_log_filter.event_mode`](audit-log-filter-variables.md#audit_log_filterevent_mode) is `FULL`. A filter that targets `query` or `parse` under `REDUCED` is rejected at parse time.
+
+Replacement happens during filtering, so the choice between literal text and digest applies regardless of which log format (XML or JSON) writes the event.
+
+## Shape
+
+A `print` item goes inside a class or event block:
+
+```json
+"print": {
+ "field": {
+ "name": "field_name",
+ "print": condition,
+ "replace": replacement_value
+ }
+}
+```
+
+* `name` — the replaceable field from the preceding table.
+
+* `print` — a condition. When the condition evaluates to **true**, the field is kept. When the condition evaluates to **false**, the field is replaced. Use `"print": false` to replace unconditionally.
+
+* `replace` — the replacement value, specified as a `function` item. Only `query_digest` (with no arguments) is permitted.
+
+The conditional form lets you mix redacted and literal statements in the same filter.
+
+## Example 1: Redact every `general` event
+
+Replace statement text in every `general` event with its digest:
+
+```json
+{
+ "filter": {
+ "class": {
+ "name": "general",
+ "print": {
+ "field": {
+ "name": "general_query.str",
+ "print": false,
+ "replace": {
+ "function": { "name": "query_digest" }
+ }
+ }
+ }
+ }
+ }
+}
+```
+
+## Example 2: Redact both statement-carrying classes
+
+`general` and `table_access` both carry statement text. Combine them into one filter:
+
+```json
+{
+ "filter": {
+ "class": [
+ {
+ "name": "general",
+ "print": {
+ "field": {
+ "name": "general_query.str",
+ "print": false,
+ "replace": { "function": { "name": "query_digest" } }
+ }
+ }
+ },
+ {
+ "name": "table_access",
+ "print": {
+ "field": {
+ "name": "query.str",
+ "print": false,
+ "replace": { "function": { "name": "query_digest" } }
+ }
+ }
+ }
+ ]
+ }
+}
+```
+
+The resulting audit stream contains only digests, with no literal SQL text. The output is a common baseline for PCI and PII environments.
+
+## Example 3: Redact only specific events
+
+Scope replacement to a subset of events by nesting `print` inside an `event` block. The following filter redacts `query.str` on `insert` and `update` `table_access` events but leaves `read` and `delete` alone:
+
+```json
+{
+ "filter": {
+ "class": {
+ "name": "table_access",
+ "event": {
+ "name": ["insert", "update"],
+ "print": {
+ "field": {
+ "name": "query.str",
+ "print": false,
+ "replace": { "function": { "name": "query_digest" } }
+ }
+ }
+ }
+ }
+ }
+}
+```
+
+## Example 4: Redact account-management statements
+
+`query_digest` with an argument is a Boolean comparator. The comparator is useful in a `log` condition to decide *whether* to log an event based on the digest. Combined with field checks, you can log and redact only specific statement types. The following filter matches `general`/`status` events for account-management DDL and replaces the literal statement with the digest:
+
+```json
+{
+ "filter": {
+ "class": {
+ "name": "general",
+ "event": {
+ "name": "status",
+ "print": {
+ "field": {
+ "name": "general_query.str",
+ "print": false,
+ "replace": {
+ "function": { "name": "query_digest" }
+ }
+ }
+ },
+ "log": {
+ "or": [
+ { "field": { "name": "general_sql_command.str", "value": "alter_user" } },
+ { "field": { "name": "general_sql_command.str", "value": "alter_user_default_role" } },
+ { "field": { "name": "general_sql_command.str", "value": "create_role" } },
+ { "field": { "name": "general_sql_command.str", "value": "create_user" } }
+ ]
+ }
+ }
+ }
+ }
+}
+```
+
+For the full set of `general_sql_command.str` values, see [Test event field values](write-filter-definitions.md#test-event-field-values).
+
+## Conditional redaction
+
+To keep literal text for *most* statements and redact only specific ones (or the reverse), use `query_digest` as a comparator inside `print`. With an argument, the function returns true when the active statement digest equals the argument.
+
+Keep the literal text when the digest matches `SELECT ?`, and replace otherwise:
+
+```json
+"print": {
+ "field": {
+ "name": "general_query.str",
+ "print": {
+ "function": {
+ "name": "query_digest",
+ "args": "SELECT ?"
+ }
+ },
+ "replace": {
+ "function": { "name": "query_digest" }
+ }
+ }
+}
+```
+
+Invert with `not` to redact only the matching statements and keep literal text for the rest:
+
+```json
+"print": {
+ "field": {
+ "name": "general_query.str",
+ "print": {
+ "not": {
+ "function": {
+ "name": "query_digest",
+ "args": "SELECT ?"
+ }
+ }
+ },
+ "replace": {
+ "function": { "name": "query_digest" }
+ }
+ }
+}
+```
+
+### `args` shorthand and typed-array form
+
+`query_digest` accepts the single string argument in two equivalent forms:
+
+* Shorthand (preferred): `"args": "SELECT ?"`. Use this form for a single literal string.
+
+* Typed array: `"args": [{"string": {"string": "SELECT ?"}}]`. Use this form when a rule generator emits the verbose typed-array layout for every function call.
+
+The component parses both forms identically. The shorthand is shorter and easier to read.
+
+## See also
+
+* [Write audit_log_filter definitions](write-filter-definitions.md) — base grammar and `log` conditions.
+
+* [Block statements with an audit log filter](block-statements-with-audit-filter.md) — use `abort` to prevent execution.
+
+* [Audit Log Filter definition fields](audit-log-filter-definition-fields.md) — complete field reference per class.
+
+* [Audit log filter functions, options, and variables](audit-log-filter-variables.md).
diff --git a/docs/uninstall-audit-log-filter.md b/docs/uninstall-audit-log-filter.md
index 3c934d1bd10..5c402e8390a 100644
--- a/docs/uninstall-audit-log-filter.md
+++ b/docs/uninstall-audit-log-filter.md
@@ -1,13 +1,25 @@
# Uninstall Audit Log Filter
-If you no longer need the audit log filter functionality, you can remove the component from server using the following command:
+To remove the Audit Log Filter component from the server, run the following statement:
```sql
UNINSTALL COMPONENT 'file://component_audit_log_filter';
```
-This command does the following:
+The statement performs the following actions:
-* `UNINSTALL COMPONENT`: This tells the server to remove a plugin or feature that was previously installed.
-
-* `file://component_audit_log_filter`: This is the identifier for the Audit Log Filter Component, which is responsible for applying rules to decide which audit log events are recorded.
+* `UNINSTALL COMPONENT` drops a previously installed component from the server.
+
+* `file://component_audit_log_filter` is the URN for the Audit Log Filter component. The component applies the configured rules and decides which audit events are recorded.
+
+## Additional reading
+
+* [Install the audit log filter](install-audit-log-filter.md)
+
+* [Audit Log Filter overview](audit-log-filter-overview.md)
+
+* [Upgrade components](upgrade-components.md)
+
+* [Disable Audit Log Filter logging](disable-audit-log-filter.md)
+
+* [Audit log filter functions, options, and variables](audit-log-filter-variables.md)
diff --git a/docs/upgrade-components.md b/docs/upgrade-components.md
index 5901bbc85c0..a63cee0d902 100644
--- a/docs/upgrade-components.md
+++ b/docs/upgrade-components.md
@@ -83,4 +83,5 @@ The timing of the transition depends on the specific plugin:
* [Use Keyring Vault component](./use-keyring-vault-component.md)
* [Install Data Masking component](./install-data-masking-component.md)
* [Audit Log Filter component overview](./audit-log-filter-overview.md)
+* [Migrate to the audit log filter component](./migrate-to-audit-log-filter-component.md)
* [Manage Audit Log Filter](./manage-audit-log-filter.md)
\ No newline at end of file
diff --git a/docs/write-filter-definitions.md b/docs/write-filter-definitions.md
index f369f3b590b..51777e3c960 100644
--- a/docs/write-filter-definitions.md
+++ b/docs/write-filter-definitions.md
@@ -1,6 +1,8 @@
-# Write audit_log_filter definitons
+# Write audit_log_filter definitions
-When you’re setting up audit log filters in Percona Server for MySQL, you use JSON values to define those filters. At their core, a filter is just a JSON object with a very simple structure.
+Audit log filters are JSON documents you pass to `audit_log_filter_set_filter()`.
+
+Every filter nests rules under a root `filter` object. For authoritative class, event, and field names that validation accepts, see [Audit Log Filter definition fields](audit-log-filter-definition-fields.md).
-| Option | Details |
-|----------------------------|-------------------------------------------------------------------------|
-| When you include the log item | The value you set (`true` or `false`) determines if events are logged. |
-| When you don’t include the log item | If no `class` or `event` items are specified, logging is enabled by default. |
-
-However, if you define specific class or event items, they can have their own log settings to control logging for just those events.
+| Option | Details |
+|--------|---------|
+| Explicit `log` | Honors `true` / `false` at that level. |
+| No `log`, no class/event rules | Defaults to on (log all). |
+| Class or event rules present | Each block can carry its own `log` override. |
### Log specific event classes
-If you want to log specific types of activities, such as connection-related events, you can define `class` item in your filter.
-
-For example, to log events in the “connection” class, your filter might look like this example.
+To limit logging to one class, set `class.name`, for example `connection`:
```json
{
@@ -140,13 +186,9 @@ For example, to log events in the “connection” class, your filter might look
}
```
-In the example, the outermost element is "filter", which represents the audit log filter you’re defining. Everything within this key specifies what you want to track in the audit logs.
-
-Inside "filter", you have a "class" element. This tells the server the general category of events you’re interested in. In this example, "class" is set to { "name": "connection" }. The "name" key within the "class" specifies the type of events within the connection category that should be logged. By using "connection", you’re instructing the server to monitor connection-related events, such as when users connect to or disconnect from the database.
+`{ "name": "connection" }` under `filter.class` audits connection events: `connect`, `disconnect`, and `change_user`. `event_mode` and optional `event` narrowing apply.
-This structure makes it easy to focus your logging on specific areas of activity in the server, helping you capture only the data you need without cluttering your logs with unnecessary details.
-
-The following filter definition does the same thing, but explicitly states that no other logging is filtered.
+The next example turns default logging off, then turns connection logging back on inside the class block:
```json
{
@@ -162,15 +204,11 @@ The following filter definition does the same thing, but explicitly states that
### Log multiple classes or events
-To log multiple classes at the same time, you have two options.
-
-Both filters achieve the same goal: they define an audit log filter to log events related to "connection", "general", and "table_access". These examples only differ in how the event classes are listed.
-
-It’s a comprehensive configuration for monitoring activities at the connection level, general server operations, and specific table interactions. This setup is useful for administrators who want broad visibility into user activity and server behavior.
+Log several classes as separate objects in an array, or as one array of names. The two forms are equivalent when you need no per-class options.
#### A list
-A list is useful when you want to expand the filter later to include more granular settings for each class.
+Use a list when you plan to add per-class keys such as `user` or `event` later.
```json
{
@@ -186,7 +224,7 @@ A list is useful when you want to expand the filter later to include more granul
#### An array
-To simplify, when you have multiple items, you can combine their values into a single array:
+Combine class names in one array when the rules stay uniform:
```json
{
@@ -197,69 +235,54 @@ To simplify, when you have multiple items, you can combine their values into a s
}
}
```
-This array object is more concise. You should use it if you do not plan to add additional settings for individual classes.
+Prefer the compact form only when no per-class overrides are needed.
#### List of event and subclass options
-The table shows the available event classes and their subclasses:
+Typical class and subclass pairs for authoring appear in the following table. Selection depends on [`audit_log_filter.event_mode`](audit-log-filter-variables.md#audit_log_filterevent_mode). Validate exact spellings in [Audit Log Filter definition fields](audit-log-filter-definition-fields.md).
-
+
| Class name | Event subclass | Details |
|---|---|---|
-| `connection` | `connect` | Tracks when a connection is initiated (successful or not) |
-| `connection` | `change_user` | Tracks when a user changes during a session |
-| `connection` | `disconnect` | Tracks when a connection is terminated |
-| `general` | `status` | Tracks the status of general server operations (for example, query success or failure) |
-| `general` | `command` | Logs SQL commands issued to the server |
-| `table_access` | `read` | Logs read statements, like `SELECT` or `INSERT INTO … SELECT` |
-| `table_access` | `delete` | Logs delete statements, like `DELETE` or `TRUNCATE TABLE` |
-| `table_access` | `insert` | Logs insert statements, like `INSERT` or `REPLACE` |
-| `table_access` | `update` | Logs update statements, like `UPDATE` |
-
-This setup gives you the flexibility to monitor the exact events that are important to you while controlling logging behavior in a detailed way.
+| `connection` | `connect` | New sessions, success or failure. |
+| `connection` | `change_user` | `CHANGE USER`. |
+| `connection` | `disconnect` | Session end. |
+| `general` | `status` | Query or command completion: success or failure. |
+| `general` | `command` | Command-level general events in `FULL` mode. |
+| `table_access` | `read` | Reads such as `SELECT` and `INSERT ... SELECT`. |
+| `table_access` | `delete` | Deletes and truncate-style operations. |
+| `table_access` | `insert` | Inserts and `REPLACE`. |
+| `table_access` | `update` | Updates. |
+| `message` | `internal` | Internal audit API messages. |
+| `message` | `user` | User-emitted messages via `audit_api_message_emit_udf()`. |
+
+
+
+Mix and match subclasses under `event` to mirror the threat model.
### Inclusive filters
-Inclusive filters capture specific database events you want to log. They allow you to precisely target and record only the actions you care about.
-
-#### Basic Structure
+Inclusive rules list what to log. Pair classes with `user`, `database`, `table`, `event`, `status`, and similar fields until the stream matches the policy.
-An inclusive filter uses a JSON configuration that defines which events to include in your audit logging. The filter specifies:
+#### Basic structure
-* What type of events to capture
+Spell out:
-* Which users to track
+* Which classes matter.
-* What specific actions to log
+* Which accounts or objects to watch.
-Common use cases for inclusive filters include security audits, compliance tracking, performance monitoring, and user behavior analysis.
+* Which subclasses or outcomes qualify.
-Event tracking can be more precise, which helps reduce unnecessary log noise. By focusing on the specific events that matter, you can enhance security monitoring and ensure that only the most relevant data is logged. This approach not only improves the clarity of your logs but also helps optimize performance by limiting the number of events being recorded, reducing overhead and making it easier to manage the system.
+Typical uses include compliance evidence, privileged-user monitoring, schema-change tracking, and incident response.
-It’s important to consider the performance impact of logging and how it might affect your server. Before deploying your filters in a production environment, test them thoroughly to ensure everything works as expected.
+Tighter filters mean less noise and less I/O. Always stage-test rules before production.
-#### Inclusive filter example
+#### Inclusive filter example
-This filter is useful for monitoring and auditing database changes made by administrative users, particularly to ensure that updates and deletions are tracked.
+Administrators often watch destructive `table_access` work. The following skeleton logs `update` and `delete` only:
```json
{
@@ -267,9 +290,16 @@ This filter is useful for monitoring and auditing database changes made by admin
"class": [
{
"name": "table_access",
- "event": [
- { "name": "update"},
- { "name": "delete"}
+ "event": [
+ {
+ "name": ["update", "delete"],
+ "log": {
+ "and": [
+ { "field": { "name": "table_database.str", "value": "app_db" } },
+ { "field": { "name": "table_name.str", "value": "sensitive_tbl" } }
+ ]
+ }
+ }
]
}
]
@@ -277,23 +307,23 @@ This filter is useful for monitoring and auditing database changes made by admin
}
```
-This filter does one thing: log all update and delete operations performed by admin users. The filter uses the following components:
+The rule matches `update` and `delete` `table_access` events. The nested `log` condition narrows logging to events where `table_database.str` equals `app_db` **and** `table_name.str` equals `sensitive_tbl`. The `read` and `insert` subclasses are not named, so those events skip the rule.
-* "class": The top-level key specifies that the filter applies to the `table_access` class. This class monitors events related to database table interactions.
+* `"class"` — begins the class rule block.
-* "name": "table_access": This defines the event class you want to track. This class captures interactions with database tables, such as read, insert, update, and delete modifications. Specifies the specific class of events
+* `"name": "table_access"` — limits the rule to table-access events. Subclasses: `read`, `insert`, `update`, and `delete`.
-* user: ["admin"]: This specifies that the filter applies only to events performed by the admin user and restricts the filter to only log actions executed by this user.
+* `"event"` — selects the `update` and `delete` subclasses. Other subclasses skip the rule.
-* event: ["update", "delete"]: This narrows down the filter to track only specific actions. In this case, the filter captures update and delete modifications. Any SELECT (read) or INSERT modifications on tables will not be logged, as they are excluded by this filter.
+* `"log"` — a per-event condition. `true` logs every match. A `field`, `and`, `or`, or `not` structure logs only matches that satisfy the condition.
-Inclusive filters give you granular control over your MySQL audit logging, allowing you to capture exactly the information you need without overwhelming your logging system.
+* `"field"` — compares an event field against a value. `table_database.str` and `table_name.str` are fields that every `table_access` event carries. See [Audit Log Filter definition fields](audit-log-filter-definition-fields.md) for the full list.
-### Exclusive filters
+To widen or narrow the rule, change the field values, add `or` branches for multiple tables, or drop one `field` check to scope by database only or by table name only.
-Exclusive filters in the audit_log_filter for Percona Server for MySQL let you exclude certain activities from being logged, helping you reduce log size and focus on what matters most. For example, you can filter out routine operations like health checks or background processes to avoid unnecessary clutter in your logs.
+#### Log only UPDATE and DELETE on a specific table
-This example defines a filter that `excludes` (negate: true) all table access events ("table_access") by the user "readonly_user". Events for other users or other classes of activity are still being logged unless additional filters are defined.
+To log `update` and `delete` against a single table — or a short list of tables inside one database — combine `table_database.str` with an `or` over `table_name.str` values:
```json
{
@@ -301,15 +331,56 @@ This example defines a filter that `excludes` (negate: true) all table access ev
"class": [
{
"name": "table_access",
- "user": ["readonly_user"],
- "negate": true
+ "event": [
+ {
+ "name": ["update", "delete"],
+ "log": {
+ "and": [
+ { "field": { "name": "table_database.str", "value": "app_db" } },
+ {
+ "or": [
+ { "field": { "name": "table_name.str", "value": "sensitive_tbl" } },
+ { "field": { "name": "table_name.str", "value": "audit_events" } }
+ ]
+ }
+ ]
+ }
+ }
+ ]
}
]
}
}
```
-### Exclusive filter example
+To scope to one database only, omit the inner `or` and keep the `table_database.str` check.
+
+`table_access` events do not carry a user field. Filter by account at assignment time with [`audit_log_filter_set_user()`](audit-log-filter-variables.md).
+
+### Exclusive filters
+
+Exclusive (or negated) rules drop noisy work. Two idioms apply:
+
+* Set `"log": true` at the filter level, then set `"log": false` on a specific class or event to suppress only that slice.
+
+* Use a `"not"` logical operator inside a `log` condition to drop matches that satisfy an expression.
+
+#### Suppress one class while logging everything else
+
+```json
+{
+ "filter": {
+ "log": true,
+ "class": { "name": "general", "log": false }
+ }
+}
+```
+
+The filter logs every event *except* `general` class events. `log: true` at the top enables logging globally. The class-level `log: false` carves out one class.
+
+#### Invert a field match with `not`
+
+Wrap a condition in `not` inside a `log` item to drop the events that match the condition. Because inversion tests a field on the current event, use a field the class carries (for example, `table_database.str` on `table_access`, `user.str` on `connection`, or `general_user.str` on `general`). The following example suppresses `table_access` events against the internal `mysql` schema:
```json
{
@@ -317,47 +388,278 @@ This example defines a filter that `excludes` (negate: true) all table access ev
"class": [
{
"name": "table_access",
- "user": ["admin", "developer"],
- "database": ["financial"],
- "event": [
- {"name":"update"},
- {"name":"delete"}
- ],
- "status": [1]
- },
+ "log": {
+ "not": {
+ "field": { "name": "table_database.str", "value": "mysql" }
+ }
+ }
+ }
+ ]
+ }
+}
+```
+
+The filter logs `table_access` events except those that touch the `mysql` system schema. For user-based suppression, apply the `not` against a field that exists on the class you are filtering: `user.str` on `connection`, or `general_user.str` on `general`. Alternatively, scope the filter to specific accounts at assignment time with [`audit_log_filter_set_user()`](audit-log-filter-variables.md).
+
+#### Exclusive filter example
+
+This filter logs everything except `general` events, and within `connection` events drops `connect`/`disconnect` subclasses (keeping only `change_user`):
+
+```json
+{
+ "filter": {
+ "log": true,
+ "class": [
{
"name": "connection",
- "user": ["external_service"],
- "status": [0]
- }
+ "event": [
+ { "name": "connect", "log": false },
+ { "name": "disconnect", "log": false }
+ ]
+ },
+ { "name": "general", "log": false }
]
}
}
```
-!!! note "Filter definition"
- In the filter definitions shown in this example, status values are displayed as integers for readability, but they must be specified as strings in your actual filter definitions (for example, `"status": ["0"]` or `"status": ["1"]`). The audit log filter does not filter on integer values, only on string values. This applies to all numeric filter criteria, including `connection_id`, `thread_id`, and `status`. If you use integer values, you will see the error: `ERROR: Incorrect rule definition.`
+* The top-level `"log": true` turns everything on by default.
+
+* The `connection` block's `event` list silences the two noisy subclasses but leaves `change_user` on.
+
+* The second class entry turns off the entire `general` class.
+
+Field-type rules — JSON numbers against strings, `connection_type` symbolic constants such as `"::tcp/ip"`, and the types accepted for each field — appear under [`audit_log_filter_set_filter()`](audit-log-filter-variables.md#audit_log_filter_set_filterfilter_name-definition) and in [Audit Log Filter definition fields](audit-log-filter-definition-fields.md#connection).
+
+## Advanced filter constructs
+
+The preceding sections cover logging on or off at class and event granularity. The filter language also supports:
+
+* Per-event **conditions**.
+
+* Execution **blocking**.
+
+* References to server **variables** and **functions**.
+
+* Field-value **replacement** (redaction).
+
+* **Dynamic** filter swapping.
+
+Validate exact field names and types against [Audit Log Filter definition fields](audit-log-filter-definition-fields.md) before deploying any of these features to production.
+
+### Test event field values
+
+Inside an `event` block, a `log` item can carry a `field` comparison. The rule logs the event only when the field equals the given value.
+
+```json
+{
+ "filter": {
+ "class": {
+ "name": "general",
+ "event": {
+ "name": "status",
+ "log": {
+ "field": { "name": "general_command.str", "value": "Query" }
+ }
+ }
+ }
+ }
+}
+```
+
+The preceding example logs `general`/`status` events only when `general_command.str` equals `Query`, which drops `Execute`, `Quit`, and `Change user`. String fields take string values. Integer fields such as `status` on `connection` or `general_error_code` on `general` take JSON numbers.
+
+Fields available on each class are listed in [Audit Log Filter definition fields](audit-log-filter-definition-fields.md). Examples:
+
+* `connection`: `status`, `user.str`, `host.str`, `ip.str`, `database.str`, `connection_type`
+
+* `general`: `general_error_code`, `general_user.str`, `general_command.str`, `general_query.str`, `general_sql_command.str`, `general_host.str`, `general_ip.str`
+
+* `table_access`: `query.str`, `table_database.str`, `table_name.str`
+
+### Combine conditions with logical operators
+
+`and`, `or`, and `not` let you build compound conditions. They take an array (for `and` / `or`) or a single sub-condition (for `not`) and can be nested arbitrarily.
+
+```json
+{
+ "filter": {
+ "class": {
+ "name": "general",
+ "event": {
+ "name": "status",
+ "log": {
+ "or": [
+ {
+ "and": [
+ { "field": { "name": "general_command.str", "value": "Query" } },
+ { "field": { "name": "general_command.length", "value": 5 } }
+ ]
+ },
+ {
+ "and": [
+ { "field": { "name": "general_command.str", "value": "Execute" } },
+ { "field": { "name": "general_command.length", "value": 7 } }
+ ]
+ }
+ ]
+ }
+ }
+ }
+ }
+}
+```
+
+The filter logs `general`/`status` events where `general_command` is either `Query` (length 5) or `Execute` (length 7).
+
+Use `not` to invert any sub-expression. For example, `{ "not": { "field": { "name": "user.str", "value": "healthcheck" } } }` matches every account except `healthcheck` on a class that carries `user.str`.
+
+### Block execution with `abort`
+
+An `event` block can carry an `abort` item. The `abort` item prevents matching statements from executing, either instead of logging or in addition to logging. Blocking is a policy-enforcement capability, not observability, so blocking has a dedicated page: [Block statements with an audit log filter](block-statements-with-audit-filter.md).
+
+### Reference predefined variables
+
+A `log` or `abort` condition can test a predefined variable with a `variable` item. The condition is true when the variable equals the given value.
+
+```json
+{
+ "filter": {
+ "class": {
+ "name": "general",
+ "event": {
+ "name": "status",
+ "log": {
+ "variable": {
+ "name": "audit_log_connection_policy_value",
+ "value": "::none"
+ }
+ }
+ }
+ }
+ }
+}
+```
+
+Predefined variables mirror the legacy-mode `audit_log_*_policy` system variables. Operators can re-tune an active filter by changing a server variable instead of rewriting the JSON:
+
+* `audit_log_connection_policy_value` — `0` or `"::none"`, `1` or `"::errors"`, `2` or `"::all"`.
+
+* `audit_log_policy_value` — `0` or `"::none"`, `1` or `"::logins"`, `2` or `"::all"`, `3` or `"::queries"`.
+
+* `audit_log_statement_policy_value` — `0` or `"::none"`, `1` or `"::errors"`, `2` or `"::all"`.
+
+Symbolic constants such as `"::none"` and `"::all"` are case-sensitive strings. The symbolic constants are interchangeable with the numeric form.
+
+### Reference predefined functions
+
+Use a `function` item to call a built-in function inside a `log` or `abort` condition. The `name` key holds the function name without parentheses. The `args` key holds an array of arguments. Omit `args` when the function takes none.
+
+```json
+{
+ "filter": {
+ "class": {
+ "name": "general",
+ "event": {
+ "name": "status",
+ "log": {
+ "function": {
+ "name": "find_in_include_list",
+ "args": [ { "string": [ { "field": "user.str" },
+ { "string": "@" },
+ { "field": "host.str" } ] } ]
+ }
+ }
+ }
+ }
+ }
+}
+```
+
+The preceding example logs `general`/`status` events only when the current account, built by concatenating `user.str`, `@`, and `host.str`, exists in `audit_log_include_accounts`.
+
+Available functions:
+
+* `audit_log_exclude_accounts_is_null()` — returns true when `audit_log_exclude_accounts` is `NULL`. Takes no arguments.
+
+* `audit_log_include_accounts_is_null()` — returns true when `audit_log_include_accounts` is `NULL`. Takes no arguments.
+
+* `find_in_exclude_list(account)` — returns true when `account` appears in `audit_log_exclude_accounts`.
+
+* `find_in_include_list(account)` — returns true when `account` appears in `audit_log_include_accounts`.
+
+* `query_digest([str])` — with no argument, returns the current event's statement digest and is usable in `replace`. With an argument, returns a Boolean that compares the argument to the current digest.
+
+* `string_find(text, substr)` — returns true when `substr` is contained in `text`. Case-sensitive.
+
+* `debug_sleep(millisec)` — sleeps the given number of milliseconds. Debug builds only. Used for performance measurement.
+
+### Replace event field values (redaction)
+
+A `print` / `replace` item inside a class or event block rewrites statement text (`general_query.str` and `query.str`) as a `query_digest` before the event is logged. The rewrite keeps literal values out of the audit stream. The capability is compliance- and PII-focused. See the dedicated page: [Redact audit log fields](redact-audit-log-fields.md).
+
+### Replace a filter dynamically
+
+A filter can swap itself for a different rule set mid-session. Nest a `filter` block inside an event and give the outer filter an `id`. Use `activate` to trigger the swap. Use `ref` to point back at the original filter.
+
+```json
+{
+ "filter": {
+ "id": "main",
+ "class": {
+ "name": "table_access",
+ "event": {
+ "name": ["update", "delete"],
+ "log": false,
+ "filter": {
+ "class": {
+ "name": "general",
+ "event": { "name": "status",
+ "filter": { "ref": "main" } }
+ },
+ "activate": {
+ "or": [
+ { "field": { "name": "table_name.str", "value": "temp_1" } },
+ { "field": { "name": "table_name.str", "value": "temp_2" } }
+ ]
+ }
+ }
+ }
+ }
+ }
+}
+```
+
+How the filter behaves:
+
+* `main` waits for `table_access` `update` or `delete` events. `log: false` means those events are not logged directly.
+
+* When one of those events touches `temp_1` or `temp_2`, the inner filter activates.
+
+* The inner filter waits for the next `general`/`status` event, typically at end of statement, and logs the event. The nested `ref: main` then restores the outer filter.
-This filter captures failed update/delete modifications by admin and developer users in the financial database and successful connections for the `external_service` user
+Net effect: you log one `general`/`status` entry per statement that touched `temp_1` or `temp_2`, instead of many `table_access` rows. A single `UPDATE temp_1, temp_3 SET ...` emits one log entry, not one per row touched.
+
+`activate` is valid only inside a subfilter. Using `activate` on the top-level `filter` raises an error. `id` values are scoped to the filter definition only. `id` values are unrelated to the `audit_log_filter_id` system variable.
## Best practices
-Following a systematic approach helps ensure successful deployment and maintenance when implementing audit log filters. Start by creating broad, inclusive filters that capture a wide range of events, giving you a comprehensive view of your database activity. For example, you might begin by logging all actions from administrative users or all changes on critical databases. As you analyze the captured data, you can refine these filters to focus on specific events, users, or changes that matter most to your organization.
+1. Start wide, then narrow. Begin with a noisy catch-all in staging, then peel away classes you do not need.
-Testing is crucial before deploying filters in production. Set up a non-production environment that mirrors your production setup as closely as possible. This non-production environment allows you to verify that your filters capture the intended events without missing critical information. During testing, pay particular attention to how different filter combinations interact and ensure they don't create any unexpected gaps in your audit coverage.
+2. Test combinations. Overlapping rules and assignments surprise operators. Validate on a clone.
-Log file management requires careful attention. Audit logs can grow rapidly, especially with detailed filtering configurations. Monitor your log file sizes regularly and implement appropriate rotation policies. Consider storage capacity, retention requirements, and system performance when determining how much detail to include in your logs. It's often helpful to calculate expected log growth based on your typical database activity and adjust your rotation policies accordingly.
+3. Plan retention. Pair filters with rotation, pruning, and disk budgets. See [Manage the Audit Log Filter files](manage-audit-log-filter.md).
-Performance impact is a critical consideration when implementing detailed logging. More granular filters typically require more system resources to process and store the audit data. Monitor your system's performance metrics while testing different filter configurations. Look for significant changes in query response times, CPU usage, or I/O operations. If you notice performance degradation, consider adjusting your filters to balance capturing necessary audit data and maintaining acceptable system performance. Remember that starting with less detailed logging is often better and gradually increasing it as needed, rather than implementing overly aggressive logging that impacts system performance.
+4. Watch overhead. Granular auditing costs CPU and I/O. Ramp detail gradually and watch latency plus `audit_log_filter_*` status counters.
## Implement the filter
-Here's how to define and implement an audit log filter in Percona Server for MySQL {{vers}}:
+Typical workflow:
### Create a filter
-To create an audit log filter, use the `audit_log_filter_set_filter()` function. This function takes two parameters: the filter name and the filter definition as a JSON string.
+Call `audit_log_filter_set_filter(filter_name, json_definition)`:
```sql
SELECT audit_log_filter_set_filter('log_all', '{ "filter": { "log": true } }');
@@ -365,7 +667,7 @@ SELECT audit_log_filter_set_filter('log_all', '{ "filter": { "log": true } }');
### Assign filter to users
-To assign a filter to specific users, use the `audit_log_filter_set_user()` function. This function takes three parameters: username, userhost, and filtername.
+Bind accounts (or the default `%` row) with `audit_log_filter_set_user('user@host', 'filter_name')`:
```sql
SELECT audit_log_filter_set_user('%', 'log_all');
@@ -373,7 +675,7 @@ SELECT audit_log_filter_set_user('%', 'log_all');
### Example: Financial tracking filter
-Here's a complete example of creating and assigning a comprehensive financial tracking filter:
+Create and assign a finance-focused filter. The filter logs DML on two tables in `financial_db` and every connection event. Account scoping applies at assignment time, because `table_access` events do not carry a user field.
```sql
-- Create the filter
@@ -382,56 +684,54 @@ SELECT audit_log_filter_set_filter('financial_tracking', '{
"class": [
{
"name": "table_access",
- "user": ["admin", "finance_team"],
- "database": ["financial_db"],
- "table": ["accounts", "transactions"],
"event": [
- {"name":"insert"},
- {"name":"update"},
- {"name":"delete"],
- ],
- "status": [0, 1]
+ {
+ "name": ["insert", "update", "delete"],
+ "log": {
+ "and": [
+ { "field": { "name": "table_database.str", "value": "financial_db" } },
+ {
+ "or": [
+ { "field": { "name": "table_name.str", "value": "accounts" } },
+ { "field": { "name": "table_name.str", "value": "transactions" } }
+ ]
+ }
+ ]
+ }
+ }
+ ]
},
{
"name": "connection",
- "user": ["admin", "finance_team"],
"event": [
- {"name":"connect"},
- {"name":"disconnect"}
- ],
- "status": [0, 1]
+ { "name": "connect" },
+ { "name": "disconnect" }
+ ]
}
]
}
}');
```
-!!! note "Filter definition"
- In the filter definition shown in this example, status values are displayed as integers (`[0, 1]`) for readability, but they must be specified as strings in your actual filter definitions (for example, `"status": ["0", "1"]`). The audit log filter does not filter on integer values, only on string values. This applies to all numeric filter criteria, including `connection_id`, `thread_id`, and `status`. If you use integer values, you will see the error: `ERROR: Incorrect rule definition.`
+Field-value typing appears under [`audit_log_filter_set_filter()`](audit-log-filter-variables.md#audit_log_filter_set_filterfilter_name-definition). Examples include JSON numbers for integer fields such as `status`, strings for `*.str` fields, and `"::tcp/ip"`-style symbolic constants for `connection_type`.
+
+Assign the filter to only the accounts that should be audited instead of to `%`:
```sql
--- Assign the filter to all users
-SELECT audit_log_filter_set_user('%', '%', 'financial_tracking');
+-- Assign to specific accounts; other users keep their existing filter (or none).
+SELECT audit_log_filter_set_user('admin@%', 'financial_tracking');
+SELECT audit_log_filter_set_user('finance_team@%', 'financial_tracking');
```
-The filter monitors two main types of activities. First, it watches all changes to your accounts and transactions tables. This monitoring means that the filter logs when someone adds new data, changes existing information, or removes records. You get a complete picture of who's touching your financial data and what they do with it.
-
-The filter tracks both successes and failures. This tracking gives you valuable information about attempted changes that didn't work out, which is helpful for troubleshooting and security monitoring.
-
-Here's what gets logged:
+The filter records:
-* Every insert, update, and delete action on your financial tables
+* `insert`, `update`, and `delete` on `financial_db.accounts` and `financial_db.transactions` for the two assigned accounts.
-* All connection attempts from your admin and finance teams, including when they log in and out
+* `connect` and `disconnect` events for the same accounts.
-* Whether each action has succeeded (status 0) or failed (status 1)
+* Nothing for other users, because those users are not bound to the filter. Nothing outside the declared schema and tables unless you extend the JSON.
-The filter focuses only on activity in your `financial_db` database. This targeted approach makes it easier to find the information you need when you need it.
-
-Tracking all these elements gives you a comprehensive view of who's accessing your financial data, what changes they're making, and whether those changes are successful. This ability is beneficial for security monitoring and compliance requirements.
-
-
-To verify your filter, you can check the audit tables:
+Inspect metadata directly:
```sql
-- Check created filters
@@ -441,4 +741,22 @@ SELECT * FROM mysql.audit_log_filter;
SELECT * FROM mysql.audit_log_user;
```
-You can examine your audit log file (the default location is the data directory) to check if events are being logged.
+Tail the audit file (default under the data directory) to confirm events stream as expected.
+
+## Additional reading
+
+* [Block statements with an audit log filter](block-statements-with-audit-filter.md)
+
+* [Redact audit log fields](redact-audit-log-fields.md)
+
+* [Audit Log Filter definition fields](audit-log-filter-definition-fields.md)
+
+* [Filter the Audit Log Filter logs](filter-audit-log-filter-files.md)
+
+* [Audit log filter functions, options, and variables](audit-log-filter-variables.md)
+
+* [Audit Log Filter overview](audit-log-filter-overview.md)
+
+* [Audit Log Filter quickstart](audit-log-filter-quickstart.md)
+
+* [Audit Log Filter restrictions](audit-log-filter-restrictions.md)
diff --git a/mkdocs-base.yml b/mkdocs-base.yml
index c7d4f6dc5e7..3bbbc082f5f 100644
--- a/mkdocs-base.yml
+++ b/mkdocs-base.yml
@@ -179,24 +179,31 @@ nav:
- Features:
- adaptive-network-buffers.md
- Audit Log Filter:
- - audit-log-filter-overview.md
- - install-audit-log-filter.md
- - Formats:
+ - Overview: audit-log-filter-overview.md
+ - Install: install-audit-log-filter.md
+ - Migrate from audit plugins: migrate-to-audit-log-filter-component.md
+ - Quickstart: audit-log-filter-quickstart.md
+ - Log file formats:
- Overview: audit-log-filter-formats.md
- - XML (New style): audit-log-filter-new.md
- - XML (Old style): audit-log-filter-old.md
- - JSON: audit-log-filter-json.md
- - write-filter-definitions.md
- - audit-log-filter-security.md
- - audit-log-filter-compression-encryption.md
- - reading-audit-log-filter-files.md
- - manage-audit-log-filter.md
- - filter-audit-log-filter-files.md
- - audit-log-filter-restrictions.md
- - audit-log-filter-naming.md
- - disable-audit-log-filter.md
- - audit-log-filter-variables.md
- - uninstall-audit-log-filter.md
+ - JSON and JSONL: audit-log-filter-json.md
+ - XML (new style): audit-log-filter-new.md
+ - Define filters:
+ - Write filter definitions: write-filter-definitions.md
+ - Block statements: block-statements-with-audit-filter.md
+ - Redact log fields: redact-audit-log-fields.md
+ - Definition fields reference: audit-log-filter-definition-fields.md
+ - Operate:
+ - Security: audit-log-filter-security.md
+ - Compression and encryption: audit-log-filter-compression-encryption.md
+ - Read log files: reading-audit-log-filter-files.md
+ - Filter existing log files: filter-audit-log-filter-files.md
+ - Manage log files: manage-audit-log-filter.md
+ - Log file naming: audit-log-filter-naming.md
+ - Reference:
+ - Functions, options, variables: audit-log-filter-variables.md
+ - Restrictions: audit-log-filter-restrictions.md
+ - Disable: disable-audit-log-filter.md
+ - Uninstall: uninstall-audit-log-filter.md
- binlog-space.md
- extended-select-into-outfile.md
- innodb-expanded-fast-index-creation.md