Merge pull request #4952 from mikix/json-exit
Format exit errors as JSON if requested
This commit is contained in:
commit
7462471c6b
4 changed files with 91 additions and 26 deletions
6
changelog/unreleased/issue-4948
Normal file
6
changelog/unreleased/issue-4948
Normal 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
|
|
@ -25,17 +25,19 @@ Exit status is 1 if there was any error.
|
||||||
Run: func(_ *cobra.Command, _ []string) {
|
Run: func(_ *cobra.Command, _ []string) {
|
||||||
if globalOptions.JSON {
|
if globalOptions.JSON {
|
||||||
type jsonVersion struct {
|
type jsonVersion struct {
|
||||||
Version string `json:"version"`
|
MessageType string `json:"message_type"` // version
|
||||||
GoVersion string `json:"go_version"`
|
Version string `json:"version"`
|
||||||
GoOS string `json:"go_os"`
|
GoVersion string `json:"go_version"`
|
||||||
GoArch string `json:"go_arch"`
|
GoOS string `json:"go_os"`
|
||||||
|
GoArch string `json:"go_arch"`
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonS := jsonVersion{
|
jsonS := jsonVersion{
|
||||||
Version: version,
|
MessageType: "version",
|
||||||
GoVersion: runtime.Version(),
|
Version: version,
|
||||||
GoOS: runtime.GOOS,
|
GoVersion: runtime.Version(),
|
||||||
GoArch: runtime.GOARCH,
|
GoOS: runtime.GOOS,
|
||||||
|
GoArch: runtime.GOARCH,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := json.NewEncoder(globalOptions.stdout).Encode(jsonS)
|
err := json.NewEncoder(globalOptions.stdout).Encode(jsonS)
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"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() {
|
func main() {
|
||||||
tweakGoGC()
|
tweakGoGC()
|
||||||
// install custom global logger into a buffer, if an error occurs
|
// install custom global logger into a buffer, if an error occurs
|
||||||
|
@ -131,21 +156,22 @@ func main() {
|
||||||
err = nil
|
err = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var exitMessage string
|
||||||
switch {
|
switch {
|
||||||
case restic.IsAlreadyLocked(err):
|
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:
|
case err == ErrInvalidSourceData:
|
||||||
fmt.Fprintf(os.Stderr, "Warning: %v\n", err)
|
exitMessage = fmt.Sprintf("Warning: %v", err)
|
||||||
case errors.IsFatal(err):
|
case errors.IsFatal(err):
|
||||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
exitMessage = err.Error()
|
||||||
case err != nil:
|
case err != nil:
|
||||||
fmt.Fprintf(os.Stderr, "%+v\n", err)
|
exitMessage = fmt.Sprintf("%+v", err)
|
||||||
|
|
||||||
if logBuffer.Len() > 0 {
|
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)
|
sc := bufio.NewScanner(logBuffer)
|
||||||
for sc.Scan() {
|
for sc.Scan() {
|
||||||
fmt.Fprintln(os.Stderr, sc.Text())
|
exitMessage += fmt.Sprintln(sc.Text())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -165,5 +191,9 @@ func main() {
|
||||||
default:
|
default:
|
||||||
exitCode = 1
|
exitCode = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if exitCode != 0 {
|
||||||
|
printExitError(exitCode, exitMessage)
|
||||||
|
}
|
||||||
Exit(exitCode)
|
Exit(exitCode)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
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
|
Output formats
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
Currently only the output on ``stdout`` is JSON formatted. Errors printed on ``stderr``
|
Commands print their main JSON output on ``stdout``.
|
||||||
are still printed as plain text messages. The generated JSON output uses one of the
|
The generated JSON output uses one of the following two formats.
|
||||||
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
|
Single JSON document
|
||||||
^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^
|
||||||
|
@ -136,6 +157,8 @@ Status
|
||||||
Error
|
Error
|
||||||
^^^^^
|
^^^^^
|
||||||
|
|
||||||
|
These errors are printed on ``stderr``.
|
||||||
|
|
||||||
+----------------------+-------------------------------------------+
|
+----------------------+-------------------------------------------+
|
||||||
| ``message_type`` | Always "error" |
|
| ``message_type`` | Always "error" |
|
||||||
+----------------------+-------------------------------------------+
|
+----------------------+-------------------------------------------+
|
||||||
|
@ -542,6 +565,8 @@ Status
|
||||||
Error
|
Error
|
||||||
^^^^^
|
^^^^^
|
||||||
|
|
||||||
|
These errors are printed on ``stderr``.
|
||||||
|
|
||||||
+----------------------+-------------------------------------------+
|
+----------------------+-------------------------------------------+
|
||||||
| ``message_type`` | Always "error" |
|
| ``message_type`` | Always "error" |
|
||||||
+----------------------+-------------------------------------------+
|
+----------------------+-------------------------------------------+
|
||||||
|
@ -691,12 +716,14 @@ version
|
||||||
|
|
||||||
The version command returns a single JSON object.
|
The version command returns a single JSON object.
|
||||||
|
|
||||||
+----------------+--------------------+
|
+------------------+--------------------+
|
||||||
| ``version`` | restic version |
|
| ``message_type`` | Always "version" |
|
||||||
+----------------+--------------------+
|
+------------------+--------------------+
|
||||||
| ``go_version`` | Go compile version |
|
| ``version`` | restic version |
|
||||||
+----------------+--------------------+
|
+------------------+--------------------+
|
||||||
| ``go_os`` | Go OS |
|
| ``go_version`` | Go compile version |
|
||||||
+----------------+--------------------+
|
+------------------+--------------------+
|
||||||
| ``go_arch`` | Go architecture |
|
| ``go_os`` | Go OS |
|
||||||
+----------------+--------------------+
|
+------------------+--------------------+
|
||||||
|
| ``go_arch`` | Go architecture |
|
||||||
|
+------------------+--------------------+
|
||||||
|
|
Loading…
Reference in a new issue