问题描述
我是GCP AppEngine的新手,我选择灵活的环境有几个原因。然而,我很震惊地发现灵活环境的非兼容运行时似乎不允许将我的应用程序的日志记录事件映射到云日志中适当的日志级别。我正确阅读这个吗?
这个页面确实没有帮助。
这是在阅读GAE日志记录困难并试图确定应用于标准环境与灵活性的几个小时之后。最好的我可以告诉,事件级映射在标准环境中是可能的。然而,为了更精细地控制
Cloud Platform Console中的日志级别显示,日志框架必须使用一个
java.util.logging适配器。
好的。这是一个模糊的参考,但我认为我在其他地方看到了更清晰的东西。无论如何,在灵活的环境中,这不应该更容易吗?谁不想通过日志记录级别轻松过滤事件?
更新:我澄清了问题以表明我询问了非在GAE灵活环境中兼容运行时间。
以下是我如何使用SLF4J进行云日志记录工作。这适用于不兼容的Java GAE Flex环境。
logback.xml
< configuration debug =true>
< appender name =FILEclass =ch.qos.logback.core.FileAppender>
< file> /var/log/app_engine/custom_logs/app.log.json< / file>
< append> true< / append>
< encoder class =ch.qos.logback.core.encoder.LayoutWrappingEncoder>
< layout
class =putyourpackagenamehere.GCPCloudLoggingJSONLayout>
< pattern>% - 4relative [%thread]%-5level%logger {35} - %msg< / pattern>
< / layout>
< /编码器>
< / appender>
< appender-ref ref =FILE/>
< / root>
< / configuration>
以下是我用来在日志文件中的单行生成JSON的PatternLayout类。 / p>
import static ch.qos.logback.classic.Level.DEBUG_INT;
导入静态ch.qos.logback.classic.Level.ERROR_INT;
导入静态ch.qos.logback.classic.Level.INFO_INT;
导入静态ch.qos.logback.classic.Level.TRACE_INT;
导入静态ch.qos.logback.classic.Level.WARN_INT;
import java.util.Map;
import org.json.JSONObject;
导入com.homedepot.ta.wh.common.logging.GCPCloudLoggingJSONLayout.GCPCloudLoggingEvent.GCPCloudLoggingTimestamp;
导入ch.qos.logback.classic.Level;
导入ch.qos.logback.classic.PatternLayout;
导入ch.qos.logback.classic.spi.ILoggingEvent;
$ b $ / **
*将LoggingEvent格式化为单行JSON对象
*
*< br> https://cloud.google.com/appengine / docs / flexible / java / writing-application-logs
*
*< br>从https://cloud.google.com/appengine/articles/logging
*< quote> ;
*使用灵活环境的应用程序应将自定义日志文件写入
* / var / log / app_engine / custom_logs的VM日志目录。这些文件将自动收集并在日志查看器中显示。
*自定义日志文件必须具有后缀.log或.log.json。如果后缀为.log.json,则日志必须为JSON
*格式,每行一个JSON对象。如果后缀是.log,则日志条目将被视为纯文本。
*< / quote>
*
* Nathan:我在google的网页上找不到这种格式的引用,但是我确实记得从一些
* GO代码中得到一个社区闲人频道的googler的格式提到我。
* /
public class GCPCloudLoggingJSONLayout extends PatternLayout {
$ b $ @Override
public String doLayout(ILoggingEvent event){
String formattedMessage = super.doLayout(event );
返回doLayout_internal(formattedMessage,event);
$ b $ *用于测试,而不必处理super.doLayout()
的复杂性*使用formattedMessage而不是event.getMessage()* /
字符串doLayout_internal(String formattedMessage,ILoggingEvent事件){
GCPCloudLoggingEvent gcpLogEvent = new GCPCloudLoggingEvent(formattedMessage
,convertTimestampToGCPLogTimestamp(event.getTimeStamp())
,mapLevelToGCPLevel(event.getLevel())
,null);
JSONObject jsonObj = new JSONObject(gcpLogEvent);
/ *添加一个换行符,以便每个JSON日志条目都在它自己的行上。
*请注意,JSON日志条目不能跨越多行也很重要。
* /
返回jsonObj.toString()+\\\
;
static GCPCloudLoggingTimestamp convertTimestampToGCPLogTimestamp(long millisSinceEpoch){
int nanos =((int)(millisSinceEpoch%1000))* 1_000_000; //仅消除毫秒并转换为纳秒
long seconds = millisSinceEpoch / 1000L; //删除毫秒
返回新的GCPCloudLoggingTimestamp(秒,毫微秒);
static String mapLevelToGCPLevel(Level level){
switch(level.toInt()){
case TRACE_INT:
returnTRACE;
case DEBUG_INT:
返回DEBUG;
case INFO_INT:
返回INFO;
case WARN_INT:
返回WARN;
case ERROR_INT:
返回ERROR;
默认值:
返回null; / *这应该映射到GCP Cloud Logging中的无级别* /
}
}
/ *必须公开JSON编组逻辑* /
public static class GCPCloudLoggingEvent {
private String message;
private GCPCloudLoggingTimestamp timestamp;
私人字符串traceId;
私人字符串严重性;
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;
}
/ *必须为JSON编组逻辑公开* /
public static class GCPCloudLoggingTimestamp {
private long seconds;
private int nanos;
public GCPCloudLoggingTimestamp(long seconds,int nanos){
super();
this.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;
}
}
}
@覆盖
public Map< String,String> getDefaultConverterMap(){
返回PatternLayout.defaultConverterMap;
}
}
I am new to GCP AppEngine and I chose the Flexible environment for several reasons. However, I am shocked to find out that the flexible environment's non-"compatible" runtimes appear to not allow me to map my app's logging events to the appropriate log levels in cloud logging. Am I reading this correctly?https://cloud.google.com/appengine/docs/flexible/java/writing-application-logs#writing_application_logs_1
And this page was really unhelpful. https://cloud.google.com/java/getting-started/logging-application-events
This is after several hours of reading GAE logging woes and trying to determine which applied to the Standard environment vs. Flexible. Best I can tell, event level mapping is possible in the standard environment.
OK. That's a vague reference, but I think I saw something more clear somewhere else.
Regardless, shouldn't this be easier in the "flexible" environment? Who doesn't want to easily filter events by Logging levels?
Update: I clarified the question to indicate that I am asking about the non-compatible runtimes in the GAE flexible environment.
Here is how I got cloud logging to work using SLF4J. This works on a non-compatible Java GAE Flex environment.
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>
Here is the PatternLayout class I used to produce the JSON on a single line in the log file.
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应用程序日志记录事件映射到GCP非灵活非兼容App Engine中相应的云日志记录事件级别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!