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}