File renovate-pretty-log-0.1.2.obscpio of Package renovate-pretty-log
07070100000000000081A400000000000000000000000168309D8F00000225000000000000000000000000000000000000002200000000renovate-pretty-log-0.1.2/LICENSECopyright 2025 Jamie Tanna
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
07070100000001000081A400000000000000000000000168309D8F0000082A000000000000000000000000000000000000002400000000renovate-pretty-log-0.1.2/README.md# `renovate-pretty-log`
Two utilities for exploring [Renovate](https://docs.renovatebot.com/) debug log files.
## `cmd/renovate-pretty-log-tui`
The `renovate-pretty-log-tui` command provides a Terminal User Interface (TUI) for interacting with a Renovate debug log export.
This provides a more rich means to explore the log lines, allowing scrolling down the list of log lines, and can expand a given log line, if there are any extra pieces of context (denoted by a `+`):
[](https://asciinema.org/a/Taz0vUWhmszaDtbWEuiJgICRi)
Usage:
```sh
# to read the log file, and by default only see INFO level logs (and above)
renovate-pretty-log-tui -path /path/to/debug.log
# to read the log file, and see INFO level logs (and above)
renovate-pretty-log-tui -path /path/to/debug.log -level info
# to read the log file, and see DEBUG level logs (and above)
renovate-pretty-log-tui -path /path/to/debug.log -level debug
```
## `cmd/renovate-pretty-log`
The `renovate-pretty-log` provides a Terminal User Interface (TUI) for interacting with a Renovate debug log export.
This provides a more rich means to explore the log lines, allowing scrolling down the list of log lines, and can expand a given log line, if there are any extra pieces of context (denoted by a `+`):
[](https://asciinema.org/a/lc0dXTIyk7g8PKPrMRRAkX0ZD)
Usage:
```sh
# to read the log file, and by default only see INFO level logs (and above)
renovate-pretty-log -path /path/to/debug.log
# to read the log file, and see INFO level logs (and above)
renovate-pretty-log -path /path/to/debug.log -level info
# to read the log file, and see DEBUG level logs (and above)
renovate-pretty-log -path /path/to/debug.log -level debug
```
## License
This project is licensed under the Apache-2.0 license.
However, note that this includes output from the following Large Language Models (LLMs), via GitHub Copilot:
- claude:3.7-sonnet
- claude:3.7-sonnet-thinking
- gpt:4o
- gpt:4.1
07070100000002000041ED00000000000000000000000268309D8F00000000000000000000000000000000000000000000001E00000000renovate-pretty-log-0.1.2/cmd07070100000003000041ED00000000000000000000000268309D8F00000000000000000000000000000000000000000000003200000000renovate-pretty-log-0.1.2/cmd/renovate-pretty-log07070100000004000041ED00000000000000000000000268309D8F00000000000000000000000000000000000000000000003600000000renovate-pretty-log-0.1.2/cmd/renovate-pretty-log-tui07070100000005000081A400000000000000000000000168309D8F0000295F000000000000000000000000000000000000003E00000000renovate-pretty-log-0.1.2/cmd/renovate-pretty-log-tui/main.gopackage main
import (
"bufio"
"encoding/json"
"flag"
"fmt"
"os"
"strings"
tea "github.com/charmbracelet/bubbletea"
charmlog "github.com/charmbracelet/log"
"gitlab.com/tanna.dev/renovate-pretty-log/internal/log"
"gitlab.com/tanna.dev/renovate-pretty-log/internal/style"
)
// parentViewportHeight indicates how many lines of logs will be shown on a given "page" of logs
const parentViewportHeight = 30
// extrasViewportHeight indicates how many lines of "extra" metadata will be shown under a given log, if expanded
const extrasViewportHeight = 20
type model struct {
// logs contains the log lines themselves. They must be pre-filtered
logs []log.Line
cursor int
// parentViewportStart is the index of the first visible log
parentViewportStart int
// expanded tracks which indexes are currently expanded
expanded map[int]bool
// extrasExpandedViewport allows expanding a given log.Line's Extras, to show any additional context the log line has
extrasExpandedViewport extrasExpandedViewport
}
func newModel(lines []log.Line) model {
return model{
logs: lines,
cursor: 0,
parentViewportStart: 0,
expanded: make(map[int]bool),
}
}
func (m model) Init() tea.Cmd {
return nil
}
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.String() {
case
"up",
"k":
m.MoveUp()
case
"down",
"j":
m.MoveDown()
case
"ctrl+u",
"pgup":
m.cursor -= parentViewportHeight
if m.cursor < 0 {
m.cursor = 0
}
m.parentViewportStart -= parentViewportHeight
if m.parentViewportStart < 0 {
m.parentViewportStart = 0
}
case
"ctrl+d",
"pgdown":
m.cursor += parentViewportHeight
if m.cursor >= len(m.logs)-1 {
m.cursor = len(m.logs) - 1
}
m.parentViewportStart += parentViewportHeight
if m.parentViewportStart >= len(m.logs)-1 {
m.parentViewportStart = len(m.logs) - 1
}
case "enter":
m.expanded[m.cursor] = !m.expanded[m.cursor]
log := m.logs[m.cursor]
extras := strings.Split(style.RenderLineExtras(log), "\n")
m.extrasExpandedViewport.cursor = 0
m.extrasExpandedViewport.length = len(extras)
case "q", "esc", "ctrl+c":
return m, tea.Quit
}
}
return m, nil
}
func (m model) View() string {
var b strings.Builder
end := min(m.parentViewportStart+parentViewportHeight, len(m.logs))
for i := m.parentViewportStart; i < end; i++ {
line := m.logs[i]
cursor := " "
if i == m.cursor {
cursor = ">"
}
hasExtras := len(line.Extras) > 0
hasExtrasIndicator := " "
if hasExtras {
hasExtrasIndicator = style.LogLineHasExtrasStyle.Render("+")
if m.expanded[i] {
hasExtrasIndicator = style.LogLineHasExtrasStyle.Render("-")
}
}
renderedLine := style.RenderLine(line, false)
fmt.Fprintf(&b, "%s %s %s", cursor, hasExtrasIndicator, renderedLine)
if m.expanded[i] && hasExtras {
extras := strings.Split(style.RenderLineExtras(line), "\n")
tooLongPrefix := ""
tooLongSuffix := ""
start := 0
end := extrasViewportHeight
if end > len(extras) {
end = len(extras)
}
if len(extras) > extrasViewportHeight {
tooLongSuffix = "\n" + style.LogLineExtraIndent + "..."
}
if i == m.cursor {
if m.extrasExpandedViewport.AtTop() {
tooLongPrefix = ""
} else {
tooLongPrefix = fmt.Sprintf("%s... %d/%d\n", style.LogLineExtraIndent, m.extrasExpandedViewport.cursor, m.extrasExpandedViewport.length)
}
if m.extrasExpandedViewport.AtBottom() {
tooLongSuffix = ""
} else {
tooLongSuffix = fmt.Sprintf("\n%s... %d/%d", style.LogLineExtraIndent, m.extrasExpandedViewport.cursor+extrasViewportHeight, m.extrasExpandedViewport.length)
}
start = m.extrasExpandedViewport.cursor
end = m.extrasExpandedViewport.cursor + extrasViewportHeight
if end > len(extras) {
end = len(extras)
}
}
fmt.Fprintf(&b, "\n%s%s%s",
tooLongPrefix,
style.LogLineExtraValueStyle.Render(strings.Join(extras[start:end], "\n")),
tooLongSuffix)
}
fmt.Fprintln(&b)
}
if len(m.logs) > end {
fmt.Fprintf(&b, "\nViewing %d/%d logs\n", m.cursor, len(m.logs)-end)
}
// TODO: migrate this to a help widget
currentLog := m.logs[m.cursor]
if len(currentLog.Extras) > 0 {
if m.expanded[m.cursor] {
fmt.Fprintf(&b, "\n[Use arrow keys to navigate, %s, q to quit]", style.LogLineHasExtrasStyle.Render("Enter to collapse"))
} else {
fmt.Fprintf(&b, "\n[Use arrow keys to navigate, %s, q to quit]", style.LogLineHasExtrasStyle.Render("Enter to expand"))
}
} else {
fmt.Fprintf(&b, "\n[Use arrow keys to navigate, %s, q to quit]", style.LogLineTimestampStyle.Render("Enter to expand"))
}
return b.String()
}
func (m *model) MoveUp() {
// if we're currently at an expanded log line ...
if m.expanded[m.cursor] {
// and we're already at the top
if m.extrasExpandedViewport.AtTop() {
// then scroll up as normal, out of the expanded log line
m.cursor--
if m.cursor < 0 {
m.cursor = 0
}
// if we've gone out of view (as we're now before the previous start point) scroll us up
if m.cursor < m.parentViewportStart {
m.parentViewportStart--
}
if m.parentViewportStart < 0 {
m.parentViewportStart = 0
}
// we may now be on a log line that is expanded, so we need to makes sure our internal state is reset
m.updateExtrasExpandedViewportStateAfterMoving()
} else {
// otherwise, we want to scroll our expanded log line up a line
m.extrasExpandedViewport.MoveUp()
}
} else if m.cursor > 0 {
// if we're on a regular line, then move us up
m.cursor--
if m.cursor < 0 {
m.cursor = 0
}
// if we've gone out of view (as we're now before the previous start point) scroll us up
if m.cursor < m.parentViewportStart {
m.parentViewportStart--
}
if m.parentViewportStart < 0 {
m.parentViewportStart = 0
}
previous := m.cursor - 1
if previous < 0 {
previous = 0
}
// we may now be on a log line that is expanded, so we need to makes sure our internal state is reset
m.updateExtrasExpandedViewportStateAfterMoving()
}
}
func (m *model) MoveDown() {
// if we're currently at an expanded log line ...
if m.expanded[m.cursor] {
// and we're already at the bottom
if m.extrasExpandedViewport.AtBottom() {
// then scroll down as normal, out of the expanded log line
m.cursor++
if m.cursor > len(m.logs)-1 {
m.cursor = len(m.logs) - 1
}
// if we've gone out of view (as we're now before the previous start point) scroll us down
if m.cursor > m.parentViewportStart {
m.parentViewportStart++
}
if m.parentViewportStart > len(m.logs)-1 {
m.parentViewportStart = len(m.logs) - 1
}
// we may now be on a log line that is expanded, so we need to makes sure our internal state is reset
m.updateExtrasExpandedViewportStateAfterMoving()
} else {
// otherwise, we want to scroll our expanded log line down a line
m.extrasExpandedViewport.MoveDown()
}
} else if m.cursor < len(m.logs)-1 {
// if we're on a regular line, then move us down
m.cursor++
if m.cursor > len(m.logs)-1 {
m.cursor = len(m.logs) - 1
}
// if we've gone out of view (as we're now before the previous start point) scroll us down
if m.cursor > m.parentViewportStart {
m.parentViewportStart++
}
if m.parentViewportStart > len(m.logs)-1 {
m.parentViewportStart = len(m.logs) - 1
}
// we may now be on a log line that is expanded, so we need to makes sure our internal state is reset
m.updateExtrasExpandedViewportStateAfterMoving()
}
}
func (m *model) updateExtrasExpandedViewportStateAfterMoving() {
log := m.logs[m.cursor]
extras := strings.Split(style.RenderLineExtras(log), "\n")
m.extrasExpandedViewport.cursor = 0
m.extrasExpandedViewport.length = len(extras)
}
// extrasExpandedViewport allows expanding a given log line's "extra" log metadata, in a scrollable fashion.
type extrasExpandedViewport struct {
cursor int
length int
}
func (e extrasExpandedViewport) AtTop() bool {
return e.cursor == 0
}
func (e extrasExpandedViewport) AtBottom() bool {
if e.cursor == (e.length - 1) {
return true
}
isShowingLastPage := (e.length)-extrasViewportHeight <= e.cursor
return isShowingLastPage
}
func (e *extrasExpandedViewport) MoveUp() {
e.cursor--
if e.cursor <= 0 {
e.cursor = 0
}
}
func (e *extrasExpandedViewport) MoveDown() {
e.cursor++
if e.cursor > e.length {
e.cursor = e.length
}
}
func parseLevelFlag(level *string) (log.Level, error) {
if level == nil {
return log.LevelInfo, nil
}
val := strings.ToLower(*level)
switch val {
case "trace":
return log.LevelTrace, nil
case "debug":
return log.LevelDebug, nil
case "info":
return log.LevelInfo, nil
case "warn":
return log.LevelWarn, nil
case "error":
return log.LevelError, nil
case "fatal":
return log.LevelFatal, nil
}
return -1, fmt.Errorf("unknown log level: %v", *level)
}
func main() {
cliLogger := charmlog.New(os.Stderr)
levelFlag := flag.String("level", "info", "Set the log level (trace, debug, info, warn, error, fatal)")
pathFlag := flag.String("path", "", "The path to the debug log file (`RENOVATE_DEBUG_LOG_FILE`)")
flag.Parse()
if pathFlag == nil {
cliLogger.Fatal("No `-path` flag was provided")
os.Exit(1)
}
if *pathFlag == "" {
cliLogger.Fatal("No `-path` flag was provided")
os.Exit(1)
}
f, err := os.Open(*pathFlag)
if err != nil {
cliLogger.Fatal(fmt.Sprintf("Error opening file: %v", err), "err", err)
os.Exit(1)
}
defer f.Close()
allowedLogLevel, err := parseLevelFlag(levelFlag)
if err != nil {
cliLogger.Fatal(fmt.Sprintf("The `-level` flag was invalid: %v", err), "err", err)
os.Exit(1)
}
var lines []log.Line
// TODO via https://gitlab.com/jamietanna/dotfiles-arch/-/blob/67d79966/go/home/go/src/jvt.me/dotfiles/jjj/main.go#L24-26 we may want to allow handling /very/ long log lines
scanner := bufio.NewScanner(f)
for scanner.Scan() {
var line log.Line
data := scanner.Bytes()
err := json.Unmarshal(data, &line)
if err != nil {
cliLogger.Fatal(fmt.Sprintf("Failed to parse a log line as JSON: %v", err), "err", err)
continue
}
if line.Level >= allowedLogLevel {
lines = append(lines, line)
}
}
if len(lines) == 0 {
fmt.Printf("The file you provided did not contain any %s level logs\n", allowedLogLevel.String())
os.Exit(0)
}
p := tea.NewProgram(newModel(lines))
if _, err := p.Run(); err != nil {
fmt.Fprintf(os.Stderr, "Error starting app: %v\n", err)
os.Exit(1)
}
}
07070100000006000081A400000000000000000000000168309D8F000007A2000000000000000000000000000000000000003A00000000renovate-pretty-log-0.1.2/cmd/renovate-pretty-log/main.gopackage main
import (
"bufio"
"encoding/json"
"flag"
"fmt"
"os"
"strings"
charmlog "github.com/charmbracelet/log"
log "gitlab.com/tanna.dev/renovate-pretty-log/internal/log"
"gitlab.com/tanna.dev/renovate-pretty-log/internal/style"
)
func parseLevelFlag(level *string) (log.Level, error) {
if level == nil {
return log.LevelInfo, nil
}
val := strings.ToLower(*level)
switch val {
case "trace":
return log.LevelTrace, nil
case "debug":
return log.LevelDebug, nil
case "info":
return log.LevelInfo, nil
case "warn":
return log.LevelWarn, nil
case "error":
return log.LevelError, nil
case "fatal":
return log.LevelFatal, nil
}
return -1, fmt.Errorf("unknown log level: %v", *level)
}
func main() {
cliLogger := charmlog.New(os.Stderr)
levelFlag := flag.String("level", "info", "Set the log level (trace, debug, info, warn, error, fatal)")
pathFlag := flag.String("path", "", "The path to the debug log file (`RENOVATE_DEBUG_LOG_FILE`)")
flag.Parse()
if pathFlag == nil {
cliLogger.Fatal("No `-path` flag was provided")
os.Exit(1)
}
f, err := os.Open(*pathFlag)
if err != nil {
cliLogger.Fatal(fmt.Sprintf("Error opening file: %v", err), "err", err)
os.Exit(1)
}
allowedLogLevel, err := parseLevelFlag(levelFlag)
if err != nil {
cliLogger.Fatal(fmt.Sprintf("The `-level` flag was invalid: %v", err), "err", err)
os.Exit(1)
}
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
var line log.Line
data := scanner.Bytes()
err := json.Unmarshal(data, &line)
if err != nil {
cliLogger.Fatal(fmt.Sprintf("Failed to parse a log line as JSON: %v", err), "err", err)
continue
}
if line.Level < allowedLogLevel {
cliLogger.Debug("Skipping log line from a log level we've not been requested to show", "level", line.Level.String(), "msg", line.Message)
continue
}
// TODO check level is enabled flag
fmt.Println(style.RenderLine(line, true))
}
}
07070100000007000081A400000000000000000000000168309D8F00000538000000000000000000000000000000000000002100000000renovate-pretty-log-0.1.2/go.modmodule gitlab.com/tanna.dev/renovate-pretty-log
go 1.24
require (
github.com/charmbracelet/bubbletea v1.3.5
github.com/charmbracelet/lipgloss v1.1.0
github.com/charmbracelet/log v0.4.2
)
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/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // 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-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
github.com/muesli/cancelreader v0.2.2 // 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/sync v0.13.0 // indirect
golang.org/x/sys v0.32.0 // indirect
golang.org/x/text v0.3.8 // indirect
)
07070100000008000081A400000000000000000000000168309D8F000014AE000000000000000000000000000000000000002100000000renovate-pretty-log-0.1.2/go.sumgithub.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/bubbletea v1.3.5 h1:JAMNLTbqMOhSwoELIr0qyP4VidFq72/6E9j7HHmRKQc=
github.com/charmbracelet/bubbletea v1.3.5/go.mod h1:TkCnmH+aBd4LrXhXcqrKiYwRs7qyQx5rBgH5fVY3v54=
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/log v0.4.2 h1:hYt8Qj6a8yLnvR+h7MwsJv/XvmBJXiueUcI3cIxsyig=
github.com/charmbracelet/log v0.4.2/go.mod h1:qifHGX/tc7eluv2R6pWIpyHDDrrb/AG71Pf2ysQu5nw=
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/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
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-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
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/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
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/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
07070100000009000041ED00000000000000000000000268309D8F00000000000000000000000000000000000000000000002300000000renovate-pretty-log-0.1.2/internal0707010000000A000041ED00000000000000000000000268309D8F00000000000000000000000000000000000000000000002700000000renovate-pretty-log-0.1.2/internal/log0707010000000B000081A400000000000000000000000168309D8F00000AA9000000000000000000000000000000000000002F00000000renovate-pretty-log-0.1.2/internal/log/main.gopackage log
import (
"encoding/json"
"fmt"
"time"
"github.com/charmbracelet/log"
)
type Level int
const (
LevelTrace Level = 10
LevelDebug Level = 20
LevelInfo Level = 30
LevelWarn Level = 40
LevelError Level = 50
LevelFatal Level = 60
)
func (l Level) AsLevel() log.Level {
// TODO remove
switch l {
case LevelTrace:
return log.DebugLevel // there's no TRACE
case LevelDebug:
return log.DebugLevel
case LevelInfo:
return log.InfoLevel
case LevelWarn:
return log.WarnLevel
case LevelError:
return log.ErrorLevel
case LevelFatal:
return log.FatalLevel
default:
return log.DebugLevel // default to DEBUG
}
}
func (l Level) String() string {
switch l {
case LevelTrace:
return "TRACE"
case LevelDebug:
return "DEBUG"
case LevelInfo:
return "INFO"
case LevelWarn:
return "WARN"
case LevelError:
return "ERROR"
case LevelFatal:
return "FATAL"
default:
return fmt.Sprintf("Unknown log Level (%d)", l)
}
}
type Line struct {
Name string `json:"name"`
Hostname string `json:"hostname"`
Pid int `json:"pid"`
Level Level `json:"level"`
LogContext string `json:"logContext"`
Repository string `json:"repository"`
Message string `json:"msg"`
Time time.Time `json:"time"`
Version int `json:"v"`
Extras map[string]any `json:"-"`
}
func (ll Line) Render() string {
return ""
}
func (ll *Line) UnmarshalJSON(data []byte) error {
var tempMap map[string]interface{}
if err := json.Unmarshal(data, &tempMap); err != nil {
return err
}
// Extract known fields and delete them from the map
if name, ok := tempMap["name"].(string); ok {
ll.Name = name
delete(tempMap, "name")
}
if hostname, ok := tempMap["hostname"].(string); ok {
ll.Hostname = hostname
delete(tempMap, "hostname")
}
if pid, ok := tempMap["pid"].(float64); ok { // JSON numbers are float64
ll.Pid = int(pid)
delete(tempMap, "pid")
}
if level, ok := tempMap["level"].(float64); ok {
ll.Level = Level(int(level))
delete(tempMap, "level")
}
if logContext, ok := tempMap["logContext"].(string); ok {
ll.LogContext = logContext
delete(tempMap, "logContext")
}
if repository, ok := tempMap["repository"].(string); ok {
ll.Repository = repository
delete(tempMap, "repository")
}
if msg, ok := tempMap["msg"].(string); ok {
ll.Message = msg
delete(tempMap, "msg")
}
if timeStr, ok := tempMap["time"].(string); ok {
parsedTime, err := time.Parse(time.RFC3339, timeStr)
if err != nil {
return err
}
ll.Time = parsedTime
delete(tempMap, "time")
}
if version, ok := tempMap["v"].(float64); ok {
ll.Version = int(version)
delete(tempMap, "v")
}
ll.Extras = tempMap
return nil
}
0707010000000C000041ED00000000000000000000000268309D8F00000000000000000000000000000000000000000000002900000000renovate-pretty-log-0.1.2/internal/style0707010000000D000081A400000000000000000000000168309D8F0000102A000000000000000000000000000000000000003100000000renovate-pretty-log-0.1.2/internal/style/main.gopackage style
import (
"bytes"
"encoding/json"
"fmt"
"regexp"
"slices"
"strings"
"time"
"github.com/charmbracelet/lipgloss"
"gitlab.com/tanna.dev/renovate-pretty-log/internal/log"
)
// HACK for https://github.com/charmbracelet/lipgloss/issues/199
var reHackToRemoveInadvertentWhitespaceIntroducedByLipGloss = regexp.MustCompile(`([ \t]*[\n\r]|[ \t]*$|[\n\r]$)`)
// HACK for https://github.com/charmbracelet/lipgloss/issues/199
var hackInadvertentTrailerBytes = []byte{
27, 91, 48, 109, 10, 27, 91, 51, 56, 59, 50, 59, 49, 56, 54, 59, 49, 54, 54, 59, 49, 50, 55, 109, 27, 91, 48, 109, 10,
}
var LogLineTraceLevelStyle = lipgloss.NewStyle().
SetString("TRAC").
Bold(true).
MaxWidth(4).
Foreground(lipgloss.Color("#2C78BF"))
var LogLineDebugLevelStyle = lipgloss.NewStyle().
SetString("DEBU").
Bold(true).
MaxWidth(4).
Foreground(lipgloss.Color("#2C78BF"))
var LogLineInfoLevelStyle = lipgloss.NewStyle().
SetString("INFO").
Bold(true).
MaxWidth(4).
Foreground(lipgloss.Color("#519F50"))
var LogLineWarnLevelStyle = lipgloss.NewStyle().
SetString("WARN").
Bold(true).
MaxWidth(4).
Foreground(lipgloss.Color("#FBB829"))
var LogLineErrorLevelStyle = lipgloss.NewStyle().
SetString("ERRO").
Bold(true).
MaxWidth(4).
Foreground(lipgloss.Color("#EF2F27"))
var LogLineFatalLevelStyle = lipgloss.NewStyle().
SetString("FATA").
Bold(true).
MaxWidth(4).
Foreground(lipgloss.Color("#F75341"))
var LogLineHasExtrasStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("#ff5c8f"))
var LogLineExtraKeyStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("#FCE8C3"))
var LogLineExtraValueStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("#BAA67F"))
var LogLineTimestampStyle = lipgloss.NewStyle().
Bold(true).
MaxWidth(19).
Foreground(lipgloss.Color("240"))
var LogLineMessageStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("#FFFFFF"))
var LogLineExtraIndent = " "
func LevelStyle(level log.Level) lipgloss.Style {
switch level {
case log.LevelTrace:
return LogLineTraceLevelStyle
case log.LevelDebug:
return LogLineDebugLevelStyle
case log.LevelInfo:
return LogLineInfoLevelStyle
case log.LevelWarn:
return LogLineWarnLevelStyle
case log.LevelError:
return LogLineErrorLevelStyle
case log.LevelFatal:
return LogLineFatalLevelStyle
default:
panic("TODO")
}
}
func TimestampStyle(timestamp *time.Time) lipgloss.Style {
return LogLineTimestampStyle.
SetString(timestamp.Format(time.RFC3339))
}
func MessageStyle(message string) lipgloss.Style {
return LogLineMessageStyle.
SetString(message)
}
func RenderLine(line log.Line, withExtras bool) string {
var parts []string
out := LevelStyle(line.Level).Render()
parts = append(parts, out)
out = TimestampStyle(&line.Time).Render()
parts = append(parts, out)
out = MessageStyle(line.Message).Render()
parts = append(parts, out)
if withExtras {
extras := RenderLineExtras(line)
if extras != "" {
parts = append(parts, "\n"+extras)
}
}
return strings.Join(parts, " ")
}
func RenderLineExtras(line log.Line) string {
keys := make([]string, 0, len(line.Extras))
for k := range line.Extras {
keys = append(keys, k)
}
slices.Sort(keys)
var buf bytes.Buffer
jsonEncoder := json.NewEncoder(&buf)
jsonEncoder.SetIndent(LogLineExtraIndent, " ")
/*
"go/google.golang.org/protobuf": {
"\u003c 1.33.0": "1.33.0"
}
*/
jsonEncoder.SetEscapeHTML(false)
var extras []string
for _, k := range keys {
v := line.Extras[k]
out := ""
switch v := v.(type) {
case bool,
float32,
float64,
string:
out = fmt.Sprintf("%#v", v)
default:
buf.Reset()
err := jsonEncoder.Encode(v)
if err == nil {
out = buf.String()
} else {
out = fmt.Sprintf("%#v", v)
}
}
out = LogLineExtraValueStyle.Render(out)
out = reHackToRemoveInadvertentWhitespaceIntroducedByLipGloss.ReplaceAllString(out, "\n")
out = strings.TrimSuffix(out, string(hackInadvertentTrailerBytes))
extras = append(extras, LogLineExtraIndent+LogLineExtraKeyStyle.Render(k)+": "+out)
}
if len(extras) > 0 {
return strings.Join(extras, "\n")
}
return ""
}
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!61 blocks