mirror of
https://github.com/safing/portbase
synced 2025-04-10 20:49:09 +00:00
153 lines
3.5 KiB
Go
153 lines
3.5 KiB
Go
package modules
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"runtime/debug"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
var (
|
|
errorReportingChannel chan *ModuleError
|
|
reportToStdErr = true
|
|
lastReportedError *ModuleError //nolint:errname
|
|
reportingLock sync.Mutex
|
|
)
|
|
|
|
// ModuleError wraps a panic, error or message into an error that can be reported.
|
|
type ModuleError struct {
|
|
Message string
|
|
|
|
ModuleName string
|
|
TaskName string
|
|
TaskType string // one of "worker", "task", "microtask" or custom
|
|
Severity string // one of "info", "error", "panic" or custom
|
|
|
|
PanicValue interface{}
|
|
StackTrace string
|
|
}
|
|
|
|
// NewInfoMessage creates a new, reportable, info message (including a stack trace).
|
|
func (m *Module) NewInfoMessage(message string) *ModuleError {
|
|
return &ModuleError{
|
|
Message: message,
|
|
ModuleName: m.Name,
|
|
Severity: "info",
|
|
StackTrace: string(debug.Stack()),
|
|
}
|
|
}
|
|
|
|
// NewErrorMessage creates a new, reportable, error message (including a stack trace).
|
|
func (m *Module) NewErrorMessage(taskName string, err error) *ModuleError {
|
|
return &ModuleError{
|
|
Message: err.Error(),
|
|
ModuleName: m.Name,
|
|
TaskName: taskName,
|
|
Severity: "error",
|
|
StackTrace: string(debug.Stack()),
|
|
}
|
|
}
|
|
|
|
// NewPanicError creates a new, reportable, panic error message (including a stack trace).
|
|
func (m *Module) NewPanicError(taskName, taskType string, panicValue interface{}) *ModuleError {
|
|
me := &ModuleError{
|
|
Message: fmt.Sprintf("panic: %s", panicValue),
|
|
ModuleName: m.Name,
|
|
TaskName: taskName,
|
|
TaskType: taskType,
|
|
Severity: "panic",
|
|
PanicValue: panicValue,
|
|
StackTrace: string(debug.Stack()),
|
|
}
|
|
me.Message = me.Error()
|
|
return me
|
|
}
|
|
|
|
// Error returns the string representation of the error.
|
|
func (me *ModuleError) Error() string {
|
|
return me.Message
|
|
}
|
|
|
|
// Format returns the error formatted in key/value form.
|
|
func (me *ModuleError) Format() string {
|
|
return fmt.Sprintf(
|
|
`Message: %s
|
|
Timestamp: %s
|
|
ModuleName: %s
|
|
TaskName: %s
|
|
TaskType: %s
|
|
Severity: %s
|
|
PanicValue: %s
|
|
StackTrace:
|
|
|
|
%s
|
|
`,
|
|
me.Message,
|
|
time.Now(),
|
|
me.ModuleName,
|
|
me.TaskName,
|
|
me.TaskType,
|
|
me.Severity,
|
|
me.PanicValue,
|
|
me.StackTrace,
|
|
)
|
|
}
|
|
|
|
// Report reports the error through the configured reporting channel.
|
|
func (me *ModuleError) Report() {
|
|
reportingLock.Lock()
|
|
defer reportingLock.Unlock()
|
|
|
|
lastReportedError = me
|
|
|
|
if errorReportingChannel != nil {
|
|
select {
|
|
case errorReportingChannel <- me:
|
|
default:
|
|
}
|
|
}
|
|
|
|
if reportToStdErr {
|
|
// default to writing to stderr
|
|
fmt.Fprintf(
|
|
os.Stderr,
|
|
"===== Error Report =====\n%s\n===== End of Report =====\n",
|
|
me.Format(),
|
|
)
|
|
}
|
|
}
|
|
|
|
// IsPanic returns whether the given error is a wrapped panic by the modules package and additionally returns it, if true.
|
|
func IsPanic(err error) (bool, *ModuleError) {
|
|
switch val := err.(type) { //nolint:errorlint // TODO: improve
|
|
case *ModuleError:
|
|
return true, val
|
|
default:
|
|
return false, nil
|
|
}
|
|
}
|
|
|
|
// SetErrorReportingChannel sets the channel to report module errors through. By default only panics are reported, all other errors need to be manually wrapped into a *ModuleError and reported.
|
|
func SetErrorReportingChannel(reportingChannel chan *ModuleError) {
|
|
reportingLock.Lock()
|
|
defer reportingLock.Unlock()
|
|
|
|
errorReportingChannel = reportingChannel
|
|
}
|
|
|
|
// SetStdErrReporting controls error reporting to stderr.
|
|
func SetStdErrReporting(on bool) {
|
|
reportingLock.Lock()
|
|
defer reportingLock.Unlock()
|
|
|
|
reportToStdErr = on
|
|
}
|
|
|
|
// GetLastReportedError returns the last reported module error.
|
|
func GetLastReportedError() *ModuleError {
|
|
reportingLock.Lock()
|
|
defer reportingLock.Unlock()
|
|
|
|
return lastReportedError
|
|
}
|