Package Status :

- optimize output writer block for Get function
- add query string params online to print all result as one line plain text format

Package logger :
- refactor closer function to specific interface
- refactor syslog linux to log message by severity instead of same severity for all message

Other :
- Bump dependencies
This commit is contained in:
nabbar
2022-11-10 15:11:23 +01:00
parent 284521c769
commit 3402a00f21
11 changed files with 339 additions and 155 deletions

24
go.mod
View File

@@ -29,11 +29,11 @@ require (
github.com/nats-io/jwt/v2 v2.3.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.24.0
github.com/onsi/ginkgo/v2 v2.5.0
github.com/onsi/gomega v1.24.1
github.com/pelletier/go-toml v1.9.5
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.13.1
github.com/prometheus/client_golang v1.14.0
github.com/shirou/gopsutil v3.21.11+incompatible
github.com/sirupsen/logrus v1.9.0
github.com/spf13/cobra v1.6.1
@@ -44,12 +44,12 @@ require (
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-20221106115401-f9659909a136
golang.org/x/net v0.1.0
golang.org/x/oauth2 v0.1.0
golang.org/x/exp v0.0.0-20221109205753-fc8884afc316
golang.org/x/net v0.2.0
golang.org/x/oauth2 v0.2.0
golang.org/x/sync v0.1.0
golang.org/x/sys v0.1.0
golang.org/x/term v0.1.0
golang.org/x/sys v0.2.0
golang.org/x/term v0.2.0
gopkg.in/yaml.v3 v3.0.1
gorm.io/driver/clickhouse v0.5.0
gorm.io/driver/mysql v1.4.3
@@ -190,11 +190,11 @@ require (
github.com/yusufpapurcu/wmi v1.2.2 // indirect
go.opentelemetry.io/otel v1.11.1 // indirect
go.opentelemetry.io/otel/trace v1.11.1 // indirect
golang.org/x/crypto v0.1.0 // indirect
golang.org/x/mod v0.6.0 // indirect
golang.org/x/crypto v0.2.0 // indirect
golang.org/x/mod v0.7.0 // indirect
golang.org/x/text v0.4.0 // indirect
golang.org/x/time v0.1.0 // indirect
golang.org/x/tools v0.2.0 // indirect
golang.org/x/time v0.2.0 // indirect
golang.org/x/tools v0.3.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect

116
logger/closer.go Normal file
View File

@@ -0,0 +1,116 @@
/***********************************************************************************************************************
*
* MIT License
*
* Copyright (c) 2021 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
**********************************************************************************************************************/
package logger
import (
"fmt"
"io"
"strings"
"sync"
)
type _Closer interface {
Add(clo io.Closer)
Get() []io.Closer
Len() int
Clean()
Clone() _Closer
Close() error
}
func _NewCloser() _Closer {
return &closer{
m: sync.Mutex{},
c: make([]io.Closer, 0),
}
}
type closer struct {
m sync.Mutex
c []io.Closer
}
func (c *closer) Add(clo io.Closer) {
lst := append(c.Get(), clo)
c.m.Lock()
defer c.m.Unlock()
c.c = lst
}
func (c *closer) Get() []io.Closer {
res := make([]io.Closer, 0)
c.m.Lock()
defer c.m.Unlock()
if len(c.c) > 0 {
for _, i := range c.c {
if i == nil {
continue
}
res = append(res, i)
}
}
return res
}
func (c *closer) Len() int {
c.m.Lock()
defer c.m.Unlock()
return len(c.c)
}
func (c *closer) Clean() {
c.m.Lock()
defer c.m.Unlock()
c.c = make([]io.Closer, 0)
}
func (c *closer) Clone() _Closer {
o := _NewCloser()
for _, i := range c.Get() {
o.Add(i)
}
return o
}
func (c *closer) Close() error {
var e = make([]string, 0)
for _, i := range c.Get() {
if err := i.Close(); err != nil {
e = append(e, err.Error())
}
}
return fmt.Errorf("%s", strings.Join(e, ", "))
}

View File

@@ -31,6 +31,7 @@ import (
"fmt"
"io"
"strings"
"sync"
"github.com/sirupsen/logrus"
)
@@ -55,13 +56,24 @@ type syslogWrapper interface {
type FuncFormatter func() logrus.Formatter
type _HookSyslog struct {
m sync.Mutex
w syslogWrapper
f logrus.Formatter
l []logrus.Level
s bool
d bool
t bool
a bool
o _HookSyslogOptions
}
type _HookSyslogOptions struct {
Net NetworkType
Hst string
Tag string
// Sev SyslogSeverity
Fac SyslogFacility
Pid bool
Tms bool
Trc bool
Acc bool
}
func NewHookSyslog(opt OptionsSyslog, format logrus.Formatter) (HookSyslog, error) {
@@ -79,19 +91,64 @@ func NewHookSyslog(opt OptionsSyslog, format logrus.Formatter) (HookSyslog, erro
LVLs = logrus.AllLevels
}
if sys, err = newSyslog(MakeNetwork(opt.Network), opt.Host, opt.Tag, MakeSeverity(opt.Severity), MakeFacility(opt.Facility)); err != nil {
return nil, err
}
return &_HookSyslog{
obj := &_HookSyslog{
m: sync.Mutex{},
w: sys,
f: format,
l: LVLs,
s: opt.DisableStack,
d: opt.DisableTimestamp,
t: opt.EnableTrace,
a: opt.EnableAccessLog,
}, nil
o: _HookSyslogOptions{
Net: MakeNetwork(opt.Network),
Hst: opt.Host,
Tag: opt.Tag,
// Sev: MakeSeverity(opt.Severity),
Fac: MakeFacility(opt.Facility),
Pid: opt.DisableStack,
Tms: opt.DisableTimestamp,
Trc: opt.EnableTrace,
Acc: opt.EnableAccessLog,
},
}
if h, e := obj.openCreate(); e != nil {
return nil, e
} else {
_ = h.Close()
}
return obj, err
}
func (o *_HookSyslog) openCreate() (syslogWrapper, error) {
return newSyslog(o.o.Net, o.o.Hst, o.o.Tag, o.o.Fac)
//return newSyslog(o.o.Net, o.o.Hst, o.o.Tag, o.o.Sev, o.o.Fac)
}
func (o *_HookSyslog) isStack() bool {
o.m.Lock()
defer o.m.Unlock()
return o.o.Pid
}
func (o *_HookSyslog) isTimeStamp() bool {
o.m.Lock()
defer o.m.Unlock()
return o.o.Tms
}
func (o *_HookSyslog) isTrace() bool {
o.m.Lock()
defer o.m.Unlock()
return o.o.Trc
}
func (o *_HookSyslog) isAccessLog() bool {
o.m.Lock()
defer o.m.Unlock()
return o.o.Acc
}
func (o *_HookSyslog) RegisterHook(log *logrus.Logger) {
@@ -106,15 +163,15 @@ func (o *_HookSyslog) Fire(entry *logrus.Entry) error {
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)
@@ -125,7 +182,7 @@ func (o *_HookSyslog) 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"
@@ -142,38 +199,59 @@ func (o *_HookSyslog) Fire(entry *logrus.Entry) error {
}
}
switch ent.Level {
case logrus.PanicLevel:
_, e = o.w.Panic(p)
case logrus.FatalLevel:
_, e = o.w.Fatal(p)
case logrus.ErrorLevel:
_, e = o.w.Error(p)
case logrus.WarnLevel:
_, e = o.w.Warning(p)
case logrus.InfoLevel:
_, e = o.w.Info(p)
case logrus.DebugLevel:
_, e = o.w.Debug(p)
default:
return nil
if _, e = o.writeLevel(ent.Level, p); e != nil {
_ = o.Close()
_, e = o.writeLevel(ent.Level, p)
}
return e
}
func (o *_HookSyslog) Write(p []byte) (n int, err error) {
return o.writeLevel(logrus.InfoLevel, p)
}
func (o *_HookSyslog) writeLevel(lvl logrus.Level, p []byte) (n int, err error) {
o.m.Lock()
defer o.m.Unlock()
if o.w == nil {
return 0, fmt.Errorf("logrus.hooksyslog: connection not setup")
var e error
if o.w, e = o.openCreate(); e != nil {
return 0, fmt.Errorf("logrus.hooksyslog: %v", e)
}
}
return o.w.Write(p)
switch lvl {
case logrus.PanicLevel:
return o.w.Panic(p)
case logrus.FatalLevel:
return o.w.Fatal(p)
case logrus.ErrorLevel:
return o.w.Error(p)
case logrus.WarnLevel:
return o.w.Warning(p)
case logrus.InfoLevel:
return o.w.Info(p)
case logrus.DebugLevel:
return o.w.Debug(p)
default:
return o.w.Write(p)
}
}
func (o *_HookSyslog) Close() error {
err := o.w.Close()
o.m.Lock()
defer o.m.Unlock()
var e error
if o.w != nil {
e = o.w.Close()
}
o.w = nil
return err
return e
}
func (o *_HookSyslog) filterKey(f logrus.Fields, key string) logrus.Fields {
@@ -187,16 +265,4 @@ func (o *_HookSyslog) filterKey(f logrus.Fields, key string) logrus.Fields {
delete(f, key)
return f
}
/*
var res = make(map[string]interface{}, 0)
for k, v := range f {
if k == key {
continue
}
res[k] = v
}
return res
*/
}

View File

@@ -137,6 +137,6 @@ func New(ctx context.Context) Logger {
s: new(atomic.Value),
f: new(atomic.Value),
w: new(atomic.Value),
c: new(atomic.Value),
c: _NewCloser(),
}
}

View File

@@ -30,20 +30,14 @@ package logger
import "sync/atomic"
func (l *logger) Close() error {
lst := l.closeGet()
l.m.Lock()
defer func() {
l.m.Unlock()
l.closeClean()
l.cancelCall()
}()
for _, c := range lst {
if c != nil {
_ = c.Close()
}
}
_ = l.c.Close()
l.c = _NewCloser()
return nil
}

View File

@@ -30,7 +30,6 @@ package logger
import (
"bytes"
"context"
"io"
"io/ioutil"
"path"
"reflect"
@@ -60,7 +59,7 @@ type logger struct {
s *atomic.Value //logrus logger
f *atomic.Value //defaults fields
w *atomic.Value //io writer level
c *atomic.Value
c _Closer // closer
}
func defaultFormatter() logrus.TextFormatter {
@@ -106,57 +105,6 @@ func (l *logger) defaultFormatterNoColor() logrus.Formatter {
return &f
}
func (l *logger) closeAdd(clo io.Closer) {
lst := append(l.closeGet(), clo)
l.m.Lock()
defer l.m.Unlock()
if l.c == nil {
l.c = new(atomic.Value)
}
l.c.Store(lst)
}
func (l *logger) closeClean() {
if l == nil {
return
}
l.m.Lock()
defer l.m.Unlock()
if l.c == nil {
l.c = new(atomic.Value)
}
l.c.Store(make([]io.Closer, 0))
}
func (l *logger) closeGet() []io.Closer {
res := make([]io.Closer, 0)
if l == nil {
return res
}
l.m.Lock()
defer l.m.Unlock()
if l.c == nil {
l.c = new(atomic.Value)
}
if i := l.c.Load(); i == nil {
return res
} else if o, ok := i.([]io.Closer); ok {
return o
}
return res
}
func (l *logger) Clone() (Logger, error) {
c := &logger{
x: l.contextGet(),
@@ -167,7 +115,7 @@ func (l *logger) Clone() (Logger, error) {
s: new(atomic.Value),
f: new(atomic.Value),
w: new(atomic.Value),
c: new(atomic.Value),
c: _NewCloser(),
}
c.setLoggerMutex(l.GetLevel())
@@ -308,13 +256,8 @@ func (l *logger) GetFields() Fields {
}
func (l *logger) setOptionsMutex(opt *Options) error {
l.setOptions(opt)
opt = l.GetOptions()
lvl := l.GetLevel()
_ = l.Close()
go func() {
var ctx = l.contextNew()
@@ -333,6 +276,7 @@ func (l *logger) setOptionsMutex(opt *Options) error {
obj.SetLevel(lvl.Logrus())
obj.SetFormatter(l.defaultFormatter(opt))
obj.SetOutput(ioutil.Discard) // Send all logs to nowhere by default
clo := _NewCloser()
if !opt.DisableStandard {
obj.AddHook(NewHookStandard(*opt, StdOut, []logrus.Level{
@@ -354,7 +298,7 @@ func (l *logger) setOptionsMutex(opt *Options) error {
if hook, err := NewHookFile(fopt, l.defaultFormatterNoColor()); err != nil {
return err
} else {
l.closeAdd(hook)
clo.Add(hook)
hook.RegisterHook(obj)
}
}
@@ -365,12 +309,20 @@ func (l *logger) setOptionsMutex(opt *Options) error {
if hook, err := NewHookSyslog(lopt, l.defaultFormatterNoColor()); err != nil {
return err
} else {
l.closeAdd(hook)
clo.Add(hook)
hook.RegisterHook(obj)
}
}
}
l.setOptions(opt)
l.m.Lock()
defer l.m.Unlock()
_ = l.c.Close()
l.c = clo
l.s = new(atomic.Value)
l.s.Store(obj)

View File

@@ -109,10 +109,10 @@ type OptionsSyslog struct {
// Host define the remote syslog to use.
// If Host and Network are empty, local syslog will be used.
Host string `json:"host,omitempty" yaml:"host,omitempty" toml:"host,omitempty" mapstructure:"host,omitempty"`
// Severity define the severity syslog to be used.
Severity string `json:"severity,omitempty" yaml:"severity,omitempty" toml:"severity,omitempty" mapstructure:"severity,omitempty"`
/*
// Severity define the severity syslog to be used.
Severity string `json:"severity,omitempty" yaml:"severity,omitempty" toml:"severity,omitempty" mapstructure:"severity,omitempty"`
*/
// Facility define the facility syslog to be used.
Facility string `json:"facility,omitempty" yaml:"facility,omitempty" toml:"facility,omitempty" mapstructure:"facility,omitempty"`

View File

@@ -111,55 +111,86 @@ type _Syslog struct {
w *syslog.Writer
}
func newSyslog(net NetworkType, host, tag string, severity SyslogSeverity, facility SyslogFacility) (syslogWrapper, error) {
func newSyslog(net NetworkType, host, tag string, fac SyslogFacility) (syslogWrapper, error) {
var (
sys *syslog.Writer
err error
)
if sys, err = syslog.Dial(net.String(), host, makePriority(severity, facility), tag); err != nil {
var obj = &_Syslog{
w: nil,
}
if obj.w, err = obj.openSyslogSev(net, host, tag, makePriority(SyslogSeverityInfo, fac)); err != nil {
_ = obj.Close()
return nil, err
}
return &_Syslog{
w: sys,
}, nil
return obj, nil
}
func (o *_Syslog) openSyslogSev(net NetworkType, host, tag string, prio syslog.Priority) (*syslog.Writer, error) {
return syslog.Dial(net.String(), host, prio, tag)
}
func (o *_Syslog) Write(p []byte) (n int, err error) {
return o.WriteSev(SyslogSeverityInfo, p)
}
func (o *_Syslog) WriteSev(sev SyslogSeverity, p []byte) (n int, err error) {
if o.w == nil {
return 0, fmt.Errorf("logrus.hooksyslog: connection not setup")
}
switch sev {
case SyslogSeverityEmerg:
return len(p), o.w.Emerg(string(p))
case SyslogSeverityAlert:
return len(p), o.w.Alert(string(p))
case SyslogSeverityCrit:
return len(p), o.w.Crit(string(p))
case SyslogSeverityErr:
return len(p), o.w.Err(string(p))
case SyslogSeverityWarning:
return len(p), o.w.Warning(string(p))
case SyslogSeverityNotice:
return len(p), o.w.Notice(string(p))
case SyslogSeverityInfo:
return len(p), o.w.Info(string(p))
case SyslogSeverityDebug:
return len(p), o.w.Debug(string(p))
}
return o.w.Write(p)
}
func (o *_Syslog) Close() error {
err := o.w.Close()
o.w = nil
return err
if o.w == nil {
return nil
}
return o.w.Close()
}
func (o *_Syslog) Panic(p []byte) (n int, err error) {
return o.Write(p)
return o.WriteSev(SyslogSeverityAlert, p)
}
func (o *_Syslog) Fatal(p []byte) (n int, err error) {
return o.Write(p)
return o.WriteSev(SyslogSeverityCrit, p)
}
func (o *_Syslog) Error(p []byte) (n int, err error) {
return o.Write(p)
return o.WriteSev(SyslogSeverityErr, p)
}
func (o *_Syslog) Warning(p []byte) (n int, err error) {
return o.Write(p)
return o.WriteSev(SyslogSeverityWarning, p)
}
func (o *_Syslog) Info(p []byte) (n int, err error) {
return o.Write(p)
return o.WriteSev(SyslogSeverityInfo, p)
}
func (o *_Syslog) Debug(p []byte) (n int, err error) {
return o.Write(p)
return o.WriteSev(SyslogSeverityDebug, p)
}

View File

@@ -125,7 +125,7 @@ type _WinLog struct {
w *eventlog.Log
}
func newSyslog(net NetworkType, host, tag string, severity SyslogSeverity, facility SyslogFacility) (syslogWrapper, error) {
func newSyslog(net NetworkType, host, tag string, facility SyslogFacility) (syslogWrapper, error) {
var (
sys *eventlog.Log
err error

View File

@@ -2,6 +2,11 @@
This package help to manage status router in a API to respond a standard response for status of API and his component.
This package requires `golib/router` + go Gin Tonic API Framework.
This package also include 2 option of call that can be passed into query string :
- `short` : if use, the response will only include the main status and no one component, but all health are still check
- `online` : if use, the response will be into a list of text line composed as `status: name (release - build) - message`, instead of a JSON output
This 2 options call be use together.
## Example of implementation
We will work on an example of file/folder tree like this :
```bash

View File

@@ -26,12 +26,16 @@
package status
import (
"bytes"
"fmt"
"net/http"
"path"
"strings"
"sync"
"sync/atomic"
"github.com/gin-gonic/gin/render"
"github.com/gin-gonic/gin"
liberr "github.com/nabbar/golib/errors"
liblog "github.com/nabbar/golib/logger"
@@ -57,7 +61,10 @@ type rtrStatus struct {
c map[string]*atomic.Value
}
const keyShortOutput = "short"
const (
keyShortOutput = "short"
keyOneLineOutput = "oneline"
)
func (r *rtrStatus) HttpStatusCode(codeOk, codeKO, codeWarning int) {
r.cOk = codeOk
@@ -204,17 +211,30 @@ func (r *rtrStatus) Get(x *gin.Context) {
}
if x.Request.URL.Query().Has(keyShortOutput) {
rsp.Components = nil
rsp.Components = make([]CptResponse, 0)
}
x.Header("Connection", "Close")
if code == r.cKO {
x.AbortWithStatusJSON(code, rsp)
x.Abort()
}
if x.Request.URL.Query().Has(keyOneLineOutput) {
var buf = bytes.NewBuffer(make([]byte, 0))
buf.WriteString(fmt.Sprintf("%s: %s (%s - %s) : %s\n", rsp.Status, rsp.Name, rsp.Release, rsp.HashBuild, rsp.Message))
for _, c := range rsp.Components {
buf.WriteString(fmt.Sprintf("%s: %s (%s - %s) : %s\n", c.Status, c.Name, c.Release, c.HashBuild, c.Message))
}
x.Render(code, render.Data{
ContentType: gin.MIMEPlain,
Data: buf.Bytes(),
})
} else {
x.JSON(code, rsp)
}
}
func (r *rtrStatus) ComponentKeys() []string {