如何为 log4j 设置单独的日志记录流?

2023-11-27

假设我有一堂这样的课:

public class MyClass
{

    private Logger log = LoggerFactory.getLogger(MyClass.class); //org.slf4j.LoggerFactory
 
    public void foo(Params p)
    {
         log.info("Foo params: " + p);
         long t1 = System.currentTimeMillis();

         Result r = someMethod(p);

         long t2 = System.currentTimeMillis();
         log.info("Foo result: " + r)
         log.info("Foo time taken: " + (t2-t1)/1000); 
    }

}

现在,在打印此信息时,我希望能够打开和关闭不同类型的信息(参数、结果、所用时间)。

使用日志记录级别来区分的问题是,较细的粒度级别也包含较粗的级别。

我怎样才能轻松地进行设置?


我认为 John Ament 的意思是,记录器名称(或类别,有时也被称为)可以自由选择。电话

LoggerFactory.getLogger(MyClass.class)

主要只是为了方便打电话

LoggerFactory.getLogger(MyClass.class.getName())

日志记录框架不要求您根据类的全名命名记录器。这只是上面第一个 getLogger 重载支持的约定。

因此,不要使用与示例中相同名称的三个不同 Logger 实现:

 private AbstractLogger l1= new LoggerOne(this.getClass());
 private AbstractLogger l2= new LoggerTwo(this.getClass());
 private AbstractLogger l3= new LoggerThree(this.getClass());

您可以简单地使用具有 3 个不同名称的标准 Logger 实现:

public class MyClass
{
    private static final String loggerBaseName = MyClass.class.getName();

    private final Logger paramsLogger = LoggerFactory.getLogger(loggerBaseName + ".params");
    private final Logger resultsLogger = LoggerFactory.getLogger(loggerBaseName + ".results");
    private final Logger durationLogger = LoggerFactory.getLogger(loggerBaseName + ".duration");

    public void foo(Params p)
    {
        paramsLogger.info("Foo params: {}", p);
        long t1 = System.currentTimeMillis();

        Result r = someMethod(p);

        long t2 = System.currentTimeMillis();
        resultsLogger.info("Foo result: {}", r)
        durationLogger.info("Foo time taken: {}", (t2-t1)/1000); 
    }
}

由于 log4j 记录器是分层的,因此您可以根据需要一起或单独控制它们。因此,如果您想启用所有这些:

log4j.logger.org.myproject.MyClass=DEBUG, stdout

如果您稍后需要关闭结果:

log4j.logger.org.myproject.MyClass=DEBUG, stdout
log4j.logger.org.myproject.MyClass.results=OFF

如果需要,您可以以同样的方式将输出发送到不同的目的地。

使用标记

以上所有内容都是仅使用任何 SLF4J 实现中可用的基本功能编写的。如果您正在使用 Log4j 2 或者愿意切换到 logback,您可以使用markers实现同样的目标,但是是在全球范围内。因此,您可以拥有多个标记,而不是在类中拥有多个记录器,如下所示:

public class GlobalMarkers
{
    public static final Marker PARAMS = MarkerFactory.getMarker("PARAMS");
    public static final Marker RESULTS = MarkerFactory.getMarker("RESULTS");
    public static final Marker DURATION = MarkerFactory.getMarker("DURATION");
}

public class MyClass
{
    private Logger logger = LoggerFactory.getLogger(MyClass.class);

    public void foo(Params p)
    {
        logger.info(GlobalMarkers.PARAMS, "Foo params: {}", p);
        long t1 = System.currentTimeMillis();

        Result r = someMethod(p);

        long t2 = System.currentTimeMillis();
        logger.info(GlobalMarkers.RESULTS, "Foo result: {}", r)
        logger.info(GlobalMarkers.DURATION, "Foo time taken: {}", (t2-t1)/1000); 
    }
}

这将允许您通过使用全局切换参数、结果和持续时间的记录Log4j 2.0 标记过滤器 or logback标记过滤器.

Log4j 2.0中的配置

Log4j 2.0 在如何使用 MarkerFilter 方面为您提供了很大的灵活性:

  1. 您可以将其应用为上下文范围的过滤器,从而关闭all例如,记录持续时间。
  2. 您可以将其应用于 org.myproject.MyClass 记录器,以关闭该特定类的结果记录(例如)。
  3. 您可以将其应用于特定的附加程序,从而将参数日志记录到结果日志记录或类似文件中的单独文件中。

logback中的配置

在 logback 中,故事更加复杂,具体取决于您希望实现的目标。要全局关闭给定标记的所有日志记录,只需使用 MarkerFilter。这是一个 TurboFilter,因此它适用于整个日志记录上下文。如果您想将不同的标记记录到不同的源,您可以使用筛选Appender并通过扩展 AbstractDiscriminator 编写一个基于标记的鉴别器。由于 logback 不支持直接在记录器上进行过滤,因此如果您需要配置每个类每个标记的输出,例如关闭 MyClass 的结果记录但为其他类保留它,则应该使用特定于类的标记而不是全局标记。

以下是与 SiftingAppender 一起使用的基于标记的鉴别器的示例实现:

public class MarkerBasedDiscriminator extends AbstractDiscriminator<ILoggingEvent> {
    private static final String KEY = "markerName";
    private String defaultValue;

    public String getDefaultValue() {
        return defaultValue;
    }

    public void setDefaultValue(String defaultValue) {
        this.defaultValue = defaultValue;
    }

    public String getKey() {
        return KEY;
    }

    public void setKey() {
        throw new UnsupportedOperationException("Key not settable. Using " + KEY);
    }

    public String getDiscriminatingValue(ILoggingEvent e) {
        Marker eventMarker = e.getMarker();

        if (eventMarker == null)
            return defaultValue;

        return eventMarker.getName();
    }
}

这个实现很大程度上受到了标准的启发基于上下文的鉴别器。您可以像这样使用 MarkerBasedDiscriminator:

<configuration>
  <appender name="SIFT" class="ch.qos.logback.classic.sift.SiftingAppender">
    <discriminator class="org.myproject.MarkerBasedDiscriminator">
      <defaultValue>general</defaultValue>
    </discriminator>
    <sift>
      <appender name="FILE-${markerName}" class="ch.qos.logback.core.FileAppender">
        <file>${markerName}.log</file>
        <append>false</append>
        <encoder>
          <pattern>%d [%thread] %level %logger{35} - %msg%n</pattern>
        </encoder>
      </appender>
    </sift>
  </appender>

  <root level="DEBUG">
    <appender-ref ref="SIFT" />
  </root>
</configuration>
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何为 log4j 设置单独的日志记录流? 的相关文章

随机推荐

  • 在 PowerShell 中关闭所有资源管理器窗口

    我正在编写以下代码来使用 PowerShell 关闭所有资源管理器窗口 New Object comObject Shell Application Windows FullName ne null FullName toLower End
  • Flutter:从 GestureDetector 获取本地位置

    我正在使用 GestureDetector 来获取此事件 onHorizontalDragStart onDragStart onHorizontalDragUpdate onDragUpdate 但如何才能将全球位置转变为本地位置呢 被移
  • 使用 twitter4j 库拥有多个 Twitter 实例。

    我需要能够做出不同的事情Twitter实例与相同的消费者密钥和秘密 但不同的用户级别访问令牌 我已经使用标准 Twitter OAuth 登录协议根据我的应用程序的消费者凭据获取了 10 个用户的访问令牌 现在 我想创建一个 Twitter
  • T4 文本模板 - 是否可以从主机获取编译符号?

    背景 我有一个用 C 编写的开源游戏库 它使用不安全代码 指针算术等对性能进行了高度调整 我最近将该库移植到了 Windows Phone 不幸的是 Windows Phone 根本不支持不安全代码 因此我不得不在源代码中添加如下预处理器指
  • 返回上一屏幕而不创建新实例

    正如图中所解释的 流程是这样的 因此 每当用户单击徽标按钮时 都应该调用 Activity A 作为简单的解决方案 我们可以使用这种方法 Intent intent new Intent activity activityToStart s
  • 如何并行化一个简单的 Python 循环?

    这可能是一个微不足道的问题 但是如何在 python 中并行化以下循环 setup output lists output1 list output2 list output3 list for j in range 0 10 calc i
  • 类型...不可分配给类型“从不”。(2322)

    下面的代码片段报错Type string is not assignable to type never 2322 在行中obj prop value我很难理解为什么 interface fooType s string n number
  • 为什么 vector::push_back 和 emplace_back 调用 value_type::constructor 两次?

    我有这门课 class Foo public Foo Foo const Foo cout lt lt constructed by lvalue reference lt
  • 如何使用 httr 发布多部分/相关内容(适用于 Google Drive API)

    我使用 httr 将简单的文件上传到 Google Drive 问题是每个文档都作为 无标题 上传 我必须修补元数据才能设置标题 PATCH 请求有时会失败 根据API 我应该能够进行分段上传 允许我将标题指定为上传文件的同一 POST 请
  • 递归方法最长路径算法的计算复杂度

    我编写了一个代码段来确定图中的最长路径 以下是代码 但由于中间的递归方法 我不知道如何获得其中的计算复杂度 由于找到最长的路径是一个 NP 完全问题 我认为它是这样的O n or O 2 n 但我怎样才能真正确定它呢 public stat
  • 部分类模板特化是这个设计问题的答案吗?

    假设您有一个类 其工作是连接到远程服务器 我想抽象这个类以提供两个版本 一个通过UDP连接 另一个通过TCP连接 我想构建尽可能精简的运行时代码 而不是使用多态性 我正在考虑模板 这是我的设想 但我不确定这是最好的方法 class udp
  • 当组不互斥时,功能类似于 group_by

    我想在 R 中创建一个函数 类似于dplyr s group by函数 当与summarise可以给出数据集的汇总统计数据 其中组成员身份并不相互排斥 即 观察结果可以属于多个组 考虑这个问题的一种方法可能是考虑标签 观察结果可能属于一个或
  • 在 Git 中维护“服务器版本”(仅更改配置文件)的正确方法是什么?

    我有时使用 Codeigniter 在本地站点上完成开发后 我需要将文件迁移到服务器 中的所有文件 config 需要更改文件夹以匹配服务器设置 对这些更改进行完整提交是不对的 我是否只是让 Git 完全忽略这些文件 或者有没有办法跟踪这些
  • C# 可为空字符串错误

    private string typeOfContract get return string ViewState typeOfContract set ViewState typeOfContract value 稍后在代码中我这样使用它
  • Spring REST - RestTemplate 可以使用多部分/混合吗?

    我想编写一个 REST 服务 它响应一个 zip 文件和一些 json 数据 所有内容都在一个多部分 混合请求中 服务器部分工作正常 我正在使用 Firefox 的 REST 客户端对其进行测试 我的服务器发送这样的多部分 k dXaXvC
  • 实体框架不适用于没有标识列的表

    我有下表 create table tbl id int identity 1 1 val varchar 100 现在 当我使用实体框架将对象映射到该表时 它可以工作 但是当我按如下方式更改表定义时 create table tbl1 i
  • 在 SendGrid C# 中将电子邮件作为日历邀请/约会发送

    我想向 Outlook 以及非 Outlook 客户端 例如 gmail yahoo 发送一封包含日历邀请 约会的电子邮件 我的应用程序托管在 Azure 上 我使用 SendGrid 发送电子邮件 电子邮件部分工作得很好 但我还没有找到任
  • 开发 URL 缩短器

    我正在尝试开发一个 URL 缩短器应用程序来练习 Django 我不明白如何为每个长 URL 创建唯一的字符串以用作短 URL 就像其他流行的 URL 缩短器一样 我怎样才能做到这一点 是否可以使所有短网址的长度相同 我不明白如何为每个长
  • 在 Perl 中获取 UTC 偏移量的最佳方法是什么? [复制]

    这个问题在这里已经有答案了 我需要以跨平台 Windows 和各种风格的 Unix 方式在 Perl 中获取当前时区的 UTC 偏移量 它应该满足以下格式 zzzzzz 表示相对于 UTC 的 hh mm 看来我应该能够通过strftime
  • 如何为 log4j 设置单独的日志记录流?

    假设我有一堂这样的课 public class MyClass private Logger log LoggerFactory getLogger MyClass class org slf4j LoggerFactory public