summaryrefslogtreecommitdiffstats
path: root/src/go/collectors/go.d.plugin/modules/weblog/charts.go
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/go/collectors/go.d.plugin/modules/weblog/charts.go890
1 files changed, 890 insertions, 0 deletions
diff --git a/src/go/collectors/go.d.plugin/modules/weblog/charts.go b/src/go/collectors/go.d.plugin/modules/weblog/charts.go
new file mode 100644
index 000000000..749a26ce7
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/weblog/charts.go
@@ -0,0 +1,890 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package weblog
+
+import (
+ "errors"
+ "fmt"
+
+ "github.com/netdata/netdata/go/go.d.plugin/agent/module"
+)
+
+type (
+ Charts = module.Charts
+ Chart = module.Chart
+ Dims = module.Dims
+ Dim = module.Dim
+)
+
+const (
+ prioReqTotal = module.Priority + iota
+ prioReqExcluded
+ prioReqType
+
+ prioRespCodesClass
+ prioRespCodes
+ prioRespCodes1xx
+ prioRespCodes2xx
+ prioRespCodes3xx
+ prioRespCodes4xx
+ prioRespCodes5xx
+
+ prioBandwidth
+
+ prioReqProcTime
+ prioRespTimeHist
+ prioUpsRespTime
+ prioUpsRespTimeHist
+
+ prioUniqIP
+
+ prioReqVhost
+ prioReqPort
+ prioReqScheme
+ prioReqMethod
+ prioReqVersion
+ prioReqIPProto
+ prioReqSSLProto
+ prioReqSSLCipherSuite
+
+ prioReqCustomFieldPattern // chart per custom field, alphabetical order
+ prioReqCustomTimeField // chart per custom time field, alphabetical order
+ prioReqCustomTimeFieldHist // histogram chart per custom time field
+ prioReqURLPattern
+ prioURLPatternStats
+
+ prioReqCustomNumericFieldSummary // 3 charts per url pattern, alphabetical order
+)
+
+// NOTE: inconsistency with python web_log
+// TODO: current histogram charts are misleading in netdata
+
+// Requests
+var (
+ reqTotal = Chart{
+ ID: "requests",
+ Title: "Total Requests",
+ Units: "requests/s",
+ Fam: "requests",
+ Ctx: "web_log.requests",
+ Priority: prioReqTotal,
+ Dims: Dims{
+ {ID: "requests", Algo: module.Incremental},
+ },
+ }
+ reqExcluded = Chart{
+ ID: "excluded_requests",
+ Title: "Excluded Requests",
+ Units: "requests/s",
+ Fam: "requests",
+ Ctx: "web_log.excluded_requests",
+ Type: module.Stacked,
+ Priority: prioReqExcluded,
+ Dims: Dims{
+ {ID: "req_unmatched", Name: "unmatched", Algo: module.Incremental},
+ },
+ }
+ // netdata specific grouping
+ reqTypes = Chart{
+ ID: "requests_by_type",
+ Title: "Requests By Type",
+ Units: "requests/s",
+ Fam: "requests",
+ Ctx: "web_log.type_requests",
+ Type: module.Stacked,
+ Priority: prioReqType,
+ Dims: Dims{
+ {ID: "req_type_success", Name: "success", Algo: module.Incremental},
+ {ID: "req_type_bad", Name: "bad", Algo: module.Incremental},
+ {ID: "req_type_redirect", Name: "redirect", Algo: module.Incremental},
+ {ID: "req_type_error", Name: "error", Algo: module.Incremental},
+ },
+ }
+)
+
+// Responses
+var (
+ respCodeClass = Chart{
+ ID: "responses_by_status_code_class",
+ Title: "Responses By Status Code Class",
+ Units: "responses/s",
+ Fam: "responses",
+ Ctx: "web_log.status_code_class_responses",
+ Type: module.Stacked,
+ Priority: prioRespCodesClass,
+ Dims: Dims{
+ {ID: "resp_2xx", Name: "2xx", Algo: module.Incremental},
+ {ID: "resp_5xx", Name: "5xx", Algo: module.Incremental},
+ {ID: "resp_3xx", Name: "3xx", Algo: module.Incremental},
+ {ID: "resp_4xx", Name: "4xx", Algo: module.Incremental},
+ {ID: "resp_1xx", Name: "1xx", Algo: module.Incremental},
+ },
+ }
+ respCodes = Chart{
+ ID: "responses_by_status_code",
+ Title: "Responses By Status Code",
+ Units: "responses/s",
+ Fam: "responses",
+ Ctx: "web_log.status_code_responses",
+ Type: module.Stacked,
+ Priority: prioRespCodes,
+ }
+ respCodes1xx = Chart{
+ ID: "status_code_class_1xx_responses",
+ Title: "Informational Responses By Status Code",
+ Units: "responses/s",
+ Fam: "responses",
+ Ctx: "web_log.status_code_class_1xx_responses",
+ Type: module.Stacked,
+ Priority: prioRespCodes1xx,
+ }
+ respCodes2xx = Chart{
+ ID: "status_code_class_2xx_responses",
+ Title: "Successful Responses By Status Code",
+ Units: "responses/s",
+ Fam: "responses",
+ Ctx: "web_log.status_code_class_2xx_responses",
+ Type: module.Stacked,
+ Priority: prioRespCodes2xx,
+ }
+ respCodes3xx = Chart{
+ ID: "status_code_class_3xx_responses",
+ Title: "Redirects Responses By Status Code",
+ Units: "responses/s",
+ Fam: "responses",
+ Ctx: "web_log.status_code_class_3xx_responses",
+ Type: module.Stacked,
+ Priority: prioRespCodes3xx,
+ }
+ respCodes4xx = Chart{
+ ID: "status_code_class_4xx_responses",
+ Title: "Client Errors Responses By Status Code",
+ Units: "responses/s",
+ Fam: "responses",
+ Ctx: "web_log.status_code_class_4xx_responses",
+ Type: module.Stacked,
+ Priority: prioRespCodes4xx,
+ }
+ respCodes5xx = Chart{
+ ID: "status_code_class_5xx_responses",
+ Title: "Server Errors Responses By Status Code",
+ Units: "responses/s",
+ Fam: "responses",
+ Ctx: "web_log.status_code_class_5xx_responses",
+ Type: module.Stacked,
+ Priority: prioRespCodes5xx,
+ }
+)
+
+// Bandwidth
+var (
+ bandwidth = Chart{
+ ID: "bandwidth",
+ Title: "Bandwidth",
+ Units: "kilobits/s",
+ Fam: "bandwidth",
+ Ctx: "web_log.bandwidth",
+ Type: module.Area,
+ Priority: prioBandwidth,
+ Dims: Dims{
+ {ID: "bytes_received", Name: "received", Algo: module.Incremental, Mul: 8, Div: 1000},
+ {ID: "bytes_sent", Name: "sent", Algo: module.Incremental, Mul: -8, Div: 1000},
+ },
+ }
+)
+
+// Timings
+var (
+ reqProcTime = Chart{
+ ID: "request_processing_time",
+ Title: "Request Processing Time",
+ Units: "milliseconds",
+ Fam: "timings",
+ Ctx: "web_log.request_processing_time",
+ Priority: prioReqProcTime,
+ Dims: Dims{
+ {ID: "req_proc_time_min", Name: "min", Div: 1000},
+ {ID: "req_proc_time_max", Name: "max", Div: 1000},
+ {ID: "req_proc_time_avg", Name: "avg", Div: 1000},
+ },
+ }
+ reqProcTimeHist = Chart{
+ ID: "requests_processing_time_histogram",
+ Title: "Requests Processing Time Histogram",
+ Units: "requests/s",
+ Fam: "timings",
+ Ctx: "web_log.requests_processing_time_histogram",
+ Priority: prioRespTimeHist,
+ }
+)
+
+// Upstream
+var (
+ upsRespTime = Chart{
+ ID: "upstream_response_time",
+ Title: "Upstream Response Time",
+ Units: "milliseconds",
+ Fam: "timings",
+ Ctx: "web_log.upstream_response_time",
+ Priority: prioUpsRespTime,
+ Dims: Dims{
+ {ID: "upstream_resp_time_min", Name: "min", Div: 1000},
+ {ID: "upstream_resp_time_max", Name: "max", Div: 1000},
+ {ID: "upstream_resp_time_avg", Name: "avg", Div: 1000},
+ },
+ }
+ upsRespTimeHist = Chart{
+ ID: "upstream_responses_time_histogram",
+ Title: "Upstream Responses Time Histogram",
+ Units: "responses/s",
+ Fam: "timings",
+ Ctx: "web_log.upstream_responses_time_histogram",
+ Priority: prioUpsRespTimeHist,
+ }
+)
+
+// Clients
+var (
+ uniqIPsCurPoll = Chart{
+ ID: "current_poll_uniq_clients",
+ Title: "Current Poll Unique Clients",
+ Units: "clients",
+ Fam: "client",
+ Ctx: "web_log.current_poll_uniq_clients",
+ Type: module.Stacked,
+ Priority: prioUniqIP,
+ Dims: Dims{
+ {ID: "uniq_ipv4", Name: "ipv4", Algo: module.Absolute},
+ {ID: "uniq_ipv6", Name: "ipv6", Algo: module.Absolute},
+ },
+ }
+)
+
+// Request By N
+var (
+ reqByVhost = Chart{
+ ID: "requests_by_vhost",
+ Title: "Requests By Vhost",
+ Units: "requests/s",
+ Fam: "vhost",
+ Ctx: "web_log.vhost_requests",
+ Type: module.Stacked,
+ Priority: prioReqVhost,
+ }
+ reqByPort = Chart{
+ ID: "requests_by_port",
+ Title: "Requests By Port",
+ Units: "requests/s",
+ Fam: "port",
+ Ctx: "web_log.port_requests",
+ Type: module.Stacked,
+ Priority: prioReqPort,
+ }
+ reqByScheme = Chart{
+ ID: "requests_by_scheme",
+ Title: "Requests By Scheme",
+ Units: "requests/s",
+ Fam: "scheme",
+ Ctx: "web_log.scheme_requests",
+ Type: module.Stacked,
+ Priority: prioReqScheme,
+ Dims: Dims{
+ {ID: "req_http_scheme", Name: "http", Algo: module.Incremental},
+ {ID: "req_https_scheme", Name: "https", Algo: module.Incremental},
+ },
+ }
+ reqByMethod = Chart{
+ ID: "requests_by_http_method",
+ Title: "Requests By HTTP Method",
+ Units: "requests/s",
+ Fam: "http method",
+ Ctx: "web_log.http_method_requests",
+ Type: module.Stacked,
+ Priority: prioReqMethod,
+ }
+ reqByVersion = Chart{
+ ID: "requests_by_http_version",
+ Title: "Requests By HTTP Version",
+ Units: "requests/s",
+ Fam: "http version",
+ Ctx: "web_log.http_version_requests",
+ Type: module.Stacked,
+ Priority: prioReqVersion,
+ }
+ reqByIPProto = Chart{
+ ID: "requests_by_ip_proto",
+ Title: "Requests By IP Protocol",
+ Units: "requests/s",
+ Fam: "ip proto",
+ Ctx: "web_log.ip_proto_requests",
+ Type: module.Stacked,
+ Priority: prioReqIPProto,
+ Dims: Dims{
+ {ID: "req_ipv4", Name: "ipv4", Algo: module.Incremental},
+ {ID: "req_ipv6", Name: "ipv6", Algo: module.Incremental},
+ },
+ }
+ reqBySSLProto = Chart{
+ ID: "requests_by_ssl_proto",
+ Title: "Requests By SSL Connection Protocol",
+ Units: "requests/s",
+ Fam: "ssl conn",
+ Ctx: "web_log.ssl_proto_requests",
+ Type: module.Stacked,
+ Priority: prioReqSSLProto,
+ }
+ reqBySSLCipherSuite = Chart{
+ ID: "requests_by_ssl_cipher_suite",
+ Title: "Requests By SSL Connection Cipher Suite",
+ Units: "requests/s",
+ Fam: "ssl conn",
+ Ctx: "web_log.ssl_cipher_suite_requests",
+ Type: module.Stacked,
+ Priority: prioReqSSLCipherSuite,
+ }
+)
+
+// Request By N Patterns
+var (
+ reqByURLPattern = Chart{
+ ID: "requests_by_url_pattern",
+ Title: "URL Field Requests By Pattern",
+ Units: "requests/s",
+ Fam: "url ptn",
+ Ctx: "web_log.url_pattern_requests",
+ Type: module.Stacked,
+ Priority: prioReqURLPattern,
+ }
+ reqByCustomFieldPattern = Chart{
+ ID: "custom_field_%s_requests_by_pattern",
+ Title: "Custom Field %s Requests By Pattern",
+ Units: "requests/s",
+ Fam: "custom field ptn",
+ Ctx: "web_log.custom_field_pattern_requests",
+ Type: module.Stacked,
+ Priority: prioReqCustomFieldPattern,
+ }
+)
+
+// custom time field
+var (
+ reqByCustomTimeField = Chart{
+ ID: "custom_time_field_%s_summary",
+ Title: `Custom Time Field "%s" Summary`,
+ Units: "milliseconds",
+ Fam: "custom time field",
+ Ctx: "web_log.custom_time_field_summary",
+ Priority: prioReqCustomTimeField,
+ Dims: Dims{
+ {ID: "custom_time_field_%s_time_min", Name: "min", Div: 1000},
+ {ID: "custom_time_field_%s_time_max", Name: "max", Div: 1000},
+ {ID: "custom_time_field_%s_time_avg", Name: "avg", Div: 1000},
+ },
+ }
+ reqByCustomTimeFieldHist = Chart{
+ ID: "custom_time_field_%s_histogram",
+ Title: `Custom Time Field "%s" Histogram`,
+ Units: "observations",
+ Fam: "custom time field",
+ Ctx: "web_log.custom_time_field_histogram",
+ Priority: prioReqCustomTimeFieldHist,
+ }
+)
+
+var (
+ customNumericFieldSummaryChartTmpl = Chart{
+ ID: "custom_numeric_field_%s_summary",
+ Title: "Custom Numeric Field Summary",
+ Units: "",
+ Fam: "custom numeric fields",
+ Ctx: "web_log.custom_numeric_field_%s_summary",
+ Priority: prioReqCustomNumericFieldSummary,
+ Dims: Dims{
+ {ID: "custom_numeric_field_%s_summary_min", Name: "min"},
+ {ID: "custom_numeric_field_%s_summary_max", Name: "max"},
+ {ID: "custom_numeric_field_%s_summary_avg", Name: "avg"},
+ },
+ }
+)
+
+// URL pattern stats
+var (
+ urlPatternRespCodes = Chart{
+ ID: "url_pattern_%s_responses_by_status_code",
+ Title: "Responses By Status Code",
+ Units: "responses/s",
+ Fam: "url ptn %s",
+ Ctx: "web_log.url_pattern_status_code_responses",
+ Type: module.Stacked,
+ Priority: prioURLPatternStats,
+ }
+ urlPatternReqMethods = Chart{
+ ID: "url_pattern_%s_requests_by_http_method",
+ Title: "Requests By HTTP Method",
+ Units: "requests/s",
+ Fam: "url ptn %s",
+ Ctx: "web_log.url_pattern_http_method_requests",
+ Type: module.Stacked,
+ Priority: prioURLPatternStats + 1,
+ }
+ urlPatternBandwidth = Chart{
+ ID: "url_pattern_%s_bandwidth",
+ Title: "Bandwidth",
+ Units: "kilobits/s",
+ Fam: "url ptn %s",
+ Ctx: "web_log.url_pattern_bandwidth",
+ Type: module.Area,
+ Priority: prioURLPatternStats + 2,
+ Dims: Dims{
+ {ID: "url_ptn_%s_bytes_received", Name: "received", Algo: module.Incremental, Mul: 8, Div: 1000},
+ {ID: "url_ptn_%s_bytes_sent", Name: "sent", Algo: module.Incremental, Mul: -8, Div: 1000},
+ },
+ }
+ urlPatternReqProcTime = Chart{
+ ID: "url_pattern_%s_request_processing_time",
+ Title: "Request Processing Time",
+ Units: "milliseconds",
+ Fam: "url ptn %s",
+ Ctx: "web_log.url_pattern_request_processing_time",
+ Priority: prioURLPatternStats + 3,
+ Dims: Dims{
+ {ID: "url_ptn_%s_req_proc_time_min", Name: "min", Div: 1000},
+ {ID: "url_ptn_%s_req_proc_time_max", Name: "max", Div: 1000},
+ {ID: "url_ptn_%s_req_proc_time_avg", Name: "avg", Div: 1000},
+ },
+ }
+)
+
+func newReqProcTimeHistChart(histogram []float64) (*Chart, error) {
+ chart := reqProcTimeHist.Copy()
+ for i, v := range histogram {
+ dim := &Dim{
+ ID: fmt.Sprintf("req_proc_time_hist_bucket_%d", i+1),
+ Name: fmt.Sprintf("%.3f", v),
+ Algo: module.Incremental,
+ }
+ if err := chart.AddDim(dim); err != nil {
+ return nil, err
+ }
+ }
+ if err := chart.AddDim(&Dim{
+ ID: "req_proc_time_hist_count",
+ Name: "+Inf",
+ Algo: module.Incremental,
+ }); err != nil {
+ return nil, err
+ }
+ return chart, nil
+}
+
+func newUpsRespTimeHistChart(histogram []float64) (*Chart, error) {
+ chart := upsRespTimeHist.Copy()
+ for i, v := range histogram {
+ dim := &Dim{
+ ID: fmt.Sprintf("upstream_resp_time_hist_bucket_%d", i+1),
+ Name: fmt.Sprintf("%.3f", v),
+ Algo: module.Incremental,
+ }
+ if err := chart.AddDim(dim); err != nil {
+ return nil, err
+ }
+ }
+ if err := chart.AddDim(&Dim{
+ ID: "upstream_resp_time_hist_count",
+ Name: "+Inf",
+ Algo: module.Incremental,
+ }); err != nil {
+ return nil, err
+ }
+ return chart, nil
+}
+
+func newURLPatternChart(patterns []userPattern) (*Chart, error) {
+ chart := reqByURLPattern.Copy()
+ for _, p := range patterns {
+ dim := &Dim{
+ ID: "req_url_ptn_" + p.Name,
+ Name: p.Name,
+ Algo: module.Incremental,
+ }
+ if err := chart.AddDim(dim); err != nil {
+ return nil, err
+ }
+ }
+ return chart, nil
+}
+
+func newURLPatternRespCodesChart(name string) *Chart {
+ chart := urlPatternRespCodes.Copy()
+ chart.ID = fmt.Sprintf(chart.ID, name)
+ chart.Fam = fmt.Sprintf(chart.Fam, name)
+ return chart
+}
+
+func newURLPatternReqMethodsChart(name string) *Chart {
+ chart := urlPatternReqMethods.Copy()
+ chart.ID = fmt.Sprintf(chart.ID, name)
+ chart.Fam = fmt.Sprintf(chart.Fam, name)
+ return chart
+}
+
+func newURLPatternBandwidthChart(name string) *Chart {
+ chart := urlPatternBandwidth.Copy()
+ chart.ID = fmt.Sprintf(chart.ID, name)
+ chart.Fam = fmt.Sprintf(chart.Fam, name)
+ for _, d := range chart.Dims {
+ d.ID = fmt.Sprintf(d.ID, name)
+ }
+ return chart
+}
+
+func newURLPatternReqProcTimeChart(name string) *Chart {
+ chart := urlPatternReqProcTime.Copy()
+ chart.ID = fmt.Sprintf(chart.ID, name)
+ chart.Fam = fmt.Sprintf(chart.Fam, name)
+ for _, d := range chart.Dims {
+ d.ID = fmt.Sprintf(d.ID, name)
+ }
+ return chart
+}
+
+func newCustomFieldCharts(fields []customField) (Charts, error) {
+ charts := Charts{}
+ for _, f := range fields {
+ chart, err := newCustomFieldChart(f)
+ if err != nil {
+ return nil, err
+ }
+ if err := charts.Add(chart); err != nil {
+ return nil, err
+ }
+ }
+ return charts, nil
+}
+
+func newCustomFieldChart(f customField) (*Chart, error) {
+ chart := reqByCustomFieldPattern.Copy()
+ chart.ID = fmt.Sprintf(chart.ID, f.Name)
+ chart.Title = fmt.Sprintf(chart.Title, f.Name)
+ for _, p := range f.Patterns {
+ dim := &Dim{
+ ID: fmt.Sprintf("custom_field_%s_%s", f.Name, p.Name),
+ Name: p.Name,
+ Algo: module.Incremental,
+ }
+ if err := chart.AddDim(dim); err != nil {
+ return nil, err
+ }
+ }
+ return chart, nil
+}
+
+func newCustomTimeFieldCharts(fields []customTimeField) (Charts, error) {
+ charts := Charts{}
+ for i, f := range fields {
+ chartTime, err := newCustomTimeFieldChart(f)
+ if err != nil {
+ return nil, err
+ }
+ chartTime.Priority += i
+ if err := charts.Add(chartTime); err != nil {
+ return nil, err
+ }
+ if len(f.Histogram) < 1 {
+ continue
+ }
+
+ chartHist, err := newCustomTimeFieldHistChart(f)
+ if err != nil {
+ return nil, err
+ }
+ chartHist.Priority += i
+
+ if err := charts.Add(chartHist); err != nil {
+ return nil, err
+ }
+ }
+ return charts, nil
+}
+
+func newCustomTimeFieldChart(f customTimeField) (*Chart, error) {
+ chart := reqByCustomTimeField.Copy()
+ chart.ID = fmt.Sprintf(chart.ID, f.Name)
+ chart.Title = fmt.Sprintf(chart.Title, f.Name)
+ for _, d := range chart.Dims {
+ d.ID = fmt.Sprintf(d.ID, f.Name)
+ }
+ return chart, nil
+}
+
+func newCustomTimeFieldHistChart(f customTimeField) (*Chart, error) {
+ chart := reqByCustomTimeFieldHist.Copy()
+ chart.ID = fmt.Sprintf(chart.ID, f.Name)
+ chart.Title = fmt.Sprintf(chart.Title, f.Name)
+ for i, v := range f.Histogram {
+ dim := &Dim{
+ ID: fmt.Sprintf("custom_time_field_%s_time_hist_bucket_%d", f.Name, i+1),
+ Name: fmt.Sprintf("%.3f", v),
+ Algo: module.Incremental,
+ }
+ if err := chart.AddDim(dim); err != nil {
+ return nil, err
+ }
+ }
+ if err := chart.AddDim(&Dim{
+ ID: fmt.Sprintf("custom_time_field_%s_time_hist_count", f.Name),
+ Name: "+Inf",
+ Algo: module.Incremental,
+ }); err != nil {
+ return nil, err
+ }
+ return chart, nil
+}
+
+func (w *WebLog) createCharts(line *logLine) error {
+ if line.empty() {
+ return errors.New("empty line")
+ }
+ w.charts = nil
+ // Following charts are created during runtime:
+ // - reqBySSLProto, reqBySSLCipherSuite - it is likely line has no SSL stuff at this moment
+ charts := &Charts{
+ reqTotal.Copy(),
+ reqExcluded.Copy(),
+ }
+ if line.hasVhost() {
+ if err := addVhostCharts(charts); err != nil {
+ return err
+ }
+ }
+ if line.hasPort() {
+ if err := addPortCharts(charts); err != nil {
+ return err
+ }
+ }
+ if line.hasReqScheme() {
+ if err := addSchemeCharts(charts); err != nil {
+ return err
+ }
+ }
+ if line.hasReqClient() {
+ if err := addClientCharts(charts); err != nil {
+ return err
+ }
+ }
+ if line.hasReqMethod() {
+ if err := addMethodCharts(charts, w.URLPatterns); err != nil {
+ return err
+ }
+ }
+ if line.hasReqURL() {
+ if err := addURLCharts(charts, w.URLPatterns); err != nil {
+ return err
+ }
+ }
+ if line.hasReqProto() {
+ if err := addReqProtoCharts(charts); err != nil {
+ return err
+ }
+ }
+ if line.hasRespCode() {
+ if err := addRespCodesCharts(charts, w.GroupRespCodes); err != nil {
+ return err
+ }
+ }
+ if line.hasReqSize() || line.hasRespSize() {
+ if err := addBandwidthCharts(charts, w.URLPatterns); err != nil {
+ return err
+ }
+ }
+ if line.hasReqProcTime() {
+ if err := addReqProcTimeCharts(charts, w.Histogram, w.URLPatterns); err != nil {
+ return err
+ }
+ }
+ if line.hasUpsRespTime() {
+ if err := addUpstreamRespTimeCharts(charts, w.Histogram); err != nil {
+ return err
+ }
+ }
+ if line.hasCustomFields() {
+ if len(w.CustomFields) > 0 {
+ if err := addCustomFieldsCharts(charts, w.CustomFields); err != nil {
+ return err
+ }
+ }
+ if len(w.CustomTimeFields) > 0 {
+ if err := addCustomTimeFieldsCharts(charts, w.CustomTimeFields); err != nil {
+ return err
+ }
+ }
+ if len(w.CustomNumericFields) > 0 {
+ if err := addCustomNumericFieldsCharts(charts, w.CustomNumericFields); err != nil {
+ return err
+ }
+ }
+ }
+
+ w.charts = charts
+
+ return nil
+}
+
+func addVhostCharts(charts *Charts) error {
+ return charts.Add(reqByVhost.Copy())
+}
+
+func addPortCharts(charts *Charts) error {
+ return charts.Add(reqByPort.Copy())
+}
+
+func addSchemeCharts(charts *Charts) error {
+ return charts.Add(reqByScheme.Copy())
+}
+
+func addClientCharts(charts *Charts) error {
+ if err := charts.Add(reqByIPProto.Copy()); err != nil {
+ return err
+ }
+ return charts.Add(uniqIPsCurPoll.Copy())
+}
+
+func addMethodCharts(charts *Charts, patterns []userPattern) error {
+ if err := charts.Add(reqByMethod.Copy()); err != nil {
+ return err
+ }
+
+ for _, p := range patterns {
+ chart := newURLPatternReqMethodsChart(p.Name)
+ if err := charts.Add(chart); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func addURLCharts(charts *Charts, patterns []userPattern) error {
+ if len(patterns) == 0 {
+ return nil
+ }
+ chart, err := newURLPatternChart(patterns)
+ if err != nil {
+ return err
+ }
+ if err := charts.Add(chart); err != nil {
+ return err
+ }
+
+ for _, p := range patterns {
+ chart := newURLPatternRespCodesChart(p.Name)
+ if err := charts.Add(chart); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func addReqProtoCharts(charts *Charts) error {
+ return charts.Add(reqByVersion.Copy())
+}
+
+func addRespCodesCharts(charts *Charts, group bool) error {
+ if err := charts.Add(reqTypes.Copy()); err != nil {
+ return err
+ }
+ if err := charts.Add(respCodeClass.Copy()); err != nil {
+ return err
+ }
+ if !group {
+ return charts.Add(respCodes.Copy())
+ }
+ for _, c := range []Chart{respCodes1xx, respCodes2xx, respCodes3xx, respCodes4xx, respCodes5xx} {
+ if err := charts.Add(c.Copy()); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func addBandwidthCharts(charts *Charts, patterns []userPattern) error {
+ if err := charts.Add(bandwidth.Copy()); err != nil {
+ return err
+ }
+
+ for _, p := range patterns {
+ chart := newURLPatternBandwidthChart(p.Name)
+ if err := charts.Add(chart); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func addReqProcTimeCharts(charts *Charts, histogram []float64, patterns []userPattern) error {
+ if err := charts.Add(reqProcTime.Copy()); err != nil {
+ return err
+ }
+ for _, p := range patterns {
+ chart := newURLPatternReqProcTimeChart(p.Name)
+ if err := charts.Add(chart); err != nil {
+ return err
+ }
+ }
+ if len(histogram) == 0 {
+ return nil
+ }
+ chart, err := newReqProcTimeHistChart(histogram)
+ if err != nil {
+ return err
+ }
+ return charts.Add(chart)
+}
+
+func addUpstreamRespTimeCharts(charts *Charts, histogram []float64) error {
+ if err := charts.Add(upsRespTime.Copy()); err != nil {
+ return err
+ }
+ if len(histogram) == 0 {
+ return nil
+ }
+ chart, err := newUpsRespTimeHistChart(histogram)
+ if err != nil {
+ return err
+ }
+ return charts.Add(chart)
+}
+
+func addCustomFieldsCharts(charts *Charts, fields []customField) error {
+ cs, err := newCustomFieldCharts(fields)
+ if err != nil {
+ return err
+ }
+ return charts.Add(cs...)
+}
+
+func addCustomTimeFieldsCharts(charts *Charts, fields []customTimeField) error {
+ cs, err := newCustomTimeFieldCharts(fields)
+ if err != nil {
+ return err
+ }
+ return charts.Add(cs...)
+}
+
+func addCustomNumericFieldsCharts(charts *module.Charts, fields []customNumericField) error {
+ for _, f := range fields {
+ chart := customNumericFieldSummaryChartTmpl.Copy()
+ chart.ID = fmt.Sprintf(chart.ID, f.Name)
+ chart.Units = f.Units
+ chart.Ctx = fmt.Sprintf(chart.Ctx, f.Name)
+ for _, dim := range chart.Dims {
+ dim.ID = fmt.Sprintf(dim.ID, f.Name)
+ dim.Div = f.Divisor
+ }
+
+ if err := charts.Add(chart); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}