You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:02:39