Logging 日志记录最佳实践-在目标方法开始时记录方法调用或日志?

Logging 日志记录最佳实践-在目标方法开始时记录方法调用或日志?,logging,language-agnostic,coding-style,Logging,Language Agnostic,Coding Style,我们什么时候登录?在函数调用之前(示例A)还是在目标方法的开头(示例B) 注意,这个问题是关于确切的记录器函数调用位置,而不是一般的最佳日志记录实践 解决方案A:在函数调用时记录: function someProcess() { log.info("Reading data"); readDataFromIO(); log.info("Outputing data"); outputDataToScreen(); } // ... other module:

我们什么时候登录?在函数调用之前(示例A)还是在目标方法的开头(示例B)

注意,这个问题是关于确切的记录器函数调用位置,而不是一般的最佳日志记录实践

解决方案A:在函数调用时记录

function someProcess() {
    log.info("Reading data");
    readDataFromIO();
    log.info("Outputing data");
    outputDataToScreen();
}

// ... other module:

function readDataFromIO() {
    ...
}

function outputDataToScreen() {
    ... 
}
function someProcess() {
    readDataFromIO();
    outputDataToScreen();
}

// ... other module:

function readDataFromIO() {
    log.info("Reading data");
    ...
}

function outputDataToScreen() {
    log.info("Outputing data");
    ... 
}
解决方案B:在方法开始时记录:

function someProcess() {
    log.info("Reading data");
    readDataFromIO();
    log.info("Outputing data");
    outputDataToScreen();
}

// ... other module:

function readDataFromIO() {
    ...
}

function outputDataToScreen() {
    ... 
}
function someProcess() {
    readDataFromIO();
    outputDataToScreen();
}

// ... other module:

function readDataFromIO() {
    log.info("Reading data");
    ...
}

function outputDataToScreen() {
    log.info("Outputing data");
    ... 
}
在解决方案A中,当效率问题出现时,您可以自定义消息或放弃日志记录,但如果日志消息看起来相同,您可能会忘记记录,并且会有大量重复的代码。在解决方案B中,不存在忘记日志记录和代码复制的风险,但您不能100%关闭日志记录逻辑,如果方法调用中出现错误(如空指针异常),您就会遇到麻烦哪种是最佳做法?

最佳做法:

  • 使用某种记录器类/模块(取决于语言),允许您在不同级别(调试、信息等)进行日志记录
  • 日志级别调试中非常基本的函数
  • 在日志级别信息中记录更复杂的函数(例如,调用其他基本函数的函数)
  • 使用日志级别警告记录潜在问题
  • 日志错误和带有日志级别错误的异常
单独的日志记录功能允许您根据效率打开/关闭日志记录。但你也有能力记录一切

示例:在Java中,log4j提供了出色的定制选项。您可以定义日志级别,还可以定义哪些类应该打开日志记录。这样,您可以在基本级别(警告)上监视系统,如果发生错误,您可以为需要检查的某些类设置调试级别


这一过程在很大程度上取决于您使用的语言(当然),但我认为这种“log4j”方法是一种非常好的方法。

以下是我根据自己的经验可以给您的提示(因为您已将此主题标记为不可知语言,我将保持通用性):

  • 使用委托函数(或使用日志函数创建中心日志类)记录消息。参数应包含日志消息、日志严重性和日志级别。 此委托函数(或日志类中的函数)应在需要记录日志的任何地方调用。 它允许您轻松替换日志机制,例如从基于文件的日志更改为Windows事件日志。 在此功能中,您还可以处理日志级别,即使其可配置为抑制警告等
  • someProcess()
    中添加一个try-catch以允许捕获运行时错误
  • 当你开始某件事,当你结束某件事时都要记录,并且总是记录可能发生的错误。这些日志调用可以在函数内部本地进行,如示例B所示
  • 如果您使用的语言支持这一点,那么编写一个记录异常的函数/方法:例如,在C#中,您可以为异常编写一个
    公共静态无效日志(此异常示例){/..logging code…}
    ,然后允许您在每个catch块中简单地调用它,比如:
    try{…}catch(Exception ex){ex.Log();}

    它还可用于向其添加可选参数,如
    public static void Log(此Exception ex,string message=“”){…logging code…}
    ,因此您可以向其传递其他信息,如
    ex.Log(“readDataFromIO()-发生读取错误”)
这样可以避免问题中提到的缺点(重复代码,而不是捕获错误)


如果可能的话,看看你正在使用的框架或语言是否已经有这样一个类(通常是这样),并使用它或开发你自己的框架(即你自己的集中式日志类),而不是完全重新发明轮子。

除了前面提到的,我使用的是某种广义的日志概念。它帮助我发现某些情况确实比预期的更频繁或更少出现的情况

我正在使用一个类
LogEvent
(代码是
Java
,但idea可以移植到大多数语言):

如何引发事件并收集事件发生情况?

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("");
    }
}
import java.util.Map;
导入java.util.TreeMap;
公共类LogEventCollection{
私人地图活动;
公共事件集合(){
events=newtreemap();
}
公共无效寄存器(字符串名称){
LogEvent ev;
if(events.containsKey(name)){
ev=events.get(名称);
ev.inc();
}
否则{
ev=新日志事件(名称);
事件。put(名称,ev);
}
}
公共作废报告(字符串标题,int minCount){
Util.info(“”);
如果(!title.isEmpty()){
实用信息(标题);
}
对于(LogEvent ev:events.values()){
如果((minCount<0)| |(ev.count()>=minCount)){
ev.报告();
}
}
Util.info(“”);
}
}
为了获得程序内部事件的概览,方法
LogEventCollection
给出了所有
LogEvents
的列表,以及它们各自的计数。此列表可以按名称或事件频率排序

多线程应用程序需要额外的锁定代码或线程安全集合,以防止并发访问LogEventCollection期间发生冲突


显然,这种方法可以通过添加条件事件来扩展(=调用
register()
受到某些条件的保护)。或者,可以在生产运行期间禁用事件日志记录

当然,最好的做法是将日志记录放在需要的地方。:-)但在你的例子中,最好的做法是