12

私は GCP AppEngine を初めて使用し、いくつかの理由からフレキシブル環境を選択しました。しかし、フレキシブル環境の「互換性のない」ランタイムでは、アプリのロギング イベントをクラウド ロギングの適切なログレベルにマッピングできないように見えることにショックを受けました。私はこれを正しく読んでいますか? https://cloud.google.com/appengine/docs/flexible/java/writing-application-logs#writing_application_logs_1

そして、このページは本当に役に立ちませんでした。https://cloud.google.com/java/getting-started/logging-application-events

これは、GAE ロギングの問題を数時間読み、スタンダード環境とフレキシブル環境にどちらが適用されるかを判断しようとした結果です。私が知る限り、標準環境ではイベント レベルのマッピングが可能です。

ただし、Cloud Platform Console でのログ レベルの表示をより細かく制御するには、ロギング フレームワークで java.util.logging アダプタを使用する必要があります。https://cloud.google.com/appengine/docs/java/how-requests-are-handled#Java_Logging

わかった。それは漠然とした参照ですが、どこかでもっと明確なものを見たと思います。

いずれにせよ、これは「柔軟な」環境ではより簡単であるべきではありませんか? ロギング レベルで簡単にイベントをフィルタリングしたくない人はいますか?

更新: GAE フレキシブル環境で互換性のないランタイムについて尋ねていることを示すために、質問を明確にしました。

4

2 に答える 2

7

SLF4J を使用してクラウド ロギングを機能させる方法を次に示します。これは、互換性のない Java GAE Flex 環境で機能します。

logback.xml

<configuration debug="true">
    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <file>/var/log/app_engine/custom_logs/app.log.json</file>
        <append>true</append>
        <!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout
                class="putyourpackagenamehere.GCPCloudLoggingJSONLayout">
                <pattern>%-4relative [%thread] %-5level %logger{35} - %msg</pattern>
            </layout>
        </encoder>
    </appender>
    <root level="DEBUG">
        <appender-ref ref="FILE" />
    </root>
</configuration>

以下は、ログ ファイルの 1 行で JSON を生成するために使用した PatternLayout クラスです。

import static ch.qos.logback.classic.Level.DEBUG_INT;
import static ch.qos.logback.classic.Level.ERROR_INT;
import static ch.qos.logback.classic.Level.INFO_INT;
import static ch.qos.logback.classic.Level.TRACE_INT;
import static ch.qos.logback.classic.Level.WARN_INT;

import java.util.Map;

import org.json.JSONObject;

import com.homedepot.ta.wh.common.logging.GCPCloudLoggingJSONLayout.GCPCloudLoggingEvent.GCPCloudLoggingTimestamp;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.PatternLayout;
import ch.qos.logback.classic.spi.ILoggingEvent;

/**
 * Format a LoggingEvent as a single line JSON object  
 * 
 *  <br>https://cloud.google.com/appengine/docs/flexible/java/writing-application-logs
 *  
 *  <br>From https://cloud.google.com/appengine/articles/logging
 *  <quote>
 *  Applications using the flexible environment should write custom log files to the VM's log directory at 
 *  /var/log/app_engine/custom_logs. These files are automatically collected and made available in the Logs Viewer. 
 *  Custom log files must have the suffix .log or .log.json. If the suffix is .log.json, the logs must be in JSON 
 *  format with one JSON object per line. If the suffix is .log, log entries are treated as plain text.
 *  </quote>
 *  
 *  Nathan: I can't find a reference to this format on the google pages but I do remember getting the format from some
 *  GO code that a googler on the community slack channel referred me to.   
 */
public class GCPCloudLoggingJSONLayout extends PatternLayout {

    @Override
    public String doLayout(ILoggingEvent event) {
        String formattedMessage = super.doLayout(event);
        return doLayout_internal(formattedMessage, event);
    }

    /* for testing without having to deal wth the complexity of super.doLayout() 
     * Uses formattedMessage instead of event.getMessage() */
    String doLayout_internal(String formattedMessage, ILoggingEvent event) {
        GCPCloudLoggingEvent gcpLogEvent = new GCPCloudLoggingEvent(formattedMessage
                                                                    , convertTimestampToGCPLogTimestamp(event.getTimeStamp())
                                                                    , mapLevelToGCPLevel(event.getLevel())
                                                                    , null);
        JSONObject jsonObj = new JSONObject(gcpLogEvent);
        /* Add a newline so that each JSON log entry is on its own line.
         * Note that it is also important that the JSON log entry does not span multiple lines.
         */
        return jsonObj.toString() + "\n"; 
    }

    static GCPCloudLoggingTimestamp convertTimestampToGCPLogTimestamp(long millisSinceEpoch) {
        int nanos = ((int) (millisSinceEpoch % 1000)) * 1_000_000; // strip out just the milliseconds and convert to nanoseconds
        long seconds = millisSinceEpoch / 1000L; // remove the milliseconds
        return new GCPCloudLoggingTimestamp(seconds, nanos);
    }

    static String mapLevelToGCPLevel(Level level) {
        switch (level.toInt()) {
        case TRACE_INT:
            return "TRACE";
        case DEBUG_INT:
            return "DEBUG";
        case INFO_INT:
            return "INFO";
        case WARN_INT:
            return "WARN";
        case ERROR_INT:
            return "ERROR";
        default:
            return null; /* This should map to no level in GCP Cloud Logging */
        }
    }

    /* Must be public for JSON marshalling logic */
    public static class GCPCloudLoggingEvent {
        private String message;
        private GCPCloudLoggingTimestamp timestamp;
        private String traceId;
        private String severity;

        public GCPCloudLoggingEvent(String message, GCPCloudLoggingTimestamp timestamp, String severity,
                String traceId) {
            super();
            this.message = message;
            this.timestamp = timestamp;
            this.traceId = traceId;
            this.severity = severity;
        }

        public String getMessage() {
            return message;
        }

        public void setMessage(String message) {
            this.message = message;
        }

        public GCPCloudLoggingTimestamp getTimestamp() {
            return timestamp;
        }

        public void setTimestamp(GCPCloudLoggingTimestamp timestamp) {
            this.timestamp = timestamp;
        }

        public String getTraceId() {
            return traceId;
        }

        public void setTraceId(String traceId) {
            this.traceId = traceId;
        }

        public String getSeverity() {
            return severity;
        }

        public void setSeverity(String severity) {
            this.severity = severity;
        }

        /* Must be public for JSON marshalling logic */
        public static class GCPCloudLoggingTimestamp {
            private long seconds;
            private int nanos;

            public GCPCloudLoggingTimestamp(long seconds, int nanos) {
                super();
                this.seconds = seconds;
                this.nanos = nanos;
            }

            public long getSeconds() {
                return seconds;
            }

            public void setSeconds(long seconds) {
                this.seconds = seconds;
            }

            public int getNanos() {
                return nanos;
            }

            public void setNanos(int nanos) {
                this.nanos = nanos;
            }

        }       
    }

    @Override
    public Map<String, String> getDefaultConverterMap() {
        return PatternLayout.defaultConverterMap;
    }   
}
于 2016-09-29T20:43:20.603 に答える
1

java.util.logging によって提供されるログ レベルは、Cloud Logging の適切なログ レベルにマッピングされます。フレキシブル ランタイムでのロギングは、基本的に Standard でのロギングと同じように機能します。

編集:「アプリケーション ログの書き込み 」ページの根拠は、Cloud Logging のマッピングがすべてのランタイムで機能しないことです。ただし、現在、少なくとも「-compat」ランタイムと Java カスタム ランタイムでは機能しているようです。その他の回避策は、ドキュメントの別の場所で提供されています (以下を参照)。

Java アプリケーションでの推奨されるデフォルトのロギング方法は、java.util.loggingを使用することです(Python の場合は「logging」モジュール、Go の場合は「log」パッケージです。これらはすべて、Cloud Logging レベルにマッピングされるログレベルを提供します) )。これらのページの更新をリクエストします。

Javaのロギングに関する正確な情報を提供するためにリンクした他のドキュメント。あなたが引用したセクションに関して、それが引き出された完全な段落は文脈を提供します。stderr または stdout に書き込む任意のロギング フレームワークが機能すると言っていますが、「INFO」または「WARNING」以外のより詳細なログ レベルが必要な場合は、「java.util.logging」を使用する必要があります。「java.util.logging」を使用するための完全なコード サンプルは、引用されたセクションのすぐ下に提供されています。その他のコードは、言及した別のドキュメント「Logging Application Events with Java」で提供されています。

更新: 「はじめに」ガイドには、各ランタイムのログを構成する方法に関する具体的な詳細が含まれています。

Java
https://cloud.google.com/java/getting-started/logging-application-events#understanding_the_code

Python
https://cloud.google.com/python/getting-started/logging-application-events#understanding_the_code

https://cloud.google.com/go/getting-started/logging-application-events に移動

NodeJS
https://cloud.google.com/nodejs/getting-started/logging-application-events#understanding_the_code

ルビー
https://cloud.google.com/ruby/getting-started/logging-application-events#application_structure

PHP
https://cloud.google.com/php/getting-started/logging-application-events

于 2016-06-25T19:30:39.150 に答える