Slightly better godocs and examples
What `godoc` renders is not quite the same as what pkg.go.dev renders
wrt whole-file examples and output comments, but this seems better
all around.
Tim Hockin
2 years ago
16 | 16 | package logr_test |
17 | 17 | |
18 | 18 | import ( |
19 | "fmt" | |
20 | ||
21 | 19 | "github.com/go-logr/logr" |
22 | "github.com/go-logr/logr/funcr" | |
23 | 20 | ) |
24 | ||
25 | // NewStdoutLogger returns a logr.Logger that prints to stdout. | |
26 | func NewStdoutLogger() logr.Logger { | |
27 | return funcr.New(func(prefix, args string) { | |
28 | if prefix != "" { | |
29 | _ = fmt.Sprintf("%s: %s\n", prefix, args) | |
30 | } else { | |
31 | fmt.Println(args) | |
32 | } | |
33 | }, funcr.Options{}) | |
34 | } | |
35 | 21 | |
36 | 22 | // ObjectRef references a Kubernetes object |
37 | 23 | type ObjectRef struct { |
0 | /* | |
1 | Copyright 2021 The logr Authors. | |
2 | ||
3 | Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | you may not use this file except in compliance with the License. | |
5 | You may obtain a copy of the License at | |
6 | ||
7 | http://www.apache.org/licenses/LICENSE-2.0 | |
8 | ||
9 | Unless required by applicable law or agreed to in writing, software | |
10 | distributed under the License is distributed on an "AS IS" BASIS, | |
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | See the License for the specific language governing permissions and | |
13 | limitations under the License. | |
14 | */ | |
15 | ||
16 | package logr_test | |
17 | ||
18 | import ( | |
19 | "fmt" | |
20 | ||
21 | "github.com/go-logr/logr" | |
22 | "github.com/go-logr/logr/funcr" | |
23 | ) | |
24 | ||
25 | // NewStdoutLogger returns a logr.Logger that prints to stdout. | |
26 | func NewStdoutLogger() logr.Logger { | |
27 | return funcr.New(func(prefix, args string) { | |
28 | if prefix != "" { | |
29 | _ = fmt.Sprintf("%s: %s\n", prefix, args) | |
30 | } else { | |
31 | fmt.Println(args) | |
32 | } | |
33 | }, funcr.Options{}) | |
34 | } | |
35 | ||
36 | func Example() { | |
37 | l := NewStdoutLogger() | |
38 | l.Info("default info log", "stringVal", "value", "intVal", 12345) | |
39 | l.V(0).Info("V(0) info log", "stringVal", "value", "intVal", 12345) | |
40 | l.Error(fmt.Errorf("an error"), "error log", "stringVal", "value", "intVal", 12345) | |
41 | // Output: | |
42 | // "level"=0 "msg"="default info log" "stringVal"="value" "intVal"=12345 | |
43 | // "level"=0 "msg"="V(0) info log" "stringVal"="value" "intVal"=12345 | |
44 | // "msg"="error log" "error"="an error" "stringVal"="value" "intVal"=12345 | |
45 | } |
0 | /* | |
1 | Copyright 2021 The logr Authors. | |
2 | ||
3 | Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | you may not use this file except in compliance with the License. | |
5 | You may obtain a copy of the License at | |
6 | ||
7 | http://www.apache.org/licenses/LICENSE-2.0 | |
8 | ||
9 | Unless required by applicable law or agreed to in writing, software | |
10 | distributed under the License is distributed on an "AS IS" BASIS, | |
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | See the License for the specific language governing permissions and | |
13 | limitations under the License. | |
14 | */ | |
15 | ||
16 | package funcr_test | |
17 | ||
18 | import ( | |
19 | "fmt" | |
20 | "strings" | |
21 | ||
22 | "github.com/go-logr/logr" | |
23 | "github.com/go-logr/logr/funcr" | |
24 | ) | |
25 | ||
26 | // NewStdoutLogger returns a logr.Logger that prints to stdout. | |
27 | // It demonstrates how to implement a custom With* function which | |
28 | // controls whether INFO or ERROR are printed in front of the log | |
29 | // message. | |
30 | func NewStdoutLogger() logr.Logger { | |
31 | l := &stdoutlogger{ | |
32 | Formatter: funcr.NewFormatter(funcr.Options{}), | |
33 | } | |
34 | return logr.New(l) | |
35 | } | |
36 | ||
37 | type stdoutlogger struct { | |
38 | funcr.Formatter | |
39 | logMsgType bool | |
40 | } | |
41 | ||
42 | func (l stdoutlogger) WithName(name string) logr.LogSink { | |
43 | l.Formatter.AddName(name) | |
44 | return &l | |
45 | } | |
46 | ||
47 | func (l stdoutlogger) WithValues(kvList ...interface{}) logr.LogSink { | |
48 | l.Formatter.AddValues(kvList) | |
49 | return &l | |
50 | } | |
51 | ||
52 | func (l stdoutlogger) WithCallDepth(depth int) logr.LogSink { | |
53 | l.Formatter.AddCallDepth(depth) | |
54 | return &l | |
55 | } | |
56 | ||
57 | func (l stdoutlogger) Info(level int, msg string, kvList ...interface{}) { | |
58 | prefix, args := l.FormatInfo(level, msg, kvList) | |
59 | l.write("INFO", prefix, args) | |
60 | } | |
61 | ||
62 | func (l stdoutlogger) Error(err error, msg string, kvList ...interface{}) { | |
63 | prefix, args := l.FormatError(err, msg, kvList) | |
64 | l.write("ERROR", prefix, args) | |
65 | } | |
66 | ||
67 | func (l stdoutlogger) write(msgType, prefix, args string) { | |
68 | var parts []string | |
69 | if l.logMsgType { | |
70 | parts = append(parts, msgType) | |
71 | } | |
72 | if prefix != "" { | |
73 | parts = append(parts, prefix) | |
74 | } | |
75 | parts = append(parts, args) | |
76 | fmt.Println(strings.Join(parts, ": ")) | |
77 | } | |
78 | ||
79 | // WithLogMsgType returns a copy of the logger with new settings for | |
80 | // logging the message type. It returns the original logger if the | |
81 | // underlying LogSink is not a stdoutlogger. | |
82 | func WithLogMsgType(log logr.Logger, logMsgType bool) logr.Logger { | |
83 | if l, ok := log.GetSink().(*stdoutlogger); ok { | |
84 | clone := *l | |
85 | clone.logMsgType = logMsgType | |
86 | log = log.WithSink(&clone) | |
87 | } | |
88 | return log | |
89 | } | |
90 | ||
91 | // Assert conformance to the interfaces. | |
92 | var _ logr.LogSink = &stdoutlogger{} | |
93 | var _ logr.CallDepthLogSink = &stdoutlogger{} | |
94 | ||
95 | func ExampleFormatter() { | |
96 | l := NewStdoutLogger() | |
97 | l.Info("no message type") | |
98 | WithLogMsgType(l, true).Info("with message type") | |
99 | // Output: | |
100 | // "level"=0 "msg"="no message type" | |
101 | // INFO: "level"=0 "msg"="with message type" | |
102 | } |
17 | 17 | |
18 | 18 | import ( |
19 | 19 | "fmt" |
20 | "strings" | |
21 | 20 | |
22 | 21 | "github.com/go-logr/logr" |
23 | 22 | "github.com/go-logr/logr/funcr" |
34 | 33 | } |
35 | 34 | // Output: hello world |
36 | 35 | } |
37 | ||
38 | // NewStdoutLogger returns a logr.Logger that prints to stdout. | |
39 | // It demonstrates how to implement a custom With* function which | |
40 | // controls whether INFO or ERROR are printed in front of the log | |
41 | // message. | |
42 | func NewStdoutLogger() logr.Logger { | |
43 | l := &stdoutlogger{ | |
44 | Formatter: funcr.NewFormatter(funcr.Options{}), | |
45 | } | |
46 | return logr.New(l) | |
47 | } | |
48 | ||
49 | type stdoutlogger struct { | |
50 | funcr.Formatter | |
51 | logMsgType bool | |
52 | } | |
53 | ||
54 | func (l stdoutlogger) WithName(name string) logr.LogSink { | |
55 | l.Formatter.AddName(name) | |
56 | return &l | |
57 | } | |
58 | ||
59 | func (l stdoutlogger) WithValues(kvList ...interface{}) logr.LogSink { | |
60 | l.Formatter.AddValues(kvList) | |
61 | return &l | |
62 | } | |
63 | ||
64 | func (l stdoutlogger) WithCallDepth(depth int) logr.LogSink { | |
65 | l.Formatter.AddCallDepth(depth) | |
66 | return &l | |
67 | } | |
68 | ||
69 | func (l stdoutlogger) Info(level int, msg string, kvList ...interface{}) { | |
70 | prefix, args := l.FormatInfo(level, msg, kvList) | |
71 | l.write("INFO", prefix, args) | |
72 | } | |
73 | ||
74 | func (l stdoutlogger) Error(err error, msg string, kvList ...interface{}) { | |
75 | prefix, args := l.FormatError(err, msg, kvList) | |
76 | l.write("ERROR", prefix, args) | |
77 | } | |
78 | ||
79 | func (l stdoutlogger) write(msgType, prefix, args string) { | |
80 | var parts []string | |
81 | if l.logMsgType { | |
82 | parts = append(parts, msgType) | |
83 | } | |
84 | if prefix != "" { | |
85 | parts = append(parts, prefix) | |
86 | } | |
87 | parts = append(parts, args) | |
88 | fmt.Println(strings.Join(parts, ": ")) | |
89 | } | |
90 | ||
91 | // WithLogMsgType returns a copy of the logger with new settings for | |
92 | // logging the message type. It returns the original logger if the | |
93 | // underlying LogSink is not a stdoutlogger. | |
94 | func WithLogMsgType(log logr.Logger, logMsgType bool) logr.Logger { | |
95 | if l, ok := log.GetSink().(*stdoutlogger); ok { | |
96 | clone := *l | |
97 | clone.logMsgType = logMsgType | |
98 | log = log.WithSink(&clone) | |
99 | } | |
100 | return log | |
101 | } | |
102 | ||
103 | // Assert conformance to the interfaces. | |
104 | var _ logr.LogSink = &stdoutlogger{} | |
105 | var _ logr.CallDepthLogSink = &stdoutlogger{} | |
106 | ||
107 | func ExampleFormatter() { | |
108 | l := NewStdoutLogger() | |
109 | l.Info("no message type") | |
110 | WithLogMsgType(l, true).Info("with message type") | |
111 | // Output: | |
112 | // "level"=0 "msg"="no message type" | |
113 | // INFO: "level"=0 "msg"="with message type" | |
114 | } |
20 | 20 | // to back that API. Packages in the Go ecosystem can depend on this package, |
21 | 21 | // while callers can implement logging with whatever backend is appropriate. |
22 | 22 | // |
23 | // # Usage | |
23 | // Usage | |
24 | 24 | // |
25 | 25 | // Logging is done using a Logger instance. Logger is a concrete type with |
26 | 26 | // methods, which defers the actual logging to a LogSink interface. The main |
44 | 44 | // LogSink implementations can choose to do things like attach additional |
45 | 45 | // information (such as stack traces) on calls to Error(). |
46 | 46 | // |
47 | // # Verbosity | |
47 | // Verbosity | |
48 | 48 | // |
49 | 49 | // Often we want to log information only when the application in "verbose |
50 | 50 | // mode". To write log lines that are more verbose, Logger has a V() method. |
61 | 61 | // We can write: |
62 | 62 | // logger.V(2).Info("an unusual thing happened") |
63 | 63 | // |
64 | // # Logger Names | |
64 | // Logger Names | |
65 | 65 | // |
66 | 66 | // Logger instances can have name strings so that all messages logged through |
67 | 67 | // that instance have additional context. For example, you might want to add |
78 | 78 | // joining operation (e.g. whitespace, commas, periods, slashes, brackets, |
79 | 79 | // quotes, etc). |
80 | 80 | // |
81 | // # Saved Values | |
81 | // Saved Values | |
82 | 82 | // |
83 | 83 | // Logger instances can store any number of key/value pairs, which will be |
84 | 84 | // logged alongside all messages logged through that instance. For example, |
96 | 96 | // // later on... |
97 | 97 | // obj.logger.Info("setting foo", "value", targetValue) |
98 | 98 | // |
99 | // # Best Practices | |
99 | // Best Practices | |
100 | 100 | // |
101 | 101 | // Logger has very few hard rules, with the goal that LogSink implementations |
102 | 102 | // might have a lot of freedom to differentiate. There are, however, some |
111 | 111 | // may be any Go value, but how the value is formatted is determined by the |
112 | 112 | // LogSink implementation. |
113 | 113 | // |
114 | // # Key Naming Conventions | |
114 | // Key Naming Conventions | |
115 | 115 | // |
116 | 116 | // Keys are not strictly required to conform to any specification or regex, but |
117 | 117 | // it is recommended that they: |
128 | 128 | // While users are generally free to use key names of their choice, it's |
129 | 129 | // generally best to avoid using the following keys, as they're frequently used |
130 | 130 | // by implementations: |
131 | // | |
132 | // * "caller": the calling information (file/line) of a particular log line. | |
133 | // * "error": the underlying error value in the `Error` method. | |
134 | // * "level": the log level. | |
135 | // * "logger": the name of the associated logger. | |
136 | // * "msg": the log message. | |
131 | // * "caller": the calling information (file/line) of a particular log line | |
132 | // * "error": the underlying error value in the `Error` method | |
133 | // * "level": the log level | |
134 | // * "logger": the name of the associated logger | |
135 | // * "msg": the log message | |
137 | 136 | // * "stacktrace": the stack trace associated with a particular log line or |
138 | // error (often from the `Error` message). | |
139 | // * "ts": the timestamp for a log line. | |
137 | // error (often from the `Error` message) | |
138 | // * "ts": the timestamp for a log line | |
140 | 139 | // |
141 | 140 | // Implementations are encouraged to make use of these keys to represent the |
142 | 141 | // above concepts, when necessary (for example, in a pure-JSON output form, it |
143 | 142 | // would be necessary to represent at least message and timestamp as ordinary |
144 | 143 | // named values). |
145 | 144 | // |
146 | // # Break Glass | |
145 | // Break Glass | |
147 | 146 | // |
148 | 147 | // Implementations may choose to give callers access to the underlying |
149 | 148 | // logging implementation. The recommended pattern for this is: |