diff --git a/logger/hookfile.go b/logger/hookfile.go index 0621c09..c950fd3 100644 --- a/logger/hookfile.go +++ b/logger/hookfile.go @@ -44,13 +44,14 @@ type HookFile interface { type _HookFile struct { f *os.File + r logrus.Formatter l []logrus.Level s bool d bool t bool } -func NewHookFile(opt OptionsFile) (HookFile, error) { +func NewHookFile(opt OptionsFile, format logrus.Formatter) (HookFile, error) { if opt.Filepath == "" { return nil, fmt.Errorf("missing file path") } @@ -92,6 +93,7 @@ func NewHookFile(opt OptionsFile) (HookFile, error) { return &_HookFile{ f: hdl, + r: format, l: LVLs, s: opt.DisableStack, d: opt.DisableTimestamp, @@ -125,7 +127,7 @@ func (o *_HookFile) Fire(entry *logrus.Entry) error { ent.Data = o.filterKey(ent.Data, FieldLine) } - if p, err := ent.Bytes(); err != nil { + if p, err := o.r.Format(ent); err != nil { return err } else if _, err = o.Write(p); err != nil { return err diff --git a/logger/hookstandard.go b/logger/hookstandard.go index 147aa2a..dfd3518 100644 --- a/logger/hookstandard.go +++ b/logger/hookstandard.go @@ -30,10 +30,20 @@ package logger import ( "fmt" "io" + "os" + + "github.com/mattn/go-colorable" "github.com/sirupsen/logrus" ) +type StdWriter uint8 + +const ( + StdOut StdWriter = iota + StdErr +) + type HookStandard interface { logrus.Hook io.WriteCloser @@ -48,11 +58,29 @@ type _HookStd struct { t bool // Disable Trace } -func NewHookStandard(opt Options, w io.Writer, lvls []logrus.Level) HookFile { +func NewHookStandard(opt Options, s StdWriter, lvls []logrus.Level) HookFile { if len(lvls) < 1 { lvls = logrus.AllLevels } + var w io.Writer + + if opt.DisableColor { + switch s { + case StdErr: + w = os.Stderr + default: + w = os.Stdout + } + } else { + switch s { + case StdErr: + w = colorable.NewColorableStderr() + default: + w = colorable.NewColorableStderr() + } + } + return &_HookStd{ w: w, l: lvls, diff --git a/logger/hooksyslog.go b/logger/hooksyslog.go index 6f98c78..d09607b 100644 --- a/logger/hooksyslog.go +++ b/logger/hooksyslog.go @@ -30,7 +30,6 @@ package logger import ( "fmt" "io" - "log/syslog" "github.com/sirupsen/logrus" ) @@ -41,18 +40,32 @@ type HookSyslog interface { RegisterHook(log *logrus.Logger) } +type syslogWrapper interface { + io.WriteCloser + + Panic(p []byte) (n int, err error) + Fatal(p []byte) (n int, err error) + Error(p []byte) (n int, err error) + Warning(p []byte) (n int, err error) + Info(p []byte) (n int, err error) + Debug(p []byte) (n int, err error) +} + +type FuncFormatter func() logrus.Formatter + type _HookSyslog struct { - w *syslog.Writer + w syslogWrapper + f logrus.Formatter l []logrus.Level s bool d bool t bool } -func NewHookSyslog(opt OptionsSyslog) (HookSyslog, error) { +func NewHookSyslog(opt OptionsSyslog, format logrus.Formatter) (HookSyslog, error) { var ( LVLs = make([]logrus.Level, 0) - sys *syslog.Writer + sys syslogWrapper err error ) @@ -64,12 +77,13 @@ func NewHookSyslog(opt OptionsSyslog) (HookSyslog, error) { LVLs = logrus.AllLevels } - if sys, err = syslog.Dial(opt.Network.String(), opt.Host, opt.Priority, opt.Tag); err != nil { + if sys, err = newSyslog(MakeNetwork(opt.Network), opt.Host, opt.Tag, MakeSeverity(opt.Severity), MakeFacility(opt.Facility)); err != nil { return nil, err } return &_HookSyslog{ w: sys, + f: format, l: LVLs, s: opt.DisableStack, d: opt.DisableTimestamp, @@ -103,13 +117,28 @@ func (o *_HookSyslog) Fire(entry *logrus.Entry) error { ent.Data = o.filterKey(ent.Data, FieldLine) } - if p, err := ent.Bytes(); err != nil { + if p, err := o.f.Format(ent); err != nil { return err - } else if _, err = o.Write(p); err != nil { + } else { + switch ent.Level { + case logrus.PanicLevel: + _, err = o.w.Panic(p) + case logrus.FatalLevel: + _, err = o.w.Fatal(p) + case logrus.ErrorLevel: + _, err = o.w.Error(p) + case logrus.WarnLevel: + _, err = o.w.Warning(p) + case logrus.InfoLevel: + _, err = o.w.Info(p) + case logrus.DebugLevel: + _, err = o.w.Debug(p) + default: + return nil + } + return err } - - return nil } func (o *_HookSyslog) Write(p []byte) (n int, err error) { diff --git a/logger/model.go b/logger/model.go index 0c8d827..c356b2a 100644 --- a/logger/model.go +++ b/logger/model.go @@ -32,7 +32,6 @@ import ( "context" "io" "io/ioutil" - "os" "path" "reflect" "runtime" @@ -42,8 +41,6 @@ import ( "sync/atomic" "time" - "github.com/mattn/go-colorable" - "github.com/sirupsen/logrus" ) @@ -66,14 +63,10 @@ type logger struct { c *atomic.Value } -func (l *logger) defaultFormatter(opt *Options) *logrus.TextFormatter { - if opt == nil { - opt = &Options{} - } - - return &logrus.TextFormatter{ - ForceColors: true, - DisableColors: opt.DisableColor, +func defaultFormatter() logrus.TextFormatter { + return logrus.TextFormatter{ + ForceColors: false, + DisableColors: false, ForceQuote: false, DisableQuote: false, EnvironmentOverrideColors: false, @@ -90,6 +83,29 @@ func (l *logger) defaultFormatter(opt *Options) *logrus.TextFormatter { } } +func (l *logger) defaultFormatter(opt *Options) logrus.Formatter { + f := defaultFormatter() + + if opt != nil && opt.DisableColor { + f.ForceColors = false + f.EnvironmentOverrideColors = false + f.DisableColors = true + } else { + f.ForceColors = true + f.DisableColors = false + } + + return &f +} + +func (l *logger) defaultFormatterNoColor() logrus.Formatter { + f := defaultFormatter() + f.ForceColors = false + f.EnvironmentOverrideColors = false + f.DisableColors = true + return &f +} + func (l *logger) closeAdd(clo io.Closer) { lst := append(l.closeGet(), clo) @@ -259,23 +275,13 @@ func (l *logger) setOptionsMutex(ctx context.Context, opt *Options) error { obj.SetOutput(ioutil.Discard) // Send all logs to nowhere by default if !opt.DisableStandard { - var ( - o io.Writer = os.Stdout - e io.Writer = os.Stderr - ) - - if opt.DisableColor { - o = colorable.NewColorableStdout() - e = colorable.NewColorableStderr() - } - - obj.AddHook(NewHookStandard(*opt, o, []logrus.Level{ + obj.AddHook(NewHookStandard(*opt, StdOut, []logrus.Level{ logrus.InfoLevel, logrus.DebugLevel, logrus.TraceLevel, })) - obj.AddHook(NewHookStandard(*opt, e, []logrus.Level{ + obj.AddHook(NewHookStandard(*opt, StdErr, []logrus.Level{ logrus.PanicLevel, logrus.FatalLevel, logrus.ErrorLevel, @@ -285,7 +291,7 @@ func (l *logger) setOptionsMutex(ctx context.Context, opt *Options) error { if len(opt.LogFile) > 0 { for _, fopt := range opt.LogFile { - if hook, err := NewHookFile(fopt); err != nil { + if hook, err := NewHookFile(fopt, l.defaultFormatterNoColor()); err != nil { return err } else { l.closeAdd(hook) @@ -296,7 +302,7 @@ func (l *logger) setOptionsMutex(ctx context.Context, opt *Options) error { if len(opt.LogSyslog) > 0 { for _, lopt := range opt.LogSyslog { - if hook, err := NewHookSyslog(lopt); err != nil { + if hook, err := NewHookSyslog(lopt, l.defaultFormatterNoColor()); err != nil { return err } else { l.closeAdd(hook) diff --git a/logger/options.go b/logger/options.go index d89633c..167538a 100644 --- a/logger/options.go +++ b/logger/options.go @@ -28,8 +28,8 @@ package logger import ( - "log/syslog" "os" + "strings" ) type FuncCustomConfig func(log Logger) @@ -53,6 +53,17 @@ func (n NetworkType) String() string { } } +func MakeNetwork(net string) NetworkType { + switch strings.ToLower(net) { + case NetworkTCP.String(): + return NetworkTCP + case NetworkUDP.String(): + return NetworkUDP + default: + return NetworkEmpty + } +} + type OptionsFile struct { // LogLevel define the allowed level of log for this file. LogLevel []string @@ -86,17 +97,21 @@ type OptionsSyslog struct { // LogLevel define the allowed level of log for this syslog. LogLevel []string - // Network define the network used to connect to this syslog. - Network NetworkType + // Network define the network used to connect to this syslog (tcp, udp, or any other to a local connection). + Network string // Host define the remote syslog to use. // If Host and Network are empty, local syslog will be used. Host string - // Priority define the priority used for this syslog. - Priority syslog.Priority + // Severity define the severity syslog to be used. + Severity string - // Tag define the syslog tag used for log message. + // Facility define the facility syslog to be used. + Facility string + + // Tag define the syslog tag used in linux syslog system or name of logger for windows event logger. + // For window, this value must be unic for each syslog config Tag string // DisableStack allow to disable the goroutine id before each message. diff --git a/logger/sys_priority.go b/logger/sys_priority.go new file mode 100644 index 0000000..ce02c1d --- /dev/null +++ b/logger/sys_priority.go @@ -0,0 +1,208 @@ +/*********************************************************************************************************************** + * + * 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 "strings" + +type SyslogSeverity uint8 + +const ( + SyslogSeverityEmerg SyslogSeverity = iota + 1 + SyslogSeverityAlert + SyslogSeverityCrit + SyslogSeverityErr + SyslogSeverityWarning + SyslogSeverityNotice + SyslogSeverityInfo + SyslogSeverityDebug +) + +func (s SyslogSeverity) String() string { + switch s { + case SyslogSeverityEmerg: + return "EMERG" + case SyslogSeverityAlert: + return "ALERT" + case SyslogSeverityCrit: + return "CRIT" + case SyslogSeverityErr: + return "ERR" + case SyslogSeverityWarning: + return "WARNING" + case SyslogSeverityNotice: + return "NOTICE" + case SyslogSeverityInfo: + return "INFO" + case SyslogSeverityDebug: + return "DEBUG" + } + + return "" +} + +func MakeSeverity(severity string) SyslogSeverity { + switch strings.ToUpper(severity) { + case SyslogSeverityEmerg.String(): + return SyslogSeverityEmerg + case SyslogSeverityAlert.String(): + return SyslogSeverityAlert + case SyslogSeverityCrit.String(): + return SyslogSeverityCrit + case SyslogSeverityErr.String(): + return SyslogSeverityErr + case SyslogSeverityWarning.String(): + return SyslogSeverityWarning + case SyslogSeverityNotice.String(): + return SyslogSeverityNotice + case SyslogSeverityInfo.String(): + return SyslogSeverityInfo + case SyslogSeverityDebug.String(): + return SyslogSeverityDebug + } + + return 0 +} + +type SyslogFacility uint8 + +const ( + SyslogFacilityKern SyslogFacility = iota + 1 + SyslogFacilityUser + SyslogFacilityMail + SyslogFacilityDaemon + SyslogFacilityAuth + SyslogFacilitySyslog + SyslogFacilityLpr + SyslogFacilityNews + SyslogFacilityUucp + SyslogFacilityCron + SyslogFacilityAuthPriv + SyslogFacilityFTP + SyslogFacilityLocal0 + SyslogFacilityLocal1 + SyslogFacilityLocal2 + SyslogFacilityLocal3 + SyslogFacilityLocal4 + SyslogFacilityLocal5 + SyslogFacilityLocal6 + SyslogFacilityLocal7 +) + +func (s SyslogFacility) String() string { + switch s { + case SyslogFacilityKern: + return "KERN" + case SyslogFacilityUser: + return "USER" + case SyslogFacilityMail: + return "MAIL" + case SyslogFacilityDaemon: + return "DAEMON" + case SyslogFacilityAuth: + return "AUTH" + case SyslogFacilitySyslog: + return "SYSLOG" + case SyslogFacilityLpr: + return "LPR" + case SyslogFacilityNews: + return "NEWS" + case SyslogFacilityUucp: + return "UUCP" + case SyslogFacilityCron: + return "CRON" + case SyslogFacilityAuthPriv: + return "AUTHPRIV" + case SyslogFacilityFTP: + return "FTP" + case SyslogFacilityLocal0: + return "LOCAL0" + case SyslogFacilityLocal1: + return "LOCAL1" + case SyslogFacilityLocal2: + return "LOCAL2" + case SyslogFacilityLocal3: + return "LOCAL3" + case SyslogFacilityLocal4: + return "LOCAL4" + case SyslogFacilityLocal5: + return "LOCAL5" + case SyslogFacilityLocal6: + return "LOCAL6" + case SyslogFacilityLocal7: + return "LOCAL7" + } + + return "" +} + +func MakeFacility(facility string) SyslogFacility { + switch strings.ToUpper(facility) { + case SyslogFacilityKern.String(): + return SyslogFacilityKern + case SyslogFacilityUser.String(): + return SyslogFacilityUser + case SyslogFacilityMail.String(): + return SyslogFacilityMail + case SyslogFacilityDaemon.String(): + return SyslogFacilityDaemon + case SyslogFacilityAuth.String(): + return SyslogFacilityAuth + case SyslogFacilitySyslog.String(): + return SyslogFacilitySyslog + case SyslogFacilityLpr.String(): + return SyslogFacilityLpr + case SyslogFacilityNews.String(): + return SyslogFacilityNews + case SyslogFacilityUucp.String(): + return SyslogFacilityUucp + case SyslogFacilityCron.String(): + return SyslogFacilityCron + case SyslogFacilityAuthPriv.String(): + return SyslogFacilityAuthPriv + case SyslogFacilityFTP.String(): + return SyslogFacilityFTP + case SyslogFacilityLocal0.String(): + return SyslogFacilityLocal0 + case SyslogFacilityLocal1.String(): + return SyslogFacilityLocal1 + case SyslogFacilityLocal2.String(): + return SyslogFacilityLocal2 + case SyslogFacilityLocal3.String(): + return SyslogFacilityLocal3 + case SyslogFacilityLocal4.String(): + return SyslogFacilityLocal4 + case SyslogFacilityLocal5.String(): + return SyslogFacilityLocal5 + case SyslogFacilityLocal6.String(): + return SyslogFacilityLocal6 + case SyslogFacilityLocal7.String(): + return SyslogFacilityLocal7 + } + + return 0 +} diff --git a/logger/sys_syslog.go b/logger/sys_syslog.go new file mode 100644 index 0000000..71c5ccd --- /dev/null +++ b/logger/sys_syslog.go @@ -0,0 +1,164 @@ +// +build !windows + +/*********************************************************************************************************************** + * + * 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" + "log/syslog" +) + +func makePriority(severity SyslogSeverity, facility SyslogFacility) syslog.Priority { + return makePriorotySeverity(severity) | makePriorotyFacility(facility) +} + +func makePriorotySeverity(sev SyslogSeverity) syslog.Priority { + switch sev { + case SyslogSeverityEmerg: + return syslog.LOG_EMERG + case SyslogSeverityAlert: + return syslog.LOG_ALERT + case SyslogSeverityCrit: + return syslog.LOG_CRIT + case SyslogSeverityErr: + return syslog.LOG_ERR + case SyslogSeverityWarning: + return syslog.LOG_WARNING + case SyslogSeverityNotice: + return syslog.LOG_NOTICE + case SyslogSeverityInfo: + return syslog.LOG_INFO + case SyslogSeverityDebug: + return syslog.LOG_DEBUG + } + return 0 +} + +func makePriorotyFacility(fac SyslogFacility) syslog.Priority { + switch fac { + case SyslogFacilityKern: + return syslog.LOG_KERN + case SyslogFacilityUser: + return syslog.LOG_USER + case SyslogFacilityMail: + return syslog.LOG_MAIL + case SyslogFacilityDaemon: + return syslog.LOG_DAEMON + case SyslogFacilityAuth: + return syslog.LOG_AUTH + case SyslogFacilitySyslog: + return syslog.LOG_SYSLOG + case SyslogFacilityLpr: + return syslog.LOG_LPR + case SyslogFacilityNews: + return syslog.LOG_NEWS + case SyslogFacilityUucp: + return syslog.LOG_UUCP + case SyslogFacilityCron: + return syslog.LOG_CRON + case SyslogFacilityAuthPriv: + return syslog.LOG_AUTHPRIV + case SyslogFacilityFTP: + return syslog.LOG_FTP + case SyslogFacilityLocal0: + return syslog.LOG_LOCAL0 + case SyslogFacilityLocal1: + return syslog.LOG_LOCAL1 + case SyslogFacilityLocal2: + return syslog.LOG_LOCAL2 + case SyslogFacilityLocal3: + return syslog.LOG_LOCAL3 + case SyslogFacilityLocal4: + return syslog.LOG_LOCAL4 + case SyslogFacilityLocal5: + return syslog.LOG_LOCAL5 + case SyslogFacilityLocal6: + return syslog.LOG_LOCAL6 + case SyslogFacilityLocal7: + return syslog.LOG_LOCAL7 + } + return 0 +} + +type _Syslog struct { + w *syslog.Writer +} + +func newSyslog(net NetworkType, host, tag string, severity SyslogSeverity, facility SyslogFacility) (syslogWrapper, error) { + var ( + sys *syslog.Writer + err error + ) + + if sys, err = syslog.Dial(net.String(), host, makePriority(severity, facility), tag); err != nil { + return nil, err + } + + return &_Syslog{ + w: sys, + }, nil +} + +func (o *_Syslog) Write(p []byte) (n int, err error) { + if o.w == nil { + return 0, fmt.Errorf("logrus.hooksyslog: connection not setup") + } + + return o.w.Write(p) +} + +func (o *_Syslog) Close() error { + err := o.w.Close() + o.w = nil + return err +} + +func (o *_Syslog) Panic(p []byte) (n int, err error) { + return o.Write(p) +} + +func (o *_Syslog) Fatal(p []byte) (n int, err error) { + return o.Write(p) +} + +func (o *_Syslog) Error(p []byte) (n int, err error) { + return o.Write(p) +} + +func (o *_Syslog) Warning(p []byte) (n int, err error) { + return o.Write(p) +} + +func (o *_Syslog) Info(p []byte) (n int, err error) { + return o.Write(p) +} + +func (o *_Syslog) Debug(p []byte) (n int, err error) { + return o.Write(p) +} diff --git a/logger/sys_winlog.go b/logger/sys_winlog.go new file mode 100644 index 0000000..326765c --- /dev/null +++ b/logger/sys_winlog.go @@ -0,0 +1,196 @@ +// +build windows + +/*********************************************************************************************************************** + * + * 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 ( + "sync/atomic" + "time" + + "golang.org/x/sys/windows/svc/eventlog" +) + +var _registred *atomic.Value + +func init() { + // register a function to clean event on stopping application to clean Windows Registry Database + go func() { + // clean all registered event + defer func() { + if _registred == nil { + _registred = new(atomic.Value) + } + + var ( + i interface{} + o []string + ok bool + ) + + if i = _registred.Load(); i == nil { + i = make([]string, 0) + } + + if o, ok = i.([]string); !ok { + o = make([]string, 0) + } + + _registred.Store(make([]string, 0)) + + for _, s := range o { + if err := eventlog.Remove(s); err != nil { + println(err) + } + } + }() + + for { + // only wait the stopping process + time.Sleep(200 * time.Millisecond) + } + }() +} + +func windowsRegister(source string) error { + if _registred == nil { + _registred = new(atomic.Value) + } + + var ( + i interface{} + o []string + ok bool + ) + + if i = _registred.Load(); i == nil { + i = make([]string, 0) + } + + if o, ok = i.([]string); !ok { + o = make([]string, 0) + } + + for _, s := range o { + if s == source { + return nil + } + } + + if err := eventlog.InstallAsEventCreate(source, eventlog.Error|eventlog.Warning|eventlog.Info); err != nil { + return err + } + + o = append(o, source) + _registred.Store(o) + + return nil +} + +const ( + _ErrorId uint32 = iota + 1 + _WarningId + _InfoId +) + +type _WinLog struct { + r bool + s string + w *eventlog.Log +} + +func newSyslog(net NetworkType, host, tag string, severity SyslogSeverity, facility SyslogFacility) (syslogWrapper, error) { + var ( + sys *eventlog.Log + err error + ) + + if net != NetworkEmpty { + sys, err = eventlog.OpenRemote(host, tag) + } else { + if err = windowsRegister(tag); err != nil { + println(err.Error()) + } + + sys, err = eventlog.Open(tag) + } + + if err != nil { + return nil, err + } + + return &_WinLog{ + r: net != NetworkEmpty, + s: tag, + w: sys, + }, nil +} + +func (o *_WinLog) Close() error { + var err error + + err = o.w.Close() + o.w = nil + + if err != nil { + return err + } + + return nil +} + +func (o *_WinLog) Write(p []byte) (n int, err error) { + return o.Info(p) +} + +func (o *_WinLog) Panic(p []byte) (n int, err error) { + return o.Error(p) +} + +func (o *_WinLog) Fatal(p []byte) (n int, err error) { + return o.Error(p) +} + +func (o *_WinLog) Error(p []byte) (n int, err error) { + err = o.w.Error(_ErrorId*100, string(p)) + return len(p), err +} + +func (o *_WinLog) Warning(p []byte) (n int, err error) { + err = o.w.Warning(_WarningId*100, string(p)) + return len(p), err +} + +func (o *_WinLog) Info(p []byte) (n int, err error) { + err = o.w.Info(_InfoId*100, string(p)) + return len(p), err +} + +func (o *_WinLog) Debug(p []byte) (n int, err error) { + return o.Info(p) +} diff --git a/test/test-httpserver-syslog/main.go b/test/test-httpserver-syslog/main.go new file mode 100644 index 0000000..0afd12c --- /dev/null +++ b/test/test-httpserver-syslog/main.go @@ -0,0 +1,202 @@ +/*********************************************************************************************************************** + * + * 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 main + +import ( + "context" + "fmt" + "net/http" + "time" + + liblog "github.com/nabbar/golib/logger" + + libtls "github.com/nabbar/golib/certificates" + liberr "github.com/nabbar/golib/errors" + libsrv "github.com/nabbar/golib/httpserver" +) + +var tlsConfigSrv = libtls.Config{ + InheritDefault: true, + VersionMin: "1.2", +} + +var cfgSrv01 = libsrv.ServerConfig{ + Name: "test-01", + Listen: "0.0.0.0:61001", + Expose: "0.0.0.0:61000", + TLSMandatory: false, + TLS: tlsConfigSrv, +} + +var cfgSrv02 = libsrv.ServerConfig{ + Name: "test-02", + Listen: "0.0.0.0:61002", + Expose: "0.0.0.0:61000", + TLSMandatory: false, + TLS: tlsConfigSrv, +} + +var cfgSrv03 = libsrv.ServerConfig{ + Name: "test-03", + Listen: "0.0.0.0:61003", + Expose: "0.0.0.0:61000", + TLSMandatory: true, + TLS: tlsConfigSrv, +} + +var ( + cfgPool libsrv.PoolServerConfig + ctx context.Context + cnl context.CancelFunc + log = liblog.New() +) + +func init() { + liberr.SetModeReturnError(liberr.ErrorReturnCodeErrorTraceFull) + + ctx, cnl = context.WithCancel(context.Background()) + + log.SetLevel(liblog.DebugLevel) + if err := log.SetOptions(ctx, &liblog.Options{ + DisableStandard: false, + DisableStack: false, + DisableTimestamp: false, + EnableTrace: true, + TraceFilter: "", + DisableColor: false, + LogSyslog: []liblog.OptionsSyslog{ + { + LogLevel: nil, + Network: "", + Host: "", + Severity: "", + Facility: "USER", + Tag: "test-http-server", + DisableStack: false, + DisableTimestamp: false, + EnableTrace: true, + }, + }, + }); err != nil { + panic(err) + } + + cfgPool = libsrv.PoolServerConfig{cfgSrv01, cfgSrv02, cfgSrv03} + cfgPool.MapUpdate(func(cfg libsrv.ServerConfig) libsrv.ServerConfig { + cfg.SetParentContext(func() context.Context { + return ctx + }) + return cfg + }) + +} + +func main() { + var ( + pool libsrv.PoolServer + lerr liberr.Error + ) + + if pool, lerr = cfgPool.PoolServer(); lerr != nil { + panic(lerr) + } else { + pool.SetLogger(getLogger) + } + + mux := http.NewServeMux() + mux.HandleFunc("/hello", hello) + mux.HandleFunc("/headers", headers) + + if lerr = pool.Listen(mux); lerr != nil { + panic(lerr) + } + + defer func() { + pool.Shutdown() + cnl() + }() + + go pool.WaitNotify(ctx, cnl) + + go func() { + l := getLogger() + for { + time.Sleep(5 * time.Second) + + if ctx.Err() != nil { + return + } + + pool.MapRun(func(srv libsrv.Server) { + n, v, _ := srv.StatusInfo() + e := l.Entry(liblog.ErrorLevel, "status message") + e = e.FieldAdd("server_name", n).FieldAdd("server_release", v) + e.ErrorAdd(true, srv.StatusHealth()) + e.Check(liblog.InfoLevel) + }) + } + }() + + var i = 0 + for { + + l := getLogger() + time.Sleep(5 * time.Second) + + if ctx.Err() != nil { + return + } + + i++ + + if i%3 == 0 { + for s := range pool.List(libsrv.FieldBind, libsrv.FieldName, "", ".*") { + l.Entry(liblog.InfoLevel, "Restarting server...").FieldAdd("server_name", s).Log() + } + pool.Restart() + i = 0 + } + } +} + +func hello(w http.ResponseWriter, req *http.Request) { + _, _ = fmt.Fprintf(w, "hello\n") +} + +func headers(w http.ResponseWriter, req *http.Request) { + for name, headers := range req.Header { + for _, h := range headers { + _, _ = fmt.Fprintf(w, "%v: %v\n", name, h) + } + } +} + +func getLogger() liblog.Logger { + l, _ := log.Clone(ctx) + return l +}