diff options
author | Vincent Ambo <tazjin@google.com> | 2019-10-05T13·55+0100 |
---|---|---|
committer | Vincent Ambo <github@tazj.in> | 2019-10-06T22·05+0100 |
commit | 95abb1bcde75253aa35669eed26f734d02c6a870 (patch) | |
tree | 2c5efe518637693124c748fa3233dc17d44d6c51 | |
parent | 0642f7044dea2127b1c7dab1d88d90638536183a (diff) |
feat(server): Initial Stackdriver-compatible log formatter
This formatter has basic support for the Stackdriver Error Reporting format, but several things are still lacking: * the service version (preferably git commit?) needs to be included in the server somehow * log streams should be split between stdout/stderr as that is how AppEngine (and several other GCP services?) seemingly differentiate between info/error logs
-rw-r--r-- | tools/nixery/server/logs.go | 68 |
1 files changed, 68 insertions, 0 deletions
diff --git a/tools/nixery/server/logs.go b/tools/nixery/server/logs.go new file mode 100644 index 000000000000..55e0a13a03ea --- /dev/null +++ b/tools/nixery/server/logs.go @@ -0,0 +1,68 @@ +package main + +// This file configures different log formatters via logrus. The +// standard formatter uses a structured JSON format that is compatible +// with Stackdriver Error Reporting. +// +// https://cloud.google.com/error-reporting/docs/formatting-error-messages + +import ( + "bytes" + "encoding/json" + log "github.com/sirupsen/logrus" +) + +type stackdriverFormatter struct{} + +type serviceContext struct { + Service string `json:"service"` + Version string `json:"version"` +} + +type reportLocation struct { + FilePath string `json:"filePath"` + LineNumber int `json:"lineNumber"` + FunctionName string `json:"functionName"` +} + +var nixeryContext = serviceContext{ + Service: "nixery", + Version: "TODO(tazjin)", // angry? +} + +// isError determines whether an entry should be logged as an error +// (i.e. with attached `context`). +// +// This requires the caller information to be present on the log +// entry, as stacktraces are not available currently. +func isError(e *log.Entry) bool { + l := e.Level + return (l == log.ErrorLevel || l == log.FatalLevel || l == log.PanicLevel) && + e.HasCaller() +} + +func (f stackdriverFormatter) Format(e *log.Entry) ([]byte, error) { + msg := e.Data + msg["serviceContext"] = &nixeryContext + msg["message"] = &e.Message + msg["eventTime"] = &e.Time + + if isError(e) { + loc := reportLocation{ + FilePath: e.Caller.File, + LineNumber: e.Caller.Line, + FunctionName: e.Caller.Function, + } + msg["context"] = &loc + } + + b := new(bytes.Buffer) + err := json.NewEncoder(b).Encode(&msg) + + return b.Bytes(), err +} + +func init() { + log.SetReportCaller(true) + log.SetFormatter(stackdriverFormatter{}) +} |