Merge pull request #4952 from mikix/json-exit

Format exit errors as JSON if requested
This commit is contained in:
Michael Eischer 2024-08-15 20:19:38 +00:00 committed by GitHub
commit 7462471c6b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 91 additions and 26 deletions

View file

@ -0,0 +1,6 @@
Enhancement: Format exit errors as JSON with --json
Restic now prints any exit error messages as JSON when requested.
https://github.com/restic/restic/issues/4948
https://github.com/restic/restic/pull/4952

View file

@ -25,6 +25,7 @@ Exit status is 1 if there was any error.
Run: func(_ *cobra.Command, _ []string) {
if globalOptions.JSON {
type jsonVersion struct {
MessageType string `json:"message_type"` // version
Version string `json:"version"`
GoVersion string `json:"go_version"`
GoOS string `json:"go_os"`
@ -32,6 +33,7 @@ Exit status is 1 if there was any error.
}
jsonS := jsonVersion{
MessageType: "version",
Version: version,
GoVersion: runtime.Version(),
GoOS: runtime.GOOS,

View file

@ -4,6 +4,7 @@ import (
"bufio"
"bytes"
"context"
"encoding/json"
"fmt"
"log"
"os"
@ -102,6 +103,30 @@ func tweakGoGC() {
}
}
func printExitError(code int, message string) {
if globalOptions.JSON {
type jsonExitError struct {
MessageType string `json:"message_type"` // exit_error
Code int `json:"code"`
Message string `json:"message"`
}
jsonS := jsonExitError{
MessageType: "exit_error",
Code: code,
Message: message,
}
err := json.NewEncoder(globalOptions.stderr).Encode(jsonS)
if err != nil {
Warnf("JSON encode failed: %v\n", err)
return
}
} else {
fmt.Fprintf(globalOptions.stderr, "%v\n", message)
}
}
func main() {
tweakGoGC()
// install custom global logger into a buffer, if an error occurs
@ -131,21 +156,22 @@ func main() {
err = nil
}
var exitMessage string
switch {
case restic.IsAlreadyLocked(err):
fmt.Fprintf(os.Stderr, "%v\nthe `unlock` command can be used to remove stale locks\n", err)
exitMessage = fmt.Sprintf("%v\nthe `unlock` command can be used to remove stale locks", err)
case err == ErrInvalidSourceData:
fmt.Fprintf(os.Stderr, "Warning: %v\n", err)
exitMessage = fmt.Sprintf("Warning: %v", err)
case errors.IsFatal(err):
fmt.Fprintf(os.Stderr, "%v\n", err)
exitMessage = err.Error()
case err != nil:
fmt.Fprintf(os.Stderr, "%+v\n", err)
exitMessage = fmt.Sprintf("%+v", err)
if logBuffer.Len() > 0 {
fmt.Fprintf(os.Stderr, "also, the following messages were logged by a library:\n")
exitMessage += "also, the following messages were logged by a library:\n"
sc := bufio.NewScanner(logBuffer)
for sc.Scan() {
fmt.Fprintln(os.Stderr, sc.Text())
exitMessage += fmt.Sprintln(sc.Text())
}
}
}
@ -165,5 +191,9 @@ func main() {
default:
exitCode = 1
}
if exitCode != 0 {
printExitError(exitCode, exitMessage)
}
Exit(exitCode)
}

View file

@ -83,12 +83,33 @@ JSON output of most restic commands are documented here.
list of allowed values is documented may be extended at any time.
Exit errors
-----------
Fatal errors will result in a final JSON message on ``stderr`` before the process exits.
It will hold the error message and the exit code.
.. note::
Some errors cannot be caught and reported this way,
such as Go runtime errors or command line parsing errors.
+----------------------+-------------------------------------------+
| ``message_type`` | Always "exit_error" |
+----------------------+-------------------------------------------+
| ``code`` | Exit code (see above chart) |
+----------------------+-------------------------------------------+
| ``error`` | Error message |
+----------------------+-------------------------------------------+
Output formats
--------------
Currently only the output on ``stdout`` is JSON formatted. Errors printed on ``stderr``
are still printed as plain text messages. The generated JSON output uses one of the
following two formats.
Commands print their main JSON output on ``stdout``.
The generated JSON output uses one of the following two formats.
.. note::
Not all messages and errors have been converted to JSON yet.
Feel free to submit a pull request!
Single JSON document
^^^^^^^^^^^^^^^^^^^^
@ -136,6 +157,8 @@ Status
Error
^^^^^
These errors are printed on ``stderr``.
+----------------------+-------------------------------------------+
| ``message_type`` | Always "error" |
+----------------------+-------------------------------------------+
@ -542,6 +565,8 @@ Status
Error
^^^^^
These errors are printed on ``stderr``.
+----------------------+-------------------------------------------+
| ``message_type`` | Always "error" |
+----------------------+-------------------------------------------+
@ -691,12 +716,14 @@ version
The version command returns a single JSON object.
+----------------+--------------------+
+------------------+--------------------+
| ``message_type`` | Always "version" |
+------------------+--------------------+
| ``version`` | restic version |
+----------------+--------------------+
+------------------+--------------------+
| ``go_version`` | Go compile version |
+----------------+--------------------+
+------------------+--------------------+
| ``go_os`` | Go OS |
+----------------+--------------------+
+------------------+--------------------+
| ``go_arch`` | Go architecture |
+----------------+--------------------+
+------------------+--------------------+