Skip to content

refactor: remove verbose flag and enable persistent debug logging#314

Merged
SuperCoolPencil merged 3 commits intomainfrom
logs-on-not-verbose
Apr 2, 2026
Merged

refactor: remove verbose flag and enable persistent debug logging#314
SuperCoolPencil merged 3 commits intomainfrom
logs-on-not-verbose

Conversation

@SuperCoolPencil
Copy link
Copy Markdown
Member

@SuperCoolPencil SuperCoolPencil commented Apr 2, 2026

Greptile Summary

This PR removes the --verbose / -v flag and replaces it with persistent, always-on debug logging: every Surge session now automatically writes a debug-*.log file to the platform-appropriate state directory, capped by the log_retention_count setting. The verbose bool parameter is removed from DetermineFilename, SetVerbose/IsVerbose are replaced by the leaner IsLoggingEnabled, and all fmt.Fprintf(os.Stderr, …) verbose prints are converted to Debug(…) calls. The previously-duplicate filetype.Match(header) call is collapsed into a single invocation whose result is shared between the debug block and the extension-inference logic.

Key changes:

  • internal/utils/debug.go — drops verbose atomic.Bool; adds IsLoggingEnabled() so callers can gate expensive work behind a fast check without entering Debug.
  • internal/utils/filename.goDetermineFilename signature simplified; filetype.Match called once and result reused; http.DetectContentType guarded by IsLoggingEnabled().
  • cmd/root_startup.go — cleanup now uses retention - 1 before creating the new session log, correctly bounding the on-disk count to log_retention_count.
  • internal/utils/filename_test.go — existing tests updated; new TestDetermineFilename_LoggingIntegration added.
  • Docs and issue template updated to reflect the always-on behaviour.

Issues found:

  • The new TestDetermineFilename_LoggingIntegration test may not exercise the debug-write code path it is named after: debugOnce (a package-level sync.Once) can be spent by an earlier test in the binary before this test configures its temp directory, silently making the "integration" half a no-op.
  • When log_retention_count is 0, the current logic deletes all existing logs but still creates one new log for the session — leaving one file where the user may have intended zero.

Confidence Score: 5/5

Safe to merge; all findings are P2 quality concerns that do not affect production behaviour.

The production code changes are mechanically correct: the verbose flag is cleanly removed, Debug fast-paths are in place, and the retention-1 arithmetic is right for all non-zero values. The two remaining issues (test isolation via debugOnce and the retention==0 edge case) are P2 concerns that don't affect real users in the default configuration.

internal/utils/filename_test.go (integration test may not verify log writes), cmd/root_startup.go (retention==0 edge case)

Important Files Changed

Filename Overview
internal/utils/debug.go Removes verbose flag and adds IsLoggingEnabled(); Debug fast-path and ConfigureDebug contract are correct for production use; debugOnce singleton is not resettable (pre-existing design limitation affecting test isolation).
internal/utils/filename.go Drops the verbose parameter and replaces fmt.Fprintf(os.Stderr) with Debug(); filetype.Match called once and result reused correctly; http.DetectContentType guarded by IsLoggingEnabled().
cmd/root_startup.go retention-1 logic correctly limits log count to the configured maximum; edge case where retention==0 still creates one log per session despite appearing to request zero logs.
internal/utils/filename_test.go Existing tests correctly updated; new LoggingIntegration test may not exercise the debug-write path it claims to test due to debugOnce being spent by earlier tests in the binary.
cmd/root.go verbose flag and SetVerbose call cleanly removed; stray blank line at top of PersistentPreRun is cosmetic.
internal/processing/probe.go DetermineFilename call updated to drop the now-removed verbose argument; no other changes.
.github/ISSUE_TEMPLATE/bug_report.md Bug-report template updated to reflect always-on logging; instructions simplified and numbered correctly.
docs/USAGE.md --verbose/-v row removed from the persistent flags table; straightforward documentation cleanup.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Surge startup
initializeGlobalState] --> B[ConfigureDebug logsDir]
    B --> C{retention > 0?}
    C -- yes --> D[CleanupLogs retention-1]
    C -- no --> E[CleanupLogs retention]
    D --> F[New session log created
on first Debug call]
    E --> F

    G[DetermineFilename called] --> H[Apply heuristics
Content-Disposition / query / path]
    H --> I[Read 512-byte header]
    I --> J[filetype.Match header
one call, result cached]
    J --> K{IsLoggingEnabled?}
    K -- yes --> L[http.DetectContentType
Debug MIME + magic type]
    K -- no --> M[Skip expensive MIME log]
    L --> N[ZIP / extension inference
uses cached kind]
    M --> N
    N --> O[Return filename]

    subgraph Debug subsystem
        P[Debug format args] --> Q{logsDir empty?}
        Q -- yes --> R[return fast path]
        Q -- no --> S[debugOnce.Do
create log file]
        S --> T[Write timestamped line]
    end
Loading
Prompt To Fix All With AI
This is a comment left during a code review.
Path: internal/utils/filename_test.go
Line: 159-183

Comment:
**`debugOnce` may already be spent when this test runs**

`debugOnce` is a package-level `sync.Once` that fires exactly once for the entire test binary run. `TestCleanupLogs` (in `debug_test.go`) defers `utils.ConfigureDebug(config.GetLogsDir())`, which restores `logsDir` to the real system logs path before returning. If any subsequent test calls `Debug(...)` with that `logsDir` set (e.g., `TestDetermineFilename_PriorityOrder` calls `DetermineFilename` which calls `Debug` on several code paths), `debugOnce.Do` fires against the real logs directory — not a temp directory.

When `TestDetermineFilename_LoggingIntegration` then calls `ConfigureDebug(tempDir)` and invokes `DetermineFilename`, `debugOnce.Do` is already a no-op. All `Debug` writes still go to the original file (or to `nil` if that file couldn't be created), never to `tempDir`. The test therefore only validates the filename return value — already covered by `TestDetermineFilename_PriorityOrder` — rather than the debug-logging integration it is named after.

To reliably test the logging path, the debug subsystem needs a way to reset its state between tests (e.g., a `resetDebugForTesting()` function that zeroes out both `logsDir` and `debugOnce`), or the test should verify that a log file was actually written to `tempDir`:

```go
entries, err := os.ReadDir(tempDir)
if err != nil || len(entries) == 0 {
    t.Error("expected a debug log file to be written to the configured temp dir")
}
```

Without such an assertion, a future regression in the `Debug`→file-write path would not be caught by this test.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: cmd/root_startup.go
Line: 56-60

Comment:
**Retention of `0` still produces one log after cleanup**

The guard `if retention > 0` correctly avoids underflow, but when `retention == 0` the else-branch calls `CleanupLogs(0)` (deletes all existing logs) and then a new log is created immediately after. A user who explicitly sets `log_retention_count: 0` expecting zero persistent logs will end up with one log per session.

`CleanupLogs` already interprets negative values as "keep all logs". If `0` is intended to mean "keep none / disable logging", the startup code should also skip `ConfigureDebug` entirely (or call `ConfigureDebug("")`) in that case:

```go
if retention == 0 {
    // User requested no log persistence; skip logging entirely
    utils.ConfigureDebug("")
    return nil
}
utils.ConfigureDebug(logsDir)
utils.CleanupLogs(retention - 1)
```

If `0` is instead meant as "use default", that should be documented in the settings struct and handled accordingly.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (2): Last reviewed commit: "refactor: optimize debug logging by addi..." | Re-trigger Greptile

Context used:

  • Rule used - What: All code changes must include tests for edge... (source)

@greptile-apps

This comment was marked as outdated.

Comment thread internal/utils/filename.go Outdated
Comment thread internal/utils/filename_test.go
Comment thread cmd/root.go
Comment on lines 412 to 414
PersistentPreRun: func(cmd *cobra.Command, args []string) {
// Set global verbose mode
utils.SetVerbose(verbose)

GlobalProgressCh = make(chan any, 100)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Stray blank line left after removing verbose setup

Removing the SetVerbose call left an empty line at the top of PersistentPreRun, making the function body start with unnecessary whitespace. Consider removing it.

Suggested change
PersistentPreRun: func(cmd *cobra.Command, args []string) {
// Set global verbose mode
utils.SetVerbose(verbose)
GlobalProgressCh = make(chan any, 100)
PersistentPreRun: func(cmd *cobra.Command, args []string) {
GlobalProgressCh = make(chan any, 100)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@SuperCoolPencil
Copy link
Copy Markdown
Member Author

@greptileai

@SuperCoolPencil SuperCoolPencil merged commit 45b2cdf into main Apr 2, 2026
15 checks passed
@SuperCoolPencil SuperCoolPencil deleted the logs-on-not-verbose branch April 2, 2026 15:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant