问题描述
我是 GCP AppEngine 的新手,出于多种原因我选择了柔性环境.然而,我震惊地发现灵活环境的非兼容"运行时似乎不允许我将我的应用程序的日志事件映射到云日志记录中的适当日志级别.我读对了吗?https://cloud.google.com/appengine/docs/flexible/java/writing-application-logs#writing_application_logs_1
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
这个页面真的没有帮助.https://cloud.google.com/java/getting-started/logging-应用程序事件
And this page was really unhelpful. https://cloud.google.com/java/getting-started/logging-application-events
这是在阅读 GAE 日志记录问题并试图确定适用于标准环境与灵活环境的几个小时之后.据我所知,事件级别映射在标准环境中是可能的.
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.
然而,为了更细粒度地控制日志级别显示Cloud Platform Console,日志框架必须使用java.util.logging 适配器.https://cloud.google.com/appengine/docs/java/how-requests-are-handled#Java_Logging
好的.这是一个模糊的参考,但我想我在其他地方看到了更清楚的东西.
OK. That's a vague reference, but I think I saw something more clear somewhere else.
无论如何,这在灵活"的环境中不是应该更容易吗?谁不想通过 Logging 级别轻松过滤事件?
Regardless, shouldn't this be easier in the "flexible" environment? Who doesn't want to easily filter events by Logging levels?
更新:我澄清了这个问题,表明我问的是 GAE 灵活环境中的不兼容运行时.
Update: I clarified the question to indicate that I am asking about the non-compatible runtimes in the GAE flexible environment.
推荐答案
这是我如何使用 SLF4J 使云日志记录工作.这适用于不兼容的 Java GAE Flex 环境.
Here is how I got cloud logging to work using SLF4J. This works on a non-compatible Java GAE Flex environment.
logback.xml
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>
这是我用来在日志文件的一行中生成 JSON 的 PatternLayout 类.
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() + "
";
}
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 Felexible 非兼容 App Engine 中相应的云日志事件级别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!