package notifier import ( "encoding/json" "os" "sync" "time" "git.riou.xyz/jriou/pgterminate/base" "git.riou.xyz/jriou/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, format string, sessions chan *base.Session) Notifier { return &File{ name: name, format: format, sessions: sessions, } } // Run starts the file notifier func (f *File) Run() { log.Info("Starting file notifier") f.open() defer f.terminate() for session := range f.sessions { timestamp := time.Now().Format(time.RFC3339) var line string if f.format == "json" { line = jsonLine(timestamp, session) } else { line = timestamp + " " + session.Format(f.format) } _, err := f.handle.WriteString(line + "\n") base.Panic(err) } } // jsonLine marshals a session as a self-contained JSON object including the // timestamp, so each file line stays valid JSON instead of being prefixed func jsonLine(timestamp string, session *base.Session) string { b, _ := json.Marshal(struct { Timestamp string `json:"timestamp"` *base.Session }{timestamp, session}) return string(b) } // open opens a log file func (f *File) open() { var err error f.handle, err = os.OpenFile(f.name, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) base.Panic(err) } // 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.Debugf("Re-opening log file %s\n", f.name) f.handle.Close() f.open() } // terminate closes the file func (f *File) terminate() { log.Debugf("Closing log file %s\n", f.name) f.handle.Close() }