From 24eb4fe203084d912222752707c4dd8ecc70d72d Mon Sep 17 00:00:00 2001 From: Julien Riou Date: Sat, 16 Feb 2019 11:47:30 +0100 Subject: [PATCH] Add log-format option --- README.md | 11 +++++++++++ base/config.go | 6 ++++-- base/session.go | 40 +++++++++++++++++----------------------- cmd/pgterminate/main.go | 12 +++++++----- config.yaml.example | 3 ++- notifier/console.go | 6 ++++-- notifier/file.go | 11 +++++++---- notifier/notifier.go | 6 +++--- notifier/syslog.go | 10 ++++++---- 9 files changed, 61 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 6fc2a8e..2bf5550 100644 --- a/README.md +++ b/README.md @@ -94,5 +94,16 @@ When exclude users list or regex is set and no include option is set, `pgtermina LISTEN queries are asynchronous. Sessions are set to "idle" state even if they are waiting for messages to be sent to the queue. `pgterminate` can exclude sessions in that state by looking at the last known query starting with "LISTEN", with the `exclude-listeners` parameter. +# Log format + +The following placeholders are available to format log messages using `log-format` option: +* `%p`: pid +* `%u`: username +* `%d`: database name +* `%r`: client (host:port) +* `%s`: state +* `%m`: state duration +* `%q`: query + # License `pgterminate` is released under [The Unlicense](LICENSE) license. Code is under public domain. diff --git a/base/config.go b/base/config.go index e005de1..2099652 100644 --- a/base/config.go +++ b/base/config.go @@ -2,13 +2,14 @@ package base import ( "fmt" - "github.com/jouir/pgterminate/log" - "gopkg.in/yaml.v2" "io/ioutil" "path/filepath" "regexp" "strings" "sync" + + "github.com/jouir/pgterminate/log" + "gopkg.in/yaml.v2" ) // AppName exposes application name to config module @@ -29,6 +30,7 @@ type Config struct { ActiveTimeout float64 `yaml:"active-timeout"` LogDestination string `yaml:"log-destination"` LogFile string `yaml:"log-file"` + LogFormat string `yaml:"log-format"` PidFile string `yaml:"pid-file"` SyslogIdent string `yaml:"syslog-ident"` SyslogFacility string `yaml:"syslog-facility"` diff --git a/base/session.go b/base/session.go index 27f2135..98150fc 100644 --- a/base/session.go +++ b/base/session.go @@ -29,31 +29,25 @@ func NewSession(pid int64, user string, db string, client string, state string, } } -// String represents a Session as a string -func (s *Session) String() string { - var output []string - if s.Pid != 0 { - output = append(output, fmt.Sprintf("pid=%d", s.Pid)) +// Format returns a Session as a string by replacing placeholders with their respective value +func (s *Session) Format(format string) string { + definitions := map[string]string{ + "%p": fmt.Sprintf("%d", s.Pid), + "%u": s.User, + "%d": s.Db, + "%r": s.Client, + "%s": s.State, + "%m": fmt.Sprintf("%f", s.StateDuration), + "%q": s.Query, } - if s.User != "" { - output = append(output, fmt.Sprintf("user=%s", s.User)) + + output := format + + for placeholder, value := range definitions { + output = strings.Replace(output, placeholder, value, -1) } - if s.Db != "" { - output = append(output, fmt.Sprintf("db=%s", s.Db)) - } - if s.Client != "" { - output = append(output, fmt.Sprintf("client=%s", s.Client)) - } - if s.State != "" { - output = append(output, fmt.Sprintf("state=%s", s.State)) - } - if s.StateDuration != 0 { - output = append(output, fmt.Sprintf("state_duration=%f", s.StateDuration)) - } - if s.Query != "" && !s.IsIdle() { - output = append(output, fmt.Sprintf("query=%s", s.Query)) - } - return strings.Join(output, " ") + + return output } // IsIdle returns true when a session is doing nothing diff --git a/cmd/pgterminate/main.go b/cmd/pgterminate/main.go index ac6e1d0..dd0771c 100644 --- a/cmd/pgterminate/main.go +++ b/cmd/pgterminate/main.go @@ -3,17 +3,18 @@ package main 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" "os" "os/signal" "regexp" "strconv" "sync" "syscall" + + "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" ) // AppVersion stores application version at compilation time @@ -40,6 +41,7 @@ func main() { flag.Float64Var(&config.ActiveTimeout, "active-timeout", 0, "Time for active connections to be terminated in seconds") flag.StringVar(&config.LogDestination, "log-destination", "console", "Log destination between 'console', 'syslog' or 'file'") flag.StringVar(&config.LogFile, "log-file", "", "Write logs to a file") + flag.StringVar(&config.LogFormat, "log-format", "pid=%p user=%u db=%d client=%r state=%s state_duration=%m query=%q", "Represent messages using this format") flag.StringVar(&config.PidFile, "pid-file", "", "Write process id into a file") flag.StringVar(&config.SyslogIdent, "syslog-ident", "pgterminate", "Define syslog tag") flag.StringVar(&config.SyslogFacility, "syslog-facility", "", "Define syslog facility from LOCAL0 to LOCAL7") diff --git a/config.yaml.example b/config.yaml.example index 13bea96..fb97dc1 100644 --- a/config.yaml.example +++ b/config.yaml.example @@ -8,6 +8,7 @@ #idle-timeout: 300 #active-timeout: 10 #log-file: /var/log/pgterminate/pgterminate.log +#log-format: 'pid=%p user=%u db=%d client=%r state=%s state_duration=%m query=%q' #pid-file: /var/run/pgterminate/pgterminate.pid #log-destination: console|file|syslog #syslog-ident: pgterminate @@ -20,4 +21,4 @@ # user1 # user2 #exclude-users-regex: "(user1|user2)" -#cancel +#cancel \ No newline at end of file diff --git a/notifier/console.go b/notifier/console.go index 480876d..daa3b77 100644 --- a/notifier/console.go +++ b/notifier/console.go @@ -7,12 +7,14 @@ import ( // Console notifier structure type Console struct { + format string sessions chan *base.Session } // NewConsole creates a console notifier -func NewConsole(sessions chan *base.Session) Notifier { +func NewConsole(format string, sessions chan *base.Session) Notifier { return &Console{ + format: format, sessions: sessions, } } @@ -21,7 +23,7 @@ func NewConsole(sessions chan *base.Session) Notifier { func (c *Console) Run() { log.Info("Starting console notifier") for session := range c.sessions { - log.Infof("%s\n", session) + log.Info(session.Format(c.format)) } } diff --git a/notifier/file.go b/notifier/file.go index f57b328..142bd69 100644 --- a/notifier/file.go +++ b/notifier/file.go @@ -1,25 +1,28 @@ package notifier import ( - "github.com/jouir/pgterminate/base" - "github.com/jouir/pgterminate/log" "os" "sync" "time" + + "github.com/jouir/pgterminate/base" + "github.com/jouir/pgterminate/log" ) // File structure for file notifier type File struct { handle *os.File + format string name string sessions chan *base.Session mutex sync.Mutex } // NewFile creates a file notifier -func NewFile(name string, sessions chan *base.Session) Notifier { +func NewFile(format string, name string, sessions chan *base.Session) Notifier { return &File{ name: name, + format: format, sessions: sessions, } } @@ -32,7 +35,7 @@ func (f *File) Run() { for session := range f.sessions { timestamp := time.Now().Format(time.RFC3339) - _, err := f.handle.WriteString(timestamp + " " + session.String() + "\n") + _, err := f.handle.WriteString(timestamp + " " + session.Format(f.format) + "\n") base.Panic(err) } } diff --git a/notifier/notifier.go b/notifier/notifier.go index 4281142..ac93252 100644 --- a/notifier/notifier.go +++ b/notifier/notifier.go @@ -15,10 +15,10 @@ type Notifier interface { func NewNotifier(ctx *base.Context) Notifier { switch ctx.Config.LogDestination { case "file": - return NewFile(ctx.Config.LogFile, ctx.Sessions) + return NewFile(ctx.Config.LogFile, ctx.Config.LogFormat, ctx.Sessions) case "syslog": - return NewSyslog(ctx.Config.SyslogFacility, ctx.Config.SyslogIdent, ctx.Sessions) + return NewSyslog(ctx.Config.SyslogFacility, ctx.Config.SyslogIdent, ctx.Config.LogFormat, ctx.Sessions) default: // console - return NewConsole(ctx.Sessions) + return NewConsole(ctx.Config.LogFormat, ctx.Sessions) } } diff --git a/notifier/syslog.go b/notifier/syslog.go index c599585..efb8335 100644 --- a/notifier/syslog.go +++ b/notifier/syslog.go @@ -1,22 +1,23 @@ package notifier import ( - "fmt" + "log/syslog" + "github.com/jouir/pgterminate/base" "github.com/jouir/pgterminate/log" - "log/syslog" ) // Syslog notifier type Syslog struct { sessions chan *base.Session ident string + format string priority syslog.Priority writer *syslog.Writer } // NewSyslog creates a syslog notifier -func NewSyslog(facility string, ident string, sessions chan *base.Session) Notifier { +func NewSyslog(facility string, ident string, format string, sessions chan *base.Session) Notifier { var priority syslog.Priority switch facility { case "LOCAL0": @@ -40,6 +41,7 @@ func NewSyslog(facility string, ident string, sessions chan *base.Session) Notif sessions: sessions, ident: ident, priority: priority, + format: format, } } @@ -51,7 +53,7 @@ func (s *Syslog) Run() { base.Panic(err) } for session := range s.sessions { - s.writer.Info(fmt.Sprintf("%s", session)) + s.writer.Info(session.Format(s.format)) } }