当我们记录时:在函数调用之前(例子A)或在目标方法开始之前(例子B)?
请注意,此问题与准确的记录器函数调用位置有关,而不是一般的最佳日志记录实践。
解决方案A:登录功能调用:
function someProcess() {
log.info("Reading data");
readDataFromIO();
log.info("Outputing data");
outputDataToScreen();
}
// ... other module:
function readDataFromIO() {
...
}
function outputDataToScreen() {
...
}
解决方案B:登录方法
function someProcess() {
readDataFromIO();
outputDataToScreen();
}
// ... other module:
function readDataFromIO() {
log.info("Reading data");
...
}
function outputDataToScreen() {
log.info("Outputing data");
...
}
在解决方案A中,您可以在效率问题上升时自定义消息或记录日志,但是如果日志消息看起来相同,您可以忘记记录并且有大量重复的代码。在解决方案B中,没有忘记日志记录和没有代码重复的风险,但是你不能100%关闭日志记录逻辑,如果在方法调用中出现错误,你就会遇到麻烦,比如空指针异常。 哪种是最佳做法?
答案 0 :(得分:8)
当然,最佳做法是将记录放在您需要的地方。 :-)但在你的例子中,最好的做法是根本不使用日志记录。
尽管在许多日志框架中存在TRACE
级别,但日志记录通常不适合跟踪程序流(这是您尝试执行的操作)。记录日志的最佳用途是记录流经系统的数据,尤其是导致问题的数据。
记录数据时,应将数据记录在可以最好地解释的上下文中。那通常是
要找出发生致命错误的位置,您应该有一个错误处理程序,该错误处理程序会通知致命错误并记录显示错误发生位置的堆栈跟踪。 (最现代的语言默认执行此操作,而较旧的语言现在可以启用此方法。)您不应尝试记录执行路径以尝试本地化问题。
答案 1 :(得分:5)
我在这里提供的做法不是来自任何来源,而是我使用并且在多年使用中发现最有效的方法。
登录方法
方法是具有特定目的的代码块。保持方法本身的每个方法的日志记录。这样,当您从其他地方重新使用该方法时,您不必在每个地方添加记录器。如果该方法碰巧是从许多地方调用的util,请减少该前缀的记录器级别或记录器优先级。
使用MDC /请求ID /主题名称
要跟踪请求流或调用源,请在记录器中设置参数或使用线程名称,以便所有后续日志都具有标记并按照该标记的日志进行操作。通常,最好在收到请求后立即在记录器中设置标记。
避免重复记录
捕获异常并在代码中的某个逻辑阶段进行记录。 例如 - 在具有以下堆栈的Web应用程序中 行动/ JSP / Web服务等 - >模块调用 - >辅助模块 - > Util - >库。
在这里,我将记录我的模块调用级别(对应于代码中的 someProcess())。任何内部调用都是放在方法内的DEBUG级别调用。或者,您可以使用更高的日志过滤器记录辅助模块等。
记录是一个非常主观的问题,它更多地与决定一种方式并在任何地方坚持它。没有一种尺寸适合所有解决方案。您需要通过逐步调整参数来确定您正在使用的记录器中的详细程度与性能和信噪比。
答案 2 :(得分:1)
最佳实践:
单独的日志记录功能允许您根据效率打开/关闭日志记录。但是你也有能力记录所有内容。
示例:在Java中,log4j提供了出色的自定义选项。您可以定义logLevel,并可以定义应该启用日志记录的类。这样您就可以在基本级别(WARN)监视系统,如果发生错误,您可以为需要检查的某些类设置DEBUG级别。
此程序在很大程度上取决于您使用的语言(当然),但我认为这种“log4j”方法非常好。
答案 3 :(得分:1)
以下是我根据自己的经验给你的提示(因为你已经标记了这个与语言无关的主题,我将保持通用):
someProcess()
中添加try-catch以允许捕获运行时错误public static void Log(this Exception ex) { // ... logging code ... }
用于例外,然后允许您在每个catch块中简单地调用它,例如:try { ... } catch (Exception ex) { ex.Log(); }
public static void Log(this Exception ex, string message="") { ... logging code ... }
添加可选参数也很有用,因此您可以将其他信息传递给它,例如ex.Log("readDataFromIO() - read error occurred");
。通过这种方式,您可以避免在问题中提到的缺点(重复代码,而不是捕获的错误)。
如果可能,查看您使用的框架或语言是否已经有这样的类(通常是这种情况)并使用它或开发自己的框架(即您自己的集中式日志记录类)而不是完全重新发明轮子。
答案 4 :(得分:1)
除了之前所说的,我使用的是一种有点概括的日志记录概念。它帮助我发现某些条件确实比预期更频繁或更少出现的情况。
我正在使用课程LogEvent
(代码为Java
,但想法可以移植到大多数语言中):
public class LogEvent {
private int count;
private String name;
public LogEvent(String name) {
this.name = name;
count = 1;
}
public int count() {
return this.count;
}
public void inc() {
count++;
}
public String name() {
return this.name;
}
public void report() {
if (count >= 1) {
Util.info(name + " (x " + count + ")");
}
else {
Util.info(name);
}
}
}
如何筹集事件并收集事件?
LogEvents
可以使用LogEventCollection
import java.util.Map;
import java.util.TreeMap;
public class LogEventCollection {
private Map<String, LogEvent> events;
public EventCollection() {
events = new TreeMap<String, Event>();
}
public void register(String name) {
LogEvent ev;
if (events.containsKey(name)) {
ev = events.get(name);
ev.inc();
}
else {
ev = new LogEvent(name);
events.put(name, ev);
}
}
public void report(String title, int minCount) {
Util.info("");
if (!title.isEmpty()) {
Util.info(title);
}
for (LogEvent ev : events.values()) {
if ((minCount < 0) || (ev.count() >= minCount)) {
ev.report();
}
}
Util.info("");
}
}
要概述我的计划的内部事件,LogEventCollection
方法report()
会显示所有LogEvents
及其各自计数的列表。此列表可以按名称或事件频率排序。
多线程应用程序需要额外的锁定代码或线程安全集合,以防止在并发访问LogEventCollection期间发生冲突。
显然,这种方法可以通过添加条件事件来扩展(=对register()
的调用受某些条件的保护)。或者可以在生产运行期间禁用事件记录。
答案 5 :(得分:0)
最佳做法是将函数调用到log()中。每次调用函数时都会看到一条跟踪,而不仅仅是从'someProcess'调用它时。
答案 6 :(得分:-1)
最简单的解决方案是重置方法,以便指定何时“记录”,何时不记录。
function someProcess() {
readDataFromIO(true); //This will make a "log"
outputDataToScreen(); //This will make no "log"
}
// ... other module:
function readDataFromIO() {
readDataFromIO(false);
}
function readDataFromIO(bool makeLog) {
if(makeLog)
log.info("Reading data");
...
}
function outputDataToScreen() {
outputDataToScreen(false);
}
function outputDataToScreen(bool makeLog) {
if(makeLog)
log.info("Outputing data");
...
}