master
go 164 lines 4.16 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package weblog
4
5 import (
6 "context"
7 _ "embed"
8 "fmt"
9
10 "github.com/netdata/netdata/go/plugins/plugin/framework/collectorapi"
11 "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/logs"
12 )
13
14 //go:embed "config_schema.json"
15 var configSchema string
16
17 func init() {
18 collectorapi.Register("web_log", collectorapi.Creator{
19 JobConfigSchema: configSchema,
20 Create: func() collectorapi.CollectorV1 { return New() },
21 Config: func() any { return &Config{} },
22 })
23 }
24
25 func New() *Collector {
26 return &Collector{
27 Config: Config{
28 ExcludePath: "*.gz",
29 GroupRespCodes: true,
30 ParserConfig: logs.ParserConfig{
31 LogType: typeAuto,
32 CSV: logs.CSVConfig{
33 FieldsPerRecord: -1,
34 Delimiter: " ",
35 TrimLeadingSpace: false,
36 CheckField: checkCSVFormatField,
37 },
38 LTSV: logs.LTSVConfig{
39 FieldDelimiter: "\t",
40 ValueDelimiter: ":",
41 },
42 RegExp: logs.RegExpConfig{},
43 JSON: logs.JSONConfig{},
44 },
45 },
46 }
47 }
48
49 type (
50 Config struct {
51 UpdateEvery int `yaml:"update_every,omitempty" json:"update_every"`
52 Path string `yaml:"path" json:"path"`
53 ExcludePath string `yaml:"exclude_path,omitempty" json:"exclude_path"`
54 logs.ParserConfig `yaml:",inline" json:""`
55 URLPatterns []userPattern `yaml:"url_patterns,omitempty" json:"url_patterns"`
56 CustomFields []customField `yaml:"custom_fields,omitempty" json:"custom_fields"`
57 CustomTimeFields []customTimeField `yaml:"custom_time_fields,omitempty" json:"custom_time_fields"`
58 CustomNumericFields []customNumericField `yaml:"custom_numeric_fields,omitempty" json:"custom_numeric_fields"`
59 Histogram []float64 `yaml:"histogram,omitempty" json:"histogram"`
60 GroupRespCodes bool `yaml:"group_response_codes" json:"group_response_codes"`
61 }
62 userPattern struct {
63 Name string `yaml:"name" json:"name"`
64 Match string `yaml:"match" json:"match"`
65 }
66 customField struct {
67 Name string `yaml:"name" json:"name"`
68 Patterns []userPattern `yaml:"patterns" json:"patterns"`
69 }
70 customTimeField struct {
71 Name string `yaml:"name" json:"name"`
72 Histogram []float64 `yaml:"histogram" json:"histogram"`
73 }
74 customNumericField struct {
75 Name string `yaml:"name" json:"name"`
76 Units string `yaml:"units" json:"units"`
77 Multiplier int `yaml:"multiplier,omitempty" json:"multiplier"`
78 Divisor int `yaml:"divisor,omitempty" json:"divisor"`
79 }
80 )
81
82 type Collector struct {
83 collectorapi.Base
84 Config `yaml:",inline" json:""`
85
86 charts *collectorapi.Charts
87
88 file *logs.Reader
89 parser logs.Parser
90 line *logLine
91
92 urlPatterns []*pattern
93 customFields map[string][]*pattern
94 customTimeFields map[string][]float64
95 customNumericFields map[string]bool
96
97 mx *metricsData
98 }
99
100 func (c *Collector) Configuration() any {
101 return c.Config
102 }
103
104 func (c *Collector) Init(context.Context) error {
105 if err := c.createURLPatterns(); err != nil {
106 return fmt.Errorf("init failed: %v", err)
107 }
108
109 if err := c.createCustomFields(); err != nil {
110 return fmt.Errorf("init failed: %v", err)
111 }
112
113 if err := c.createCustomTimeFields(); err != nil {
114 return fmt.Errorf("init failed: %v", err)
115 }
116
117 if err := c.createCustomNumericFields(); err != nil {
118 c.Errorf("init failed: %v", err)
119 }
120
121 c.createLogLine()
122 c.mx = newMetricsData(c.Config)
123
124 return nil
125 }
126
127 func (c *Collector) Check(context.Context) error {
128 // Note: these inits are here to make auto-detection retry working
129 if err := c.createLogReader(); err != nil {
130 return fmt.Errorf("failed to create log reader: %v", err)
131 }
132
133 if err := c.createParser(); err != nil {
134 return fmt.Errorf("failed to create parser: %v", err)
135 }
136
137 if err := c.createCharts(c.line); err != nil {
138 return fmt.Errorf("failed to create charts: %v", err)
139 }
140
141 return nil
142 }
143
144 func (c *Collector) Charts() *collectorapi.Charts {
145 return c.charts
146 }
147
148 func (c *Collector) Collect(context.Context) map[string]int64 {
149 mx, err := c.collect()
150 if err != nil {
151 c.Error(err)
152 }
153
154 if len(mx) == 0 {
155 return nil
156 }
157 return mx
158 }
159
160 func (c *Collector) Cleanup(context.Context) {
161 if c.file != nil {
162 _ = c.file.Close()
163 }
164 }