Style pre-execute errors via logrus formatter#366
Conversation
Reviewer's GuideIntroduces a terminal-aware logrus formatter that uses lipgloss and a theme-based color scheme to render error-level logs with styled headers and wrapped text, and threads the theme color scheme into logging initialization and CLI startup while keeping non-TTY and tests behavior unchanged. Sequence diagram for terminal-aware styled error loggingsequenceDiagram
actor User
participant CliMain as Main
participant Logging as InitLogging
participant FormatterFactory as newFormatter
participant Formatter
User->>CliMain: Main
CliMain->>Logging: InitLogging(os.Stdout, os.Stderr, ColorSchemeByName(theme))
Logging->>FormatterFactory: newFormatter(errWriter, cs)
FormatterFactory->>FormatterFactory: term.IsTerminal(file.Fd())
alt is_terminal and cs_non_nil
FormatterFactory->>FormatterFactory: lipgloss.HasDarkBackground(os.Stdin, file)
FormatterFactory->>FormatterFactory: cs(lipgloss.LightDark(isDark))
FormatterFactory->>Formatter: init errorStyles
else not_terminal_or_no_scheme
FormatterFactory-->>Logging: Formatter{errorStyles:nil}
end
FormatterFactory-->>Logging: Formatter
Logging->>Logging: log.SetFormatter(formatter)
User->>Formatter: Format(errorEntry)
alt error_level_and_styling_enabled
Formatter->>Formatter: formatStyledError(entry)
else other_levels_or_no_styles
Formatter->>Formatter: standard formatting
end
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
fixes #361 |
There was a problem hiding this comment.
Hey - I've found 2 issues, and left some high level feedback:
- Consider whether
Formatshould also use the styled error path forFatalLevelandPanicLevel, not justErrorLevel, so all terminal-facing failures are rendered consistently. - In
formatStyledError, you unconditionallycapitalizeFirstand append a period, which may lead to odd output for messages that already start with an uppercase letter or end with punctuation; consider only adding punctuation when missing and avoiding extra allocation when the message is already correctly formatted.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Consider whether `Format` should also use the styled error path for `FatalLevel` and `PanicLevel`, not just `ErrorLevel`, so all terminal-facing failures are rendered consistently.
- In `formatStyledError`, you unconditionally `capitalizeFirst` and append a period, which may lead to odd output for messages that already start with an uppercase letter or end with punctuation; consider only adding punctuation when missing and avoiding extra allocation when the message is already correctly formatted.
## Individual Comments
### Comment 1
<location path="internal/logging/formatter.go" line_range="50" />
<code_context>
+ isDark := lipgloss.HasDarkBackground(os.Stdin, file)
+ scheme := cs(lipgloss.LightDark(isDark))
+
+ w, _, err := term.GetSize(file.Fd())
+ if err != nil || w == 0 {
+ w = 160
</code_context>
<issue_to_address>
**issue:** Handle very narrow terminal widths to avoid negative or zero lipgloss width.
For very small widths (e.g. `w == 1`), `Width(w - 2)` may be 0 or negative, which can cause broken or undefined lipgloss rendering. Clamp `w` to a sane minimum (e.g. `w = max(20, min(w, 160))`) or otherwise ensure the value passed to `Width` is always >= 1.
</issue_to_address>
### Comment 2
<location path="internal/logging/formatter.go" line_range="96-101" />
<code_context>
return buff.Bytes(), nil
}
+func (f *Formatter) formatStyledError(entry *log.Entry) []byte {
+ var buf bytes.Buffer
+ buf.WriteString(f.errorStyles.header.String())
+ buf.WriteString("\n")
+ buf.WriteString(f.errorStyles.text.Render(capitalizeFirst(entry.Message) + "."))
+ buf.WriteString("\n\n")
+ return buf.Bytes()
+}
</code_context>
<issue_to_address>
**issue (bug_risk):** Styled error formatting drops all structured fields attached to the entry.
The non-styled formatter includes `entry.Data` via `writeData`, but this styled error formatter omits it and only prints the message. That means any `WithFields` metadata is lost when styling is enabled. Please also render `entry.Data` (e.g., in a secondary style) so structured context is preserved.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| isDark := lipgloss.HasDarkBackground(os.Stdin, file) | ||
| scheme := cs(lipgloss.LightDark(isDark)) | ||
|
|
||
| w, _, err := term.GetSize(file.Fd()) |
There was a problem hiding this comment.
issue: Handle very narrow terminal widths to avoid negative or zero lipgloss width.
For very small widths (e.g. w == 1), Width(w - 2) may be 0 or negative, which can cause broken or undefined lipgloss rendering. Clamp w to a sane minimum (e.g. w = max(20, min(w, 160))) or otherwise ensure the value passed to Width is always >= 1.
| func (f *Formatter) formatStyledError(entry *log.Entry) []byte { | ||
| var buf bytes.Buffer | ||
| buf.WriteString(f.errorStyles.header.String()) | ||
| buf.WriteString("\n") | ||
| buf.WriteString(f.errorStyles.text.Render(capitalizeFirst(entry.Message) + ".")) | ||
| buf.WriteString("\n\n") |
There was a problem hiding this comment.
issue (bug_risk): Styled error formatting drops all structured fields attached to the entry.
The non-styled formatter includes entry.Data via writeData, but this styled error formatter omits it and only prints the message. That means any WithFields metadata is lost when styling is enabled. Please also render entry.Data (e.g., in a secondary style) so structured context is preserved.
Summary by Sourcery
Style error-level log output using theme-aware lipgloss formatting when running in a TTY, while preserving existing behavior otherwise.
New Features:
Enhancements:
Tests: