@@ -7,35 +7,57 @@ package log
77
88import (
99 "context"
10- "errors"
1110 "fmt"
1211 "log"
1312 "os"
1413 "strings"
1514 "sync"
1615
17- "cloud.google.com/go/logging"
18- "golang.org/x/pkgsite/internal/derrors"
1916 "golang.org/x/pkgsite/internal/experiment"
2017)
2118
19+ type Severity int
20+
21+ const (
22+ SeverityDefault = Severity (iota )
23+ SeverityDebug
24+ SeverityInfo
25+ SeverityWarning
26+ SeverityError
27+ SeverityCritical
28+ )
29+
30+ func (s Severity ) String () string {
31+ switch s {
32+ case SeverityDefault :
33+ return "Default"
34+ case SeverityDebug :
35+ return "Debug"
36+ case SeverityInfo :
37+ return "Info"
38+ case SeverityWarning :
39+ return "Warning"
40+ case SeverityError :
41+ return "Error"
42+ case SeverityCritical :
43+ return "Critical"
44+ default :
45+ return fmt .Sprint (int (s ))
46+ }
47+ }
48+
49+ type Logger interface {
50+ Log (ctx context.Context , s Severity , payload any )
51+ Flush ()
52+ }
53+
2254var (
2355 mu sync.Mutex
24- logger interface {
25- log (context.Context , logging.Severity , any )
26- } = stdlibLogger {}
56+ logger Logger = stdlibLogger {}
2757
2858 // currentLevel holds current log level.
2959 // No logs will be printed below currentLevel.
30- currentLevel = logging .Default
31- )
32-
33- type (
34- // traceIDKey is the type of the context key for trace IDs.
35- traceIDKey struct {}
36-
37- // labelsKey is the type of the context key for labels.
38- labelsKey struct {}
60+ currentLevel = SeverityDefault
3961)
4062
4163// Set the log level
@@ -45,79 +67,17 @@ func SetLevel(v string) {
4567 currentLevel = toLevel (v )
4668}
4769
48- func getLevel () logging. Severity {
70+ func getLevel () Severity {
4971 mu .Lock ()
5072 defer mu .Unlock ()
5173 return currentLevel
5274}
5375
54- // NewContextWithTraceID creates a new context from ctx that adds the trace ID.
55- func NewContextWithTraceID (ctx context.Context , traceID string ) context.Context {
56- return context .WithValue (ctx , traceIDKey {}, traceID )
57- }
58-
59- // NewContextWithLabel creates anew context from ctx that adds a label that will
60- // appear in the log entry.
61- func NewContextWithLabel (ctx context.Context , key , value string ) context.Context {
62- oldLabels , _ := ctx .Value (labelsKey {}).(map [string ]string )
63- // Copy the labels, to preserve immutability of contexts.
64- newLabels := map [string ]string {}
65- for k , v := range oldLabels {
66- newLabels [k ] = v
67- }
68- newLabels [key ] = value
69- return context .WithValue (ctx , labelsKey {}, newLabels )
70- }
71-
72- // stackdriverLogger logs to GCP Stackdriver.
73- type stackdriverLogger struct {
74- sdlogger * logging.Logger
75- }
76-
77- func (l * stackdriverLogger ) log (ctx context.Context , s logging.Severity , payload any ) {
78- // Convert errors to strings, or they may serialize as the empty JSON object.
79- if err , ok := payload .(error ); ok {
80- payload = err .Error ()
81- }
82- traceID , _ := ctx .Value (traceIDKey {}).(string ) // if not present, traceID is "", which is fine
83- labels , _ := ctx .Value (labelsKey {}).(map [string ]string )
84- es := experimentString (ctx )
85- if len (es ) > 0 {
86- nl := map [string ]string {}
87- for k , v := range labels {
88- nl [k ] = v
89- }
90- nl ["experiments" ] = es
91- labels = nl
92- }
93- l .sdlogger .Log (logging.Entry {
94- Severity : s ,
95- Labels : labels ,
96- Payload : payload ,
97- Trace : traceID ,
98- })
99- }
100-
10176// stdlibLogger uses the Go standard library logger.
10277type stdlibLogger struct {}
10378
104- func init () {
105- // Log to stdout on GKE so the log messages are severity Info, rather than Error.
106- if os .Getenv ("GO_DISCOVERY_ON_GKE" ) != "" {
107- log .SetOutput (os .Stdout )
108-
109- }
110- }
111-
112- func (stdlibLogger ) log (ctx context.Context , s logging.Severity , payload any ) {
79+ func (stdlibLogger ) Log (ctx context.Context , s Severity , payload any ) {
11380 var extras []string
114- traceID , _ := ctx .Value (traceIDKey {}).(string ) // if not present, traceID is ""
115- if traceID != "" {
116- extras = append (extras , fmt .Sprintf ("traceID %s" , traceID ))
117- }
118- if labels , ok := ctx .Value (labelsKey {}).(map [string ]string ); ok {
119- extras = append (extras , fmt .Sprint (labels ))
120- }
12181 es := experimentString (ctx )
12282 if len (es ) > 0 {
12383 extras = append (extras , fmt .Sprintf ("experiments %s" , es ))
@@ -130,126 +90,106 @@ func (stdlibLogger) log(ctx context.Context, s logging.Severity, payload any) {
13090
13191}
13292
93+ func (stdlibLogger ) Flush () {}
94+
13395func experimentString (ctx context.Context ) string {
13496 return strings .Join (experiment .FromContext (ctx ).Active (), ", " )
13597}
13698
137- // UseStackdriver switches from the default stdlib logger to a Stackdriver
138- // logger. It assumes config.Init has been called. UseStackdriver returns a
139- // "parent" *logging.Logger that should be used to log the start and end of a
140- // request. It also creates and remembers internally a "child" logger that will
141- // be used to log within a request. The two loggers are necessary to get request-scoped
142- // logs in Stackdriver.
143- // See https://cloud.google.com/appengine/docs/standard/go/writing-application-logs.
144- //
145- // UseStackdriver can only be called once. If it is called a second time, it returns an error.
146- func UseStackdriver (ctx context.Context , logName , projectID string , opts []logging.LoggerOption ) (_ * logging.Logger , err error ) {
147- defer derrors .Wrap (& err , "UseStackdriver(ctx, %q)" , logName )
148- client , err := logging .NewClient (ctx , projectID )
149- if err != nil {
150- return nil , err
151- }
152- parent := client .Logger (logName , opts ... )
153- child := client .Logger (logName + "-child" , opts ... )
99+ func Use (l Logger ) {
154100 mu .Lock ()
155101 defer mu .Unlock ()
156- if _ , ok := logger .(* stackdriverLogger ); ok {
157- return nil , errors .New ("already called once" )
158- }
159- logger = & stackdriverLogger {child }
160- return parent , nil
102+ logger = l
161103}
162104
163105// Infof logs a formatted string at the Info level.
164106func Infof (ctx context.Context , format string , args ... any ) {
165- logf (ctx , logging . Info , format , args )
107+ logf (ctx , SeverityInfo , format , args )
166108}
167109
168110// Warningf logs a formatted string at the Warning level.
169111func Warningf (ctx context.Context , format string , args ... any ) {
170- logf (ctx , logging . Warning , format , args )
112+ logf (ctx , SeverityWarning , format , args )
171113}
172114
173115// Errorf logs a formatted string at the Error level.
174116func Errorf (ctx context.Context , format string , args ... any ) {
175- logf (ctx , logging . Error , format , args )
117+ logf (ctx , SeverityError , format , args )
176118}
177119
178120// Debugf logs a formatted string at the Debug level.
179121func Debugf (ctx context.Context , format string , args ... any ) {
180- logf (ctx , logging . Debug , format , args )
122+ logf (ctx , SeverityDebug , format , args )
181123}
182124
183125// Fatalf logs formatted string at the Critical level followed by exiting the program.
184126func Fatalf (ctx context.Context , format string , args ... any ) {
185- logf (ctx , logging . Critical , format , args )
127+ logf (ctx , SeverityCritical , format , args )
186128 die ()
187129}
188130
189- func logf (ctx context.Context , s logging. Severity , format string , args []any ) {
131+ func logf (ctx context.Context , s Severity , format string , args []any ) {
190132 doLog (ctx , s , fmt .Sprintf (format , args ... ))
191133}
192134
193135// Info logs arg, which can be a string or a struct, at the Info level.
194- func Info (ctx context.Context , arg any ) { doLog (ctx , logging . Info , arg ) }
136+ func Info (ctx context.Context , arg any ) { doLog (ctx , SeverityInfo , arg ) }
195137
196138// Warning logs arg, which can be a string or a struct, at the Warning level.
197- func Warning (ctx context.Context , arg any ) { doLog (ctx , logging . Warning , arg ) }
139+ func Warning (ctx context.Context , arg any ) { doLog (ctx , SeverityWarning , arg ) }
198140
199141// Error logs arg, which can be a string or a struct, at the Error level.
200- func Error (ctx context.Context , arg any ) { doLog (ctx , logging . Error , arg ) }
142+ func Error (ctx context.Context , arg any ) { doLog (ctx , SeverityError , arg ) }
201143
202144// Debug logs arg, which can be a string or a struct, at the Debug level.
203- func Debug (ctx context.Context , arg any ) { doLog (ctx , logging . Debug , arg ) }
145+ func Debug (ctx context.Context , arg any ) { doLog (ctx , SeverityDebug , arg ) }
204146
205147// Fatal logs arg, which can be a string or a struct, at the Critical level followed by exiting the program.
206148func Fatal (ctx context.Context , arg any ) {
207- doLog (ctx , logging . Critical , arg )
149+ doLog (ctx , SeverityCritical , arg )
208150 die ()
209151}
210152
211- func doLog (ctx context.Context , s logging. Severity , payload any ) {
153+ func doLog (ctx context.Context , s Severity , payload any ) {
212154 if getLevel () > s {
213155 return
214156 }
215157 mu .Lock ()
216158 l := logger
217159 mu .Unlock ()
218- l .log (ctx , s , payload )
160+ l .Log (ctx , s , payload )
219161}
220162
221163func die () {
222164 mu .Lock ()
223- if sl , ok := logger .(* stackdriverLogger ); ok {
224- sl .sdlogger .Flush ()
225- }
165+ logger .Flush ()
226166 mu .Unlock ()
227167 os .Exit (1 )
228168}
229169
230170// toLevel returns the logging.Severity for a given string.
231171// Possible input values are "", "debug", "info", "warning", "error", "fatal".
232172// In case of invalid string input, it maps to DefaultLevel.
233- func toLevel (v string ) logging. Severity {
173+ func toLevel (v string ) Severity {
234174 v = strings .ToLower (v )
235175
236176 switch v {
237177 case "" :
238178 // default log level will print everything.
239- return logging . Default
179+ return SeverityDefault
240180 case "debug" :
241- return logging . Debug
181+ return SeverityDebug
242182 case "info" :
243- return logging . Info
183+ return SeverityInfo
244184 case "warning" :
245- return logging . Warning
185+ return SeverityWarning
246186 case "error" :
247- return logging . Error
187+ return SeverityError
248188 case "fatal" :
249- return logging . Critical
189+ return SeverityCritical
250190 }
251191
252192 // Default log level in case of invalid input.
253193 log .Printf ("Error: %s is invalid LogLevel. Possible values are [debug, info, warning, error, fatal]" , v )
254- return logging . Default
194+ return SeverityDefault
255195}
0 commit comments