原文连接:5 Techniques to Improve Your Server Logginghtml
如下为译文
服务器
但有一件事这些工具却心有余而力不足,由于它们彻底依赖你实际投入的日志数据,而如何保证数据的质量和数量则须要用户自行完成。所以,在关键时刻,若是你须要基于部分或者遗漏日志作代码调试时,事情可能会变得很是棘手。网络
为了减小这种状况发生,在这里分享五个建议,在你记录日志时最好能铭记于心:多线程
正如 Ringo,线程名称这个属性是 Java 中最被低估的方法之一。其缘由是线程名称大部分是描述性的。然而问题一样出如今这里,相似人们本身,起名时一般会被赋予必定的意义。而在多线程日志中,线程名一样挥着关键做用。一般状况下,大多很多天志框架会记录当前所调用的线程名称。可悲的是,咱们一般会看到 http-nio-8080-exec-3
这种名字,简单地由线程池或容器进行分配。架构
出于某种缘由,咱们曾不止一次地听过这种误解——线程名称是不可变的。与之相反,在日志中,线程名称占据基本主要地位,你应该确保能正确使用。好比将它与具体情境结合起来,例如 Servlet 的名字、任务相关,或者一些动态语境如用户或消息 ID。app
这样的话,代码接口应该是这样:框架
Thread.currentThread().setName(ProcessTask.class.getName() + “: “+ message.getID);
更先进的版本将被加载到当前线程的线程局部变量,配置 log appender,并自动将其添加到日志条目。分布式
当多个线程写入服务器日志,但你须要集中在单一线程上时,这将会很是有用。若是你在一个分布式 /SOA 环境下运行,更能看到它得天独厚的优点。工具
在 SOA 或消息驱动的架构,任务执行极可能跨多台机器。当处理这种环境下的故障时,链接相关机器和它们的状态将是了解具体状况的关键。大多很多天志分析器会将这些日志信息分组,假设你为它们提供了惟一标识,它们即可以做为实际日志消息的一部分。this
从设计的角度出发,这意味着,从进入系统到操做完成,每个入站操做应该有其惟一的 ID 对应。请注意,一个持久的标识符,如用户 ID 可能不是一个好容器。在记录日志文件的过程当中,用户可能有多个操做,这将使得隔离特定流更加困难。UUIDs 多是个不错的选择。它的值能够被加载到实际线程名称或者做为 TLS-thread 的局部储存器。
不少时候,你会看到一段代码在紧密的循环中运行,并执行相应的日志操做。基本假设是,该代码运行的次数是有限的。
极可能运行状况很是良好。可是当代码获得意外输入时,循环可能并不会中断。在这种状况下,你不仅是处理一个无限循环「虽然这样已经很糟糕了」,你正在处理的代码正将无限量的数据写到磁盘或网络。
在单机场景中它可能会形成一台服务器崩溃,而在分布式场景中,被影响的则是整个集群。所以若是可能,不要在紧密循环中记录日志。捕获错误时,这一点尤为如此。
下面这个例子,记录了一个 while 循环中的异常:
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. } } } }
另外一种方法是从循环中移除日志记录,并保存第一/最后一个异常对象并在其它地方记录。
Westeros 有最后一道防护墙,而你有 Thread.uncaughtExceptionHandler
。所以,尽可能使用它们。若是没有安装这些处理程序,在异常抛出时,你只能得到不多有价值的上下文,同时你也没法控制在结束以前你已经将其记录,并肯定记录的位置。
请注意,即便在未捕获的异常处理程序,看起来你没有任何办法访问线程中(已终止)的任何变量,你仍然能够得到实际线程对象的引用。若是你坚持# 1步,你仍然会获得一个有意义的thread.getName()
值可记录。
每当调用一个外部的 API, JVM 异常的概率将大大增长。这包括 Web 服务、 HTTP、 DB、 文件系统、操做系统和任何其余 JNI 调用。认真对待每一个调用,由于它随时会爆炸 「它颇有可能发生在一样的点」。
大多数状况下,外部 API 故障的缘由是意外输入,日志中对其记录是修复代码的关键。
在这一点上,你能够选择不记录错误,只是抛出异常也能够。在这种状况下,只要收集到调用的相关参数,并将其解析为异常错误信息。
只要确保异常被捕获并记录在更高级别的堆栈调用便可。
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); //you can also throw a nested exception here with err instead. }