parser.go 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  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. // Log record parser. Use specific constructors to initialize it.
  14. type Parser struct {
  15. format string
  16. regexp *regexp.Regexp
  17. }
  18. // 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. // Parse log file line using internal format regexp. If line do not match
  26. // 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 parse nginx conf file to find log_format with given name and
  45. // returns parser for this format. It returns an error if cannot find the needle.
  46. func NewNginxParser(conf io.Reader, name string) (parser *Parser, err error) {
  47. scanner := bufio.NewScanner(conf)
  48. re := regexp.MustCompile(fmt.Sprintf(`^\s*log_format\s+%v\s+(.+)\s*$`, name))
  49. found := false
  50. var format string
  51. for scanner.Scan() {
  52. var line string
  53. if !found {
  54. // Find a log_format definition
  55. line = scanner.Text()
  56. formatDef := re.FindStringSubmatch(line)
  57. if formatDef == nil {
  58. continue
  59. }
  60. found = true
  61. line = formatDef[1]
  62. } else {
  63. line = scanner.Text()
  64. }
  65. // Look for a definition end
  66. re = regexp.MustCompile(`^\s*(.*?)\s*(;|$)`)
  67. lineSplit := re.FindStringSubmatch(line)
  68. if l := len(lineSplit[1]); l > 2 {
  69. format += lineSplit[1][1 : l-1]
  70. }
  71. if lineSplit[2] == ";" {
  72. break
  73. }
  74. }
  75. if !found {
  76. err = fmt.Errorf("`log_format %v` not found in given config", name)
  77. } else {
  78. err = scanner.Err()
  79. }
  80. parser = NewParser(format)
  81. return
  82. }