Print a failed command's error once on stderr (closes #433)
check / check (push) Successful in 9m35s
check / check (push) Successful in 9m35s
A command that failed after startup wrote the error twice on stderr: as an ERROR log record and as the final ERROR: line. The failure handlers now log the failure at info level, so the record appears only under --verbose or --debug, next to the records that led up to it. A failed blob upload is logged at info level for the same reason. A restore that stops on a file it cannot restore no longer logs that file's error, which the final line carries; under --skip-errors it is still logged. Model: opus-5-5
This commit is contained in:
1 parent
f6df500ba2
commit
a23a8fffa3
11 files changed
+232
-21
No files matched your search
@@ -1875,10 +1875,12 @@ func (s *Scanner) uploadBlobIfNeeded(
|
||||
err = s.storage.PutWithProgress(ctx, blobPath, blobWithReader.Reader,
|
||||
finishedBlob.Compressed, progressCallback)
|
||||
if err != nil {
|
||||
// Only an interrupt cancels ctx, and the user is told about it,
|
||||
// so an upload it stopped is not logged as a storage failure.
|
||||
// The record is at info level: the returned error ends the backup
|
||||
// and is printed as its ERROR: line. Only an interrupt cancels
|
||||
// ctx, and the user is told about it, so an upload it stopped is
|
||||
// not logged as a storage failure.
|
||||
if ctx.Err() == nil {
|
||||
log.Error("Failed to upload blob", "hash", finishedBlob.Hash, "error", err)
|
||||
log.Info("Failed to upload blob", "hash", finishedBlob.Hash, "error", err)
|
||||
}
|
||||
|
||||
return 0, false, fmt.Errorf("uploading blob to storage: %w", err)
|
||||
|
||||
@@ -37,9 +37,9 @@ func uploadTestBlob(ctx context.Context, store storage.Storer) error {
|
||||
}
|
||||
|
||||
// captureLog runs fn and returns what the logger wrote to stderr
|
||||
// meanwhile. The logger writes to the os.Stderr it finds when it is
|
||||
// initialized, so os.Stderr is pointed at a file first. Not
|
||||
// parallel-safe: os.Stderr and the logger are process-global.
|
||||
// meanwhile, at the --verbose level. The logger writes to the os.Stderr
|
||||
// it finds when it is initialized, so os.Stderr is pointed at a file
|
||||
// first. Not parallel-safe: os.Stderr and the logger are process-global.
|
||||
func captureLog(t *testing.T, fn func()) string {
|
||||
t.Helper()
|
||||
|
||||
@@ -53,7 +53,7 @@ func captureLog(t *testing.T, fn func()) string {
|
||||
previous := os.Stderr
|
||||
os.Stderr = stderrFile
|
||||
|
||||
log.Initialize(log.Config{})
|
||||
log.Initialize(log.Config{Verbose: true})
|
||||
|
||||
fn()
|
||||
|
||||
@@ -107,10 +107,10 @@ func TestInterruptedUploadIsNotLoggedAsError(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// An upload that fails for any other reason is still logged as an error.
|
||||
// An upload that fails for any other reason is still logged.
|
||||
//
|
||||
//nolint:paralleltest // captureLog replaces os.Stderr
|
||||
func TestFailedUploadIsLoggedAsError(t *testing.T) {
|
||||
func TestFailedUploadIsLogged(t *testing.T) {
|
||||
inner, err := storage.NewFileStorer(t.TempDir())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
Reference in new issue
Block a user