本地调用没有问题,然而到生产环境就报错这种问题该怎么排查? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
tiRolin
V2EX    Java

本地调用没有问题,然而到生产环境就报错这种问题该怎么排查?

  •  
  •   tiRolin 319 天前 2722 次点击
    这是一个创建于 319 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近我负责的某个项目新增了功能,然而发版之后就报错,具体日志报下面的问题

    java.lang.NullPointerException at java.util.regex.Matcher.getTextLength(Matcher.java:1283) at java.util.regex.Matcher.reset(Matcher.java:309) at java.util.regex.Matcher.<ini>(Matcher.java:229) at java.util.regex.Pattern.matcher(Pattern.java:1093) at java.util.Formatter.parse(Formatter.java:2547) at java.util.Formatter.format(Formatter.java:2501) at java.util.Formatter.format(Formatter.java:2455) at java.lang.String.format(String.java:2940) at com.orgexample.common.core.exception.BusinessException.<init>(BusinessException.java:36) at com.orgexample.common.core.exception.BusinessException.<init>(BusinessException.java:26) at com.orgexample.tourism.tourism.util.DstZipUtil.compressFileList(DstZipUtil.java:46) at com.orgexample.tourism.tourism.versionfile.service.VersionFileService.generateZipFile(VersionFileService.java:142) at com.orgexample.tourism.tourism.versionfile.service.VersionFileService.generateVersionZipFile(VersionFileService.java:97) at com.orgexample.tourism.tourism.support.ExecutionApiTimerTaskService.generateJsonOfflinePackage(ExecutionApiTimerTaskService.java:331) at com.orgexample.tourism.tourism.support.ExecutionApiTimerTaskService.executeTimeTask(ExecutionApiTimerTaskService.java:212) at com.orgexample.tourism.tourism.support.ExecutionApiTimerTaskService.updateDstApiManageVersion(ExecutionApiTimerTaskService.java:80) at com.orgexample.tourism.tourism.support.ExecutionApiTimerTaskService$$FastClassBySpringCGLIB$$c7558311.invoke(<generated>) at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:746) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) at org.springframework.aop.interceptor.AsyncExecutionInterceptor.lambda$invoke$0(AsyncExecutionInterceptor.java:115) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.lang.Thread.run(Thread.java:750) 

    根据异常,显然定位到发生问题的异常代码在下面的代码中

     /** * 批量压缩文件 v4.0 * * @param fileNames 需要压缩的文件名称列表(包含相对路径) * @param zipOutName 压缩后的文件名称 **/ public static void compressFileList(String zipOutName, List<String> fileNameList) { ThreadFactory factory = new ThreadFactoryBuilder().setNameFormat("compressFileList-pool-").build(); // ExecutorService executor = new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(20), factory); ExecutorService executor = Executors.newFixedThreadPool(40, factory); ParallelScatterZipCreator parallelScatterZipCreator = new ParallelScatterZipCreator(executor); try (OutputStream outputStream = new FileOutputStream(zipOutName)) { ZipArchiveOutputStream zipArchiveOutputStream = new ZipArchiveOutputStream(outputStream); zipArchiveOutputStream.setEncoding("UTF-8"); for (String fileName : fileNameList) { File file = new File(fileName); getFiles(parallelScatterZipCreator, file.getName(), file); } parallelScatterZipCreator.writeTo(zipArchiveOutputStream); zipArchiveOutputStream.close(); } catch (IOException | ExecutionException | InterruptedException e) { throw new BusinessException(e.getMessage()); } } 

    这里麻烦各位先忽略这个代码的槽点吧,我也很想吐槽,但是这是 21 点的代码,不知道是哪位离职的同事写的,改是没法改了,只能将就着看了

    由于生产环境不能 Debug 调试,所以只能确定是这里出了空指针异常,但是不知道具体是哪里出了异常,又是因为什么出的异常

    我新增的功能联系了三张表,我尝试将这三张表都提取出来,在本地环境还原到没部署前的节点,此时在本地运行该接口,并不会报错,但是在生产环境就报错,而且该问题是在我新版本发版之后产生的,确定过好几遍,数据库是没问题的,几乎是保证和生产环境和本地环境一致了,然而在本地就是没问题,在生产就是有问题

    代码问题也排查过了,进入部署的 jar 包中查看发现本次发版部署的代码也存在

    生产环境的权限只有运维有权限操作,我一个开发尽可能就不想打印很多日志然后让生产部署上去再运行一遍查看效果就是,所以打印更详细的日志这个方法还没使用

    想问问各位知道出现这种问题有什么解决思路吗?我想先试试你们的解决方法,如果能解决就不搞这么麻烦了

    19 条回复    2025-01-11 07:31:44 +08:00
    wowo243
        1
    wowo243  
       319 天前
    arthas 调试看下入参返回值之类的
    noobility
        2
    noobility  
       319 天前
    异常发生在 com.orgexample.common.core.exception.BusinessException.<init>(BusinessException.java:26),看着是你的 BusinessException 会对传入的 e.getMessage()调用 String.format ,然而这个字符串为 null ,就抛 NPE 了
    zbatman
        3
    zbatman  
       319 天前
    看样子像是 BusinessException 本身的异常
    F281M6Dh8DXpD1g2
        4
    F281M6Dh8DXpD1g2  
       319 天前
    NPE 这么好查还不会么,看到哪断了就是那有问题
    niurougaodanbaid
        5
    niurougaodanbaid  
       319 天前
    arthas
    pxiphx891
        6
    pxiphx891  
       319 天前
    报的哪一行,就看哪一行不就行了吗
    label
        7
    label  
       319 天前
    增加日志, 把每行结果都打印一下
    XXWHCA
        8
    XXWHCA  
       319 天前
    #2 #3 楼正确答案,e.getMessage()返回值是可为空的,所以进行 format 之前需要判空。
    多种异常一起捕获,message 的内容更加不可控,这种情况先打印日志再抛出异常。
    chairuosen
        9
    chairuosen  
       319 天前
    不知道 java 能不能打印 catch 的那个 e 的 stack ,能打印原始错误栈最好。
    另外,最土的办法,但也最实用,在 try catch 外面定义一个变量记录状态,try 里面每一行代码之后改变一下记录的值比如 stage=012345 ,在 catch 时打印这个 stage ,就知道报错时走到哪一步了,另外把中间变量也记住,就能知道具体报错的 fileName 或者 file 是咋回事。
    wolfie
        10
    wolfie  
       319 天前
    没人吐槽线程池?
    调用一次,创建一个新的线程池,还不关闭。
    prosgtsr
        11
    prosgtsr  
       319 天前
    BusinessException.<init>
    看看 new 方法

    话说:这是 21 点(年)的代码,不知道是哪位离职的同事写的,改是没法改了,只能将就着看了
    我的工作:这一块是 16 年的代码,但是还得改
    oliverCC
        12
    oliverCC  
       319 天前
    throw new BusinessException(e.getMessage());
    从日志上看这行抛出了异常,原因是你的 try catch 中发生了空指针异常,但是你这行代码没有记录日志。
    正常写法应该是
    log.error(String.format("压缩文件出错%s",e.getMessage()),e);
    throw new BusinessException(e.getMessage(),e);

    从你现有代码来看,大概率是 循环中的 getFiles 方法内有问题导致的。

    之所以生产环境会报错,而本地自测不报错 那是因为程序运行时变量不同(变量包括不限于程序报错时的出入参、机器各项指标参数等 代码相同只是这些变量中的一个)

    对于已经出现的这个报错如果不能添加日志来排查,可以看下能否找到这个报错之前的一些出入参和打包的文件,所有变量和本地报错一致自测复现。
    如果日志这条路走不通,可以按照楼上说的 安装 arthas 或者 分布式链路追踪 pinpoint 、SkyWalking 、Zipkin 这些工具也可以作为排查的思路。
    sampeng
        13
    sampeng  
       319 天前   1
    昨天还和同事聊 AI 的影响呢,现在就已经很多很多 3-5 年的程序员连怎么调试和分析日志都做不到,或者很慢了。
    如果 AI 编程变成非常普遍的工具,AI 能解决的,AI 会分析出来告诉你怎么搞。AI 不能解决的呢?这玩意真的是经验值,不是 AI 训练得出来的。个人感觉排 bug 有点俺寻思之力的味道,就是经验在那里,有时候突然灵光一闪,是和这个 bug 半毛钱关系的地方可能有影响,我不觉得 AI 能分析一个远大过他 token 数目的复杂项目。
    xuanbg
        14
    xuanbg  
       319 天前
    可能是服务器上没有足够权限
    tiRolin
        15
    tiRolin  
    OP
       319 天前
    @sampeng 这些事情只能靠经验吧,我还在实习,大学期间调过项目,但是基本都是本地的 Bug 很好解决,这种问题是第一次见,我不知道该如何快速解决,我的水平比较低,只能一步一步走
    lastexile
        16
    lastexile  
       319 天前
    应该是这里的问题 File file = new File(fileName);
    本地调用读的是 windows 的文件路径,
    生产读的是 jar 包里的文件的话,是不能这样读文件的,必须要用 resource 来读,类似这样
    Resource[] resources = applicationContext.getResources("classpath*:dirname/*.zip");
    for (Resource r : resources) {
    InputStream is = r.getInputStream()
    // do other things
    }
    lastexile
        17
    lastexile  
       319 天前
    然后就是把这个 e 的调用栈打印出来,有可能是这个 e.getMessage()为空,导致 throw new BusinessException 也抛出空指针了,这是后续引起的问题了,直接问题应该还是 File 对象那里的问题,导致 getFiles 报错
    pocketz
        18
    pocketz  
       318 天前
    楼上说的没错,你的原始异常实际上被包在了 BusinessException 内。建议是把原始异常打出来看看
    wowo243
        19
    wowo243  
       318 天前
    @wolfie #10 说明他们的服务还没崩,等到他们发现内存泄露的时候他们就会发现了
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3415 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 04:27 PVG 12:27 LAX 20:27 JFK 23:27
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86