`
deepinmind
  • 浏览: 444011 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
1dc14e59-7bdf-33ab-841a-02d087aed982
Java函数式编程
浏览量:40739
社区版块
存档分类
最新评论

日志打印的5点建议

阅读更多
最近我们介绍了几款日志分析的工具,比如Scribe和LogStash这类的开源项目,以及Splunk这样的企业级工具,还有像SumoLogic和PaperTrail这样的托管服务。你可以使用这些工具从海量的日志数据提取到一些有价值的信息。

不过还有一件事它们是帮不了你的。它们都依赖于你实际输出到日志文件里面的数据。日志数据保质保量的重任就落到你肩上了。因此万一情况不妙,你又得在日志文件不全的情况下自己去调试代码,那估计你只好赶紧把原先订好的晚餐取消掉了。

为了减少出现类似的情况,我这里想分享5点日志打印的心得,希望能对你有所帮助:

1. 线程名

就像Ringo一样,线程名应该是Java里最被低估的功能之一了。因为其实它的表述性很强。那又怎样?就像我们的名字一样,我们会给它赋予一个含义。

线程名最有用的时候应该就是多线程的情况下了。许多日志框架都会记录当前方法调用所在线程的名字。不幸的是,一般看起来都是这样的:“http-nio-8080-exec-3″,这是线程池或者容器自动分配的线程名。

我经常听到有谣传称线程名是不可变的。当然不是。线程名就是你日志中最优质的不动产,你得确保自己能正确的使用它们。通常给它赋值会带上上下文的详细信息,比如说Servlet或者任务的名字之类的,以及一些动态的上下文信息比如用户ID。

这么做的话,你的代码看起来应该是这样的:

Thread.currentThread().setName(ProcessTask.class.getName() + “: “+ message.getID);


更高级的做法是引入一个ThreadLocal的变量,然后配置一个appender,自动把里面的信息输出到日志中。

当多个线程同时在往文件中写入日志而你需要关注其中某个线程的时候,这个功能尤其有用。如果你在一个分布式或者SOA环境中运行的话,这么做还会有一个额外的好处,下面我们很快就会看到。

2. 分布式标识符

在SOA或者消息驱动的架构中,某个任务的执行可能会涉及到多台机器。这种架构下如果出了错要进行处理的话,要想知道到底发生了什么,这里所牵涉到的相关机器以及它们的状态就显得至关重要。很多日志分析器只是帮你把这些日志收集起来,它们假设你已经有一个唯一的标志符,可以用它来进行过滤。

从设计的角度来看,这意味着系统中每一个入站操作都需要有一个唯一的ID,处理过程中会一直携带着这个ID直到处理结束。这里如果使用持久性标识比如说用户ID之类的可能并不适合,因为在一个日志文件中一个用户可能会有多个请求在同时进行处理,这就很难提取出具体的某个处理流。UUID是个不错的选择,你可以把它存储到线程名或者TLS——ThreadLocal Storage里面。

3. 不要使用循环

你经常会看到有在循环体中进行日志打印,这么做的前提是循环的次数是有限的。

如果不出什么问题的话当然还好。不过如果代码碰到一些异常的输入导致循环无法退出的话,这就不妙了。这可不止是循环无法结束的问题了,你的程序还一直在往磁盘或者网络中写入数据。

如果只是写到自己的设备中,结果可能就只是挂了一台服务器,但如果是一个分布式的环境,就可能就是一整个集群都瘫了。所以最好还是不要在循环里面打印日志,尤其是当涉及到异常处理的时候。

我们来看一个例子,这里是在循环中来打印异常的信息:

void read() {
    while (hasNext()) {
        try {
            readData();
        } catch {Exception e) {
            // this isn’t recommend
            logger.error(“error reading data“, e);
        }
    }
}


如果readData()抛出异常并且hasNext()返回true,这段代码就会不停在打印日志。一个解决方法就是不要每次都打印出来:

void read() {
    int exceptionsThrown = 0;
    while (hasNext()) {
        try {
            readData();
        } catch {Exception e) {
            if (exceptionsThrown < THRESHOLD) {
                logger.error(“error reading data", e);
                exceptionsThrown++;
            } else {
                // Now the error won’t choke the system.
            }
        }
    }
}


还有一个方法就是把日志操作从循环中去掉,在另外的地方进行打印,只记录第一个或者最后一个异常就好了。

4. 未捕获的异常

维斯特洛有一道最后的防御墙,而你有Thread.uncaughtExceptionHandler。请确认你已经用上它们了。如果没有的话,你的异常可能这么没了,而你只能拿到很少的一些上下文信息,同时这些异常在哪打印,是否打印,你也不好控制。

如果你的代码出现异常却没有记录下来,或者记录下来了却没有相关的状态信息,那真是非常失败。

尽管在uncaughtExceptionHandler里面看似已经访问不了线程里面的任何变量了(它已经挂了),但你至少还有一个当前线程的引用。如果结合刚才提到的第一条建议的话,至少日志中还能打印出一个有意义的thread.getName()的值。

5. 捕获外部调用的异常

只要你调用到了JVM以外的接口,那么发生异常的概率就大大提升了。这包括WEB服务,HTTP,数据库,文件系统,操作系统或者其它的一些JNI调用。你得非常小心地处理每一个调用。

大多数情况下,外部调用之所以会失败是因为传入了错误的参数。为了修复这些问题,把这些请求参数记录到日志中是非常有必要的。

你可能不想记录错误信息,而是直接去抛出异常,这样做也没有问题。不过这么做的话,你要尽可能把相关的参数都收集起来,放到异常信息里面去。

你得确保在上一层调用中捕获了异常并且记录到了日志里。



try {
    return s3client.generatePresignedUrl(request);
} catch (Exception e) {
    String err = String.format(“Error generating request: %s bucket: %s key: %s. method: %s", request, bucket, path, method);
    log.error(err, e); //这里你也可以抛出一个异常,记得把ERR信息带上。
}




原创文章转载请注明出处:http://it.deepinmind.com

英文原文链接

3
0
分享到:
评论

相关推荐

    java打印日志的10个建议.doc

    java打印日志的10个建议

    很好用的打印日志模块的源码

    好用的打印日志的源码,因为该源码商未用在具体工程上,但是经过了严格的测试。所以有什么问题及好的建议请联系 QQ915566420,感激不尽。

    logger日志

    日志记录器(Logger)是日志处理的核心组件。log4j具有5种正常级别(Level)。...比如在这里定义了INFO级别,则应用程序中所有DEBUG级别的日志信息将不被打印出来,也是说大于等于的级别的日志才输出。

    日志输出DLL LogDll

    一个日志输出DLL,经过多线程测试,内有源码与测试程序源码。 因为项目需要所以重写了一个DELPHI版本的DLL,支持日志文件大小控制,是否需要压缩备份。 因为里面含有压缩功能,故需要...若有任何问题或修改建议可联系

    WPF学习(包含调用服务端API、日志输出、出场动效、数据库操作、Excel导入,调用打印机打印等)

    使用WPF编写的装箱系统,要点: 1、UI框架使用HandyControl 2、与服务端交互使用RestSharp和Newtonsoft.Json 3、日志使用NLog ...注:导入到VS后建议把与后端交互部分的代码先注释掉以便更方便查看效果

    Docker日志收集最佳实践

    本文主要从传统日志处理开始谈起,接下来着重分析Docker日志处理,包括stdout和文件日志,其中还有fluentd-pilot,接着分享了日志存储方案Elasticsearch、graylog2和SLS,最后对正确写日志给出了建议。以下是精彩...

    用微信小程序云开发做一个错误日志

    云开发业务并不稳定,且有限制,所以不建议直接用做整个小程序的后台开发。 做错误日志并不会影响小程序的流程 出bug后,难定位问题,尤其是线上错误 如果叫后端小伙伴给接口记录错误,总是不方便,还是自己动手...

    NodeLog统计方案yog-log.zip

    日志是否在二级目录打印,目录名为 APP_NAME log_path 插件安装地址/log 日志存放目录,注意需要设置 data_path 插件安装地址/data 格式数据存放的目录,可不用设置 is_omp 0 是否开启omp日志...

    如何写好代码的一些建议(Java版)

    文章详细解释了命名的含义和规则、日志打印的不同级别和要求、线程池和线程安全的相关概念、数组和链表的特点和使用场景、异常类型和使用误区、事务的传播方式和应用,以及设计原则中的单一职责和开闭原则等。

    java实习报告(7).doc

    java实习报告 java实习报告周记精选(一) XML:概念:可扩展的标记语言、优点:结构化,平台、语言无关,标准的 XML读写...日志信息的输出目的地指定了日志将打印到控制台还是文件 中;而输出格式则控制了日志信息的显示

    2017春季组成原理课程设计教师工作日志1

    1. 讲解 PPT 布置任务(主要是纪律层面讲解) 2. 部分没有分组的重修生留级生分配任务并分组 3. 打印台签,建议以后设计一个表格放在一起写文档中让学生自

    spring-boot mybaits shiro redis整合

    logback打印日志,业务日志和调试日志分开打印。同时基于时间和文件大小分割日志文件。 9、项目构建 =========== mybatis generator生成mybatis映射文件。 请先安装eclipse mybatis-generator插件。 10、...

    spring-boot mybaits spring security redis整合

    logback打印日志,业务日志和调试日志分开打印。同时基于时间和文件大小分割日志文件。 9、项目构建 =========== mybatis generator生成mybatis映射文件。 请先安装eclipse mybatis-generator插件。 10、...

    一份超级详细的Java面试题【大厂面试真题+Java学习指南+工作总结】

    日志打印的15个建议 25种代码坏味道总结+优化示例 聊聊日常开发中,如何减少bug呢? 工作四年,分享50个让你代码更好的小建议 写代码有这16个好习惯,可以减少80%非业务的bug Java日常开发的21个坑,你踩过几个? ...

    nb-iot sdk

    在Preprocessor Symbols处添加条件编译选项,条件编译选项如下:  NBIOT_DEBUG – 输出sdk调试日志  LWM2M_WITH_LOGS – 输出lwm2m相关的日志  COAP_WITH_LOGS – 输出coap相关的...因此不建议打开所有的打印。

    只给十位和个位求四位整数(C语言代码)

    资源共享,有什么错误和好的建议请指出 如果没资源分,我可以直接发给你)

    餐饮管理系统 9月5日更新

    4.增加3个打印模板,01-02消费结算打印模板,增加商品打印模板。 5.增加了防破解系统,给每个商业用户建立档案,在明年就可以自行查看自己的资料。 升级说明: 新用户直接下载安装即可 老用户请下载这里的更新文件...

    mina4-android:Android TCP框架(基于MINA 2.0.0-M3)、增加Bytes工厂、无需依赖slf4j(新增Mina4Log打印输出日志), 处理Bytes粘包、半包、断包(ByteArrayDecoder)

    Android TCP框架(基于MINA 2.0.0-M3)、增加Bytes工厂、无需依赖slf4j(新增Mina4Log打印输出日志), 处理Bytes粘包、半包、断包(ByteArrayDecoder),需配置自己的首尾标识符, 如果与首尾标识符相同的数据出现在首尾...

    WifiAutoConnect.dll

    此DLL有线程休眠,建议不要放到主线程中使用。连接参数class1类中bool connect(wifiname,wifipwd),带有日志打印功能。日志存放在程序根目录。

    IDPhotosPro8.6.3.2一款专门处理证件照的软件.exe

    如打印数量,出口,CD/DVD刻录,还包括操作员活动的详细作业日志5、法语ePhoto格式ID照片Pro 8是为法国国家ePhoto计划批准的为数不多的软件之一,使用Wacom平板电脑进行操作,以捕获ePhoto应用程序过程中所需的签名6...

Global site tag (gtag.js) - Google Analytics