parser.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. package indexer
  2. import (
  3. "strconv"
  4. "strings"
  5. "time"
  6. "github.com/0xJacky/Nginx-UI/internal/geolite"
  7. "github.com/0xJacky/Nginx-UI/internal/nginx_log/parser"
  8. "github.com/uozi-tech/cosy/logger"
  9. )
  10. // Global parser instance
  11. var (
  12. logParser *parser.OptimizedParser // Use the concrete type
  13. )
  14. func init() {
  15. // Initialize the parser with all its dependencies during package initialization.
  16. uaParser := parser.NewSimpleUserAgentParser()
  17. var geoIPService parser.GeoIPService
  18. geoService, err := geolite.GetService()
  19. if err != nil {
  20. logger.Warnf("Failed to initialize GeoIP service, geo-enrichment will be disabled: %v", err)
  21. } else {
  22. geoIPService = parser.NewGeoLiteAdapter(geoService)
  23. }
  24. // Create the optimized parser with real dependencies.
  25. logParser = parser.NewOptimizedParser(nil, uaParser, geoIPService)
  26. }
  27. // ParseLogLine parses a raw log line into a structured LogDocument
  28. func ParseLogLine(line string) (*LogDocument, error) {
  29. entry, err := logParser.ParseLine(line)
  30. if err != nil {
  31. return nil, err
  32. }
  33. // Convert parser.AccessLogEntry to indexer.LogDocument
  34. // This mapping is necessary because the indexer and parser might have different data structures.
  35. logDoc := &LogDocument{
  36. Timestamp: entry.Timestamp,
  37. IP: entry.IP,
  38. RegionCode: entry.RegionCode,
  39. Province: entry.Province,
  40. City: entry.City,
  41. Method: entry.Method,
  42. Path: entry.Path,
  43. PathExact: entry.Path, // Use the same for now
  44. Protocol: entry.Protocol,
  45. Status: entry.Status,
  46. BytesSent: entry.BytesSent,
  47. Referer: entry.Referer,
  48. UserAgent: entry.UserAgent,
  49. Browser: entry.Browser,
  50. BrowserVer: entry.BrowserVer,
  51. OS: entry.OS,
  52. OSVersion: entry.OSVersion,
  53. DeviceType: entry.DeviceType,
  54. RequestTime: entry.RequestTime,
  55. Raw: entry.Raw,
  56. }
  57. if entry.UpstreamTime != nil {
  58. logDoc.UpstreamTime = entry.UpstreamTime
  59. }
  60. return logDoc, nil
  61. }
  62. // Quick parse for request field "GET /path HTTP/1.1"
  63. func parseRequestField(request string) (method, path, protocol string) {
  64. parts := strings.Split(request, " ")
  65. if len(parts) == 3 {
  66. return parts[0], parts[1], parts[2]
  67. }
  68. return "UNKNOWN", request, "UNKNOWN"
  69. }
  70. // Quick parse for timestamp, e.g., "02/Jan/2006:15:04:05 -0700"
  71. func parseTimestamp(ts string) int64 {
  72. t, err := time.Parse("02/Jan/2006:15:04:05 -0700", ts)
  73. if err != nil {
  74. return 0
  75. }
  76. return t.Unix()
  77. }
  78. // Quick string to int64 conversion
  79. func toInt64(s string) int64 {
  80. val, _ := strconv.ParseInt(s, 10, 64)
  81. return val
  82. }
  83. // Quick string to int conversion
  84. func toInt(s string) int {
  85. val, _ := strconv.Atoi(s)
  86. return val
  87. }