setup.go 3.42 KB
Newer Older
1
package log
2 3

import (
4
	"io"
5 6
	"log"
	"os"
7
	"path/filepath"
8

9
	"github.com/hashicorp/go-syslog"
10 11
	"github.com/mholt/caddy"
	"github.com/mholt/caddy/caddyhttp/httpserver"
12 13
)

14 15
// setup sets up the logging middleware.
func setup(c *caddy.Controller) error {
16 17
	rules, err := logParse(c)
	if err != nil {
18
		return err
19 20 21
	}

	// Open the log files for writing when the server starts
22
	c.OnStartup(func() error {
23 24 25 26 27 28 29 30 31 32 33 34 35 36
		for _, rule := range rules {
			for _, entry := range rule.Entries {
				var err error
				var writer io.Writer

				if entry.OutputFile == "stdout" {
					writer = os.Stdout
				} else if entry.OutputFile == "stderr" {
					writer = os.Stderr
				} else if entry.OutputFile == "syslog" {
					writer, err = gsyslog.NewLogger(gsyslog.LOG_INFO, "LOCAL0", "caddy")
					if err != nil {
						return err
					}
37
				} else {
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
					err := os.MkdirAll(filepath.Dir(entry.OutputFile), 0744)
					if err != nil {
						return err
					}
					file, err := os.OpenFile(entry.OutputFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
					if err != nil {
						return err
					}
					if entry.Roller != nil {
						file.Close()
						entry.Roller.Filename = entry.OutputFile
						writer = entry.Roller.GetLogWriter()
					} else {
						entry.file = file
						writer = file
					}
54
				}
55

56 57
				entry.Log = log.New(writer, "", 0)
			}
58 59 60 61 62
		}

		return nil
	})

63 64 65
	// When server stops, close any open log files
	c.OnShutdown(func() error {
		for _, rule := range rules {
66 67 68 69
			for _, entry := range rule.Entries {
				if entry.file != nil {
					entry.file.Close()
				}
70 71 72 73 74
			}
		}
		return nil
	})

75
	httpserver.GetConfig(c).AddMiddleware(func(next httpserver.Handler) httpserver.Handler {
76 77 78 79
		return Logger{Next: next, Rules: rules, ErrorFunc: httpserver.DefaultErrorFunc}
	})

	return nil
80 81
}

82 83
func logParse(c *caddy.Controller) ([]*Rule, error) {
	var rules []*Rule
84 85 86 87

	for c.Next() {
		args := c.RemainingArgs()

88
		var logRoller *httpserver.LogRoller
89 90 91 92 93
		if c.NextBlock() {
			if c.Val() == "rotate" {
				if c.NextArg() {
					if c.Val() == "{" {
						var err error
94
						logRoller, err = httpserver.ParseRoller(c)
95 96 97 98 99 100 101 102 103 104 105 106 107
						if err != nil {
							return nil, err
						}
						// This part doesn't allow having something after the rotate block
						if c.Next() {
							if c.Val() != "}" {
								return nil, c.ArgErr()
							}
						}
					}
				}
			}
		}
108 109
		if len(args) == 0 {
			// Nothing specified; use defaults
110
			rules = appendEntry(rules, "/", &Entry{
111 112
				OutputFile: DefaultLogFilename,
				Format:     DefaultLogFormat,
113
				Roller:     logRoller,
114 115 116
			})
		} else if len(args) == 1 {
			// Only an output file specified
117
			rules = appendEntry(rules, "/", &Entry{
118
				OutputFile: args[0],
119
				Format:     DefaultLogFormat,
120
				Roller:     logRoller,
121 122 123 124
			})
		} else {
			// Path scope, output file, and maybe a format specified

125
			format := DefaultLogFormat
126 127 128 129

			if len(args) > 2 {
				switch args[2] {
				case "{common}":
130
					format = CommonLogFormat
131
				case "{combined}":
132
					format = CombinedLogFormat
133 134 135 136 137
				default:
					format = args[2]
				}
			}

138
			rules = appendEntry(rules, args[0], &Entry{
139 140
				OutputFile: args[1],
				Format:     format,
141
				Roller:     logRoller,
142 143 144 145 146 147
			})
		}
	}

	return rules, nil
}
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163

func appendEntry(rules []*Rule, pathScope string, entry *Entry) []*Rule {
	for _, rule := range rules {
		if rule.PathScope == pathScope {
			rule.Entries = append(rule.Entries, entry)
			return rules
		}
	}

	rules = append(rules, &Rule{
		PathScope: pathScope,
		Entries:   []*Entry{entry},
	})

	return rules
}