diff --git a/plugin/log/README.md b/plugin/log/README.md index 502d928c2..bbd853bd3 100644 --- a/plugin/log/README.md +++ b/plugin/log/README.md @@ -23,10 +23,10 @@ log Or if you want/need slightly more control: ~~~ txt -log [NAME] [FORMAT] +log [NAMES...] [FORMAT] ~~~ -* `NAME` is the name to match in order to be logged +* `NAMES` is the name list to match in order to be logged * `FORMAT` is the log format to use (default is Common Log Format), `{common}` is used as a shortcut for the Common Log Format. You can also use `{combined}` for a format that adds the query opcode `{>opcode}` to the Common Log Format. @@ -34,7 +34,7 @@ log [NAME] [FORMAT] You can further specify the classes of responses that get logged: ~~~ txt -log [NAME] [FORMAT] { +log [NAMES...] [FORMAT] { class CLASSES... } ~~~ diff --git a/plugin/log/setup.go b/plugin/log/setup.go index 4a2afceda..6f92fca3d 100644 --- a/plugin/log/setup.go +++ b/plugin/log/setup.go @@ -1,6 +1,8 @@ package log import ( + "strings" + "github.com/coredns/coredns/core/dnsserver" "github.com/coredns/coredns/plugin" "github.com/coredns/coredns/plugin/pkg/response" @@ -34,62 +36,75 @@ func logParse(c *caddy.Controller) ([]Rule, error) { for c.Next() { args := c.RemainingArgs() + length := len(rules) - if len(args) == 0 { + switch len(args) { + case 0: // Nothing specified; use defaults rules = append(rules, Rule{ NameScope: ".", Format: DefaultLogFormat, Class: make(map[response.Class]struct{}), }) - } else if len(args) == 1 { + case 1: rules = append(rules, Rule{ NameScope: dns.Fqdn(args[0]), Format: DefaultLogFormat, Class: make(map[response.Class]struct{}), }) - } else { - // Name scope, and maybe a format specified + default: + // Name scopes, and maybe a format specified format := DefaultLogFormat - switch args[1] { - case "{common}": - format = CommonLogFormat - case "{combined}": - format = CombinedLogFormat - default: - format = args[1] + if strings.Contains(args[len(args)-1], "{") { + switch args[len(args)-1] { + case "{common}": + format = CommonLogFormat + case "{combined}": + format = CombinedLogFormat + default: + format = args[len(args)-1] + } + + args = args[:len(args)-1] } - rules = append(rules, Rule{ - NameScope: dns.Fqdn(args[0]), - Format: format, - Class: make(map[response.Class]struct{}), - }) + for _, str := range args { + rules = append(rules, Rule{ + NameScope: dns.Fqdn(str), + Format: format, + Class: make(map[response.Class]struct{}), + }) + } } // Class refinements in an extra block. + classes := make(map[response.Class]struct{}) for c.NextBlock() { switch c.Val() { // class followed by combinations of all, denial, error and success. case "class": - classes := c.RemainingArgs() - if len(classes) == 0 { + classesArgs := c.RemainingArgs() + if len(classesArgs) == 0 { return nil, c.ArgErr() } - for _, c := range classes { + for _, c := range classesArgs { cls, err := response.ClassFromString(c) if err != nil { return nil, err } - rules[len(rules)-1].Class[cls] = struct{}{} + classes[cls] = struct{}{} } default: return nil, c.ArgErr() } } - if len(rules[len(rules)-1].Class) == 0 { - rules[len(rules)-1].Class[response.All] = struct{}{} + if len(classes) == 0 { + classes[response.All] = struct{}{} + } + + for i := len(rules) - 1; i >= length; i -= 1 { + rules[i].Class = classes } } diff --git a/plugin/log/setup_test.go b/plugin/log/setup_test.go index 3c65c1c85..678b6cf16 100644 --- a/plugin/log/setup_test.go +++ b/plugin/log/setup_test.go @@ -55,6 +55,35 @@ func TestLogParse(t *testing.T) { Format: "{when}", Class: map[response.Class]struct{}{response.All: struct{}{}}, }}}, + {`log example.org example.net`, false, []Rule{{ + NameScope: "example.org.", + Format: DefaultLogFormat, + Class: map[response.Class]struct{}{response.All: struct{}{}}, + }, { + NameScope: "example.net.", + Format: DefaultLogFormat, + Class: map[response.Class]struct{}{response.All: struct{}{}}, + }}}, + {`log example.org example.net {host}`, false, []Rule{{ + NameScope: "example.org.", + Format: "{host}", + Class: map[response.Class]struct{}{response.All: struct{}{}}, + }, { + NameScope: "example.net.", + Format: "{host}", + Class: map[response.Class]struct{}{response.All: struct{}{}}, + }}}, + {`log example.org example.net {when} { + class denial + }`, false, []Rule{{ + NameScope: "example.org.", + Format: "{when}", + Class: map[response.Class]struct{}{response.Denial: struct{}{}}, + }, { + NameScope: "example.net.", + Format: "{when}", + Class: map[response.Class]struct{}{response.Denial: struct{}{}}, + }}}, {`log example.org { class all