Browse Source

Add ability to log to file

The -logfile command line option logs output to a file (rather than
stderr). If the log file is rotated (via logrotate or similar),
this is detected.

Signed-off-by: Alex Bligh <[email protected]>
Alex Bligh 10 years ago
parent
commit
76fd448381
2 changed files with 83 additions and 2 deletions
  1. 6 1
      geodns.go
  2. 77 1
      log.go

+ 6 - 1
geodns.go

@@ -54,6 +54,7 @@ var (
 	flaghttp        = flag.String("http", ":8053", "http listen address (:8053)")
 	flaglog         = flag.Bool("log", false, "be more verbose")
 	flagcpus        = flag.Int("cpus", 1, "Set the maximum number of CPUs to use")
+	flagLogFile     = flag.String("logfile", "", "log to file")
 
 	flagShowVersion = flag.Bool("version", false, "Show dnsconfig version")
 
@@ -82,6 +83,10 @@ func main() {
 		os.Exit(0)
 	}
 
+	if len(*flagLogFile) > 0 {
+		logToFileOpen(*flagLogFile)
+	}
+
 	if len(*flagidentifier) > 0 {
 		ids := strings.Split(*flagidentifier, ",")
 		serverID = ids[0]
@@ -190,5 +195,5 @@ func main() {
 		pprof.WriteHeapProfile(f)
 		f.Close()
 	}
-
+	logToFileClose()
 }

+ 77 - 1
log.go

@@ -1,6 +1,26 @@
 package main
 
-import "log"
+import (
+	"log"
+	"os"
+	"time"
+)
+
+type logToFile struct {
+	fn      string
+	file    *os.File
+	closing chan chan error // channel to close the file. Pass a 'chan error' which returns the error
+}
+
+var ltf *logToFile
+
+func newlogToFile(fn string) *logToFile {
+	return &logToFile{
+		fn:      fn,
+		file:    nil,
+		closing: make(chan chan error),
+	}
+}
 
 func logPrintf(format string, a ...interface{}) {
 	if *flaglog {
@@ -13,3 +33,59 @@ func logPrintln(a ...interface{}) {
 		log.Println(a...)
 	}
 }
+
+func logToFileMonitor() {
+	for {
+		select {
+		case errc := <-ltf.closing: // a close has been requested
+			if ltf.file != nil {
+				log.SetOutput(os.Stderr)
+				ltf.file.Close()
+				ltf.file = nil
+			}
+			errc <- nil // pass a 'nil' error back, as everything worked fine
+			return
+		case <-time.After(time.Duration(5 * time.Second)):
+			if fi, err := os.Stat(ltf.fn); err != nil || fi.Size() == 0 {
+				// it has rotated - first check we can open the new file
+				if f, err := os.OpenFile(ltf.fn, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666); err != nil {
+					// Send the error to the current log file - not ideal
+					log.Printf("Could not open new log file: %v", err)
+				} else {
+					log.SetOutput(f)
+					log.Printf("Rotating log file")
+					ltf.file.Close()
+					ltf.file = f
+				}
+			}
+		}
+	}
+}
+
+func logToFileOpen(fn string) {
+
+	ltf = newlogToFile(fn)
+
+	var err error
+	ltf.file, err = os.OpenFile(fn, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
+	if err != nil {
+		log.Fatalf("Error writing log file: %v", err)
+	}
+	// we deliberately do not close logFile here, because we keep it open pretty much for ever
+
+	log.SetOutput(ltf.file)
+	log.Printf("Opening log file")
+
+	go logToFileMonitor()
+}
+
+func logToFileClose() {
+	if ltf != nil {
+		log.Printf("Closing log file")
+		errc := make(chan error) // pass a 'chan error' through the closing channel
+		ltf.closing <- errc
+		_ = <-errc         // wait until the monitor has closed the log file and exited
+		close(ltf.closing) // close our 'chan error' channel
+		ltf = nil
+	}
+}