github.com/netdata/go.d.plugin@v0.58.1/modules/weblog/init.go (about) 1 // SPDX-License-Identifier: GPL-3.0-or-later 2 3 package weblog 4 5 import ( 6 "errors" 7 "fmt" 8 "strings" 9 10 "github.com/netdata/go.d.plugin/pkg/logs" 11 "github.com/netdata/go.d.plugin/pkg/matcher" 12 ) 13 14 type pattern struct { 15 name string 16 matcher.Matcher 17 } 18 19 func newPattern(up userPattern) (*pattern, error) { 20 if up.Name == "" || up.Match == "" { 21 return nil, errors.New("empty 'name' or 'match'") 22 } 23 24 m, err := matcher.Parse(up.Match) 25 if err != nil { 26 return nil, err 27 } 28 return &pattern{name: up.Name, Matcher: m}, nil 29 } 30 31 func (w *WebLog) createURLPatterns() error { 32 if len(w.URLPatterns) == 0 { 33 w.Debug("skipping URL patterns creating, no patterns provided") 34 return nil 35 } 36 w.Debug("starting URL patterns creating") 37 for _, up := range w.URLPatterns { 38 p, err := newPattern(up) 39 if err != nil { 40 return fmt.Errorf("create pattern %+v: %v", up, err) 41 } 42 w.Debugf("created pattern '%s', type '%T', match '%s'", p.name, p.Matcher, up.Match) 43 w.urlPatterns = append(w.urlPatterns, p) 44 } 45 w.Debugf("created %d URL pattern(s)", len(w.URLPatterns)) 46 return nil 47 } 48 49 func (w *WebLog) createCustomFields() error { 50 if len(w.CustomFields) == 0 { 51 w.Debug("skipping custom fields creating, no custom fields provided") 52 return nil 53 } 54 55 w.Debug("starting custom fields creating") 56 w.customFields = make(map[string][]*pattern) 57 for i, cf := range w.CustomFields { 58 if cf.Name == "" { 59 return fmt.Errorf("create custom field: name not set (field %d)", i+1) 60 } 61 for _, up := range cf.Patterns { 62 p, err := newPattern(up) 63 if err != nil { 64 return fmt.Errorf("create field '%s' pattern %+v: %v", cf.Name, up, err) 65 } 66 w.Debugf("created field '%s', pattern '%s', type '%T', match '%s'", cf.Name, p.name, p.Matcher, up.Match) 67 w.customFields[cf.Name] = append(w.customFields[cf.Name], p) 68 } 69 } 70 w.Debugf("created %d custom field(s)", len(w.CustomFields)) 71 return nil 72 } 73 74 func (w *WebLog) createCustomTimeFields() error { 75 if len(w.CustomTimeFields) == 0 { 76 w.Debug("skipping custom time fields creating, no custom time fields provided") 77 return nil 78 } 79 80 w.Debug("starting custom time fields creating") 81 w.customTimeFields = make(map[string][]float64) 82 for i, ctf := range w.CustomTimeFields { 83 if ctf.Name == "" { 84 return fmt.Errorf("create custom field: name not set (field %d)", i+1) 85 } 86 w.customTimeFields[ctf.Name] = ctf.Histogram 87 w.Debugf("created time field '%s', histogram '%v'", ctf.Name, ctf.Histogram) 88 } 89 w.Debugf("created %d custom time field(s)", len(w.CustomTimeFields)) 90 return nil 91 } 92 93 func (w *WebLog) createCustomNumericFields() error { 94 if len(w.CustomNumericFields) == 0 { 95 w.Debug("no custom time fields provided") 96 return nil 97 } 98 99 w.Debugf("creating custom numeric fields for '%+v'", w.CustomNumericFields) 100 101 w.customNumericFields = make(map[string]bool) 102 103 for i := range w.CustomNumericFields { 104 v := w.CustomNumericFields[i] 105 if v.Name == "" { 106 return fmt.Errorf("custom numeric field (%d): 'name' not set", i+1) 107 } 108 if v.Units == "" { 109 return fmt.Errorf("custom numeric field (%s): 'units' not set", v.Name) 110 } 111 if v.Multiplier <= 0 { 112 v.Multiplier = 1 113 } 114 if v.Divisor <= 0 { 115 v.Divisor = 1 116 } 117 w.CustomNumericFields[i] = v 118 w.customNumericFields[v.Name] = true 119 } 120 121 return nil 122 } 123 124 func (w *WebLog) createLogLine() { 125 w.line = newEmptyLogLine() 126 127 for v := range w.customFields { 128 w.line.custom.fields[v] = struct{}{} 129 } 130 for v := range w.customTimeFields { 131 w.line.custom.fields[v] = struct{}{} 132 } 133 for v := range w.customNumericFields { 134 w.line.custom.fields[v] = struct{}{} 135 } 136 } 137 138 func (w *WebLog) createLogReader() error { 139 w.Cleanup() 140 w.Debug("starting log reader creating") 141 142 reader, err := logs.Open(w.Path, w.ExcludePath, w.Logger) 143 if err != nil { 144 return fmt.Errorf("creating log reader: %v", err) 145 } 146 147 w.Debugf("created log reader, current file '%s'", reader.CurrentFilename()) 148 w.file = reader 149 150 return nil 151 } 152 153 func (w *WebLog) createParser() error { 154 w.Debug("starting parser creating") 155 156 const readLinesNum = 100 157 158 lines, err := logs.ReadLastLines(w.file.CurrentFilename(), readLinesNum) 159 if err != nil { 160 return fmt.Errorf("failed to read last lines: %v", err) 161 } 162 163 var found bool 164 for _, line := range lines { 165 if line = strings.TrimSpace(line); line == "" { 166 continue 167 } 168 w.Debugf("last line: '%s'", line) 169 170 w.parser, err = w.newParser([]byte(line)) 171 if err != nil { 172 w.Debugf("failed to create parser from line: %v", err) 173 continue 174 } 175 176 w.line.reset() 177 178 if err = w.parser.Parse([]byte(line), w.line); err != nil { 179 w.Debugf("failed to parse line: %v", err) 180 continue 181 } 182 183 if err = w.line.verify(); err != nil { 184 w.Debugf("failed to verify line: %v", err) 185 continue 186 } 187 188 found = true 189 break 190 } 191 192 if !found { 193 return fmt.Errorf("failed to create log parser (file '%s')", w.file.CurrentFilename()) 194 } 195 196 return nil 197 }