deps: Update otelgrpc
Signed-off-by: Bryant Biggs <bryantbiggs@gmail.com>
This commit is contained in:
		
							
								
								
									
										67
									
								
								vendor/github.com/go-logr/logr/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										67
									
								
								vendor/github.com/go-logr/logr/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -91,11 +91,12 @@ logr design but also left out some parts and changed others:
 | 
			
		||||
| Adding a name to a logger | `WithName` | no API |
 | 
			
		||||
| Modify verbosity of log entries in a call chain | `V` | no API |
 | 
			
		||||
| Grouping of key/value pairs | not supported | `WithGroup`, `GroupValue` |
 | 
			
		||||
| Pass context for extracting additional values | no API | API variants like `InfoCtx` |
 | 
			
		||||
 | 
			
		||||
The high-level slog API is explicitly meant to be one of many different APIs
 | 
			
		||||
that can be layered on top of a shared `slog.Handler`. logr is one such
 | 
			
		||||
alternative API, with [interoperability](#slog-interoperability) provided by the [`slogr`](slogr)
 | 
			
		||||
package.
 | 
			
		||||
alternative API, with [interoperability](#slog-interoperability) provided by
 | 
			
		||||
some conversion functions.
 | 
			
		||||
 | 
			
		||||
### Inspiration
 | 
			
		||||
 | 
			
		||||
@@ -145,24 +146,24 @@ There are implementations for the following logging libraries:
 | 
			
		||||
## slog interoperability
 | 
			
		||||
 | 
			
		||||
Interoperability goes both ways, using the `logr.Logger` API with a `slog.Handler`
 | 
			
		||||
and using the `slog.Logger` API with a `logr.LogSink`. [slogr](./slogr) provides `NewLogr` and
 | 
			
		||||
`NewSlogHandler` API calls to convert between a `logr.Logger` and a `slog.Handler`.
 | 
			
		||||
and using the `slog.Logger` API with a `logr.LogSink`. `FromSlogHandler` and
 | 
			
		||||
`ToSlogHandler` convert between a `logr.Logger` and a `slog.Handler`.
 | 
			
		||||
As usual, `slog.New` can be used to wrap such a `slog.Handler` in the high-level
 | 
			
		||||
slog API. `slogr` itself leaves that to the caller.
 | 
			
		||||
slog API.
 | 
			
		||||
 | 
			
		||||
## Using a `logr.Sink` as backend for slog
 | 
			
		||||
### Using a `logr.LogSink` as backend for slog
 | 
			
		||||
 | 
			
		||||
Ideally, a logr sink implementation should support both logr and slog by
 | 
			
		||||
implementing both the normal logr interface(s) and `slogr.SlogSink`.  Because
 | 
			
		||||
implementing both the normal logr interface(s) and `SlogSink`.  Because
 | 
			
		||||
of a conflict in the parameters of the common `Enabled` method, it is [not
 | 
			
		||||
possible to implement both slog.Handler and logr.Sink in the same
 | 
			
		||||
type](https://github.com/golang/go/issues/59110).
 | 
			
		||||
 | 
			
		||||
If both are supported, log calls can go from the high-level APIs to the backend
 | 
			
		||||
without the need to convert parameters. `NewLogr` and `NewSlogHandler` can
 | 
			
		||||
without the need to convert parameters. `FromSlogHandler` and `ToSlogHandler` can
 | 
			
		||||
convert back and forth without adding additional wrappers, with one exception:
 | 
			
		||||
when `Logger.V` was used to adjust the verbosity for a `slog.Handler`, then
 | 
			
		||||
`NewSlogHandler` has to use a wrapper which adjusts the verbosity for future
 | 
			
		||||
`ToSlogHandler` has to use a wrapper which adjusts the verbosity for future
 | 
			
		||||
log calls.
 | 
			
		||||
 | 
			
		||||
Such an implementation should also support values that implement specific
 | 
			
		||||
@@ -187,13 +188,13 @@ Not supporting slog has several drawbacks:
 | 
			
		||||
These drawbacks are severe enough that applications using a mixture of slog and
 | 
			
		||||
logr should switch to a different backend.
 | 
			
		||||
 | 
			
		||||
## Using a `slog.Handler` as backend for logr
 | 
			
		||||
### Using a `slog.Handler` as backend for logr
 | 
			
		||||
 | 
			
		||||
Using a plain `slog.Handler` without support for logr works better than the
 | 
			
		||||
other direction:
 | 
			
		||||
- All logr verbosity levels can be mapped 1:1 to their corresponding slog level
 | 
			
		||||
  by negating them.
 | 
			
		||||
- Stack unwinding is done by the `slogr.SlogSink` and the resulting program
 | 
			
		||||
- Stack unwinding is done by the `SlogSink` and the resulting program
 | 
			
		||||
  counter is passed to the `slog.Handler`.
 | 
			
		||||
- Names added via `Logger.WithName` are gathered and recorded in an additional
 | 
			
		||||
  attribute with `logger` as key and the names separated by slash as value.
 | 
			
		||||
@@ -205,27 +206,39 @@ ideally support both `logr.Marshaler` and `slog.Valuer`. If compatibility
 | 
			
		||||
with logr implementations without slog support is not important, then
 | 
			
		||||
`slog.Valuer` is sufficient.
 | 
			
		||||
 | 
			
		||||
## Context support for slog
 | 
			
		||||
### Context support for slog
 | 
			
		||||
 | 
			
		||||
Storing a logger in a `context.Context` is not supported by
 | 
			
		||||
slog. `logr.NewContext` and `logr.FromContext` can be used with slog like this
 | 
			
		||||
to fill this gap:
 | 
			
		||||
slog. `NewContextWithSlogLogger` and `FromContextAsSlogLogger` can be
 | 
			
		||||
used to fill this gap. They store and retrieve a `slog.Logger` pointer
 | 
			
		||||
under the same context key that is also used by `NewContext` and
 | 
			
		||||
`FromContext` for `logr.Logger` value.
 | 
			
		||||
 | 
			
		||||
    func HandlerFromContext(ctx context.Context) slog.Handler {
 | 
			
		||||
        logger, err := logr.FromContext(ctx)
 | 
			
		||||
        if err == nil {
 | 
			
		||||
            return slogr.NewSlogHandler(logger)
 | 
			
		||||
        }
 | 
			
		||||
        return slog.Default().Handler()
 | 
			
		||||
    }
 | 
			
		||||
When `NewContextWithSlogLogger` is followed by `FromContext`, the latter will
 | 
			
		||||
automatically convert the `slog.Logger` to a
 | 
			
		||||
`logr.Logger`. `FromContextAsSlogLogger` does the same for the other direction.
 | 
			
		||||
 | 
			
		||||
    func ContextWithHandler(ctx context.Context, handler slog.Handler) context.Context {
 | 
			
		||||
        return logr.NewContext(ctx, slogr.NewLogr(handler))
 | 
			
		||||
    }
 | 
			
		||||
With this approach, binaries which use either slog or logr are as efficient as
 | 
			
		||||
possible with no unnecessary allocations. This is also why the API stores a
 | 
			
		||||
`slog.Logger` pointer: when storing a `slog.Handler`, creating a `slog.Logger`
 | 
			
		||||
on retrieval would need to allocate one.
 | 
			
		||||
 | 
			
		||||
The downside is that storing and retrieving a `slog.Handler` needs more
 | 
			
		||||
allocations compared to using a `logr.Logger`. Therefore the recommendation is
 | 
			
		||||
to use the `logr.Logger` API in code which uses contextual logging.
 | 
			
		||||
The downside is that switching back and forth needs more allocations. Because
 | 
			
		||||
logr is the API that is already in use by different packages, in particular
 | 
			
		||||
Kubernetes, the recommendation is to use the `logr.Logger` API in code which
 | 
			
		||||
uses contextual logging.
 | 
			
		||||
 | 
			
		||||
An alternative to adding values to a logger and storing that logger in the
 | 
			
		||||
context is to store the values in the context and to configure a logging
 | 
			
		||||
backend to extract those values when emitting log entries. This only works when
 | 
			
		||||
log calls are passed the context, which is not supported by the logr API.
 | 
			
		||||
 | 
			
		||||
With the slog API, it is possible, but not
 | 
			
		||||
required. https://github.com/veqryn/slog-context is a package for slog which
 | 
			
		||||
provides additional support code for this approach. It also contains wrappers
 | 
			
		||||
for the context functions in logr, so developers who prefer to not use the logr
 | 
			
		||||
APIs directly can use those instead and the resulting code will still be
 | 
			
		||||
interoperable with logr.
 | 
			
		||||
 | 
			
		||||
## FAQ
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										33
									
								
								vendor/github.com/go-logr/logr/context.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								vendor/github.com/go-logr/logr/context.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2023 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
 | 
			
		||||
 | 
			
		||||
// contextKey is how we find Loggers in a context.Context. With Go < 1.21,
 | 
			
		||||
// the value is always a Logger value. With Go >= 1.21, the value can be a
 | 
			
		||||
// Logger value or a slog.Logger pointer.
 | 
			
		||||
type contextKey struct{}
 | 
			
		||||
 | 
			
		||||
// notFoundError exists to carry an IsNotFound method.
 | 
			
		||||
type notFoundError struct{}
 | 
			
		||||
 | 
			
		||||
func (notFoundError) Error() string {
 | 
			
		||||
	return "no logr.Logger was present"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (notFoundError) IsNotFound() bool {
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										49
									
								
								vendor/github.com/go-logr/logr/context_noslog.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								vendor/github.com/go-logr/logr/context_noslog.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
			
		||||
//go:build !go1.21
 | 
			
		||||
// +build !go1.21
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
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 logr
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// FromContext returns a Logger from ctx or an error if no Logger is found.
 | 
			
		||||
func FromContext(ctx context.Context) (Logger, error) {
 | 
			
		||||
	if v, ok := ctx.Value(contextKey{}).(Logger); ok {
 | 
			
		||||
		return v, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return Logger{}, notFoundError{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FromContextOrDiscard returns a Logger from ctx.  If no Logger is found, this
 | 
			
		||||
// returns a Logger that discards all log messages.
 | 
			
		||||
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, which carries the
 | 
			
		||||
// provided Logger.
 | 
			
		||||
func NewContext(ctx context.Context, logger Logger) context.Context {
 | 
			
		||||
	return context.WithValue(ctx, contextKey{}, logger)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										83
									
								
								vendor/github.com/go-logr/logr/context_slog.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								vendor/github.com/go-logr/logr/context_slog.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,83 @@
 | 
			
		||||
//go:build go1.21
 | 
			
		||||
// +build go1.21
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
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 logr
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log/slog"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// FromContext returns a Logger from ctx or an error if no Logger is found.
 | 
			
		||||
func FromContext(ctx context.Context) (Logger, error) {
 | 
			
		||||
	v := ctx.Value(contextKey{})
 | 
			
		||||
	if v == nil {
 | 
			
		||||
		return Logger{}, notFoundError{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch v := v.(type) {
 | 
			
		||||
	case Logger:
 | 
			
		||||
		return v, nil
 | 
			
		||||
	case *slog.Logger:
 | 
			
		||||
		return FromSlogHandler(v.Handler()), nil
 | 
			
		||||
	default:
 | 
			
		||||
		// Not reached.
 | 
			
		||||
		panic(fmt.Sprintf("unexpected value type for logr context key: %T", v))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FromContextAsSlogLogger returns a slog.Logger from ctx or nil if no such Logger is found.
 | 
			
		||||
func FromContextAsSlogLogger(ctx context.Context) *slog.Logger {
 | 
			
		||||
	v := ctx.Value(contextKey{})
 | 
			
		||||
	if v == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch v := v.(type) {
 | 
			
		||||
	case Logger:
 | 
			
		||||
		return slog.New(ToSlogHandler(v))
 | 
			
		||||
	case *slog.Logger:
 | 
			
		||||
		return v
 | 
			
		||||
	default:
 | 
			
		||||
		// Not reached.
 | 
			
		||||
		panic(fmt.Sprintf("unexpected value type for logr context key: %T", v))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FromContextOrDiscard returns a Logger from ctx.  If no Logger is found, this
 | 
			
		||||
// returns a Logger that discards all log messages.
 | 
			
		||||
func FromContextOrDiscard(ctx context.Context) Logger {
 | 
			
		||||
	if logger, err := FromContext(ctx); err == nil {
 | 
			
		||||
		return logger
 | 
			
		||||
	}
 | 
			
		||||
	return Discard()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewContext returns a new Context, derived from ctx, which carries the
 | 
			
		||||
// provided Logger.
 | 
			
		||||
func NewContext(ctx context.Context, logger Logger) context.Context {
 | 
			
		||||
	return context.WithValue(ctx, contextKey{}, logger)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewContextWithSlogLogger returns a new Context, derived from ctx, which carries the
 | 
			
		||||
// provided slog.Logger.
 | 
			
		||||
func NewContextWithSlogLogger(ctx context.Context, logger *slog.Logger) context.Context {
 | 
			
		||||
	return context.WithValue(ctx, contextKey{}, logger)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										189
									
								
								vendor/github.com/go-logr/logr/funcr/funcr.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										189
									
								
								vendor/github.com/go-logr/logr/funcr/funcr.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -100,6 +100,11 @@ type Options struct {
 | 
			
		||||
	// details, see docs for Go's time.Layout.
 | 
			
		||||
	TimestampFormat string
 | 
			
		||||
 | 
			
		||||
	// LogInfoLevel tells funcr what key to use to log the info level.
 | 
			
		||||
	// If not specified, the info level will be logged as "level".
 | 
			
		||||
	// If this is set to "", the info level will not be logged at all.
 | 
			
		||||
	LogInfoLevel *string
 | 
			
		||||
 | 
			
		||||
	// Verbosity tells funcr which V logs to produce.  Higher values enable
 | 
			
		||||
	// more logs.  Info logs at or below this level will be written, while logs
 | 
			
		||||
	// above this level will be discarded.
 | 
			
		||||
@@ -213,6 +218,10 @@ func newFormatter(opts Options, outfmt outputFormat) Formatter {
 | 
			
		||||
	if opts.MaxLogDepth == 0 {
 | 
			
		||||
		opts.MaxLogDepth = defaultMaxLogDepth
 | 
			
		||||
	}
 | 
			
		||||
	if opts.LogInfoLevel == nil {
 | 
			
		||||
		opts.LogInfoLevel = new(string)
 | 
			
		||||
		*opts.LogInfoLevel = "level"
 | 
			
		||||
	}
 | 
			
		||||
	f := Formatter{
 | 
			
		||||
		outputFormat: outfmt,
 | 
			
		||||
		prefix:       "",
 | 
			
		||||
@@ -227,12 +236,15 @@ func newFormatter(opts Options, outfmt outputFormat) Formatter {
 | 
			
		||||
// implementation. It should be constructed with NewFormatter. Some of
 | 
			
		||||
// its methods directly implement logr.LogSink.
 | 
			
		||||
type Formatter struct {
 | 
			
		||||
	outputFormat outputFormat
 | 
			
		||||
	prefix       string
 | 
			
		||||
	values       []any
 | 
			
		||||
	valuesStr    string
 | 
			
		||||
	depth        int
 | 
			
		||||
	opts         *Options
 | 
			
		||||
	outputFormat    outputFormat
 | 
			
		||||
	prefix          string
 | 
			
		||||
	values          []any
 | 
			
		||||
	valuesStr       string
 | 
			
		||||
	parentValuesStr string
 | 
			
		||||
	depth           int
 | 
			
		||||
	opts            *Options
 | 
			
		||||
	group           string // for slog groups
 | 
			
		||||
	groupDepth      int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// outputFormat indicates which outputFormat to use.
 | 
			
		||||
@@ -253,33 +265,62 @@ func (f Formatter) render(builtins, args []any) string {
 | 
			
		||||
	// Empirically bytes.Buffer is faster than strings.Builder for this.
 | 
			
		||||
	buf := bytes.NewBuffer(make([]byte, 0, 1024))
 | 
			
		||||
	if f.outputFormat == outputJSON {
 | 
			
		||||
		buf.WriteByte('{')
 | 
			
		||||
		buf.WriteByte('{') // for the whole line
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vals := builtins
 | 
			
		||||
	if hook := f.opts.RenderBuiltinsHook; hook != nil {
 | 
			
		||||
		vals = hook(f.sanitize(vals))
 | 
			
		||||
	}
 | 
			
		||||
	f.flatten(buf, vals, false, false) // keys are ours, no need to escape
 | 
			
		||||
	continuing := len(builtins) > 0
 | 
			
		||||
	if len(f.valuesStr) > 0 {
 | 
			
		||||
 | 
			
		||||
	if f.parentValuesStr != "" {
 | 
			
		||||
		if continuing {
 | 
			
		||||
			if f.outputFormat == outputJSON {
 | 
			
		||||
				buf.WriteByte(',')
 | 
			
		||||
			} else {
 | 
			
		||||
				buf.WriteByte(' ')
 | 
			
		||||
			}
 | 
			
		||||
			buf.WriteByte(f.comma())
 | 
			
		||||
		}
 | 
			
		||||
		buf.WriteString(f.parentValuesStr)
 | 
			
		||||
		continuing = true
 | 
			
		||||
		buf.WriteString(f.valuesStr)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	groupDepth := f.groupDepth
 | 
			
		||||
	if f.group != "" {
 | 
			
		||||
		if f.valuesStr != "" || len(args) != 0 {
 | 
			
		||||
			if continuing {
 | 
			
		||||
				buf.WriteByte(f.comma())
 | 
			
		||||
			}
 | 
			
		||||
			buf.WriteString(f.quoted(f.group, true)) // escape user-provided keys
 | 
			
		||||
			buf.WriteByte(f.colon())
 | 
			
		||||
			buf.WriteByte('{') // for the group
 | 
			
		||||
			continuing = false
 | 
			
		||||
		} else {
 | 
			
		||||
			// The group was empty
 | 
			
		||||
			groupDepth--
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if f.valuesStr != "" {
 | 
			
		||||
		if continuing {
 | 
			
		||||
			buf.WriteByte(f.comma())
 | 
			
		||||
		}
 | 
			
		||||
		buf.WriteString(f.valuesStr)
 | 
			
		||||
		continuing = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vals = args
 | 
			
		||||
	if hook := f.opts.RenderArgsHook; hook != nil {
 | 
			
		||||
		vals = hook(f.sanitize(vals))
 | 
			
		||||
	}
 | 
			
		||||
	f.flatten(buf, vals, continuing, true) // escape user-provided keys
 | 
			
		||||
	if f.outputFormat == outputJSON {
 | 
			
		||||
		buf.WriteByte('}')
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < groupDepth; i++ {
 | 
			
		||||
		buf.WriteByte('}') // for the groups
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if f.outputFormat == outputJSON {
 | 
			
		||||
		buf.WriteByte('}') // for the whole line
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return buf.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -298,9 +339,16 @@ func (f Formatter) flatten(buf *bytes.Buffer, kvList []any, continuing bool, esc
 | 
			
		||||
	if len(kvList)%2 != 0 {
 | 
			
		||||
		kvList = append(kvList, noValue)
 | 
			
		||||
	}
 | 
			
		||||
	copied := false
 | 
			
		||||
	for i := 0; i < len(kvList); i += 2 {
 | 
			
		||||
		k, ok := kvList[i].(string)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			if !copied {
 | 
			
		||||
				newList := make([]any, len(kvList))
 | 
			
		||||
				copy(newList, kvList)
 | 
			
		||||
				kvList = newList
 | 
			
		||||
				copied = true
 | 
			
		||||
			}
 | 
			
		||||
			k = f.nonStringKey(kvList[i])
 | 
			
		||||
			kvList[i] = k
 | 
			
		||||
		}
 | 
			
		||||
@@ -308,7 +356,7 @@ func (f Formatter) flatten(buf *bytes.Buffer, kvList []any, continuing bool, esc
 | 
			
		||||
 | 
			
		||||
		if i > 0 || continuing {
 | 
			
		||||
			if f.outputFormat == outputJSON {
 | 
			
		||||
				buf.WriteByte(',')
 | 
			
		||||
				buf.WriteByte(f.comma())
 | 
			
		||||
			} else {
 | 
			
		||||
				// In theory the format could be something we don't understand.  In
 | 
			
		||||
				// practice, we control it, so it won't be.
 | 
			
		||||
@@ -316,24 +364,35 @@ func (f Formatter) flatten(buf *bytes.Buffer, kvList []any, continuing bool, esc
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if escapeKeys {
 | 
			
		||||
			buf.WriteString(prettyString(k))
 | 
			
		||||
		} else {
 | 
			
		||||
			// this is faster
 | 
			
		||||
			buf.WriteByte('"')
 | 
			
		||||
			buf.WriteString(k)
 | 
			
		||||
			buf.WriteByte('"')
 | 
			
		||||
		}
 | 
			
		||||
		if f.outputFormat == outputJSON {
 | 
			
		||||
			buf.WriteByte(':')
 | 
			
		||||
		} else {
 | 
			
		||||
			buf.WriteByte('=')
 | 
			
		||||
		}
 | 
			
		||||
		buf.WriteString(f.quoted(k, escapeKeys))
 | 
			
		||||
		buf.WriteByte(f.colon())
 | 
			
		||||
		buf.WriteString(f.pretty(v))
 | 
			
		||||
	}
 | 
			
		||||
	return kvList
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f Formatter) quoted(str string, escape bool) string {
 | 
			
		||||
	if escape {
 | 
			
		||||
		return prettyString(str)
 | 
			
		||||
	}
 | 
			
		||||
	// this is faster
 | 
			
		||||
	return `"` + str + `"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f Formatter) comma() byte {
 | 
			
		||||
	if f.outputFormat == outputJSON {
 | 
			
		||||
		return ','
 | 
			
		||||
	}
 | 
			
		||||
	return ' '
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f Formatter) colon() byte {
 | 
			
		||||
	if f.outputFormat == outputJSON {
 | 
			
		||||
		return ':'
 | 
			
		||||
	}
 | 
			
		||||
	return '='
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f Formatter) pretty(value any) string {
 | 
			
		||||
	return f.prettyWithFlags(value, 0, 0)
 | 
			
		||||
}
 | 
			
		||||
@@ -407,12 +466,12 @@ func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string {
 | 
			
		||||
		}
 | 
			
		||||
		for i := 0; i < len(v); i += 2 {
 | 
			
		||||
			if i > 0 {
 | 
			
		||||
				buf.WriteByte(',')
 | 
			
		||||
				buf.WriteByte(f.comma())
 | 
			
		||||
			}
 | 
			
		||||
			k, _ := v[i].(string) // sanitize() above means no need to check success
 | 
			
		||||
			// arbitrary keys might need escaping
 | 
			
		||||
			buf.WriteString(prettyString(k))
 | 
			
		||||
			buf.WriteByte(':')
 | 
			
		||||
			buf.WriteByte(f.colon())
 | 
			
		||||
			buf.WriteString(f.prettyWithFlags(v[i+1], 0, depth+1))
 | 
			
		||||
		}
 | 
			
		||||
		if flags&flagRawStruct == 0 {
 | 
			
		||||
@@ -481,7 +540,7 @@ func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			if printComma {
 | 
			
		||||
				buf.WriteByte(',')
 | 
			
		||||
				buf.WriteByte(f.comma())
 | 
			
		||||
			}
 | 
			
		||||
			printComma = true // if we got here, we are rendering a field
 | 
			
		||||
			if fld.Anonymous && fld.Type.Kind() == reflect.Struct && name == "" {
 | 
			
		||||
@@ -492,10 +551,8 @@ func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string {
 | 
			
		||||
				name = fld.Name
 | 
			
		||||
			}
 | 
			
		||||
			// field names can't contain characters which need escaping
 | 
			
		||||
			buf.WriteByte('"')
 | 
			
		||||
			buf.WriteString(name)
 | 
			
		||||
			buf.WriteByte('"')
 | 
			
		||||
			buf.WriteByte(':')
 | 
			
		||||
			buf.WriteString(f.quoted(name, false))
 | 
			
		||||
			buf.WriteByte(f.colon())
 | 
			
		||||
			buf.WriteString(f.prettyWithFlags(v.Field(i).Interface(), 0, depth+1))
 | 
			
		||||
		}
 | 
			
		||||
		if flags&flagRawStruct == 0 {
 | 
			
		||||
@@ -520,7 +577,7 @@ func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string {
 | 
			
		||||
		buf.WriteByte('[')
 | 
			
		||||
		for i := 0; i < v.Len(); i++ {
 | 
			
		||||
			if i > 0 {
 | 
			
		||||
				buf.WriteByte(',')
 | 
			
		||||
				buf.WriteByte(f.comma())
 | 
			
		||||
			}
 | 
			
		||||
			e := v.Index(i)
 | 
			
		||||
			buf.WriteString(f.prettyWithFlags(e.Interface(), 0, depth+1))
 | 
			
		||||
@@ -534,7 +591,7 @@ func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string {
 | 
			
		||||
		i := 0
 | 
			
		||||
		for it.Next() {
 | 
			
		||||
			if i > 0 {
 | 
			
		||||
				buf.WriteByte(',')
 | 
			
		||||
				buf.WriteByte(f.comma())
 | 
			
		||||
			}
 | 
			
		||||
			// If a map key supports TextMarshaler, use it.
 | 
			
		||||
			keystr := ""
 | 
			
		||||
@@ -556,7 +613,7 @@ func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string {
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			buf.WriteString(keystr)
 | 
			
		||||
			buf.WriteByte(':')
 | 
			
		||||
			buf.WriteByte(f.colon())
 | 
			
		||||
			buf.WriteString(f.prettyWithFlags(it.Value().Interface(), 0, depth+1))
 | 
			
		||||
			i++
 | 
			
		||||
		}
 | 
			
		||||
@@ -706,6 +763,53 @@ func (f Formatter) sanitize(kvList []any) []any {
 | 
			
		||||
	return kvList
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// startGroup opens a new group scope (basically a sub-struct), which locks all
 | 
			
		||||
// the current saved values and starts them anew.  This is needed to satisfy
 | 
			
		||||
// slog.
 | 
			
		||||
func (f *Formatter) startGroup(group string) {
 | 
			
		||||
	// Unnamed groups are just inlined.
 | 
			
		||||
	if group == "" {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Any saved values can no longer be changed.
 | 
			
		||||
	buf := bytes.NewBuffer(make([]byte, 0, 1024))
 | 
			
		||||
	continuing := false
 | 
			
		||||
 | 
			
		||||
	if f.parentValuesStr != "" {
 | 
			
		||||
		buf.WriteString(f.parentValuesStr)
 | 
			
		||||
		continuing = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if f.group != "" && f.valuesStr != "" {
 | 
			
		||||
		if continuing {
 | 
			
		||||
			buf.WriteByte(f.comma())
 | 
			
		||||
		}
 | 
			
		||||
		buf.WriteString(f.quoted(f.group, true)) // escape user-provided keys
 | 
			
		||||
		buf.WriteByte(f.colon())
 | 
			
		||||
		buf.WriteByte('{') // for the group
 | 
			
		||||
		continuing = false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if f.valuesStr != "" {
 | 
			
		||||
		if continuing {
 | 
			
		||||
			buf.WriteByte(f.comma())
 | 
			
		||||
		}
 | 
			
		||||
		buf.WriteString(f.valuesStr)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// NOTE: We don't close the scope here - that's done later, when a log line
 | 
			
		||||
	// is actually rendered (because we have N scopes to close).
 | 
			
		||||
 | 
			
		||||
	f.parentValuesStr = buf.String()
 | 
			
		||||
 | 
			
		||||
	// Start collecting new values.
 | 
			
		||||
	f.group = group
 | 
			
		||||
	f.groupDepth++
 | 
			
		||||
	f.valuesStr = ""
 | 
			
		||||
	f.values = nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Init configures this Formatter from runtime info, such as the call depth
 | 
			
		||||
// imposed by logr itself.
 | 
			
		||||
// Note that this receiver is a pointer, so depth can be saved.
 | 
			
		||||
@@ -740,7 +844,10 @@ func (f Formatter) FormatInfo(level int, msg string, kvList []any) (prefix, args
 | 
			
		||||
	if policy := f.opts.LogCaller; policy == All || policy == Info {
 | 
			
		||||
		args = append(args, "caller", f.caller())
 | 
			
		||||
	}
 | 
			
		||||
	args = append(args, "level", level, "msg", msg)
 | 
			
		||||
	if key := *f.opts.LogInfoLevel; key != "" {
 | 
			
		||||
		args = append(args, key, level)
 | 
			
		||||
	}
 | 
			
		||||
	args = append(args, "msg", msg)
 | 
			
		||||
	return prefix, f.render(args, kvList)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										105
									
								
								vendor/github.com/go-logr/logr/funcr/slogsink.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								vendor/github.com/go-logr/logr/funcr/slogsink.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,105 @@
 | 
			
		||||
//go:build go1.21
 | 
			
		||||
// +build go1.21
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2023 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 funcr
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"log/slog"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-logr/logr"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var _ logr.SlogSink = &fnlogger{}
 | 
			
		||||
 | 
			
		||||
const extraSlogSinkDepth = 3 // 2 for slog, 1 for SlogSink
 | 
			
		||||
 | 
			
		||||
func (l fnlogger) Handle(_ context.Context, record slog.Record) error {
 | 
			
		||||
	kvList := make([]any, 0, 2*record.NumAttrs())
 | 
			
		||||
	record.Attrs(func(attr slog.Attr) bool {
 | 
			
		||||
		kvList = attrToKVs(attr, kvList)
 | 
			
		||||
		return true
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	if record.Level >= slog.LevelError {
 | 
			
		||||
		l.WithCallDepth(extraSlogSinkDepth).Error(nil, record.Message, kvList...)
 | 
			
		||||
	} else {
 | 
			
		||||
		level := l.levelFromSlog(record.Level)
 | 
			
		||||
		l.WithCallDepth(extraSlogSinkDepth).Info(level, record.Message, kvList...)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l fnlogger) WithAttrs(attrs []slog.Attr) logr.SlogSink {
 | 
			
		||||
	kvList := make([]any, 0, 2*len(attrs))
 | 
			
		||||
	for _, attr := range attrs {
 | 
			
		||||
		kvList = attrToKVs(attr, kvList)
 | 
			
		||||
	}
 | 
			
		||||
	l.AddValues(kvList)
 | 
			
		||||
	return &l
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l fnlogger) WithGroup(name string) logr.SlogSink {
 | 
			
		||||
	l.startGroup(name)
 | 
			
		||||
	return &l
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// attrToKVs appends a slog.Attr to a logr-style kvList.  It handle slog Groups
 | 
			
		||||
// and other details of slog.
 | 
			
		||||
func attrToKVs(attr slog.Attr, kvList []any) []any {
 | 
			
		||||
	attrVal := attr.Value.Resolve()
 | 
			
		||||
	if attrVal.Kind() == slog.KindGroup {
 | 
			
		||||
		groupVal := attrVal.Group()
 | 
			
		||||
		grpKVs := make([]any, 0, 2*len(groupVal))
 | 
			
		||||
		for _, attr := range groupVal {
 | 
			
		||||
			grpKVs = attrToKVs(attr, grpKVs)
 | 
			
		||||
		}
 | 
			
		||||
		if attr.Key == "" {
 | 
			
		||||
			// slog says we have to inline these
 | 
			
		||||
			kvList = append(kvList, grpKVs...)
 | 
			
		||||
		} else {
 | 
			
		||||
			kvList = append(kvList, attr.Key, PseudoStruct(grpKVs))
 | 
			
		||||
		}
 | 
			
		||||
	} else if attr.Key != "" {
 | 
			
		||||
		kvList = append(kvList, attr.Key, attrVal.Any())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return kvList
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// levelFromSlog adjusts the level by the logger's verbosity and negates it.
 | 
			
		||||
// It ensures that the result is >= 0. This is necessary because the result is
 | 
			
		||||
// passed to a LogSink and that API did not historically document whether
 | 
			
		||||
// levels could be negative or what that meant.
 | 
			
		||||
//
 | 
			
		||||
// Some example usage:
 | 
			
		||||
//
 | 
			
		||||
//	logrV0 := getMyLogger()
 | 
			
		||||
//	logrV2 := logrV0.V(2)
 | 
			
		||||
//	slogV2 := slog.New(logr.ToSlogHandler(logrV2))
 | 
			
		||||
//	slogV2.Debug("msg") // =~ logrV2.V(4) =~ logrV0.V(6)
 | 
			
		||||
//	slogV2.Info("msg")  // =~  logrV2.V(0) =~ logrV0.V(2)
 | 
			
		||||
//	slogv2.Warn("msg")  // =~ logrV2.V(-4) =~ logrV0.V(0)
 | 
			
		||||
func (l fnlogger) levelFromSlog(level slog.Level) int {
 | 
			
		||||
	result := -level
 | 
			
		||||
	if result < 0 {
 | 
			
		||||
		result = 0 // because LogSink doesn't expect negative V levels
 | 
			
		||||
	}
 | 
			
		||||
	return int(result)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										43
									
								
								vendor/github.com/go-logr/logr/logr.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										43
									
								
								vendor/github.com/go-logr/logr/logr.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -207,10 +207,6 @@ limitations under the License.
 | 
			
		||||
// those.
 | 
			
		||||
package logr
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// New returns a new Logger instance.  This is primarily used by libraries
 | 
			
		||||
// implementing LogSink, rather than end users.  Passing a nil sink will create
 | 
			
		||||
// a Logger which discards all log lines.
 | 
			
		||||
@@ -410,45 +406,6 @@ func (l Logger) IsZero() bool {
 | 
			
		||||
	return l.sink == nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// contextKey is how we find Loggers in a context.Context.
 | 
			
		||||
type contextKey struct{}
 | 
			
		||||
 | 
			
		||||
// FromContext returns a Logger from ctx or an error if no Logger is found.
 | 
			
		||||
func FromContext(ctx context.Context) (Logger, error) {
 | 
			
		||||
	if v, ok := ctx.Value(contextKey{}).(Logger); ok {
 | 
			
		||||
		return v, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return Logger{}, notFoundError{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// notFoundError exists to carry an IsNotFound method.
 | 
			
		||||
type notFoundError struct{}
 | 
			
		||||
 | 
			
		||||
func (notFoundError) Error() string {
 | 
			
		||||
	return "no logr.Logger was present"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (notFoundError) IsNotFound() bool {
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FromContextOrDiscard returns a Logger from ctx.  If no Logger is found, this
 | 
			
		||||
// returns a Logger that discards all log messages.
 | 
			
		||||
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, which carries the
 | 
			
		||||
// provided Logger.
 | 
			
		||||
func NewContext(ctx context.Context, logger Logger) context.Context {
 | 
			
		||||
	return context.WithValue(ctx, contextKey{}, logger)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RuntimeInfo holds information that the logr "core" library knows which
 | 
			
		||||
// LogSinks might want to know.
 | 
			
		||||
type RuntimeInfo struct {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										192
									
								
								vendor/github.com/go-logr/logr/sloghandler.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								vendor/github.com/go-logr/logr/sloghandler.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,192 @@
 | 
			
		||||
//go:build go1.21
 | 
			
		||||
// +build go1.21
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2023 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"
 | 
			
		||||
	"log/slog"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type slogHandler struct {
 | 
			
		||||
	// May be nil, in which case all logs get discarded.
 | 
			
		||||
	sink LogSink
 | 
			
		||||
	// Non-nil if sink is non-nil and implements SlogSink.
 | 
			
		||||
	slogSink SlogSink
 | 
			
		||||
 | 
			
		||||
	// groupPrefix collects values from WithGroup calls. It gets added as
 | 
			
		||||
	// prefix to value keys when handling a log record.
 | 
			
		||||
	groupPrefix string
 | 
			
		||||
 | 
			
		||||
	// levelBias can be set when constructing the handler to influence the
 | 
			
		||||
	// slog.Level of log records. A positive levelBias reduces the
 | 
			
		||||
	// slog.Level value. slog has no API to influence this value after the
 | 
			
		||||
	// handler got created, so it can only be set indirectly through
 | 
			
		||||
	// Logger.V.
 | 
			
		||||
	levelBias slog.Level
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var _ slog.Handler = &slogHandler{}
 | 
			
		||||
 | 
			
		||||
// groupSeparator is used to concatenate WithGroup names and attribute keys.
 | 
			
		||||
const groupSeparator = "."
 | 
			
		||||
 | 
			
		||||
// GetLevel is used for black box unit testing.
 | 
			
		||||
func (l *slogHandler) GetLevel() slog.Level {
 | 
			
		||||
	return l.levelBias
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *slogHandler) Enabled(_ context.Context, level slog.Level) bool {
 | 
			
		||||
	return l.sink != nil && (level >= slog.LevelError || l.sink.Enabled(l.levelFromSlog(level)))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *slogHandler) Handle(ctx context.Context, record slog.Record) error {
 | 
			
		||||
	if l.slogSink != nil {
 | 
			
		||||
		// Only adjust verbosity level of log entries < slog.LevelError.
 | 
			
		||||
		if record.Level < slog.LevelError {
 | 
			
		||||
			record.Level -= l.levelBias
 | 
			
		||||
		}
 | 
			
		||||
		return l.slogSink.Handle(ctx, record)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// No need to check for nil sink here because Handle will only be called
 | 
			
		||||
	// when Enabled returned true.
 | 
			
		||||
 | 
			
		||||
	kvList := make([]any, 0, 2*record.NumAttrs())
 | 
			
		||||
	record.Attrs(func(attr slog.Attr) bool {
 | 
			
		||||
		kvList = attrToKVs(attr, l.groupPrefix, kvList)
 | 
			
		||||
		return true
 | 
			
		||||
	})
 | 
			
		||||
	if record.Level >= slog.LevelError {
 | 
			
		||||
		l.sinkWithCallDepth().Error(nil, record.Message, kvList...)
 | 
			
		||||
	} else {
 | 
			
		||||
		level := l.levelFromSlog(record.Level)
 | 
			
		||||
		l.sinkWithCallDepth().Info(level, record.Message, kvList...)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// sinkWithCallDepth adjusts the stack unwinding so that when Error or Info
 | 
			
		||||
// are called by Handle, code in slog gets skipped.
 | 
			
		||||
//
 | 
			
		||||
// This offset currently (Go 1.21.0) works for calls through
 | 
			
		||||
// slog.New(ToSlogHandler(...)).  There's no guarantee that the call
 | 
			
		||||
// chain won't change. Wrapping the handler will also break unwinding. It's
 | 
			
		||||
// still better than not adjusting at all....
 | 
			
		||||
//
 | 
			
		||||
// This cannot be done when constructing the handler because FromSlogHandler needs
 | 
			
		||||
// access to the original sink without this adjustment. A second copy would
 | 
			
		||||
// work, but then WithAttrs would have to be called for both of them.
 | 
			
		||||
func (l *slogHandler) sinkWithCallDepth() LogSink {
 | 
			
		||||
	if sink, ok := l.sink.(CallDepthLogSink); ok {
 | 
			
		||||
		return sink.WithCallDepth(2)
 | 
			
		||||
	}
 | 
			
		||||
	return l.sink
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *slogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
 | 
			
		||||
	if l.sink == nil || len(attrs) == 0 {
 | 
			
		||||
		return l
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	clone := *l
 | 
			
		||||
	if l.slogSink != nil {
 | 
			
		||||
		clone.slogSink = l.slogSink.WithAttrs(attrs)
 | 
			
		||||
		clone.sink = clone.slogSink
 | 
			
		||||
	} else {
 | 
			
		||||
		kvList := make([]any, 0, 2*len(attrs))
 | 
			
		||||
		for _, attr := range attrs {
 | 
			
		||||
			kvList = attrToKVs(attr, l.groupPrefix, kvList)
 | 
			
		||||
		}
 | 
			
		||||
		clone.sink = l.sink.WithValues(kvList...)
 | 
			
		||||
	}
 | 
			
		||||
	return &clone
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *slogHandler) WithGroup(name string) slog.Handler {
 | 
			
		||||
	if l.sink == nil {
 | 
			
		||||
		return l
 | 
			
		||||
	}
 | 
			
		||||
	if name == "" {
 | 
			
		||||
		// slog says to inline empty groups
 | 
			
		||||
		return l
 | 
			
		||||
	}
 | 
			
		||||
	clone := *l
 | 
			
		||||
	if l.slogSink != nil {
 | 
			
		||||
		clone.slogSink = l.slogSink.WithGroup(name)
 | 
			
		||||
		clone.sink = clone.slogSink
 | 
			
		||||
	} else {
 | 
			
		||||
		clone.groupPrefix = addPrefix(clone.groupPrefix, name)
 | 
			
		||||
	}
 | 
			
		||||
	return &clone
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// attrToKVs appends a slog.Attr to a logr-style kvList.  It handle slog Groups
 | 
			
		||||
// and other details of slog.
 | 
			
		||||
func attrToKVs(attr slog.Attr, groupPrefix string, kvList []any) []any {
 | 
			
		||||
	attrVal := attr.Value.Resolve()
 | 
			
		||||
	if attrVal.Kind() == slog.KindGroup {
 | 
			
		||||
		groupVal := attrVal.Group()
 | 
			
		||||
		grpKVs := make([]any, 0, 2*len(groupVal))
 | 
			
		||||
		prefix := groupPrefix
 | 
			
		||||
		if attr.Key != "" {
 | 
			
		||||
			prefix = addPrefix(groupPrefix, attr.Key)
 | 
			
		||||
		}
 | 
			
		||||
		for _, attr := range groupVal {
 | 
			
		||||
			grpKVs = attrToKVs(attr, prefix, grpKVs)
 | 
			
		||||
		}
 | 
			
		||||
		kvList = append(kvList, grpKVs...)
 | 
			
		||||
	} else if attr.Key != "" {
 | 
			
		||||
		kvList = append(kvList, addPrefix(groupPrefix, attr.Key), attrVal.Any())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return kvList
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func addPrefix(prefix, name string) string {
 | 
			
		||||
	if prefix == "" {
 | 
			
		||||
		return name
 | 
			
		||||
	}
 | 
			
		||||
	if name == "" {
 | 
			
		||||
		return prefix
 | 
			
		||||
	}
 | 
			
		||||
	return prefix + groupSeparator + name
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// levelFromSlog adjusts the level by the logger's verbosity and negates it.
 | 
			
		||||
// It ensures that the result is >= 0. This is necessary because the result is
 | 
			
		||||
// passed to a LogSink and that API did not historically document whether
 | 
			
		||||
// levels could be negative or what that meant.
 | 
			
		||||
//
 | 
			
		||||
// Some example usage:
 | 
			
		||||
//
 | 
			
		||||
//	logrV0 := getMyLogger()
 | 
			
		||||
//	logrV2 := logrV0.V(2)
 | 
			
		||||
//	slogV2 := slog.New(logr.ToSlogHandler(logrV2))
 | 
			
		||||
//	slogV2.Debug("msg") // =~ logrV2.V(4) =~ logrV0.V(6)
 | 
			
		||||
//	slogV2.Info("msg")  // =~  logrV2.V(0) =~ logrV0.V(2)
 | 
			
		||||
//	slogv2.Warn("msg")  // =~ logrV2.V(-4) =~ logrV0.V(0)
 | 
			
		||||
func (l *slogHandler) levelFromSlog(level slog.Level) int {
 | 
			
		||||
	result := -level
 | 
			
		||||
	result += l.levelBias // in case the original Logger had a V level
 | 
			
		||||
	if result < 0 {
 | 
			
		||||
		result = 0 // because LogSink doesn't expect negative V levels
 | 
			
		||||
	}
 | 
			
		||||
	return int(result)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										100
									
								
								vendor/github.com/go-logr/logr/slogr.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								vendor/github.com/go-logr/logr/slogr.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,100 @@
 | 
			
		||||
//go:build go1.21
 | 
			
		||||
// +build go1.21
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2023 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"
 | 
			
		||||
	"log/slog"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// FromSlogHandler returns a Logger which writes to the slog.Handler.
 | 
			
		||||
//
 | 
			
		||||
// The logr verbosity level is mapped to slog levels such that V(0) becomes
 | 
			
		||||
// slog.LevelInfo and V(4) becomes slog.LevelDebug.
 | 
			
		||||
func FromSlogHandler(handler slog.Handler) Logger {
 | 
			
		||||
	if handler, ok := handler.(*slogHandler); ok {
 | 
			
		||||
		if handler.sink == nil {
 | 
			
		||||
			return Discard()
 | 
			
		||||
		}
 | 
			
		||||
		return New(handler.sink).V(int(handler.levelBias))
 | 
			
		||||
	}
 | 
			
		||||
	return New(&slogSink{handler: handler})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ToSlogHandler returns a slog.Handler which writes to the same sink as the Logger.
 | 
			
		||||
//
 | 
			
		||||
// The returned logger writes all records with level >= slog.LevelError as
 | 
			
		||||
// error log entries with LogSink.Error, regardless of the verbosity level of
 | 
			
		||||
// the Logger:
 | 
			
		||||
//
 | 
			
		||||
//	logger := <some Logger with 0 as verbosity level>
 | 
			
		||||
//	slog.New(ToSlogHandler(logger.V(10))).Error(...) -> logSink.Error(...)
 | 
			
		||||
//
 | 
			
		||||
// The level of all other records gets reduced by the verbosity
 | 
			
		||||
// level of the Logger and the result is negated. If it happens
 | 
			
		||||
// to be negative, then it gets replaced by zero because a LogSink
 | 
			
		||||
// is not expected to handled negative levels:
 | 
			
		||||
//
 | 
			
		||||
//	slog.New(ToSlogHandler(logger)).Debug(...) -> logger.GetSink().Info(level=4, ...)
 | 
			
		||||
//	slog.New(ToSlogHandler(logger)).Warning(...) -> logger.GetSink().Info(level=0, ...)
 | 
			
		||||
//	slog.New(ToSlogHandler(logger)).Info(...) -> logger.GetSink().Info(level=0, ...)
 | 
			
		||||
//	slog.New(ToSlogHandler(logger.V(4))).Info(...) -> logger.GetSink().Info(level=4, ...)
 | 
			
		||||
func ToSlogHandler(logger Logger) slog.Handler {
 | 
			
		||||
	if sink, ok := logger.GetSink().(*slogSink); ok && logger.GetV() == 0 {
 | 
			
		||||
		return sink.handler
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	handler := &slogHandler{sink: logger.GetSink(), levelBias: slog.Level(logger.GetV())}
 | 
			
		||||
	if slogSink, ok := handler.sink.(SlogSink); ok {
 | 
			
		||||
		handler.slogSink = slogSink
 | 
			
		||||
	}
 | 
			
		||||
	return handler
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SlogSink is an optional interface that a LogSink can implement to support
 | 
			
		||||
// logging through the slog.Logger or slog.Handler APIs better. It then should
 | 
			
		||||
// also support special slog values like slog.Group. When used as a
 | 
			
		||||
// slog.Handler, the advantages are:
 | 
			
		||||
//
 | 
			
		||||
//   - stack unwinding gets avoided in favor of logging the pre-recorded PC,
 | 
			
		||||
//     as intended by slog
 | 
			
		||||
//   - proper grouping of key/value pairs via WithGroup
 | 
			
		||||
//   - verbosity levels > slog.LevelInfo can be recorded
 | 
			
		||||
//   - less overhead
 | 
			
		||||
//
 | 
			
		||||
// Both APIs (Logger and slog.Logger/Handler) then are supported equally
 | 
			
		||||
// well. Developers can pick whatever API suits them better and/or mix
 | 
			
		||||
// packages which use either API in the same binary with a common logging
 | 
			
		||||
// implementation.
 | 
			
		||||
//
 | 
			
		||||
// This interface is necessary because the type implementing the LogSink
 | 
			
		||||
// interface cannot also implement the slog.Handler interface due to the
 | 
			
		||||
// different prototype of the common Enabled method.
 | 
			
		||||
//
 | 
			
		||||
// An implementation could support both interfaces in two different types, but then
 | 
			
		||||
// additional interfaces would be needed to convert between those types in FromSlogHandler
 | 
			
		||||
// and ToSlogHandler.
 | 
			
		||||
type SlogSink interface {
 | 
			
		||||
	LogSink
 | 
			
		||||
 | 
			
		||||
	Handle(ctx context.Context, record slog.Record) error
 | 
			
		||||
	WithAttrs(attrs []slog.Attr) SlogSink
 | 
			
		||||
	WithGroup(name string) SlogSink
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										120
									
								
								vendor/github.com/go-logr/logr/slogsink.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								vendor/github.com/go-logr/logr/slogsink.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,120 @@
 | 
			
		||||
//go:build go1.21
 | 
			
		||||
// +build go1.21
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2023 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"
 | 
			
		||||
	"log/slog"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	_ LogSink          = &slogSink{}
 | 
			
		||||
	_ CallDepthLogSink = &slogSink{}
 | 
			
		||||
	_ Underlier        = &slogSink{}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Underlier is implemented by the LogSink returned by NewFromLogHandler.
 | 
			
		||||
type Underlier interface {
 | 
			
		||||
	// GetUnderlying returns the Handler used by the LogSink.
 | 
			
		||||
	GetUnderlying() slog.Handler
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// nameKey is used to log the `WithName` values as an additional attribute.
 | 
			
		||||
	nameKey = "logger"
 | 
			
		||||
 | 
			
		||||
	// errKey is used to log the error parameter of Error as an additional attribute.
 | 
			
		||||
	errKey = "err"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type slogSink struct {
 | 
			
		||||
	callDepth int
 | 
			
		||||
	name      string
 | 
			
		||||
	handler   slog.Handler
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *slogSink) Init(info RuntimeInfo) {
 | 
			
		||||
	l.callDepth = info.CallDepth
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *slogSink) GetUnderlying() slog.Handler {
 | 
			
		||||
	return l.handler
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *slogSink) WithCallDepth(depth int) LogSink {
 | 
			
		||||
	newLogger := *l
 | 
			
		||||
	newLogger.callDepth += depth
 | 
			
		||||
	return &newLogger
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *slogSink) Enabled(level int) bool {
 | 
			
		||||
	return l.handler.Enabled(context.Background(), slog.Level(-level))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *slogSink) Info(level int, msg string, kvList ...interface{}) {
 | 
			
		||||
	l.log(nil, msg, slog.Level(-level), kvList...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *slogSink) Error(err error, msg string, kvList ...interface{}) {
 | 
			
		||||
	l.log(err, msg, slog.LevelError, kvList...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *slogSink) log(err error, msg string, level slog.Level, kvList ...interface{}) {
 | 
			
		||||
	var pcs [1]uintptr
 | 
			
		||||
	// skip runtime.Callers, this function, Info/Error, and all helper functions above that.
 | 
			
		||||
	runtime.Callers(3+l.callDepth, pcs[:])
 | 
			
		||||
 | 
			
		||||
	record := slog.NewRecord(time.Now(), level, msg, pcs[0])
 | 
			
		||||
	if l.name != "" {
 | 
			
		||||
		record.AddAttrs(slog.String(nameKey, l.name))
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		record.AddAttrs(slog.Any(errKey, err))
 | 
			
		||||
	}
 | 
			
		||||
	record.Add(kvList...)
 | 
			
		||||
	_ = l.handler.Handle(context.Background(), record)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l slogSink) WithName(name string) LogSink {
 | 
			
		||||
	if l.name != "" {
 | 
			
		||||
		l.name += "/"
 | 
			
		||||
	}
 | 
			
		||||
	l.name += name
 | 
			
		||||
	return &l
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l slogSink) WithValues(kvList ...interface{}) LogSink {
 | 
			
		||||
	l.handler = l.handler.WithAttrs(kvListToAttrs(kvList...))
 | 
			
		||||
	return &l
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func kvListToAttrs(kvList ...interface{}) []slog.Attr {
 | 
			
		||||
	// We don't need the record itself, only its Add method.
 | 
			
		||||
	record := slog.NewRecord(time.Time{}, 0, "", 0)
 | 
			
		||||
	record.Add(kvList...)
 | 
			
		||||
	attrs := make([]slog.Attr, 0, record.NumAttrs())
 | 
			
		||||
	record.Attrs(func(attr slog.Attr) bool {
 | 
			
		||||
		attrs = append(attrs, attr)
 | 
			
		||||
		return true
 | 
			
		||||
	})
 | 
			
		||||
	return attrs
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user