blob: 11c2ecaa02b775bb9e97dec34e403260f6f2a182 [file] [log] [blame]
package cloud_logging
import (
"context"
"fmt"
"os"
"strings"
"time"
"cloud.google.com/go/logging"
"go.skia.org/infra/go/skerr"
"go.skia.org/infra/go/sklog/sklog_impl"
"golang.org/x/oauth2"
"google.golang.org/api/option"
)
// cloudLogger is a sklog_impl.Logger which uses the non-deprecated
// cloud.google.com/go/logging package.
type cloudLogger struct {
logger *logging.Logger
}
// New returns a sklog_impl.Logger instance. Writes the log URL to stdout.
func New(ctx context.Context, projectId, logId string, ts oauth2.TokenSource, labels map[string]string) (*cloudLogger, error) {
logsClient, err := logging.NewClient(ctx, projectId, option.WithTokenSource(ts))
if err != nil {
return nil, err
}
logger := logsClient.Logger(logId, logging.CommonLabels(labels))
fmt.Printf("Connected Cloud Logging; logs can be found here: https://console.cloud.google.com/logs/viewer?project=%s&resource=gce_instance&logName=projects%%2F%s%%2Flogs%%2F%s", projectId, projectId, logId)
return &cloudLogger{
logger: logger,
}, nil
}
// Return the logging.Logger.
func (cl *cloudLogger) Logger() *logging.Logger {
return cl.logger
}
func convertSeverity(severity sklog_impl.Severity) logging.Severity {
switch severity {
case sklog_impl.Debug:
return logging.Debug
case sklog_impl.Info:
return logging.Info
case sklog_impl.Warning:
return logging.Warning
case sklog_impl.Error:
return logging.Error
case sklog_impl.Fatal:
return logging.Alert
default:
return logging.Default
}
}
// Log implements sklog_impl.Logger.
func (cl *cloudLogger) Log(depth int, severity sklog_impl.Severity, format string, args ...interface{}) {
// The following is copied from previous impl; we should consider replacing this with structured
// logs.
// See doc on sklog.Logger interface.
stackDepth := 2 + depth
stacks := skerr.CallStack(5, stackDepth)
prettyPayload := strings.Builder{}
_, _ = prettyPayload.WriteString(stacks[0].String())
_ = prettyPayload.WriteByte(' ')
if len(format) == 0 {
_, _ = fmt.Fprint(&prettyPayload, args...)
} else {
_, _ = fmt.Fprintf(&prettyPayload, format, args...)
}
stack := map[string]string{
"stacktrace_0": stacks[0].String(),
"stacktrace_1": stacks[1].String(),
"stacktrace_2": stacks[2].String(),
"stacktrace_3": stacks[3].String(),
"stacktrace_4": stacks[4].String(),
}
cl.logger.Log(logging.Entry{
Payload: prettyPayload.String(),
Timestamp: time.Now(),
Severity: convertSeverity(severity),
Labels: stack,
})
}
// LogAndDie implements sklog_impl.Logger.
func (cl *cloudLogger) LogAndDie(depth int, format string, args ...interface{}) {
sklog_impl.DefaultLogAndDie(cl, depth, format, args)
}
// Flush implements sklog_impl.Logger.
func (cl *cloudLogger) Flush() {
if err := cl.logger.Flush(); err != nil {
fmt.Fprintf(os.Stderr, "Failed to flush logging.Logger: %s", err)
}
}
var _ sklog_impl.Logger = (*cloudLogger)(nil)