Add logging control
This commit is contained in:
parent
96ade1c1d7
commit
f0d3cd5bdf
9 changed files with 164 additions and 24 deletions
|
@ -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 != "" {
|
||||
|
|
14
base/db.go
14
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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
107
log/log.go
Normal 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)
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue