sql server、mysql、postgresql 均不默认记录存储过程调用者及参数值,需通过extended events(sql server)、手动审计表、audit_log插件配合日志解析(mysql)或pg_stat_statements联合log_line_prefix(postgresql)等主动埋点方式实现,且参数审计必须由应用层或过程内显式处理。

SQL Server、MySQL、PostgreSQL 都不默认记录「谁调用了哪个存储过程 + 传了什么参数」,必须组合机制或主动埋点。原生日志最多记到 CALL proc_name() 或 EXEC dbo.uspLog 这一级,参数值和调用者身份需要额外处理。
SQL Server 中 audit 不记录参数?得靠 rpc_completed 事件
SQL Server 的 CREATE SERVER AUDIT 和 CREATE DATABASE AUDIT SPECIFICATION 能捕获 EXECUTE ON DATABASE::[db] 行为,但只存 object_name、session_id、principal_name,不存参数值。
- 真正能拿到参数的路径是启用
Extended Events捕获rpc_completed事件,它包含statement字段(如EXEC uspGetUser @id=123),但需注意:如果参数是变量(EXEC uspGetUser @p1),实际值仍不可见 -
fn_get_audit_file()解析审计文件时,字段server_principal_name可确认调用者,object_name是过程名,但statement字段为空 —— 这是设计限制,不是配置错误 - 若必须审计参数值,唯一可靠方式是在存储过程开头手动插入审计表:
INSERT INTO audit_log (proc_name, user_name, param_json, exec_time) VALUES (@proc_name, SUSER_SNAME(), JSON_OBJECT('id', @id), GETDATE())
MySQL 8.0 开启 audit_log 插件后仍看不到参数?检查 audit_log_policy
audit_log 插件在 MySQL 8.0.19+ 支持 command_class = 'call_procedure',但它只记录 CALL my_proc() 字符串,不解析括号内内容。
- 确保插件已加载:
INSTALL PLUGIN audit_log SONAME 'audit_log.so',且audit_log_policy设为ALL或LOGINS;设为VALIDATE会漏掉 CALL -
general_log更简单但副作用大:开启后所有语句都进日志,包括连接、SET、SELECT,CALL行虽含参数(如CALL get_user(456)),但无法区分是应用直连还是事件调度器触发 - 参数脱敏风险:日志明文记录参数,若含身份证、手机号等,需提前过滤或加密,不能依赖数据库层自动掩码
PostgreSQL 用 pg_stat_statements + 日志,但查不到具体用户?
pg_stat_statements 的 query 字段是参数化模板(如 CALL log_action($1, $2)),calls 和 total_time 可统计频次与耗时,但没用户信息。
- 要补全调用者,必须配合
log_line_prefix = '%u@%d %h %t ',让日志每行带username@dbname client_ip timestamp,再通过时间戳对齐pg_stat_statements的last_call -
log_statement = 'all'会把所有CALL写进日志,但量太大;折中方案是log_statement = 'mod'+log_min_duration_statement = 0,只捕获含 DML/DDL 的 CALL(比如内部有 INSERT/UPDATE 的过程) - 输出参数无法被日志捕获 —— 因为它是过程执行后的结果,不是输入语句的一部分;只能靠过程内
RAISE LOG或写审计表实现
为什么所有方案都不记录参数?这不是 bug,是设计取舍
参数值可能包含敏感数据、超长字符串或二进制内容,全量记录会快速撑爆日志、拖慢性能、违反 GDPR 类合规要求。数据库引擎默认只审计“行为”而非“内容”。
- 真正需要参数审计的场景(如金融交易、权限变更),必须由应用层在调用前打点,或在存储过程中第一行做
INSERT INTO audit_table,并显式拼接或序列化参数 - 不要依赖
sys.dm_exec_input_buffer(SQL Server)或information_schema.routines(MySQL)来反推参数 —— 它们只存定义,不存运行时值 - 跨库统一审计?别指望 SQL 层自动对齐。不同数据库的
CALL/EXEC语法、权限模型、日志结构差异太大,最终方案一定是应用侧统一封装 + 各 DB 单独适配











