当前位置: 首页 > 知识库问答 >
问题:

更改附加器级别而不是记录器级别

蒋典
2023-03-14

我的团队使用Spring Boot Admin来控制我们的Spring应用程序
Spring Boot Admin中,我们可以选择更改运行时中的记录器级别,

我们为每个任务(线程)有一个单独的记录器,如果我们想只看到一个线程的控制台日志,我们关闭所有其他线程记录器,问题是每个记录器都为stdout和某个文件发送输出,我们只想关闭stdout输出。

log4j2.xml配置示例:

<Loggers>
   <Logger name="task1" level="info">
     <AppenderRef ref="Console"/>
     <AppenderRef ref="File"/>
   </Logger>
   <Logger name="task2" level="info">
     <AppenderRef ref="Console"/>
     <AppenderRef ref="File"/>
   </Logger>
</Loggers>

我们尝试了很多解决方案:

  • 将父记录器与可加性结合使用,并将每个附加器分离到不同的记录器,对此有什么想法吗?

共有1个答案

寿和通
2023-03-14

默认情况下,Log4j2不允许管理System.out和System.err流。

要说明控制台记录器的工作方式,只需consoleappender将其输出打印到system.out或system.err。根据文档,如果默认情况下未指定目标,它将打印到System.out:

https://logging.apache.org/log4j/2.x/manual/appenders.html

目标字符串“system_out”或“system_err”。默认值为“SYSTEM_OUT”。

这里有一个例子:

log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <Properties>
        <Property name="log-pattern">%d{ISO8601} %-5p %m\n</Property>
    </Properties>
    <appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout>
                <pattern>${log-pattern}</pattern>
            </PatternLayout>
        </Console>
    </appenders>
    <Loggers>
        <logger name="testLogger" level="info" additivity="false">
            <AppenderRef ref="Console"/>
        </logger>
    </Loggers>
</configuration>
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class LogApp {
    public static void main(String[] args) {
        Logger log = LogManager.getLogger("testLogger");
        log.info("Logger output test!");
        System.out.println("System out test!");
    }
}
2019-01-08T19:08:57,587 INFO  Logger output test!
System out test!

https://sysgears.com/articles/how-to-redirect-stdout-and-stderr-writing-to-a-log4j-appender/

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Logger;

import java.io.IOException;
import java.io.OutputStream;

/**
  * A change was made on the existing code:
  * - At (LoggingOutputStream#flush) method 'count' could contain 
  *  single space character, this types of logs has been skipped
  */
public class LoggingOutputStream extends OutputStream {
    private static final int DEFAULT_BUFFER_LENGTH = 2048;
    private boolean hasBeenClosed = false;
    private byte[] buf;
    private int count;

    private int curBufLength;

    private Logger log;

    private Level level;

    public LoggingOutputStream(final Logger log,
                               final Level level)
            throws IllegalArgumentException {
        if (log == null || level == null) {
            throw new IllegalArgumentException(
                    "Logger or log level must be not null");
        }
        this.log = log;
        this.level = level;
        curBufLength = DEFAULT_BUFFER_LENGTH;
        buf = new byte[curBufLength];
        count = 0;
    }

    public void write(final int b) throws IOException {
        if (hasBeenClosed) {
            throw new IOException("The stream has been closed.");
        }
        // don't log nulls
        if (b == 0) {
            return;
        }
        // would this be writing past the buffer?
        if (count == curBufLength) {
            // grow the buffer
            final int newBufLength = curBufLength +
                    DEFAULT_BUFFER_LENGTH;
            final byte[] newBuf = new byte[newBufLength];
            System.arraycopy(buf, 0, newBuf, 0, curBufLength);
            buf = newBuf;
            curBufLength = newBufLength;
        }

        buf[count] = (byte) b;
        count++;
    }

    public void flush() {
        if (count <= 1) {
            count = 0;
            return;
        }
        final byte[] bytes = new byte[count];
        System.arraycopy(buf, 0, bytes, 0, count);
        String str = new String(bytes);
        log.log(level, str);
        count = 0;
    }

    public void close() {
        flush();
        hasBeenClosed = true;
    }
}

并为系统输出流创建自定义记录器,然后注册它。

下面是记录器使用的完整代码:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <Properties>
        <Property name="log-pattern">%d{ISO8601} %-5p %m\n</Property>
    </Properties>
    <appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout>
                <pattern>${log-pattern}</pattern>
            </PatternLayout>
        </Console>
    </appenders>
    <Loggers>
        <logger name="testLogger" level="info" additivity="false">
            <AppenderRef ref="Console"/>
        </logger>
        <logger name="systemOut" level="info" additivity="true"/>
    </Loggers>
</configuration>
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;

import java.io.PrintStream;

public class SystemLogging {
    public void enableOutStreamLogging() {
        System.setOut(createPrintStream("systemOut", Level.INFO));
    }

    private PrintStream createPrintStream(String name, Level level) {
        return new PrintStream(new LoggingOutputStream(LogManager.getLogger(name), level), true);
    }
}
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class LogApp {
    public static void main(String[] args) {
        new SystemLogging().enableOutStreamLogging();

        Logger log = LogManager.getLogger("testLogger");
        log.info("Logger output test!");
        System.out.println("System out test!");
    }
}

最终输出

2019-01-08T19:30:43,456 INFO  Logger output test!
19:30:43.457 [main] INFO  systemOut - System out test!

现在,根据您的需要,使用新的记录器配置自定义系统输出。

加;如果您不想重写system.out而只想保存它:在commons-io库中有TeeOutputStream。您可以使用将同时写入两个流的原始System.outLoggingOutputStream的组合来替换原始System.out。这不会更改原始输出,但允许您使用日志附加器保存system.out

 类似资料:
  • 在使用log4j写入多个日志文件时,我遇到了重复日志消息的问题。 目前,我正在尝试在我的文件中记录名为foobar的特定记录器的级数据(及以上),然后在文件中记录所有记录器的所有级日志消息(及以上)。 因此,重复的日志消息被写入文件(每行记录两次),经过一些快速研究,我发现解决这一问题的建议是将添加到属性文件中。 这样做的问题是,尽管它会阻止重复行,但foobar记录器的消息从未写入文件。 我的l

  • 我目前正在尝试降低我在java项目中使用的PDFBox 1.8.6库的日志记录级别,但失败了。基于前面的问题,我在 /src/目录中有以下log4j.properties文件。 我不确定我还遗漏了什么,因为我仍然在控制台中收到类似以下消息的垃圾邮件。 如果有一种方法可以通过编程方式更改日志级别,那么我根本不会附加到属性文件,因为我在其他任何地方都不使用log4j。我不知道它是否使用log4j。属性

  • 问题内容: 我想在我的应用程序(Spring Integration)中有两个日志文件,debug.log和main.log。我想在INFO级别运行main.log,在DEBUG级别运行debug.log。这可以通过追加程序上的过滤器完成。我想根据源将不同级别记录到附加程序。换一种说法 总结一下: 弹簧记录器 主要->错误 调试->调试 com.myapp记录器 主要->信息 调试->调试 因此,

  • 我有点小问题,找不到解决办法。我想为级别信息而不是级别警告设置图案布局。如果我有一个级别信息的日志,一切都正常,但是如果日志是级别,它会被写入控制台两次(作为级别信息和作为级别警告)。只需将特定级别的所有日志写入该级别和以下级别的us日志即可。 我想将级别信息写入控制台,如:和级别如。

  • 问题内容: 因此,我正在尝试学习log4j2,并把头放在记录器及其级别和父母传播上。 当前,我的源层次结构运行是: 而我的CalculatorMain是: 我的log4j2.xml是 问题是将输出到控制台的root记录程序设置为。根据我对级别的理解,这意味着我的root记录器应仅输出错误日志或更低的错误日志。然后是我的 记录器,前者应仅记录错误并降低记录,而后者应记录跟踪并降低记录。因此,我的理解

  • 我正在使用log4j进行日志记录。我有一个场景,我必须为不同的严重性使用单独的日志记录。例如,对于软件包foo,我必须在控制台中打印具有严重性ERROR的消息,而我必须在日志文件中打印具有严重性WARN的消息。我如何配置我的log4j.xml相同。