parser.go 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. package gonx
  2. import (
  3. "bufio"
  4. "fmt"
  5. "io"
  6. "regexp"
  7. "strings"
  8. )
  9. // StringParser is the interface that wraps the ParseString method.
  10. type StringParser interface {
  11. ParseString(line string) (entry *Entry, err error)
  12. }
  13. // Parser is a log record parser. Use specific constructors to initialize it.
  14. type Parser struct {
  15. format string
  16. regexp *regexp.Regexp
  17. }
  18. // NewParser returns a new Parser, use given log format to create its internal
  19. // strings parsing regexp.
  20. func NewParser(format string) *Parser {
  21. re := regexp.MustCompile(`\\\$([a-z_]+)(\\?(.))`).ReplaceAllString(
  22. regexp.QuoteMeta(format+" "), "(?P<$1>[^$3]*)$2")
  23. return &Parser{format, regexp.MustCompile(fmt.Sprintf("^%v", strings.Trim(re, " ")))}
  24. }
  25. // ParseString parses a log file line using internal format regexp. If a line
  26. // does not match the given format an error will be returned.
  27. func (parser *Parser) ParseString(line string) (entry *Entry, err error) {
  28. re := parser.regexp
  29. fields := re.FindStringSubmatch(line)
  30. if fields == nil {
  31. err = fmt.Errorf("access log line '%v' does not match given format '%v'", line, re)
  32. return
  33. }
  34. // Iterate over subexp foung and fill the map record
  35. entry = NewEmptyEntry()
  36. for i, name := range re.SubexpNames() {
  37. if i == 0 {
  38. continue
  39. }
  40. entry.SetField(name, fields[i])
  41. }
  42. return
  43. }
  44. // NewNginxParser parses the nginx conf file to find log_format with the given
  45. // name and returns a parser for this format. It returns an error if cannot find
  46. // the given log format.
  47. func NewNginxParser(conf io.Reader, name string) (parser *Parser, err error) {
  48. scanner := bufio.NewScanner(conf)
  49. re := regexp.MustCompile(fmt.Sprintf(`^\s*log_format\s+%v\s+(.+)\s*$`, name))
  50. found := false
  51. var format string
  52. for scanner.Scan() {
  53. var line string
  54. if !found {
  55. // Find a log_format definition
  56. line = scanner.Text()
  57. formatDef := re.FindStringSubmatch(line)
  58. if formatDef == nil {
  59. continue
  60. }
  61. found = true
  62. line = formatDef[1]
  63. } else {
  64. line = scanner.Text()
  65. }
  66. // Look for a definition end
  67. re = regexp.MustCompile(`^\s*(.*?)\s*(;|$)`)
  68. lineSplit := re.FindStringSubmatch(line)
  69. if l := len(lineSplit[1]); l > 2 {
  70. format += lineSplit[1][1 : l-1]
  71. }
  72. if lineSplit[2] == ";" {
  73. break
  74. }
  75. }
  76. if !found {
  77. err = fmt.Errorf("`log_format %v` not found in given config", name)
  78. } else {
  79. err = scanner.Err()
  80. }
  81. parser = NewParser(format)
  82. return
  83. }