Java应用中如何用log4j将Oracle存储过程名记录到日志文件
刚好之前处理过类似的需求,给你几个实用的方案,帮你把Oracle存储过程的名称稳稳记录到log4j日志里:
方案1:直接在调用处手动记录日志(最直接,适合调用点不多的场景)
如果你的存储过程调用点数量不多,最直接的方式就是在每次调用前后手动添加log4j日志。比如用JDBC直接调用的场景,代码示例如下:
import org.apache.log4j.Logger; import java.sql.*; public class ProcCallExample { private static final Logger logger = Logger.getLogger(ProcCallExample.class); public void callUserInfoProc(int userId) throws SQLException { // 提前定义存储过程名称,方便日志输出 String procName = "PKG_USER.PROC_GET_USER_INFO"; // 记录调用开始 logger.info(String.format("开始调用Oracle存储过程: %s", procName)); try (Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@//host:port/db", "user", "pwd"); CallableStatement cs = conn.prepareCall(String.format("{call %s(?, ?)}", procName))) { // 设置输入参数 cs.setInt(1, userId); // 注册输出参数 cs.registerOutParameter(2, Types.VARCHAR); cs.execute(); // 记录调用成功 logger.info(String.format("Oracle存储过程%s调用成功,返回结果: %s", procName, cs.getString(2))); } catch (SQLException e) { // 记录调用异常,一定要带上堆栈信息 logger.error(String.format("Oracle存储过程%s调用失败", procName), e); throw e; } } }
这种方式简单粗暴,不需要额外依赖,适合小范围的调用场景。
方案2:用AOP切面统一记录(适合大量调用的场景)
如果你的应用里有几十上百个存储过程调用,手动加日志太繁琐,可以用Spring AOP或者AspectJ做一个统一的日志切面,拦截所有存储过程调用的方法,自动记录名称。
假设你有一个统一的存储过程调用工具类ProcCaller,里面有一个通用的调用方法callProc(String procName, Object... params),那可以给这个方法做切面:
import org.apache.log4j.Logger; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component; @Aspect @Component public class ProcLoggingAspect { private final Logger logger = Logger.getLogger(ProcLoggingAspect.class); // 拦截ProcCaller类的callProc方法,第一个参数是存储过程名称 @Around("execution(* com.yourcompany.utils.ProcCaller.callProc(String, ..))") public Object logProcInvocation(ProceedingJoinPoint joinPoint) throws Throwable { // 获取存储过程名称参数 String procName = (String) joinPoint.getArgs()[0]; logger.info(String.format("开始调用Oracle存储过程: %s", procName)); try { // 执行原方法 Object result = joinPoint.proceed(); logger.info(String.format("Oracle存储过程%s调用成功", procName)); return result; } catch (Exception e) { logger.error(String.format("Oracle存储过程%s调用失败", procName), e); throw e; } } }
这样不管你在代码的哪个地方调用ProcCaller.callProc(),都会自动记录日志,一次性解决所有调用点的日志问题。
方案3:MyBatis场景下的日志记录
如果是用MyBatis调用存储过程(比如Mapper接口方法对应存储过程),可以自定义MyBatis拦截器来自动识别并记录存储过程名称:
import org.apache.log4j.Logger; import org.apache.ibatis.executor.statement.StatementHandler; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.plugin.*; import java.sql.Connection; import java.util.Properties; import java.util.regex.Matcher; import java.util.regex.Pattern; @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})}) public class ProcLoggingInterceptor implements Interceptor { private static final Logger logger = Logger.getLogger(ProcLoggingInterceptor.class); // 匹配存储过程调用的正则,可根据你的SQL格式调整 private static final Pattern PROC_PATTERN = Pattern.compile("\\{call\\s+(\\w+\\.\\w+)\\s*\\(", Pattern.CASE_INSENSITIVE); @Override public Object intercept(Invocation invocation) throws Throwable { StatementHandler statementHandler = (StatementHandler) invocation.getTarget(); BoundSql boundSql = statementHandler.getBoundSql(); String sql = boundSql.getSql().trim(); // 判断是否是存储过程调用 if (sql.startsWith("{call")) { Matcher matcher = PROC_PATTERN.matcher(sql); if (matcher.find()) { String procName = matcher.group(1); logger.info(String.format("MyBatis开始调用Oracle存储过程: %s", procName)); } } // 继续执行原逻辑 return invocation.proceed(); } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { // 这里可以配置自定义参数,比如日志级别、正则表达式等 } }
然后在MyBatis的配置文件里注册这个拦截器:
<configuration> <plugins> <plugin interceptor="com.yourcompany.mybatis.interceptor.ProcLoggingInterceptor"/> </plugins> </configuration>
这样所有MyBatis执行的存储过程都会被自动记录到日志里。
几个注意点
- 确保log4j的配置文件正确,日志级别设置为
INFO或更低(比如DEBUG),并且指定了正确的日志输出文件路径 - 如果你的存储过程名称有特殊格式(比如带引号、包含特殊字符),需要调整正则表达式或者字符串提取逻辑
- 异常日志一定要带上堆栈信息(也就是
logger.error(message, e)里的第二个参数e),方便后续排查问题
内容的提问来源于stack exchange,提问作者Swam
相关产品推荐
相关产品推荐

