通用查询日志(general_log)能记录所有客户端连接与原始SQL语句,是最轻量级审计手段,但无法替代audit_log等专业审计插件;开启后无日志通常因log_output=NONE、general_log_file路径不可写或SELinux/AppArmor拦截。

通用查询日志(general_log)能记录所有客户端连接与原始SQL语句,是快速审计操作的最轻量级手段——但它不是真正的审计插件,不能替代 server_audit 或企业版 audit_log,开错就可能丢日志、写满磁盘或查不到关键语句。
SET GLOBAL general_log = ON 为什么没看到日志?
执行后日志没生成,大概率是三个环节断了:
-
log_output是NONE:这是静默失效的元凶。必须显式设为'FILE'或'TABLE',否则 MySQL 直接丢弃日志 -
general_log_file路径不可写:MySQL 进程用户(通常是mysql)对父目录无写权限,或路径中某级目录不存在(general_log_file不会自动创建多级目录) - SELinux/AppArmor 拦截:Ubuntu/CentOS 上常见,用
sudo aa-disable /usr/sbin/mysqld或setenforce 0临时验证
确认状态的最小命令组合:
SET GLOBAL general_log = ON; SET GLOBAL log_output = 'FILE'; SET GLOBAL general_log_file = '/var/log/mysql/general.log'; SHOW VARIABLES LIKE 'general_log%';
执行完立刻 ls -l /var/log/mysql/ 看文件是否生成、属主是否为 mysql。
FILE 和 TABLE 两种输出方式怎么选?
选哪种取决于你“查什么”和“查多久”:
-
log_output = 'FILE':适合实时tail -f监控,配合grep -E '(GRANT|REVOKE|INSERT|UPDATE)'快速抓权限变更或 DML;但无法做条件聚合,日志轮转得靠外部工具(logrotate) -
log_output = 'TABLE':日志存进mysql.general_log表(CSV 引擎),可用 SQL 过滤,例如:SELECT * FROM mysql.general_log WHERE argument LIKE 'GRANT%' ORDER BY event_time DESC LIMIT 10;
但注意:mysql.general_log不支持索引,数据量大时查询极慢;且TRUNCATE TABLE无效,只能DROP TABLE+CREATE TABLE重建
为什么 GRANT/REVOKE 语句在日志里找不到?
不是日志没开,而是你没抓对时机或条件:
-
general_log只记录被 MySQL 成功接收并进入解析流程的语句。如果语句在客户端被代理改写、被连接池复用旧连接跳过重发、或因语法错误/权限不足被前端拦截,就不会出现在日志里 - 用
TABLE模式时,argument字段可能被截断(受max_allowed_packet影响),导致LIKE 'GRANT%'匹配失败;建议加LOWER(argument)并用%grant%宽松匹配 - 用
FILE模式时,别只盯general_log_file的最后几行——GRANT常出现在连接建立初期,可能被大量SELECT冲到前面去了,得grep -n 'GRANT' /var/log/mysql/general.log | tail -5往前翻
永久配置写进 my.cnf 却不生效?
根本问题往往不是配置写错了,而是 MySQL 根本没读到这个文件:
- 先查真实加载路径:
mysqld --help --verbose | grep "Default options",输出类似Default options are read from the following files in the given order: /etc/my.cnf /etc/mysql/my.cnf ~/.my.cnf - 确认你改的是排在最前面的那个文件(通常是
/etc/my.cnf),且[mysqld]段落下写了这两行:general_log = ONgeneral_log_file = /var/log/mysql/general.log - 重启后仍无效?检查 MySQL 启动日志:
journalctl -u mysql -n 50 --no-pager,看有没有Can't open log file或unknown variable报错
真正容易被忽略的是:general_log 记录的是“客户端发来的原始字节流”,它不解析权限、不校验语法、不区分事务边界——这意味着一条 BEGIN; INSERT; UPDATE; COMMIT; 会被记成四行独立事件,而中间任意一步失败,后续语句也不会进日志。想靠它还原完整业务行为,得自己拼时间戳+连接ID+线程ID,不是开个开关就能直接用的。


















