slf4j에서 런타임시 메시지의 로그 수준 설정
log4j를 사용하는 경우 Logger.log(Priority p, Object message)
메서드를 사용할 수 있으며 런타임에 결정된 로그 수준에서 메시지를 기록하는 데 사용할 수 있습니다. 이 사실 과이 팁 을 사용하여 stderr를 특정 로그 수준의 로거로 리디렉션합니다.
slf4j에는 log()
내가 찾을 수 있는 일반적인 방법 이 없습니다 . 위를 구현할 방법이 없다는 의미입니까?
으로이 작업을 수행 할 수있는 방법이 없습니다 slf4j
.
이 기능이 누락 된 이유 는 파사드 뒤의 가능한 모든 로깅 구현에서 사용되는 (또는 동등한) 유형에 효율적으로 매핑 될 수 있는 Level
유형 을 구성하는 것이 거의 불가능하기 때문이라고 생각합니다 . 또는 설계자들은 귀하의 사용 사례가 이를 지원하는 오버 헤드를 정당화 하기에는 너무 이례적 이라고 결정 했습니다.slf4j
Level
에 관한 @ ripper234 의 사용 사례 (단위 테스트), 나는 실용적인 솔루션은 단위 테스트를 실행할 때 로깅 시스템 ...을 SLF4J 외관 뒤에 무엇의 하드 와이어 지식 단위 테스트 (들)을 수정할 생각합니다.
Richard Fearn은 올바른 아이디어를 가지고 있으므로 그의 골격 코드를 기반으로 전체 클래스를 작성했습니다. 여기에 게시하기에 충분히 짧기를 바랍니다. 즐거움을 위해 복사 및 붙여 넣기. 마법 주문도 추가해야 할 것 같습니다. "이 코드는 공개 도메인에 공개되었습니다."
import org.slf4j.Logger;
public class LogLevel {
/**
* Allowed levels, as an enum. Import using "import [package].LogLevel.Level"
* Every logging implementation has something like this except SLF4J.
*/
public static enum Level {
TRACE, DEBUG, INFO, WARN, ERROR
}
/**
* This class cannot be instantiated, why would you want to?
*/
private LogLevel() {
// Unreachable
}
/**
* Log at the specified level. If the "logger" is null, nothing is logged.
* If the "level" is null, nothing is logged. If the "txt" is null,
* behaviour depends on the SLF4J implementation.
*/
public static void log(Logger logger, Level level, String txt) {
if (logger != null && level != null) {
switch (level) {
case TRACE:
logger.trace(txt);
break;
case DEBUG:
logger.debug(txt);
break;
case INFO:
logger.info(txt);
break;
case WARN:
logger.warn(txt);
break;
case ERROR:
logger.error(txt);
break;
}
}
}
/**
* Log at the specified level. If the "logger" is null, nothing is logged.
* If the "level" is null, nothing is logged. If the "format" or the "argArray"
* are null, behaviour depends on the SLF4J-backing implementation.
*/
public static void log(Logger logger, Level level, String format, Object[] argArray) {
if (logger != null && level != null) {
switch (level) {
case TRACE:
logger.trace(format, argArray);
break;
case DEBUG:
logger.debug(format, argArray);
break;
case INFO:
logger.info(format, argArray);
break;
case WARN:
logger.warn(format, argArray);
break;
case ERROR:
logger.error(format, argArray);
break;
}
}
}
/**
* Log at the specified level, with a Throwable on top. If the "logger" is null,
* nothing is logged. If the "level" is null, nothing is logged. If the "format" or
* the "argArray" or the "throwable" are null, behaviour depends on the SLF4J-backing
* implementation.
*/
public static void log(Logger logger, Level level, String txt, Throwable throwable) {
if (logger != null && level != null) {
switch (level) {
case TRACE:
logger.trace(txt, throwable);
break;
case DEBUG:
logger.debug(txt, throwable);
break;
case INFO:
logger.info(txt, throwable);
break;
case WARN:
logger.warn(txt, throwable);
break;
case ERROR:
logger.error(txt, throwable);
break;
}
}
}
/**
* Check whether a SLF4J logger is enabled for a certain loglevel.
* If the "logger" or the "level" is null, false is returned.
*/
public static boolean isEnabledFor(Logger logger, Level level) {
boolean res = false;
if (logger != null && level != null) {
switch (level) {
case TRACE:
res = logger.isTraceEnabled();
break;
case DEBUG:
res = logger.isDebugEnabled();
break;
case INFO:
res = logger.isInfoEnabled();
break;
case WARN:
res = logger.isWarnEnabled();
break;
case ERROR:
res = logger.isErrorEnabled();
break;
}
}
return res;
}
}
Logback으로 전환하고 사용하십시오.
ch.qos.logback.classic.Logger rootLogger = (ch.qos.logback.classic.Logger)LoggerFactory.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME);
rootLogger.setLevel(Level.toLevel("info"));
나는 이것이 Logback에 대한 유일한 호출이며 나머지 코드는 변경되지 않을 것이라고 믿습니다. Logback은 SLF4J를 사용하며 마이그레이션은 간단하며 xml 구성 파일 만 변경하면됩니다.
완료 한 후에는 로그 수준을 다시 설정해야합니다.
Java 8 람다를 사용하여이를 구현할 수 있습니다.
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.event.Level;
public class LevelLogger {
private static final Logger LOGGER = LoggerFactory.getLogger(LevelLogger.class);
private static final Map<Level, LoggingFunction> map;
static {
map = new HashMap<>();
map.put(Level.TRACE, (o) -> LOGGER.trace(o));
map.put(Level.DEBUG, (o) -> LOGGER.debug(o));
map.put(Level.INFO, (o) -> LOGGER.info(o));
map.put(Level.WARN, (o) -> LOGGER.warn(o));
map.put(Level.ERROR, (o) -> LOGGER.error(o));
}
public static void log(Level level, String s) {
map.get(level).log(s);
}
@FunctionalInterface
private interface LoggingFunction {
public void log(String arg);
}
}
이 작업은 enum
및 도우미 메서드를 사용하여 수행 할 수 있습니다 .
enum LogLevel {
TRACE,
DEBUG,
INFO,
WARN,
ERROR,
}
public static void log(Logger logger, LogLevel level, String format, Object[] argArray) {
switch (level) {
case TRACE:
logger.trace(format, argArray);
break;
case DEBUG:
logger.debug(format, argArray);
break;
case INFO:
logger.info(format, argArray);
break;
case WARN:
logger.warn(format, argArray);
break;
case ERROR:
logger.error(format, argArray);
break;
}
}
// example usage:
private static final Logger logger = ...
final LogLevel level = ...
log(logger, level, "Something bad happened", ...);
log
SLF4J의 1- 파라미터 또는 2- 파라미터 warn
/ error
/ etc 의 일반적인 등가물을 원한다면 의 다른 변형을 추가 할 수 있습니다 . 행동 양식.
이 문제에 대한 드롭 인 완전 SLF4J 호환 솔루션을 원하는 사람은 Lidalia SLF4J Extensions 를 확인하는 것이 좋습니다 . Maven Central에 있습니다.
나는 그저 그런 것이 필요했고 다음을 생각 해냈다.
@RequiredArgsConstructor //lombok annotation
public enum LogLevel{
TRACE(l -> l::trace),
INFO (l -> l::info),
WARN (l -> l::warn),
ERROR(l -> l::error);
private final Function<Logger, Consumer<String>> function;
public void log(Logger logger, String message) {
function.apply(logger).accept(message);
}
}
용법:
LogLevel level = LogLevel.TRACE;
level.log(logger, "message");
Logger는 호출 중에 전달되므로 클래스 정보는 정상이어야하며 @ Slf4j lombok 주석과 잘 작동합니다.
것입니다 하지 sjf4j의 로그 레벨을 지정할 수 1.x
상자 밖으로. 그러나 SLF4J에 대한 희망이 2.0
해결하는 문제는 . 2.0에서는 다음과 같이 보일 수 있습니다.
// POTENTIAL 2.0 SOLUTION
import org.slf4j.helpers.Util;
import static org.slf4j.spi.LocationAwareLogger.*;
// does not work with slf4j 1.x
Util.log(logger, DEBUG_INT, "hello world!");
그 동안 slf4j 1.x의 경우 다음 해결 방법을 사용할 수 있습니다.
이 클래스를 클래스 경로에 복사하십시오.
import org.slf4j.Logger;
import java.util.function.Function;
public enum LogLevel {
TRACE(l -> l::trace, Logger::isTraceEnabled),
DEBUG(l -> l::debug, Logger::isDebugEnabled),
INFO(l -> l::info, Logger::isInfoEnabled),
WARN(l -> l::warn, Logger::isWarnEnabled),
ERROR(l -> l::error, Logger::isErrorEnabled);
interface LogMethod {
void log(String format, Object... arguments);
}
private final Function<Logger, LogMethod> logMethod;
private final Function<Logger, Boolean> isEnabledMethod;
LogLevel(Function<Logger, LogMethod> logMethod, Function<Logger, Boolean> isEnabledMethod) {
this.logMethod = logMethod;
this.isEnabledMethod = isEnabledMethod;
}
public LogMethod prepare(Logger logger) {
return logMethod.apply(logger);
}
public boolean isEnabled(Logger logger) {
return isEnabledMethod.apply(logger);
}
}
그런 다음 다음과 같이 사용할 수 있습니다.
Logger logger = LoggerFactory.getLogger(Application.class);
LogLevel level = LogLevel.ERROR;
level.prepare(logger).log("It works!"); // just message, without parameter
level.prepare(logger).log("Hello {}!", "world"); // with slf4j's parameter replacing
try {
throw new RuntimeException("Oops");
} catch (Throwable t) {
level.prepare(logger).log("Exception", t);
}
if (level.isEnabled(logger)) {
level.prepare(logger).log("logging is enabled");
}
다음과 같은 로그가 출력됩니다.
[main] ERROR Application - It works!
[main] ERROR Application - Hello world!
[main] ERROR Application - Exception
java.lang.RuntimeException: Oops
at Application.main(Application.java:14)
[main] ERROR Application - logging is enabled
그만한 가치가 있습니까?
- Pro그것은 계속 소스 코드의 위치 (클래스 이름, 메소드 이름, 줄 번호를 가리 킵니다 당신의 코드)
- Pro변수 , 매개 변수 및 반환 유형을 다음과 같이 쉽게 정의 할 수 있습니다.
LogLevel
- Pro비즈니스 코드는 짧고 읽기 쉬우 며 추가 종속성이 필요 하지 않습니다.
최소한의 예제 인 소스 코드는 GitHub에서 호스팅 됩니다 .
나는 방금 비슷한 요구에 직면했습니다. 제 경우에는 slf4j가 Java 로깅 어댑터 (jdk14 어댑터)로 구성되어 있습니다. 다음 코드 조각을 사용하여 런타임에 디버그 수준을 변경했습니다.
Logger logger = LoggerFactory.getLogger("testing");
java.util.logging.Logger julLogger = java.util.logging.Logger.getLogger("testing");
julLogger.setLevel(java.util.logging.Level.FINE);
logger.debug("hello world");
massimo virgilio의 답변을 기반으로 introspection을 사용하여 slf4j-log4j로 수행했습니다. HTH.
Logger LOG = LoggerFactory.getLogger(MyOwnClass.class);
org.apache.logging.slf4j.Log4jLogger LOGGER = (org.apache.logging.slf4j.Log4jLogger) LOG;
try {
Class loggerIntrospected = LOGGER.getClass();
Field fields[] = loggerIntrospected.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
String fieldName = fields[i].getName();
if (fieldName.equals("logger")) {
fields[i].setAccessible(true);
org.apache.logging.log4j.core.Logger loggerImpl = (org.apache.logging.log4j.core.Logger) fields[i].get(LOGGER);
loggerImpl.setLevel(Level.DEBUG);
}
}
} catch (Exception e) {
System.out.println("ERROR :" + e.getMessage());
}
다음은 @Paul Croarkin만큼 사용자 친화적이지 않은 람다 솔루션입니다 (레벨이 효과적으로 두 번 전달됨). 그러나 나는 (a) 사용자가 Logger를 통과해야한다고 생각합니다. (b) AFAIU 원래 질문은 응용 프로그램의 모든 곳에서 편리한 방법을 요구하는 것이 아니라 라이브러리 내부에서 사용이 거의없는 상황 일뿐이었습니다.
package test.lambda;
import java.util.function.*;
import org.slf4j.*;
public class LoggerLambda {
private static final Logger LOG = LoggerFactory.getLogger(LoggerLambda.class);
private LoggerLambda() {}
public static void log(BiConsumer<? super String, ? super Object[]> logFunc, Supplier<Boolean> logEnabledPredicate,
String format, Object... args) {
if (logEnabledPredicate.get()) {
logFunc.accept(format, args);
}
}
public static void main(String[] args) {
int a = 1, b = 2, c = 3;
Throwable e = new Exception("something went wrong", new IllegalArgumentException());
log(LOG::info, LOG::isInfoEnabled, "a = {}, b = {}, c = {}", a, b, c);
// warn(String, Object...) instead of warn(String, Throwable), but prints stacktrace nevertheless
log(LOG::warn, LOG::isWarnEnabled, "error doing something: {}", e, e);
}
}
slf4j 는 varargs param 내부에 Throwable (스택 추적이 기록되어야하는)을 허용 하므로 log
다른 소비자를 위해 헬퍼 메서드를 오버로딩 할 필요가 없다고 생각 (String, Object[])
합니다.
먼저 SLF4J Logger 인스턴스를 요청한 다음 바인딩 수준 을 설정 하여 JDK14 바인딩에 대해이를 수행 할 수있었습니다 . Log4J 바인딩에 대해 시도해 볼 수 있습니다.
private void setLevel(Class loggerClass, java.util.logging.Level level) {
org.slf4j.LoggerFactory.getLogger(loggerClass);
java.util.logging.Logger.getLogger(loggerClass.getName()).setLevel(level);
}
내가 사용하는 방법은 ch.qos.logback 모듈을 가져온 다음 slf4j Logger 인스턴스를 ch.qos.logback.classic.Logger로 유형 캐스팅하는 것입니다. 이 인스턴스에는 setLevel () 메서드가 포함되어 있습니다.
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
Logger levelSet = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
// Now you can set the desired logging-level
levelSet.setLevel( Level.OFF );
가능한 로깅 수준을 확인하려면 ch.qos.logback 클래스를 확장하여 Level에 대해 가능한 모든 값을 볼 수 있습니다 .
prompt$ javap -cp logback-classic-1.2.3.jar ch.qos.logback.classic.Level
결과는 다음과 같습니다.
{
// ...skipping
public static final ch.qos.logback.classic.Level OFF;
public static final ch.qos.logback.classic.Level ERROR;
public static final ch.qos.logback.classic.Level WARN;
public static final ch.qos.logback.classic.Level INFO;
public static final ch.qos.logback.classic.Level DEBUG;
public static final ch.qos.logback.classic.Level TRACE;
public static final ch.qos.logback.classic.Level ALL;
}
slf4j API로는 로그 레벨을 동적으로 변경할 수 없지만 로그 백 (사용하는 경우)은 직접 구성 할 수 있습니다. 이 경우 로거에 대한 팩토리 클래스를 만들고 필요한 구성으로 루트 로거를 구현하십시오.
LoggerContext loggerContext = new LoggerContext();
ch.qos.logback.classic.Logger root = loggerContext.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
// Configure appender
final TTLLLayout layout = new TTLLLayout();
layout.start(); // default layout of logging messages (the form that message displays
// e.g. 10:26:49.113 [main] INFO com.yourpackage.YourClazz - log message
final LayoutWrappingEncoder<ILoggingEvent> encoder = new LayoutWrappingEncoder<>();
encoder.setCharset(StandardCharsets.UTF_8);
encoder.setLayout(layout);
final ConsoleAppender<ILoggingEvent> appender = new ConsoleAppender<>();
appender.setContext(loggerContext);
appender.setEncoder(encoder);
appender.setName("console");
appender.start();
root.addAppender(appender);
루트 로거를 구성한 후 (한 번이면 충분 함) 다음을 통해 새 로거 가져 오기를 위임 할 수 있습니다.
final ch.qos.logback.classic.Logger logger = loggerContext.getLogger(clazz);
동일한 loggerContext
.
에서 제공 한 루트 로거를 사용하면 로그 수준을 쉽게 변경할 수 있습니다 loggerContext
.
root.setLevel(Level.DEBUG);
Java introspection을 사용하면 다음과 같이 할 수 있습니다.
private void changeRootLoggerLevel(int level) {
if (logger instanceof org.slf4j.impl.Log4jLoggerAdapter) {
try {
Class loggerIntrospected = logger.getClass();
Field fields[] = loggerIntrospected.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
String fieldName = fields[i].getName();
if (fieldName.equals("logger")) {
fields[i].setAccessible(true);
org.apache.log4j.Logger loggerImpl = (org.apache.log4j.Logger) fields[i]
.get(logger);
if (level == DIAGNOSTIC_LEVEL) {
loggerImpl.setLevel(Level.DEBUG);
} else {
loggerImpl.setLevel(org.apache.log4j.Logger.getRootLogger().getLevel());
}
// fields[i].setAccessible(false);
}
}
} catch (Exception e) {
org.apache.log4j.Logger.getLogger(LoggerSLF4JImpl.class).error("An error was thrown while changing the Logger level", e);
}
}
}
아니요, 많은 메서드, info (), debug (), warn () 등이 있습니다 (우선 순위 필드를 대체 함).
전체 Logger API 는 http://www.slf4j.org/api/org/slf4j/Logger.html 을 참조하십시오 .
참고 URL : https://stackoverflow.com/questions/2621701/setting-log-level-of-message-at-runtime-in-slf4j
'code' 카테고리의 다른 글
간헐적 인 asp.net mvc 예외 : "공개 작업 방법 ABC를 컨트롤러 XYZ에서 찾을 수 없습니다." (0) | 2020.09.08 |
---|---|
python numpy.where ()는 어떻게 작동합니까? (0) | 2020.09.08 |
WPF에서 탭 순서 설정 (0) | 2020.09.07 |
로그에 Spring 트랜잭션 표시 (0) | 2020.09.07 |
저장소 패턴을 사용하지 않고 ORM을있는 그대로 사용 (EF) (0) | 2020.09.07 |