Skip to content

Commit 533ecbb

Browse files
authored
feat(logging): Add (*Logger). StandardLoggerFromTemplate() method. (#7261)
1 parent d5ea93c commit 533ecbb

File tree

4 files changed

+144
-11
lines changed

4 files changed

+144
-11
lines changed

logging/examples_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,23 @@ func ExampleLogger_StandardLogger() {
203203
slg.Println("an informative message")
204204
}
205205

206+
func ExampleLogger_StandardLoggerFromTemplate() {
207+
ctx := context.Background()
208+
client, err := logging.NewClient(ctx, "my-project")
209+
if err != nil {
210+
// TODO: Handle error.
211+
}
212+
lg := client.Logger("my-log")
213+
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
214+
slg := lg.StandardLoggerFromTemplate(&logging.Entry{
215+
Severity: logging.Info,
216+
HTTPRequest: &logging.HTTPRequest{Request: r},
217+
})
218+
slg.Println("Before hello world")
219+
fmt.Fprintf(w, "Hello world!\n")
220+
})
221+
}
222+
206223
func ExampleParseSeverity() {
207224
sev := logging.ParseSeverity("ALERT")
208225
fmt.Println(sev)

logging/logging.go

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,8 @@ var (
9696
ErrRedirectProtoPayloadNotSupported = errors.New("printEntryToStdout: cannot find valid payload")
9797

9898
// For testing:
99-
now = time.Now
99+
now = time.Now
100+
toLogEntryInternal = toLogEntryInternalImpl
100101

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

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

304-
type severityWriter struct {
305-
l *Logger
306-
s Severity
306+
type templateEntryWriter struct {
307+
l *Logger
308+
template *Entry
307309
}
308310

309-
func (w severityWriter) Write(p []byte) (n int, err error) {
310-
w.l.Log(Entry{
311-
Severity: w.s,
312-
Payload: string(p),
313-
})
311+
func (w templateEntryWriter) Write(p []byte) (n int, err error) {
312+
e := *w.template
313+
e.Payload = string(p)
314+
w.l.Log(e)
314315
return len(p), nil
315316
}
316317

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

725+
// StandardLoggerFromTemplate returns a Go Standard Logging API *log.Logger.
726+
//
727+
// The returned logger emits logs using logging.(*Logger).Log() with an entry
728+
// constructed from the provided template Entry struct.
729+
//
730+
// The caller is responsible for ensuring that the template Entry struct
731+
// does not change during the the lifetime of the returned *log.Logger.
732+
//
733+
// Prefer (*Logger).StandardLogger() which is more efficient if the template
734+
// only sets Severity.
735+
func (l *Logger) StandardLoggerFromTemplate(template *Entry) *log.Logger {
736+
return log.New(templateEntryWriter{l, template}, "", 0)
737+
}
738+
724739
func populateTraceInfo(e *Entry, req *http.Request) bool {
725740
if req == nil {
726741
if e.HTTPRequest != nil && e.HTTPRequest.Request != nil {
@@ -834,7 +849,7 @@ func (l *Logger) ToLogEntry(e Entry, parent string) (*logpb.LogEntry, error) {
834849
return toLogEntryInternal(e, l, parent, 1)
835850
}
836851

837-
func toLogEntryInternal(e Entry, l *Logger, parent string, skipLevels int) (*logpb.LogEntry, error) {
852+
func toLogEntryInternalImpl(e Entry, l *Logger, parent string, skipLevels int) (*logpb.LogEntry, error) {
838853
if e.LogName != "" {
839854
return nil, errors.New("logging: Entry.LogName should be not be set when writing")
840855
}

logging/logging_test.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"net/http"
2929
"net/url"
3030
"os"
31+
"reflect"
3132
"runtime"
3233
"strings"
3334
"sync"
@@ -717,6 +718,100 @@ func TestStandardLogger(t *testing.T) {
717718
}
718719
}
719720

721+
func TestStandardLoggerFromTemplate(t *testing.T) {
722+
tests := []struct {
723+
name string
724+
template logging.Entry
725+
message string
726+
want logging.Entry
727+
}{
728+
{
729+
name: "severity only",
730+
template: logging.Entry{
731+
Severity: logging.Error,
732+
},
733+
message: "log message",
734+
want: logging.Entry{
735+
Severity: logging.Error,
736+
Payload: "log message\n",
737+
},
738+
},
739+
{
740+
name: "severity and trace",
741+
template: logging.Entry{
742+
Severity: logging.Info,
743+
Trace: "projects/P/traces/105445aa7843bc8bf206b120001000",
744+
},
745+
message: "log message",
746+
want: logging.Entry{
747+
Severity: logging.Info,
748+
Payload: "log message\n",
749+
Trace: "projects/P/traces/105445aa7843bc8bf206b120001000",
750+
},
751+
},
752+
{
753+
name: "severity and http request",
754+
template: logging.Entry{
755+
Severity: logging.Info,
756+
HTTPRequest: &logging.HTTPRequest{
757+
Request: &http.Request{
758+
Method: "GET",
759+
Host: "example.com",
760+
},
761+
Status: 200,
762+
},
763+
},
764+
message: "log message",
765+
want: logging.Entry{
766+
Severity: logging.Info,
767+
Payload: "log message\n",
768+
HTTPRequest: &logging.HTTPRequest{
769+
Request: &http.Request{
770+
Method: "GET",
771+
Host: "example.com",
772+
},
773+
Status: 200,
774+
},
775+
},
776+
},
777+
{
778+
name: "payload in template is ignored",
779+
template: logging.Entry{
780+
Severity: logging.Info,
781+
Payload: "this should not be set in the template",
782+
Trace: "projects/P/traces/105445aa7843bc8bf206b120001000",
783+
},
784+
message: "log message",
785+
want: logging.Entry{
786+
Severity: logging.Info,
787+
Payload: "log message\n",
788+
Trace: "projects/P/traces/105445aa7843bc8bf206b120001000",
789+
},
790+
},
791+
}
792+
lg := client.Logger(testLogID)
793+
for _, tc := range tests {
794+
t.Run(tc.name, func(t *testing.T) {
795+
mock := func(got logging.Entry, l *logging.Logger, parent string, skipLevels int) (*logpb.LogEntry, error) {
796+
if !reflect.DeepEqual(got, tc.want) {
797+
t.Errorf("Emitted Entry incorrect. Expected %v got %v", tc.want, got)
798+
}
799+
// Return value is not interesting
800+
return &logpb.LogEntry{}, nil
801+
}
802+
803+
f := logging.SetToLogEntryInternal(mock)
804+
defer func() { logging.SetToLogEntryInternal(f) }()
805+
806+
slg := lg.StandardLoggerFromTemplate(&tc.template)
807+
slg.Print(tc.message)
808+
if err := lg.Flush(); err != nil {
809+
t.Fatal(err)
810+
}
811+
})
812+
}
813+
}
814+
720815
func TestSeverity(t *testing.T) {
721816
if got, want := logging.Info.String(), "Info"; got != want {
722817
t.Errorf("got %q, want %q", got, want)

logging/logging_unexported_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"time"
2525

2626
"cloud.google.com/go/internal/testutil"
27+
logpb "cloud.google.com/go/logging/apiv2/loggingpb"
2728
"github.com/golang/protobuf/proto"
2829
durpb "github.com/golang/protobuf/ptypes/duration"
2930
structpb "github.com/golang/protobuf/ptypes/struct"
@@ -355,3 +356,8 @@ func SetNow(f func() time.Time) func() time.Time {
355356
now, f = f, now
356357
return f
357358
}
359+
360+
func SetToLogEntryInternal(f func(Entry, *Logger, string, int) (*logpb.LogEntry, error)) func(Entry, *Logger, string, int) (*logpb.LogEntry, error) {
361+
toLogEntryInternal, f = f, toLogEntryInternal
362+
return f
363+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy