From f0d3cd5bdf669bd1b89429c390e5821c764446ad Mon Sep 17 00:00:00 2001 From: Julien Riou Date: Sun, 24 Jun 2018 17:49:48 +0200 Subject: [PATCH] Add logging control --- base/config.go | 4 +- base/db.go | 14 ++++- base/utils.go | 4 +- cmd/pgterminate/main.go | 30 ++++++++--- log/log.go | 107 +++++++++++++++++++++++++++++++++++++++ notifier/console.go | 6 ++- notifier/file.go | 8 +-- notifier/syslog.go | 4 ++ terminator/terminator.go | 11 ++-- 9 files changed, 164 insertions(+), 24 deletions(-) create mode 100644 log/log.go diff --git a/base/config.go b/base/config.go index 9c0d396..c6e3778 100644 --- a/base/config.go +++ b/base/config.go @@ -2,9 +2,9 @@ package base import ( "fmt" + "github.com/jouir/pgterminate/log" "gopkg.in/yaml.v2" "io/ioutil" - "log" "path/filepath" "strings" "sync" @@ -64,7 +64,7 @@ func (c *Config) Read(file string) error { // Reload reads from file and update configuration func (c *Config) Reload() { - log.Println("Reloading configuration") + log.Debug("Reloading configuration") c.mutex.Lock() defer c.mutex.Unlock() if c.File != "" { diff --git a/base/db.go b/base/db.go index ee210f5..afc3e29 100644 --- a/base/db.go +++ b/base/db.go @@ -2,8 +2,9 @@ package base import ( "database/sql" + "fmt" + "github.com/jouir/pgterminate/log" "github.com/lib/pq" - "strconv" ) const ( @@ -42,7 +43,15 @@ func (db *Db) Disconnect() { // Sessions connects to the database and returns current sessions func (db *Db) Sessions() (sessions []Session) { - query := `select pid as pid, usename as user, datname as db, host(client_addr)::text || ':' || client_port::text as client, state as state, substring(query from 1 for ` + strconv.Itoa(maxQueryLength) + `) as query, coalesce(extract(epoch from now() - state_change), 0) as "stateDuration" from pg_catalog.pg_stat_activity where pid <> pg_backend_pid();` + query := fmt.Sprintf(`select pid as pid, + usename as user, + datname as db, + host(client_addr)::text || ':' || client_port::text as client, + state as state, substring(query from 1 for %d) as query, + coalesce(extract(epoch from now() - state_change), 0) as "stateDuration" + from pg_catalog.pg_stat_activity + where pid <> pg_backend_pid();`, maxQueryLength) + log.Debugf("query: %s\n", query) rows, err := db.conn.Query(query) Panic(err) defer rows.Close() @@ -70,6 +79,7 @@ func (db *Db) TerminateSessions(sessions []Session) { } if len(pids) > 0 { query := `select pg_terminate_backend(pid) from pg_stat_activity where pid = any($1);` + log.Debugf("query: %s\n", query) _, err := db.conn.Exec(query, pq.Array(pids)) Panic(err) } diff --git a/base/utils.go b/base/utils.go index a8e326b..c7e7c8d 100644 --- a/base/utils.go +++ b/base/utils.go @@ -1,12 +1,12 @@ package base import ( - "log" + "github.com/jouir/pgterminate/log" ) // Panic prints a non-nil error and terminates the program func Panic(err error) { if err != nil { - log.Fatalln(err) + log.Fatalf("%s\n", err) } } diff --git a/cmd/pgterminate/main.go b/cmd/pgterminate/main.go index c816600..0c84b7f 100644 --- a/cmd/pgterminate/main.go +++ b/cmd/pgterminate/main.go @@ -4,10 +4,10 @@ import ( "flag" "fmt" "github.com/jouir/pgterminate/base" + "github.com/jouir/pgterminate/log" "github.com/jouir/pgterminate/notifier" "github.com/jouir/pgterminate/terminator" "golang.org/x/crypto/ssh/terminal" - "log" "os" "os/signal" "regexp" @@ -23,6 +23,9 @@ func main() { var err error config := base.NewConfig() + quiet := flag.Bool("quiet", false, "Quiet mode") + verbose := flag.Bool("verbose", false, "Verbose mode") + debug := flag.Bool("debug", false, "Debug mode") version := flag.Bool("version", false, "Print version") flag.StringVar(&config.File, "config", "", "Configuration file") flag.StringVar(&config.Host, "host", "", "Instance host address") @@ -42,6 +45,17 @@ func main() { flag.StringVar(&config.SyslogFacility, "syslog-facility", "", "Define syslog facility from LOCAL0 to LOCAL7") flag.Parse() + log.SetLevel(log.WarnLevel) + if *debug { + log.SetLevel(log.DebugLevel) + } + if *verbose { + log.SetLevel(log.InfoLevel) + } + if *quiet { + log.SetLevel(log.ErrorLevel) + } + if *version { if AppVersion == "" { AppVersion = "unknown" @@ -64,18 +78,18 @@ func main() { } if config.ActiveTimeout == 0 && config.IdleTimeout == 0 { - log.Fatalln("Parameter -active-timeout or -idle-timeout required") + log.Fatal("Parameter -active-timeout or -idle-timeout required") } if config.LogDestination != "console" && config.LogDestination != "file" && config.LogDestination != "syslog" { - log.Fatalln("Log destination must be 'console', 'file' or 'syslog'") + log.Fatal("Log destination must be 'console', 'file' or 'syslog'") } if config.LogDestination == "syslog" && config.SyslogFacility != "" { matched, err := regexp.MatchString("^LOCAL[0-7]$", config.SyslogFacility) base.Panic(err) if !matched { - log.Fatalln("Syslog facility must range from LOCAL0 to LOCAL7") + log.Fatal("Syslog facility must range from LOCAL0 to LOCAL7") } } @@ -115,7 +129,7 @@ func handleSignals(ctx *base.Context, n notifier.Notifier) { signal.Notify(c, syscall.SIGTERM) go func() { for sig := range c { - log.Printf("Received %v signal\n", sig) + log.Debugf("Received %v signal\n", sig) close(ctx.Sessions) ctx.Done <- true } @@ -126,7 +140,7 @@ func handleSignals(ctx *base.Context, n notifier.Notifier) { signal.Notify(h, syscall.SIGHUP) go func() { for sig := range h { - log.Printf("Received %v signal\n", sig) + log.Debugf("Received %v signal\n", sig) ctx.Config.Reload() n.Reload() } @@ -135,7 +149,7 @@ func handleSignals(ctx *base.Context, n notifier.Notifier) { // writePid writes current pid into a pid file func writePid(file string) { - log.Println("Creating pid file", file) + log.Infof("Creating pid file %s", file) pid := strconv.Itoa(os.Getpid()) f, err := os.OpenFile(file, os.O_CREATE|os.O_WRONLY, 0644) @@ -149,7 +163,7 @@ func writePid(file string) { // removePid removes pid file func removePid(file string) { if _, err := os.Stat(file); err == nil { - log.Println("Removing pid file", file) + log.Infof("Removing pid file %s", file) err := os.Remove(file) base.Panic(err) } diff --git a/log/log.go b/log/log.go new file mode 100644 index 0000000..adbd42e --- /dev/null +++ b/log/log.go @@ -0,0 +1,107 @@ +package log + +import ( + "errors" + "log" + "os" +) + +const ( + // DebugLevel for debug messages + DebugLevel int = iota + // InfoLevel for info messages + InfoLevel + // WarnLevel for warning messages + WarnLevel + // ErrorLevel for error messages + ErrorLevel + // FatalLevel for fatal messages + FatalLevel +) + +var level int + +func init() { + level = WarnLevel +} + +// SetLevel configures logging level +func SetLevel(logLevel int) error { + if logLevel < DebugLevel || logLevel > FatalLevel { + return errors.New("Wrong logging level") + } + level = logLevel + return nil +} + +// Debug prints debug messages +func Debug(m string) { + if level <= DebugLevel { + log.Println("[DEBUG] " + m) + } +} + +// Debugf prints debug messages with printf format +func Debugf(format string, values ...interface{}) { + if level <= DebugLevel { + log.Printf("[DEBUG] "+format, values...) + } +} + +// Info prints info messages +func Info(m string) { + if level <= InfoLevel { + log.Println("[INFO] " + m) + } +} + +// Infof prints info messages with printf format +func Infof(format string, values ...interface{}) { + if level <= InfoLevel { + log.Printf("[INFO] "+format, values...) + } +} + +// Warn prints warning messages +func Warn(m string) { + if level <= WarnLevel { + log.Println("[WARN] " + m) + } +} + +// Warnf prints warning messages with printf format +func Warnf(format string, values ...interface{}) { + if level <= WarnLevel { + log.Printf("[WARN] "+format, values...) + } +} + +// Error prints error messages +func Error(m string) { + if level <= ErrorLevel { + log.Println("[ERROR] " + m) + } +} + +// Errorf prints error messages with printf format +func Errorf(format string, values ...interface{}) { + if level <= WarnLevel { + log.Printf("[ERROR] "+format, values...) + } +} + +// Fatal prints fatal messages and exit +func Fatal(m string) { + if level <= FatalLevel { + log.Println("[FATAL] " + m) + } + os.Exit(1) +} + +// Fatalf prints fatal messages with printf format and exit +func Fatalf(format string, values ...interface{}) { + if level <= FatalLevel { + log.Printf("[FATAL] "+format, values...) + } + os.Exit(1) +} diff --git a/notifier/console.go b/notifier/console.go index 4bee564..21a160d 100644 --- a/notifier/console.go +++ b/notifier/console.go @@ -2,7 +2,7 @@ package notifier import ( "github.com/jouir/pgterminate/base" - "log" + "github.com/jouir/pgterminate/log" ) // Console notifier structure @@ -19,11 +19,13 @@ func NewConsole(sessions chan base.Session) Notifier { // Run starts console notifier func (c *Console) Run() { + log.Info("Starting console notifier") for session := range c.sessions { - log.Printf("%s", session) + log.Infof("%s\n", session) } } // Reload for handling SIGHUP signals func (c *Console) Reload() { + log.Info("Reloading console notifier") } diff --git a/notifier/file.go b/notifier/file.go index dca052d..d4dc1a8 100644 --- a/notifier/file.go +++ b/notifier/file.go @@ -2,7 +2,7 @@ package notifier import ( "github.com/jouir/pgterminate/base" - "log" + "github.com/jouir/pgterminate/log" "os" "sync" "time" @@ -26,6 +26,7 @@ func NewFile(name string, sessions chan base.Session) Notifier { // Run starts the file notifier func (f *File) Run() { + log.Info("Starting file notifier") f.open() defer f.terminate() @@ -45,15 +46,16 @@ func (f *File) open() { // Reload closes and re-open the file to be compatible with logrotate func (f *File) Reload() { + log.Info("Reloading file notifier") f.mutex.Lock() defer f.mutex.Unlock() - log.Println("Re-opening log file", f.name) + log.Debugf("Re-opening log file %s\n", f.name) f.handle.Close() f.open() } // terminate closes the file func (f *File) terminate() { - log.Println("Closing log file", f.name) + log.Debugf("Closing log file %s\n", f.name) f.handle.Close() } diff --git a/notifier/syslog.go b/notifier/syslog.go index 8ca7ec0..ab5d103 100644 --- a/notifier/syslog.go +++ b/notifier/syslog.go @@ -3,6 +3,7 @@ package notifier import ( "fmt" "github.com/jouir/pgterminate/base" + "github.com/jouir/pgterminate/log" "log/syslog" ) @@ -44,6 +45,7 @@ func NewSyslog(facility string, ident string, sessions chan base.Session) Notifi // Run starts syslog notifier func (s *Syslog) Run() { + log.Info("Starting syslog notifier") var err error if s.writer, err = syslog.New(s.priority, s.ident); err != nil { base.Panic(err) @@ -56,7 +58,9 @@ func (s *Syslog) Run() { // Reload disconnect from syslog daemon and re-connect // Executed when receiving SIGHUP signal func (s *Syslog) Reload() { + log.Info("Reloading syslog notifier") if s.writer != nil { + log.Debug("Re-connecting to syslog daemon") s.disconnect() s.connect() } diff --git a/terminator/terminator.go b/terminator/terminator.go index e2739c5..5749600 100644 --- a/terminator/terminator.go +++ b/terminator/terminator.go @@ -2,7 +2,7 @@ package terminator import ( "github.com/jouir/pgterminate/base" - "log" + "github.com/jouir/pgterminate/log" "time" ) @@ -26,8 +26,9 @@ func NewTerminator(ctx *base.Context) *Terminator { // Run starts the Terminator func (t *Terminator) Run() { + log.Info("Starting terminator") t.db = base.NewDb(t.config.Dsn()) - log.Println("Connecting to instance") + log.Info("Connecting to instance") t.db.Connect() defer t.terminate() @@ -39,12 +40,12 @@ func (t *Terminator) Run() { sessions := t.db.Sessions() if t.config.ActiveTimeout != 0 { actives := activeSessions(sessions, t.config.ActiveTimeout) - go t.terminateAndNotify(actives) + t.terminateAndNotify(actives) } if t.config.IdleTimeout != 0 { idles := idleSessions(sessions, t.config.IdleTimeout) - go t.terminateAndNotify(idles) + t.terminateAndNotify(idles) } time.Sleep(time.Duration(t.config.Interval*1000) * time.Millisecond) } @@ -62,7 +63,7 @@ func (t *Terminator) terminateAndNotify(sessions []base.Session) { // terminate terminates gracefully func (t *Terminator) terminate() { - log.Println("Disconnecting from instance") + log.Info("Disconnecting from instance") t.db.Disconnect() }