mirror of
https://github.com/nabbar/golib.git
synced 2025-10-16 21:00:48 +08:00
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:

committed by
Nicolas JUHEL

parent
6ff86118fe
commit
39948566e3
@@ -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 {
|
||||
|
@@ -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,
|
||||
|
@@ -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
11
go.mod
@@ -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
|
||||
|
@@ -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,
|
||||
|
@@ -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 {
|
||||
|
@@ -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
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
}
|
||||
|
||||
|
@@ -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)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@@ -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
|
||||
|
@@ -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() {
|
||||
|
@@ -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,
|
||||
|
163
status/model.go
163
status/model.go
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user