Package Logger :

- Fix too many open file for logger file
- Fix invalid FD for log file
- Fix bug with field into entry
- Fix race detection on testing

Package Status :
- Add capability to send short into query to received a short output without any result of component
- Add Connection Header as Close to router status return of Get
- Fix race detection into health with concurrent read/write data
- Fix race detection with update of main router

Package HTTPServer :
- Add option to disable keepalive for server
- Fix config tag error
- Fix logger usage / close

Package Config :
- Fix component using logger to implement a close
- Fix logger initialization :
  - use local var to setup new logger based on logger clone if loger still existing
  - if error while configuring new logger, do not change logger
  - closing old logger before replace it with new

Package Router :
- Fix missing ignore error return
- Fix golib logger not closed

Other :
- Bump dependencies
This commit is contained in:
Nicolas JUHEL
2022-11-04 14:26:15 +01:00
committed by Nicolas JUHEL
parent 6ff86118fe
commit 39948566e3
13 changed files with 387 additions and 116 deletions

View File

@@ -46,7 +46,7 @@ type ComponentAws interface {
SetAws(a libaws.AWS)
}
func New(drv ConfigDriver, logKey string) ComponentAws {
func New(drv ConfigDriver) ComponentAws {
return &componentAws{
ctx: nil,
get: nil,
@@ -64,8 +64,8 @@ func Register(cfg libcfg.Config, key string, cpt ComponentAws) {
cfg.ComponentSet(key, cpt)
}
func RegisterNew(cfg libcfg.Config, drv ConfigDriver, key, logKey string) {
cfg.ComponentSet(key, New(drv, logKey))
func RegisterNew(cfg libcfg.Config, drv ConfigDriver, key string) {
cfg.ComponentSet(key, New(drv))
}
func Load(getCpt libcfg.FuncComponentGet, key string) ComponentAws {

View File

@@ -28,6 +28,7 @@ package http
import (
"net/http"
"sync"
libcfg "github.com/nabbar/golib/config"
libhts "github.com/nabbar/golib/httpserver"
@@ -60,6 +61,7 @@ func New(tlsKey, logKey string, handler map[string]http.Handler) ComponentHttp {
}
return &componentHttp{
m: sync.Mutex{},
tls: tlsKey,
log: logKey,
run: false,

View File

@@ -75,35 +75,52 @@ func (c *componentLog) _runCli(getCfg libcfg.FuncComponentConfigGet) liberr.Erro
c.m.Lock()
defer c.m.Unlock()
if c.l == nil {
if c.ctx == nil {
return ErrorComponentNotInitialized.Error(nil)
}
if c.l == nil {
c.l = liblog.New(c.ctx())
c.l.SetLevel(c.v)
}
var (
e error
log liblog.Logger
cnf *liblog.Options
err liberr.Error
)
if log, e = c.l.Clone(); e != nil {
log = liblog.New(c.ctx())
log.SetLevel(c.v)
}
if cnf, err = c._GetOptions(getCfg); err != nil {
return err
} else if cnf == nil {
return ErrorConfigInvalid.Error(nil)
} else if e = log.SetOptions(cnf); e != nil {
return ErrorReloadLog.Error(err)
}
if c.l != nil {
_ = c.l.Close()
}
c.l = log
return nil
}
func (c *componentLog) _run(getCfg libcfg.FuncComponentConfigGet) liberr.Error {
var (
cnf *liblog.Options
)
fb, fa := c._getFct()
if err := c._runFct(fb); err != nil {
return err
} else if err = c._runCli(getCfg); err != nil {
return err
} else if cnf, err = c._GetOptions(getCfg); err != nil {
return err
} else if cnf == nil {
return ErrorConfigInvalid.Error(nil)
} else if e := c.l.SetOptions(cnf); e != nil {
return ErrorReloadLog.Error(err)
} else if err = c._runFct(fa); err != nil {
return err
}

11
go.mod
View File

@@ -27,10 +27,10 @@ require (
github.com/mattn/go-colorable v0.1.13
github.com/mitchellh/go-homedir v1.1.0
github.com/nats-io/jwt/v2 v2.3.0
github.com/nats-io/nats-server/v2 v2.9.5
github.com/nats-io/nats.go v1.19.0
github.com/nats-io/nats-server/v2 v2.9.6
github.com/nats-io/nats.go v1.19.1
github.com/onsi/ginkgo/v2 v2.4.0
github.com/onsi/gomega v1.23.0
github.com/onsi/gomega v1.24.0
github.com/pelletier/go-toml v1.9.5
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.13.1
@@ -38,13 +38,13 @@ require (
github.com/sirupsen/logrus v1.9.0
github.com/spf13/cobra v1.6.1
github.com/spf13/jwalterweatherman v1.1.0
github.com/spf13/viper v1.13.0
github.com/spf13/viper v1.14.0
github.com/vbauerster/mpb/v5 v5.4.0
github.com/xanzy/go-gitlab v0.74.0
github.com/xhit/go-simple-mail v2.2.2+incompatible
github.com/xujiajun/nutsdb v0.11.0
github.com/xujiajun/utils v0.0.0-20220904132955-5f7c5b914235
golang.org/x/exp v0.0.0-20221031165847-c99f073a8326
golang.org/x/exp v0.0.0-20221106115401-f9659909a136
golang.org/x/net v0.1.0
golang.org/x/oauth2 v0.1.0
golang.org/x/sync v0.1.0
@@ -97,7 +97,6 @@ require (
github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect
github.com/go-errors/errors v1.4.2 // indirect
github.com/go-faster/city v1.0.1 // indirect
github.com/go-faster/errors v0.6.1 // indirect
github.com/go-logr/logr v1.2.3 // indirect

View File

@@ -215,35 +215,41 @@ type ServerConfig struct {
// which may be active globally, which is MaxHandlers.
// If zero, MaxConcurrentStreams defaults to at least 100, per
// the HTTP/2 spec's recommendations.
MaxConcurrentStreams uint32 `json:"max_concurrent_streams" json:"max_concurrent_streams" yaml:"max_concurrent_streams" toml:"max_concurrent_streams"`
MaxConcurrentStreams uint32 `mapstructure:"max_concurrent_streams" json:"max_concurrent_streams" yaml:"max_concurrent_streams" toml:"max_concurrent_streams"`
// MaxReadFrameSize optionally specifies the largest frame
// this server is willing to read. A valid value is between
// 16k and 16M, inclusive. If zero or otherwise invalid, a
// default value is used.
MaxReadFrameSize uint32 `json:"max_read_frame_size" json:"max_read_frame_size" yaml:"max_read_frame_size" toml:"max_read_frame_size"`
MaxReadFrameSize uint32 `mapstructure:"max_read_frame_size" json:"max_read_frame_size" yaml:"max_read_frame_size" toml:"max_read_frame_size"`
// PermitProhibitedCipherSuites, if true, permits the use of
// cipher suites prohibited by the HTTP/2 spec.
PermitProhibitedCipherSuites bool `json:"permit_prohibited_cipher_suites" json:"permit_prohibited_cipher_suites" yaml:"permit_prohibited_cipher_suites" toml:"permit_prohibited_cipher_suites"`
PermitProhibitedCipherSuites bool `mapstructure:"permit_prohibited_cipher_suites" json:"permit_prohibited_cipher_suites" yaml:"permit_prohibited_cipher_suites" toml:"permit_prohibited_cipher_suites"`
// IdleTimeout specifies how long until idle clients should be
// closed with a GOAWAY frame. PING frames are not considered
// activity for the purposes of IdleTimeout.
IdleTimeout time.Duration `json:"idle_timeout" json:"idle_timeout" yaml:"idle_timeout" toml:"idle_timeout"`
IdleTimeout time.Duration `mapstructure:"idle_timeout" json:"idle_timeout" yaml:"idle_timeout" toml:"idle_timeout"`
// MaxUploadBufferPerConnection is the size of the initial flow
// control window for each connections. The HTTP/2 spec does not
// allow this to be smaller than 65535 or larger than 2^32-1.
// If the value is outside this range, a default value will be
// used instead.
MaxUploadBufferPerConnection int32 `json:"max_upload_buffer_per_connection" json:"max_upload_buffer_per_connection" yaml:"max_upload_buffer_per_connection" toml:"max_upload_buffer_per_connection"`
MaxUploadBufferPerConnection int32 `mapstructure:"max_upload_buffer_per_connection" json:"max_upload_buffer_per_connection" yaml:"max_upload_buffer_per_connection" toml:"max_upload_buffer_per_connection"`
// MaxUploadBufferPerStream is the size of the initial flow control
// window for each stream. The HTTP/2 spec does not allow this to
// be larger than 2^32-1. If the value is zero or larger than the
// maximum, a default value will be used instead.
MaxUploadBufferPerStream int32 `json:"max_upload_buffer_per_stream" json:"max_upload_buffer_per_stream" yaml:"max_upload_buffer_per_stream" toml:"max_upload_buffer_per_stream"`
MaxUploadBufferPerStream int32 `mapstructure:"max_upload_buffer_per_stream" json:"max_upload_buffer_per_stream" yaml:"max_upload_buffer_per_stream" toml:"max_upload_buffer_per_stream"`
// DisableKeepAlive controls whether HTTP keep-alives are disabled.
// By default, keep-alives are always enabled. Only very
// resource-constrained environments or servers in the process of
// shutting down should disable them.
DisableKeepAlive bool `mapstructure:"disable_keep_alive" json:"disable_keep_alive" yaml:"disable_keep_alive" toml:"disable_keep_alive"`
}
func (c *ServerConfig) Clone() ServerConfig {
@@ -262,6 +268,7 @@ func (c *ServerConfig) Clone() ServerConfig {
IdleTimeout: c.IdleTimeout,
MaxUploadBufferPerConnection: c.MaxUploadBufferPerConnection,
MaxUploadBufferPerStream: c.MaxUploadBufferPerStream,
DisableKeepAlive: c.DisableKeepAlive,
Name: c.Name,
Listen: c.Listen,
Expose: c.Expose,

View File

@@ -189,9 +189,13 @@ func (s *srvRun) Listen(cfg *ServerConfig, handler http.Handler) liberr.Error {
s.bnd = bind
s.tls = sTls || ssl.LenCertificatePair() > 0
var (
l = s.getLogger()
)
srv := &http.Server{
Addr: cfg.GetListen().Host,
ErrorLog: s.getLogger().GetStdLogger(liblog.ErrorLevel, log.LstdFlags|log.Lmicroseconds),
ErrorLog: l.GetStdLogger(liblog.ErrorLevel, log.LstdFlags|log.Lmicroseconds),
}
if cfg.ReadTimeout > 0 {
@@ -214,6 +218,12 @@ func (s *srvRun) Listen(cfg *ServerConfig, handler http.Handler) liberr.Error {
srv.IdleTimeout = cfg.IdleTimeout
}
if cfg.DisableKeepAlive {
srv.SetKeepAlivesEnabled(false)
} else {
srv.SetKeepAlivesEnabled(true)
}
if ssl.LenCertificatePair() > 0 {
srv.TLSConfig = ssl.TlsConfig("")
}
@@ -279,15 +289,20 @@ func (s *srvRun) Listen(cfg *ServerConfig, handler http.Handler) liberr.Error {
s.srv = srv
s.m.Unlock()
go func(ctx context.Context, cnl context.CancelFunc, name, host string, tlsMandatory bool) {
var _log = s.getLogger()
go func(ctx context.Context, cnl context.CancelFunc, _log liblog.Logger, name, host string, tlsMandatory bool) {
ent := _log.Entry(liblog.InfoLevel, "server stopped")
defer func() {
ent.Log()
if _log != nil {
_ = _log.Close()
}
if ctx != nil && cnl != nil && ctx.Err() == nil {
cnl()
}
s.setRunning(false)
}()
@@ -323,7 +338,7 @@ func (s *srvRun) Listen(cfg *ServerConfig, handler http.Handler) liberr.Error {
ent.Level = liblog.ErrorLevel
ent.ErrorAdd(true, er)
}
}(s.ctx, s.cnl, name, bind, sTls)
}(s.ctx, s.cnl, l, name, bind, sTls)
return nil
}
@@ -347,6 +362,7 @@ func (s *srvRun) srvShutdown() {
defer func() {
cancel()
if s.srv != nil {
err := s.srv.Close()
@@ -356,6 +372,10 @@ func (s *srvRun) srvShutdown() {
_log.Entry(liblog.ErrorLevel, "closing server").ErrorAdd(true, err).Check(liblog.InfoLevel)
}
if _log != nil {
_ = _log.Close()
}
}()
if s.srv != nil {

View File

@@ -93,13 +93,13 @@ func (e *Entry) SetGinContext(ctx *gin.Context) *Entry {
// FieldAdd allow to add one couple key/val as type string/interface into the custom field of the entry.
func (e *Entry) FieldAdd(key string, val interface{}) *Entry {
e.Fields.Add(key, val)
e.Fields = e.Fields.Add(key, val)
return e
}
// FieldMerge allow to merge a Field pointer into the custom field of the entry.
func (e *Entry) FieldMerge(fields Fields) *Entry {
e.Fields.Merge(fields)
e.Fields = e.Fields.Merge(fields)
return e
}
@@ -110,7 +110,7 @@ func (e *Entry) FieldSet(fields Fields) *Entry {
}
func (e *Entry) FieldClean(keys ...string) *Entry {
e.Fields.Clean(keys...)
e.Fields = e.Fields.Clean(keys...)
return e
}

View File

@@ -33,6 +33,7 @@ import (
"os"
"strings"
"sync"
"time"
"github.com/nabbar/golib/ioutils"
"github.com/sirupsen/logrus"
@@ -46,6 +47,8 @@ type HookFile interface {
type _HookFile struct {
m sync.Mutex
h *os.File
w time.Time
r logrus.Formatter
l []logrus.Level
s bool
@@ -95,6 +98,8 @@ func NewHookFile(opt OptionsFile, format logrus.Formatter) (HookFile, error) {
obj := &_HookFile{
m: sync.Mutex{},
h: nil,
w: time.Time{},
r: format,
l: LVLs,
s: opt.DisableStack,
@@ -137,6 +142,34 @@ func (o *_HookFile) openCreate() (*os.File, error) {
}
}
func (o *_HookFile) isStack() bool {
o.m.Lock()
defer o.m.Unlock()
return o.s
}
func (o *_HookFile) isTimeStamp() bool {
o.m.Lock()
defer o.m.Unlock()
return o.d
}
func (o *_HookFile) isTrace() bool {
o.m.Lock()
defer o.m.Unlock()
return o.t
}
func (o *_HookFile) isAccessLog() bool {
o.m.Lock()
defer o.m.Unlock()
return o.a
}
func (o *_HookFile) RegisterHook(log *logrus.Logger) {
log.AddHook(o)
}
@@ -146,21 +179,18 @@ func (o *_HookFile) Levels() []logrus.Level {
}
func (o *_HookFile) Fire(entry *logrus.Entry) error {
o.m.Lock()
defer o.m.Unlock()
ent := entry.Dup()
ent.Level = entry.Level
if o.s {
if !o.isStack() {
ent.Data = o.filterKey(ent.Data, FieldStack)
}
if o.d {
if !o.isTimeStamp() {
ent.Data = o.filterKey(ent.Data, FieldTime)
}
if !o.t {
if !o.isTrace() {
ent.Data = o.filterKey(ent.Data, FieldCaller)
ent.Data = o.filterKey(ent.Data, FieldFile)
ent.Data = o.filterKey(ent.Data, FieldLine)
@@ -171,7 +201,7 @@ func (o *_HookFile) Fire(entry *logrus.Entry) error {
e error
)
if o.a {
if o.isAccessLog() {
if len(entry.Message) > 0 {
if !strings.HasSuffix(entry.Message, "\n") {
entry.Message += "\n"
@@ -195,24 +225,72 @@ func (o *_HookFile) Fire(entry *logrus.Entry) error {
return nil
}
func (o *_HookFile) Write(p []byte) (n int, err error) {
h, e := o.openCreate()
defer func() {
if h != nil {
_ = h.Close()
}
}()
func (o *_HookFile) write(p []byte) (n int, err error) {
o.m.Lock()
defer o.m.Unlock()
if e != nil {
var e error
if o.h == nil {
if o.h, e = o.openCreate(); e != nil {
return 0, fmt.Errorf("logrus.hookfile: cannot open '%s': %v", o.o.FilePath, e)
} else if n, e = h.Write(p); e != nil {
return n, e
} else {
return n, h.Sync()
}
} else if _, e = o.h.Seek(0, io.SeekEnd); e != nil {
return 0, fmt.Errorf("logrus.hookfile: cannot seek file '%s' to EOF: %v", o.o.FilePath, e)
}
return o.h.Write(p)
}
func (o *_HookFile) Write(p []byte) (n int, err error) {
if n, err = o.write(p); err != nil {
_ = o.Close()
n, err = o.write(p)
}
if err != nil {
return n, err
}
o.m.Lock()
defer o.m.Unlock()
if o.w.IsZero() {
_ = o.h.Sync()
o.w = time.Now()
return n, err
} else if time.Since(o.w) > 30*time.Second {
_ = o.h.Sync()
o.w = time.Now()
return n, err
}
return n, err
}
func (o *_HookFile) Close() error {
o.m.Lock()
defer o.m.Unlock()
if o.h != nil {
var e error
if er := o.h.Sync(); er != nil {
e = fmt.Errorf("logrus.hookfile: sync file error '%s': %v", o.o.FilePath, er)
}
if er := o.h.Close(); er != nil {
if e != nil {
e = fmt.Errorf("%v, close file error '%s': %v", e, o.o.FilePath, er)
} else {
e = fmt.Errorf("logrus.hookfile: close file error '%s': %v", o.o.FilePath, er)
}
}
o.h = nil
return e
}
return nil
}

View File

@@ -26,7 +26,11 @@
package logger_test
import (
"context"
"path/filepath"
"time"
libsem "github.com/nabbar/golib/semaphore"
"github.com/nabbar/golib/logger"
. "github.com/onsi/ginkgo/v2"
@@ -114,7 +118,7 @@ var _ = Describe("Logger", func() {
log.LogDetails(logger.InfoLevel, "test logger with field", nil, nil, nil)
})
})
Context("Create New Logger with file in multithread mode", func() {
Context("Create New Logger with file in multithreading mode", func() {
It("Must succeed", func() {
log := logger.New(GetContext())
log.SetLevel(logger.DebugLevel)
@@ -166,9 +170,22 @@ var _ = Describe("Logger", func() {
}(sub)
log.SetFields(logger.NewFields().Add("logger", "main"))
for i := 0; i < 10; i++ {
log.Entry(logger.InfoLevel, "test multithreading logger").FieldAdd("id", i).Log()
sem := libsem.NewSemaphoreWithContext(context.Background(), 0)
defer sem.DeferMain()
for i := 0; i < 25; i++ {
Expect(sem.NewWorker()).ToNot(HaveOccurred())
go func(id int) {
defer sem.DeferWorker()
ent := log.Entry(logger.InfoLevel, "test multithreading logger")
ent.FieldAdd("id", id)
ent.Log()
}(i)
}
Expect(sem.WaitAll()).ToNot(HaveOccurred())
time.Sleep(100 * time.Millisecond)
})
})
})

View File

@@ -102,6 +102,12 @@ func GinAccessLog(log liblog.FuncLog) gin.HandlerFunc {
} else if l := log(); l == nil {
return
} else {
defer func() {
if l != nil {
_ = l.Close()
}
}()
sttm := time.Unix(0, c.GetInt64(GinContextStartUnixNanoTime))
path := c.GetString(GinContextRequestPath)
@@ -163,6 +169,12 @@ func GinErrorLog(log liblog.FuncLog) gin.HandlerFunc {
} else if l := log(); l == nil {
return
} else {
defer func() {
if l != nil {
_ = l.Close()
}
}()
if len(c.Errors) > 0 {
for _, e := range c.Errors {
ent := l.Entry(liblog.ErrorLevel, "error on request \"%s %s %s\"", c.Request.Method, path, c.Request.Proto)
@@ -193,7 +205,7 @@ func DefaultGinWithTrustyProxy(trustyProxy []string) *gin.Engine {
engine.Use(gin.Logger(), gin.Recovery())
if len(trustyProxy) > 0 {
engine.SetTrustedProxies(trustyProxy)
_ = engine.SetTrustedProxies(trustyProxy)
}
return engine

View File

@@ -26,6 +26,7 @@
package status
import (
"sync"
"time"
"github.com/gin-gonic/gin"
@@ -55,6 +56,7 @@ type Status interface {
func NewStatus(health FctHealth, msg FctMessage, cacheDuration time.Duration) Status {
return &status{
m: sync.Mutex{},
fh: health,
fm: msg,
c: nil,
@@ -64,6 +66,7 @@ func NewStatus(health FctHealth, msg FctMessage, cacheDuration time.Duration) St
}
type status struct {
m sync.Mutex
fh FctHealth
fm FctMessage
@@ -72,6 +75,43 @@ type status struct {
d time.Duration
}
func (s *status) getInfo() (string, string) {
s.m.Lock()
defer s.m.Unlock()
if s.fm != nil {
return s.fm()
}
return "", ""
}
func (s *status) getHealth() error {
s.m.Lock()
defer s.m.Unlock()
if s.fh != nil {
return s.fh()
}
return nil
}
func (s *status) setCache(obj *StatusResponse) {
s.m.Lock()
defer s.m.Unlock()
s.c = obj
s.t = time.Now()
}
func (s *status) getCache() StatusResponse {
s.m.Lock()
defer s.m.Unlock()
return s.c.Clone()
}
func (s *status) Get(x *gin.Context) StatusResponse {
if !s.IsValid() {
var (
@@ -80,13 +120,8 @@ func (s *status) Get(x *gin.Context) StatusResponse {
msgKO string
)
if s.fm != nil {
msgOk, msgKO = s.fm()
}
if s.fh != nil {
err = s.fh()
}
msgOk, msgKO = s.getInfo()
err = s.getHealth()
c := &StatusResponse{}
@@ -99,19 +134,24 @@ func (s *status) Get(x *gin.Context) StatusResponse {
c.Message = msgOk
}
s.c = c
s.t = time.Now()
s.setCache(c)
}
return s.c.Clone()
return s.getCache()
}
func (s *status) Clean() {
s.m.Lock()
defer s.m.Unlock()
s.c = nil
s.t = time.Now()
}
func (s *status) IsValid() bool {
s.m.Lock()
defer s.m.Unlock()
if s.c == nil {
return false
} else if s.t.IsZero() {

View File

@@ -27,6 +27,7 @@ package status
import (
"net/http"
"sync"
"sync/atomic"
librtr "github.com/nabbar/golib/router"
@@ -50,7 +51,8 @@ type RouteStatus interface {
func New(Name string, Release string, Hash string, msgOk string, msgKo string, msgWarm string) RouteStatus {
return &rtrStatus{
m: make([]gin.HandlerFunc, 0),
m: sync.Mutex{},
f: make([]gin.HandlerFunc, 0),
n: Name,
v: Release,
h: Hash,

View File

@@ -29,6 +29,7 @@ import (
"net/http"
"path"
"strings"
"sync"
"sync/atomic"
"github.com/gin-gonic/gin"
@@ -39,7 +40,8 @@ import (
)
type rtrStatus struct {
m []gin.HandlerFunc
m sync.Mutex
f []gin.HandlerFunc
n string
v string
@@ -55,6 +57,8 @@ type rtrStatus struct {
c map[string]*atomic.Value
}
const keyShortOutput = "short"
func (r *rtrStatus) HttpStatusCode(codeOk, codeKO, codeWarning int) {
r.cOk = codeOk
r.cKO = codeKO
@@ -62,11 +66,11 @@ func (r *rtrStatus) HttpStatusCode(codeOk, codeKO, codeWarning int) {
}
func (r *rtrStatus) MiddlewareAdd(mdw ...gin.HandlerFunc) {
if len(r.m) < 1 {
r.m = make([]gin.HandlerFunc, 0)
if len(r.f) < 1 {
r.f = make([]gin.HandlerFunc, 0)
}
r.m = append(r.m, mdw...)
r.f = append(r.f, mdw...)
}
func (r *rtrStatus) cleanPrefix(prefix string) string {
@@ -76,7 +80,7 @@ func (r *rtrStatus) cleanPrefix(prefix string) string {
func (r *rtrStatus) Register(prefix string, register librtr.RegisterRouter) {
prefix = r.cleanPrefix(prefix)
var m = r.m
var m = r.f
m = append(m, r.Get)
register(http.MethodGet, prefix, m...)
@@ -88,7 +92,7 @@ func (r *rtrStatus) Register(prefix string, register librtr.RegisterRouter) {
func (r *rtrStatus) RegisterGroup(group, prefix string, register librtr.RegisterRouterInGroup) {
prefix = r.cleanPrefix(prefix)
var m = r.m
var m = r.f
m = append(m, r.Get)
register(group, http.MethodGet, prefix, m...)
@@ -97,80 +101,153 @@ func (r *rtrStatus) RegisterGroup(group, prefix string, register librtr.Register
}
}
func (r *rtrStatus) getInfo() (name string, release string, hash string) {
r.m.Lock()
defer r.m.Unlock()
return r.n, r.v, r.h
}
func (r *rtrStatus) getMsgOk() string {
r.m.Lock()
defer r.m.Unlock()
return r.mOK
}
func (r *rtrStatus) getMsgKo() string {
r.m.Lock()
defer r.m.Unlock()
return r.mKO
}
func (r *rtrStatus) getMsgWarn() string {
r.m.Lock()
defer r.m.Unlock()
return r.mWM
}
func (r *rtrStatus) Get(x *gin.Context) {
var (
atm *atomic.Value
key string
err liberr.Error
rsp *Response
sem libsem.Sem
s libsem.Sem
)
defer func() {
if sem != nil {
sem.DeferMain()
if s != nil {
s.DeferMain()
}
}()
rsp = &Response{
InfoResponse: InfoResponse{
Name: r.n,
Release: r.v,
HashBuild: r.h,
inf := InfoResponse{
Mandatory: true,
},
StatusResponse: StatusResponse{
}
inf.Name, inf.Release, inf.HashBuild = r.getInfo()
sts := StatusResponse{
Status: DefMessageOK,
Message: r.mOK,
},
}
sts.Message = r.getMsgOk()
rsp = &Response{
InfoResponse: inf,
StatusResponse: sts,
Components: make([]CptResponse, 0),
}
sem = libsem.NewSemaphoreWithContext(x, 0)
s = libsem.NewSemaphoreWithContext(x, 0)
for key, atm = range r.c {
var (
ok bool
c Component
)
for _, key = range r.ComponentKeys() {
var c Component
if atm == nil {
if c = r.ComponentGet(key); c == nil {
continue
}
if c, ok = atm.Load().(Component); !ok {
continue
}
err = sem.NewWorker()
err = s.NewWorker()
if liblog.ErrorLevel.LogGinErrorCtxf(liblog.DebugLevel, "init new thread to collect data for component '%s'", err, x, key) {
continue
}
go func(c Component) {
go func(ctx *gin.Context, sem libsem.Sem, cpt Component, resp *Response) {
defer sem.DeferWorker()
rsp.appendNewCpt(c.Get(x))
}(c)
resp.appendNewCpt(cpt.Get(ctx))
}(x, s, c, rsp)
}
err = sem.WaitAll()
err = s.WaitAll()
var (
code int
)
if liblog.ErrorLevel.LogGinErrorCtx(liblog.DebugLevel, "waiting all thread to collect data component ", err, x) {
rsp.Message = r.mKO
rsp.Message = r.getMsgKo()
rsp.Status = DefMessageKO
x.AbortWithStatusJSON(r.cKO, rsp)
code = r.cKO
} else if !rsp.IsOkMandatory() {
rsp.Message = r.mKO
rsp.Message = r.getMsgKo()
rsp.Status = DefMessageKO
x.AbortWithStatusJSON(r.cKO, rsp)
code = r.cKO
} else if !rsp.IsOk() {
rsp.Message = r.mWM
rsp.Message = r.getMsgWarn()
rsp.Status = DefMessageOK
x.JSON(r.cWM, rsp)
code = r.cWM
} else {
rsp.Message = r.mOK
rsp.Message = r.getMsgOk()
rsp.Status = DefMessageOK
x.JSON(r.cOk, rsp)
code = r.cOk
}
if x.Request.URL.Query().Has(keyShortOutput) {
rsp.Components = nil
}
x.Header("Connection", "Close")
if code == r.cKO {
x.AbortWithStatusJSON(code, rsp)
} else {
x.JSON(code, rsp)
}
}
func (r *rtrStatus) ComponentKeys() []string {
var l = make([]string, 0)
r.m.Lock()
defer r.m.Unlock()
for k := range r.c {
if len(k) > 0 {
l = append(l, k)
}
}
return l
}
func (r *rtrStatus) ComponentGet(key string) Component {
var (
v *atomic.Value
i interface{}
o Component
ok bool
)
if v, ok = r.c[key]; !ok || v == nil {
return nil
} else if i = v.Load(); i == nil {
return nil
} else if o, ok = i.(Component); !ok {
return nil
} else {
return o
}
}