Как мне сопоставить события регистрации моего java-приложения с соответствующими уровнями событий регистрации в облаке в GCP Fe flex-a non-compat App Engine?

Я новичок в GCP AppEngine и выбрал гибкую среду по нескольким причинам. Однако я потрясен, обнаружив, что несовместимые среды выполнения гибкой среды, по-видимому, не позволяют мне сопоставлять события регистрации моего приложения с соответствующими уровнями журнала в облачной регистрации. Я правильно это читаю? https://cloud.google.com/appengine/docs/flexible/java/writing-application-logs

И эта страница была действительно бесполезной. https://cloud.google.com/java/getting-started/logging-application-events

Это происходит после нескольких часов чтения проблем журналирования GAE и попытки определить, какие из них применимы к стандартной среде и гибкой. Насколько я могу судить, отображение уровня событий возможно в стандартной среде.

Однако для более детального управления отображением уровня журнала в консоли Cloud Platform в среде ведения журнала должен использоваться адаптер java.util.logging. https://cloud.google.com/appengine/docs/java/how-requests-are-handled

ХОРОШО. Это расплывчатая ссылка, но я думаю, что где-то еще увидел нечто более ясное.

В любом случае, разве это не должно быть проще в "гибкой" среде? Кто не хочет легко фильтровать события по уровням регистрации?

Обновление: я уточнил вопрос, чтобы указать, что я спрашиваю о несовместимых средах выполнения в гибкой среде GAE.

2 ответа

Решение

Вот как я получил облачную регистрацию для работы с использованием 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>

Вот класс PatternLayout, который я использовал для создания JSON в одной строке в файле журнала.

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;
    }   
}

Уровни журналов, предоставляемые java.util.logging, будут сопоставлены с соответствующими уровнями журналов в Cloud Logging. Вход в систему Flexible Runtime по существу работает так же, как и в Standard.

Редактирование. Похоже, что обоснование для страницы " Запись журналов приложений" состоит в том, что сопоставления ведения журнала в облаке не работают для всех сред выполнения. Однако в настоящее время они, по-видимому, работают по крайней мере для сред выполнения '-compat' и пользовательских сред выполнения Java. Обходные пути для других предусмотрены в других документах (см. Ниже):

Рекомендуемый метод регистрации по умолчанию в приложении Java - использовать java.util.logging (для Python это модуль 'logging', а для Go это пакет 'log', каждый из которых предоставляет уровни журнала, которые отображаются на уровни Cloud Logging). Я попрошу, чтобы эти страницы были обновлены.

Другие документы, на которые вы ссылаетесь, предоставляют точную информацию о ведении журнала для Java. Что касается цитируемого вами раздела, то полный абзац, из которого он был извлечен, предоставляет контекст. Это говорит о том, что любая инфраструктура журналирования, которая пишет в stderr или stdout, будет работать, но ей нужно использовать java.util.logging, если вы хотите более детализированные уровни журналов, кроме "INFO" или "WARNING". Полный пример кода для использования "java.util.logging" приведен непосредственно под цитируемым разделом, а другие приведены в другом упомянутом вами документе " Регистрация событий приложения с Java".

Обновление. Руководства "Приступая к работе" содержат конкретные сведения о настройке ведения журнала для каждой среды выполнения:

Джава
https://cloud.google.com/java/getting-started/logging-application-events

питон
https://cloud.google.com/python/getting-started/logging-application-events

Идти
https://cloud.google.com/go/getting-started/logging-application-events

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

Рубин
https://cloud.google.com/ruby/getting-started/logging-application-events

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

Другие вопросы по тегам