From b0e5bc90c29c635dff1cc31eb0726dd328392b40 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 4 Nov 2022 16:10:25 +0100 Subject: [PATCH 1/4] Fix api endpoint log message --- api/endpoints.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/endpoints.go b/api/endpoints.go index 37a8b45..9e2586a 100644 --- a/api/endpoints.go +++ b/api/endpoints.go @@ -388,18 +388,18 @@ func (e *Endpoint) ServeHTTP(w http.ResponseWriter, r *http.Request) { if eMethod != e.ReadMethod { log.Tracer(r.Context()).Warningf( "api: method %q does not match required read method %q%s", - " - this will be an error and abort the request in the future", r.Method, e.ReadMethod, + " - this will be an error and abort the request in the future", ) } } else { if eMethod != e.WriteMethod { log.Tracer(r.Context()).Warningf( "api: method %q does not match required write method %q%s", - " - this will be an error and abort the request in the future", r.Method, - e.ReadMethod, + e.WriteMethod, + " - this will be an error and abort the request in the future", ) } } From d21c8e6cda5b9a24d4ad94e48f125ac37ff62470 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 4 Nov 2022 16:10:57 +0100 Subject: [PATCH 2/4] Add module status export func and api endpoint --- api/endpoints_modules.go | 20 +++++++ modules/export.go | 117 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 modules/export.go diff --git a/api/endpoints_modules.go b/api/endpoints_modules.go index 480fdef..7d51e5c 100644 --- a/api/endpoints_modules.go +++ b/api/endpoints_modules.go @@ -3,9 +3,21 @@ package api import ( "errors" "fmt" + + "github.com/safing/portbase/modules" ) func registerModulesEndpoints() error { + if err := RegisterEndpoint(Endpoint{ + Path: "modules/status", + Read: PermitUser, + StructFunc: getStatusfunc, + Name: "Get Module Status", + Description: "Returns status information of all modules.", + }); err != nil { + return err + } + if err := RegisterEndpoint(Endpoint{ Path: "modules/{moduleName:.+}/trigger/{eventName:.+}", Write: PermitSelf, @@ -19,6 +31,14 @@ func registerModulesEndpoints() error { return nil } +func getStatusfunc(ar *Request) (i interface{}, err error) { + status := modules.GetStatus() + if status == nil { + return nil, errors.New("modules not yet initialized") + } + return status, nil +} + func triggerEvent(ar *Request) (msg string, err error) { // Get parameters. moduleName := ar.URLVars["moduleName"] diff --git a/modules/export.go b/modules/export.go new file mode 100644 index 0000000..0f241cd --- /dev/null +++ b/modules/export.go @@ -0,0 +1,117 @@ +package modules + +import "sync/atomic" + +// Status holds an exported status summary of the modules system. +type Status struct { + Modules map[string]*ModuleStatus + Total struct { + Workers int + Tasks int + MicroTasks int + CtrlFuncRunning int + } + Config struct { + MicroTasksThreshhold int + MediumPriorityDelay string + LowPriorityDelay string + } +} + +// ModuleStatus holds an exported status summary of one module. +type ModuleStatus struct { //nolint:maligned + Enabled bool + + Status string + FailureType string + FailureID string + FailureMsg string + + Workers int + Tasks int + MicroTasks int + CtrlFuncRunning bool +} + +// GetStatus exports status data from the module system. +func GetStatus() *Status { + // Check if modules have been initialized. + if modulesLocked.IsNotSet() { + return nil + } + + // Create new status. + status := &Status{ + Modules: make(map[string]*ModuleStatus, len(modules)), + } + + // Add config. + status.Config.MicroTasksThreshhold = int(atomic.LoadInt32(microTasksThreshhold)) + status.Config.MediumPriorityDelay = defaultMediumPriorityMaxDelay.String() + status.Config.LowPriorityDelay = defaultLowPriorityMaxDelay.String() + + // Gather status data. + for name, module := range modules { + moduleStatus := &ModuleStatus{ + Enabled: module.Enabled(), + Status: getStatusName(module.Status()), + Workers: int(atomic.LoadInt32(module.workerCnt)), + Tasks: int(atomic.LoadInt32(module.taskCnt)), + MicroTasks: int(atomic.LoadInt32(module.microTaskCnt)), + CtrlFuncRunning: module.ctrlFuncRunning.IsSet(), + } + + // Add failure status. + failureStatus, failureID, failureMsg := module.FailureStatus() + moduleStatus.FailureType = getFailureStatusName(failureStatus) + moduleStatus.FailureID = failureID + moduleStatus.FailureMsg = failureMsg + + // Add to total counts. + status.Total.Workers += moduleStatus.Workers + status.Total.Tasks += moduleStatus.Tasks + status.Total.MicroTasks += moduleStatus.MicroTasks + if moduleStatus.CtrlFuncRunning { + status.Total.CtrlFuncRunning++ + } + + // Add to export. + status.Modules[name] = moduleStatus + } + + return status +} + +func getStatusName(status uint8) string { + switch status { + case StatusDead: + return "dead" + case StatusPreparing: + return "preparing" + case StatusOffline: + return "offline" + case StatusStopping: + return "stopping" + case StatusStarting: + return "starting" + case StatusOnline: + return "online" + default: + return "unknown" + } +} + +func getFailureStatusName(status uint8) string { + switch status { + case FailureNone: + return "" + case FailureHint: + return "hint" + case FailureWarning: + return "warning" + case FailureError: + return "error" + default: + return "unknown" + } +} From 70b58138b92a5015469608ebf16768808859e67a Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 4 Nov 2022 16:11:38 +0100 Subject: [PATCH 3/4] Set default microtask threshold in init for easier override --- modules/microtasks.go | 3 ++- modules/start.go | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/microtasks.go b/modules/microtasks.go index 5fa8d72..2916685 100644 --- a/modules/microtasks.go +++ b/modules/microtasks.go @@ -30,7 +30,8 @@ const ( func init() { var microTasksVal int32 microTasks = µTasksVal - var microTasksThreshholdVal int32 + + microTasksThreshholdVal := int32(runtime.GOMAXPROCS(0) * 2) microTasksThreshhold = µTasksThreshholdVal } diff --git a/modules/start.go b/modules/start.go index c5ab4ab..d73da5e 100644 --- a/modules/start.go +++ b/modules/start.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" "os" - "runtime" "github.com/tevino/abool" @@ -36,7 +35,6 @@ func Start() error { defer mgmtLock.Unlock() // start microtask scheduler - SetMaxConcurrentMicroTasks(runtime.GOMAXPROCS(0)) go microTaskScheduler() // inter-link modules From f6f644fd8e3d1257737599c18f5ecd86e7c1d35b Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 4 Nov 2022 16:12:42 +0100 Subject: [PATCH 4/4] Improve logging control flow --- log/input.go | 5 ++++- log/logging.go | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/log/input.go b/log/input.go index dd0b293..609cff0 100644 --- a/log/input.go +++ b/log/input.go @@ -93,7 +93,10 @@ func log(level Severity, msg string, tracer *ContextTracer) { // wake up writer if necessary if logsWaitingFlag.SetToIf(false, true) { - logsWaiting <- struct{}{} + select { + case logsWaiting <- struct{}{}: + default: + } } } diff --git a/log/logging.go b/log/logging.go index 4000df4..fe777ab 100644 --- a/log/logging.go +++ b/log/logging.go @@ -109,7 +109,7 @@ var ( pkgLevels = make(map[string]Severity) pkgLevelsLock sync.Mutex - logsWaiting = make(chan struct{}, 4) + logsWaiting = make(chan struct{}, 1) logsWaitingFlag = abool.NewBool(false) shutdownFlag = abool.NewBool(false)