influxdb/errors.go

152 lines
4.1 KiB
Go

package platform
import (
"encoding/json"
"errors"
"fmt"
"strings"
)
// Some error code constant, ideally we want define common platform codes here
// projects on use platform's error, should have their own central place like this.
const (
EInternal = "internal error"
ENotFound = "not found"
EConflict = "conflict" // action cannot be performed
EInvalid = "invalid" // validation failed
EEmptyValue = "empty value"
)
// Error is the error struct of platform.
//
// Errors may have error codes, human-readable messages,
// and a logical stack trace.
//
// The Code targets automated handlers so that recovery can occur.
// Msg is used by the system operator to help diagnose and fix the problem.
// Op and Err chain errors together in a logical stack trace to
// further help operators.
//
// To create a simple error,
// &Error{
// Code:ENotFound,
// }
// To show where the error happens, add Op.
// &Error{
// Code: ENotFound,
// Op: "bolt.FindUserByID"
// }
// To show an error with a unpredictable value, add the value in Msg.
// &Error{
// Code: EConflict,
// Message: fmt.Sprintf("organization with name %s already exist", aName),
// }
// To show an error wrapped with another error.
// &Error{
// Code:EInternal,
// Err: err,
// }.
type Error struct {
Code string `json:"code"` // Code is the machine-readable error code.
Msg string `json:"msg,omitempty"` // Msg is a human-readable message.
Op string `json:"op,omitempty"` // Op describes the logical code operation during error.
Err error `json:"err,omitempty"` // Err is a stack of additional errors.
}
// Error implement the error interface by outputing the Code and Err.
func (e *Error) Error() string {
var b strings.Builder
// Print the current operation in our stack, if any.
if e.Op != "" {
fmt.Fprintf(&b, "%s: ", e.Op)
}
// If wrapping an error, print its Error() message.
// Otherwise print the error code & message.
if e.Err != nil {
b.WriteString(e.Err.Error())
} else {
if e.Code != "" {
fmt.Fprintf(&b, "<%s>", e.Code)
if e.Msg != "" {
b.WriteString(" ")
}
}
b.WriteString(e.Msg)
}
return b.String()
}
// ErrorCode returns the code of the root error, if available; otherwise returns EINTERNAL.
func ErrorCode(err error) string {
if err == nil {
return ""
} else if e, ok := err.(*Error); ok && e.Code != "" {
return e.Code
}
return EInternal
}
// ErrorMessage returns the human-readable message of the error, if available.
// Otherwise returns a generic error message.
func ErrorMessage(err error) string {
if err == nil {
return ""
} else if e, ok := err.(*Error); ok && e.Msg != "" {
return e.Msg
} else if ok && e.Err != nil {
return ErrorMessage(e.Err)
}
return "An internal error has occurred."
}
// errEncode an JSON encoding helper that is needed to handle the recursive stack of errors.
type errEncode struct {
Code string `json:"code"` // Code is the machine-readable error code.
Msg string `json:"msg,omitempty"` // Msg is a human-readable message.
Op string `json:"op,omitempty"` // Op describes the logical code operation during error.
Err string `json:"err,omitempty"` // Err is a stack of additional errors.
}
// MarshalJSON recursively marshals the stack of Err.
func (e *Error) MarshalJSON() (result []byte, err error) {
ee := errEncode{
Code: e.Code,
Msg: e.Msg,
Op: e.Op,
}
if e.Err != nil {
if _, ok := e.Err.(*Error); ok {
b, err := e.Err.(*Error).MarshalJSON()
if err != nil {
return result, err
}
ee.Err = string(b)
} else {
ee.Err = e.Err.Error()
}
}
return json.Marshal(ee)
}
// UnmarshalJSON recursively unmarshals the error stack.
func (e *Error) UnmarshalJSON(b []byte) (err error) {
ee := new(errEncode)
err = json.Unmarshal(b, ee)
e.Code = ee.Code
e.Msg = ee.Msg
e.Op = ee.Op
if ee.Err != "" {
var innerErr error
innerResult := new(Error)
innerErr = innerResult.UnmarshalJSON([]byte(ee.Err))
if innerErr != nil {
e.Err = errors.New(ee.Err)
return err
}
e.Err = innerResult
}
return err
}