From ac807ed0b070b1484b8bfd6d6d54b9fbc183807b Mon Sep 17 00:00:00 2001 From: James Hillyerd Date: Mon, 5 Nov 2012 12:50:16 -0800 Subject: [PATCH] Cycle logfiles on SIGHUP for logrotated --- inbucket.go | 66 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 57 insertions(+), 9 deletions(-) diff --git a/inbucket.go b/inbucket.go index 12abe6a..81170c0 100644 --- a/inbucket.go +++ b/inbucket.go @@ -13,15 +13,22 @@ import ( "github.com/jhillyerd/inbucket/web" golog "log" "os" + "os/signal" + "syscall" "time" ) +// Command line flags var help = flag.Bool("help", false, "Displays this help") var pidfile = flag.String("pidfile", "none", "Write our PID into the specified file") var logfile = flag.String("logfile", "stderr", "Write out log into the specified file") +// startTime is used to calculate uptime of Inbucket var startTime = time.Now() +// The file we send log output to, will be nil for stderr or stdout +var logf *os.File + func main() { flag.Parse() if *help { @@ -40,34 +47,37 @@ func main() { os.Exit(1) } + // Setup signal handler + sigChan := make(chan os.Signal) + signal.Notify(sigChan, syscall.SIGHUP) + go signalProcessor(sigChan) + // Configure logging, close std* fds + level, _ := config.Config.String("logging", "level") + log.SetLogLevel(level) + if *logfile != "stderr" { // stderr is the go logging default if *logfile == "stdout" { // set to stdout golog.SetOutput(os.Stdout) } else { - // use specificed log file - logf, err := os.OpenFile(*logfile, os.O_WRONLY | os.O_APPEND | os.O_CREATE, 0666) + err = openLogFile() if err != nil { - fmt.Fprintf(os.Stderr, "Failed to create %v: %v\n", *logfile, err) + fmt.Fprintf(os.Stderr, "%v", err) os.Exit(1) } - defer logf.Close() - golog.SetOutput(logf) + defer closeLogFile() // close std* streams os.Stdout.Close() - os.Stderr.Close() + os.Stderr.Close() // Warning: this will hide panic() output os.Stdin.Close() os.Stdout = logf os.Stderr = logf } } - level, _ := config.Config.String("logging", "level") - log.SetLogLevel(level) - // Write pidfile if requested // TODO: Probably supposed to remove pidfile during shutdown if *pidfile != "none" { @@ -87,6 +97,43 @@ func main() { web.Start() } +// openLogFile creates or appends to the logfile passed on commandline +func openLogFile() error { + // use specified log file + var err error + logf, err = os.OpenFile(*logfile, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666) + if err != nil { + return fmt.Errorf("Failed to create %v: %v\n", *logfile, err) + } + golog.SetOutput(logf) + log.Trace("Opened new logfile") + return nil +} + +// closeLogFile closes the current logfile +func closeLogFile() error { + log.Trace("Closing logfile") + return logf.Close() +} + +// signalProcessor is a goroutine that handles OS signals +func signalProcessor(c <-chan os.Signal) { + for { + sig := <-c + switch sig { + case syscall.SIGHUP: + // Rotate logs if configured + if logf != nil { + log.Info("Recieved SIGHUP, cycling logfile") + closeLogFile() + openLogFile() + } else { + log.Info("Ignoring SIGHUP, logfile not configured") + } + } + } +} + func init() { flag.Usage = func() { fmt.Fprintln(os.Stderr, "Usage of inbucket [options] :") @@ -96,6 +143,7 @@ func init() { expvar.Publish("uptime", expvar.Func(uptime)) } +// uptime() is published as an expvar func uptime() interface{} { return time.Since(startTime) / time.Second }