pax_global_header00006660000000000000000000000064140044032400014501gustar00rootroot0000000000000052 comment=6123e0f340cd23589dcff60026c857584f663f14 golang-github-go-logr-logr-0.4.0/000077500000000000000000000000001400440324000165365ustar00rootroot00000000000000golang-github-go-logr-logr-0.4.0/LICENSE000066400000000000000000000261351400440324000175520ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. golang-github-go-logr-logr-0.4.0/README.md000066400000000000000000000171221400440324000200200ustar00rootroot00000000000000# A more minimal logging API for Go Before you consider this package, please read [this blog post by the inimitable Dave Cheney][warning-makes-no-sense]. I really appreciate what he has to say, and it largely aligns with my own experiences. Too many choices of levels means inconsistent logs. This package offers a purely abstract interface, based on these ideas but with a few twists. Code can depend on just this interface and have the actual logging implementation be injected from callers. Ideally only `main()` knows what logging implementation is being used. # Differences from Dave's ideas The main differences are: 1) Dave basically proposes doing away with the notion of a logging API in favor of `fmt.Printf()`. I disagree, especially when you consider things like output locations, timestamps, file and line decorations, and structured logging. I restrict the API to just 2 types of logs: info and error. Info logs are things you want to tell the user which are not errors. Error logs are, well, errors. If your code receives an `error` from a subordinate function call and is logging that `error` *and not returning it*, use error logs. 2) Verbosity-levels on info logs. This gives developers a chance to indicate arbitrary grades of importance for info logs, without assigning names with semantic meaning such as "warning", "trace", and "debug". Superficially this may feel very similar, but the primary difference is the lack of semantics. Because verbosity is a numerical value, it's safe to assume that an app running with higher verbosity means more (and less important) logs will be generated. This is a BETA grade API. There are implementations for the following logging libraries: - **github.com/google/glog**: [glogr](https://github.com/go-logr/glogr) - **k8s.io/klog**: [klogr](https://git.k8s.io/klog/klogr) - **go.uber.org/zap**: [zapr](https://github.com/go-logr/zapr) - **log** (the Go standard library logger): [stdr](https://github.com/go-logr/stdr) - **github.com/sirupsen/logrus**: [logrusr](https://github.com/bombsimon/logrusr) - **github.com/wojas/genericr**: [genericr](https://github.com/wojas/genericr) (makes it easy to implement your own backend) - **logfmt** (Heroku style [logging](https://www.brandur.org/logfmt)): [logfmtr](https://github.com/iand/logfmtr) # FAQ ## Conceptual ## Why structured logging? - **Structured logs are more easily queriable**: Since you've got key-value pairs, it's much easier to query your structured logs for particular values by filtering on the contents of a particular key -- think searching request logs for error codes, Kubernetes reconcilers for the name and namespace of the reconciled object, etc - **Structured logging makes it easier to have cross-referencable logs**: Similarly to searchability, if you maintain conventions around your keys, it becomes easy to gather all log lines related to a particular concept. - **Structured logs allow better dimensions of filtering**: if you have structure to your logs, you've got more precise control over how much information is logged -- you might choose in a particular configuration to log certain keys but not others, only log lines where a certain key matches a certain value, etc, instead of just having v-levels and names to key off of. - **Structured logs better represent structured data**: sometimes, the data that you want to log is inherently structured (think tuple-link objects). Structured logs allow you to preserve that structure when outputting. ## Why V-levels? **V-levels give operators an easy way to control the chattiness of log operations**. V-levels provide a way for a given package to distinguish the relative importance or verbosity of a given log message. Then, if a particular logger or package is logging too many messages, the user of the package can simply change the v-levels for that library. ## Why not more named levels, like Warning? Read [Dave Cheney's post][warning-makes-no-sense]. Then read [Differences from Dave's ideas](#differences-from-daves-ideas). ## Why not allow format strings, too? **Format strings negate many of the benefits of structured logs**: - They're not easily searchable without resorting to fuzzy searching, regular expressions, etc - They don't store structured data well, since contents are flattened into a string - They're not cross-referencable - They don't compress easily, since the message is not constant (unless you turn positional parameters into key-value pairs with numerical keys, at which point you've gotten key-value logging with meaningless keys) ## Practical ## Why key-value pairs, and not a map? Key-value pairs are *much* easier to optimize, especially around allocations. Zap (a structured logger that inspired logr's interface) has [performance measurements](https://github.com/uber-go/zap#performance) that show this quite nicely. While the interface ends up being a little less obvious, you get potentially better performance, plus avoid making users type `map[string]string{}` every time they want to log. ## What if my V-levels differ between libraries? That's fine. Control your V-levels on a per-logger basis, and use the `WithName` function to pass different loggers to different libraries. Generally, you should take care to ensure that you have relatively consistent V-levels within a given logger, however, as this makes deciding on what verbosity of logs to request easier. ## But I *really* want to use a format string! That's not actually a question. Assuming your question is "how do I convert my mental model of logging with format strings to logging with constant messages": 1. figure out what the error actually is, as you'd write in a TL;DR style, and use that as a message 2. For every place you'd write a format specifier, look to the word before it, and add that as a key value pair For instance, consider the following examples (all taken from spots in the Kubernetes codebase): - `klog.V(4).Infof("Client is returning errors: code %v, error %v", responseCode, err)` becomes `logger.Error(err, "client returned an error", "code", responseCode)` - `klog.V(4).Infof("Got a Retry-After %ds response for attempt %d to %v", seconds, retries, url)` becomes `logger.V(4).Info("got a retry-after response when requesting url", "attempt", retries, "after seconds", seconds, "url", url)` If you *really* must use a format string, place it as a key value, and call `fmt.Sprintf` yourself -- for instance, `log.Printf("unable to reflect over type %T")` becomes `logger.Info("unable to reflect over type", "type", fmt.Sprintf("%T"))`. In general though, the cases where this is necessary should be few and far between. ## How do I choose my V-levels? This is basically the only hard constraint: increase V-levels to denote more verbose or more debug-y logs. Otherwise, you can start out with `0` as "you always want to see this", `1` as "common logging that you might *possibly* want to turn off", and `10` as "I would like to performance-test your log collection stack". Then gradually choose levels in between as you need them, working your way down from 10 (for debug and trace style logs) and up from 1 (for chattier info-type logs). ## How do I choose my keys - make your keys human-readable - constant keys are generally a good idea - be consistent across your codebase - keys should naturally match parts of the message string While key names are mostly unrestricted (and spaces are acceptable), it's generally a good idea to stick to printable ascii characters, or at least match the general character set of your log lines. [warning-makes-no-sense]: http://dave.cheney.net/2015/11/05/lets-talk-about-logging golang-github-go-logr-logr-0.4.0/discard.go000066400000000000000000000025351400440324000205030ustar00rootroot00000000000000/* Copyright 2020 The logr Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package logr // Discard returns a valid Logger that discards all messages logged to it. // It can be used whenever the caller is not interested in the logs. func Discard() Logger { return DiscardLogger{} } // DiscardLogger is a Logger that discards all messages. type DiscardLogger struct{} func (l DiscardLogger) Enabled() bool { return false } func (l DiscardLogger) Info(msg string, keysAndValues ...interface{}) { } func (l DiscardLogger) Error(err error, msg string, keysAndValues ...interface{}) { } func (l DiscardLogger) V(level int) Logger { return l } func (l DiscardLogger) WithValues(keysAndValues ...interface{}) Logger { return l } func (l DiscardLogger) WithName(name string) Logger { return l } // Verify that it actually implements the interface var _ Logger = DiscardLogger{} golang-github-go-logr-logr-0.4.0/discard_test.go000066400000000000000000000020431400440324000215340ustar00rootroot00000000000000/* Copyright 2020 The logr Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package logr import ( "errors" "testing" ) func TestDiscard(t *testing.T) { l := Discard() if _, ok := l.(DiscardLogger); !ok { t.Error("did not return the expected underlying type") } // Verify that none of the methods panic, there is not more we can test. l.WithName("discard").WithValues("z", 5).Info("Hello world") l.Info("Hello world", "x", 1, "y", 2) l.V(1).Error(errors.New("foo"), "a", 123) if l.Enabled() { t.Error("discard logger must always say it is disabled") } } golang-github-go-logr-logr-0.4.0/examples/000077500000000000000000000000001400440324000203545ustar00rootroot00000000000000golang-github-go-logr-logr-0.4.0/examples/tab_logger.go000066400000000000000000000041021400440324000230050ustar00rootroot00000000000000/* Copyright 2019 The logr Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package main import ( "fmt" "os" "text/tabwriter" "github.com/go-logr/logr" ) // TabLogger is a sample logr.Logger that logs to stderr. // It's terribly inefficient, and is *only* a basic example. type TabLogger struct { name string keyValues map[string]interface{} writer *tabwriter.Writer } var _ logr.Logger = &TabLogger{} func (l *TabLogger) Info(msg string, kvs ...interface{}) { fmt.Fprintf(l.writer, "%s\t%s\t", l.name, msg) for k, v := range l.keyValues { fmt.Fprintf(l.writer, "%s: %+v ", k, v) } for i := 0; i < len(kvs); i += 2 { fmt.Fprintf(l.writer, "%s: %+v ", kvs[i], kvs[i+1]) } fmt.Fprintf(l.writer, "\n") l.writer.Flush() } func (_ *TabLogger) Enabled() bool { return true } func (l *TabLogger) Error(err error, msg string, kvs ...interface{}) { kvs = append(kvs, "error", err) l.Info(msg, kvs...) } func (l *TabLogger) V(_ int) logr.Logger { return l } func (l *TabLogger) WithName(name string) logr.Logger { return &TabLogger{ name: l.name + "." + name, keyValues: l.keyValues, writer: l.writer, } } func (l *TabLogger) WithValues(kvs ...interface{}) logr.Logger { newMap := make(map[string]interface{}, len(l.keyValues)+len(kvs)/2) for k, v := range l.keyValues { newMap[k] = v } for i := 0; i < len(kvs); i += 2 { newMap[kvs[i].(string)] = kvs[i+1] } return &TabLogger{ name: l.name, keyValues: newMap, writer: l.writer, } } func NewTabLogger() logr.Logger { return &TabLogger{ writer: tabwriter.NewWriter(os.Stderr, 40, 8, 2, '\t', 0), } } golang-github-go-logr-logr-0.4.0/examples/usage_example.go000066400000000000000000000074051400440324000235300ustar00rootroot00000000000000/* Copyright 2019 The logr Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package main import ( "fmt" "math/rand" "time" "github.com/go-logr/logr" ) // This application demonstrates the usage of logger. // It's a simple reconciliation loop that pretends to // receive notifications about updates from a some API // server, make some changes, and then submit updates of // its own. // This uses object-based logging. It's also possible // (but a bit trickier) to use file-level "base" loggers. var objectMap = map[string]Object{ "obj1": Object{ Name: "obj1", Kind: "one", Details: 33, }, "obj2": Object{ Name: "obj2", Kind: "two", Details: "hi", }, "obj3": Object{ Name: "obj3", Kind: "one", Details: 1, }, } type Object struct { Name string Kind string Details interface{} } type Client struct { objects map[string]Object log logr.Logger } func (c *Client) Get(key string) (Object, error) { c.log.V(1).Info("fetching object", "key", key) obj, ok := c.objects[key] if !ok { return Object{}, fmt.Errorf("no object %s exists", key) } c.log.V(1).Info("pretending to deserialize object", "key", key, "json", "[insert real json here]") return obj, nil } func (c *Client) Save(obj Object) error { c.log.V(1).Info("saving object", "key", obj.Name, "object", obj) if rand.Intn(2) == 0 { return fmt.Errorf("couldn't save to %s", obj.Name) } c.log.V(1).Info("pretending to post object", "key", obj.Name, "url", "https://fake.test") return nil } func (c *Client) WatchNext() string { time.Sleep(2*time.Second) keyInd := rand.Intn(len(c.objects)) currInd := 0 for key := range c.objects { if currInd == keyInd { return key } currInd++ } c.log.Info("watch ended") return "" } type Controller struct { log logr.Logger expectedKind string client *Client } func (c *Controller) Run() { c.log.Info("starting reconciliation") for key := c.client.WatchNext(); key != ""; key = c.client.WatchNext() { // we can make more specific loggers if we always want to attach a particular named value log := c.log.WithValues("key", key) // fetch our object obj, err := c.client.Get(key) if err != nil { log.Error(err, "unable to reconcile object") continue } // make sure it's as expected if obj.Kind != c.expectedKind { log.Error(nil, "got object that wasn't expected kind", "actual-kind", obj.Kind, "object", obj) continue } // always log the object with log messages log = log.WithValues("object", obj) log.V(1).Info("reconciling object for key") // Do some complicated updates updates obj.Details = obj.Details.(int) * 2 // actually save the updates log.V(1).Info("updating object", "details", obj.Details) if err := c.client.Save(obj); err != nil { log.Error(err, "unable to reconcile object") } } c.log.Info("stopping reconciliation") } func NewController(log logr.Logger, objectKind string) *Controller { ctrlLogger := log.WithName("controller").WithName(objectKind) client := &Client{ log: ctrlLogger.WithName("client"), objects: objectMap, } return &Controller{ log: ctrlLogger, expectedKind: objectKind, client: client, } } func main() { // use a fake implementation just for demonstration purposes log := NewTabLogger() // update objects with the "one" kind ctrl := NewController(log, "one") ctrl.Run() } golang-github-go-logr-logr-0.4.0/go.mod000066400000000000000000000000501400440324000176370ustar00rootroot00000000000000module github.com/go-logr/logr go 1.14 golang-github-go-logr-logr-0.4.0/logr.go000066400000000000000000000265601400440324000200410ustar00rootroot00000000000000/* Copyright 2019 The logr Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ // This design derives from Dave Cheney's blog: // http://dave.cheney.net/2015/11/05/lets-talk-about-logging // // This is a BETA grade API. Until there is a significant 2nd implementation, // I don't really know how it will change. // Package logr defines abstract interfaces for logging. Packages can depend on // these interfaces and callers can implement logging in whatever way is // appropriate. // // Usage // // Logging is done using a Logger. Loggers can have name prefixes and named // values attached, so that all log messages logged with that Logger have some // base context associated. // // The term "key" is used to refer to the name associated with a particular // value, to disambiguate it from the general Logger name. // // For instance, suppose we're trying to reconcile the state of an object, and // we want to log that we've made some decision. // // With the traditional log package, we might write: // log.Printf("decided to set field foo to value %q for object %s/%s", // targetValue, object.Namespace, object.Name) // // With logr's structured logging, we'd write: // // elsewhere in the file, set up the logger to log with the prefix of // // "reconcilers", and the named value target-type=Foo, for extra context. // log := mainLogger.WithName("reconcilers").WithValues("target-type", "Foo") // // // later on... // log.Info("setting foo on object", "value", targetValue, "object", object) // // Depending on our logging implementation, we could then make logging decisions // based on field values (like only logging such events for objects in a certain // namespace), or copy the structured information into a structured log store. // // For logging errors, Logger has a method called Error. Suppose we wanted to // log an error while reconciling. With the traditional log package, we might // write: // log.Errorf("unable to reconcile object %s/%s: %v", object.Namespace, object.Name, err) // // With logr, we'd instead write: // // assuming the above setup for log // log.Error(err, "unable to reconcile object", "object", object) // // This functions similarly to: // log.Info("unable to reconcile object", "error", err, "object", object) // // However, it ensures that a standard key for the error value ("error") is used // across all error logging. Furthermore, certain implementations may choose to // attach additional information (such as stack traces) on calls to Error, so // it's preferred to use Error to log errors. // // Parts of a log line // // Each log message from a Logger has four types of context: // logger name, log verbosity, log message, and the named values. // // The Logger name consists of a series of name "segments" added by successive // calls to WithName. These name segments will be joined in some way by the // underlying implementation. It is strongly recommended that name segments // contain simple identifiers (letters, digits, and hyphen), and do not contain // characters that could muddle the log output or confuse the joining operation // (e.g. whitespace, commas, periods, slashes, brackets, quotes, etc). // // Log verbosity represents how little a log matters. Level zero, the default, // matters most. Increasing levels matter less and less. Try to avoid lots of // different verbosity levels, and instead provide useful keys, logger names, // and log messages for users to filter on. It's illegal to pass a log level // below zero. // // The log message consists of a constant message attached to the log line. // This should generally be a simple description of what's occurring, and should // never be a format string. // // Variable information can then be attached using named values (key/value // pairs). Keys are arbitrary strings, while values may be any Go value. // // Key Naming Conventions // // Keys are not strictly required to conform to any specification or regex, but // it is recommended that they: // * be human-readable and meaningful (not auto-generated or simple ordinals) // * be constant (not dependent on input data) // * contain only printable characters // * not contain whitespace or punctuation // // These guidelines help ensure that log data is processed properly regardless // of the log implementation. For example, log implementations will try to // output JSON data or will store data for later database (e.g. SQL) queries. // // While users are generally free to use key names of their choice, it's // generally best to avoid using the following keys, as they're frequently used // by implementations: // // * `"caller"`: the calling information (file/line) of a particular log line. // * `"error"`: the underlying error value in the `Error` method. // * `"level"`: the log level. // * `"logger"`: the name of the associated logger. // * `"msg"`: the log message. // * `"stacktrace"`: the stack trace associated with a particular log line or // error (often from the `Error` message). // * `"ts"`: the timestamp for a log line. // // Implementations are encouraged to make use of these keys to represent the // above concepts, when necessary (for example, in a pure-JSON output form, it // would be necessary to represent at least message and timestamp as ordinary // named values). // // Implementations may choose to give callers access to the underlying // logging implementation. The recommended pattern for this is: // // Underlier exposes access to the underlying logging implementation. // // Since callers only have a logr.Logger, they have to know which // // implementation is in use, so this interface is less of an abstraction // // and more of way to test type conversion. // type Underlier interface { // GetUnderlying() // } package logr import ( "context" ) // TODO: consider adding back in format strings if they're really needed // TODO: consider other bits of zap/zapcore functionality like ObjectMarshaller (for arbitrary objects) // TODO: consider other bits of glog functionality like Flush, OutputStats // Logger represents the ability to log messages, both errors and not. type Logger interface { // Enabled tests whether this Logger is enabled. For example, commandline // flags might be used to set the logging verbosity and disable some info // logs. Enabled() bool // Info logs a non-error message with the given key/value pairs as context. // // The msg argument should be used to add some constant description to // the log line. The key/value pairs can then be used to add additional // variable information. The key/value pairs should alternate string // keys and arbitrary values. Info(msg string, keysAndValues ...interface{}) // Error logs an error, with the given message and key/value pairs as context. // It functions similarly to calling Info with the "error" named value, but may // have unique behavior, and should be preferred for logging errors (see the // package documentations for more information). // // The msg field should be used to add context to any underlying error, // while the err field should be used to attach the actual error that // triggered this log line, if present. Error(err error, msg string, keysAndValues ...interface{}) // V returns an Logger value for a specific verbosity level, relative to // this Logger. In other words, V values are additive. V higher verbosity // level means a log message is less important. It's illegal to pass a log // level less than zero. V(level int) Logger // WithValues adds some key-value pairs of context to a logger. // See Info for documentation on how key/value pairs work. WithValues(keysAndValues ...interface{}) Logger // WithName adds a new element to the logger's name. // Successive calls with WithName continue to append // suffixes to the logger's name. It's strongly recommended // that name segments contain only letters, digits, and hyphens // (see the package documentation for more information). WithName(name string) Logger } // InfoLogger provides compatibility with code that relies on the v0.1.0 // interface. // // Deprecated: InfoLogger is an artifact of early versions of this API. New // users should never use it and existing users should use Logger instead. This // will be removed in a future release. type InfoLogger = Logger type contextKey struct{} // FromContext returns a Logger constructed from ctx or nil if no // logger details are found. func FromContext(ctx context.Context) Logger { if v, ok := ctx.Value(contextKey{}).(Logger); ok { return v } return nil } // FromContextOrDiscard returns a Logger constructed from ctx or a Logger // that discards all messages if no logger details are found. func FromContextOrDiscard(ctx context.Context) Logger { if v, ok := ctx.Value(contextKey{}).(Logger); ok { return v } return Discard() } // NewContext returns a new context derived from ctx that embeds the Logger. func NewContext(ctx context.Context, l Logger) context.Context { return context.WithValue(ctx, contextKey{}, l) } // CallDepthLogger represents a Logger that knows how to climb the call stack // to identify the original call site and can offset the depth by a specified // number of frames. This is useful for users who have helper functions // between the "real" call site and the actual calls to Logger methods. // Implementations that log information about the call site (such as file, // function, or line) would otherwise log information about the intermediate // helper functions. // // This is an optional interface and implementations are not required to // support it. type CallDepthLogger interface { Logger // WithCallDepth returns a Logger that will offset the call stack by the // specified number of frames when logging call site information. If depth // is 0 the attribution should be to the direct caller of this method. If // depth is 1 the attribution should skip 1 call frame, and so on. // Successive calls to this are additive. WithCallDepth(depth int) Logger } // WithCallDepth returns a Logger that will offset the call stack by the // specified number of frames when logging call site information, if possible. // This is useful for users who have helper functions between the "real" call // site and the actual calls to Logger methods. If depth is 0 the attribution // should be to the direct caller of this function. If depth is 1 the // attribution should skip 1 call frame, and so on. Successive calls to this // are additive. // // If the underlying log implementation supports the CallDepthLogger interface, // the WithCallDepth method will be called and the result returned. If the // implementation does not support CallDepthLogger, the original Logger will be // returned. // // Callers which care about whether this was supported or not should test for // CallDepthLogger support themselves. func WithCallDepth(logger Logger, depth int) Logger { if decorator, ok := logger.(CallDepthLogger); ok { return decorator.WithCallDepth(depth) } return logger } golang-github-go-logr-logr-0.4.0/logr_test.go000066400000000000000000000060311400440324000210670ustar00rootroot00000000000000/* Copyright 2021 The logr Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package logr import ( "context" "testing" ) // testLogger is a Logger just for testing that does nothing. type testLogger struct{} func (l *testLogger) Enabled() bool { return false } func (l *testLogger) Info(msg string, keysAndValues ...interface{}) { } func (l *testLogger) Error(err error, msg string, keysAndValues ...interface{}) { } func (l *testLogger) V(level int) Logger { return l } func (l *testLogger) WithValues(keysAndValues ...interface{}) Logger { return l } func (l *testLogger) WithName(name string) Logger { return l } // Verify that it actually implements the interface var _ Logger = &testLogger{} func TestContext(t *testing.T) { ctx := context.TODO() if out := FromContext(ctx); out != nil { t.Errorf("expected nil logger, got %#v", out) } if out := FromContextOrDiscard(ctx); out == nil { t.Errorf("expected non-nil logger") } else if _, ok := out.(DiscardLogger); !ok { t.Errorf("expected a DiscardLogger, got %#v", out) } logger := &testLogger{} lctx := NewContext(ctx, logger) if out := FromContext(lctx); out == nil { t.Errorf("expected non-nil logger") } else if out.(*testLogger) != logger { t.Errorf("expected output to be the same as input: got in=%p, out=%p", logger, out) } if out := FromContextOrDiscard(lctx); out == nil { t.Errorf("expected non-nil logger") } else if out.(*testLogger) != logger { t.Errorf("expected output to be the same as input: got in=%p, out=%p", logger, out) } } // testCallDepthLogger is a Logger just for testing that does nothing. type testCallDepthLogger struct { *testLogger depth int } func (l *testCallDepthLogger) WithCallDepth(depth int) Logger { return &testCallDepthLogger{l.testLogger, l.depth + depth} } // Verify that it actually implements the interface var _ CallDepthLogger = &testCallDepthLogger{} func TestWithCallDepth(t *testing.T) { // Test an impl that does not support it. t.Run("not supported", func(t *testing.T) { in := &testLogger{} out := WithCallDepth(in, 42) if out.(*testLogger) != in { t.Errorf("expected output to be the same as input: got in=%p, out=%p", in, out) } }) // Test an impl that does support it. t.Run("supported", func(t *testing.T) { in := &testCallDepthLogger{&testLogger{}, 0} out := WithCallDepth(in, 42) if out.(*testCallDepthLogger) == in { t.Errorf("expected output to be different than input: got in=out=%p", in) } if cdl := out.(*testCallDepthLogger); cdl.depth != 42 { t.Errorf("expected depth=42, got %d", cdl.depth) } }) } golang-github-go-logr-logr-0.4.0/testing/000077500000000000000000000000001400440324000202135ustar00rootroot00000000000000golang-github-go-logr-logr-0.4.0/testing/null.go000066400000000000000000000015501400440324000215150ustar00rootroot00000000000000/* Copyright 2019 The logr Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package testing import "github.com/go-logr/logr" // NullLogger is a logr.Logger that does nothing. // // Deprecated: NullLogger is idenitcal to logr.DiscardLogger. It is retained // for backwards compatibility, but new users should use logr.DiscardLogger // instead. type NullLogger = logr.DiscardLogger golang-github-go-logr-logr-0.4.0/testing/test.go000066400000000000000000000023771400440324000215320ustar00rootroot00000000000000/* Copyright 2019 The logr Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package testing import ( "testing" "github.com/go-logr/logr" ) // TestLogger is a logr.Logger that prints through a testing.T object. // Only error logs will have any effect. type TestLogger struct { T *testing.T } var _ logr.Logger = TestLogger{} func (_ TestLogger) Info(_ string, _ ...interface{}) { // Do nothing. } func (_ TestLogger) Enabled() bool { return false } func (log TestLogger) Error(err error, msg string, args ...interface{}) { log.T.Logf("%s: %v -- %v", msg, err, args) } func (log TestLogger) V(v int) logr.Logger { return log } func (log TestLogger) WithName(_ string) logr.Logger { return log } func (log TestLogger) WithValues(_ ...interface{}) logr.Logger { return log }