Add logging control

This commit is contained in:
Julien Riou 2018-06-24 17:49:48 +02:00
parent 96ade1c1d7
commit f0d3cd5bdf
No known key found for this signature in database
GPG key ID: BA3E15176E45E85D
9 changed files with 164 additions and 24 deletions

View file

@ -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 != "" {

View file

@ -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)
}

View file

@ -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)
}
}

View file

@ -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)
}

107
log/log.go Normal file
View file

@ -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)
}

View file

@ -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")
}

View file

@ -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()
}

View file

@ -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()
}

View file

@ -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()
}