Skip to content

Commit

Permalink
feat(logging): Add (*Logger). StandardLoggerFromTemplate() method. (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
tang-fh committed Jan 26, 2023
1 parent d5ea93c commit 533ecbb
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 11 deletions.
17 changes: 17 additions & 0 deletions logging/examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,23 @@ func ExampleLogger_StandardLogger() {
slg.Println("an informative message")
}

func ExampleLogger_StandardLoggerFromTemplate() {
ctx := context.Background()
client, err := logging.NewClient(ctx, "my-project")
if err != nil {
// TODO: Handle error.
}
lg := client.Logger("my-log")
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
slg := lg.StandardLoggerFromTemplate(&logging.Entry{
Severity: logging.Info,
HTTPRequest: &logging.HTTPRequest{Request: r},
})
slg.Println("Before hello world")
fmt.Fprintf(w, "Hello world!\n")
})
}

func ExampleParseSeverity() {
sev := logging.ParseSeverity("ALERT")
fmt.Println(sev)
Expand Down
37 changes: 26 additions & 11 deletions logging/logging.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ var (
ErrRedirectProtoPayloadNotSupported = errors.New("printEntryToStdout: cannot find valid payload")

// For testing:
now = time.Now
now = time.Now
toLogEntryInternal = toLogEntryInternalImpl

// ErrOverflow signals that the number of buffered entries for a Logger
// exceeds its BufferLimit.
Expand Down Expand Up @@ -287,7 +288,8 @@ func (c *Client) Logger(logID string, opts ...LoggerOption) *Logger {
}
l.stdLoggers = map[Severity]*log.Logger{}
for s := range severityName {
l.stdLoggers[s] = log.New(severityWriter{l, s}, "", 0)
e := Entry{Severity: s}
l.stdLoggers[s] = log.New(templateEntryWriter{l, &e}, "", 0)
}

c.loggers.Add(1)
Expand All @@ -301,16 +303,15 @@ func (c *Client) Logger(logID string, opts ...LoggerOption) *Logger {
return l
}

type severityWriter struct {
l *Logger
s Severity
type templateEntryWriter struct {
l *Logger
template *Entry
}

func (w severityWriter) Write(p []byte) (n int, err error) {
w.l.Log(Entry{
Severity: w.s,
Payload: string(p),
})
func (w templateEntryWriter) Write(p []byte) (n int, err error) {
e := *w.template
e.Payload = string(p)
w.l.Log(e)
return len(p), nil
}

Expand Down Expand Up @@ -721,6 +722,20 @@ func (l *Logger) writeLogEntries(entries []*logpb.LogEntry) {
// (for example by calling SetFlags or SetPrefix).
func (l *Logger) StandardLogger(s Severity) *log.Logger { return l.stdLoggers[s] }

// StandardLoggerFromTemplate returns a Go Standard Logging API *log.Logger.
//
// The returned logger emits logs using logging.(*Logger).Log() with an entry
// constructed from the provided template Entry struct.
//
// The caller is responsible for ensuring that the template Entry struct
// does not change during the the lifetime of the returned *log.Logger.
//
// Prefer (*Logger).StandardLogger() which is more efficient if the template
// only sets Severity.
func (l *Logger) StandardLoggerFromTemplate(template *Entry) *log.Logger {
return log.New(templateEntryWriter{l, template}, "", 0)
}

func populateTraceInfo(e *Entry, req *http.Request) bool {
if req == nil {
if e.HTTPRequest != nil && e.HTTPRequest.Request != nil {
Expand Down Expand Up @@ -834,7 +849,7 @@ func (l *Logger) ToLogEntry(e Entry, parent string) (*logpb.LogEntry, error) {
return toLogEntryInternal(e, l, parent, 1)
}

func toLogEntryInternal(e Entry, l *Logger, parent string, skipLevels int) (*logpb.LogEntry, error) {
func toLogEntryInternalImpl(e Entry, l *Logger, parent string, skipLevels int) (*logpb.LogEntry, error) {
if e.LogName != "" {
return nil, errors.New("logging: Entry.LogName should be not be set when writing")
}
Expand Down
95 changes: 95 additions & 0 deletions logging/logging_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"net/http"
"net/url"
"os"
"reflect"
"runtime"
"strings"
"sync"
Expand Down Expand Up @@ -717,6 +718,100 @@ func TestStandardLogger(t *testing.T) {
}
}

func TestStandardLoggerFromTemplate(t *testing.T) {
tests := []struct {
name string
template logging.Entry
message string
want logging.Entry
}{
{
name: "severity only",
template: logging.Entry{
Severity: logging.Error,
},
message: "log message",
want: logging.Entry{
Severity: logging.Error,
Payload: "log message\n",
},
},
{
name: "severity and trace",
template: logging.Entry{
Severity: logging.Info,
Trace: "projects/P/traces/105445aa7843bc8bf206b120001000",
},
message: "log message",
want: logging.Entry{
Severity: logging.Info,
Payload: "log message\n",
Trace: "projects/P/traces/105445aa7843bc8bf206b120001000",
},
},
{
name: "severity and http request",
template: logging.Entry{
Severity: logging.Info,
HTTPRequest: &logging.HTTPRequest{
Request: &http.Request{
Method: "GET",
Host: "example.com",
},
Status: 200,
},
},
message: "log message",
want: logging.Entry{
Severity: logging.Info,
Payload: "log message\n",
HTTPRequest: &logging.HTTPRequest{
Request: &http.Request{
Method: "GET",
Host: "example.com",
},
Status: 200,
},
},
},
{
name: "payload in template is ignored",
template: logging.Entry{
Severity: logging.Info,
Payload: "this should not be set in the template",
Trace: "projects/P/traces/105445aa7843bc8bf206b120001000",
},
message: "log message",
want: logging.Entry{
Severity: logging.Info,
Payload: "log message\n",
Trace: "projects/P/traces/105445aa7843bc8bf206b120001000",
},
},
}
lg := client.Logger(testLogID)
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
mock := func(got logging.Entry, l *logging.Logger, parent string, skipLevels int) (*logpb.LogEntry, error) {
if !reflect.DeepEqual(got, tc.want) {
t.Errorf("Emitted Entry incorrect. Expected %v got %v", tc.want, got)
}
// Return value is not interesting
return &logpb.LogEntry{}, nil
}

f := logging.SetToLogEntryInternal(mock)
defer func() { logging.SetToLogEntryInternal(f) }()

slg := lg.StandardLoggerFromTemplate(&tc.template)
slg.Print(tc.message)
if err := lg.Flush(); err != nil {
t.Fatal(err)
}
})
}
}

func TestSeverity(t *testing.T) {
if got, want := logging.Info.String(), "Info"; got != want {
t.Errorf("got %q, want %q", got, want)
Expand Down
6 changes: 6 additions & 0 deletions logging/logging_unexported_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"time"

"cloud.google.com/go/internal/testutil"
logpb "cloud.google.com/go/logging/apiv2/loggingpb"
"github.com/golang/protobuf/proto"
durpb "github.com/golang/protobuf/ptypes/duration"
structpb "github.com/golang/protobuf/ptypes/struct"
Expand Down Expand Up @@ -355,3 +356,8 @@ func SetNow(f func() time.Time) func() time.Time {
now, f = f, now
return f
}

func SetToLogEntryInternal(f func(Entry, *Logger, string, int) (*logpb.LogEntry, error)) func(Entry, *Logger, string, int) (*logpb.LogEntry, error) {
toLogEntryInternal, f = f, toLogEntryInternal
return f
}

0 comments on commit 533ecbb

Please sign in to comment.