7

Ich bin neu bei GCP AppEngine und habe die Flexible Umgebung aus verschiedenen Gründen gewählt. Ich bin jedoch schockiert, wenn ich feststelle, dass die nicht kompatiblen Laufzeiten der flexiblen Umgebung es mir nicht erlauben, die Protokollierungsereignisse meiner App dem Protokoll in der Cloud-Protokollierung zuzuordnen. Lies ich das richtig? https://cloud.google.com/appengine/docs/flexible/java/writing-application-logs#writing_application_logs_1Wie ordne ich meine Java-Anwendungsprotokollierungsereignisse den entsprechenden Cloud-Protokollierungsereignissebenen in GCP Feexible nicht kompatiblem App Engine zu?

Und diese Seite war wirklich nicht hilfreich. https://cloud.google.com/java/getting-started/logging-application-events

Dies ist nach mehreren Stunden des Lesens GAE Logging Leiden und versuchen zu bestimmen, welche auf die Standard-Umgebung vs Flexible angewendet. Am besten kann ich sagen, Event-Level-Mapping ist in der Standardumgebung möglich.

jedoch für feinere Kontrolle über die Log-Level-Anzeige in der Cloud Platform-Konsole muss die Logging-Framework ein java.util.logging Adapter verwenden. https://cloud.google.com/appengine/docs/java/how-requests-are-handled#Java_Logging

OK. Das ist eine vage Andeutung, aber ich glaube, ich habe woanders etwas klarer gesehen.

Egal, sollte das in der "flexiblen" Umgebung nicht einfacher sein? Wer möchte Events nicht einfach nach Logging-Levels filtern?

Update: Ich klärte die Frage, um anzuzeigen, dass ich über die nicht kompatiblen Laufzeiten in der flexiblen GAE-Umgebung frage.

Antwort

4

Hier ist, wie ich Cloud-Protokollierung mit SLF4J funktioniert. Dies funktioniert auf einer nicht kompatiblen Java GAE Flex-Umgebung.

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> 

ist die Pattern Klasse I auf einer einzigen Zeile in der Log-Datei, die die JSON produzieren verwendet.

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

Danke für das Posten - löste ein großes Problem für mich. Irgendein Gedanke, dies auf GitHub zu setzen, damit wir es verbessern können? Eine Sache, die nicht funktioniert, besteht darin, alle Protokolle für eine einzelne Anforderung in einer einzigen Gruppe zusammenzufassen, so wie GAE classic funktioniert. – sappenin

0

Die von java.util.logging bereitgestellten Protokollebenen werden den entsprechenden Protokollebenen in Cloud Logging zugeordnet. Anmelden Flexible Laufzeiten funktionieren im Wesentlichen genauso wie bei Standard.

bearbeiten: Es scheint, die Begründung für die Seite ‚Writing Application Logs‘, dass die Logging-Mappings Cloud ist für alle der Runtimes nicht funktionieren. Momentan scheinen sie jedoch zumindest für die Laufzeiten und die benutzerdefinierten Laufzeiten von Java zu funktionieren. Problemumgehungen für andere finden Sie an anderer Stelle in der Dokumentation (siehe unten):

Die empfohlene Standardmethode für die Anmeldung in einer Java-Anwendung ist java.util.logging (für Python ist es das Protokollierungsmodul und für Go ist es das Protokoll Paket, die alle Protokollebenen bereitstellen, die den Cloud Logging-Ebenen zugeordnet sind). Ich möchte, dass diese Seiten aktualisiert werden.

Die anderen Dokumente, die Sie verlinkt haben, liefern genaue Informationen zur Protokollierung für Java. In Bezug auf den Abschnitt, den Sie zitiert haben, bietet der full paragraph it was pulled from Kontext. Es wird gesagt, dass jedes Logging-Framework, das in stderr oder stdout schreibt, funktioniert, aber es muss "java.util.logging" verwenden, wenn Sie feingranularere Log-Level als "INFO" oder "WARNING" wünschen. Ein vollständiges Codebeispiel für die Verwendung von 'java.util.logging' befindet sich direkt unter dem zitierten Abschnitt und andere sind in dem anderen Dokument enthalten, das Sie erwähnten, 'Logging Application Events with Java'.

aktualisieren: Die 'Erste Schritte' Führer enthalten spezifische Informationen, wie für jede Laufzeit konfigurieren Protokollierung:

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

Zum
https://cloud.google.com/go/getting-started/logging-application-events

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

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

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

+1

Haben Sie das versucht? Ich glaube, ich habe es getan und es hat nicht für mich funktioniert. IIRC, Protokollebenen wurden überhaupt nicht zugeordnet, und Protokollereignisse mit mehreren Zeilen wurden nicht zusammengehalten. Am Ende habe ich einen Log-Event-Formatierer (ich benutzte SLF4J) verwendet, um meine Log-Ereignisse als JSON-Dokument mit einer einzigen Zeile zu formatieren, das nicht formal spezifiziert wurde. Ich habe das Format in einem GO-Code gefunden, auf den mich jemand in der GCP-Slack-Community hingewiesen hat. – successhawk

+0

Ich habe dies auf den Laufzeiten java-compat, jetty9-compat und python-compat-multicore mit 'runtime: custom' in app.yaml getestet. Die Standardlaufzeitumgebung 'runtime: java' in app.yaml wählt die Option '[Java 8/Jetty 9.3 Runtime] (https://cloud.google.com/appengine/docs/flexible/java/configuring-your-app-with- app-yaml), die wahrscheinlich nicht über den Cloud-Logging-Connector verfügt. – Adam

+0

Danke, Adam. Ich habe meine Frage geklärt, um darauf hinzuweisen, dass ich die "kompatiblen" Laufzeiten nicht verwende. Ich wusste nicht, dass es einen Unterschied in Bezug auf die Protokollierung geben würde, basierend auf der gesamten Dokumentation, die ich gesehen habe. – successhawk