Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create the easeprobe.pid file & Accept HUP Signal to Rotate Log #75

Merged
merged 16 commits into from
May 13, 2022
Merged
4 changes: 1 addition & 3 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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 ./...
72 changes: 70 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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 manualy by the administrator.
haoel marked this conversation as resolved.
Show resolved Hide resolved

```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
Expand Down Expand Up @@ -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"
Expand All @@ -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
Expand Down Expand Up @@ -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

Expand Down
39 changes: 39 additions & 0 deletions cmd/easeprobe/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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...")
Expand All @@ -117,6 +155,7 @@ func main() {
wg.Wait()
doneWatch <- true
doneSave <- true
doneRotate <- true
}

log.Info("Graceful Exit Successfully!")
Expand Down
63 changes: 15 additions & 48 deletions conf/conf.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand Down Expand Up @@ -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{},
Expand All @@ -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{
Expand Down Expand Up @@ -221,18 +223,18 @@ 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)))

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()
Expand All @@ -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) {

}

Expand All @@ -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()
Expand Down
Loading