【问题标题】:Is it possible in any way to use wildcards in slf4j configuration with Spring? (NOT as a root logger!)是否可以在 Spring 的 slf4j 配置中以任何方式使用通配符? (不是作为根记录器!)
【发布时间】:2020-01-12 10:58:29
【问题描述】:

虽然还有一些其他类似的问题,但他们似乎都误解了关于通配符/根记录器的日志记录配置。我有一个不同的问题。

我的代码结构如下:

服务 1

com.some.package.service1
    |-> subpackage1
    |-> subpackage2

服务 2

com.some.package.service2
    |-> subpackage1
    |-> subpackage2

我想将subpackage1 中所有类的记录器的日志级别设置为DEBUG,同时将subpackage2 中的所有类记录器的日志级别设置为WARN,而其余的则保留例如,INFO

我曾希望我能够简单地配置如下内容:

logging:
    level:
        com.some.package: INFO
        com.some.package.*.subpackage1: DEBUG
        com.some.package.*.subpackage2: WARN

不幸的是,这根本不起作用 - 带有通配符的配置会被忽略。因为我有很多服务,所以我不想用大量的包日志定义来阻塞我的配置文件,每当我添加新服务时也必须更新这些定义。不幸的是,更改代码结构不是我的选择。

  1. 是否可以使用 slf4j 执行此操作,无论是通过简单配置还是以编程方式(理想情况下只使用 slf4j API,但我可以接受特定于实现的解决方案)?
  2. 如果没有,是否有替代解决方案?

【问题讨论】:

  • 可能的替代方案:在 appender 级别进行过滤,或对创建的文件进行外部后处理(在日志轮换或导入 Logstash 等期间)。但是这两个会有一个缺点,即代码保护 logger.isInfoEnabled 将不起作用(Logger 不会知道 appender 稍后会丢弃输出)。
  • 如何在构建或部署时动态生成日志配置文件(从模板和当时可以发现的服务列表)?
  • 感谢您的评论。 1) 不幸的是,这不是我的选择,因为我想根据日志级别使用代码保护来记录更少/更多的细节(例如DEBUGlogs 方法入口/退出点,TRACElogs arguments/returns以及)。 2)可能是一种可能性,唯一的缺点是它需要使用不是配置文件或代码库本身的脚本(尽管我猜像 ANT 之类的东西可能能够做到) - 我会看看再深入一点。
  • 为什么你的构建脚本不是代码库的一部分?没有pom.xml 之类的吗? (老实说,我只会在您添加服务时手动更新该记录器配置。无需过度设计。无论如何,您都需要提交以添加新服务及其自己的配置,不是吗?最重要的是,这也使您可以灵活地为某些服务配置不同的日志记录,以备不时之需——用于故障排除等)
  • 我说错了 - 我确实有一个 build.gradle,因为我正在使用 Gradle。我只是说它不在源代码中,并且可能必须由构建脚本作为预编译步骤调用。

标签: java spring logging log4j slf4j


【解决方案1】:

不幸的是,我使用特定的 Logback Classic Logger 实现来做到这一点。

最终结果是这样的:

@Component
public class PackageScanningLoggingConfiguration {

    // Fields
    // ...

    @PostConstruct
    public void postConstruct() {
        initLoggingLevels();
    }

    private void initLoggingLevels() {
        String basePackage = getBasePackage();

        List<String> candidatePackageNames = Arrays.stream(Package.getPackages())
                .filter(pkg -> pkg.getName().startsWith(basePackage))
                .filter(pkg -> {
                    String packageName = pkg.getName();
                    return packageName.contains(subpackage1Package) 
                        || packageName.contains(subpackage2Package);
                })
                .map(Package::getName)
                .collect(Collectors.toList());

        candidatePackageNames.forEach(this::setConfiguredLoggingLevel);
    }

    private void setConfiguredLoggingLevel(final String packageName) {
        // ch.qos.logback.classic.Logger;
        // org.slf4j.LoggerFactory;
        val logger = (Logger)LoggerFactory.getLogger(packageName);
        if (logger != null) {
            if (packageName.endsWith(subpackage1Package)) {
                logger.setLevel(Level.toLevel(subpackage1Level));
            } else if (packageName.endsWith(subpackage2Package)) {
                logger.setLevel(Level.toLevel(subpackage2Level));
            }
        }
    }

    // resolve com.some.package.* to specific instance
    private String getBasePackage() {
        // In my case, I have only two "types" of services, so this
        // returns either com.some.package.service1 or com.some.package.service2
    }

}

不幸的是,由于 Logback 的内部结构,似乎要使此解决方案起作用,我的根包 (com.some.package) 必须设置为 TRACE 并且所有其他包都必须低于该值 - 反之亦然好像不行。

因此,我不太喜欢这个解决方案(它有效,但有点奇怪)。我会暂时保留这个问题,以防有人知道更好的方法。

【讨论】:

    猜你喜欢
    • 2011-01-02
    • 1970-01-01
    • 2017-09-26
    • 1970-01-01
    • 1970-01-01
    • 2018-11-03
    • 1970-01-01
    • 2010-10-31
    • 2020-10-24
    相关资源
    最近更新 更多