如何设置Spring Boot日志记录器仅针对定时任务?

huangapple go评论80阅读模式
英文:

How to set up the spring boot logger to scheduled tasks only?

问题

I have a spring boot project, so I set up the hibernate logger to log all queries in a request like this:

In my application.properties file

logging.level.org.hibernate.type=trace
logging.level.org.hibernate.SQL=debug
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=trace

This works fine, All my requests show the log for the hibernate queries.

But I also have a scheduled task to run every one second like this:

@Scheduled(cron = "${* * * * * *}")
public void task() {
  ...
}

So, I want to turn off the hibernate queries log only for this task execution to avoid
mess up my requests output log.

How can I set up the logger to do this?! Thanks.

英文:

I have a spring boot project, so I set up the hibernate logger to log all queries in a request like this:

In my application.properties file

logging.level.org.hibernate.type=trace
logging.level.org.hibernate.SQL=debug
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=trace

This works fine, All my requests show the log for the hibernate queries.

But I also have a scheduled task to run every one second like this:

@Scheduled(cron = "${* * * * * *}")
public void task() {
  ...
}

So, I want to turn off the hibernate queries log only for this task execution to avoid
mess up my requests output log.

How can I set up the logger to do this?! Thanks.

答案1

得分: 7

如果您正在使用Slf4j和logback,您可以基于Mapped Diagnostic Context (MDC)(http://logback.qos.ch/manual/mdc.html)进行过滤。

MDC是线程本地的,您可以在其中放入任何您想要在日志记录器中使用的键值对。例如:

@Scheduled(cron = "${* * * * * *}")
public void task() {
  MDC.put("DONOTLOG", "true");
  try {
    ....
  } finally {
    MDC.remove("DONOTLOG");
  }
}

您可以在自定义logback过滤器(http://logback.qos.ch/manual/filters.html)中使用这个功能,方法是将以下内容放入您的logback.xml文件中:

<configuration>
  <appender name="...">
    <filter class="...SampleFilter" />
  </appender>
</configuration>

SampleFilter:

public class SampleFilter extends Filter<ILoggingEvent> {

  @Override
  public FilterReply decide(ILoggingEvent event) {    
    if (event.getMDCPropertyMap().containsKey("DONOTLOG")) {
      return FilterReply.DENY;
    } else {
      return FilterReply.NEUTRAL;
    }
  }
}

如有需要,可以提出进一步的问题。

英文:

If you're using Slf4j and logback, you can filter based on the Mapped Diagnostic Context (http://logback.qos.ch/manual/mdc.html).

The MDC is thread-local, and you can put any key-value pairs in it that you want to use in the logger. E.g:

@Scheduled(cron = &quot;${* * * * * *}&quot;)
public void task() {
  MDC.put(&quot;DONOTLOG&quot;, &quot;true&quot;)
  try {
    ....
  } finally {
    MDC.remove(&quot;DONOTLOG&quot;);
  }
}

You can use this in a custom logback filter (http://logback.qos.ch/manual/filters.html), by putting this in your logback.xml:

&lt;configuration&gt;
  &lt;appender name=&quot;...&quot;&gt;

    &lt;filter class=&quot;...SampleFilter&quot; /&gt;

  &lt;/appender&gt;

SampleFilter:

public class SampleFilter extends Filter&lt;ILoggingEvent&gt; {

  @Override
  public FilterReply decide(ILoggingEvent event) {    
    if (event.getMDCPropertyMap().containsKey(&quot;DONOTLOG&quot;)) {
      return FilterReply.DENY;
    } else {
      return FilterReply.NEUTRAL;
    }
  }
}

答案2

得分: 2

I assume you are using Slf4j.

This library does not support that functionality out of the box.

The capacity of changing the logger level at runtime largely depends on the underlying logging libraries (Logback, Log4J2, etcetera) configured.

But Spring Boot provides an underlying logging system that handles all this stuff for us. We can use that underlying logging system to achieve the functionality that you need.

You can do something like the following example:

@Scheduled(cron = "${* * * * * *}")
public void task() {
  // At the beginning of the method, disable logging levels
  final List<String> loggerNames = Arrays.asList("org.hibernate.SQL", "org.hibernate.type", "org.hibernate.type.descriptor.sql.BasicBinder");
  Map<String, LogLevel> previousLogLevels = new HashMap();
  loggerNames.forEach(loggerName -> {
    final LoggerConfiguration loggerConfiguration = loggingSystem.getLoggerConfiguration(loggerName);
    final LogLevel logLevel = loggerConfiguration.getEffectiveLevel();
    previousLogLevels.put(loggerName, logLevel);
    loggingSystem.setLogLevel(loggerName, LogLevel.OFF);
  });
  
  try {
    // Do your stuff
    ...
  } finally {
    // Restore the logging levels to the original ones
    previousLogLevels.forEach((loggerName, logLevel) -> loggingSystem.setLogLevel(loggerName, logLevel));
  }
}

You must inject a bean of type LoggingSystem in your scheduled task bean so you can reference it in the code.

This functionality can be easily reused in different contexts: if you need to, you can create an aspect for that, for instance, freeing the business logic from this need.

Edit

After reading the @KavithakaranKanapathippillai answer, and reading again, carefully, your question, I fully understand your need.

He is right: please, be aware that with this setup you will modify the logging level of the affected logs globally, not only for the async task, and the rest of the code will be also affected by the change, so there will always be a chance to lose some "normal" application code log traces if that "normal" code uses Hibernate and is executed while the async task is running.

If this is important or not will largely depend on how often the async task is running - I suppose it runs very fast if it is executed every second but, on the other hand, it runs a lot of times - and how many transactions your application receives.

英文:

I assume you are using Slf4j.

This library does not support that functionally out of the box.

The capacity of changing the logger level at runtime largely depends on the underlying logging libraries (Logback, Log4J2, etcetera) configured.

But Spring Boot provides an underlying logging system that handles all this stuff for us. We can use that underlying logging system to achieve the functionally that you need.

You can do something like the following example:

@Scheduled(cron = &quot;${* * * * * *}&quot;)
public void task() {
  // At the beginning of the method, disable logging levels
  final List&lt;String&gt; loggerNames = Arrays.asList(&quot;org.hibernate.SQL&quot;, &quot;org.hibernate.type&quot;, &quot;org.hibernate.type.descriptor.sql.BasicBinder&quot;);
Map&lt;String, LogLevel&gt; previousLogLevels = new HashMap();
loggerNames.forEach(loggerName -&gt; {
  final LoggerConfiguration loggerConfiguration = loggingSystem.getLoggerConfiguration(loggerName);
  final LogLevel logLevel = loggerConfiguration.getEffectiveLevel();
  previousLogLevels.put(loggerName, logLevel);
  loggingSystem.setLogLevel(loggerName, LogLevel.OFF);
});
  
  try {
    // Do your stuff
    ...
  } finally {
    // Restore the logging levels to the original ones
    previousLogLevels.forEach((loggerName, logLevel) -&gt; loggingSystem.setLogLevel(loggerName, logLevel));
  }
}

You must inject a bean of type LoggingSystem in your scheduled task bean so you can reference it in the code.

This functionally can be easily reused in different contexts: if you need to, you can create an aspect for that, for instance, freeing the business logic from this need.

Edit

After read the @KavithakaranKanapathippillai answer, and read again, carefully, your question, I fully understand your need.

He is right: please, be aware that with this setup you will modify the logging level of the affected logs globally, not only for the async task, and the rest of the code will be also affected for the change, so it will be always a chance for lost some "normal" application code log traces if that "normal" code uses Hibernate and is executed while the async task is running.

If this is important or not will largely depend on how much the async task is running - I suppose it runs very fast if it is executed every second but, on the other hand, it runs a lot times - and how many transactions your application receives.

答案3

得分: 2

我不认为你能够达到你的目标。

即使你设法在运行时关闭hibernate的日志级别,它也会在应用程序中全局关闭hibernate的日志级别,因为它与调用者类加载器绑定。

这意味着如果有hibernate调用的并发执行,在第一个同步执行中你想记录日志,在第二个并发异步执行中你不想记录日志,它们会相互干扰,你将得到非确定性的结果。

英文:

I don't think you will be able to achieve your result.

Even if you manage to switch off the logging levels for hibernate at runtime, it will switch off the log level globally in the application for hibernate because it is tied to the caller class loader

It means if there concurrent executions of hibernate calls where you want to log in the first synchronous execution and you don't want in the second concurrent asynchronous execution, they are going to interfere with each other and you will get non deterministic results

答案4

得分: 0

My answer is inspired by GreyFairer's.

Actually, Spring Boot默认使用logback,如果没有使用log4j进行覆盖,则只需创建

logback-spring.xml

并放置在类路径(WEB-INF/classes)中
示例取自http://logback.qos.ch/manual/filters.html

logback-spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/base.xml"/>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
            <evaluator class="ch.qos.logback.classic.boolex.GEventEvaluator">
                <expression>
                    (e.mdc?.get("DONOTLOG") =~ /true/ )
                </expression>
            </evaluator>
            <OnMismatch>NEUTRAL</OnMismatch>
            <OnMatch>DENY</OnMatch>
        </filter>
        <encoder>
            <pattern>
                %-4relative [%thread] %-5level %logger - %msg%n
            </pattern>
        </encoder>
    </appender>

    <logger name="org.hibernate.sql" level="DEBUG" additivity="false">
        <appender-ref ref="STDOUT"/>
    </logger>
</configuration>

然后

@Scheduled(cron = "${* * * * * *}")
public void task() {
  MDC.put("DONOTLOG", "true");
  try {
    ....
  } finally {
    MDC.remove("DONOTLOG");
  }
}

应该解决这个问题。

英文:

My answer is inspired by GreyFairer's.

Actually StringBoot by default uses logback so if havent overriden with log4j then you just need to create

logback-spring.xml

and put in classpath (WEB-INF/classes)
Example taken from http://logback.qos.ch/manual/filters.html

logback-spring.xml

    &lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;configuration&gt;
    &lt;include resource=&quot;org/springframework/boot/logging/logback/base.xml&quot;/&gt;
	&lt;appender name=&quot;STDOUT&quot; class=&quot;ch.qos.logback.core.ConsoleAppender&quot;&gt;
    &lt;filter class=&quot;ch.qos.logback.core.filter.EvaluatorFilter&quot;&gt;      
      &lt;evaluator class=&quot;ch.qos.logback.classic.boolex.GEventEvaluator&quot;&gt; 
        &lt;expression&gt;
           (e.mdc?.get(&quot;DONOTLOG&quot;) =~ /true/ )
        &lt;/expression&gt;
      &lt;/evaluator&gt;
      &lt;OnMismatch&gt;NEUTRAL&lt;/OnMismatch&gt;
      &lt;OnMatch&gt;DENY&lt;/OnMatch&gt;
    &lt;/filter&gt;
    &lt;encoder&gt;
      &lt;pattern&gt;
        %-4relative [%thread] %-5level %logger - %msg%n
      &lt;/pattern&gt;
    &lt;/encoder&gt;
  &lt;/appender&gt;

  &lt;logger name=&quot;org.hibernate.sql&quot; level=&quot;DEBUG&quot; additivity=&quot;false&quot;&gt;
        &lt;appender-ref ref=&quot;STDOUT&quot;/&gt;
    &lt;/logger&gt;
 &lt;/configuration&gt;

and then

@Scheduled(cron = &quot;${* * * * * *}&quot;)
public void task() {
  MDC.put(&quot;DONOTLOG&quot;, &quot;true&quot;)
  try {
    ....
  } finally {
    MDC.remove(&quot;DONOTLOG&quot;);
  }
}

should solve the purpose.

huangapple
  • 本文由 发表于 2020年8月12日 23:17:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/63379625.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定