记录最佳实践 - 在目标方法的开头记录方法调用或记录?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了记录最佳实践 - 在目标方法的开头记录方法调用或记录?相关的知识,希望对你有一定的参考价值。
我们应该记录:在函数调用之前(例子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%关闭日志记录逻辑,如果在方法调用中出现错误,你就会遇到麻烦,比如空指针异常。这是最好的做法?
我在这里给出的做法不是来自任何来源,而是我使用并且在多年使用中发现最有效的。
登录方法
方法是具有特定目的的代码块。保持方法本身的每个方法的日志记录。这样,当您从其他地方重新使用该方法时,您不必在每个地方添加记录器。如果该方法恰好是从许多位置调用的util,请降低该前缀的记录器级别或记录器优先级。
使用MDC /请求ID /线程名称
要跟踪请求流或调用源,请在记录器中设置参数或使用线程名称,以便所有后续日志都具有标记并按照该标记的日志进行操作。通常,最好在收到请求后立即在记录器中设置标记。
避免重复记录
捕获异常并在代码中的某个逻辑阶段进行记录。例如 - 在具有以下堆栈Action / JSP / Web服务等的Web应用程序中 - >模块调用 - >帮助程序模块 - > Util - >库。
在这里,我将记录我的模块调用级别(对应于代码中的someProcess())。任何内部调用都是放在方法内的DEBUG级别调用。或者,您可以使用更高的日志过滤器记录辅助模块等。
记录是一个非常主观的问题,它更多地与决定一种方式并在任何地方坚持它。没有一种尺寸适合所有解决方案。您需要通过随时间逐步调整参数来确定您正在使用的记录器中的详细程度与性能和信噪比。
当然,最佳做法是将记录放在您需要的地方。 :-)但在你的例子中,最好的做法是根本不使用日志记录。
尽管在许多日志记录框架中存在TRACE
级别,但日志记录通常不利于跟踪程序流(这是您尝试做的事情)。日志记录的最佳用途是记录流经系统的数据,尤其是导致问题的数据。
记录数据时,应将数据记录在可以最好地解释的上下文中。那通常是
- 在函数的开头(记录输入),
- 在函数结束时(记录输出或定时),或
- 检测到错误的地方。
要找出发生致命错误的位置,您应该有一个错误处理程序,该错误处理程序会通知致命错误并记录显示错误发生位置的堆栈跟踪。 (最现代的语言默认执行此操作,而较旧的语言现在可以启用此方法。)您不应尝试记录执行路径以尝试本地化问题。
最佳实践:
- 使用某种Logger类/模块(取决于语言),允许您在不同级别(DEBUG,INFO等)进行登录
- 在LogLevel DEBUG中记录非常基本的函数
- 在LogLevel INFO中记录更复杂的函数(例如,调用其他基本函数的函数)
- 使用LogLevel WARN记录潜在问题
- 使用LogLevel ERROR记录错误和异常
单独的日志记录功能允许您根据效率打开/关闭日志记录。但是你也有能力记录所有内容。
示例:在Java中,log4j提供了出色的自定义选项。您可以定义logLevel,并可以定义应该启用日志记录的类。这样您就可以在基本级别(WARN)监视系统,如果发生错误,您可以为需要检查的某些类设置DEBUG级别。
这个程序在很大程度上取决于你使用的语言(当然),但我认为这种“log4j”方法非常好。
除了之前所说的,我使用了一个有点概括的日志记录概念。它帮助我发现某些条件确实比预期更频繁或更少出现的情况。
我正在使用类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()
受某些条件的保护)。或者可以在生产运行期间禁用事件记录。
以下是我可以根据自己的经验给你的提示(因为你已经标记了这个主题与语言无关,我将保持通用):
- 使用委托函数(或创建具有日志功能的中央日志类)来记录消息。参数应包含日志消息,日志严重性和日志级别。应该在需要记录的任何地方调用此委托函数(或日志类中的函数)。它允许您轻松替换日志机制,例如从基于文件的日志更改为Windows事件日志。在此功能中,您还可以处理日志级别,即使其可配置以抑制警告等。
- 在
someProcess()
中添加一个try-catch以允许捕获运行时错误 - 在您开始某事时,以及在您结束某些事情时记录,并始终记录可能发生的错误。这些日志调用可以在函数内部进行,如示例B所示。
- 如果您使用的语言支持,请编写用于记录异常的函数/方法:例如,在C#中,您可以为异常编写extension method
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");
一样向其传递其他信息。
这样您就可以避免在问题中提到的缺点(重复代码,而不是捕获的错误)。
如果可能,查看您正在使用的框架或语言是否已经有这样的类(通常是这种情况)并使用它或开发自己的框架(即您自己的集中式日志记录类)而不是重新发明车轮完全。
最佳做法是将函数调用log()。每次调用函数时都会看到一条跟踪,而不仅仅是从'someProcess'调用它时。
最简单的解决方案是重置方法,以便指定何时“记录”,何时不记录。
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");
...
}
以上是关于记录最佳实践 - 在目标方法的开头记录方法调用或记录?的主要内容,如果未能解决你的问题,请参考以下文章