首页 > 解决方案 > 是否可以在 Spring 的 slf4j 配置中以任何方式使用通配符?(不是作为根记录器!)

问题描述

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

我的代码结构如下:

服务1

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

服务 2

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

我想将类的所有记录器的日志级别设置subpackage1DEBUG,同时将类的所有记录器的日志级别设置为 ,subpackage2WARN将其余的日志级别设置为INFO

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

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

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

  1. 是否可以通过简单的配置或以编程方式使用 slf4j 来做到这一点(理想情况下只使用 slf4j API,但我可以接受特定于实现的解决方案)?
  2. 如果没有,是否有替代解决方案?

标签: javaspringlogginglog4jslf4j

解决方案


I managed to do this using, unfortunately, using the specific Logback Classic Logger implementation.

The end result is something like:

@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
    }

}

Unfortunately, due to Logback internals, it seems that for this solution to work, my root package (com.some.package) has to be set to TRACE and all others have to be lower than that - the other way around does not seem to work.

Due to this, I don't like this solution very much (it works, but it's kind of weird). I'll leave this question open for a bit in case anyone has an idea of a better way to do this.


推荐阅读