7

Sono nuovo di GCP AppEngine e ho scelto l'ambiente flessibile per diversi motivi. Tuttavia, sono scioccato nello scoprire che i runtime non compatibili "flessibili dell'ambiente non sembrano consentirmi di mappare gli eventi di registrazione della mia app ai livelli appropriati del registro nella registrazione del cloud. Sto leggendo questo correttamente? https://cloud.google.com/appengine/docs/flexible/java/writing-application-logs#writing_application_logs_1Come si associano gli eventi di registrazione delle app java ai corrispondenti livelli di eventi di registrazione cloud in GCP Felexible App Engine non compatibile?

E questa pagina non è stata molto utile. https://cloud.google.com/java/getting-started/logging-application-events

Questo è dopo diverse ore di lettura guai di registrazione GAE e cercando di determinare quale applicato all'ambiente Standard rispetto Flessibile. La cosa migliore che posso dire è che la mappatura del livello degli eventi è possibile nell'ambiente standard.

Tuttavia, per un controllo più capillare del display livello di log della Console di Cloud Platform , il quadro di registrazione deve utilizzare un adattatore java.util.logging. https://cloud.google.com/appengine/docs/java/how-requests-are-handled#Java_Logging

OK. Questo è un riferimento vago, ma penso di aver visto qualcosa di più chiaro da qualche altra parte.

Indipendentemente da ciò, non dovrebbe essere più semplice nell'ambiente "flessibile"? Chi non desidera filtrare facilmente gli eventi tramite i livelli di registrazione?

Aggiornamento: Ho chiarito la domanda per indicare che sto chiedendo i runtime non compatibili nell'ambiente flessibile GAE.

risposta

4

Ecco come è stato possibile eseguire il cloud logging utilizzando SLF4J. Funziona su un ambiente Java GAE Flex non compatibile.

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> 

Ecco la classe PatternLayout ho usato per produrre il JSON su una sola riga nel file di registro.

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

Grazie per aver postato questo - ho risolto un grosso problema per me. Qualche idea di metterlo su GitHub in modo che possiamo migliorarlo? Una cosa che non funziona è il collasso di tutti i registri per una singola richiesta in un singolo gruppo, il modo in cui funziona GAE classic. – sappenin

0

I livelli di registro forniti da java.util.logging verranno associati ai livelli di registro appropriati in Cloud Logging. L'accesso a runtime flessibili funziona essenzialmente come su Standard.

Edit: Sembra il razionale per l' 'Writing Application Logs' pagina è che i mapping di registrazione cloud non funzionano per tutti i tempi di esecuzione. Tuttavia, sembrano funzionare attualmente almeno per i runtime "-compat" e i runtime personalizzati di Java. Soluzioni alternative per gli altri sono fornite altrove nella documentazione (vedi sotto):

Il metodo di registrazione predefinito in un'applicazione Java consigliato è l'utilizzo di java.util.logging (per Python è il modulo "registrazione" e per Go è il "registro" pacchetto, che fornisce tutti i livelli di log che si associano ai livelli di Cloud Logging). Chiederò che queste pagine vengano aggiornate.

Gli altri documenti a cui è stato collegato forniscono informazioni accurate sulla registrazione per Java. Per quanto riguarda la sezione che hai citato, lo full paragraph it was pulled from fornisce il contesto. Sta dicendo che qualsiasi framework di registrazione che scriva su stderr o stdout funzionerà, ma è necessario utilizzare 'java.util.logging' se si desidera più livelli di log a grana fine diversi da 'INFO' o 'AVVISO'. Un esempio di codice completo per l'utilizzo di "java.util.logging" viene fornito direttamente sotto la sezione citata, mentre altri vengono forniti sull'altro documento che hai citato, "Logging Application Events with Java".

Aggiornamento: Le guide 'Getting Started' contengono informazioni specifiche su come configurare la registrazione per ogni fase di esecuzione:

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

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

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

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

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

+1

Hai provato questo? Credo di averlo fatto e non ha funzionato per me. IIRC, i livelli di log non sono stati mappati e gli eventi di log su più righe non sono stati mantenuti insieme. Ho finito per utilizzare un formattatore di eventi di log (ho usato SLF4J) per formattare i miei eventi di registro come un documento JSON a linea singola, che non è stato formalmente specificato. Ho trovato il formato in qualche codice GO che mi ha indicato un utente della comunità di GCP. – successhawk

+0

Ho provato questo sui runtime java-compat, jetty9-compat e python-compat-multicore usando 'runtime: custom' in app.yaml. Il runtime predefinito 'runtime: java' in app.yaml seleziona '[Java 8/Jetty 9.3 Runtime] (https://cloud.google.com/appengine/docs/flexible/java/configuring-your-app-with- app-yaml) "che probabilmente non ha il connettore di registrazione cloud. – Adam

+0

Grazie, Adam. Ho chiarito la mia domanda per sottolineare che non sto utilizzando i runtime "compatibili". Non avevo capito che ci sarebbe stata una differenza relativa alla registrazione basata su tutta la documentazione che ho visto. – successhawk

Problemi correlati