spongy

A Unixy IRC client
git clone https://git.woozle.org/neale/spongy.git

spongy / spongyd
Neale Pickett  ·  2016-01-18

logfile.go

  1package main
  2
  3import (
  4	"fmt"
  5	"os"
  6	"log"
  7	"path"
  8	"time"
  9)
 10
 11type Logfile struct {
 12	baseDir string
 13	file *os.File
 14	name string
 15	nlines int
 16	maxlines int
 17	outq chan string
 18}
 19
 20func timestamp(s string) string {
 21	ret := fmt.Sprintf("%d %s", time.Now().Unix(), s)
 22	return ret
 23}
 24
 25func NewLogfile(baseDir string, maxlines int) (*Logfile) {
 26	lf := Logfile{baseDir, nil, "", 0, maxlines, make(chan string, 50)}
 27	go lf.processQueue();
 28	return &lf
 29}
 30
 31func (lf *Logfile) Close() {
 32	if lf.file != nil {
 33		lf.Log("EXIT")
 34		close(lf.outq)
 35	}
 36}
 37
 38func (lf *Logfile) Log(s string) error {
 39	lf.outq <- timestamp(s)
 40	return nil
 41}
 42
 43//
 44//
 45
 46func (lf *Logfile) processQueue() {
 47	for line := range lf.outq {
 48		if (lf.file == nil) || (lf.nlines >= lf.maxlines) {
 49			if err := lf.rotate(); err != nil {
 50				// Just keep trying, I guess.
 51				log.Print(err)
 52				continue
 53			}
 54			lf.nlines = 0
 55		}
 56
 57		if _, err := fmt.Fprintln(lf.file, line); err != nil {
 58			log.Print(err)
 59			continue
 60		}
 61		lf.nlines += 1
 62	}
 63
 64	lf.file.Close()
 65}
 66
 67func (lf *Logfile) rotate() error {
 68	fn := fmt.Sprintf("%s.log", time.Now().UTC().Format(time.RFC3339))
 69	pathn := path.Join(lf.baseDir, "log", fn)
 70	newf, err := os.OpenFile(pathn, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666)
 71	if err != nil {
 72		return err
 73	}
 74	
 75	currentPath := path.Join(lf.baseDir, "log", "current")
 76	
 77	if lf.file == nil {
 78		// Open "current" to append a NEXTLOG line.
 79		// If there's no "current", that's okay
 80		lf.file, _ = os.OpenFile(currentPath, os.O_WRONLY|os.O_APPEND, 0666)
 81	}
 82	
 83	if lf.file != nil {
 84		// Note location of new log
 85		logmsg := fmt.Sprintf("NEXTLOG %s", fn)
 86		fmt.Fprintln(lf.file, timestamp(logmsg))
 87		
 88		// All done with the current log
 89		lf.file.Close()
 90	}
 91	
 92	// Point to new log file
 93	lf.file = newf
 94		
 95	// Record symlink to new log
 96	os.Remove(currentPath)
 97	os.Symlink(fn, currentPath)
 98	
 99	logmsg := fmt.Sprintf("PREVLOG %s", lf.name)
100	fmt.Fprintln(lf.file, timestamp(logmsg))
101	
102	lf.name = fn
103	
104	return nil
105}