pax_global_header 0000666 0000000 0000000 00000000064 15123607207 0014515 g ustar 00root root 0000000 0000000 52 comment=83088c5311ada2e8c2e09695eb3f332b91e415d0 golang-github-charmbracelet-log-0.4.2/ 0000775 0000000 0000000 00000000000 15123607207 0017620 5 ustar 00root root 0000000 0000000 golang-github-charmbracelet-log-0.4.2/.github/ 0000775 0000000 0000000 00000000000 15123607207 0021160 5 ustar 00root root 0000000 0000000 golang-github-charmbracelet-log-0.4.2/.github/CODEOWNERS 0000664 0000000 0000000 00000000021 15123607207 0022544 0 ustar 00root root 0000000 0000000 * @aymanbagabas golang-github-charmbracelet-log-0.4.2/.github/dependabot.yml 0000664 0000000 0000000 00000001471 15123607207 0024013 0 ustar 00root root 0000000 0000000 version: 2 updates: - package-ecosystem: "gomod" directory: "/" schedule: interval: "weekly" day: "monday" time: "05:00" timezone: "America/New_York" labels: - "dependencies" commit-message: prefix: "chore" include: "scope" - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" day: "monday" time: "05:00" timezone: "America/New_York" labels: - "dependencies" commit-message: prefix: "chore" include: "scope" - package-ecosystem: "docker" directory: "/" schedule: interval: "weekly" day: "monday" time: "05:00" timezone: "America/New_York" labels: - "dependencies" commit-message: prefix: "chore" include: "scope" golang-github-charmbracelet-log-0.4.2/.github/workflows/ 0000775 0000000 0000000 00000000000 15123607207 0023215 5 ustar 00root root 0000000 0000000 golang-github-charmbracelet-log-0.4.2/.github/workflows/build.yml 0000664 0000000 0000000 00000000707 15123607207 0025043 0 ustar 00root root 0000000 0000000 name: build on: [push, pull_request] jobs: build: strategy: matrix: go_version: ["1.19", stable] uses: charmbracelet/meta/.github/workflows/build.yml@main with: go_version: ${{ matrix.go_version }} snapshot: uses: charmbracelet/meta/.github/workflows/snapshot.yml@main secrets: goreleaser_key: ${{ secrets.GORELEASER_KEY }} coverage: uses: charmbracelet/meta/.github/workflows/coverage.yml@main golang-github-charmbracelet-log-0.4.2/.github/workflows/dependabot-sync.yml 0000664 0000000 0000000 00000000643 15123607207 0027022 0 ustar 00root root 0000000 0000000 name: dependabot-sync on: schedule: - cron: "0 0 * * 0" # every Sunday at midnight workflow_dispatch: # allows manual triggering permissions: contents: write pull-requests: write jobs: dependabot-sync: uses: charmbracelet/meta/.github/workflows/dependabot-sync.yml@main with: repo_name: ${{ github.event.repository.name }} secrets: gh_token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} golang-github-charmbracelet-log-0.4.2/.github/workflows/goreleaser.yml 0000664 0000000 0000000 00000001221 15123607207 0026064 0 ustar 00root root 0000000 0000000 name: goreleaser on: push: tags: - v*.*.* concurrency: group: goreleaser cancel-in-progress: true jobs: goreleaser: uses: charmbracelet/meta/.github/workflows/goreleaser.yml@main secrets: docker_username: ${{ secrets.DOCKERHUB_USERNAME }} docker_token: ${{ secrets.DOCKERHUB_TOKEN }} gh_pat: ${{ secrets.PERSONAL_ACCESS_TOKEN }} goreleaser_key: ${{ secrets.GORELEASER_KEY }} fury_token: ${{ secrets.FURY_TOKEN }} nfpm_gpg_key: ${{ secrets.NFPM_GPG_KEY }} nfpm_passphrase: ${{ secrets.NFPM_PASSPHRASE }} # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json golang-github-charmbracelet-log-0.4.2/.github/workflows/lint-sync.yml 0000664 0000000 0000000 00000000417 15123607207 0025662 0 ustar 00root root 0000000 0000000 name: lint-sync on: schedule: # every Sunday at midnight - cron: "0 0 * * 0" workflow_dispatch: # allows manual triggering permissions: contents: write pull-requests: write jobs: lint: uses: charmbracelet/meta/.github/workflows/lint-sync.yml@main golang-github-charmbracelet-log-0.4.2/.github/workflows/lint.yml 0000664 0000000 0000000 00000000163 15123607207 0024706 0 ustar 00root root 0000000 0000000 name: lint on: push: pull_request: jobs: lint: uses: charmbracelet/meta/.github/workflows/lint.yml@main golang-github-charmbracelet-log-0.4.2/.github/workflows/nightly.yml 0000664 0000000 0000000 00000000465 15123607207 0025423 0 ustar 00root root 0000000 0000000 name: nightly on: push: branches: - main jobs: nightly: uses: charmbracelet/meta/.github/workflows/nightly.yml@main secrets: docker_username: ${{ secrets.DOCKERHUB_USERNAME }} docker_token: ${{ secrets.DOCKERHUB_TOKEN }} goreleaser_key: ${{ secrets.GORELEASER_KEY }} golang-github-charmbracelet-log-0.4.2/.gitignore 0000664 0000000 0000000 00000000431 15123607207 0021606 0 ustar 00root root 0000000 0000000 *.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 golang-github-charmbracelet-log-0.4.2/.golangci.yml 0000664 0000000 0000000 00000001165 15123607207 0022207 0 ustar 00root root 0000000 0000000 version: "2" run: tests: false linters: enable: - bodyclose - exhaustive - goconst - godot - godox - gomoddirectives - goprintffuncname - gosec - misspell - nakedret - nestif - nilerr - noctx - nolintlint - prealloc - revive - rowserrcheck - sqlclosecheck - tparallel - unconvert - unparam - whitespace - wrapcheck exclusions: generated: lax presets: - common-false-positives issues: max-issues-per-linter: 0 max-same-issues: 0 formatters: enable: - gofumpt - goimports exclusions: generated: lax golang-github-charmbracelet-log-0.4.2/.goreleaser.yml 0000664 0000000 0000000 00000000117 15123607207 0022550 0 ustar 00root root 0000000 0000000 includes: - from_url: url: charmbracelet/meta/main/goreleaser-lib.yaml golang-github-charmbracelet-log-0.4.2/LICENSE 0000664 0000000 0000000 00000002070 15123607207 0020624 0 ustar 00root root 0000000 0000000 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. golang-github-charmbracelet-log-0.4.2/README.md 0000664 0000000 0000000 00000032054 15123607207 0021103 0 ustar 00root root 0000000 0000000 # Log
A minimal and colorful Go logging library. ๐ชต
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).
Charm็ญ็ฑๅผๆบ โข Charm loves open source โข ูุญูู ูุญุจ ุงูู
ุตุงุฏุฑ ุงูู
ูุชูุญุฉ
golang-github-charmbracelet-log-0.4.2/context.go 0000664 0000000 0000000 00000001155 15123607207 0021635 0 ustar 00root root 0000000 0000000 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"}
golang-github-charmbracelet-log-0.4.2/context_test.go 0000664 0000000 0000000 00000001204 15123607207 0022667 0 ustar 00root root 0000000 0000000 package log
import (
"bytes"
"context"
"io"
"testing"
"github.com/stretchr/testify/require"
)
func TestLogContext_empty(t *testing.T) {
require.Equal(t, Default(), FromContext(context.TODO()))
}
func TestLogContext_simple(t *testing.T) {
l := New(io.Discard)
ctx := WithContext(context.Background(), l)
require.Equal(t, l, FromContext(ctx))
}
func TestLogContext_fields(t *testing.T) {
var buf bytes.Buffer
l := New(&buf)
l.SetLevel(DebugLevel)
ctx := WithContext(context.Background(), l.With("foo", "bar"))
l = FromContext(ctx)
require.NotNil(t, l)
l.Debug("test")
require.Equal(t, "DEBU test foo=bar\n", buf.String())
}
golang-github-charmbracelet-log-0.4.2/examples/ 0000775 0000000 0000000 00000000000 15123607207 0021436 5 ustar 00root root 0000000 0000000 golang-github-charmbracelet-log-0.4.2/examples/README.md 0000664 0000000 0000000 00000000405 15123607207 0022714 0 ustar 00root root 0000000 0000000 # Examples
For those of you who are new to Go, check out
[pkg.go.dev](https://pkg.go.dev/github.com/charmbracelet/log) to
view our library's API. From there, you can search the repo for more detailed
usage examples for whichever functions match your use case.
golang-github-charmbracelet-log-0.4.2/examples/app/ 0000775 0000000 0000000 00000000000 15123607207 0022216 5 ustar 00root root 0000000 0000000 golang-github-charmbracelet-log-0.4.2/examples/app/app.go 0000664 0000000 0000000 00000002236 15123607207 0023330 0 ustar 00root root 0000000 0000000 package main
import (
"fmt"
"strings"
"time"
"github.com/charmbracelet/log"
)
type cup int
func (c cup) String() string {
s := fmt.Sprintf("%d cup", c)
if c > 1 {
s += "s"
}
return s
}
func startOven(degree int) {
log.Helper()
log.Debug("Starting oven", "temperature", degree)
}
func main() {
log.SetTimeFormat(time.Kitchen)
log.SetLevel(log.DebugLevel)
var (
butter = cup(1)
chocolate = cup(2)
flour = cup(3)
sugar = cup(5)
temp = 375
bakeTime = 10
)
startOven(temp)
time.Sleep(time.Second)
log.Debug("Mixing ingredients", "ingredients",
strings.Join([]string{
butter.String() + " of butter",
chocolate.String() + " of chocolate",
flour.String() + " of flour",
sugar.String() + " of sugar",
}, "\n"),
)
time.Sleep(time.Second)
if sugar > 2 {
log.Warn("That's a lot of sugar", "amount", sugar)
}
log.Info("Baking cookies", "time", fmt.Sprintf("%d minutes", bakeTime))
time.Sleep(2 * time.Second)
log.Info("Increasing temperature", "amount", 300)
temp += 300
time.Sleep(time.Second)
if temp > 500 {
log.Error("Oven is too hot", "temperature", temp)
log.Fatal("The kitchen is on fire ๐ฅ")
}
}
golang-github-charmbracelet-log-0.4.2/examples/app/app.tape 0000664 0000000 0000000 00000000135 15123607207 0023650 0 ustar 00root root 0000000 0000000 Output app.gif
Set FontSize 24
Set Width 1200
Set Height 600
Type "./app" Enter
Sleep 10s
golang-github-charmbracelet-log-0.4.2/examples/batch2/ 0000775 0000000 0000000 00000000000 15123607207 0022601 5 ustar 00root root 0000000 0000000 golang-github-charmbracelet-log-0.4.2/examples/batch2/batch2.go 0000664 0000000 0000000 00000000536 15123607207 0024277 0 ustar 00root root 0000000 0000000 package main
import (
"github.com/charmbracelet/log"
)
func main() {
logger := log.Default().With("batch", 2, "chocolateChips", true)
logger.SetPrefix("baking ๐ช ")
logger.SetReportTimestamp(false)
logger.SetReportCaller(false)
logger.SetLevel(log.DebugLevel)
logger.Debug("Preparing batch 2...")
logger.Debug("Adding chocolate chips")
}
golang-github-charmbracelet-log-0.4.2/examples/batch2/batch2.tape 0000664 0000000 0000000 00000000136 15123607207 0024617 0 ustar 00root root 0000000 0000000 Output batch2.gif
Set Height 300
Set Width 1100
Type "./batch2" Sleep 500ms Enter
Sleep 4s
golang-github-charmbracelet-log-0.4.2/examples/chocolate-chips/ 0000775 0000000 0000000 00000000000 15123607207 0024503 5 ustar 00root root 0000000 0000000 golang-github-charmbracelet-log-0.4.2/examples/chocolate-chips/chocolate-chips.go 0000664 0000000 0000000 00000000645 15123607207 0030104 0 ustar 00root root 0000000 0000000 package main
import (
"github.com/charmbracelet/log"
)
func main() {
logger := log.Default().With()
logger.SetPrefix("Baking ๐ช ")
logger.SetReportTimestamp(false)
logger.SetReportCaller(false)
logger.SetLevel(log.DebugLevel)
logger.Debug("Preparing batch 2...") // DEBUG baking ๐ช: Preparing batch 2...}
batch2 := logger.With("batch", 2, "chocolateChips", true)
batch2.Debug("Adding chocolate chips")
}
golang-github-charmbracelet-log-0.4.2/examples/cookie/ 0000775 0000000 0000000 00000000000 15123607207 0022707 5 ustar 00root root 0000000 0000000 golang-github-charmbracelet-log-0.4.2/examples/cookie/cookie.go 0000664 0000000 0000000 00000000171 15123607207 0024506 0 ustar 00root root 0000000 0000000 package main
import "github.com/charmbracelet/log"
func main() {
log.Debug("Cookie ๐ช")
log.Info("Hello World!")
}
golang-github-charmbracelet-log-0.4.2/examples/cookie/cookie.tape 0000664 0000000 0000000 00000000135 15123607207 0025032 0 ustar 00root root 0000000 0000000 Output cookie.gif
Set Height 250
Set Width 700
Type "./cookie" Sleep 500ms Enter
Sleep 3s
golang-github-charmbracelet-log-0.4.2/examples/error/ 0000775 0000000 0000000 00000000000 15123607207 0022567 5 ustar 00root root 0000000 0000000 golang-github-charmbracelet-log-0.4.2/examples/error/error.go 0000664 0000000 0000000 00000000250 15123607207 0024244 0 ustar 00root root 0000000 0000000 package main
import (
"fmt"
"github.com/charmbracelet/log"
)
func main() {
err := fmt.Errorf("too much sugar")
log.Error("failed to bake cookies", "err", err)
}
golang-github-charmbracelet-log-0.4.2/examples/error/error.tape 0000664 0000000 0000000 00000000134 15123607207 0024571 0 ustar 00root root 0000000 0000000 Output error.gif
Set Height 250
Set Width 1100
Type "./error" Sleep 500ms Enter
Sleep 3s
golang-github-charmbracelet-log-0.4.2/examples/format/ 0000775 0000000 0000000 00000000000 15123607207 0022726 5 ustar 00root root 0000000 0000000 golang-github-charmbracelet-log-0.4.2/examples/format/format.go 0000664 0000000 0000000 00000000335 15123607207 0024546 0 ustar 00root root 0000000 0000000 package main
import (
"fmt"
"time"
"github.com/charmbracelet/log"
)
func main() {
for item := 1; item <= 100; item++ {
log.Info(fmt.Sprintf("Baking %d / 100 ...", item))
time.Sleep(100 * time.Millisecond)
}
}
golang-github-charmbracelet-log-0.4.2/examples/format/format.tape 0000664 0000000 0000000 00000000135 15123607207 0025070 0 ustar 00root root 0000000 0000000 Output format.gif
Set Height 400
Set Width 800
Type "./format" Sleep 500ms Enter
Sleep 6s
golang-github-charmbracelet-log-0.4.2/examples/go.mod 0000664 0000000 0000000 00000001753 15123607207 0022552 0 ustar 00root root 0000000 0000000 module examples
go 1.17
replace github.com/charmbracelet/log => ../
require (
github.com/charmbracelet/lipgloss v1.1.0
github.com/charmbracelet/log v0.0.0-00010101000000-000000000000
)
require (
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
github.com/charmbracelet/x/ansi v0.8.0 // indirect
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
github.com/charmbracelet/x/term v0.2.1 // indirect
github.com/go-logfmt/logfmt v0.6.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/muesli/termenv v0.16.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
golang.org/x/sys v0.30.0 // indirect
)
golang-github-charmbracelet-log-0.4.2/examples/go.sum 0000664 0000000 0000000 00000022542 15123607207 0022576 0 ustar 00root root 0000000 0000000 github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA=
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE=
github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q=
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8=
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
golang-github-charmbracelet-log-0.4.2/examples/log/ 0000775 0000000 0000000 00000000000 15123607207 0022217 5 ustar 00root root 0000000 0000000 golang-github-charmbracelet-log-0.4.2/examples/log/log.go 0000664 0000000 0000000 00000000136 15123607207 0023327 0 ustar 00root root 0000000 0000000 package main
import "github.com/charmbracelet/log"
func main() {
log.Print("Baking 101")
}
golang-github-charmbracelet-log-0.4.2/examples/log/log.tape 0000664 0000000 0000000 00000000127 15123607207 0023653 0 ustar 00root root 0000000 0000000 Output log.gif
Set Height 250
Set Width 800
Type "./log" Sleep 500ms Enter
Sleep 3s
golang-github-charmbracelet-log-0.4.2/examples/new/ 0000775 0000000 0000000 00000000000 15123607207 0022227 5 ustar 00root root 0000000 0000000 golang-github-charmbracelet-log-0.4.2/examples/new/new.go 0000664 0000000 0000000 00000000226 15123607207 0023347 0 ustar 00root root 0000000 0000000 package main
import (
"os"
"github.com/charmbracelet/log"
)
func main() {
logger := log.New(os.Stderr)
logger.Warn("chewy!", "butter", true)
}
golang-github-charmbracelet-log-0.4.2/examples/new/new.tape 0000664 0000000 0000000 00000000127 15123607207 0023673 0 ustar 00root root 0000000 0000000 Output new.gif
Set Height 250
Set Width 500
Type "./new" Sleep 500ms Enter
Sleep 3s
golang-github-charmbracelet-log-0.4.2/examples/options/ 0000775 0000000 0000000 00000000000 15123607207 0023131 5 ustar 00root root 0000000 0000000 golang-github-charmbracelet-log-0.4.2/examples/options/options.go 0000664 0000000 0000000 00000000547 15123607207 0025161 0 ustar 00root root 0000000 0000000 package main
import (
"os"
"time"
"github.com/charmbracelet/log"
)
func main() {
logger := log.New(os.Stderr)
logger.SetPrefix("Baking ๐ช ")
logger.SetTimeFormat(time.Kitchen)
logger.SetReportTimestamp(true)
logger.SetReportCaller(true)
logger.Info("Starting oven!", "degree", 375)
time.Sleep(3 * time.Second)
logger.Info("Finished baking")
}
golang-github-charmbracelet-log-0.4.2/examples/options/options.tape 0000664 0000000 0000000 00000000140 15123607207 0025472 0 ustar 00root root 0000000 0000000 Output options.gif
Set Height 300
Set Width 1200
Type "./options" Sleep 500ms Enter
Sleep 6s
golang-github-charmbracelet-log-0.4.2/examples/oven/ 0000775 0000000 0000000 00000000000 15123607207 0022405 5 ustar 00root root 0000000 0000000 golang-github-charmbracelet-log-0.4.2/examples/oven/oven.go 0000664 0000000 0000000 00000000313 15123607207 0023700 0 ustar 00root root 0000000 0000000 package main
import "github.com/charmbracelet/log"
func startOven(degree int) {
log.Helper()
log.Info("Starting oven", "degree", degree)
}
func main() {
log.SetReportCaller(true)
startOven(400)
}
golang-github-charmbracelet-log-0.4.2/examples/oven/oven.tape 0000664 0000000 0000000 00000000132 15123607207 0024223 0 ustar 00root root 0000000 0000000 Output oven.gif
Set Height 250
Set Width 1200
Type "./oven" Sleep 500ms Enter
Sleep 3s
golang-github-charmbracelet-log-0.4.2/examples/slog/ 0000775 0000000 0000000 00000000000 15123607207 0022402 5 ustar 00root root 0000000 0000000 golang-github-charmbracelet-log-0.4.2/examples/slog/main.go 0000664 0000000 0000000 00000000713 15123607207 0023656 0 ustar 00root root 0000000 0000000 package main
import (
"fmt"
"log/slog"
"os"
"time"
"github.com/charmbracelet/log"
)
func main() {
// baseline
fmt.Println(time.Now().UTC().Format(time.RFC3339), "foo")
fmt.Println(time.Now().Format(time.RFC3339), "bar")
handler := log.NewWithOptions(os.Stdout, log.Options{
ReportTimestamp: true,
TimeFunction: log.NowUTC,
TimeFormat: time.RFC3339,
})
handler.Info("foobar")
logger := slog.New(handler)
logger.Info("foobar")
}
golang-github-charmbracelet-log-0.4.2/examples/styles/ 0000775 0000000 0000000 00000000000 15123607207 0022761 5 ustar 00root root 0000000 0000000 golang-github-charmbracelet-log-0.4.2/examples/styles/styles.go 0000664 0000000 0000000 00000001161 15123607207 0024632 0 ustar 00root root 0000000 0000000 package main
import (
"os"
"time"
"github.com/charmbracelet/lipgloss"
"github.com/charmbracelet/log"
)
func main() {
// Set up our custom styles
styles := log.DefaultStyles()
styles.Levels[log.ErrorLevel] = lipgloss.NewStyle().
SetString("ERROR!!").
Padding(0, 1, 0, 1).
Background(lipgloss.Color("204")).
Foreground(lipgloss.Color("0"))
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")
time.Sleep(3 * time.Second)
}
golang-github-charmbracelet-log-0.4.2/examples/styles/styles.tape 0000664 0000000 0000000 00000000135 15123607207 0025156 0 ustar 00root root 0000000 0000000 Output styles.gif
Set Height 250
Set Width 700
Type "./styles" Sleep 500ms Enter
Sleep 3s
golang-github-charmbracelet-log-0.4.2/examples/temperature/ 0000775 0000000 0000000 00000000000 15123607207 0023773 5 ustar 00root root 0000000 0000000 golang-github-charmbracelet-log-0.4.2/examples/temperature/temperature.go 0000664 0000000 0000000 00000000365 15123607207 0026663 0 ustar 00root root 0000000 0000000 package main
import (
"fmt"
"time"
"github.com/charmbracelet/log"
)
func main() {
for temp := 375; temp <= 400; temp++ {
log.Info("Increasing temperature", "degree", fmt.Sprintf("%dยฐF", temp))
time.Sleep(100 * time.Millisecond)
}
}
golang-github-charmbracelet-log-0.4.2/examples/temperature/temperature.tape 0000664 0000000 0000000 00000000150 15123607207 0027177 0 ustar 00root root 0000000 0000000 Output temperature.gif
Set Height 400
Set Width 1100
Type "./temperature" Sleep 500ms Enter
Sleep 6s
golang-github-charmbracelet-log-0.4.2/formatter.go 0000664 0000000 0000000 00000001341 15123607207 0022151 0 ustar 00root root 0000000 0000000 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"
)
golang-github-charmbracelet-log-0.4.2/go.mod 0000664 0000000 0000000 00000002030 15123607207 0020721 0 ustar 00root root 0000000 0000000 module github.com/charmbracelet/log
go 1.19
require (
github.com/charmbracelet/lipgloss v1.1.0
github.com/go-logfmt/logfmt v0.6.0
github.com/muesli/termenv v0.16.0
github.com/stretchr/testify v1.10.0
golang.org/x/exp v0.0.0-20231006140011-7918f672742d
)
require (
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
github.com/charmbracelet/x/ansi v0.8.0 // indirect
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
github.com/charmbracelet/x/term v0.2.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
golang.org/x/sys v0.30.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
golang-github-charmbracelet-log-0.4.2/go.sum 0000664 0000000 0000000 00000007506 15123607207 0020763 0 ustar 00root root 0000000 0000000 github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE=
github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q=
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8=
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
golang-github-charmbracelet-log-0.4.2/json.go 0000664 0000000 0000000 00000005673 15123607207 0021133 0 ustar 00root root 0000000 0000000 package log
import (
"bytes"
"encoding/json"
"fmt"
"time"
)
func (l *Logger) jsonFormatter(keyvals ...interface{}) {
jw := &jsonWriter{w: &l.b}
jw.start()
i := 0
for i < len(keyvals) {
switch kv := keyvals[i].(type) {
case slogAttr:
l.jsonFormatterRoot(jw, kv.Key, kv.Value)
i++
default:
if i+1 < len(keyvals) {
l.jsonFormatterRoot(jw, keyvals[i], keyvals[i+1])
}
i += 2
}
}
jw.end()
l.b.WriteRune('\n')
}
func (l *Logger) jsonFormatterRoot(jw *jsonWriter, key, value any) {
switch key {
case TimestampKey:
if t, ok := value.(time.Time); ok {
jw.objectItem(TimestampKey, t.Format(l.timeFormat))
}
case LevelKey:
if level, ok := value.(Level); ok {
jw.objectItem(LevelKey, level.String())
}
case CallerKey:
if caller, ok := value.(string); ok {
jw.objectItem(CallerKey, caller)
}
case PrefixKey:
if prefix, ok := value.(string); ok {
jw.objectItem(PrefixKey, prefix)
}
case MessageKey:
if msg := value; msg != nil {
jw.objectItem(MessageKey, fmt.Sprint(msg))
}
default:
l.jsonFormatterItem(jw, key, value)
}
}
func (l *Logger) jsonFormatterItem(jw *jsonWriter, key, value any) {
switch k := key.(type) {
case fmt.Stringer:
jw.objectKey(k.String())
case error:
jw.objectKey(k.Error())
default:
jw.objectKey(fmt.Sprint(k))
}
switch v := value.(type) {
case error:
jw.objectValue(v.Error())
case slogLogValuer:
l.writeSlogValue(jw, v.LogValue())
case slogValue:
l.writeSlogValue(jw, v.Resolve())
case fmt.Stringer:
jw.objectValue(v.String())
default:
jw.objectValue(v)
}
}
func (l *Logger) writeSlogValue(jw *jsonWriter, v slogValue) {
switch v.Kind() { //nolint:exhaustive
case slogKindGroup:
jw.start()
for _, attr := range v.Group() {
l.jsonFormatterItem(jw, attr.Key, attr.Value)
}
jw.end()
default:
a := v.Any()
_, jm := a.(json.Marshaler)
if err, ok := a.(error); ok && !jm {
jw.objectValue(err.Error())
} else {
jw.objectValue(a)
}
}
}
type jsonWriter struct {
w *bytes.Buffer
d int
}
func (w *jsonWriter) start() {
w.w.WriteRune('{')
w.d = 0
}
func (w *jsonWriter) end() {
w.w.WriteRune('}')
}
func (w *jsonWriter) objectItem(key string, value any) {
w.objectKey(key)
w.objectValue(value)
}
func (w *jsonWriter) objectKey(key string) {
if w.d > 0 {
w.w.WriteRune(',')
}
w.d++
pos := w.w.Len()
err := w.writeEncoded(key)
if err != nil {
w.w.Truncate(pos)
w.w.WriteString(`"invalid key"`)
}
w.w.WriteRune(':')
}
func (w *jsonWriter) objectValue(value any) {
pos := w.w.Len()
err := w.writeEncoded(value)
if err != nil {
w.w.Truncate(pos)
w.w.WriteString(`"invalid value"`)
}
}
func (w *jsonWriter) writeEncoded(v any) error {
e := json.NewEncoder(w.w)
e.SetEscapeHTML(false)
if err := e.Encode(v); err != nil {
return fmt.Errorf("failed to encode value: %w", err)
}
// trailing \n added by json.Encode
b := w.w.Bytes()
if len(b) > 0 && b[len(b)-1] == '\n' {
w.w.Truncate(w.w.Len() - 1)
}
return nil
}
golang-github-charmbracelet-log-0.4.2/json_test.go 0000664 0000000 0000000 00000017043 15123607207 0022164 0 ustar 00root root 0000000 0000000 package log
import (
"bytes"
"errors"
"fmt"
"path/filepath"
"runtime"
"testing"
"github.com/stretchr/testify/require"
)
func TestJson(t *testing.T) {
var buf bytes.Buffer
l := New(&buf)
l.SetFormatter(JSONFormatter)
cases := []struct {
name string
expected string
msg string
kvs []interface{}
f func(msg interface{}, kvs ...interface{})
}{
{
name: "default logger info with timestamp",
expected: "{\"level\":\"info\",\"msg\":\"info\"}\n",
msg: "info",
kvs: nil,
f: l.Info,
},
{
name: "default logger debug with timestamp",
expected: "",
msg: "info",
kvs: nil,
f: l.Debug,
},
{
name: "default logger error with timestamp",
expected: "{\"level\":\"error\",\"msg\":\"info\"}\n",
msg: "info",
kvs: nil,
f: l.Error,
},
{
name: "multiline message",
expected: "{\"level\":\"error\",\"msg\":\"info\\ninfo\"}\n",
msg: "info\ninfo",
kvs: nil,
f: l.Error,
},
{
name: "multiline kvs",
expected: "{\"level\":\"error\",\"msg\":\"info\",\"multiline\":\"info\\ninfo\"}\n",
msg: "info",
kvs: []interface{}{"multiline", "info\ninfo"},
f: l.Error,
},
{
name: "odd number of kvs",
expected: "{\"level\":\"error\",\"msg\":\"info\",\"foo\":\"bar\",\"baz\":\"missing value\"}\n",
msg: "info",
kvs: []interface{}{"foo", "bar", "baz"},
f: l.Error,
},
{
name: "error field",
expected: "{\"level\":\"error\",\"msg\":\"info\",\"error\":\"error message\"}\n",
msg: "info",
kvs: []interface{}{"error", errors.New("error message")},
f: l.Error,
},
{
name: "struct field",
expected: "{\"level\":\"info\",\"msg\":\"info\",\"struct\":{}}\n",
msg: "info",
kvs: []interface{}{"struct", struct{ foo string }{foo: "bar"}},
f: l.Info,
},
{
name: "slice field",
expected: "{\"level\":\"info\",\"msg\":\"info\",\"slice\":[1,2,3]}\n",
msg: "info",
kvs: []interface{}{"slice", []int{1, 2, 3}},
f: l.Info,
},
{
name: "slice of structs",
expected: "{\"level\":\"info\",\"msg\":\"info\",\"slice\":[{},{}]}\n",
msg: "info",
kvs: []interface{}{"slice", []struct{ foo string }{{foo: "bar"}, {foo: "baz"}}},
f: l.Info,
},
{
name: "slice of strings",
expected: "{\"level\":\"info\",\"msg\":\"info\",\"slice\":[\"foo\",\"bar\"]}\n",
msg: "info",
kvs: []interface{}{"slice", []string{"foo", "bar"}},
f: l.Info,
},
{
name: "slice of errors",
expected: "{\"level\":\"info\",\"msg\":\"info\",\"slice\":[{},{}]}\n",
msg: "info",
kvs: []interface{}{"slice", []error{errors.New("error message1"), errors.New("error message2")}},
f: l.Info,
},
{
name: "map of strings",
expected: "{\"level\":\"info\",\"msg\":\"info\",\"map\":{\"a\":\"b\",\"foo\":\"bar\"}}\n",
msg: "info",
kvs: []interface{}{"map", map[string]string{"a": "b", "foo": "bar"}},
f: l.Info,
},
{
name: "slog any value error type",
expected: "{\"level\":\"info\",\"msg\":\"info\",\"error\":\"error message\"}\n",
msg: "info",
kvs: []interface{}{"error", slogAnyValue(fmt.Errorf("error message"))},
f: l.Info,
},
}
for _, c := range cases {
buf.Reset()
t.Run(c.name, func(t *testing.T) {
c.f(c.msg, c.kvs...)
require.Equal(t, c.expected, buf.String())
})
}
}
func TestJsonCaller(t *testing.T) {
var buf bytes.Buffer
l := New(&buf)
l.SetFormatter(JSONFormatter)
l.SetReportCaller(true)
l.SetLevel(DebugLevel)
_, file, line, _ := runtime.Caller(0)
cases := []struct {
name string
expected string
msg string
kvs []interface{}
f func(msg interface{}, kvs ...interface{})
}{
{
name: "simple caller",
expected: fmt.Sprintf("{\"level\":\"info\",\"caller\":\"log/%s:%d\",\"msg\":\"info\"}\n", filepath.Base(file), line+30),
msg: "info",
kvs: nil,
f: l.Info,
},
{
name: "nested caller",
expected: fmt.Sprintf("{\"level\":\"info\",\"caller\":\"log/%s:%d\",\"msg\":\"info\"}\n", filepath.Base(file), line+30),
msg: "info",
kvs: nil,
f: func(msg interface{}, kvs ...interface{}) {
l.Helper()
l.Info(msg, kvs...)
},
},
}
for _, c := range cases {
buf.Reset()
t.Run(c.name, func(t *testing.T) {
c.f(c.msg, c.kvs...)
require.Equal(t, c.expected, buf.String())
})
}
}
func TestJsonTime(t *testing.T) {
var buf bytes.Buffer
logger := New(&buf)
logger.SetTimeFunction(_zeroTime)
logger.SetFormatter(JSONFormatter)
logger.SetReportTimestamp(true)
logger.Info("info")
require.Equal(t, "{\"time\":\"0002/01/01 00:00:00\",\"level\":\"info\",\"msg\":\"info\"}\n", buf.String())
}
func TestJsonPrefix(t *testing.T) {
var buf bytes.Buffer
logger := New(&buf)
logger.SetFormatter(JSONFormatter)
logger.SetPrefix("my-prefix")
logger.Info("info")
require.Equal(t, "{\"level\":\"info\",\"prefix\":\"my-prefix\",\"msg\":\"info\"}\n", buf.String())
}
func TestJsonCustomKey(t *testing.T) {
var buf bytes.Buffer
oldTsKey := TimestampKey
defer func() {
TimestampKey = oldTsKey
}()
TimestampKey = "other-time"
logger := New(&buf)
logger.SetTimeFunction(_zeroTime)
logger.SetFormatter(JSONFormatter)
logger.SetReportTimestamp(true)
logger.Info("info")
require.Equal(t, "{\"other-time\":\"0002/01/01 00:00:00\",\"level\":\"info\",\"msg\":\"info\"}\n", buf.String())
}
func TestJsonWriter(t *testing.T) {
testCases := []struct {
name string
fn func(w *jsonWriter)
expected string
}{
{
"string",
func(w *jsonWriter) {
w.start()
w.objectItem("a", "value")
w.end()
},
`{"a":"value"}`,
},
{
"int",
func(w *jsonWriter) {
w.start()
w.objectItem("a", 123)
w.end()
},
`{"a":123}`,
},
{
"bytes",
func(w *jsonWriter) {
w.start()
w.objectItem("b", []byte{0x0, 0x1})
w.end()
},
`{"b":"AAE="}`,
},
{
"no fields",
func(w *jsonWriter) {
w.start()
w.end()
},
`{}`,
},
{
"multiple in asc order",
func(w *jsonWriter) {
w.start()
w.objectItem("a", "value")
w.objectItem("b", "some-other")
w.end()
},
`{"a":"value","b":"some-other"}`,
},
{
"multiple in desc order",
func(w *jsonWriter) {
w.start()
w.objectItem("b", "some-other")
w.objectItem("a", "value")
w.end()
},
`{"b":"some-other","a":"value"}`,
},
{
"depth",
func(w *jsonWriter) {
w.start()
w.objectItem("a", map[string]int{"b": 123})
w.end()
},
`{"a":{"b":123}}`,
},
{
"key contains reserved",
func(w *jsonWriter) {
w.start()
w.objectItem("a:\"b", "value")
w.end()
},
`{"a:\"b":"value"}`,
},
{
"pointer",
func(w *jsonWriter) {
w.start()
w.objectItem("a", ptr("pointer"))
w.end()
},
`{"a":"pointer"}`,
},
{
"double-pointer",
func(w *jsonWriter) {
w.start()
w.objectItem("a", ptr(ptr("pointer")))
w.end()
},
`{"a":"pointer"}`,
},
{
"invalid",
func(w *jsonWriter) {
w.start()
w.objectItem("a", invalidJSON{})
w.end()
},
`{"a":"invalid value"}`,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
var buf bytes.Buffer
tc.fn(&jsonWriter{w: &buf})
require.Equal(t, tc.expected, buf.String())
})
}
}
func ptr[T any](v T) *T {
return &v
}
type invalidJSON struct{}
func (invalidJSON) MarshalJSON() ([]byte, error) {
return nil, errors.New("invalid json error")
}
golang-github-charmbracelet-log-0.4.2/level.go 0000664 0000000 0000000 00000002574 15123607207 0021266 0 ustar 00root root 0000000 0000000 package log
import (
"errors"
"fmt"
"math"
"strings"
)
// Level is a logging level.
type Level int
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.MaxInt
)
// String returns the string representation of the level.
func (l Level) String() string {
switch l { //nolint:exhaustive
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)
}
}
golang-github-charmbracelet-log-0.4.2/level_test.go 0000664 0000000 0000000 00000002425 15123607207 0022320 0 ustar 00root root 0000000 0000000 package log
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
)
func TestDefaultLevel(t *testing.T) {
var level Level
assert.Equal(t, InfoLevel, level)
}
func TestParseLevel(t *testing.T) {
testCases := []struct {
name string
input string
expected Level
error error
}{
{
name: "Parse debug",
input: "debug",
expected: DebugLevel,
error: nil,
},
{
name: "Parse info",
input: "Info",
expected: InfoLevel,
error: nil,
},
{
name: "Parse warn",
input: "WARN",
expected: WarnLevel,
error: nil,
},
{
name: "Parse error",
input: "error",
expected: ErrorLevel,
error: nil,
},
{
name: "Parse fatal",
input: "FATAL",
expected: FatalLevel,
error: nil,
},
{
name: "Default",
input: "",
expected: InfoLevel,
error: fmt.Errorf("%w: %q", ErrInvalidLevel, ""),
},
{
name: "Wrong level, set INFO",
input: "WRONG_LEVEL",
expected: InfoLevel,
error: fmt.Errorf("%w: %q", ErrInvalidLevel, "WRONG_LEVEL"),
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
lvl, err := ParseLevel(tc.input)
assert.Equal(t, tc.expected, lvl)
assert.Equal(t, tc.error, err)
})
}
}
golang-github-charmbracelet-log-0.4.2/logfmt.go 0000664 0000000 0000000 00000001335 15123607207 0021441 0 ustar 00root root 0000000 0000000 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()
}
golang-github-charmbracelet-log-0.4.2/logfmt_test.go 0000664 0000000 0000000 00000006260 15123607207 0022502 0 ustar 00root root 0000000 0000000 package log
import (
"bytes"
"errors"
"testing"
"github.com/stretchr/testify/assert"
)
func TestLogfmt(t *testing.T) {
var buf bytes.Buffer
l := New(&buf)
l.SetFormatter(LogfmtFormatter)
cases := []struct {
name string
expected string
msg string
kvs []interface{}
f func(msg interface{}, kvs ...interface{})
}{
{
name: "simple",
expected: "level=info msg=info\n",
msg: "info",
kvs: nil,
f: l.Info,
},
{
name: "ignored message",
expected: "",
msg: "info",
kvs: nil,
f: l.Debug,
},
{
name: "message with keyvals",
expected: "level=info msg=info foo=bar\n",
msg: "info",
kvs: []interface{}{"foo", "bar"},
f: l.Info,
},
{
name: "message with multiline keyvals",
expected: "level=info msg=info foo=\"bar\\nbaz\"\n",
msg: "info",
kvs: []interface{}{"foo", "bar\nbaz"},
f: l.Info,
},
{
name: "multiline message",
expected: "level=info msg=\"info\\nfoo\"\n",
msg: "info\nfoo",
kvs: nil,
f: l.Info,
},
{
name: "message with error",
expected: "level=info msg=info err=\"foo: bar\"\n",
msg: "info",
kvs: []interface{}{"err", errors.New("foo: bar")},
f: l.Info,
},
{
name: "odd number of keyvals",
expected: "level=info msg=info foo=bar baz=\"missing value\"\n",
msg: "info",
kvs: []interface{}{"foo", "bar", "baz"},
f: l.Info,
},
{
name: "struct field",
expected: "level=info msg=info foo=\"{bar:foo bar}\"\n",
msg: "info",
kvs: []interface{}{"foo", struct{ bar string }{"foo bar"}},
f: l.Info,
},
{
name: "multiple struct fields",
expected: "level=info msg=info foo={bar:baz} foo2={bar:baz}\n",
msg: "info",
kvs: []interface{}{"foo", struct{ bar string }{"baz"}, "foo2", struct{ bar string }{"baz"}},
f: l.Info,
},
{
name: "slice of structs",
expected: "level=info msg=info foo=\"[{bar:baz} {bar:baz}]\"\n",
msg: "info",
kvs: []interface{}{"foo", []struct{ bar string }{{"baz"}, {"baz"}}},
f: l.Info,
},
{
name: "slice of strings",
expected: "level=info msg=info foo=\"[bar baz]\"\n",
msg: "info",
kvs: []interface{}{"foo", []string{"bar", "baz"}},
f: l.Info,
},
{
name: "slice of errors",
expected: "level=info msg=info foo=\"[error1 error2]\"\n",
msg: "info",
kvs: []interface{}{"foo", []error{errors.New("error1"), errors.New("error2")}},
f: l.Info,
},
{
name: "map of strings",
expected: "level=info msg=info foo=map[bar:baz]\n",
msg: "info",
kvs: []interface{}{"foo", map[string]string{"bar": "baz"}},
f: l.Info,
},
{
name: "map with interface",
expected: "level=info msg=info foo=map[bar:baz]\n",
msg: "info",
kvs: []interface{}{"foo", map[string]interface{}{"bar": "baz"}},
f: l.Info,
},
}
for _, c := range cases {
buf.Reset()
t.Run(c.name, func(t *testing.T) {
c.f(c.msg, c.kvs...)
assert.Equal(t, c.expected, buf.String())
})
}
}
golang-github-charmbracelet-log-0.4.2/logger.go 0000664 0000000 0000000 00000023145 15123607207 0021433 0 ustar 00root root 0000000 0000000 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 int64
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.LoadInt64(&l.level) > int64(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...)
case TextFormatter:
fallthrough
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.StoreInt64(&l.level, int64(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(make([]interface{}, 0, len(l.fields)+len(keyvals)), l.fields...)
sl.fields = append(sl.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...))
}
golang-github-charmbracelet-log-0.4.2/logger_121.go 0000664 0000000 0000000 00000003416 15123607207 0022015 0 ustar 00root root 0000000 0000000 //go:build go1.21
// +build go1.21
package log
import (
"context"
"log/slog"
"runtime"
"sync/atomic"
)
// type aliases for slog.
type (
slogAttr = slog.Attr
slogValue = slog.Value
slogLogValuer = slog.LogValuer
)
var slogAnyValue = slog.AnyValue
const slogKindGroup = slog.KindGroup
// 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.LoadInt64(&l.level) <= int64(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)
return true
})
// Get the caller frame using the record's PC.
frames := runtime.CallersFrames([]uintptr{record.PC})
frame, _ := frames.Next()
l.handle(Level(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)
golang-github-charmbracelet-log-0.4.2/logger_121_test.go 0000664 0000000 0000000 00000013503 15123607207 0023052 0 ustar 00root root 0000000 0000000 //go:build go1.21
// +build go1.21
package log
import (
"bytes"
"context"
"log/slog"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestSlogSimple(t *testing.T) {
var buf bytes.Buffer
h := New(&buf)
h.SetLevel(DebugLevel)
l := slog.New(h)
cases := []struct {
name string
expected string
msg string
attrs []any
print func(string, ...any)
}{
{
name: "slog debug",
expected: "DEBU slog debug\n",
msg: "slog debug",
print: l.Debug,
attrs: nil,
},
{
name: "slog info",
expected: "INFO slog info\n",
msg: "slog info",
print: l.Info,
attrs: nil,
},
{
name: "slog warn",
expected: "WARN slog warn\n",
msg: "slog warn",
print: l.Warn,
attrs: nil,
},
{
name: "slog error",
expected: "ERRO slog error\n",
msg: "slog error",
print: l.Error,
attrs: nil,
},
{
name: "slog error attrs",
expected: "ERRO slog error foo=bar\n",
msg: "slog error",
print: l.Error,
attrs: []any{"foo", "bar"},
},
}
for _, c := range cases {
buf.Reset()
t.Run(c.name, func(t *testing.T) {
c.print(c.msg, c.attrs...)
assert.Equal(t, c.expected, buf.String())
})
}
}
func TestSlogWith(t *testing.T) {
var buf bytes.Buffer
h := New(&buf)
h.SetLevel(DebugLevel)
l := slog.New(h).With("a", "b")
cases := []struct {
name string
expected string
msg string
attrs []any
print func(string, ...any)
}{
{
name: "slog debug",
expected: "DEBU slog debug a=b foo=bar\n",
msg: "slog debug",
print: l.Debug,
attrs: []any{"foo", "bar"},
},
{
name: "slog info",
expected: "INFO slog info a=b foo=bar\n",
msg: "slog info",
print: l.Info,
attrs: []any{"foo", "bar"},
},
{
name: "slog warn",
expected: "WARN slog warn a=b foo=bar\n",
msg: "slog warn",
print: l.Warn,
attrs: []any{"foo", "bar"},
},
{
name: "slog error",
expected: "ERRO slog error a=b foo=bar\n",
msg: "slog error",
print: l.Error,
attrs: []any{"foo", "bar"},
},
}
for _, c := range cases {
buf.Reset()
t.Run(c.name, func(t *testing.T) {
c.print(c.msg, c.attrs...)
assert.Equal(t, c.expected, buf.String())
})
}
}
func TestSlogWithGroup(t *testing.T) {
var buf bytes.Buffer
h := New(&buf)
l := slog.New(h).WithGroup("charm").WithGroup("bracelet")
cases := []struct {
name string
expected string
msg string
}{
{
name: "simple",
msg: "message",
expected: "INFO charm.bracelet: message\n",
},
{
name: "empty",
msg: "",
expected: "INFO charm.bracelet:\n",
},
}
for _, c := range cases {
buf.Reset()
t.Run(c.name, func(t *testing.T) {
l.Info(c.msg)
assert.Equal(t, c.expected, buf.String())
})
}
}
func TestSlogCustomLevel(t *testing.T) {
var buf bytes.Buffer
cases := []struct {
name string
expected string
level slog.Level
minLevel Level
}{
{
name: "custom level not enabled",
expected: "",
level: slog.Level(500),
minLevel: Level(600),
},
{
name: "custom level enabled",
expected: "foo\n",
level: slog.Level(500),
minLevel: Level(100),
},
}
for _, c := range cases {
buf.Reset()
t.Run(c.name, func(t *testing.T) {
l := New(&buf)
l.SetLevel(c.minLevel)
l.Handle(context.Background(), slog.NewRecord(time.Now(), c.level, "foo", 0))
assert.Equal(t, c.expected, buf.String())
})
}
}
type testLogValue struct {
v slog.Value
}
func (v testLogValue) LogValue() slog.Value {
return v.v
}
func TestSlogAttr(t *testing.T) {
cases := []struct {
name string
expected string
kvs []interface{}
}{
{
name: "any",
expected: `{"level":"info","msg":"message","any":42}` + "\n",
kvs: []any{"any", slog.AnyValue(42)},
},
{
name: "bool",
expected: `{"level":"info","msg":"message","bool":false}` + "\n",
kvs: []any{"bool", slog.BoolValue(false)},
},
{
name: "duration",
expected: `{"level":"info","msg":"message","duration":10800000000000}` + "\n",
kvs: []any{"duration", slog.DurationValue(3 * time.Hour)},
},
{
name: "float64",
expected: `{"level":"info","msg":"message","float64":123}` + "\n",
kvs: []any{"float64", slog.Float64Value(123)},
},
{
name: "string",
expected: `{"level":"info","msg":"message","string":"hello"}` + "\n",
kvs: []any{"string", slog.StringValue("hello")},
},
{
name: "time",
expected: `{"level":"info","msg":"message","_time":"1970-01-01T00:00:00Z"}` + "\n",
kvs: []any{"_time", slog.TimeValue(time.Unix(0, 0).UTC())},
},
{
name: "uint64",
expected: `{"level":"info","msg":"message","uint64":42}` + "\n",
kvs: []any{"uint64", slog.Uint64Value(42)},
},
{
name: "group",
expected: `{"level":"info","msg":"message","g":{"b":true}}` + "\n",
kvs: []any{slog.Group("g", slog.Bool("b", true))},
},
{
name: "log valuer",
expected: `{"level":"info","msg":"message","lv":42}` + "\n",
kvs: []any{
"lv", testLogValue{slog.AnyValue(42)},
},
},
{
name: "log valuer",
expected: `{"level":"info","msg":"message","lv":{"first":"hello","last":"world"}}` + "\n",
kvs: []any{
"lv", testLogValue{slog.GroupValue(
slog.String("first", "hello"),
slog.String("last", "world"),
)},
},
},
}
for _, c := range cases {
c := c
t.Run(c.name, func(t *testing.T) {
t.Parallel()
// expect same output from slog and log
var buf bytes.Buffer
l := NewWithOptions(&buf, Options{Formatter: JSONFormatter})
l.Info("message", c.kvs...)
assert.Equal(t, c.expected, buf.String())
buf.Truncate(0)
sl := slog.New(l)
sl.Info("message", c.kvs...)
assert.Equal(t, c.expected, buf.String())
})
}
}
golang-github-charmbracelet-log-0.4.2/logger_no121.go 0000664 0000000 0000000 00000003346 15123607207 0022354 0 ustar 00root root 0000000 0000000 //go:build !go1.21
// +build !go1.21
package log
import (
"context"
"runtime"
"sync/atomic"
"golang.org/x/exp/slog"
)
// type alises for slog.
type (
slogAttr = slog.Attr
slogValue = slog.Value
slogLogValuer = slog.LogValuer
)
var slogAnyValue = slog.AnyValue
const slogKindGroup = slog.KindGroup
// 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.LoadInt64(&l.level) <= int64(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)
return true
})
// Get the caller frame using the record's PC.
frames := runtime.CallersFrames([]uintptr{record.PC})
frame, _ := frames.Next()
l.handle(Level(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)
golang-github-charmbracelet-log-0.4.2/logger_test.go 0000664 0000000 0000000 00000012633 15123607207 0022472 0 ustar 00root root 0000000 0000000 package log
import (
"bytes"
"fmt"
"io"
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestSubLogger(t *testing.T) {
var buf bytes.Buffer
l := New(&buf)
cases := []struct {
name string
expected string
msg string
fields []interface{}
kvs []interface{}
}{
{
name: "sub logger nil fields",
expected: "INFO info\n",
msg: "info",
fields: nil,
kvs: nil,
},
{
name: "sub logger info",
expected: "INFO info foo=bar\n",
msg: "info",
fields: []interface{}{"foo", "bar"},
kvs: nil,
},
{
name: "sub logger info with kvs",
expected: "INFO info foo=bar foobar=baz\n",
msg: "info",
fields: []interface{}{"foo", "bar"},
kvs: []interface{}{"foobar", "baz"},
},
{
name: "emoji",
expected: "INFO ๐ ๐ฑ\n",
msg: "๐ ๐ฑ",
fields: nil,
kvs: nil,
},
}
for _, c := range cases {
buf.Reset()
t.Run(c.name, func(t *testing.T) {
l.With(c.fields...).Info(c.msg, c.kvs...)
assert.Equal(t, c.expected, buf.String())
})
}
}
func TestWrongLevel(t *testing.T) {
var buf bytes.Buffer
cases := []struct {
name string
expected string
level Level
}{
{
name: "wrong level",
expected: "",
level: Level(999),
},
{
name: "wrong level negative",
expected: "INFO info\n",
level: Level(-999),
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
buf.Reset()
l := New(&buf)
l.SetLevel(c.level)
l.Info("info")
assert.Equal(t, c.expected, buf.String())
})
}
}
func TestLogFormatter(t *testing.T) {
var buf bytes.Buffer
l := New(&buf)
l.SetLevel(DebugLevel)
cases := []struct {
name string
format string
args []interface{}
fun func(string, ...interface{})
expected string
}{
{
name: "info format",
format: "%s %s",
args: []interface{}{"foo", "bar"},
fun: l.Infof,
expected: "INFO foo bar\n",
},
{
name: "debug format",
format: "%s %s",
args: []interface{}{"foo", "bar"},
fun: l.Debugf,
expected: "DEBU foo bar\n",
},
{
name: "warn format",
format: "%s %s",
args: []interface{}{"foo", "bar"},
fun: l.Warnf,
expected: "WARN foo bar\n",
},
{
name: "error format",
format: "%s %s",
args: []interface{}{"foo", "bar"},
fun: l.Errorf,
expected: "ERRO foo bar\n",
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
buf.Reset()
c.fun(c.format, "foo", "bar")
assert.Equal(t, c.expected, buf.String())
})
}
}
func TestEmptyMessage(t *testing.T) {
var buf bytes.Buffer
l := New(&buf)
cases := []struct {
name string
expected string
msg string
fields []interface{}
kvs []interface{}
}{
{
name: "empty message nil fields",
expected: "INFO\n",
msg: "",
fields: nil,
kvs: nil,
},
{
name: "empty message with fields",
expected: "INFO foo=bar\n",
msg: "",
fields: []interface{}{"foo", "bar"},
kvs: nil,
},
{
name: "empty message with fields & kvs",
expected: "INFO foo=bar foobar=baz\n",
msg: "",
fields: []interface{}{"foo", "bar"},
kvs: []interface{}{"foobar", "baz"},
},
}
for _, c := range cases {
buf.Reset()
t.Run(c.name, func(t *testing.T) {
l.With(c.fields...).Info(c.msg, c.kvs...)
assert.Equal(t, c.expected, buf.String())
})
}
}
func TestLogWithPrefix(t *testing.T) {
var buf bytes.Buffer
cases := []struct {
name string
expected string
prefix string
msg string
}{
{
name: "with prefix",
expected: "INFO prefix: info\n",
prefix: "prefix",
msg: "info",
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
buf.Reset()
l := New(&buf)
l.SetPrefix(c.prefix)
l.Info(c.msg)
assert.Equal(t, c.expected, buf.String())
})
}
}
func TestLogWithRaceCondition(t *testing.T) {
w := io.Discard
cases := []struct {
name string
}{
{
name: "must be run with -race",
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
l := New(w)
var done sync.WaitGroup
longArgs := make([]interface{}, 0, 1000)
for i := 0; i < 1000; i++ {
longArgs = append(longArgs, fmt.Sprintf("arg%d", i), fmt.Sprintf("val%d", i))
}
l = l.With(longArgs...)
for i := 0; i < 100; i++ {
done.Add(1)
go func() {
ll := l.With("arg1", "val1", "arg2", "val2")
ll.Info("kinda long long log message")
done.Done()
}()
done.Add(1)
go func() {
l.Info("kinda long log message")
done.Done()
}()
}
done.Wait()
})
}
}
func TestRace(t *testing.T) {
t.Parallel()
w := io.Discard
l := New(w)
for i := 0; i < 100; i++ {
t.Run("race", func(t *testing.T) {
t.Parallel()
s := l.StandardLog()
l.Info("foo")
l.GetLevel()
l.Print("foo")
s.Print("foo")
s.Writer().Write([]byte("bar"))
s.Output(1, "baz")
l.SetOutput(w)
l.Debug("foo")
l.SetLevel(InfoLevel)
l.GetPrefix()
o := l.With("foo", "bar")
o.Printf("foo %s", "bar")
o.SetTimeFormat(time.Kitchen)
o.Warn("foo")
o.SetOutput(w)
o.Error("foo")
o.SetFormatter(JSONFormatter)
})
}
}
func TestCustomLevel(t *testing.T) {
var buf bytes.Buffer
level500 := Level(500)
l := New(&buf)
l.SetLevel(level500)
l.Logf(level500, "foo")
assert.Equal(t, "foo\n", buf.String())
}
golang-github-charmbracelet-log-0.4.2/options.go 0000664 0000000 0000000 00000004055 15123607207 0021646 0 ustar 00root root 0000000 0000000 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
}
golang-github-charmbracelet-log-0.4.2/options_test.go 0000664 0000000 0000000 00000003470 15123607207 0022705 0 ustar 00root root 0000000 0000000 package log
import (
"bytes"
"fmt"
"io"
"testing"
"github.com/stretchr/testify/require"
)
func TestOptions(t *testing.T) {
opts := Options{
Level: ErrorLevel,
ReportCaller: true,
Fields: []interface{}{"foo", "bar"},
}
logger := NewWithOptions(io.Discard, opts)
require.Equal(t, ErrorLevel, logger.GetLevel())
require.True(t, logger.reportCaller)
require.False(t, logger.reportTimestamp)
require.Equal(t, []interface{}{"foo", "bar"}, logger.fields)
require.Equal(t, TextFormatter, logger.formatter)
require.Equal(t, DefaultTimeFormat, logger.timeFormat)
require.NotNil(t, logger.timeFunc)
}
func TestCallerFormatter(t *testing.T) {
var buf bytes.Buffer
l := NewWithOptions(&buf, Options{ReportCaller: true})
frames := l.frames(0)
frame, _ := frames.Next()
file, line, fn := frame.File, frame.Line, frame.Function
hi := func() { l.Info("hi") }
cases := []struct {
name string
expected string
format CallerFormatter
}{
{
name: "short caller formatter",
expected: fmt.Sprintf("INFO