code

Java 및 일반적으로 로그인 : 모범 사례?

codestyles 2020. 11. 25. 07:59
반응형

Java 및 일반적으로 로그인 : 모범 사례?


가끔 내 로깅 코드를 볼 때 제대로하고 있는지 궁금합니다. 이에 대한 확실한 답이 없을 수도 있지만 다음과 같은 우려가 있습니다.

도서관 수업

일부 INFO메시지를 기록 할 수있는 여러 라이브러리 클래스가 있습니다 . 치명적인 오류는 예외로보고됩니다. 현재 클래스 이름이 로깅 이름 인 정적 로거 인스턴스가 클래스에 있습니다. (Log4j의는 다음과 같습니다 Logger.getLogger(MyClass.class))

이것이 올바른 방법입니까? 이 라이브러리 클래스의 사용자는 내 구현에서 메시지를 원하지 않거나 응용 프로그램 별 로그로 리디렉션하려고 할 수 있습니다. 사용자가 "외부 세계"에서 로거를 설정하도록 허용해야합니까? 그러한 경우를 어떻게 처리합니까?

일반 로그

일부 응용 프로그램에서 내 클래스는 클래스 이름으로 식별되지 않는 특정 로그에 로그 메시지를 작성하려고 할 수 있습니다. (예 :) HTTP Request log그런 일을하는 가장 좋은 방법은 무엇입니까? 조회 서비스가 떠 오릅니다 ...


당신의 관습은 꽤 표준적이고 꽤 괜찮습니다 (imho).

주의해야 할 한 가지는 과도한 unnedded 디버그 호출로 인한 메모리 조각화이므로 Log4J (및 대부분의 다른 Java 로깅 프레임 워크)를 사용하면 다음과 같은 결과가 발생합니다.

if (log.isDebugEnabled()) {
  log.debug("...");
}

그 로그 메시지 (아마도 사용하지 않을 것)를 구성하는 것은 비용이 많이들 수 있기 때문입니다.

INFO 레벨 로깅은 너무 "수다스럽지"않아야합니다 (그리고 당신의 말로는 그렇지 않은 것처럼 들립니다). INFO 메시지는 일반적으로 응용 프로그램이 시작되고 중지되는 것처럼 의미 있고 중요해야합니다. 문제가 발생하면 알고 싶은 것. 디버그 / 미세 수준 로깅은 실제로 진단하려는 문제가있을 때 더 많이 사용됩니다. 디버그 / 미세 로깅은 일반적으로 필요할 때만 설정됩니다. 정보는 일반적으로 항상 표시됩니다.

누군가가 당신의 클래스로부터 특정 INFO 메시지를 원하지 않는다면, 그들은 물론 그것들을 얻지 않도록 당신의 log4j 구성을 자유롭게 변경할 수 있습니다. Log4j는이 부서에서 매우 간단합니다 (Java 1.4 로깅과 반대).

HTTP에 관해서는 일반적으로 단일 클래스가 관심있는 항목을 담당하므로 한 곳에 배치하면되므로 일반적으로 Java 로깅에 문제가되는 것을 찾지 못했습니다. (내 경험상 드물게) 겉보기에 관련이없는 클래스에서 공통 로그 메시지를 원할 때 쉽게 알 수있는 토큰을 넣으십시오.


다음은 우수한 성능을 보장하기 위해 모든 프로젝트에서 따르는 일련의 지침입니다. 나는 인터넷의 다양한 소스로부터의 입력을 기반으로이 일련의 지침을 작성하게되었습니다.

오늘과 마찬가지로 Log4j 2가 Java 로그인에 가장 적합한 옵션이라고 생각합니다.

벤치 마크는 여기에서 확인할 수 있습니다 . 최상의 성능을 얻기 위해 따르는 방법은 다음과 같습니다.

  1. 다음과 같은 이유로 현재 SLF4J를 사용하지 않습니다.
  2. 더 나은 성능을 위해 비동기 로거를 사용하여 모든 일반 로깅 수행
  3. 오류 메시지가 발생하는 즉시 확인하기 위해 동기식 로거를 사용하여 별도의 파일에 오류 메시지를 기록합니다.
  4. 일반 로깅에서 파일 이름, 클래스 이름, 메서드 이름, 줄 번호와 같은 위치 정보를 사용하지 마십시오. 이러한 정보를 파생하기 위해 프레임 워크는 스택의 스냅 샷을 찍고 살펴보기 때문입니다. 이것은 성능에 영향을 미칩니다. 따라서 위치 정보는 일반 로그가 아닌 오류 로그에서만 사용하십시오.
  5. 별도의 스레드에서 처리하는 개별 요청을 추적하려면 여기에 설명 된대로 스레드 컨텍스트 및 임의 UUID 사용을 고려 하십시오.
  6. 별도의 파일에 오류를 기록하므로 오류 로그에도 컨텍스트 정보를 기록하는 것이 매우 중요합니다. 예를 들어 응용 프로그램에서 파일을 처리하는 동안 오류가 발생한 경우 파일 이름과 처리중인 파일 레코드를 스택 추적과 함께 오류 로그 파일에 인쇄합니다.
  7. 로그 파일은 grep 가능하고 이해하기 쉬워야합니다. 예를 들어 애플리케이션이 여러 파일의 고객 레코드를 처리하는 경우 각 로그 메시지는 다음과 같아야합니다.
12:01:00,127 INFO FILE_NAME=file1.txt - Processing starts
12:01:00,127 DEBUG FILE_NAME=file1.txt, CUSTOMER_ID=756
12:01:00,129 INFO FILE_NAME=file1.txt - Processing ends
  1. 아래와 같이 SQL 마커를 사용하여 모든 SQL 문을 기록하고 필터를 사용하여 활성화 또는 비활성화합니다.
private static final Marker sqlMarker = 
  MarkerManager.getMarker("SQL");

private void method1() {
    logger.debug(sqlMarker, "SELECT * FROM EMPLOYEE");
}
  1. Java 8 Lambda를 사용하여 모든 파라미터를 기록합니다. 이렇게하면 지정된 로그 수준이 비활성화 될 때 메시지 형식 지정에서 응용 프로그램이 저장됩니다.
int i=5, j=10;
logger.info("Sample output {}, {}", ()->i, ()->j);
  1. 문자열 연결을 사용하지 마십시오. 위에 표시된대로 매개 변수화 된 메시지 사용

  2. 애플리케이션이 애플리케이션을 다시 시작할 필요없이 로깅 구성의 변경 사항을 자동으로 다시로드하도록 로깅 구성의 동적 재로드를 사용하십시오.

  3. printStackTrace()또는 사용하지 마십시오System.out.println()

  4. 애플리케이션은 종료하기 전에 로거를 종료해야합니다.

LogManager.shutdown();
  1. 마지막으로 모든 사람의 참조를 위해 다음 구성을 사용합니다.
<?xml version="1.0" encoding="UTF-8"?>
<Configuration monitorinterval="300" status="info" strict="true">
    <Properties>
        <Property name="filePath">${env:LOG_ROOT}/SAMPLE</Property>
        <Property name="filename">${env:LOG_ROOT}/SAMPLE/sample
        </Property>
        <property name="logSize">10 MB</property>
    </Properties>
    <Appenders>
        <RollingFile name="RollingFileRegular" fileName="${filename}.log"
            filePattern="${filePath}/sample-%d{yyyy-dd-MM}-%i.log">
            <Filters>
                <MarkerFilter marker="SQL" onMatch="DENY"
                    onMismatch="NEUTRAL" />
            </Filters>
            <PatternLayout>
                <Pattern>%d{HH:mm:ss,SSS} %m%n
                </Pattern>
            </PatternLayout>
            <Policies>
                <TimeBasedTriggeringPolicy
                    interval="1" modulate="true" />
                <SizeBasedTriggeringPolicy
                    size="${logSize}" />

            </Policies>
        </RollingFile>
        <RollingFile name="RollingFileError" 
            fileName="${filename}_error.log"
            filePattern="${filePath}/sample_error-%d{yyyy-dd-MM}-%i.log"
            immediateFlush="true">
            <PatternLayout>
                <Pattern>%d{HH:mm:ss,SSS} %p %c{1.}[%L] [%t] %m%n
                </Pattern>
            </PatternLayout>
            <Policies>
                <TimeBasedTriggeringPolicy
                    interval="1" modulate="true" />
                <SizeBasedTriggeringPolicy
                    size="${logSize}" />
            </Policies>
        </RollingFile>
    </Appenders>
    <Loggers>
        <AsyncLogger name="com"
            level="trace">
            <AppenderRef ref="RollingFileRegular"/>
        </AsyncLogger>
        <Root includeLocation="true" level="trace">
            <AppenderRef ref="RollingFileError" level="error" />
        </Root>
    </Loggers>
</Configuration>
  1. 필요한 Maven 종속성은 다음과 같습니다.
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.8.1</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.8.1</version>
</dependency>
<dependency>
    <groupId>com.lmax</groupId>
    <artifactId>disruptor</artifactId>
    <version>3.3.6</version>
</dependency>
<!-- (Optional)To be used when working 
with the applications using Log4j 1.x -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-1.2-api</artifactId>
    <version>2.8.1</version>
</dependency>

In @cletus' answer, he wrote of the problem of

if (log.isDebugEnabled()) {
  log.debug("val is " + value);
}

which might be overcome by using SL4J. It provides a formatting help

log.debug("val is {}", value);

where the message is only constructed if the level is debug.

So, nowaday, using SL4J and its companion logger, Logback, is advised for performance and stability reasons.


With respect to instantiating loggers, I have had some success using an Eclipse Java Template for setting up my loggers:

private static Logger log = Logger.getLogger(${enclosing_type}.class);

This avoids the problem of a JVM mucking about with your stack trace, and reduces (trivially, perhaps) the overhead from creating the stack trace in the first place.

The great thing about using a template like this is that you can share it with your team if you want to set a consistent standard across the board for loggers.

It looks like IntelliJ supports the same concept for a template variable representing the name of the enclosing type. I don't see a way to do it easily in NetBeans.


The preferred option for the kind of log4j configuration you're describing is to use the log4j configuration file. This allows users of your implementation to do exactly what you're asking for, since they can override your configuration later on with something more suitable for their own implementation. See here for a very thorough primer.


I have probably stolen this from somewhere, but it's nice.

It reduces the risk of mixing up loggers when copying and pasti^h^h^h refactoring, and it's less to type.

In your code:

private final static Logger logger = LoggerFactory.make();

...and in LoggerFactory:

public static Logger make() {
    Throwable t = new Throwable();
    StackTraceElement directCaller = t.getStackTrace()[1];
    return Logger.getLogger(directCaller.getClassName());
}

(Note that the stackdump is done during initialisation. The stacktrace will probably not be optimized away by the JVM but there are actually no guarantees)


I'm reviewing log-levels of an application and I'm currently detecting a pattern:

private static final Logger logger = Logger.getLogger(Things.class)

public void bla() {
  logger.debug("Starting " + ...)
  // Do stuff
  ...
  logger.debug("Situational")

  // Algorithms
  for(Thing t : things) {
    logger.trace(...)
  }

  // Breaking happy things
  if(things.isEmpty){
    logger.warn("Things shouldn't be empty!")
  }

  // Catching things
  try {
    ...
  } catch(Exception e) {
    logger.error("Something bad happened")
  }

  logger.info("Completed "+...)
}

A log4j2-file defines a socket-appender, with a fail-over file-appender. And a console-appender. Sometimes I use log4j2 Markers when the situation requires it.

Thought an extra perspective might help.


As an addition, I think it's important to mean Simple Logging Facade for Java (SLF4J) (http://www.slf4j.org/). Due to some issues of using different logging frameworks in diversified parts of a big project, SLF4J is the de facto standard to solve a problem to manage these parts successfully, isn't it?

The second one notion: it's seems that some old-school tasks can be substituted by Aspect-Oriented-Programming, Spring frmwrk has it's own implementation, AOP-approach for logging considered here at StackOverflow and here on Spring blog.

참고URL : https://stackoverflow.com/questions/906233/logging-in-java-and-in-general-best-practices

반응형