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