master
go 890 lines 22.6 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package weblog
4
5 import (
6 "errors"
7 "fmt"
8
9 "github.com/netdata/netdata/go/plugins/plugin/framework/collectorapi"
10 )
11
12 type (
13 Charts = collectorapi.Charts
14 Chart = collectorapi.Chart
15 Dims = collectorapi.Dims
16 Dim = collectorapi.Dim
17 )
18
19 const (
20 prioReqTotal = collectorapi.Priority + iota
21 prioReqExcluded
22 prioReqType
23
24 prioRespCodesClass
25 prioRespCodes
26 prioRespCodes1xx
27 prioRespCodes2xx
28 prioRespCodes3xx
29 prioRespCodes4xx
30 prioRespCodes5xx
31
32 prioBandwidth
33
34 prioReqProcTime
35 prioRespTimeHist
36 prioUpsRespTime
37 prioUpsRespTimeHist
38
39 prioUniqIP
40
41 prioReqVhost
42 prioReqPort
43 prioReqScheme
44 prioReqMethod
45 prioReqVersion
46 prioReqIPProto
47 prioReqSSLProto
48 prioReqSSLCipherSuite
49
50 prioReqCustomFieldPattern // chart per custom field, alphabetical order
51 prioReqCustomTimeField // chart per custom time field, alphabetical order
52 prioReqCustomTimeFieldHist // histogram chart per custom time field
53 prioReqURLPattern
54 prioURLPatternStats
55
56 prioReqCustomNumericFieldSummary // 3 charts per url pattern, alphabetical order
57 )
58
59 // NOTE: inconsistency with python web_log
60 // TODO: current histogram charts are misleading in netdata
61
62 // Requests
63 var (
64 reqTotal = Chart{
65 ID: "requests",
66 Title: "Total Requests",
67 Units: "requests/s",
68 Fam: "requests",
69 Ctx: "web_log.requests",
70 Priority: prioReqTotal,
71 Dims: Dims{
72 {ID: "requests", Algo: collectorapi.Incremental},
73 },
74 }
75 reqExcluded = Chart{
76 ID: "excluded_requests",
77 Title: "Excluded Requests",
78 Units: "requests/s",
79 Fam: "requests",
80 Ctx: "web_log.excluded_requests",
81 Type: collectorapi.Stacked,
82 Priority: prioReqExcluded,
83 Dims: Dims{
84 {ID: "req_unmatched", Name: "unmatched", Algo: collectorapi.Incremental},
85 },
86 }
87 // netdata specific grouping
88 reqTypes = Chart{
89 ID: "requests_by_type",
90 Title: "Requests By Type",
91 Units: "requests/s",
92 Fam: "requests",
93 Ctx: "web_log.type_requests",
94 Type: collectorapi.Stacked,
95 Priority: prioReqType,
96 Dims: Dims{
97 {ID: "req_type_success", Name: "success", Algo: collectorapi.Incremental},
98 {ID: "req_type_bad", Name: "bad", Algo: collectorapi.Incremental},
99 {ID: "req_type_redirect", Name: "redirect", Algo: collectorapi.Incremental},
100 {ID: "req_type_error", Name: "error", Algo: collectorapi.Incremental},
101 },
102 }
103 )
104
105 // Responses
106 var (
107 respCodeClass = Chart{
108 ID: "responses_by_status_code_class",
109 Title: "Responses By Status Code Class",
110 Units: "responses/s",
111 Fam: "responses",
112 Ctx: "web_log.status_code_class_responses",
113 Type: collectorapi.Stacked,
114 Priority: prioRespCodesClass,
115 Dims: Dims{
116 {ID: "resp_2xx", Name: "2xx", Algo: collectorapi.Incremental},
117 {ID: "resp_5xx", Name: "5xx", Algo: collectorapi.Incremental},
118 {ID: "resp_3xx", Name: "3xx", Algo: collectorapi.Incremental},
119 {ID: "resp_4xx", Name: "4xx", Algo: collectorapi.Incremental},
120 {ID: "resp_1xx", Name: "1xx", Algo: collectorapi.Incremental},
121 },
122 }
123 respCodes = Chart{
124 ID: "responses_by_status_code",
125 Title: "Responses By Status Code",
126 Units: "responses/s",
127 Fam: "responses",
128 Ctx: "web_log.status_code_responses",
129 Type: collectorapi.Stacked,
130 Priority: prioRespCodes,
131 }
132 respCodes1xx = Chart{
133 ID: "status_code_class_1xx_responses",
134 Title: "Informational Responses By Status Code",
135 Units: "responses/s",
136 Fam: "responses",
137 Ctx: "web_log.status_code_class_1xx_responses",
138 Type: collectorapi.Stacked,
139 Priority: prioRespCodes1xx,
140 }
141 respCodes2xx = Chart{
142 ID: "status_code_class_2xx_responses",
143 Title: "Successful Responses By Status Code",
144 Units: "responses/s",
145 Fam: "responses",
146 Ctx: "web_log.status_code_class_2xx_responses",
147 Type: collectorapi.Stacked,
148 Priority: prioRespCodes2xx,
149 }
150 respCodes3xx = Chart{
151 ID: "status_code_class_3xx_responses",
152 Title: "Redirects Responses By Status Code",
153 Units: "responses/s",
154 Fam: "responses",
155 Ctx: "web_log.status_code_class_3xx_responses",
156 Type: collectorapi.Stacked,
157 Priority: prioRespCodes3xx,
158 }
159 respCodes4xx = Chart{
160 ID: "status_code_class_4xx_responses",
161 Title: "Client Errors Responses By Status Code",
162 Units: "responses/s",
163 Fam: "responses",
164 Ctx: "web_log.status_code_class_4xx_responses",
165 Type: collectorapi.Stacked,
166 Priority: prioRespCodes4xx,
167 }
168 respCodes5xx = Chart{
169 ID: "status_code_class_5xx_responses",
170 Title: "Server Errors Responses By Status Code",
171 Units: "responses/s",
172 Fam: "responses",
173 Ctx: "web_log.status_code_class_5xx_responses",
174 Type: collectorapi.Stacked,
175 Priority: prioRespCodes5xx,
176 }
177 )
178
179 // Bandwidth
180 var (
181 bandwidth = Chart{
182 ID: "bandwidth",
183 Title: "Bandwidth",
184 Units: "kilobits/s",
185 Fam: "bandwidth",
186 Ctx: "web_log.bandwidth",
187 Type: collectorapi.Area,
188 Priority: prioBandwidth,
189 Dims: Dims{
190 {ID: "bytes_received", Name: "received", Algo: collectorapi.Incremental, Mul: 8, Div: 1000},
191 {ID: "bytes_sent", Name: "sent", Algo: collectorapi.Incremental, Mul: -8, Div: 1000},
192 },
193 }
194 )
195
196 // Timings
197 var (
198 reqProcTime = Chart{
199 ID: "request_processing_time",
200 Title: "Request Processing Time",
201 Units: "milliseconds",
202 Fam: "timings",
203 Ctx: "web_log.request_processing_time",
204 Priority: prioReqProcTime,
205 Dims: Dims{
206 {ID: "req_proc_time_min", Name: "min", Div: 1000},
207 {ID: "req_proc_time_max", Name: "max", Div: 1000},
208 {ID: "req_proc_time_avg", Name: "avg", Div: 1000},
209 },
210 }
211 reqProcTimeHist = Chart{
212 ID: "requests_processing_time_histogram",
213 Title: "Requests Processing Time Histogram",
214 Units: "requests/s",
215 Fam: "timings",
216 Ctx: "web_log.requests_processing_time_histogram",
217 Priority: prioRespTimeHist,
218 }
219 )
220
221 // Upstream
222 var (
223 upsRespTime = Chart{
224 ID: "upstream_response_time",
225 Title: "Upstream Response Time",
226 Units: "milliseconds",
227 Fam: "timings",
228 Ctx: "web_log.upstream_response_time",
229 Priority: prioUpsRespTime,
230 Dims: Dims{
231 {ID: "upstream_resp_time_min", Name: "min", Div: 1000},
232 {ID: "upstream_resp_time_max", Name: "max", Div: 1000},
233 {ID: "upstream_resp_time_avg", Name: "avg", Div: 1000},
234 },
235 }
236 upsRespTimeHist = Chart{
237 ID: "upstream_responses_time_histogram",
238 Title: "Upstream Responses Time Histogram",
239 Units: "responses/s",
240 Fam: "timings",
241 Ctx: "web_log.upstream_responses_time_histogram",
242 Priority: prioUpsRespTimeHist,
243 }
244 )
245
246 // Clients
247 var (
248 uniqIPsCurPoll = Chart{
249 ID: "current_poll_uniq_clients",
250 Title: "Current Poll Unique Clients",
251 Units: "clients",
252 Fam: "client",
253 Ctx: "web_log.current_poll_uniq_clients",
254 Type: collectorapi.Stacked,
255 Priority: prioUniqIP,
256 Dims: Dims{
257 {ID: "uniq_ipv4", Name: "ipv4", Algo: collectorapi.Absolute},
258 {ID: "uniq_ipv6", Name: "ipv6", Algo: collectorapi.Absolute},
259 },
260 }
261 )
262
263 // Request By N
264 var (
265 reqByVhost = Chart{
266 ID: "requests_by_vhost",
267 Title: "Requests By Vhost",
268 Units: "requests/s",
269 Fam: "vhost",
270 Ctx: "web_log.vhost_requests",
271 Type: collectorapi.Stacked,
272 Priority: prioReqVhost,
273 }
274 reqByPort = Chart{
275 ID: "requests_by_port",
276 Title: "Requests By Port",
277 Units: "requests/s",
278 Fam: "port",
279 Ctx: "web_log.port_requests",
280 Type: collectorapi.Stacked,
281 Priority: prioReqPort,
282 }
283 reqByScheme = Chart{
284 ID: "requests_by_scheme",
285 Title: "Requests By Scheme",
286 Units: "requests/s",
287 Fam: "scheme",
288 Ctx: "web_log.scheme_requests",
289 Type: collectorapi.Stacked,
290 Priority: prioReqScheme,
291 Dims: Dims{
292 {ID: "req_http_scheme", Name: "http", Algo: collectorapi.Incremental},
293 {ID: "req_https_scheme", Name: "https", Algo: collectorapi.Incremental},
294 },
295 }
296 reqByMethod = Chart{
297 ID: "requests_by_http_method",
298 Title: "Requests By HTTP Method",
299 Units: "requests/s",
300 Fam: "http method",
301 Ctx: "web_log.http_method_requests",
302 Type: collectorapi.Stacked,
303 Priority: prioReqMethod,
304 }
305 reqByVersion = Chart{
306 ID: "requests_by_http_version",
307 Title: "Requests By HTTP Version",
308 Units: "requests/s",
309 Fam: "http version",
310 Ctx: "web_log.http_version_requests",
311 Type: collectorapi.Stacked,
312 Priority: prioReqVersion,
313 }
314 reqByIPProto = Chart{
315 ID: "requests_by_ip_proto",
316 Title: "Requests By IP Protocol",
317 Units: "requests/s",
318 Fam: "ip proto",
319 Ctx: "web_log.ip_proto_requests",
320 Type: collectorapi.Stacked,
321 Priority: prioReqIPProto,
322 Dims: Dims{
323 {ID: "req_ipv4", Name: "ipv4", Algo: collectorapi.Incremental},
324 {ID: "req_ipv6", Name: "ipv6", Algo: collectorapi.Incremental},
325 },
326 }
327 reqBySSLProto = Chart{
328 ID: "requests_by_ssl_proto",
329 Title: "Requests By SSL Connection Protocol",
330 Units: "requests/s",
331 Fam: "ssl conn",
332 Ctx: "web_log.ssl_proto_requests",
333 Type: collectorapi.Stacked,
334 Priority: prioReqSSLProto,
335 }
336 reqBySSLCipherSuite = Chart{
337 ID: "requests_by_ssl_cipher_suite",
338 Title: "Requests By SSL Connection Cipher Suite",
339 Units: "requests/s",
340 Fam: "ssl conn",
341 Ctx: "web_log.ssl_cipher_suite_requests",
342 Type: collectorapi.Stacked,
343 Priority: prioReqSSLCipherSuite,
344 }
345 )
346
347 // Request By N Patterns
348 var (
349 reqByURLPattern = Chart{
350 ID: "requests_by_url_pattern",
351 Title: "URL Field Requests By Pattern",
352 Units: "requests/s",
353 Fam: "url ptn",
354 Ctx: "web_log.url_pattern_requests",
355 Type: collectorapi.Stacked,
356 Priority: prioReqURLPattern,
357 }
358 reqByCustomFieldPattern = Chart{
359 ID: "custom_field_%s_requests_by_pattern",
360 Title: "Custom Field %s Requests By Pattern",
361 Units: "requests/s",
362 Fam: "custom field ptn",
363 Ctx: "web_log.custom_field_pattern_requests",
364 Type: collectorapi.Stacked,
365 Priority: prioReqCustomFieldPattern,
366 }
367 )
368
369 // custom time field
370 var (
371 reqByCustomTimeField = Chart{
372 ID: "custom_time_field_%s_summary",
373 Title: `Custom Time Field "%s" Summary`,
374 Units: "milliseconds",
375 Fam: "custom time field",
376 Ctx: "web_log.custom_time_field_summary",
377 Priority: prioReqCustomTimeField,
378 Dims: Dims{
379 {ID: "custom_time_field_%s_time_min", Name: "min", Div: 1000},
380 {ID: "custom_time_field_%s_time_max", Name: "max", Div: 1000},
381 {ID: "custom_time_field_%s_time_avg", Name: "avg", Div: 1000},
382 },
383 }
384 reqByCustomTimeFieldHist = Chart{
385 ID: "custom_time_field_%s_histogram",
386 Title: `Custom Time Field "%s" Histogram`,
387 Units: "observations",
388 Fam: "custom time field",
389 Ctx: "web_log.custom_time_field_histogram",
390 Priority: prioReqCustomTimeFieldHist,
391 }
392 )
393
394 var (
395 customNumericFieldSummaryChartTmpl = Chart{
396 ID: "custom_numeric_field_%s_summary",
397 Title: "Custom Numeric Field Summary",
398 Units: "",
399 Fam: "custom numeric fields",
400 Ctx: "web_log.custom_numeric_field_%s_summary",
401 Priority: prioReqCustomNumericFieldSummary,
402 Dims: Dims{
403 {ID: "custom_numeric_field_%s_summary_min", Name: "min"},
404 {ID: "custom_numeric_field_%s_summary_max", Name: "max"},
405 {ID: "custom_numeric_field_%s_summary_avg", Name: "avg"},
406 },
407 }
408 )
409
410 // URL pattern stats
411 var (
412 urlPatternRespCodes = Chart{
413 ID: "url_pattern_%s_responses_by_status_code",
414 Title: "Responses By Status Code",
415 Units: "responses/s",
416 Fam: "url ptn %s",
417 Ctx: "web_log.url_pattern_status_code_responses",
418 Type: collectorapi.Stacked,
419 Priority: prioURLPatternStats,
420 }
421 urlPatternReqMethods = Chart{
422 ID: "url_pattern_%s_requests_by_http_method",
423 Title: "Requests By HTTP Method",
424 Units: "requests/s",
425 Fam: "url ptn %s",
426 Ctx: "web_log.url_pattern_http_method_requests",
427 Type: collectorapi.Stacked,
428 Priority: prioURLPatternStats + 1,
429 }
430 urlPatternBandwidth = Chart{
431 ID: "url_pattern_%s_bandwidth",
432 Title: "Bandwidth",
433 Units: "kilobits/s",
434 Fam: "url ptn %s",
435 Ctx: "web_log.url_pattern_bandwidth",
436 Type: collectorapi.Area,
437 Priority: prioURLPatternStats + 2,
438 Dims: Dims{
439 {ID: "url_ptn_%s_bytes_received", Name: "received", Algo: collectorapi.Incremental, Mul: 8, Div: 1000},
440 {ID: "url_ptn_%s_bytes_sent", Name: "sent", Algo: collectorapi.Incremental, Mul: -8, Div: 1000},
441 },
442 }
443 urlPatternReqProcTime = Chart{
444 ID: "url_pattern_%s_request_processing_time",
445 Title: "Request Processing Time",
446 Units: "milliseconds",
447 Fam: "url ptn %s",
448 Ctx: "web_log.url_pattern_request_processing_time",
449 Priority: prioURLPatternStats + 3,
450 Dims: Dims{
451 {ID: "url_ptn_%s_req_proc_time_min", Name: "min", Div: 1000},
452 {ID: "url_ptn_%s_req_proc_time_max", Name: "max", Div: 1000},
453 {ID: "url_ptn_%s_req_proc_time_avg", Name: "avg", Div: 1000},
454 },
455 }
456 )
457
458 func newReqProcTimeHistChart(histogram []float64) (*Chart, error) {
459 chart := reqProcTimeHist.Copy()
460 for i, v := range histogram {
461 dim := &Dim{
462 ID: fmt.Sprintf("req_proc_time_hist_bucket_%d", i+1),
463 Name: fmt.Sprintf("%.3f", v),
464 Algo: collectorapi.Incremental,
465 }
466 if err := chart.AddDim(dim); err != nil {
467 return nil, err
468 }
469 }
470 if err := chart.AddDim(&Dim{
471 ID: "req_proc_time_hist_count",
472 Name: "+Inf",
473 Algo: collectorapi.Incremental,
474 }); err != nil {
475 return nil, err
476 }
477 return chart, nil
478 }
479
480 func newUpsRespTimeHistChart(histogram []float64) (*Chart, error) {
481 chart := upsRespTimeHist.Copy()
482 for i, v := range histogram {
483 dim := &Dim{
484 ID: fmt.Sprintf("upstream_resp_time_hist_bucket_%d", i+1),
485 Name: fmt.Sprintf("%.3f", v),
486 Algo: collectorapi.Incremental,
487 }
488 if err := chart.AddDim(dim); err != nil {
489 return nil, err
490 }
491 }
492 if err := chart.AddDim(&Dim{
493 ID: "upstream_resp_time_hist_count",
494 Name: "+Inf",
495 Algo: collectorapi.Incremental,
496 }); err != nil {
497 return nil, err
498 }
499 return chart, nil
500 }
501
502 func newURLPatternChart(patterns []userPattern) (*Chart, error) {
503 chart := reqByURLPattern.Copy()
504 for _, p := range patterns {
505 dim := &Dim{
506 ID: "req_url_ptn_" + p.Name,
507 Name: p.Name,
508 Algo: collectorapi.Incremental,
509 }
510 if err := chart.AddDim(dim); err != nil {
511 return nil, err
512 }
513 }
514 return chart, nil
515 }
516
517 func newURLPatternRespCodesChart(name string) *Chart {
518 chart := urlPatternRespCodes.Copy()
519 chart.ID = fmt.Sprintf(chart.ID, name)
520 chart.Fam = fmt.Sprintf(chart.Fam, name)
521 return chart
522 }
523
524 func newURLPatternReqMethodsChart(name string) *Chart {
525 chart := urlPatternReqMethods.Copy()
526 chart.ID = fmt.Sprintf(chart.ID, name)
527 chart.Fam = fmt.Sprintf(chart.Fam, name)
528 return chart
529 }
530
531 func newURLPatternBandwidthChart(name string) *Chart {
532 chart := urlPatternBandwidth.Copy()
533 chart.ID = fmt.Sprintf(chart.ID, name)
534 chart.Fam = fmt.Sprintf(chart.Fam, name)
535 for _, d := range chart.Dims {
536 d.ID = fmt.Sprintf(d.ID, name)
537 }
538 return chart
539 }
540
541 func newURLPatternReqProcTimeChart(name string) *Chart {
542 chart := urlPatternReqProcTime.Copy()
543 chart.ID = fmt.Sprintf(chart.ID, name)
544 chart.Fam = fmt.Sprintf(chart.Fam, name)
545 for _, d := range chart.Dims {
546 d.ID = fmt.Sprintf(d.ID, name)
547 }
548 return chart
549 }
550
551 func newCustomFieldCharts(fields []customField) (Charts, error) {
552 charts := Charts{}
553 for _, f := range fields {
554 chart, err := newCustomFieldChart(f)
555 if err != nil {
556 return nil, err
557 }
558 if err := charts.Add(chart); err != nil {
559 return nil, err
560 }
561 }
562 return charts, nil
563 }
564
565 func newCustomFieldChart(f customField) (*Chart, error) {
566 chart := reqByCustomFieldPattern.Copy()
567 chart.ID = fmt.Sprintf(chart.ID, f.Name)
568 chart.Title = fmt.Sprintf(chart.Title, f.Name)
569 for _, p := range f.Patterns {
570 dim := &Dim{
571 ID: fmt.Sprintf("custom_field_%s_%s", f.Name, p.Name),
572 Name: p.Name,
573 Algo: collectorapi.Incremental,
574 }
575 if err := chart.AddDim(dim); err != nil {
576 return nil, err
577 }
578 }
579 return chart, nil
580 }
581
582 func newCustomTimeFieldCharts(fields []customTimeField) (Charts, error) {
583 charts := Charts{}
584 for i, f := range fields {
585 chartTime, err := newCustomTimeFieldChart(f)
586 if err != nil {
587 return nil, err
588 }
589 chartTime.Priority += i
590 if err := charts.Add(chartTime); err != nil {
591 return nil, err
592 }
593 if len(f.Histogram) < 1 {
594 continue
595 }
596
597 chartHist, err := newCustomTimeFieldHistChart(f)
598 if err != nil {
599 return nil, err
600 }
601 chartHist.Priority += i
602
603 if err := charts.Add(chartHist); err != nil {
604 return nil, err
605 }
606 }
607 return charts, nil
608 }
609
610 func newCustomTimeFieldChart(f customTimeField) (*Chart, error) {
611 chart := reqByCustomTimeField.Copy()
612 chart.ID = fmt.Sprintf(chart.ID, f.Name)
613 chart.Title = fmt.Sprintf(chart.Title, f.Name)
614 for _, d := range chart.Dims {
615 d.ID = fmt.Sprintf(d.ID, f.Name)
616 }
617 return chart, nil
618 }
619
620 func newCustomTimeFieldHistChart(f customTimeField) (*Chart, error) {
621 chart := reqByCustomTimeFieldHist.Copy()
622 chart.ID = fmt.Sprintf(chart.ID, f.Name)
623 chart.Title = fmt.Sprintf(chart.Title, f.Name)
624 for i, v := range f.Histogram {
625 dim := &Dim{
626 ID: fmt.Sprintf("custom_time_field_%s_time_hist_bucket_%d", f.Name, i+1),
627 Name: fmt.Sprintf("%.3f", v),
628 Algo: collectorapi.Incremental,
629 }
630 if err := chart.AddDim(dim); err != nil {
631 return nil, err
632 }
633 }
634 if err := chart.AddDim(&Dim{
635 ID: fmt.Sprintf("custom_time_field_%s_time_hist_count", f.Name),
636 Name: "+Inf",
637 Algo: collectorapi.Incremental,
638 }); err != nil {
639 return nil, err
640 }
641 return chart, nil
642 }
643
644 func (c *Collector) createCharts(line *logLine) error {
645 if line.empty() {
646 return errors.New("empty line")
647 }
648 c.charts = nil
649 // Following charts are created during runtime:
650 // - reqBySSLProto, reqBySSLCipherSuite - it is likely line has no SSL stuff at this moment
651 charts := &Charts{
652 reqTotal.Copy(),
653 reqExcluded.Copy(),
654 }
655 if line.hasVhost() {
656 if err := addVhostCharts(charts); err != nil {
657 return err
658 }
659 }
660 if line.hasPort() {
661 if err := addPortCharts(charts); err != nil {
662 return err
663 }
664 }
665 if line.hasReqScheme() {
666 if err := addSchemeCharts(charts); err != nil {
667 return err
668 }
669 }
670 if line.hasReqClient() {
671 if err := addClientCharts(charts); err != nil {
672 return err
673 }
674 }
675 if line.hasReqMethod() {
676 if err := addMethodCharts(charts, c.URLPatterns); err != nil {
677 return err
678 }
679 }
680 if line.hasReqURL() {
681 if err := addURLCharts(charts, c.URLPatterns); err != nil {
682 return err
683 }
684 }
685 if line.hasReqProto() {
686 if err := addReqProtoCharts(charts); err != nil {
687 return err
688 }
689 }
690 if line.hasRespCode() {
691 if err := addRespCodesCharts(charts, c.GroupRespCodes); err != nil {
692 return err
693 }
694 }
695 if line.hasReqSize() || line.hasRespSize() {
696 if err := addBandwidthCharts(charts, c.URLPatterns); err != nil {
697 return err
698 }
699 }
700 if line.hasReqProcTime() {
701 if err := addReqProcTimeCharts(charts, c.Histogram, c.URLPatterns); err != nil {
702 return err
703 }
704 }
705 if line.hasUpsRespTime() {
706 if err := addUpstreamRespTimeCharts(charts, c.Histogram); err != nil {
707 return err
708 }
709 }
710 if line.hasCustomFields() {
711 if len(c.CustomFields) > 0 {
712 if err := addCustomFieldsCharts(charts, c.CustomFields); err != nil {
713 return err
714 }
715 }
716 if len(c.CustomTimeFields) > 0 {
717 if err := addCustomTimeFieldsCharts(charts, c.CustomTimeFields); err != nil {
718 return err
719 }
720 }
721 if len(c.CustomNumericFields) > 0 {
722 if err := addCustomNumericFieldsCharts(charts, c.CustomNumericFields); err != nil {
723 return err
724 }
725 }
726 }
727
728 c.charts = charts
729
730 return nil
731 }
732
733 func addVhostCharts(charts *Charts) error {
734 return charts.Add(reqByVhost.Copy())
735 }
736
737 func addPortCharts(charts *Charts) error {
738 return charts.Add(reqByPort.Copy())
739 }
740
741 func addSchemeCharts(charts *Charts) error {
742 return charts.Add(reqByScheme.Copy())
743 }
744
745 func addClientCharts(charts *Charts) error {
746 if err := charts.Add(reqByIPProto.Copy()); err != nil {
747 return err
748 }
749 return charts.Add(uniqIPsCurPoll.Copy())
750 }
751
752 func addMethodCharts(charts *Charts, patterns []userPattern) error {
753 if err := charts.Add(reqByMethod.Copy()); err != nil {
754 return err
755 }
756
757 for _, p := range patterns {
758 chart := newURLPatternReqMethodsChart(p.Name)
759 if err := charts.Add(chart); err != nil {
760 return err
761 }
762 }
763 return nil
764 }
765
766 func addURLCharts(charts *Charts, patterns []userPattern) error {
767 if len(patterns) == 0 {
768 return nil
769 }
770 chart, err := newURLPatternChart(patterns)
771 if err != nil {
772 return err
773 }
774 if err := charts.Add(chart); err != nil {
775 return err
776 }
777
778 for _, p := range patterns {
779 chart := newURLPatternRespCodesChart(p.Name)
780 if err := charts.Add(chart); err != nil {
781 return err
782 }
783 }
784 return nil
785 }
786
787 func addReqProtoCharts(charts *Charts) error {
788 return charts.Add(reqByVersion.Copy())
789 }
790
791 func addRespCodesCharts(charts *Charts, group bool) error {
792 if err := charts.Add(reqTypes.Copy()); err != nil {
793 return err
794 }
795 if err := charts.Add(respCodeClass.Copy()); err != nil {
796 return err
797 }
798 if !group {
799 return charts.Add(respCodes.Copy())
800 }
801 for _, c := range []Chart{respCodes1xx, respCodes2xx, respCodes3xx, respCodes4xx, respCodes5xx} {
802 if err := charts.Add(c.Copy()); err != nil {
803 return err
804 }
805 }
806 return nil
807 }
808
809 func addBandwidthCharts(charts *Charts, patterns []userPattern) error {
810 if err := charts.Add(bandwidth.Copy()); err != nil {
811 return err
812 }
813
814 for _, p := range patterns {
815 chart := newURLPatternBandwidthChart(p.Name)
816 if err := charts.Add(chart); err != nil {
817 return err
818 }
819 }
820 return nil
821 }
822
823 func addReqProcTimeCharts(charts *Charts, histogram []float64, patterns []userPattern) error {
824 if err := charts.Add(reqProcTime.Copy()); err != nil {
825 return err
826 }
827 for _, p := range patterns {
828 chart := newURLPatternReqProcTimeChart(p.Name)
829 if err := charts.Add(chart); err != nil {
830 return err
831 }
832 }
833 if len(histogram) == 0 {
834 return nil
835 }
836 chart, err := newReqProcTimeHistChart(histogram)
837 if err != nil {
838 return err
839 }
840 return charts.Add(chart)
841 }
842
843 func addUpstreamRespTimeCharts(charts *Charts, histogram []float64) error {
844 if err := charts.Add(upsRespTime.Copy()); err != nil {
845 return err
846 }
847 if len(histogram) == 0 {
848 return nil
849 }
850 chart, err := newUpsRespTimeHistChart(histogram)
851 if err != nil {
852 return err
853 }
854 return charts.Add(chart)
855 }
856
857 func addCustomFieldsCharts(charts *Charts, fields []customField) error {
858 cs, err := newCustomFieldCharts(fields)
859 if err != nil {
860 return err
861 }
862 return charts.Add(cs...)
863 }
864
865 func addCustomTimeFieldsCharts(charts *Charts, fields []customTimeField) error {
866 cs, err := newCustomTimeFieldCharts(fields)
867 if err != nil {
868 return err
869 }
870 return charts.Add(cs...)
871 }
872
873 func addCustomNumericFieldsCharts(charts *collectorapi.Charts, fields []customNumericField) error {
874 for _, f := range fields {
875 chart := customNumericFieldSummaryChartTmpl.Copy()
876 chart.ID = fmt.Sprintf(chart.ID, f.Name)
877 chart.Units = f.Units
878 chart.Ctx = fmt.Sprintf(chart.Ctx, f.Name)
879 for _, dim := range chart.Dims {
880 dim.ID = fmt.Sprintf(dim.ID, f.Name)
881 dim.Div = f.Divisor
882 }
883
884 if err := charts.Add(chart); err != nil {
885 return err
886 }
887 }
888
889 return nil
890 }