Commit ce7d3db1 authored by Julian V. Modesto's avatar Julian V. Modesto Committed by Matt Holt

Roll all logs by default (#1379)

* Use new subdirectives and flatten rolling config

* Set default rotate config

* Set default rolling config (hopefully) errwhere

* Make private

* Flatten errors directive and remove c.IncrNest()

* Don't skip first error log roller subdirective we see

* Remove hadBlock

* Try lumberjack import

* Unname import
parent f32eed19
...@@ -120,12 +120,6 @@ func (d *Dispenser) NextBlock() bool { ...@@ -120,12 +120,6 @@ func (d *Dispenser) NextBlock() bool {
return true return true
} }
// IncrNest adds a level of nesting to the dispenser.
func (d *Dispenser) IncrNest() {
d.nesting++
return
}
// Val gets the text of the current token. If there is no token // Val gets the text of the current token. If there is no token
// loaded, it returns empty string. // loaded, it returns empty string.
func (d *Dispenser) Val() string { func (d *Dispenser) Val() string {
......
...@@ -40,33 +40,20 @@ func errorsParse(c *caddy.Controller) (*ErrorHandler, error) { ...@@ -40,33 +40,20 @@ func errorsParse(c *caddy.Controller) (*ErrorHandler, error) {
cfg := httpserver.GetConfig(c) cfg := httpserver.GetConfig(c)
optionalBlock := func() (bool, error) { optionalBlock := func() error {
var hadBlock bool
for c.NextBlock() { for c.NextBlock() {
hadBlock = true
what := c.Val() what := c.Val()
if !c.NextArg() { if !c.NextArg() {
return hadBlock, c.ArgErr() return c.ArgErr()
} }
where := c.Val() where := c.Val()
if what == "log" { if httpserver.IsLogRollerSubdirective(what) {
if where == "visible" { var err error
handler.Debug = true err = httpserver.ParseRoller(handler.Log.Roller, what, where)
} else {
handler.Log.Output = where
if c.NextArg() {
if c.Val() == "{" {
c.IncrNest()
logRoller, err := httpserver.ParseRoller(c)
if err != nil { if err != nil {
return hadBlock, err return err
}
handler.Log.Roller = logRoller
}
}
} }
} else { } else {
// Error page; ensure it exists // Error page; ensure it exists
...@@ -82,24 +69,24 @@ func errorsParse(c *caddy.Controller) (*ErrorHandler, error) { ...@@ -82,24 +69,24 @@ func errorsParse(c *caddy.Controller) (*ErrorHandler, error) {
if what == "*" { if what == "*" {
if handler.GenericErrorPage != "" { if handler.GenericErrorPage != "" {
return hadBlock, c.Errf("Duplicate status code entry: %s", what) return c.Errf("Duplicate status code entry: %s", what)
} }
handler.GenericErrorPage = where handler.GenericErrorPage = where
} else { } else {
whatInt, err := strconv.Atoi(what) whatInt, err := strconv.Atoi(what)
if err != nil { if err != nil {
return hadBlock, c.Err("Expecting a numeric status code or '*', got '" + what + "'") return c.Err("Expecting a numeric status code or '*', got '" + what + "'")
} }
if _, exists := handler.ErrorPages[whatInt]; exists { if _, exists := handler.ErrorPages[whatInt]; exists {
return hadBlock, c.Errf("Duplicate status code entry: %s", what) return c.Errf("Duplicate status code entry: %s", what)
} }
handler.ErrorPages[whatInt] = where handler.ErrorPages[whatInt] = where
} }
} }
} }
return hadBlock, nil return nil
} }
for c.Next() { for c.Next() {
...@@ -107,21 +94,23 @@ func errorsParse(c *caddy.Controller) (*ErrorHandler, error) { ...@@ -107,21 +94,23 @@ func errorsParse(c *caddy.Controller) (*ErrorHandler, error) {
if c.Val() == "}" { if c.Val() == "}" {
continue continue
} }
// Configuration may be in a block
hadBlock, err := optionalBlock()
if err != nil {
return handler, err
}
// Otherwise, the only argument would be an error log file name or 'visible' args := c.RemainingArgs()
if !hadBlock {
if c.NextArg() { if len(args) == 1 {
if c.Val() == "visible" { switch args[0] {
case "visible":
handler.Debug = true handler.Debug = true
} else { default:
handler.Log.Output = c.Val() handler.Log.Output = args[0]
handler.Log.Roller = httpserver.DefaultLogRoller()
} }
} }
// Configuration may be in a block
err := optionalBlock()
if err != nil {
return handler, err
} }
} }
......
...@@ -62,62 +62,70 @@ func TestErrorsParse(t *testing.T) { ...@@ -62,62 +62,70 @@ func TestErrorsParse(t *testing.T) {
}}, }},
{`errors errors.txt`, false, ErrorHandler{ {`errors errors.txt`, false, ErrorHandler{
ErrorPages: map[int]string{}, ErrorPages: map[int]string{},
Log: &httpserver.Logger{Output: "errors.txt"}, Log: &httpserver.Logger{
Output: "errors.txt",
Roller: httpserver.DefaultLogRoller(),
},
}}, }},
{`errors visible`, false, ErrorHandler{ {`errors visible`, false, ErrorHandler{
ErrorPages: map[int]string{}, ErrorPages: map[int]string{},
Debug: true, Debug: true,
Log: &httpserver.Logger{}, Log: &httpserver.Logger{},
}}, }},
{`errors { log visible }`, false, ErrorHandler{ {`errors errors.txt {
ErrorPages: map[int]string{},
Debug: true,
Log: &httpserver.Logger{},
}},
{`errors { log errors.txt
404 404.html 404 404.html
500 500.html 500 500.html
}`, false, ErrorHandler{ }`, false, ErrorHandler{
ErrorPages: map[int]string{ ErrorPages: map[int]string{
404: "404.html", 404: "404.html",
500: "500.html", 500: "500.html",
}, },
Log: &httpserver.Logger{Output: "errors.txt"}, Log: &httpserver.Logger{
Output: "errors.txt",
Roller: httpserver.DefaultLogRoller(),
},
}}, }},
{`errors { log errors.txt { size 2 age 10 keep 3 } }`, false, ErrorHandler{ {`errors errors.txt { rotate_size 2 rotate_age 10 rotate_keep 3 }`, false, ErrorHandler{
ErrorPages: map[int]string{}, ErrorPages: map[int]string{},
Log: &httpserver.Logger{Output: "errors.txt", Roller: &httpserver.LogRoller{ Log: &httpserver.Logger{
Output: "errors.txt", Roller: &httpserver.LogRoller{
MaxSize: 2, MaxSize: 2,
MaxAge: 10, MaxAge: 10,
MaxBackups: 3, MaxBackups: 3,
LocalTime: true, LocalTime: true,
}}},
}, },
{`errors { log errors.txt { },
size 3 }},
age 11 {`errors errors.txt {
keep 5 rotate_size 3
} rotate_age 11
rotate_keep 5
404 404.html 404 404.html
503 503.html 503 503.html
}`, false, ErrorHandler{ }`, false, ErrorHandler{
ErrorPages: map[int]string{ ErrorPages: map[int]string{
404: "404.html", 404: "404.html",
503: "503.html", 503: "503.html",
}, },
Log: &httpserver.Logger{Output: "errors.txt", Roller: &httpserver.LogRoller{ Log: &httpserver.Logger{
Output: "errors.txt",
Roller: &httpserver.LogRoller{
MaxSize: 3, MaxSize: 3,
MaxAge: 11, MaxAge: 11,
MaxBackups: 5, MaxBackups: 5,
LocalTime: true, LocalTime: true,
}, },
}}}, },
{`errors { log errors.txt }},
{`errors errors.txt {
* generic_error.html * generic_error.html
404 404.html 404 404.html
503 503.html 503 503.html
}`, false, ErrorHandler{ }`, false, ErrorHandler{
Log: &httpserver.Logger{Output: "errors.txt"}, Log: &httpserver.Logger{
Output: "errors.txt",
Roller: httpserver.DefaultLogRoller(),
},
GenericErrorPage: "generic_error.html", GenericErrorPage: "generic_error.html",
ErrorPages: map[int]string{ ErrorPages: map[int]string{
404: "404.html", 404: "404.html",
...@@ -158,7 +166,7 @@ func TestErrorsParse(t *testing.T) { ...@@ -158,7 +166,7 @@ func TestErrorsParse(t *testing.T) {
} }
if !reflect.DeepEqual(actualErrorsRule, &test.expectedErrorHandler) { if !reflect.DeepEqual(actualErrorsRule, &test.expectedErrorHandler) {
t.Errorf("Test %d expect %v, but got %v", i, t.Errorf("Test %d expect %v, but got %v", i,
actualErrorsRule, test.expectedErrorHandler) test.expectedErrorHandler, actualErrorsRule)
} }
} }
} }
...@@ -5,8 +5,6 @@ import ( ...@@ -5,8 +5,6 @@ import (
"path/filepath" "path/filepath"
"strconv" "strconv"
"github.com/mholt/caddy"
"gopkg.in/natefinch/lumberjack.v2" "gopkg.in/natefinch/lumberjack.v2"
) )
...@@ -46,40 +44,57 @@ func (l LogRoller) GetLogWriter() io.Writer { ...@@ -46,40 +44,57 @@ func (l LogRoller) GetLogWriter() io.Writer {
return lj return lj
} }
// IsLogRollerSubdirective is true if the subdirective is for the log roller.
func IsLogRollerSubdirective(subdir string) bool {
return subdir == directiveRotateSize ||
subdir == directiveRotateAge ||
subdir == directiveRotateKeep
}
// ParseRoller parses roller contents out of c. // ParseRoller parses roller contents out of c.
func ParseRoller(c *caddy.Controller) (*LogRoller, error) { func ParseRoller(l *LogRoller, what string, where string) error {
var size, age, keep int if l == nil {
// This is kind of a hack to support nested blocks: l = DefaultLogRoller()
// As we are already in a block: either log or errors,
// c.nesting > 0 but, as soon as c meets a }, it thinks
// the block is over and return false for c.NextBlock.
for c.NextBlock() {
what := c.Val()
if !c.NextArg() {
return nil, c.ArgErr()
} }
value := c.Val() var value int
var err error var err error
switch what { value, err = strconv.Atoi(where)
case "size":
size, err = strconv.Atoi(value)
case "age":
age, err = strconv.Atoi(value)
case "keep":
keep, err = strconv.Atoi(value)
}
if err != nil { if err != nil {
return nil, err return err
} }
switch what {
case directiveRotateSize:
l.MaxSize = value
case directiveRotateAge:
l.MaxAge = value
case directiveRotateKeep:
l.MaxBackups = value
} }
return nil
}
// DefaultLogRoller will roll logs by default.
func DefaultLogRoller() *LogRoller {
return &LogRoller{ return &LogRoller{
MaxSize: size, MaxSize: defaultRotateSize,
MaxAge: age, MaxAge: defaultRotateAge,
MaxBackups: keep, MaxBackups: defaultRotateKeep,
LocalTime: true, LocalTime: true,
}, nil }
} }
const (
// defaultRotateSize is 100 MB.
defaultRotateSize = 100
// defaultRotateAge is 14 days.
defaultRotateAge = 14
// defaultRotateKeep is 10 files.
defaultRotateKeep = 10
directiveRotateSize = "rotate_size"
directiveRotateAge = "rotate_age"
directiveRotateKeep = "rotate_keep"
)
// lumberjacks maps log filenames to the logger // lumberjacks maps log filenames to the logger
// that is being used to keep them rolled/maintained. // that is being used to keep them rolled/maintained.
var lumberjacks = make(map[string]*lumberjack.Logger) var lumberjacks = make(map[string]*lumberjack.Logger)
...@@ -32,25 +32,24 @@ func logParse(c *caddy.Controller) ([]*Rule, error) { ...@@ -32,25 +32,24 @@ func logParse(c *caddy.Controller) ([]*Rule, error) {
args := c.RemainingArgs() args := c.RemainingArgs()
var logRoller *httpserver.LogRoller var logRoller *httpserver.LogRoller
if c.NextBlock() { logRoller = httpserver.DefaultLogRoller()
if c.Val() == "rotate" {
if c.NextArg() { for c.NextBlock() {
if c.Val() == "{" { what := c.Val()
if !c.NextArg() {
return nil, c.ArgErr()
}
where := c.Val()
if httpserver.IsLogRollerSubdirective(what) {
var err error var err error
logRoller, err = httpserver.ParseRoller(c) err = httpserver.ParseRoller(logRoller, what, where)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// This part doesn't allow having something after the rotate block
if c.Next() {
if c.Val() != "}" {
return nil, c.ArgErr()
}
}
}
}
} }
} }
if len(args) == 0 { if len(args) == 0 {
// Nothing specified; use defaults // Nothing specified; use defaults
rules = appendEntry(rules, "/", &Entry{ rules = appendEntry(rules, "/", &Entry{
......
...@@ -32,7 +32,10 @@ func TestSetup(t *testing.T) { ...@@ -32,7 +32,10 @@ func TestSetup(t *testing.T) {
t.Errorf("Expected / as the default PathScope") t.Errorf("Expected / as the default PathScope")
} }
expectedLogger := &httpserver.Logger{Output: DefaultLogFilename} expectedLogger := &httpserver.Logger{
Output: DefaultLogFilename,
Roller: httpserver.DefaultLogRoller(),
}
if !reflect.DeepEqual(myHandler.Rules[0].Entries[0].Log, expectedLogger) { if !reflect.DeepEqual(myHandler.Rules[0].Entries[0].Log, expectedLogger) {
t.Errorf("Expected %v as the default Log, got: %v", expectedLogger, myHandler.Rules[0].Entries[0].Log) t.Errorf("Expected %v as the default Log, got: %v", expectedLogger, myHandler.Rules[0].Entries[0].Log)
...@@ -40,7 +43,6 @@ func TestSetup(t *testing.T) { ...@@ -40,7 +43,6 @@ func TestSetup(t *testing.T) {
if myHandler.Rules[0].Entries[0].Format != DefaultLogFormat { if myHandler.Rules[0].Entries[0].Format != DefaultLogFormat {
t.Errorf("Expected %s as the default Log Format", DefaultLogFormat) t.Errorf("Expected %s as the default Log Format", DefaultLogFormat)
} }
if !httpserver.SameNext(myHandler.Next, httpserver.EmptyNext) { if !httpserver.SameNext(myHandler.Next, httpserver.EmptyNext) {
t.Error("'Next' field of handler was not set properly") t.Error("'Next' field of handler was not set properly")
} }
...@@ -55,56 +57,80 @@ func TestLogParse(t *testing.T) { ...@@ -55,56 +57,80 @@ func TestLogParse(t *testing.T) {
{`log`, false, []Rule{{ {`log`, false, []Rule{{
PathScope: "/", PathScope: "/",
Entries: []*Entry{{ Entries: []*Entry{{
Log: &httpserver.Logger{Output: DefaultLogFilename}, Log: &httpserver.Logger{
Output: DefaultLogFilename,
Roller: httpserver.DefaultLogRoller(),
},
Format: DefaultLogFormat, Format: DefaultLogFormat,
}}, }},
}}}, }}},
{`log log.txt`, false, []Rule{{ {`log log.txt`, false, []Rule{{
PathScope: "/", PathScope: "/",
Entries: []*Entry{{ Entries: []*Entry{{
Log: &httpserver.Logger{Output: "log.txt"}, Log: &httpserver.Logger{
Output: "log.txt",
Roller: httpserver.DefaultLogRoller(),
},
Format: DefaultLogFormat, Format: DefaultLogFormat,
}}, }},
}}}, }}},
{`log syslog://127.0.0.1:5000`, false, []Rule{{ {`log syslog://127.0.0.1:5000`, false, []Rule{{
PathScope: "/", PathScope: "/",
Entries: []*Entry{{ Entries: []*Entry{{
Log: &httpserver.Logger{Output: "syslog://127.0.0.1:5000"}, Log: &httpserver.Logger{
Output: "syslog://127.0.0.1:5000",
Roller: httpserver.DefaultLogRoller(),
},
Format: DefaultLogFormat, Format: DefaultLogFormat,
}}, }},
}}}, }}},
{`log syslog+tcp://127.0.0.1:5000`, false, []Rule{{ {`log syslog+tcp://127.0.0.1:5000`, false, []Rule{{
PathScope: "/", PathScope: "/",
Entries: []*Entry{{ Entries: []*Entry{{
Log: &httpserver.Logger{Output: "syslog+tcp://127.0.0.1:5000"}, Log: &httpserver.Logger{
Output: "syslog+tcp://127.0.0.1:5000",
Roller: httpserver.DefaultLogRoller(),
},
Format: DefaultLogFormat, Format: DefaultLogFormat,
}}, }},
}}}, }}},
{`log /api log.txt`, false, []Rule{{ {`log /api log.txt`, false, []Rule{{
PathScope: "/api", PathScope: "/api",
Entries: []*Entry{{ Entries: []*Entry{{
Log: &httpserver.Logger{Output: "log.txt"}, Log: &httpserver.Logger{
Output: "log.txt",
Roller: httpserver.DefaultLogRoller(),
},
Format: DefaultLogFormat, Format: DefaultLogFormat,
}}, }},
}}}, }}},
{`log /serve stdout`, false, []Rule{{ {`log /serve stdout`, false, []Rule{{
PathScope: "/serve", PathScope: "/serve",
Entries: []*Entry{{ Entries: []*Entry{{
Log: &httpserver.Logger{Output: "stdout"}, Log: &httpserver.Logger{
Output: "stdout",
Roller: httpserver.DefaultLogRoller(),
},
Format: DefaultLogFormat, Format: DefaultLogFormat,
}}, }},
}}}, }}},
{`log /myapi log.txt {common}`, false, []Rule{{ {`log /myapi log.txt {common}`, false, []Rule{{
PathScope: "/myapi", PathScope: "/myapi",
Entries: []*Entry{{ Entries: []*Entry{{
Log: &httpserver.Logger{Output: "log.txt"}, Log: &httpserver.Logger{
Output: "log.txt",
Roller: httpserver.DefaultLogRoller(),
},
Format: CommonLogFormat, Format: CommonLogFormat,
}}, }},
}}}, }}},
{`log /test accesslog.txt {combined}`, false, []Rule{{ {`log /test accesslog.txt {combined}`, false, []Rule{{
PathScope: "/test", PathScope: "/test",
Entries: []*Entry{{ Entries: []*Entry{{
Log: &httpserver.Logger{Output: "accesslog.txt"}, Log: &httpserver.Logger{
Output: "accesslog.txt",
Roller: httpserver.DefaultLogRoller(),
},
Format: CombinedLogFormat, Format: CombinedLogFormat,
}}, }},
}}}, }}},
...@@ -112,13 +138,19 @@ func TestLogParse(t *testing.T) { ...@@ -112,13 +138,19 @@ func TestLogParse(t *testing.T) {
log /api2 accesslog.txt {combined}`, false, []Rule{{ log /api2 accesslog.txt {combined}`, false, []Rule{{
PathScope: "/api1", PathScope: "/api1",
Entries: []*Entry{{ Entries: []*Entry{{
Log: &httpserver.Logger{Output: "log.txt"}, Log: &httpserver.Logger{
Output: "log.txt",
Roller: httpserver.DefaultLogRoller(),
},
Format: DefaultLogFormat, Format: DefaultLogFormat,
}}, }},
}, { }, {
PathScope: "/api2", PathScope: "/api2",
Entries: []*Entry{{ Entries: []*Entry{{
Log: &httpserver.Logger{Output: "accesslog.txt"}, Log: &httpserver.Logger{
Output: "accesslog.txt",
Roller: httpserver.DefaultLogRoller(),
},
Format: CombinedLogFormat, Format: CombinedLogFormat,
}}, }},
}}}, }}},
...@@ -126,20 +158,28 @@ func TestLogParse(t *testing.T) { ...@@ -126,20 +158,28 @@ func TestLogParse(t *testing.T) {
log /api4 log.txt {when}`, false, []Rule{{ log /api4 log.txt {when}`, false, []Rule{{
PathScope: "/api3", PathScope: "/api3",
Entries: []*Entry{{ Entries: []*Entry{{
Log: &httpserver.Logger{Output: "stdout"}, Log: &httpserver.Logger{
Output: "stdout",
Roller: httpserver.DefaultLogRoller(),
},
Format: "{host}", Format: "{host}",
}}, }},
}, { }, {
PathScope: "/api4", PathScope: "/api4",
Entries: []*Entry{{ Entries: []*Entry{{
Log: &httpserver.Logger{Output: "log.txt"}, Log: &httpserver.Logger{
Output: "log.txt",
Roller: httpserver.DefaultLogRoller(),
},
Format: "{when}", Format: "{when}",
}}, }},
}}}, }}},
{`log access.log { rotate { size 2 age 10 keep 3 } }`, false, []Rule{{ {`log access.log { rotate_size 2 rotate_age 10 rotate_keep 3 }`, false, []Rule{{
PathScope: "/", PathScope: "/",
Entries: []*Entry{{ Entries: []*Entry{{
Log: &httpserver.Logger{Output: "access.log", Roller: &httpserver.LogRoller{ Log: &httpserver.Logger{
Output: "access.log",
Roller: &httpserver.LogRoller{
MaxSize: 2, MaxSize: 2,
MaxAge: 10, MaxAge: 10,
MaxBackups: 3, MaxBackups: 3,
...@@ -152,10 +192,16 @@ func TestLogParse(t *testing.T) { ...@@ -152,10 +192,16 @@ func TestLogParse(t *testing.T) {
log / log.txt {when}`, false, []Rule{{ log / log.txt {when}`, false, []Rule{{
PathScope: "/", PathScope: "/",
Entries: []*Entry{{ Entries: []*Entry{{
Log: &httpserver.Logger{Output: "stdout"}, Log: &httpserver.Logger{
Output: "stdout",
Roller: httpserver.DefaultLogRoller(),
},
Format: "{host}", Format: "{host}",
}, { }, {
Log: &httpserver.Logger{Output: "log.txt"}, Log: &httpserver.Logger{
Output: "log.txt",
Roller: httpserver.DefaultLogRoller(),
},
Format: "{when}", Format: "{when}",
}}, }},
}}}, }}},
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment