2세대 Java 런타임으로 로깅 마이그레이션

2세대 런타임(Java 11+)의 로깅은 1세대 런타임(Java 8)과 다릅니다. 1세대 런타임과 달리 2세대 런타임에서는 앱 로그가 로그 탐색기 내의 요청 로그 아래에 그룹화되지 않습니다. 또한 2세대 런타임에서 App Engine은 요청과 관련된 각 앱 로그를 Cloud Logging의 개별 로그 항목으로 작성합니다.

이 가이드에서는 1세대 런타임 앱에서 사용한 것과 동일한 로깅 방법을 구현하고 앱을 Java 11+로 마이그레이션할 때 거의 동일한 필터링 및 로깅 상관관계 결과를 달성하는 방법을 설명합니다.

주요 차이점

다음 표에서는 1세대 런타임과 2세대 런타임 로깅 간의 차이점을 설명합니다.

1세대 런타임(Java 8) 2세대 런타임(Java 11+)
요청 및 앱 로그(애플리케이션 로그라고도 함) App Engine이 모든 앱 로그를 요청 로그 내에 삽입합니다. App Engine이 앱 로그를 요청 로그 내에 삽입하지 않습니다.
stdoutstderr App Engine이 stdoutstderr에 작성된 로그를 요청 로그 내에 삽입합니다. App Engine이 stdoutstderr에 작성된 로그를 삽입하지 않습니다.
App Engine 플랫폼 로그 App Engine이 내부 플랫폼 로그를 내보내지 않습니다. App Engine이 인스턴스를 시작하거나 종료할 때 /var/log/google_init.log 로그 이름을 사용하여 내부 플랫폼 로그를 내보냅니다.
Cloud Logging API 할당량 각 요청은 한 번의 Cloud Logging 쓰기 작업에 해당합니다. 요청과 연결된 각 앱 로그 항목은 한 번의 Cloud Logging 쓰기 작업에 해당합니다. Cloud Logging API 사용량은 2세대 런타임에서 증가합니다.

요청 로그 및 앱 로그

1세대 런타임과 2세대 런타임의 로깅 동작은 다음과 같은 점에서 차이가 있습니다.

  • 1세대 런타임에서 App Engine은 요청 로그의 protoPayload.line 필드에 앱 로그를 삽입합니다. 로그 탐색기에서 요청을 확장하여 앱 로그를 볼 수 있습니다.

    다음 이미지는 1세대 런타임에서 상관관계가 지정된 요청과 앱 로그를 보여줍니다.

    Java 8의 상관관계가 지정된 로그

  • 2세대 런타임에서는 protoPayload.line 필드가 요청 로그에 없기 때문에 요청 로그에 앱 로그 항목이 포함되지 않습니다. 대신 App Engine이 각 앱 로그를 개별 항목으로 로깅합니다. 로그 이름은 로깅 설정에 따라 달라집니다. var/log/app에는 java.util.logging 또는 Java용 단순 로깅 퍼사드(SLF4J)와 같은 표준 로깅 프레임워크를 사용하여 내보내는 로그가 포함됩니다. stderrstdout 로그에는 System.err.print() 또는 System.out.print()를 사용하여 로깅되는 앱 로그가 포함됩니다.

    다음 이미지는 2세대 런타임의 별도의 요청 로그와 앱 로그를 보여줍니다.

    Java 11의 개별 로그

stdoutstderr

1세대 런타임에서 App Engine은 stdoutstderr로 내보낸 로그를 앱 로그로 간주하고 이러한 앱 로그 항목을 연관된 요청 로그 아래에 자동으로 그룹화합니다.

2세대 런타임에서 App Engine은 기본적으로 stdoutstderr로 내보낸 로그를 연결하지 않습니다. stdoutstderr을 사용하여 앱 로그 항목을 요청 로그와 그룹화하려면 stdoutstderr에 구조화된 로그 작성의 안내를 따르세요.

특정 App Engine 플랫폼 로그(JVM, Jetty, 내부 인프라 로그)도 stderr로 내보내기 때문에 stdoutstderr에 로깅은 권장되지 않습니다. 앱 로그 및 플랫폼 로그는 stderr 로그 이름을 사용하여 표시되기 때문에 모호성이 발생합니다. App Engine에 더 많은 수의 플랫폼 로그가 생성되기 때문에 2세대 런타임에서 이 문제가 더 커집니다.

App Engine 플랫폼 로그

1세대 런타임에서 App Engine은 플랫폼 로그를 내보내지 않습니다.

2세대 런타임에서 App Engine은 인스턴스를 시작하거나 종료할 때 로그 이름 /var/log/google_init.log를 사용하여 플랫폼 로그를 내보냅니다.

플랫폼 로그는 무시해도 됩니다. 이러한 로그가 표시되지 않도록 하려면 logName="/var/log/google_init.log" 쿼리를 사용해서 로그 탐색기에 필터를 추가합니다.

다음 이미지는 2세대 런타임의 플랫폼 로그를 표시합니다.

플랫폼 로그

Cloud Logging API 할당량

1세대 런타임에서 App Engine은 단일 요청 로그에 요청을 수행하는 동안 내보낸 모든 앱 로그를 삽입하고 삽입된 요청을 Cloud Logging으로 전송합니다. 각 요청은 한 번의 Cloud Logging 쓰기 작업에 해당합니다.

2세대 런타임에서 App Engine은 각 앱 로그를 개별 로그 항목으로 Cloud Logging에 전송하여 요청당 총 Cloud Logging 쓰기 작업 수를 늘립니다.

Google Cloud는 Cloud Logging에 사용되는 전체 스토리지를 기반으로 청구를 결정합니다. 로그에 필요한 전체 스토리지가 크게 증가하지 않더라도 App Engine이 요청당 작성하는 앱 로그 수에 따라 분당 쓰기 작업이 증가합니다. 앱이 Cloud Logging 쓰기 할당량을 초과하면 사용 할당량의 증가를 요청할 수 있습니다.

자세한 내용은 Cloud Logging의 가격 책정을 참조하세요.

마이그레이션 프로세스 개요

앱 로그가 2세대 런타임의 요청 로그 내에 삽입되지 않은 경우에도 1세대 앱과 같은 로그 보기 환경을 유지할 수 있습니다.

2세대 런타임에서 로깅을 설정하기 위해 다음 옵션 중 하나를 선택할 수 있습니다.

java.util.logging(JUL) 패키지 사용

1세대 런타임을 사용하는 앱이 모든 로깅 요구사항에 대해 java.util.logging 패키지를 구현하는 경우 2세대 런타임으로 마이그레이션하기 위해서는 코드 변경이 필요하지 않습니다.

다음 샘플에서는 2세대 런타임에서 로깅을 위해 java.util.logging 패키지를 사용하는 방법을 보여줍니다.

    package com.example.appengine.my_app;
    import java.io.IOException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.*;
    import java.util.logging.Logger;

    @WebServlet(name = "HelloAppEngine", value = "/")
    public class HelloAppEngine extends HttpServlet {

      // Use the java.util.logging.Logger to log messages
      private static final Logger logger = Logger.getLogger(HelloAppEngine.class.getName());

      @Override
      public void doGet(HttpServletRequest request, HttpServletResponse response)
          throws IOException {
        // Sample log messages
        logger.info("Info message");
        logger.warning("Warning message");
        logger.severe("Severe message");

        response.setContentType("text/plain");
        response.getWriter().println("Hello App Engine");
      }
    }

요청 로그로 앱 로그 그룹화

2세대 런타임에서 요청 로그의 protoPayload.line 필드에는 앱 로그가 포함되지 않습니다. 로그 탐색기는 trace 필드를 사용해서 요청 로그 및 앱 로그를 그룹화합니다. java.util.logging 패키지는 모든 요청 및 앱 로그에 trace ID를 자동으로 추가합니다. 로그 탐색기에서 상관관계가 지정된 로그를 보려면 상관관계가 지정된 로그 보기를 참조하세요.

Java용 단순 로깅 퍼사드(SLF4J) 사용

SLF4J는 Java 애플리케이션에서 사용되는 가장 인기 있는 로깅 인터페이스입니다. 퍼사드인 SLF4J 인터페이스는 로깅 백엔드의 플랫폼과 독립적이며 앱에 적합한 백엔드를 선택할 수 있게 해줍니다.

다음 이미지는 java.util.logging 라이브러리 및 Logback 어펜더를 포함하여 2개의 SLF4J 백엔드에 대한 통합 옵션을 보여줍니다.

SLF4J 개요

java.util.logging 패키지를 사용하는 SLF4J

SLF4J를 사용하면 java.util.logging 로깅 백엔드와 함께 SLF4J를 사용할 때 앱을 Cloud Logging에 통합할 수 있습니다. 기본적으로 App Engine은 모든 요청 및 앱 로그에 trace ID를 추가합니다. 이 메커니즘을 사용하면 로그 탐색기에서 요청 및 앱 로그를 그룹화할 수 있습니다.

java.util.logging으로 SLF4J를 구현하려면 다음 단계를 수행합니다.

  1. slf4j-jdk14.jar 종속 항목을 pom.xml 파일에 추가합니다. slf4j-jdk14.jar 파일은 java.util.logging 라이브러리에 대한 백엔드 통합을 제공합니다.

    <!-- SLF4J interface -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>2.0.4</version>
    </dependency>
    <!-- JUL implementation for SLF4J -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-jdk14</artifactId>
      <version>2.0.9</version>
    </dependency>
    
  2. WEB-INF/logging.properties 파일을 만들어 커스텀 구성을 추가합니다.

    com.example.appengine.java8.HelloAppEngine.level = INFO
    
  3. WEB-INF/logging.properties에 대해 <property> 값을 포함하도록 appengine-web.xml 파일을 업데이트합니다.

    <system-properties>
      <property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/>
    </system-properties>
    

다음 이미지는 java.util.logging과 함께 SLF4J가 앱 로그에 trace ID를 자동으로 추가하는 방법을 보여줍니다.

trace ID를 사용하여 요청별로 앱 로그 그룹화

1세대 런타임과 같이 로그 탐색기 보기를 요청 중심의 보기로 조정하려면 상관관계가 지정된 로그 보기를 참조하세요.

Logback 어펜더와 함께 SLF4J 사용

1세대 런타임 앱에 Logback과 함께 SLF4J에 대한 기본 제공 구현이 사용되는 경우 소스 코드를 업데이트하고 요청 및 앱 로그를 그룹화하기 위한 추가 단계를 구현해야 합니다.

앱을 Cloud Logging에 통합하려면 다음 단계를 수행합니다.

  1. pom.xml 파일에 google-cloud-logging-logback 어펜더를 추가하여 Logback에 대한 로깅 어펜더를 설치합니다.

     <!-- SLF4J interface -->
     <dependency>
           <groupId>org.slf4j</groupId>
           <artifactId>slf4j-api</artifactId>
           <version>2.0.4</version>
     </dependency>
    
     <!-- Logback JARs -->
     <dependency>
           <groupId>ch.qos.logback</groupId>
           <artifactId>logback-classic</artifactId>
           <version>1.3.6</version>
     </dependency>
     <dependency>
           <groupId>ch.qos.logback</groupId>
           <artifactId>logback-core</artifactId>
           <version>1.3.5</version>
     </dependency>
    
     <!-- Google Cloud logging appender for Logback -->
     <dependency>
       <groupId>com.google.cloud</groupId>
       <artifactId>google-cloud-logging-logback</artifactId>
     </dependency>
    
  2. LogEntry 필드에 trace ID를 추가하기 위해 로깅 개선 도구를 만듭니다. 다음 TraceIdLoggingEnhancer 클래스는 ApiProxy API를 사용하여 요청과 연결된 trace ID를 검색합니다.

      import com.google.appengine.api.utils.SystemProperty;
      import com.google.cloud.logging.LogEntry;
      import com.google.cloud.logging.LoggingEnhancer;
    
      import com.google.apphosting.api.ApiProxy;
      import com.google.apphosting.api.ApiProxy.Environment;
    
      // Add trace ID to the log entry
      public class TraceIdLoggingEnhancer implements LoggingEnhancer {
    
        @Override
        public void enhanceLogEntry(LogEntry.Builder logEntry) {
          final String PROJECT_ID = SystemProperty.applicationId.get();
    
          Environment environment = ApiProxy.getCurrentEnvironment();
    
          if (environment instanceof ApiProxy.EnvironmentWithTrace) {
            ApiProxy.EnvironmentWithTrace environmentWithTrace = (ApiProxy.EnvironmentWithTrace) environment;
            environmentWithTrace
                .getTraceId()
                .ifPresent(
                    id ->
                      logEntry.setTrace(String.format("projects/%s/traces/%s", PROJECT_ID, id)));
          }
        }
      }
      // [END logging_enhancer]
    
  3. logback.xml 파일의 Cloud Logging 어펜더를 추가하여 Logback을 구성합니다. Logback 프레임워크는 WEB-INF/classes에서 logback.xml 파일을 통해 구성을 관리합니다.

    <configuration>
      <appender name="CLOUD" class="com.google.cloud.logging.logback.LoggingAppender">
          <!-- This should be set to the new Logging Enhancer in the app code. -->
          <enhancer>com.example.appengine.my_app.enhancers.TraceIdLoggingEnhancer</enhancer>
          <resourceType>gae_app</resourceType>
      </appender>
      <root level="info">
          <appender-ref ref="CLOUD" />
      </root>
    </configuration>
    

    다음 이미지는 최종 앱 디렉터리 구조를 보여줍니다.

    디렉터리 구조

상관관계가 지정된 로그 보기

로그 탐색기에서 각 로그 항목의 trace ID 필드를 사용하여 상관관계가 지정된 로그를 볼 수 있습니다. 로그 탐색기에서 상관관계가 지정된 로그를 보려면 다음 안내를 따르세요.

  1. Google Cloud 콘솔의 탐색 패널에서 Logging을 선택한 후 로그 탐색기를 선택합니다.

    로그 탐색기로 이동

  2. 리소스 유형에서 GAE 애플리케이션을 선택합니다.

  3. 요청 로그를 보고 상관관계를 지정하려면 로그 이름에서 request_log를 선택합니다. 또는 요청 로그별로 상관관계를 지정하려면 상관관계 지정 기준을 클릭하고 request_log를 선택합니다.

    로그 상관관계 지정

  4. 쿼리 결과 창에서 로그 항목을 펼치려면 펼치기를 클릭합니다. 펼치면 각 요청 로그에 연결된 앱 로그가 표시됩니다.