본문 바로가기
공부/프로그래밍

[log4j] runtime 중 레벨 변경(@Slf4j 같이 적용)

by demonic_ 2021. 12. 20.
반응형

Spring을 사용하지 않은 프로젝트에서 log4j 를 설정하기 위해서는 다음의 경로에 log4j.properties 또는 log4j.xml 파일이 있어야 한다.

 

src/main/resources/log4j.properties

 

이 파일을 읽을 수 있는건 org.apache.log4j.LogManager 에서 가능한데, 나중에 로그레벨을 변경하는 것도 여기서 한다. 파일안을 보면 다음으로 구성되어 있다

/**
 * Use the <code>LogManager</code> class to retreive {@link Logger}
 * instances or to operate on the current {@link
 * LoggerRepository}. When the <code>LogManager</code> class is loaded
 * into memory the default initalzation procedure is inititated. The
 * default intialization procedure</a> is described in the <a
 * href="../../../../manual.html#defaultInit">short log4j manual</a>.
 *
 * @author Ceki G&uuml;lc&uuml; */
public class LogManager {

  /**
   * @deprecated This variable is for internal use only. It will
   * become package protected in future versions.
   * */
  static public final String DEFAULT_CONFIGURATION_FILE = "log4j.properties";
  
  static final String DEFAULT_XML_CONFIGURATION_FILE = "log4j.xml";
...
  static {
    // By default we use a DefaultRepositorySelector which always returns 'h'.
    Hierarchy h = new Hierarchy(new RootLogger((Level) Level.DEBUG));
    repositorySelector = new DefaultRepositorySelector(h);

    /** Search for the properties file log4j.properties in the CLASSPATH.  */
    String override =OptionConverter.getSystemProperty(DEFAULT_INIT_OVERRIDE_KEY,
						       null);

    // if there is no default init override, then get the resource
    // specified by the user or the default config file.
    if(override == null || "false".equalsIgnoreCase(override)) {

      String configurationOptionStr = OptionConverter.getSystemProperty(
							  DEFAULT_CONFIGURATION_KEY, 
							  null);

      String configuratorClassName = OptionConverter.getSystemProperty(
                                                   CONFIGURATOR_CLASS_KEY, 
						   null);

      URL url = null;

      // if the user has not specified the log4j.configuration
      // property, we search first for the file "log4j.xml" and then
      // "log4j.properties"
      if(configurationOptionStr == null) {	
	url = Loader.getResource(DEFAULT_XML_CONFIGURATION_FILE);
	if(url == null) {
	  url = Loader.getResource(DEFAULT_CONFIGURATION_FILE);
	}
      } else {
	try {
	  url = new URL(configurationOptionStr);
	} catch (MalformedURLException ex) {
	  // so, resource is not a URL:
	  // attempt to get the resource from the class path
	  url = Loader.getResource(configurationOptionStr); 
	}	
      }
      
      // If we have a non-null url, then delegate the rest of the
      // configuration to the OptionConverter.selectAndConfigure
      // method.
      if(url != null) {
	    LogLog.debug("Using URL ["+url+"] for automatic log4j configuration.");
        try {
            OptionConverter.selectAndConfigure(url, configuratorClassName,
					   LogManager.getLoggerRepository());
        } catch (NoClassDefFoundError e) {
            LogLog.warn("Error during default initialization", e);
        }
      } else {
	    LogLog.debug("Could not find resource: ["+configurationOptionStr+"].");
      }
    } else {
        LogLog.debug("Default initialization of overridden by " + 
            DEFAULT_INIT_OVERRIDE_KEY + "property."); 
    }  
  }

 

static 메서드에서 보면 해당경로에서 읽는 것을 확인할 수 있다. 때문에 어플리케이션이 시작되면 여기서 파일을 읽는다.

 

그럼 중간에 바꾸려면 어떻게 해야할까?

LogManager 에서 아래 2개를 변경해야 한다

1) RootLevel 변경

2) Package 기준 Level 변경

 

다음처럼 변경할 수 있다

String packageName = "com.deoklab.app";

Level level = Level.WARN;
LogManager.getRootLogger().setLevel(level);
LogManager.getLogger(packageName).setLevel(level);

 

packageName의 경우는 프로젝트의 Root 패키지 이름을 가져오면 된다. 참고로 같은 방식을 설정을 log4j.properties 에다가 할때에는 다음처럼 설정한다

log4j.rootLoger=debug  # root 로그 설정
log4j.logger.com.deoklab.app=WARN  # 패키지별 로그 설정

 

그럼 다음처럼 메서드를 만든 후에 변경됐는지 확인해보자

...
    private void setLogLevel(String logLevel) {
        Level level;
        switch (logLevel.toUpperCase()) {
            case "TRACE":
                level = Level.TRACE;
                break;
            case "DEBUG":
                level = Level.DEBUG;
                break;
            case "INFO":
                level = Level.INFO;
                break;
            case "WARN":
                level = Level.WARN;
                break;
            case "ERROR":
                level = Level.ERROR;
                break;
            case "":
                level = Level.INFO;
                break;
            default:
                throw new WidetnsException("로그레벨이 잘못 설정되었습니다");
        }


        // 변경기준 패키지명(root package name)
        String packageName = "com.deoklab.app";

        // 변경 전 로그레벨
        Level beforeLevelRoot = LogManager.getRootLogger().getLevel();
        Level beforeLevelPackage = LogManager.getLogger(packageName).getLevel();

        Logger loggerBefore = LoggerFactory.getLogger(packageName);
        System.out.println("loggerBefore root level=" + beforeLevelRoot);
        System.out.println("loggerBefore package debug=" + loggerBefore.isDebugEnabled());
        System.out.println("loggerBefore package info=" + loggerBefore.isInfoEnabled());
        System.out.println("loggerBefore package warn=" + loggerBefore.isWarnEnabled());

        System.out.println("------------------------------------------");

        // root level 변경
        LogManager.getRootLogger().setLevel(level);
        // 패키지 기준 하위 레벨 변경
        LogManager.getLogger(packageName).setLevel(level);


        Level LevelRootAfter = LogManager.getRootLogger().getLevel();
        Logger loggerAfter = LoggerFactory.getLogger(packageName);
        System.out.println("loggerAfter root level=" + LevelRootAfter);
        System.out.println("loggerAfter package debug=" + loggerAfter.isDebugEnabled());
        System.out.println("loggerAfter package info=" + loggerAfter.isInfoEnabled());
        System.out.println("loggerAfter package warn=" + loggerAfter.isWarnEnabled());

        // lombok의 @Slf4j를 사용하여 로그출력
        log.info("log root level change {} => {}", beforeLevelRoot, level);
        log.info("log package level change {} => {}", beforeLevelPackage, level);
    }
...

로그 (WARN => INFO 로 변경)

loggerBefore root level=DEBUG
loggerBefore package debug=false
loggerBefore package info=false
loggerBefore package warn=true
------------------------------------------
loggerAfter root level=INFO
loggerAfter package debug=false
loggerAfter package info=true
loggerAfter package warn=true


log root level change DEBUG => INFO
log package level change WARN => INFO

 

잘 변경된 것을 확인할 수 있다.

 

 

내 경우 Lombok을 이용해서 @Slf4j를 사용하고 있는데 같이 적용된다.

 

 

끝.

 

반응형

댓글