Confusion about Audit_log_filter rules and excludes

Hi All,

I’m having trouble understanding the logic of the audit_log_filter. Even trying the simplest two filter system of log_all and log_none where a specific user or set of users is set to log_none the system will still log their actions due to having log_all as a the default rule with ‘%’

This was very easily achieved in the old audit_log plugin where you could simply comma delimit write the user@hosts as they are listed in the mysql.users and it would exclude them from the audit_log.

For example:

SET @filter = ‘{ “filter”: { “log”: true }}’;
SELECT audit_log_filter_set_filter(‘log_all’, @filter);
SELECT audit_log_filter_set_user(‘%’, ‘log_all’);
SET @filter = ‘{ “filter”: { “log”: false }}’;
SELECT audit_log_filter_set_filter(‘log_none’, @filter);
SELECT audit_log_filter_set_user(‘appuser@10.0.0.%’, ‘log_none’);
mysql> select * from mysql.audit_log_user;
±----------±-----------±-----------+
| username  | userhost   | filtername |
±----------±-----------±-----------+
| %         | %          | log_all    |
| appuser | 10.0.0.% | log_none  |
±----------±-----------±-----------+
2 rows in set (0.00 sec)

appuser connecting from 10.0.0.1 still gets logged.

How is this best achieved in audit_log_filter?

Also, side question: Does Percona 8.0.43’s audit_log_filter have all the same features and configuration available to it as Percona 8.4.x? The doco for the former is very sparse in comparison.

The rules would break down like:

  • Most Users: All actions by any user connecting from any host will be logged to the audit log.
  • Specific Exemption: No actions by the user appuser will be logged, as long as they are connecting from an IP address within the 10.0.0.0/24 subnet.

Your understanding of the rules is correct. You dont happen to have appuser coming from somewhere out side of 10.0.0.%?

I would like to test this in my lab to see if I can duplicate the behavior can be duplicated. I will report back shortly.

I was able to reproduce the exact behavior in my lab.
You can see the appuser was logged when I had it drop a table:

    "timestamp": "2025-09-16 08:06:19",
    "id": 231,
    "class": "general",
    "event": "status",
    "connection_id": 37,
    "account": { "user": "appuser[appuser] @ pi8gb.lan [192.168.1.62]", "host": "pi8gb.lan" },
    "login": { "user": "appuser[appuser] @ pi8gb.lan [192.168.1.62]", "ip": "192.168.1.62", "proxy": "" },
    "general_data": { "status": 0 }
  },
  {
    "timestamp": "2025-09-16 08:06:19",
    "id": 232,
    "class": "command",
    "event": "command_end",
    "connection_id": 37,
    "command_data": {
      "name": "command_end",
      "status": 0,
      "command": "Field List"}
  },
  {
    "timestamp": "2025-09-16 08:06:37",
    "id": 233,
    "class": "command",
    "event": "command_start",
    "connection_id": 37,
    "command_data": {
      "name": "command_start",
      "status": 0,
      "command": "Query"}
  },
  {
    "timestamp": "2025-09-16 08:06:37",
    "id": 234,
    "class": "parse",
    "event": "query_rewritten",
    "connection_id": 37,
    "parse_data": {
      "flags": 0,
      "query": "drop table t1",
      "rewritten_query": ""}
  },

You may have found a bug. You can open a bug report here: https://jira.percona.com

Hi Wayne,

Thanks for confirming my issue. I don’t seem able to resolve the address https://jira.percona.com so can’t raise a bug report as you suggest. Is that URL correct?

Also, potential bug aside, do you know if Percona 8.0.43 implementation of audit_log_filter is the same as the component version that is a part of Percona 8.4.x?

I’m so sorry about the link. This one should work for you: Jira

If you still have issues let me know and I will put the bug report in.

The Audit Log Filter component in MySQL 8.4 is essentially the same feature introduced in MySQL 8.0, with only minor changes related to deprecations and the transition away from the old audit log plugin.

Here’s the breakdown:

MySQL 8.0

  • Introduced the audit log filter component to replace the older audit log plugin (which was marked as deprecated).
  • Filters are defined using audit_log_filter_set_filter(), audit_log_filter_set_user(), etc.
  • JSON-based filtering rules allow per-user and global filtering.
  • Installed via INSTALL COMPONENT 'file://component_audit_log_filter';.

MySQL 8.4

  • Continues to use the audit log filter component (plugin-based auditing is fully deprecated/removed in MySQL 8.4).
  • The syntax and behavior of filter rules (audit_log_filter_set_user(), audit_log_filter_remove_filter(), etc.) remain the same.
  • Still JSON-based, same capabilities: filtering by event class, command class, SQL operation, table, etc.
  • Default logging location and formats are unchanged.
  • Biggest change: You can no longer rely on the old audit log plugin—the component is the only supported method.

Key Point

If your audit log filter rules work in MySQL 8.0 using the component, they will work in MySQL 8.4 without changes.
The only difference is that in 8.4, the plugin path is gone, so only the component method is supported.

Thanks for the clarification re: functionality between 8.0.43 and 8.4.x. Good to know that should I get audit logging in audit_log_filter to a satisfactory state it’ll be easier to eventually migrate to 8.4.x.

Raised bug here: Jira

Unsure if filled out correctly as there were many fields I didn’t have an answer too that seemed pertinent to internal percona staff.

The bug report looks great. Thank you!

@Daniel_Wake

Please provide the output of the following query when connected as appuser:

select user(), current_user();

This will help confirm the authenticated account (USER()) versus the effective security context (CURRENT_USER()), which is critical for understanding how privileges and audit log filters are being applied.

mysql> select user(), current_user();
+----------------------+----------------------+
| user()               | current_user()       |
+----------------------+----------------------+
| appuser@10.0.0.1     | appuser@10.0.0.%     |
+----------------------+----------------------+
1 row in set (0.00 sec)

and

mysql> select User, Host from mysql.user where User = 'appuser';
+-----------+------------+
| User      | Host       |
+-----------+------------+
| appuser   | 10.0.0.%   |
+-----------+------------+
1 row in set (0.00 sec)

Could you try this rule:

select audit_log_filter_set_filter('log_connection', '{ "filter": { "class": { "name": "connection" } } } )';

Any more context? From my understanding of the doco that would create a log all connections filter should I apply it to a user?

I should test such a filter with ‘%’ and do the normal log false with my appuser?

Yes please test with you appuser@10.0.0.%

Sorry I should have been a bit more clear.

It logs the connections as expected? According to the doco in 8.0 and 8.4 if “log” is omitted then it defaults to true right?

Going further along that line of working:

mysql> select audit_log_filter_set_user('%', 'log_connection');
+--------------------------------------------------+
| audit_log_filter_set_user('%', 'log_connection') |
+--------------------------------------------------+
| OK                                               |
+--------------------------------------------------+
1 row in set (0.00 sec)

mysql> select audit_log_filter_set_filter('log_connection_false', '{ "filter": { "class": { "name": "connection", "log": false } } } ');
+---------------------------------------------------------------------------------------------------------------------------+
| audit_log_filter_set_filter('log_connection_false', '{ "filter": { "class": { "name": "connection", "log": false } } } ') |
+---------------------------------------------------------------------------------------------------------------------------+
| OK                                                                                                                        |
+---------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> select audit_log_filter_set_user('svintuser@10.39.45.%', 'log_connection_false');
+---------------------------------------------------------------------------+
| audit_log_filter_set_user('svintuser@10.39.45.%', 'log_connection_false') |
+---------------------------------------------------------------------------+
| OK                                                                        |
+---------------------------------------------------------------------------+
1 row in set (0.00 sec)

  {
    "timestamp": "2025-09-25 15:12:33",
    "id": 65,
    "class": "connection",
    "event": "connect",
    "connection_id": 56,
    "account": { "user": "root", "host": "localhost" },
    "login": { "user": "root", "os": "", "ip": "", "proxy": "" },
    "connection_data": {
      "connection_type": "socket",
      "status": 0,
      "db": ""
    },
    "connection_attributes": {
      "_pid": "3360330",
      "_platform": "x86_64",
      "_os": "Linux",
      "_client_name": "libmysql",
      "os_sudouser": "omitted_user",
      "os_user": "root",
      "_client_version": "8.0.43-34"
    }
  },
  {
    "timestamp": "2025-09-25 15:12:41",
    "id": 66,
    "class": "connection",
    "event": "disconnect",
    "connection_id": 56,
    "account": { "user": "root", "host": "localhost" },
    "login": { "user": "root", "os": "", "ip": "", "proxy": "" },
    "connection_data": {
      "connection_type": "socket",
      "status": 0,
      "db": ""
    },
    "connection_attributes": {
      "_pid": "3360330",
      "_platform": "x86_64",
      "_os": "Linux",
      "_client_name": "libmysql",
      "os_sudouser": "omitted_user",
      "os_user": "root",
      "_client_version": "8.0.43-34"
    }
  }

Looks like it works for connection class if I make two filters with one log: false.

Works for table_access as well. Matches the Jira pre_authenticate and general log events are still generated even when user filter is set to {“filter”: {“log”: false}} issue exactly where as soon as I add class General I get logs.

Unfortunately the general log might be the one I most need as it seems the closest to how the old audit_log_filter used to work?:

{“audit_record”:{“name”:“Connect”,“record”:“3667_2025-09-24T04:16:08”,“timestamp”:“2025-09-25T05:36:47Z”,“connection_id”:“46592”,“status”:0,“user”:“root”,“priv_user”:“root”,“os_login”:“”,“proxy_user”:“”,“host”:“localhost”,“ip”:“”,“db”:“”}}
{“audit_record”:{“name”:“Query”,“record”:“3668_2025-09-24T04:16:08”,“timestamp”:“2025-09-25T05:36:47Z”,“command_class”:“select”,“connection_id”:“46592”,“status”:0,“sqltext”:“select @@version_comment limit 1”,“user”:“root[root] @ localhost ”,“host”:“localhost”,“os_user”:“”,“ip”:“”,“db”:“”}}
{“audit_record”:{“name”:“Query”,“record”:“3669_2025-09-24T04:16:08”,“timestamp”:“2025-09-25T05:36:59Z”,“command_class”:“select”,“connection_id”:“46592”,“status”:0,“sqltext”:“select * from app_schema.app_table.skin”,“user”:“root[root] @ localhost ”,“host”:“localhost”,“os_user”:“”,“ip”:“”,“db”:“”}}
{“audit_record”:{“name”:“Quit”,“record”:“3670_2025-09-24T04:16:08”,“timestamp”:“2025-09-25T05:37:01Z”,“connection_id”:“46592”,“status”:0,“user”:“root”,“priv_user”:“root”,“os_login”:“”,“proxy_user”:“”,“host”:“localhost”,“ip”:“”,“db”:“”}}

Is there any combination of Class and deeper key/vals that can replicate this simple connection of user to query?

This rule will get table_access, connection and general. Is this what your looking for?

select audit_log_filter_set_filter('log_table_access', '{ "filter": { "class": { "name": [ "table_access", "connection", "general" ] } } } ');

@Daniel_Wake

Please checkout my latest blog: Audit Log Filters Part II | Percona Community