diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index e12a7cb3..41c90c57 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -47,6 +47,4 @@ jobs: - name: Go Get dependencies run: go get -v -t -d ./... - name: Go Test - run: go test -cover ./... - - name: Go Race Test - run: go test -race ./... + run: go test -cover -race ./... diff --git a/README.md b/README.md index 7e47f9c3..8da9eea8 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ EaseProbe is a simple, standalone, and lightWeight tool that can do health/statu - [1.1 Probe](#11-probe) - [1.2 Notification](#12-notification) - [1.3 Report](#13-report) + - [1.4 Administration](#14-administration) - [2. Getting Started](#2-getting-started) - [2.1 Build](#21-build) - [2.2 Configure](#22-configure) @@ -233,6 +234,61 @@ Check the [Notification Configuration](#37-notification-configuration) to see h For more information, please check the [Global Setting Configuration](#38-global-setting-configuration) + +### 1.4 Administration + +There are some administration configuration options: + +**1) PID file** + + The EaseProbe would create a PID file (default `$CWD/easeprobe.pid`) when it starts. it can be configured by: + + ```YAML + settings: + pid: /var/run/easeprobe.pid + ``` + + - If the file already exists, EaseProbe would overwrite it. + - If the file cannot be written, EaseProbe would exit with an error. + + If you want to disable the PID file, you can configure the pid file to "". + + ```YAML + settings: + pid: "" # EaseProbe won't create a PID file + ``` + +**2) Log file Rotation** + + There are two types of log file: **Application Log** and **HTTP Access Log**. + + Both Application Log and HTTP Access Log would be StdOut by default. They all can be configured by: + + ```YAML + log: + file: /path/to/log/file + self_rotate: true # default: true + ``` + + If `self_rotate` is `true`, EaseProbe would rotate the log automatically, and the following options are available: + + ```YAML + size: 10 # max size of log file. default: 10M + age: 7 # max age days of log file. default: 7 days + backups: 5 # max backup log files. default: 5 + compress: true # compress. default: true + ``` + + If `self_rotate` is `false`, EaseProbe would not rotate the log, and the log file would be rotated by the 3rd-party tool (such as `logrotate`) or manually by the administrator. + + ```shell + mv /path/to/easeprobe.log /path/to/easeprobe.log.0 + kill -HUP `cat /path/to/easeprobe.pid` + ``` + + EaseProbe accepts the `HUP` signal to rotate the log. + + ## 2. Getting Started ### 2.1 Build @@ -617,6 +673,12 @@ notify: # Global settings for all probes and notifiers. settings: + # Daemon settings + + # pid file path, default: $CWD/easeprobe.pid, + # if set to "", will not create pid file. + pid: /var/run/easeprobe.pid + # A HTTP Server configuration http: ip: 127.0.0.1 # the IP address of the server. default:"0.0.0.0" @@ -625,6 +687,9 @@ settings: log: file: /path/to/access.log # access log file. default: Stdout # Log Rotate Configuration (optional) + self_rotate: true # true: self rotate log file. default: true + # false: managed by outside (e.g logrotate) + # the blow settings will be ignored. size: 10 # max of access log file size. default: 10m age: 7 # max of access log file age. default: 7 days backups: 5 # max of access log file backups. default: 5 @@ -660,9 +725,12 @@ settings: # Log Level Configuration # can be: panic, fatal, error, warn, info, debug. level: "debug" - # Log Rotate Configuration (optional) + # Log Rotate Configuration (optional) + self_rotate: true # true: self rotate log file. default: true + # false: managed by outside (e.g logrotate) + # the blow settings will be ignored. size: 10 # max of access log file size. default: 10m - age: 7 # max of access log file age. default: 7 days + age: 7 # max of access log file age. default: 7 days backups: 5 # max of access log file backups. default: 5 compress: true # compress the access log file. default: true diff --git a/cmd/easeprobe/main.go b/cmd/easeprobe/main.go index 5b1842a5..6b7bd165 100644 --- a/cmd/easeprobe/main.go +++ b/cmd/easeprobe/main.go @@ -21,11 +21,13 @@ import ( "flag" "os" "os/signal" + "strings" "sync" "syscall" "time" "github.com/megaease/easeprobe/conf" + "github.com/megaease/easeprobe/daemon" "github.com/megaease/easeprobe/global" "github.com/megaease/easeprobe/notify" "github.com/megaease/easeprobe/probe" @@ -54,6 +56,21 @@ func main() { os.Exit(-1) } + // Create the pid file if the file name is not empty + if len(strings.TrimSpace(c.Settings.PIDFile)) > 0 { + d, err := daemon.NewPIDFile(c.Settings.PIDFile) + if err != nil { + log.Fatalf("Fatal: Cannot create the PID file: %s!", err) + os.Exit(-1) + } + log.Infof("Successfully created the PID file: %s", d.PIDFile) + defer d.RemovePIDFile() + } else { + log.Info("Skipping PID file creation (pidfile empty).") + } + + c.InitAllLogs() + // if dry notification mode is specificed in command line, overwrite the configuration if *dryNotify { c.Settings.Notify.Dry = *dryNotify @@ -106,6 +123,27 @@ func main() { signal.Notify(done, syscall.SIGTERM) signal.Notify(done, syscall.SIGINT) + // Rotate the log file + rotateLog := make(chan os.Signal, 1) + doneRotate := make(chan bool, 1) + signal.Notify(rotateLog, syscall.SIGHUP) + go func() { + for { + c := conf.Get() + select { + case <-doneRotate: + log.Info("Received the exit signal, Rotating log file process exiting...") + c.Settings.Log.Close() + c.Settings.HTTPServer.AccessLog.Close() + return + case <-rotateLog: + log.Info("Received SIGHUP, rotating the log file...") + c.Settings.Log.Rotate() + c.Settings.HTTPServer.AccessLog.Rotate() + } + } + }() + select { case <-done: log.Infof("Received the exit signal, exiting...") @@ -117,6 +155,7 @@ func main() { wg.Wait() doneWatch <- true doneSave <- true + doneRotate <- true } log.Info("Graceful Exit Successfully!") diff --git a/conf/conf.go b/conf/conf.go index 8104e9d9..45433ea6 100644 --- a/conf/conf.go +++ b/conf/conf.go @@ -111,6 +111,7 @@ type HTTPServer struct { // Settings is the EaseProbe configuration type Settings struct { + PIDFile string `yaml:"pid"` Log Log `yaml:"log"` TimeFormat string `yaml:"timeformat"` Probe Probe `yaml:"probe"` @@ -176,7 +177,7 @@ func getYamlFile(path string) ([]byte, error) { } // New read the configuration from yaml -func New(conf *string) (Conf, error) { +func New(conf *string) (*Conf, error) { c := Conf{ HTTP: []http.HTTP{}, TCP: []tcp.TCP{}, @@ -192,6 +193,7 @@ func New(conf *string) (Conf, error) { }, Notify: notify.Config{}, Settings: Settings{ + PIDFile: filepath.Join(global.GetWorkDir(), global.DefaultPIDFile), Log: NewLog(), TimeFormat: "2006-01-02 15:04:05 UTC", Probe: Probe{ @@ -221,7 +223,7 @@ func New(conf *string) (Conf, error) { y, err := getYamlFile(*conf) if err != nil { log.Errorf("error: %v ", err) - return c, err + return &c, err } y = []byte(os.ExpandEnv(string(y))) @@ -229,10 +231,10 @@ func New(conf *string) (Conf, error) { err = yaml.Unmarshal(y, &c) if err != nil { log.Errorf("error: %v", err) - return c, err + return &c, err } - c.Init() + c.initData() ssh.BastionMap.ParseAllBastionHost() host.BastionMap.ParseAllBastionHost() @@ -249,45 +251,20 @@ func New(conf *string) (Conf, error) { } } - return c, err + return &c, err } -// Init initialize the configuration -func (conf *Conf) Init() { - conf.initAppLog() - conf.initAccessLog() - conf.initData() -} +// InitAllLogs initialize all logs +func (conf *Conf) InitAllLogs() { -func (conf *Conf) initAppLog() { - log.SetFormatter(&log.TextFormatter{FullTimestamp: true}) - if conf == nil { - log.SetOutput(os.Stdout) - log.SetLevel(log.InfoLevel) - return - } + conf.Settings.Log.InitLog(nil) + conf.Settings.Log.LogInfo("Application") - // if logfile is not set, use stdout - if conf.Settings.Log.File == "" { - log.Infoln("Using Standard Output as the log output...") - log.SetOutput(os.Stdout) - log.SetLevel(conf.Settings.Log.Level.GetLevel()) - return - } + conf.Settings.HTTPServer.AccessLog.InitLog(log.New()) + conf.Settings.HTTPServer.AccessLog.LogInfo("Web Access") +} - // open the log file - f, err := os.OpenFile(conf.Settings.Log.File, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0660) - if err != nil { - log.Warnf("Cannot open log file (%s): %v", conf.Settings.Log.File, err) - log.Infoln("Using Standard Output as the log output...") - log.SetOutput(os.Stdout) - } else { - f.Close() - conf.Settings.Log.CheckDefault() - log.Infof("Using %s as the log output...", conf.Settings.Log.File) - log.SetOutput(conf.Settings.Log.GetWriter()) - } - log.SetLevel(conf.Settings.Log.Level.GetLevel()) +func logLogfileInfo(name string, file string) { } @@ -311,16 +288,6 @@ func (conf *Conf) initData() { } -func (conf *Conf) initAccessLog() { - filename := conf.Settings.HTTPServer.AccessLog.File - if filename != "" { - filename = global.MakeDirectory(filename) - log.Infof("Using %s as the access log output...", filename) - conf.Settings.HTTPServer.AccessLog.File = filename - conf.Settings.HTTPServer.AccessLog.CheckDefault() - } -} - // isProbe checks whether a interface is a probe type func isProbe(t reflect.Type) bool { modelType := reflect.TypeOf((*probe.Prober)(nil)).Elem() diff --git a/conf/log.go b/conf/log.go index 24a429aa..d21407d0 100644 --- a/conf/log.go +++ b/conf/log.go @@ -18,6 +18,8 @@ package conf import ( + "io" + "os" "strings" "github.com/megaease/easeprobe/global" @@ -69,26 +71,45 @@ func (l *LogLevel) GetLevel() log.Level { // Log is the log settings type Log struct { - Level LogLevel `yaml:"level"` - File string `yaml:"file"` - MaxSize int `yaml:"size"` - MaxAge int `yaml:"age"` - MaxBackups int `yaml:"backups"` - Compress bool `yaml:"compress"` + Level LogLevel `yaml:"level"` + File string `yaml:"file"` + SelfRotate bool `yaml:"self_rotate"` + MaxSize int `yaml:"size"` + MaxAge int `yaml:"age"` + MaxBackups int `yaml:"backups"` + Compress bool `yaml:"compress"` + Writter io.Writer `yaml:"-"` + Logger *log.Logger `yaml:"-"` + IsStdout bool `yaml:"-"` } // NewLog create a new Log func NewLog() Log { return Log{ - File: "", Level: LogLevel(log.InfoLevel), + File: "", + SelfRotate: true, MaxSize: global.DefaultMaxLogSize, MaxAge: global.DefaultMaxLogAge, MaxBackups: global.DefaultMaxBackups, Compress: true, + Writter: nil, + Logger: nil, + IsStdout: true, } } +// InitLog initialize the log +func (l *Log) InitLog(logger *log.Logger) { + l.Logger = logger + l.CheckDefault() + if l.File != "" { + global.MakeDirectory(l.File) + } + l.Open() + l.ConfigureLogger() +} + // CheckDefault initialize the Log configuration func (l *Log) CheckDefault() { if l.MaxAge == 0 { @@ -105,14 +126,103 @@ func (l *Log) CheckDefault() { } } +// Open open the log file +func (l *Log) Open() { + // using stdout if no log file + if l.File == "" { + l.IsStdout = true + l.Writter = os.Stdout + return + } + // using lumberjack if self rotate + if l.SelfRotate == true { + log.Debugf("[Log] Self Rotate log file %s", l.File) + l.IsStdout = false + l.Writter = &lumberjack.Logger{ + Filename: l.File, + MaxSize: l.MaxSize, // megabytes + MaxBackups: l.MaxBackups, + MaxAge: l.MaxAge, //days + Compress: l.Compress, + } + return + } + // using log file if not self rotate + f, err := os.OpenFile(l.File, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0640) + if err != nil { + log.Warnf("[Log] Cannot open log file: %v", err) + log.Infoln("[Log] Using Standard Output as the log output...") + l.IsStdout = true + l.Writter = os.Stdout + return + } + l.IsStdout = false + l.Writter = f +} + +// Close close the log file +func (l *Log) Close() { + if l.Writter == nil || l.IsStdout { + return + } + if f, ok := l.Writter.(*os.File); ok { + f.Close() + } +} + // GetWriter return the log writer -func (l *Log) GetWriter() *lumberjack.Logger { - return &lumberjack.Logger{ - Filename: l.File, - MaxSize: l.MaxSize, // megabytes - MaxBackups: l.MaxBackups, - MaxAge: l.MaxAge, //days - Compress: l.Compress, +func (l *Log) GetWriter() io.Writer { + if l.Writter == nil { + l.Open() + } + return (io.Writer)(l.Writter) +} + +//Rotate rotate the log file +func (l *Log) Rotate() { + if l.Writter == nil || l.IsStdout == true { + return + } + if lumberjackLogger, ok := l.Writter.(*lumberjack.Logger); ok { + // self rotate + if err := lumberjackLogger.Rotate(); err != nil { + log.Errorf("[Log] Rotate log file failed: %s", err) + } + } else if fileLogger, ok := l.Writter.(*os.File); ok { + // rotate managed by outside program (e.g. logrotate) + // just close and open current log file + if err := fileLogger.Close(); err != nil { + log.Errorf("[Log] Close log file failed: %s", err) + } + l.Open() // open another writer + l.ConfigureLogger() // set the new logger writer. + } + +} + +// ConfigureLogger configure the logger +func (l *Log) ConfigureLogger() { + if l.Logger != nil { + l.Logger.SetOutput(l.Writter) + l.Logger.SetLevel(l.Level.GetLevel()) + l.Logger.SetFormatter(&log.TextFormatter{FullTimestamp: true}) + } else { //system-wide log + log.SetOutput(l.Writter) + log.SetLevel(l.Level.GetLevel()) + log.SetFormatter(&log.TextFormatter{FullTimestamp: true}) } +} +// LogInfo log info +func (l *Log) LogInfo(name string) { + logger := log.New() + rotate := "Third-Party Rotate (e.g. logrotate)" + if l.SelfRotate { + rotate = "Self-Rotate" + } + if l.File != "" { + logger.Infof("%s Log File [%s] - %s", name, l.File, rotate) + } else { + logger.Infof("%s Log File [Stdout] - %s ", name, rotate) + } } diff --git a/daemon/daemon.go b/daemon/daemon.go new file mode 100644 index 00000000..a2958b59 --- /dev/null +++ b/daemon/daemon.go @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2022, MegaEase + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package daemon + +import ( + "errors" + "fmt" + "os" + "path/filepath" + "strconv" + "strings" + + "github.com/megaease/easeprobe/global" +) + +// Config is the daemon config +type Config struct { + PIDFile string + pidFd *os.File +} + +// NewPIDFile create a new pid file +func NewPIDFile(pidfile string) (*Config, error) { + if pidfile == "" { + return nil, fmt.Errorf("pid file is empty") + } + + fi, err := os.Stat(pidfile) + + if err == nil { // file exists + if fi.IsDir() { + pidfile = filepath.Join(pidfile, global.DefaultPIDFile) + } + li, _ := os.Lstat(pidfile) + if li != nil && (li.Mode()&os.ModeSymlink == os.ModeSymlink) { + os.Remove(pidfile) + } + } else if errors.Is(err, os.ErrNotExist) { // file not exists + // create all of directories + if e := os.MkdirAll(filepath.Dir(pidfile), 0755); e != nil { + return nil, e + } + } else { + return nil, err + } + + c := &Config{ + PIDFile: pidfile, + } + + pidstr := fmt.Sprintf("%d", os.Getpid()) + if err := os.WriteFile(c.PIDFile, []byte(pidstr), 0600); err != nil { + return nil, err + } + + c.pidFd, _ = os.OpenFile(c.PIDFile, os.O_APPEND|os.O_EXCL, 0600) + return c, nil +} + +// CheckPIDFile check if the pid file exists +// if the PID file exists, return the PID of the process +// if the PID file does not exist, return -1 +func (c *Config) CheckPIDFile() (int, error) { + buf, err := os.ReadFile(c.PIDFile) + if err != nil { + return -1, nil + } + + pidstr := strings.TrimSpace(string(buf)) + pid, err := strconv.Atoi(pidstr) + if err != nil { + return -1, nil + } + + if processExists(pid) { + return pid, fmt.Errorf("pid file(%s) found, ensure %s(%d) is not running", c.PIDFile, global.Prog, pid) + } + + return -1, nil +} + +// RemovePIDFile remove the pid file +func (c *Config) RemovePIDFile() error { + c.pidFd.Close() + return os.Remove(c.PIDFile) +} diff --git a/daemon/daemon_darwin.go b/daemon/daemon_darwin.go new file mode 100644 index 00000000..259c14a5 --- /dev/null +++ b/daemon/daemon_darwin.go @@ -0,0 +1,30 @@ +//go:build darwin +// +build darwin + +/* + * Copyright (c) 2022, MegaEase + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package daemon + +import "golang.org/x/sys/unix" + +func processExists(pid int) bool { + // OS X does not have a proc filesystem. + // Use kill -0 pid to judge if the process exists. + err := unix.Kill(pid, 0) + return err == nil +} diff --git a/daemon/daemon_linux.go b/daemon/daemon_linux.go new file mode 100644 index 00000000..b8938e17 --- /dev/null +++ b/daemon/daemon_linux.go @@ -0,0 +1,32 @@ +//go:build !windows && !darwin +// +build !windows,!darwin + +/* + * Copyright (c) 2022, MegaEase + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package daemon + +import ( + "os" + "path/filepath" + "strconv" +) + +func processExists(pid int) bool { + _, err := os.Stat(filepath.Join("/proc", strconv.Itoa(pid))) + return err == nil // err is nil if file exists +} diff --git a/daemon/daemon_test.go b/daemon/daemon_test.go new file mode 100644 index 00000000..b2346726 --- /dev/null +++ b/daemon/daemon_test.go @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2022, MegaEase + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package daemon + +import ( + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/megaease/easeprobe/global" + + "github.com/stretchr/testify/assert" +) + +func testPIDFile(pidfile string, t *testing.T) { + c, err := NewPIDFile(pidfile) + if err != nil { + t.Fatalf("Could not create the pid file, %v", err) + } + + _, err = c.CheckPIDFile() + if err == nil { + t.Fatalf("Could not found the pid file, %v", err) + } + + if err := c.RemovePIDFile(); err != nil { + t.Fatalf("Could not remove the pid file, %v", err) + } +} + +func TestPIDFileNotExist(t *testing.T) { + pidfile := filepath.Join(global.GetWorkDir(), global.DefaultPIDFile) + testPIDFile(pidfile, t) +} + +func TestPIDFileExist(t *testing.T) { + path := filepath.Join(global.GetWorkDir(), global.DefaultPIDFile) + ioutil.WriteFile(path, []byte("1"), 0644) + testPIDFile(path, t) +} + +func TestPIDFileDir(t *testing.T) { + pidfile := global.GetWorkDir() + testPIDFile(pidfile, t) +} + +func TestPIDFileSymLink(t *testing.T) { + path := filepath.Join(global.GetWorkDir(), "test") + target := "test.txt" + os.MkdirAll(path, 0755) + ioutil.WriteFile(filepath.Join(path, "test.txt"), []byte("Hello\n"), 0644) + symlink := filepath.Join(path, "easeprobe.pid") + os.Symlink(target, symlink) + + c, err := NewPIDFile(symlink) + if err != nil { + t.Fatalf("Could not create the pid file, %v", err) + } + + _, err = c.CheckPIDFile() + if err == nil { + t.Fatalf("Could not found the pid file, %v", err) + } + + buf, err := ioutil.ReadFile(filepath.Join(path, "test.txt")) + if err != nil { + t.Fatalf("Could not read the pid file, %v", err) + } + + assert.Equal(t, "Hello\n", string(buf)) + + if err := c.RemovePIDFile(); err != nil { + t.Fatalf("Could not remove the pid file, %v", err) + } + + os.RemoveAll(path) +} diff --git a/daemon/daemon_windows.go b/daemon/daemon_windows.go new file mode 100644 index 00000000..78ce20b1 --- /dev/null +++ b/daemon/daemon_windows.go @@ -0,0 +1,46 @@ +//go: build windows +//go:build windows +// +build windows + +/* + * Copyright (c) 2022, MegaEase + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package daemon + +import ( + "golang.org/x/sys/windows" +) + +const ( + processQueryLimitedInformation = 0x1000 + + stillActive = 259 +) + +func processExists(pid int) bool { + h, err := windows.OpenProcess(processQueryLimitedInformation, false, uint32(pid)) + if err != nil { + return false + } + var c uint32 + err = windows.GetExitCodeProcess(h, &c) + windows.Close(h) + if err != nil { + return c == stillActive + } + return true +} diff --git a/global/global.go b/global/global.go index ab95a802..fc0370f6 100644 --- a/global/global.go +++ b/global/global.go @@ -70,6 +70,8 @@ const ( DefaultAccessLogFile = "access.log" // DefaultDataFile is the default data file name DefaultDataFile = "data.yaml" + // DefaultPIDFile is the default pid file name + DefaultPIDFile = "easeprobe.pid" ) const ( diff --git a/web/server.go b/web/server.go index 7efc7d90..0aaf6029 100644 --- a/web/server.go +++ b/web/server.go @@ -88,14 +88,12 @@ func Server() { // Start the http server go func() { - r := chi.NewRouter() filename := c.Settings.HTTPServer.AccessLog.File if len(filename) > 0 { log.Infof("[Web] Access Log output file: %s", filename) - logger := log.New() - logger.SetOutput(c.Settings.HTTPServer.AccessLog.GetWriter()) + logger := c.Settings.HTTPServer.AccessLog.Logger r.Use(NewStructuredLogger(logger)) }