forked from toolshed/abra
chore: vendor
This commit is contained in:
17
vendor/github.com/charmbracelet/log/.gitignore
generated
vendored
Normal file
17
vendor/github.com/charmbracelet/log/.gitignore
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
*.txt
|
||||
*.gif
|
||||
|
||||
examples/batch2/batch2
|
||||
examples/chocolate-chips/chocolate-chips
|
||||
examples/cookie/cookie
|
||||
examples/error/error
|
||||
examples/format/format
|
||||
examples/log/log
|
||||
examples/new/new
|
||||
examples/options/options
|
||||
examples/oven/oven
|
||||
examples/temperature/temperature
|
||||
|
||||
.vscode
|
||||
.history
|
||||
go.work
|
34
vendor/github.com/charmbracelet/log/.golangci.yml
generated
vendored
Normal file
34
vendor/github.com/charmbracelet/log/.golangci.yml
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
run:
|
||||
tests: false
|
||||
|
||||
issues:
|
||||
include:
|
||||
- EXC0001
|
||||
- EXC0005
|
||||
- EXC0011
|
||||
- EXC0012
|
||||
- EXC0013
|
||||
|
||||
max-issues-per-linter: 0
|
||||
max-same-issues: 0
|
||||
|
||||
linters:
|
||||
enable:
|
||||
- bodyclose
|
||||
- dupl
|
||||
- exportloopref
|
||||
- goconst
|
||||
- godot
|
||||
- godox
|
||||
- goimports
|
||||
- goprintffuncname
|
||||
- gosec
|
||||
- misspell
|
||||
- nolintlint
|
||||
- prealloc
|
||||
- revive
|
||||
- rowserrcheck
|
||||
- sqlclosecheck
|
||||
- unconvert
|
||||
- unparam
|
||||
- whitespace
|
3
vendor/github.com/charmbracelet/log/.goreleaser.yml
generated
vendored
Normal file
3
vendor/github.com/charmbracelet/log/.goreleaser.yml
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
includes:
|
||||
- from_url:
|
||||
url: charmbracelet/meta/main/goreleaser-lib.yaml
|
21
vendor/github.com/charmbracelet/log/LICENSE
generated
vendored
Normal file
21
vendor/github.com/charmbracelet/log/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022-2023 Charmbracelet, Inc
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
367
vendor/github.com/charmbracelet/log/README.md
generated
vendored
Normal file
367
vendor/github.com/charmbracelet/log/README.md
generated
vendored
Normal file
@ -0,0 +1,367 @@
|
||||
# Log
|
||||
|
||||
<p>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://user-images.githubusercontent.com/25087/219742757-c8afe0d9-608a-4845-a555-ef59c0af9ebc.png" width="359">
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/25087/219743408-3d7bef51-1409-40c0-8159-acc6e52f078e.png" width="359">
|
||||
<img src="https://user-images.githubusercontent.com/25087/219742757-c8afe0d9-608a-4845-a555-ef59c0af9ebc.png" width="359" />
|
||||
</picture>
|
||||
<br>
|
||||
<a href="https://github.com/charmbracelet/log/releases"><img src="https://img.shields.io/github/release/charmbracelet/log.svg" alt="Latest Release"></a>
|
||||
<a href="https://pkg.go.dev/github.com/charmbracelet/log?tab=doc"><img src="https://godoc.org/github.com/golang/gddo?status.svg" alt="Go Docs"></a>
|
||||
<a href="https://github.com/charmbracelet/log/actions"><img src="https://github.com/charmbracelet/log/workflows/build/badge.svg" alt="Build Status"></a>
|
||||
<a href="https://codecov.io/gh/charmbracelet/log"><img alt="Codecov branch" src="https://img.shields.io/codecov/c/github/charmbracelet/log/main.svg"></a>
|
||||
<a href="https://goreportcard.com/report/github.com/charmbracelet/log"><img alt="Go Report Card" src="https://goreportcard.com/badge/github.com/charmbracelet/log"></a>
|
||||
</p>
|
||||
|
||||
A minimal and colorful Go logging library. 🪵
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://vhs.charm.sh/vhs-1wBImk2iSIuiiD7Ib9rufi.gif">
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://vhs.charm.sh/vhs-1wBImk2iSIuiiD7Ib9rufi.gif">
|
||||
<!-- <source media="(prefers-color-scheme: light)" srcset="https://vhs.charm.sh/vhs-2NvOYS29AauVRgRRPmquXx.gif"> -->
|
||||
<img src="https://vhs.charm.sh/vhs-1wBImk2iSIuiiD7Ib9rufi.gif" alt="Made with VHS" />
|
||||
</picture>
|
||||
|
||||
It provides a leveled structured human readable logger with a small API. Unlike
|
||||
[standard `log`][stdlog], the Charm logger provides customizable colorful human
|
||||
readable logging with batteries included.
|
||||
|
||||
- Uses [Lip Gloss][lipgloss] to style and colorize the output.
|
||||
- Colorful, human readable format.
|
||||
- Ability to customize the time stamp format.
|
||||
- Skips caller frames and marks function as helpers.
|
||||
- Leveled logging.
|
||||
- Text, JSON, and Logfmt formatters.
|
||||
- Store and retrieve logger in and from context.
|
||||
- Slog handler.
|
||||
- Standard log adapter.
|
||||
|
||||
## Usage
|
||||
|
||||
Use `go get` to download the dependency.
|
||||
|
||||
```bash
|
||||
go get github.com/charmbracelet/log@latest
|
||||
```
|
||||
|
||||
Then, `import` it in your Go files:
|
||||
|
||||
```go
|
||||
import "github.com/charmbracelet/log"
|
||||
```
|
||||
|
||||
The Charm logger comes with a global package-wise logger with timestamps turned
|
||||
on, and the logging level set to `info`.
|
||||
|
||||
```go
|
||||
log.Debug("Cookie 🍪") // won't print anything
|
||||
log.Info("Hello World!")
|
||||
```
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" width="400" srcset="https://vhs.charm.sh/vhs-cKiS8OuRrF1VVVpscM9e3.gif">
|
||||
<source media="(prefers-color-scheme: light)" width="400" srcset="https://vhs.charm.sh/vhs-cKiS8OuRrF1VVVpscM9e3.gif">
|
||||
<!-- <source media="(prefers-color-scheme: light)" width="400" srcset="https://vhs.charm.sh/vhs-4AeLaEuO3tDbECR1qe9Jvp.gif"> -->
|
||||
<img width="400" src="https://vhs.charm.sh/vhs-4AeLaEuO3tDbECR1qe9Jvp.gif" alt="Made with VHS" />
|
||||
</picture>
|
||||
|
||||
All logging levels accept optional key/value pairs to be printed along with a
|
||||
message.
|
||||
|
||||
```go
|
||||
err := fmt.Errorf("too much sugar")
|
||||
log.Error("failed to bake cookies", "err", err)
|
||||
```
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" width="600" srcset="https://vhs.charm.sh/vhs-65KIpGw4FTESK0IzkDB9VQ.gif" >
|
||||
<source media="(prefers-color-scheme: light)" width="600" srcset="https://vhs.charm.sh/vhs-65KIpGw4FTESK0IzkDB9VQ.gif" >
|
||||
<!-- <source media="(prefers-color-scheme: light)" width="600" srcset="https://vhs.charm.sh/vhs-7rk8wALXRDoFw8SLFwn9rW.gif"> -->
|
||||
<img width="600" src="https://vhs.charm.sh/vhs-65KIpGw4FTESK0IzkDB9VQ.gif" alt="Made with VHS">
|
||||
</picture>
|
||||
|
||||
You can use `log.Print()` to print messages without a level prefix.
|
||||
|
||||
```go
|
||||
log.Print("Baking 101")
|
||||
// 2023/01/04 10:04:06 Baking 101
|
||||
```
|
||||
|
||||
### New loggers
|
||||
|
||||
Use `New()` to create new loggers.
|
||||
|
||||
```go
|
||||
logger := log.New(os.Stderr)
|
||||
if butter {
|
||||
logger.Warn("chewy!", "butter", true)
|
||||
}
|
||||
```
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" width="300" srcset="https://vhs.charm.sh/vhs-3QQdzOW4Zc0bN2tOhAest9.gif">
|
||||
<source media="(prefers-color-scheme: light)" width="300" srcset="https://vhs.charm.sh/vhs-3QQdzOW4Zc0bN2tOhAest9.gif">
|
||||
<!-- <source media="(prefers-color-scheme: light)" width="300" srcset="https://vhs.charm.sh/vhs-1nrhNSuFnQkxWD4RoMlE4O.gif"> -->
|
||||
<img width="300" src="https://vhs.charm.sh/vhs-3QQdzOW4Zc0bN2tOhAest9.gif">
|
||||
</picture>
|
||||
|
||||
### Levels
|
||||
|
||||
Log offers multiple levels to filter your logs on. Available levels are:
|
||||
|
||||
```go
|
||||
log.DebugLevel
|
||||
log.InfoLevel
|
||||
log.WarnLevel
|
||||
log.ErrorLevel
|
||||
log.FatalLevel
|
||||
```
|
||||
|
||||
Use `log.SetLevel()` to set the log level. You can also create a new logger with
|
||||
a specific log level using `log.Options{Level: }`.
|
||||
|
||||
Use the corresponding function to log a message:
|
||||
|
||||
```go
|
||||
err := errors.New("Baking error 101")
|
||||
log.Debug(err)
|
||||
log.Info(err)
|
||||
log.Warn(err)
|
||||
log.Error(err)
|
||||
log.Fatal(err) // this calls os.Exit(1)
|
||||
log.Print(err) // prints regardless of log level
|
||||
```
|
||||
|
||||
Or use the formatter variant:
|
||||
|
||||
```go
|
||||
format := "%s %d"
|
||||
log.Debugf(format, "chocolate", 10)
|
||||
log.Warnf(format, "adding more", 5)
|
||||
log.Errorf(format, "increasing temp", 420)
|
||||
log.Fatalf(format, "too hot!", 500) // this calls os.Exit(1)
|
||||
log.Printf(format, "baking cookies") // prints regardless of log level
|
||||
|
||||
// Use these in conjunction with `With(...)` to add more context
|
||||
log.With("err", err).Errorf("unable to start %s", "oven")
|
||||
```
|
||||
|
||||
### Structured
|
||||
|
||||
All the functions above take a message and key-value pairs of anything. The
|
||||
message can also be of type any.
|
||||
|
||||
```go
|
||||
ingredients := []string{"flour", "butter", "sugar", "chocolate"}
|
||||
log.Debug("Available ingredients", "ingredients", ingredients)
|
||||
// DEBUG Available ingredients ingredients="[flour butter sugar chocolate]"
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
You can customize the logger with options. Use `log.NewWithOptions()` and
|
||||
`log.Options{}` to customize your new logger.
|
||||
|
||||
```go
|
||||
logger := log.NewWithOptions(os.Stderr, log.Options{
|
||||
ReportCaller: true,
|
||||
ReportTimestamp: true,
|
||||
TimeFormat: time.Kitchen,
|
||||
Prefix: "Baking 🍪 ",
|
||||
})
|
||||
logger.Info("Starting oven!", "degree", 375)
|
||||
time.Sleep(10 * time.Minute)
|
||||
logger.Info("Finished baking")
|
||||
```
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" width="700" srcset="https://vhs.charm.sh/vhs-6oSCJcQ5EmFKKELcskJhLo.gif">
|
||||
<source media="(prefers-color-scheme: light)" width="700" srcset="https://vhs.charm.sh/vhs-6oSCJcQ5EmFKKELcskJhLo.gif">
|
||||
<!-- <source media="(prefers-color-scheme: light)" width="700" srcset="https://vhs.charm.sh/vhs-2X8Esd8ZsHo4DVPVgR36yx.gif"> -->
|
||||
<img width="700" src="https://vhs.charm.sh/vhs-6oSCJcQ5EmFKKELcskJhLo.gif">
|
||||
</picture>
|
||||
|
||||
You can also use logger setters to customize the logger.
|
||||
|
||||
```go
|
||||
logger := log.New(os.Stderr)
|
||||
logger.SetReportTimestamp(false)
|
||||
logger.SetReportCaller(false)
|
||||
logger.SetLevel(log.DebugLevel)
|
||||
```
|
||||
|
||||
Use `log.SetFormatter()` or `log.Options{Formatter: }` to change the output
|
||||
format. Available options are:
|
||||
|
||||
- `log.TextFormatter` (_default_)
|
||||
- `log.JSONFormatter`
|
||||
- `log.LogfmtFormatter`
|
||||
|
||||
> **Note** styling only affects the `TextFormatter`. Styling is disabled if the
|
||||
> output is not a TTY.
|
||||
|
||||
For a list of available options, refer to [options.go](./options.go).
|
||||
|
||||
### Styles
|
||||
|
||||
You can customize the logger styles using [Lipgloss][lipgloss]. The styles are
|
||||
defined at a global level in [styles.go](./styles.go).
|
||||
|
||||
```go
|
||||
// Override the default error level style.
|
||||
styles := log.DefaultStyles()
|
||||
styles.Levels[log.ErrorLevel] = lipgloss.NewStyle().
|
||||
SetString("ERROR!!").
|
||||
Padding(0, 1, 0, 1).
|
||||
Background(lipgloss.Color("204")).
|
||||
Foreground(lipgloss.Color("0"))
|
||||
// Add a custom style for key `err`
|
||||
styles.Keys["err"] = lipgloss.NewStyle().Foreground(lipgloss.Color("204"))
|
||||
styles.Values["err"] = lipgloss.NewStyle().Bold(true)
|
||||
logger := log.New(os.Stderr)
|
||||
logger.SetStyles(styles)
|
||||
logger.Error("Whoops!", "err", "kitchen on fire")
|
||||
```
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" width="400" srcset="https://vhs.charm.sh/vhs-4LXsGvzyH4RdjJaTF4a9MG.gif">
|
||||
<source media="(prefers-color-scheme: light)" width="400" srcset="https://vhs.charm.sh/vhs-4LXsGvzyH4RdjJaTF4a9MG.gif">
|
||||
<!-- <source media="(prefers-color-scheme: light)" width="400" srcset="https://vhs.charm.sh/vhs-4f6qLnIfudMMLDD9sxXUrv.gif"> -->
|
||||
<img width="400" src="https://vhs.charm.sh/vhs-4LXsGvzyH4RdjJaTF4a9MG.gif">
|
||||
</picture>
|
||||
|
||||
### Sub-logger
|
||||
|
||||
Create sub-loggers with their specific fields.
|
||||
|
||||
```go
|
||||
logger := log.NewWithOptions(os.Stderr, log.Options{
|
||||
Prefix: "Baking 🍪 "
|
||||
})
|
||||
batch2 := logger.With("batch", 2, "chocolateChips", true)
|
||||
batch2.Debug("Preparing batch 2...")
|
||||
batch2.Debug("Adding chocolate chips")
|
||||
```
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" width="700" srcset="https://vhs.charm.sh/vhs-1JgP5ZRL0oXVspeg50CczR.gif">
|
||||
<source media="(prefers-color-scheme: light)" width="700" srcset="https://vhs.charm.sh/vhs-1JgP5ZRL0oXVspeg50CczR.gif">
|
||||
<img width="700" src="https://vhs.charm.sh/vhs-1JgP5ZRL0oXVspeg50CczR.gif">
|
||||
</picture>
|
||||
|
||||
### Format Messages
|
||||
|
||||
You can use `fmt.Sprintf()` to format messages.
|
||||
|
||||
```go
|
||||
for item := 1; i <= 100; i++ {
|
||||
log.Info(fmt.Sprintf("Baking %d/100...", item))
|
||||
}
|
||||
```
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" width="500" srcset="https://vhs.charm.sh/vhs-4nX5I7qHT09aJ2gU7OaGV5.gif">
|
||||
<source media="(prefers-color-scheme: light)" width="500" srcset="https://vhs.charm.sh/vhs-4nX5I7qHT09aJ2gU7OaGV5.gif">
|
||||
<!-- <source media="(prefers-color-scheme: light)" width="500" srcset="https://vhs.charm.sh/vhs-4RHXd4JSucomcPqJGZTpKh.gif"> -->
|
||||
<img width="500" src="https://vhs.charm.sh/vhs-4nX5I7qHT09aJ2gU7OaGV5.gif">
|
||||
</picture>
|
||||
|
||||
Or arguments:
|
||||
|
||||
```go
|
||||
for temp := 375; temp <= 400; temp++ {
|
||||
log.Info("Increasing temperature", "degree", fmt.Sprintf("%d°F", temp))
|
||||
}
|
||||
```
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" width="700" srcset="https://vhs.charm.sh/vhs-79YvXcDOsqgHte3bv42UTr.gif">
|
||||
<source media="(prefers-color-scheme: light)" width="700" srcset="https://vhs.charm.sh/vhs-79YvXcDOsqgHte3bv42UTr.gif">
|
||||
<!-- <source media="(prefers-color-scheme: light)" width="700" srcset="https://vhs.charm.sh/vhs-4AvAnoA2S53QTOteX8krp4.gif"> -->
|
||||
<img width="700" src="https://vhs.charm.sh/vhs-79YvXcDOsqgHte3bv42UTr.gif">
|
||||
</picture>
|
||||
|
||||
### Helper Functions
|
||||
|
||||
Skip caller frames in helper functions. Similar to what you can do with
|
||||
`testing.TB().Helper()`.
|
||||
|
||||
```go
|
||||
func startOven(degree int) {
|
||||
log.Helper()
|
||||
log.Info("Starting oven", "degree", degree)
|
||||
}
|
||||
|
||||
log.SetReportCaller(true)
|
||||
startOven(400) // INFO <cookies/oven.go:123> Starting oven degree=400
|
||||
```
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" width="700" srcset="https://vhs.charm.sh/vhs-6CeQGIV8Ovgr8GD0N6NgTq.gif">
|
||||
<source media="(prefers-color-scheme: light)" width="700" srcset="https://vhs.charm.sh/vhs-6CeQGIV8Ovgr8GD0N6NgTq.gif">
|
||||
<!-- <source media="(prefers-color-scheme: light)" width="700" srcset="https://vhs.charm.sh/vhs-6DPg0bVL4K4TkfoHkAn2ap.gif"> -->
|
||||
<img width="700" src="https://vhs.charm.sh/vhs-6CeQGIV8Ovgr8GD0N6NgTq.gif">
|
||||
</picture>
|
||||
|
||||
This will use the _caller_ function (`startOven`) line number instead of the
|
||||
logging function (`log.Info`) to report the source location.
|
||||
|
||||
### Slog Handler
|
||||
|
||||
You can use Log as an [`log/slog`](https://pkg.go.dev/log/slog) handler. Just
|
||||
pass a logger instance to Slog and you're good to go.
|
||||
|
||||
```go
|
||||
handler := log.New(os.Stderr)
|
||||
logger := slog.New(handler)
|
||||
logger.Error("meow?")
|
||||
```
|
||||
|
||||
### Standard Log Adapter
|
||||
|
||||
Some Go libraries, especially the ones in the standard library, will only accept
|
||||
the [standard logger][stdlog] interface. For instance, the HTTP Server from
|
||||
`net/http` will only take a `*log.Logger` for its `ErrorLog` field.
|
||||
|
||||
For this, you can use the standard log adapter, which simply wraps the logger in
|
||||
a `*log.Logger` interface.
|
||||
|
||||
```go
|
||||
logger := log.NewWithOptions(os.Stderr, log.Options{Prefix: "http"})
|
||||
stdlog := logger.StandardLog(log.StandardLogOptions{
|
||||
ForceLevel: log.ErrorLevel,
|
||||
})
|
||||
s := &http.Server{
|
||||
Addr: ":8080",
|
||||
Handler: handler,
|
||||
ErrorLog: stdlog,
|
||||
}
|
||||
stdlog.Printf("Failed to make bake request, %s", fmt.Errorf("temperature is too low"))
|
||||
// ERROR http: Failed to make bake request, temperature is too low
|
||||
```
|
||||
|
||||
## Gum
|
||||
|
||||
<img src="https://vhs.charm.sh/vhs-6jupuFM0s2fXiUrBE0I1vU.gif" width="600" alt="Running gum log with debug and error levels" />
|
||||
|
||||
Log integrates with [Gum][gum] to log messages to output. Use `gum log [flags]
|
||||
[message]` to handle logging in your shell scripts. See
|
||||
[charmbracelet/gum](https://github.com/charmbracelet/gum#log) for more
|
||||
information.
|
||||
|
||||
[gum]: https://github.com/charmbracelet/gum
|
||||
[lipgloss]: https://github.com/charmbracelet/lipgloss
|
||||
[stdlog]: https://pkg.go.dev/log
|
||||
|
||||
## License
|
||||
|
||||
[MIT](https://github.com/charmbracelet/log/raw/master/LICENSE)
|
||||
|
||||
---
|
||||
|
||||
Part of [Charm](https://charm.sh).
|
||||
|
||||
<a href="https://charm.sh/"><img alt="the Charm logo" src="https://stuff.charm.sh/charm-badge.jpg" width="400"></a>
|
||||
|
||||
Charm热爱开源 • Charm loves open source • نحنُ نحب المصادر المفتوحة
|
23
vendor/github.com/charmbracelet/log/context.go
generated
vendored
Normal file
23
vendor/github.com/charmbracelet/log/context.go
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
package log
|
||||
|
||||
import "context"
|
||||
|
||||
// WithContext wraps the given logger in context.
|
||||
func WithContext(ctx context.Context, logger *Logger) context.Context {
|
||||
return context.WithValue(ctx, ContextKey, logger)
|
||||
}
|
||||
|
||||
// FromContext returns the logger from the given context.
|
||||
// This will return the default package logger if no logger
|
||||
// found in context.
|
||||
func FromContext(ctx context.Context) *Logger {
|
||||
if logger, ok := ctx.Value(ContextKey).(*Logger); ok {
|
||||
return logger
|
||||
}
|
||||
return Default()
|
||||
}
|
||||
|
||||
type contextKey struct{ string }
|
||||
|
||||
// ContextKey is the key used to store the logger in context.
|
||||
var ContextKey = contextKey{"log"}
|
27
vendor/github.com/charmbracelet/log/formatter.go
generated
vendored
Normal file
27
vendor/github.com/charmbracelet/log/formatter.go
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
package log
|
||||
|
||||
// Formatter is a formatter for log messages.
|
||||
type Formatter uint8
|
||||
|
||||
const (
|
||||
// TextFormatter is a formatter that formats log messages as text. Suitable for
|
||||
// console output and log files.
|
||||
TextFormatter Formatter = iota
|
||||
// JSONFormatter is a formatter that formats log messages as JSON.
|
||||
JSONFormatter
|
||||
// LogfmtFormatter is a formatter that formats log messages as logfmt.
|
||||
LogfmtFormatter
|
||||
)
|
||||
|
||||
var (
|
||||
// TimestampKey is the key for the timestamp.
|
||||
TimestampKey = "time"
|
||||
// MessageKey is the key for the message.
|
||||
MessageKey = "msg"
|
||||
// LevelKey is the key for the level.
|
||||
LevelKey = "level"
|
||||
// CallerKey is the key for the caller.
|
||||
CallerKey = "caller"
|
||||
// PrefixKey is the key for the prefix.
|
||||
PrefixKey = "prefix"
|
||||
)
|
61
vendor/github.com/charmbracelet/log/json.go
generated
vendored
Normal file
61
vendor/github.com/charmbracelet/log/json.go
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (l *Logger) jsonFormatter(keyvals ...interface{}) {
|
||||
m := make(map[string]interface{}, len(keyvals)/2)
|
||||
for i := 0; i < len(keyvals); i += 2 {
|
||||
switch keyvals[i] {
|
||||
case TimestampKey:
|
||||
if t, ok := keyvals[i+1].(time.Time); ok {
|
||||
m[TimestampKey] = t.Format(l.timeFormat)
|
||||
}
|
||||
case LevelKey:
|
||||
if level, ok := keyvals[i+1].(Level); ok {
|
||||
m[LevelKey] = level.String()
|
||||
}
|
||||
case CallerKey:
|
||||
if caller, ok := keyvals[i+1].(string); ok {
|
||||
m[CallerKey] = caller
|
||||
}
|
||||
case PrefixKey:
|
||||
if prefix, ok := keyvals[i+1].(string); ok {
|
||||
m[PrefixKey] = prefix
|
||||
}
|
||||
case MessageKey:
|
||||
if msg := keyvals[i+1]; msg != nil {
|
||||
m[MessageKey] = fmt.Sprint(msg)
|
||||
}
|
||||
default:
|
||||
var (
|
||||
key string
|
||||
val interface{}
|
||||
)
|
||||
switch k := keyvals[i].(type) {
|
||||
case fmt.Stringer:
|
||||
key = k.String()
|
||||
case error:
|
||||
key = k.Error()
|
||||
default:
|
||||
key = fmt.Sprint(k)
|
||||
}
|
||||
switch v := keyvals[i+1].(type) {
|
||||
case error:
|
||||
val = v.Error()
|
||||
case fmt.Stringer:
|
||||
val = v.String()
|
||||
default:
|
||||
val = v
|
||||
}
|
||||
m[key] = val
|
||||
}
|
||||
}
|
||||
|
||||
e := json.NewEncoder(&l.b)
|
||||
e.SetEscapeHTML(false)
|
||||
_ = e.Encode(m)
|
||||
}
|
65
vendor/github.com/charmbracelet/log/level.go
generated
vendored
Normal file
65
vendor/github.com/charmbracelet/log/level.go
generated
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Level is a logging level.
|
||||
type Level int32
|
||||
|
||||
const (
|
||||
// DebugLevel is the debug level.
|
||||
DebugLevel Level = -4
|
||||
// InfoLevel is the info level.
|
||||
InfoLevel Level = 0
|
||||
// WarnLevel is the warn level.
|
||||
WarnLevel Level = 4
|
||||
// ErrorLevel is the error level.
|
||||
ErrorLevel Level = 8
|
||||
// FatalLevel is the fatal level.
|
||||
FatalLevel Level = 12
|
||||
// noLevel is used with log.Print.
|
||||
noLevel Level = math.MaxInt32
|
||||
)
|
||||
|
||||
// String returns the string representation of the level.
|
||||
func (l Level) String() string {
|
||||
switch l {
|
||||
case DebugLevel:
|
||||
return "debug"
|
||||
case InfoLevel:
|
||||
return "info"
|
||||
case WarnLevel:
|
||||
return "warn"
|
||||
case ErrorLevel:
|
||||
return "error"
|
||||
case FatalLevel:
|
||||
return "fatal"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// ErrInvalidLevel is an error returned when parsing an invalid level string.
|
||||
var ErrInvalidLevel = errors.New("invalid level")
|
||||
|
||||
// ParseLevel converts level in string to Level type. Default level is InfoLevel.
|
||||
func ParseLevel(level string) (Level, error) {
|
||||
switch strings.ToLower(level) {
|
||||
case DebugLevel.String():
|
||||
return DebugLevel, nil
|
||||
case InfoLevel.String():
|
||||
return InfoLevel, nil
|
||||
case WarnLevel.String():
|
||||
return WarnLevel, nil
|
||||
case ErrorLevel.String():
|
||||
return ErrorLevel, nil
|
||||
case FatalLevel.String():
|
||||
return FatalLevel, nil
|
||||
default:
|
||||
return 0, fmt.Errorf("%w: %q", ErrInvalidLevel, level)
|
||||
}
|
||||
}
|
15
vendor/github.com/charmbracelet/log/level_121.go
generated
vendored
Normal file
15
vendor/github.com/charmbracelet/log/level_121.go
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
//go:build go1.21
|
||||
// +build go1.21
|
||||
|
||||
package log
|
||||
|
||||
import "log/slog"
|
||||
|
||||
// fromSlogLevel converts slog.Level to log.Level.
|
||||
var fromSlogLevel = map[slog.Level]Level{
|
||||
slog.LevelDebug: DebugLevel,
|
||||
slog.LevelInfo: InfoLevel,
|
||||
slog.LevelWarn: WarnLevel,
|
||||
slog.LevelError: ErrorLevel,
|
||||
slog.Level(12): FatalLevel,
|
||||
}
|
15
vendor/github.com/charmbracelet/log/level_no121.go
generated
vendored
Normal file
15
vendor/github.com/charmbracelet/log/level_no121.go
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
//go:build !go1.21
|
||||
// +build !go1.21
|
||||
|
||||
package log
|
||||
|
||||
import "golang.org/x/exp/slog"
|
||||
|
||||
// fromSlogLevel converts slog.Level to log.Level.
|
||||
var fromSlogLevel = map[slog.Level]Level{
|
||||
slog.LevelDebug: DebugLevel,
|
||||
slog.LevelInfo: InfoLevel,
|
||||
slog.LevelWarn: WarnLevel,
|
||||
slog.LevelError: ErrorLevel,
|
||||
slog.Level(12): FatalLevel,
|
||||
}
|
32
vendor/github.com/charmbracelet/log/logfmt.go
generated
vendored
Normal file
32
vendor/github.com/charmbracelet/log/logfmt.go
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/go-logfmt/logfmt"
|
||||
)
|
||||
|
||||
func (l *Logger) logfmtFormatter(keyvals ...interface{}) {
|
||||
e := logfmt.NewEncoder(&l.b)
|
||||
|
||||
for i := 0; i < len(keyvals); i += 2 {
|
||||
switch keyvals[i] {
|
||||
case TimestampKey:
|
||||
if t, ok := keyvals[i+1].(time.Time); ok {
|
||||
keyvals[i+1] = t.Format(l.timeFormat)
|
||||
}
|
||||
default:
|
||||
if key := fmt.Sprint(keyvals[i]); key != "" {
|
||||
keyvals[i] = key
|
||||
}
|
||||
}
|
||||
err := e.EncodeKeyval(keyvals[i], keyvals[i+1])
|
||||
if err != nil && errors.Is(err, logfmt.ErrUnsupportedValueType) {
|
||||
// If the value is not supported by logfmt, we try to convert it to a string.
|
||||
_ = e.EncodeKeyval(keyvals[i], fmt.Sprintf("%+v", keyvals[i+1]))
|
||||
}
|
||||
}
|
||||
_ = e.EndRecord()
|
||||
}
|
409
vendor/github.com/charmbracelet/log/logger.go
generated
vendored
Normal file
409
vendor/github.com/charmbracelet/log/logger.go
generated
vendored
Normal file
@ -0,0 +1,409 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/muesli/termenv"
|
||||
)
|
||||
|
||||
// ErrMissingValue is returned when a key is missing a value.
|
||||
var ErrMissingValue = fmt.Errorf("missing value")
|
||||
|
||||
// LoggerOption is an option for a logger.
|
||||
type LoggerOption = func(*Logger)
|
||||
|
||||
// Logger is a Logger that implements Logger.
|
||||
type Logger struct {
|
||||
w io.Writer
|
||||
b bytes.Buffer
|
||||
mu *sync.RWMutex
|
||||
re *lipgloss.Renderer
|
||||
|
||||
isDiscard uint32
|
||||
|
||||
level int32
|
||||
prefix string
|
||||
timeFunc TimeFunction
|
||||
timeFormat string
|
||||
callerOffset int
|
||||
callerFormatter CallerFormatter
|
||||
formatter Formatter
|
||||
|
||||
reportCaller bool
|
||||
reportTimestamp bool
|
||||
|
||||
fields []interface{}
|
||||
|
||||
helpers *sync.Map
|
||||
styles *Styles
|
||||
}
|
||||
|
||||
// Logf logs a message with formatting.
|
||||
func (l *Logger) Logf(level Level, format string, args ...interface{}) {
|
||||
l.Log(level, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// Log logs the given message with the given keyvals for the given level.
|
||||
func (l *Logger) Log(level Level, msg interface{}, keyvals ...interface{}) {
|
||||
if atomic.LoadUint32(&l.isDiscard) != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// check if the level is allowed
|
||||
if atomic.LoadInt32(&l.level) > int32(level) {
|
||||
return
|
||||
}
|
||||
|
||||
var frame runtime.Frame
|
||||
if l.reportCaller {
|
||||
// Skip log.log, the caller, and any offset added.
|
||||
frames := l.frames(l.callerOffset + 2)
|
||||
for {
|
||||
f, more := frames.Next()
|
||||
_, helper := l.helpers.Load(f.Function)
|
||||
if !helper || !more {
|
||||
// Found a frame that wasn't a helper function.
|
||||
// Or we ran out of frames to check.
|
||||
frame = f
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
l.handle(level, l.timeFunc(time.Now()), []runtime.Frame{frame}, msg, keyvals...)
|
||||
}
|
||||
|
||||
func (l *Logger) handle(level Level, ts time.Time, frames []runtime.Frame, msg interface{}, keyvals ...interface{}) {
|
||||
var kvs []interface{}
|
||||
if l.reportTimestamp && !ts.IsZero() {
|
||||
kvs = append(kvs, TimestampKey, ts)
|
||||
}
|
||||
|
||||
_, ok := l.styles.Levels[level]
|
||||
if ok {
|
||||
kvs = append(kvs, LevelKey, level)
|
||||
}
|
||||
|
||||
if l.reportCaller && len(frames) > 0 && frames[0].PC != 0 {
|
||||
file, line, fn := l.location(frames)
|
||||
if file != "" {
|
||||
caller := l.callerFormatter(file, line, fn)
|
||||
kvs = append(kvs, CallerKey, caller)
|
||||
}
|
||||
}
|
||||
|
||||
if l.prefix != "" {
|
||||
kvs = append(kvs, PrefixKey, l.prefix)
|
||||
}
|
||||
|
||||
if msg != nil {
|
||||
if m := fmt.Sprint(msg); m != "" {
|
||||
kvs = append(kvs, MessageKey, m)
|
||||
}
|
||||
}
|
||||
|
||||
// append logger fields
|
||||
kvs = append(kvs, l.fields...)
|
||||
if len(l.fields)%2 != 0 {
|
||||
kvs = append(kvs, ErrMissingValue)
|
||||
}
|
||||
|
||||
// append the rest
|
||||
kvs = append(kvs, keyvals...)
|
||||
if len(keyvals)%2 != 0 {
|
||||
kvs = append(kvs, ErrMissingValue)
|
||||
}
|
||||
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
switch l.formatter {
|
||||
case LogfmtFormatter:
|
||||
l.logfmtFormatter(kvs...)
|
||||
case JSONFormatter:
|
||||
l.jsonFormatter(kvs...)
|
||||
default:
|
||||
l.textFormatter(kvs...)
|
||||
}
|
||||
|
||||
// WriteTo will reset the buffer
|
||||
l.b.WriteTo(l.w) //nolint: errcheck
|
||||
}
|
||||
|
||||
// Helper marks the calling function as a helper
|
||||
// and skips it for source location information.
|
||||
// It's the equivalent of testing.TB.Helper().
|
||||
func (l *Logger) Helper() {
|
||||
l.helper(1)
|
||||
}
|
||||
|
||||
func (l *Logger) helper(skip int) {
|
||||
var pcs [1]uintptr
|
||||
// Skip runtime.Callers, and l.helper
|
||||
n := runtime.Callers(skip+2, pcs[:])
|
||||
frames := runtime.CallersFrames(pcs[:n])
|
||||
frame, _ := frames.Next()
|
||||
l.helpers.LoadOrStore(frame.Function, struct{}{})
|
||||
}
|
||||
|
||||
// frames returns the runtime.Frames for the caller.
|
||||
func (l *Logger) frames(skip int) *runtime.Frames {
|
||||
// Copied from testing.T
|
||||
const maxStackLen = 50
|
||||
var pc [maxStackLen]uintptr
|
||||
|
||||
// Skip runtime.Callers, and l.frame
|
||||
n := runtime.Callers(skip+2, pc[:])
|
||||
frames := runtime.CallersFrames(pc[:n])
|
||||
return frames
|
||||
}
|
||||
|
||||
func (l *Logger) location(frames []runtime.Frame) (file string, line int, fn string) {
|
||||
if len(frames) == 0 {
|
||||
return "", 0, ""
|
||||
}
|
||||
f := frames[0]
|
||||
return f.File, f.Line, f.Function
|
||||
}
|
||||
|
||||
// Cleanup a path by returning the last n segments of the path only.
|
||||
func trimCallerPath(path string, n int) string {
|
||||
// lovely borrowed from zap
|
||||
// nb. To make sure we trim the path correctly on Windows too, we
|
||||
// counter-intuitively need to use '/' and *not* os.PathSeparator here,
|
||||
// because the path given originates from Go stdlib, specifically
|
||||
// runtime.Caller() which (as of Mar/17) returns forward slashes even on
|
||||
// Windows.
|
||||
//
|
||||
// See https://github.com/golang/go/issues/3335
|
||||
// and https://github.com/golang/go/issues/18151
|
||||
//
|
||||
// for discussion on the issue on Go side.
|
||||
|
||||
// Return the full path if n is 0.
|
||||
if n <= 0 {
|
||||
return path
|
||||
}
|
||||
|
||||
// Find the last separator.
|
||||
idx := strings.LastIndexByte(path, '/')
|
||||
if idx == -1 {
|
||||
return path
|
||||
}
|
||||
|
||||
for i := 0; i < n-1; i++ {
|
||||
// Find the penultimate separator.
|
||||
idx = strings.LastIndexByte(path[:idx], '/')
|
||||
if idx == -1 {
|
||||
return path
|
||||
}
|
||||
}
|
||||
|
||||
return path[idx+1:]
|
||||
}
|
||||
|
||||
// SetReportTimestamp sets whether the timestamp should be reported.
|
||||
func (l *Logger) SetReportTimestamp(report bool) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.reportTimestamp = report
|
||||
}
|
||||
|
||||
// SetReportCaller sets whether the caller location should be reported.
|
||||
func (l *Logger) SetReportCaller(report bool) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.reportCaller = report
|
||||
}
|
||||
|
||||
// GetLevel returns the current level.
|
||||
func (l *Logger) GetLevel() Level {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
return Level(l.level)
|
||||
}
|
||||
|
||||
// SetLevel sets the current level.
|
||||
func (l *Logger) SetLevel(level Level) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
atomic.StoreInt32(&l.level, int32(level))
|
||||
}
|
||||
|
||||
// GetPrefix returns the current prefix.
|
||||
func (l *Logger) GetPrefix() string {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
return l.prefix
|
||||
}
|
||||
|
||||
// SetPrefix sets the current prefix.
|
||||
func (l *Logger) SetPrefix(prefix string) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.prefix = prefix
|
||||
}
|
||||
|
||||
// SetTimeFormat sets the time format.
|
||||
func (l *Logger) SetTimeFormat(format string) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.timeFormat = format
|
||||
}
|
||||
|
||||
// SetTimeFunction sets the time function.
|
||||
func (l *Logger) SetTimeFunction(f TimeFunction) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.timeFunc = f
|
||||
}
|
||||
|
||||
// SetOutput sets the output destination.
|
||||
func (l *Logger) SetOutput(w io.Writer) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if w == nil {
|
||||
w = os.Stderr
|
||||
}
|
||||
l.w = w
|
||||
var isDiscard uint32
|
||||
if w == io.Discard {
|
||||
isDiscard = 1
|
||||
}
|
||||
atomic.StoreUint32(&l.isDiscard, isDiscard)
|
||||
// Reuse cached renderers
|
||||
if v, ok := registry.Load(w); ok {
|
||||
l.re = v.(*lipgloss.Renderer)
|
||||
} else {
|
||||
l.re = lipgloss.NewRenderer(w, termenv.WithColorCache(true))
|
||||
registry.Store(w, l.re)
|
||||
}
|
||||
}
|
||||
|
||||
// SetFormatter sets the formatter.
|
||||
func (l *Logger) SetFormatter(f Formatter) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.formatter = f
|
||||
}
|
||||
|
||||
// SetCallerFormatter sets the caller formatter.
|
||||
func (l *Logger) SetCallerFormatter(f CallerFormatter) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.callerFormatter = f
|
||||
}
|
||||
|
||||
// SetCallerOffset sets the caller offset.
|
||||
func (l *Logger) SetCallerOffset(offset int) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.callerOffset = offset
|
||||
}
|
||||
|
||||
// SetColorProfile force sets the underlying Lip Gloss renderer color profile
|
||||
// for the TextFormatter.
|
||||
func (l *Logger) SetColorProfile(profile termenv.Profile) {
|
||||
l.re.SetColorProfile(profile)
|
||||
}
|
||||
|
||||
// SetStyles sets the logger styles for the TextFormatter.
|
||||
func (l *Logger) SetStyles(s *Styles) {
|
||||
if s == nil {
|
||||
s = DefaultStyles()
|
||||
}
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.styles = s
|
||||
}
|
||||
|
||||
// With returns a new logger with the given keyvals added.
|
||||
func (l *Logger) With(keyvals ...interface{}) *Logger {
|
||||
var st Styles
|
||||
l.mu.Lock()
|
||||
sl := *l
|
||||
st = *l.styles
|
||||
l.mu.Unlock()
|
||||
sl.b = bytes.Buffer{}
|
||||
sl.mu = &sync.RWMutex{}
|
||||
sl.helpers = &sync.Map{}
|
||||
sl.fields = append(l.fields, keyvals...)
|
||||
sl.styles = &st
|
||||
return &sl
|
||||
}
|
||||
|
||||
// WithPrefix returns a new logger with the given prefix.
|
||||
func (l *Logger) WithPrefix(prefix string) *Logger {
|
||||
sl := l.With()
|
||||
sl.SetPrefix(prefix)
|
||||
return sl
|
||||
}
|
||||
|
||||
// Debug prints a debug message.
|
||||
func (l *Logger) Debug(msg interface{}, keyvals ...interface{}) {
|
||||
l.Log(DebugLevel, msg, keyvals...)
|
||||
}
|
||||
|
||||
// Info prints an info message.
|
||||
func (l *Logger) Info(msg interface{}, keyvals ...interface{}) {
|
||||
l.Log(InfoLevel, msg, keyvals...)
|
||||
}
|
||||
|
||||
// Warn prints a warning message.
|
||||
func (l *Logger) Warn(msg interface{}, keyvals ...interface{}) {
|
||||
l.Log(WarnLevel, msg, keyvals...)
|
||||
}
|
||||
|
||||
// Error prints an error message.
|
||||
func (l *Logger) Error(msg interface{}, keyvals ...interface{}) {
|
||||
l.Log(ErrorLevel, msg, keyvals...)
|
||||
}
|
||||
|
||||
// Fatal prints a fatal message and exits.
|
||||
func (l *Logger) Fatal(msg interface{}, keyvals ...interface{}) {
|
||||
l.Log(FatalLevel, msg, keyvals...)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Print prints a message with no level.
|
||||
func (l *Logger) Print(msg interface{}, keyvals ...interface{}) {
|
||||
l.Log(noLevel, msg, keyvals...)
|
||||
}
|
||||
|
||||
// Debugf prints a debug message with formatting.
|
||||
func (l *Logger) Debugf(format string, args ...interface{}) {
|
||||
l.Log(DebugLevel, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// Infof prints an info message with formatting.
|
||||
func (l *Logger) Infof(format string, args ...interface{}) {
|
||||
l.Log(InfoLevel, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// Warnf prints a warning message with formatting.
|
||||
func (l *Logger) Warnf(format string, args ...interface{}) {
|
||||
l.Log(WarnLevel, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// Errorf prints an error message with formatting.
|
||||
func (l *Logger) Errorf(format string, args ...interface{}) {
|
||||
l.Log(ErrorLevel, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// Fatalf prints a fatal message with formatting and exits.
|
||||
func (l *Logger) Fatalf(format string, args ...interface{}) {
|
||||
l.Log(FatalLevel, fmt.Sprintf(format, args...))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Printf prints a message with no level and formatting.
|
||||
func (l *Logger) Printf(format string, args ...interface{}) {
|
||||
l.Log(noLevel, fmt.Sprintf(format, args...))
|
||||
}
|
62
vendor/github.com/charmbracelet/log/logger_121.go
generated
vendored
Normal file
62
vendor/github.com/charmbracelet/log/logger_121.go
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
//go:build go1.21
|
||||
// +build go1.21
|
||||
|
||||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"runtime"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// Enabled reports whether the logger is enabled for the given level.
|
||||
//
|
||||
// Implements slog.Handler.
|
||||
func (l *Logger) Enabled(_ context.Context, level slog.Level) bool {
|
||||
return atomic.LoadInt32(&l.level) <= int32(fromSlogLevel[level])
|
||||
}
|
||||
|
||||
// Handle handles the Record. It will only be called if Enabled returns true.
|
||||
//
|
||||
// Implements slog.Handler.
|
||||
func (l *Logger) Handle(ctx context.Context, record slog.Record) error {
|
||||
if !l.Enabled(ctx, record.Level) {
|
||||
return nil
|
||||
}
|
||||
|
||||
fields := make([]interface{}, 0, record.NumAttrs()*2)
|
||||
record.Attrs(func(a slog.Attr) bool {
|
||||
fields = append(fields, a.Key, a.Value.String())
|
||||
return true
|
||||
})
|
||||
// Get the caller frame using the record's PC.
|
||||
frames := runtime.CallersFrames([]uintptr{record.PC})
|
||||
frame, _ := frames.Next()
|
||||
l.handle(fromSlogLevel[record.Level], l.timeFunc(record.Time), []runtime.Frame{frame}, record.Message, fields...)
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithAttrs returns a new Handler with the given attributes added.
|
||||
//
|
||||
// Implements slog.Handler.
|
||||
func (l *Logger) WithAttrs(attrs []slog.Attr) slog.Handler {
|
||||
fields := make([]interface{}, 0, len(attrs)*2)
|
||||
for _, attr := range attrs {
|
||||
fields = append(fields, attr.Key, attr.Value)
|
||||
}
|
||||
return l.With(fields...)
|
||||
}
|
||||
|
||||
// WithGroup returns a new Handler with the given group name prepended to the
|
||||
// current group name or prefix.
|
||||
//
|
||||
// Implements slog.Handler.
|
||||
func (l *Logger) WithGroup(name string) slog.Handler {
|
||||
if l.prefix != "" {
|
||||
name = l.prefix + "." + name
|
||||
}
|
||||
return l.WithPrefix(name)
|
||||
}
|
||||
|
||||
var _ slog.Handler = (*Logger)(nil)
|
59
vendor/github.com/charmbracelet/log/logger_no121.go
generated
vendored
Normal file
59
vendor/github.com/charmbracelet/log/logger_no121.go
generated
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
//go:build !go1.21
|
||||
// +build !go1.21
|
||||
|
||||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
"runtime"
|
||||
"sync/atomic"
|
||||
|
||||
"golang.org/x/exp/slog"
|
||||
)
|
||||
|
||||
// Enabled reports whether the logger is enabled for the given level.
|
||||
//
|
||||
// Implements slog.Handler.
|
||||
func (l *Logger) Enabled(_ context.Context, level slog.Level) bool {
|
||||
return atomic.LoadInt32(&l.level) <= int32(fromSlogLevel[level])
|
||||
}
|
||||
|
||||
// Handle handles the Record. It will only be called if Enabled returns true.
|
||||
//
|
||||
// Implements slog.Handler.
|
||||
func (l *Logger) Handle(_ context.Context, record slog.Record) error {
|
||||
fields := make([]interface{}, 0, record.NumAttrs()*2)
|
||||
record.Attrs(func(a slog.Attr) bool {
|
||||
fields = append(fields, a.Key, a.Value.String())
|
||||
return true
|
||||
})
|
||||
// Get the caller frame using the record's PC.
|
||||
frames := runtime.CallersFrames([]uintptr{record.PC})
|
||||
frame, _ := frames.Next()
|
||||
l.handle(fromSlogLevel[record.Level], l.timeFunc(record.Time), []runtime.Frame{frame}, record.Message, fields...)
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithAttrs returns a new Handler with the given attributes added.
|
||||
//
|
||||
// Implements slog.Handler.
|
||||
func (l *Logger) WithAttrs(attrs []slog.Attr) slog.Handler {
|
||||
fields := make([]interface{}, 0, len(attrs)*2)
|
||||
for _, attr := range attrs {
|
||||
fields = append(fields, attr.Key, attr.Value)
|
||||
}
|
||||
return l.With(fields...)
|
||||
}
|
||||
|
||||
// WithGroup returns a new Handler with the given group name prepended to the
|
||||
// current group name or prefix.
|
||||
//
|
||||
// Implements slog.Handler.
|
||||
func (l *Logger) WithGroup(name string) slog.Handler {
|
||||
if l.prefix != "" {
|
||||
name = l.prefix + "." + name
|
||||
}
|
||||
return l.WithPrefix(name)
|
||||
}
|
||||
|
||||
var _ slog.Handler = (*Logger)(nil)
|
61
vendor/github.com/charmbracelet/log/options.go
generated
vendored
Normal file
61
vendor/github.com/charmbracelet/log/options.go
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DefaultTimeFormat is the default time format.
|
||||
const DefaultTimeFormat = "2006/01/02 15:04:05"
|
||||
|
||||
// TimeFunction is a function that returns a time.Time.
|
||||
type TimeFunction = func(time.Time) time.Time
|
||||
|
||||
// NowUTC is a convenient function that returns the
|
||||
// current time in UTC timezone.
|
||||
//
|
||||
// This is to be used as a time function.
|
||||
// For example:
|
||||
//
|
||||
// log.SetTimeFunction(log.NowUTC)
|
||||
func NowUTC(t time.Time) time.Time {
|
||||
return t.UTC()
|
||||
}
|
||||
|
||||
// CallerFormatter is the caller formatter.
|
||||
type CallerFormatter func(string, int, string) string
|
||||
|
||||
// ShortCallerFormatter is a caller formatter that returns the last 2 levels of the path
|
||||
// and line number.
|
||||
func ShortCallerFormatter(file string, line int, _ string) string {
|
||||
return fmt.Sprintf("%s:%d", trimCallerPath(file, 2), line)
|
||||
}
|
||||
|
||||
// LongCallerFormatter is a caller formatter that returns the full path and line number.
|
||||
func LongCallerFormatter(file string, line int, _ string) string {
|
||||
return fmt.Sprintf("%s:%d", file, line)
|
||||
}
|
||||
|
||||
// Options is the options for the logger.
|
||||
type Options struct {
|
||||
// TimeFunction is the time function for the logger. The default is time.Now.
|
||||
TimeFunction TimeFunction
|
||||
// TimeFormat is the time format for the logger. The default is "2006/01/02 15:04:05".
|
||||
TimeFormat string
|
||||
// Level is the level for the logger. The default is InfoLevel.
|
||||
Level Level
|
||||
// Prefix is the prefix for the logger. The default is no prefix.
|
||||
Prefix string
|
||||
// ReportTimestamp is whether the logger should report the timestamp. The default is false.
|
||||
ReportTimestamp bool
|
||||
// ReportCaller is whether the logger should report the caller location. The default is false.
|
||||
ReportCaller bool
|
||||
// CallerFormatter is the caller format for the logger. The default is ShortCallerFormatter.
|
||||
CallerFormatter CallerFormatter
|
||||
// CallerOffset is the caller format for the logger. The default is 0.
|
||||
CallerOffset int
|
||||
// Fields is the fields for the logger. The default is no fields.
|
||||
Fields []interface{}
|
||||
// Formatter is the formatter for the logger. The default is TextFormatter.
|
||||
Formatter Formatter
|
||||
}
|
246
vendor/github.com/charmbracelet/log/pkg.go
generated
vendored
Normal file
246
vendor/github.com/charmbracelet/log/pkg.go
generated
vendored
Normal file
@ -0,0 +1,246 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/muesli/termenv"
|
||||
)
|
||||
|
||||
var (
|
||||
// registry is a map of all registered lipgloss renderers.
|
||||
registry = sync.Map{}
|
||||
|
||||
// defaultLogger is the default global logger instance.
|
||||
defaultLoggerOnce sync.Once
|
||||
defaultLogger *Logger
|
||||
)
|
||||
|
||||
// Default returns the default logger. The default logger comes with timestamp enabled.
|
||||
func Default() *Logger {
|
||||
defaultLoggerOnce.Do(func() {
|
||||
if defaultLogger != nil {
|
||||
// already set via SetDefault.
|
||||
return
|
||||
}
|
||||
defaultLogger = NewWithOptions(os.Stderr, Options{ReportTimestamp: true})
|
||||
})
|
||||
return defaultLogger
|
||||
}
|
||||
|
||||
// SetDefault sets the default global logger.
|
||||
func SetDefault(logger *Logger) {
|
||||
defaultLogger = logger
|
||||
}
|
||||
|
||||
// New returns a new logger with the default options.
|
||||
func New(w io.Writer) *Logger {
|
||||
return NewWithOptions(w, Options{})
|
||||
}
|
||||
|
||||
// NewWithOptions returns a new logger using the provided options.
|
||||
func NewWithOptions(w io.Writer, o Options) *Logger {
|
||||
l := &Logger{
|
||||
b: bytes.Buffer{},
|
||||
mu: &sync.RWMutex{},
|
||||
helpers: &sync.Map{},
|
||||
level: int32(o.Level),
|
||||
reportTimestamp: o.ReportTimestamp,
|
||||
reportCaller: o.ReportCaller,
|
||||
prefix: o.Prefix,
|
||||
timeFunc: o.TimeFunction,
|
||||
timeFormat: o.TimeFormat,
|
||||
formatter: o.Formatter,
|
||||
fields: o.Fields,
|
||||
callerFormatter: o.CallerFormatter,
|
||||
callerOffset: o.CallerOffset,
|
||||
}
|
||||
|
||||
l.SetOutput(w)
|
||||
l.SetLevel(Level(l.level))
|
||||
l.SetStyles(DefaultStyles())
|
||||
|
||||
if l.callerFormatter == nil {
|
||||
l.callerFormatter = ShortCallerFormatter
|
||||
}
|
||||
|
||||
if l.timeFunc == nil {
|
||||
l.timeFunc = func(t time.Time) time.Time { return t }
|
||||
}
|
||||
|
||||
if l.timeFormat == "" {
|
||||
l.timeFormat = DefaultTimeFormat
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
// SetReportTimestamp sets whether to report timestamp for the default logger.
|
||||
func SetReportTimestamp(report bool) {
|
||||
Default().SetReportTimestamp(report)
|
||||
}
|
||||
|
||||
// SetReportCaller sets whether to report caller location for the default logger.
|
||||
func SetReportCaller(report bool) {
|
||||
Default().SetReportCaller(report)
|
||||
}
|
||||
|
||||
// SetLevel sets the level for the default logger.
|
||||
func SetLevel(level Level) {
|
||||
Default().SetLevel(level)
|
||||
}
|
||||
|
||||
// GetLevel returns the level for the default logger.
|
||||
func GetLevel() Level {
|
||||
return Default().GetLevel()
|
||||
}
|
||||
|
||||
// SetTimeFormat sets the time format for the default logger.
|
||||
func SetTimeFormat(format string) {
|
||||
Default().SetTimeFormat(format)
|
||||
}
|
||||
|
||||
// SetTimeFunction sets the time function for the default logger.
|
||||
func SetTimeFunction(f TimeFunction) {
|
||||
Default().SetTimeFunction(f)
|
||||
}
|
||||
|
||||
// SetOutput sets the output for the default logger.
|
||||
func SetOutput(w io.Writer) {
|
||||
Default().SetOutput(w)
|
||||
}
|
||||
|
||||
// SetFormatter sets the formatter for the default logger.
|
||||
func SetFormatter(f Formatter) {
|
||||
Default().SetFormatter(f)
|
||||
}
|
||||
|
||||
// SetCallerFormatter sets the caller formatter for the default logger.
|
||||
func SetCallerFormatter(f CallerFormatter) {
|
||||
Default().SetCallerFormatter(f)
|
||||
}
|
||||
|
||||
// SetCallerOffset sets the caller offset for the default logger.
|
||||
func SetCallerOffset(offset int) {
|
||||
Default().SetCallerOffset(offset)
|
||||
}
|
||||
|
||||
// SetPrefix sets the prefix for the default logger.
|
||||
func SetPrefix(prefix string) {
|
||||
Default().SetPrefix(prefix)
|
||||
}
|
||||
|
||||
// SetColorProfile force sets the underlying Lip Gloss renderer color profile
|
||||
// for the TextFormatter.
|
||||
func SetColorProfile(profile termenv.Profile) {
|
||||
Default().SetColorProfile(profile)
|
||||
}
|
||||
|
||||
// SetStyles sets the logger styles for the TextFormatter.
|
||||
func SetStyles(s *Styles) {
|
||||
Default().SetStyles(s)
|
||||
}
|
||||
|
||||
// GetPrefix returns the prefix for the default logger.
|
||||
func GetPrefix() string {
|
||||
return Default().GetPrefix()
|
||||
}
|
||||
|
||||
// With returns a new logger with the given keyvals.
|
||||
func With(keyvals ...interface{}) *Logger {
|
||||
return Default().With(keyvals...)
|
||||
}
|
||||
|
||||
// WithPrefix returns a new logger with the given prefix.
|
||||
func WithPrefix(prefix string) *Logger {
|
||||
return Default().WithPrefix(prefix)
|
||||
}
|
||||
|
||||
// Helper marks the calling function as a helper
|
||||
// and skips it for source location information.
|
||||
// It's the equivalent of testing.TB.Helper().
|
||||
func Helper() {
|
||||
Default().helper(1)
|
||||
}
|
||||
|
||||
// Log logs a message with the given level.
|
||||
func Log(level Level, msg interface{}, keyvals ...interface{}) {
|
||||
Default().Log(level, msg, keyvals...)
|
||||
}
|
||||
|
||||
// Debug logs a debug message.
|
||||
func Debug(msg interface{}, keyvals ...interface{}) {
|
||||
Default().Log(DebugLevel, msg, keyvals...)
|
||||
}
|
||||
|
||||
// Info logs an info message.
|
||||
func Info(msg interface{}, keyvals ...interface{}) {
|
||||
Default().Log(InfoLevel, msg, keyvals...)
|
||||
}
|
||||
|
||||
// Warn logs a warning message.
|
||||
func Warn(msg interface{}, keyvals ...interface{}) {
|
||||
Default().Log(WarnLevel, msg, keyvals...)
|
||||
}
|
||||
|
||||
// Error logs an error message.
|
||||
func Error(msg interface{}, keyvals ...interface{}) {
|
||||
Default().Log(ErrorLevel, msg, keyvals...)
|
||||
}
|
||||
|
||||
// Fatal logs a fatal message and exit.
|
||||
func Fatal(msg interface{}, keyvals ...interface{}) {
|
||||
Default().Log(FatalLevel, msg, keyvals...)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Print logs a message with no level.
|
||||
func Print(msg interface{}, keyvals ...interface{}) {
|
||||
Default().Log(noLevel, msg, keyvals...)
|
||||
}
|
||||
|
||||
// Logf logs a message with formatting and level.
|
||||
func Logf(level Level, format string, args ...interface{}) {
|
||||
Default().Logf(level, format, args...)
|
||||
}
|
||||
|
||||
// Debugf logs a debug message with formatting.
|
||||
func Debugf(format string, args ...interface{}) {
|
||||
Default().Log(DebugLevel, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// Infof logs an info message with formatting.
|
||||
func Infof(format string, args ...interface{}) {
|
||||
Default().Log(InfoLevel, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// Warnf logs a warning message with formatting.
|
||||
func Warnf(format string, args ...interface{}) {
|
||||
Default().Log(WarnLevel, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// Errorf logs an error message with formatting.
|
||||
func Errorf(format string, args ...interface{}) {
|
||||
Default().Log(ErrorLevel, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// Fatalf logs a fatal message with formatting and exit.
|
||||
func Fatalf(format string, args ...interface{}) {
|
||||
Default().Log(FatalLevel, fmt.Sprintf(format, args...))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Printf logs a message with formatting and no level.
|
||||
func Printf(format string, args ...interface{}) {
|
||||
Default().Log(noLevel, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// StandardLog returns a standard logger from the default logger.
|
||||
func StandardLog(opts ...StandardLogOptions) *log.Logger {
|
||||
return Default().StandardLog(opts...)
|
||||
}
|
67
vendor/github.com/charmbracelet/log/stdlog.go
generated
vendored
Normal file
67
vendor/github.com/charmbracelet/log/stdlog.go
generated
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type stdLogWriter struct {
|
||||
l *Logger
|
||||
opt *StandardLogOptions
|
||||
}
|
||||
|
||||
func (l *stdLogWriter) Write(p []byte) (n int, err error) {
|
||||
str := strings.TrimSuffix(string(p), "\n")
|
||||
|
||||
if l.opt != nil {
|
||||
switch l.opt.ForceLevel {
|
||||
case DebugLevel:
|
||||
l.l.Debug(str)
|
||||
case InfoLevel:
|
||||
l.l.Info(str)
|
||||
case WarnLevel:
|
||||
l.l.Warn(str)
|
||||
case ErrorLevel:
|
||||
l.l.Error(str)
|
||||
}
|
||||
} else {
|
||||
switch {
|
||||
case strings.HasPrefix(str, "DEBUG"):
|
||||
l.l.Debug(strings.TrimSpace(str[5:]))
|
||||
case strings.HasPrefix(str, "INFO"):
|
||||
l.l.Info(strings.TrimSpace(str[4:]))
|
||||
case strings.HasPrefix(str, "WARN"):
|
||||
l.l.Warn(strings.TrimSpace(str[4:]))
|
||||
case strings.HasPrefix(str, "ERROR"):
|
||||
l.l.Error(strings.TrimSpace(str[5:]))
|
||||
case strings.HasPrefix(str, "ERR"):
|
||||
l.l.Error(strings.TrimSpace(str[3:]))
|
||||
default:
|
||||
l.l.Info(str)
|
||||
}
|
||||
}
|
||||
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// StandardLogOptions can be used to configure the standard log adapter.
|
||||
type StandardLogOptions struct {
|
||||
ForceLevel Level
|
||||
}
|
||||
|
||||
// StandardLog returns a standard logger from Logger. The returned logger
|
||||
// can infer log levels from message prefix. Expected prefixes are DEBUG, INFO,
|
||||
// WARN, ERROR, and ERR.
|
||||
func (l *Logger) StandardLog(opts ...StandardLogOptions) *log.Logger {
|
||||
nl := l.With()
|
||||
// The caller stack is
|
||||
// log.Printf() -> l.Output() -> l.out.Write(stdLogger.Write)
|
||||
nl.callerOffset += 3
|
||||
sl := &stdLogWriter{
|
||||
l: nl,
|
||||
}
|
||||
if len(opts) > 0 {
|
||||
sl.opt = &opts[0]
|
||||
}
|
||||
return log.New(sl, "", 0)
|
||||
}
|
82
vendor/github.com/charmbracelet/log/styles.go
generated
vendored
Normal file
82
vendor/github.com/charmbracelet/log/styles.go
generated
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
)
|
||||
|
||||
// Styles defines the styles for the text logger.
|
||||
type Styles struct {
|
||||
// Timestamp is the style for timestamps.
|
||||
Timestamp lipgloss.Style
|
||||
|
||||
// Caller is the style for source caller.
|
||||
Caller lipgloss.Style
|
||||
|
||||
// Prefix is the style for prefix.
|
||||
Prefix lipgloss.Style
|
||||
|
||||
// Message is the style for messages.
|
||||
Message lipgloss.Style
|
||||
|
||||
// Key is the style for keys.
|
||||
Key lipgloss.Style
|
||||
|
||||
// Value is the style for values.
|
||||
Value lipgloss.Style
|
||||
|
||||
// Separator is the style for separators.
|
||||
Separator lipgloss.Style
|
||||
|
||||
// Levels are the styles for each level.
|
||||
Levels map[Level]lipgloss.Style
|
||||
|
||||
// Keys overrides styles for specific keys.
|
||||
Keys map[string]lipgloss.Style
|
||||
|
||||
// Values overrides value styles for specific keys.
|
||||
Values map[string]lipgloss.Style
|
||||
}
|
||||
|
||||
// DefaultStyles returns the default styles.
|
||||
func DefaultStyles() *Styles {
|
||||
return &Styles{
|
||||
Timestamp: lipgloss.NewStyle(),
|
||||
Caller: lipgloss.NewStyle().Faint(true),
|
||||
Prefix: lipgloss.NewStyle().Bold(true).Faint(true),
|
||||
Message: lipgloss.NewStyle(),
|
||||
Key: lipgloss.NewStyle().Faint(true),
|
||||
Value: lipgloss.NewStyle(),
|
||||
Separator: lipgloss.NewStyle().Faint(true),
|
||||
Levels: map[Level]lipgloss.Style{
|
||||
DebugLevel: lipgloss.NewStyle().
|
||||
SetString(strings.ToUpper(DebugLevel.String())).
|
||||
Bold(true).
|
||||
MaxWidth(4).
|
||||
Foreground(lipgloss.Color("63")),
|
||||
InfoLevel: lipgloss.NewStyle().
|
||||
SetString(strings.ToUpper(InfoLevel.String())).
|
||||
Bold(true).
|
||||
MaxWidth(4).
|
||||
Foreground(lipgloss.Color("86")),
|
||||
WarnLevel: lipgloss.NewStyle().
|
||||
SetString(strings.ToUpper(WarnLevel.String())).
|
||||
Bold(true).
|
||||
MaxWidth(4).
|
||||
Foreground(lipgloss.Color("192")),
|
||||
ErrorLevel: lipgloss.NewStyle().
|
||||
SetString(strings.ToUpper(ErrorLevel.String())).
|
||||
Bold(true).
|
||||
MaxWidth(4).
|
||||
Foreground(lipgloss.Color("204")),
|
||||
FatalLevel: lipgloss.NewStyle().
|
||||
SetString(strings.ToUpper(FatalLevel.String())).
|
||||
Bold(true).
|
||||
MaxWidth(4).
|
||||
Foreground(lipgloss.Color("134")),
|
||||
},
|
||||
Keys: map[string]lipgloss.Style{},
|
||||
Values: map[string]lipgloss.Style{},
|
||||
}
|
||||
}
|
272
vendor/github.com/charmbracelet/log/text.go
generated
vendored
Normal file
272
vendor/github.com/charmbracelet/log/text.go
generated
vendored
Normal file
@ -0,0 +1,272 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
const (
|
||||
separator = "="
|
||||
indentSeparator = " │ "
|
||||
)
|
||||
|
||||
func (l *Logger) writeIndent(w io.Writer, str string, indent string, newline bool, key string) {
|
||||
st := l.styles
|
||||
|
||||
// kindly borrowed from hclog
|
||||
for {
|
||||
nl := strings.IndexByte(str, '\n')
|
||||
if nl == -1 {
|
||||
if str != "" {
|
||||
_, _ = w.Write([]byte(indent))
|
||||
val := escapeStringForOutput(str, false)
|
||||
if valueStyle, ok := st.Values[key]; ok {
|
||||
val = valueStyle.Renderer(l.re).Render(val)
|
||||
} else {
|
||||
val = st.Value.Renderer(l.re).Render(val)
|
||||
}
|
||||
_, _ = w.Write([]byte(val))
|
||||
if newline {
|
||||
_, _ = w.Write([]byte{'\n'})
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = w.Write([]byte(indent))
|
||||
val := escapeStringForOutput(str[:nl], false)
|
||||
val = st.Value.Renderer(l.re).Render(val)
|
||||
_, _ = w.Write([]byte(val))
|
||||
_, _ = w.Write([]byte{'\n'})
|
||||
str = str[nl+1:]
|
||||
}
|
||||
}
|
||||
|
||||
func needsEscaping(str string) bool {
|
||||
for _, b := range str {
|
||||
if !unicode.IsPrint(b) || b == '"' {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
const (
|
||||
lowerhex = "0123456789abcdef"
|
||||
)
|
||||
|
||||
var bufPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(strings.Builder)
|
||||
},
|
||||
}
|
||||
|
||||
func escapeStringForOutput(str string, escapeQuotes bool) string {
|
||||
// kindly borrowed from hclog
|
||||
if !needsEscaping(str) {
|
||||
return str
|
||||
}
|
||||
|
||||
bb := bufPool.Get().(*strings.Builder)
|
||||
bb.Reset()
|
||||
|
||||
defer bufPool.Put(bb)
|
||||
for _, r := range str {
|
||||
if escapeQuotes && r == '"' {
|
||||
bb.WriteString(`\"`)
|
||||
} else if unicode.IsPrint(r) {
|
||||
bb.WriteRune(r)
|
||||
} else {
|
||||
switch r {
|
||||
case '\a':
|
||||
bb.WriteString(`\a`)
|
||||
case '\b':
|
||||
bb.WriteString(`\b`)
|
||||
case '\f':
|
||||
bb.WriteString(`\f`)
|
||||
case '\n':
|
||||
bb.WriteString(`\n`)
|
||||
case '\r':
|
||||
bb.WriteString(`\r`)
|
||||
case '\t':
|
||||
bb.WriteString(`\t`)
|
||||
case '\v':
|
||||
bb.WriteString(`\v`)
|
||||
default:
|
||||
switch {
|
||||
case r < ' ':
|
||||
bb.WriteString(`\x`)
|
||||
bb.WriteByte(lowerhex[byte(r)>>4])
|
||||
bb.WriteByte(lowerhex[byte(r)&0xF])
|
||||
case !utf8.ValidRune(r):
|
||||
r = 0xFFFD
|
||||
fallthrough
|
||||
case r < 0x10000:
|
||||
bb.WriteString(`\u`)
|
||||
for s := 12; s >= 0; s -= 4 {
|
||||
bb.WriteByte(lowerhex[r>>uint(s)&0xF])
|
||||
}
|
||||
default:
|
||||
bb.WriteString(`\U`)
|
||||
for s := 28; s >= 0; s -= 4 {
|
||||
bb.WriteByte(lowerhex[r>>uint(s)&0xF])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bb.String()
|
||||
}
|
||||
|
||||
func needsQuoting(s string) bool {
|
||||
for i := 0; i < len(s); {
|
||||
b := s[i]
|
||||
if b < utf8.RuneSelf {
|
||||
if needsQuotingSet[b] {
|
||||
return true
|
||||
}
|
||||
i++
|
||||
continue
|
||||
}
|
||||
r, size := utf8.DecodeRuneInString(s[i:])
|
||||
if r == utf8.RuneError || unicode.IsSpace(r) || !unicode.IsPrint(r) {
|
||||
return true
|
||||
}
|
||||
i += size
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var needsQuotingSet = [utf8.RuneSelf]bool{
|
||||
'"': true,
|
||||
'=': true,
|
||||
}
|
||||
|
||||
func init() {
|
||||
for i := 0; i < utf8.RuneSelf; i++ {
|
||||
r := rune(i)
|
||||
if unicode.IsSpace(r) || !unicode.IsPrint(r) {
|
||||
needsQuotingSet[i] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func writeSpace(w io.Writer, first bool) {
|
||||
if !first {
|
||||
w.Write([]byte{' '}) //nolint: errcheck
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) textFormatter(keyvals ...interface{}) {
|
||||
st := l.styles
|
||||
lenKeyvals := len(keyvals)
|
||||
|
||||
for i := 0; i < lenKeyvals; i += 2 {
|
||||
firstKey := i == 0
|
||||
moreKeys := i < lenKeyvals-2
|
||||
|
||||
switch keyvals[i] {
|
||||
case TimestampKey:
|
||||
if t, ok := keyvals[i+1].(time.Time); ok {
|
||||
ts := t.Format(l.timeFormat)
|
||||
ts = st.Timestamp.Renderer(l.re).Render(ts)
|
||||
writeSpace(&l.b, firstKey)
|
||||
l.b.WriteString(ts)
|
||||
}
|
||||
case LevelKey:
|
||||
if level, ok := keyvals[i+1].(Level); ok {
|
||||
var lvl string
|
||||
lvlStyle, ok := st.Levels[level]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
lvl = lvlStyle.Renderer(l.re).String()
|
||||
if lvl != "" {
|
||||
writeSpace(&l.b, firstKey)
|
||||
l.b.WriteString(lvl)
|
||||
}
|
||||
}
|
||||
case CallerKey:
|
||||
if caller, ok := keyvals[i+1].(string); ok {
|
||||
caller = fmt.Sprintf("<%s>", caller)
|
||||
caller = st.Caller.Renderer(l.re).Render(caller)
|
||||
writeSpace(&l.b, firstKey)
|
||||
l.b.WriteString(caller)
|
||||
}
|
||||
case PrefixKey:
|
||||
if prefix, ok := keyvals[i+1].(string); ok {
|
||||
prefix = st.Prefix.Renderer(l.re).Render(prefix + ":")
|
||||
writeSpace(&l.b, firstKey)
|
||||
l.b.WriteString(prefix)
|
||||
}
|
||||
case MessageKey:
|
||||
if msg := keyvals[i+1]; msg != nil {
|
||||
m := fmt.Sprint(msg)
|
||||
m = st.Message.Renderer(l.re).Render(m)
|
||||
writeSpace(&l.b, firstKey)
|
||||
l.b.WriteString(m)
|
||||
}
|
||||
default:
|
||||
sep := separator
|
||||
indentSep := indentSeparator
|
||||
sep = st.Separator.Renderer(l.re).Render(sep)
|
||||
indentSep = st.Separator.Renderer(l.re).Render(indentSep)
|
||||
key := fmt.Sprint(keyvals[i])
|
||||
val := fmt.Sprintf("%+v", keyvals[i+1])
|
||||
raw := val == ""
|
||||
if raw {
|
||||
val = `""`
|
||||
}
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
actualKey := key
|
||||
valueStyle := st.Value
|
||||
if vs, ok := st.Values[actualKey]; ok {
|
||||
valueStyle = vs
|
||||
}
|
||||
if keyStyle, ok := st.Keys[key]; ok {
|
||||
key = keyStyle.Renderer(l.re).Render(key)
|
||||
} else {
|
||||
key = st.Key.Renderer(l.re).Render(key)
|
||||
}
|
||||
|
||||
// Values may contain multiple lines, and that format
|
||||
// is preserved, with each line prefixed with a " | "
|
||||
// to show it's part of a collection of lines.
|
||||
//
|
||||
// Values may also need quoting, if not all the runes
|
||||
// in the value string are "normal", like if they
|
||||
// contain ANSI escape sequences.
|
||||
if strings.Contains(val, "\n") {
|
||||
l.b.WriteString("\n ")
|
||||
l.b.WriteString(key)
|
||||
l.b.WriteString(sep + "\n")
|
||||
l.writeIndent(&l.b, val, indentSep, moreKeys, actualKey)
|
||||
} else if !raw && needsQuoting(val) {
|
||||
writeSpace(&l.b, firstKey)
|
||||
l.b.WriteString(key)
|
||||
l.b.WriteString(sep)
|
||||
l.b.WriteString(valueStyle.Renderer(l.re).Render(fmt.Sprintf(`"%s"`,
|
||||
escapeStringForOutput(val, true))))
|
||||
} else {
|
||||
val = valueStyle.Renderer(l.re).Render(val)
|
||||
writeSpace(&l.b, firstKey)
|
||||
l.b.WriteString(key)
|
||||
l.b.WriteString(sep)
|
||||
l.b.WriteString(val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add a newline to the end of the log message.
|
||||
l.b.WriteByte('\n')
|
||||
}
|
Reference in New Issue
Block a user