如何在云端使用 Stackdriver 日志 运行

How to use Stackdriver logging on Cloud Run

我正在尝试让 stackdriver 日志记录在 Google 云 运行(完全托管)中为一个简单的 Go 应用程序工作 运行ning,但在中看不到 stackdriver 条目云运行 日志。

我创建了simplest possible demo app based on "official" stackdriver golang example

云 运行 文档 states 不应执行其他操作来写入 stackdriver 日志

我的服务使用默认服务帐户

我正在使用 Go 1.13 编译代码(Dockerfile 是从 Cloud Run example “按原样”复制的)

我尝试将它部署到不同的区域,但没有成功

当运行在本地使用服务帐户凭证在本地连接容器时,stackdriver 日志消息不会出现在本地终端或 stackdriver 控制台中

无论如何,在应用程序启动时我只看到 "Before stackdriver logging",然后是 "After stackdriver logging",中间没有其他 messages\errors

这是部分日志记录代码(使用上面的 link 获取完整源代码、Dockerfile 和构建说明以及 运行 应用程序):

import (
    "context"
    "log"
    "os"
    ...

    "cloud.google.com/go/compute/metadata"
    "cloud.google.com/go/logging"
)

func main() {
    loggingClient, err := stackdriverClient()
    ...
    log.Println("Before stackdriver logging")
    logger.StandardLogger(logging.Info).Println("Stackdriver log")
    if err = logger.Flush(); err != nil {
        log.Fatalf("Failed to flush client: %v", err)
    }
    if err = loggingClient.Close(); err != nil {
        log.Fatalf("Failed to close client: %v", err)
    }
    log.Println("After stackdriver logging")
    ...
}

func stackdriverClient() (client *logging.Client, err error) {
    var projectID string
    if projectID, err = metadata.ProjectID(); err == nil {
        client, err = logging.NewClient(context.Background(), projectID)
    }
    return
}

原来日志条目写入成功
但是 logviewer web UI 中的默认 Cloud 运行 过滤器不包括它们
下面的过滤器表达式对我有用以获取所有日志:

resource.type = "project" OR resource.type = "cloud_run_revision"

(省略服务名称、位置、严重性)

"stdout\stderr" 日志条目匹配 resource.type="cloud_run_revision",而 stackdriver 日志条目匹配 resource.type="project"

更新:I've created a ticket for this in google tracker

由于 Cloud 运行 已与 Cloud Logging 集成,因此无需使用 Go Client Library。 我们 运行 Cloud 运行 上的所有 gRPC 服务,并使用以下内容确保日志在 Cloud Logging 中的格式正确:

package main

import (
    "github.com/rs/zerolog"
    "github.com/rs/zerolog/log"
    "os"
)

// initializeLogging sets up the logging configuration for the service.
// Invoke this method in your Init() method.
func initializeLogging() {
    // Set logging options for production development
    if os.Getenv("ENV") != "DEV" {
        // change the level field name to ensure these are parsed correctly in Stackdriver
        zerolog.LevelFieldName = "severity"
        // UNIX Time is faster and smaller than most timestamps
        zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
    } else {
        // Set logging options for local development
        log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
        zerolog.SetGlobalLevel(zerolog.DebugLevel)
    }
    
    // Example log
    log.Info().Msg("This is how you log at Info level")
}

然后可以很好地显示日志以供本地开发。

如果您不想使用任何第 3 方日志记录库,一个简单的方法是构建您自己的 Entry 对象。

package main

import (
    "context"
    "encoding/json"
    "fmt"
    "google.golang.org/grpc"
    "google.golang.org/grpc/metadata"
    "log"
    "os"
    "strings"
)

// LogSeverity is used to map the logging levels consistent with Google Cloud Logging.
type LogSeverity string

const (
    // LogSeverity_DEBUG Debug or trace information.
    LogSeverity_DEBUG LogSeverity = "DEBUG"
    // LogSeverity_INFO Routine information, such as ongoing status or performance.
    LogSeverity_INFO LogSeverity = "INFO"
    // LogSeverity_NOTICE Normal but significant events, such as start up, shut down, or
    // a configuration change.
    LogSeverity_NOTICE LogSeverity = "NOTICE"
    // LogSeverity_WARNING Warning events might cause problems.
    LogSeverity_WARNING LogSeverity = "WARNING"
    // LogSeverity_ERROR Error events are likely to cause problems.
    LogSeverity_ERROR LogSeverity = "ERROR"
    // LogSeverity_CRITICAL Critical events cause more severe problems or outages.
    LogSeverity_CRITICAL LogSeverity = "CRITICAL"
    // LogSeverity_ALERT A person must take an action immediately.
    LogSeverity_ALERT LogSeverity = "ALERT"
    // LogSeverity_EMERGENCY One or more systems are unusable.
    LogSeverity_EMERGENCY LogSeverity = "EMERGENCY"
)

// Entry defines a log entry.
// If logs are provided in this format, Google Cloud Logging automatically
// parses the attributes into their LogEntry format as per
// https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry which then automatically
// makes the logs available in Google Cloud Logging and Tracing.
type Entry struct {
    Message  string      `json:"message"`
    Severity LogSeverity `json:"severity,omitempty"`
    Trace    string      `json:"logging.googleapis.com/trace,omitempty"`
    // To extend details sent to the logs, you may add the attributes here.
    //MyAttr1 string `json:"component,omitempty"`
}

// String renders an entry structure to the JSON format expected by Cloud Logging.
func (e Entry) String() string {

    // Defaults to INFO level.
    if e.Severity == "" {
        e.Severity = LogSeverity_INFO
    }

    // if Development is local then print out all logs
    if os.Getenv("ENV") == "LOCAL" {
        var prefix string
        switch e.Severity {
        case LogSeverity_DEBUG:
            prefix = colorize("DBG:      ", 90)
        case LogSeverity_INFO:
            prefix = colorize("INFO:     ", 32)
        case LogSeverity_NOTICE:
            prefix = colorize("NOTICE:   ", 34)
        case LogSeverity_WARNING:
            prefix = colorize("WARNING:  ", 33)
        case LogSeverity_ERROR:
            prefix = colorize("ERROR:    ", 31)
        case LogSeverity_ALERT:
            prefix = colorize("ALERT:    ", 91)
        case LogSeverity_CRITICAL:
            prefix = colorize("CRITICAL: ", 41)
        case LogSeverity_EMERGENCY:
            prefix = colorize("EMERGENCY:", 101)
        }
        return prefix + " " + e.Message
    } else {
        out, err := json.Marshal(e)
        if err != nil {
            log.Printf("json.Marshal: %v", err)
        }
        return string(out)
    }
}

// colorize returns the string s wrapped in ANSI code c
// Codes available at https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
func colorize(s interface{}, c int) string {
    return fmt.Sprintf("\x1b[%dm%v\x1b[0m", c, s)
}

使用 Google Cloud 的 Special Fields 可以与他们的 Cloud Logging 产品更紧密地集成。