From 2b35f74042e48b397c913221bfbe9912849d12de Mon Sep 17 00:00:00 2001 From: Yuri Date: Wed, 8 Mar 2023 19:46:56 -0300 Subject: [PATCH 1/5] add path metric --- cloudflare.go | 63 +++++++++++++++++++++++++++++++++++++++++++++++---- main.go | 25 +++++++++++--------- prometheus.go | 22 ++++++++++++++++++ 3 files changed, 95 insertions(+), 15 deletions(-) diff --git a/cloudflare.go b/cloudflare.go index f1a3037..13ced0e 100644 --- a/cloudflare.go +++ b/cloudflare.go @@ -2,6 +2,7 @@ package main import ( "context" + "encoding/json" "time" "github.com/cloudflare/cloudflare-go" @@ -156,6 +157,16 @@ type zoneResp struct { } `json:"dimensions"` } `json:"httpRequestsAdaptiveGroups"` + HTTPRequestsClientRequestPath []struct { + Count uint64 `json:"count"` + Dimensions struct { + OriginResponseStatus uint16 `json:"originResponseStatus"` + ClientRequestHTTPHost string `json:"clientRequestHTTPHost"` + ClientRequestPath string `json:"clientRequestPath"` + ClientRequestHTTPMethodName string `json:"clientRequestHTTPMethodName"` + } `json:"dimensions"` + } `json:"httpRequestsClientRequestPath"` + HTTPRequestsEdgeCountryHost []struct { Count uint64 `json:"count"` Dimensions struct { @@ -269,8 +280,8 @@ func fetchZoneTotals(zoneIDs []string) (*cloudflareResponse, error) { now = now.Truncate(s) now1mAgo := now.Add(-60 * time.Second) - request := graphql.NewRequest(` -query ($zoneIDs: [String!], $mintime: Time!, $maxtime: Time!, $limit: Int!) { + query := ` +query ($zoneIDs: [String!], $mintime: Time!, $maxtime: Time!, $limit: Int!, $httpRequestsClientRequestPathFilter: ZoneHttpRequestsAdaptiveGroupsFilter_InputObject!) { viewer { zones(filter: { zoneTag_in: $zoneIDs }) { zoneTag @@ -342,6 +353,32 @@ query ($zoneIDs: [String!], $mintime: Time!, $maxtime: Time!, $limit: Int!) { originResponseStatus clientCountryName clientRequestHTTPHost + + } + } + httpRequestsClientRequestPath: httpRequestsAdaptiveGroups( + limit: 100, + filter: $httpRequestsClientRequestPathFilter, + orderBy: [count_DESC]) + { + count + avg { + originResponseDurationMs + sampleInterval + } + ratio { + status4xx + status5xx + } + sum { + edgeResponseBytes + visits + } + dimensions { + originResponseStatus + clientRequestPath + clientRequestHTTPHost + clientRequestHTTPMethodName } } httpRequestsEdgeCountryHost: httpRequestsAdaptiveGroups(limit: $limit, filter: { datetime_geq: $mintime, datetime_lt: $maxtime }) { @@ -363,14 +400,32 @@ query ($zoneIDs: [String!], $mintime: Time!, $maxtime: Time!, $limit: Int!) { } } } -} -`) +}` + + request := graphql.NewRequest(query) if len(cfgCfAPIToken) > 0 { request.Header.Set("Authorization", "Bearer "+cfgCfAPIToken) } else { request.Header.Set("X-AUTH-EMAIL", cfgCfAPIEmail) request.Header.Set("X-AUTH-KEY", cfgCfAPIKey) } + + var httpRequestsClientRequestPathFilter map[string]interface{} + + err := json.Unmarshal([]byte(cfgClientRequestPathFilters), &httpRequestsClientRequestPathFilter) + if err != nil { + return nil, err + } + + httpRequestsClientRequestPathFilter["AND"] = append( + httpRequestsClientRequestPathFilter["AND"].([]interface{}), + map[string]time.Time{ + "datetime_geq": now1mAgo, + "datetime_lt": now, + }, + ) + + request.Var("httpRequestsClientRequestPathFilter", httpRequestsClientRequestPathFilter) request.Var("limit", 9999) request.Var("maxtime", now) request.Var("mintime", now1mAgo) diff --git a/main.go b/main.go index d07c180..1036fce 100644 --- a/main.go +++ b/main.go @@ -15,17 +15,18 @@ import ( ) var ( - cfgListen = ":8080" - cfgCfAPIKey = "" - cfgCfAPIEmail = "" - cfgCfAPIToken = "" - cfgMetricsPath = "/metrics" - cfgZones = "" - cfgExcludeZones = "" - cfgScrapeDelay = 300 - cfgFreeTier = false - cfgBatchSize = 10 - cfgMetricsDenylist = "" + cfgListen = ":8080" + cfgCfAPIKey = "" + cfgCfAPIEmail = "" + cfgCfAPIToken = "" + cfgMetricsPath = "/metrics" + cfgZones = "" + cfgExcludeZones = "" + cfgScrapeDelay = 300 + cfgFreeTier = false + cfgBatchSize = 10 + cfgMetricsDenylist = "" + cfgClientRequestPathFilters = "" ) func getTargetZones() []string { @@ -139,8 +140,10 @@ func main() { flag.StringVar(&cfgExcludeZones, "cf_exclude_zones", cfgExcludeZones, "cloudflare zones to exclude, comma delimited list") flag.IntVar(&cfgScrapeDelay, "scrape_delay", cfgScrapeDelay, "scrape delay in seconds, defaults to 300") flag.IntVar(&cfgBatchSize, "cf_batch_size", cfgBatchSize, "cloudflare zones batch size (1-10), defaults to 10") + flag.StringVar(&cfgClientRequestPathFilters, "cf_path_filters", cfgClientRequestPathFilters, "add filters to path query") flag.BoolVar(&cfgFreeTier, "free_tier", cfgFreeTier, "scrape only metrics included in free plan") flag.StringVar(&cfgMetricsDenylist, "metrics_denylist", cfgMetricsDenylist, "metrics to not expose, comma delimited list") + flag.Parse() if !(len(cfgCfAPIToken) > 0 || (len(cfgCfAPIEmail) > 0 && len(cfgCfAPIKey) > 0)) { log.Fatal("Please provide CF_API_KEY+CF_API_EMAIL or CF_API_TOKEN") diff --git a/prometheus.go b/prometheus.go index d10ef20..e65fd36 100644 --- a/prometheus.go +++ b/prometheus.go @@ -25,6 +25,7 @@ const ( zoneRequestHTTPStatusMetricName MetricName = "cloudflare_zone_requests_status" zoneRequestBrowserMapMetricName MetricName = "cloudflare_zone_requests_browser_map_page_views_count" zoneRequestOriginStatusCountryHostMetricName MetricName = "cloudflare_zone_requests_origin_status_country_host" + zoneRequestOriginStatusPathHostName MetricName = "cloudflare_zone_requests_origin_status_path_host" zoneRequestStatusCountryHostMetricName MetricName = "cloudflare_zone_requests_status_country_host" zoneBandwidthTotalMetricName MetricName = "cloudflare_zone_bandwidth_total" zoneBandwidthCachedMetricName MetricName = "cloudflare_zone_bandwidth_cached" @@ -110,6 +111,12 @@ var ( }, []string{"zone", "status", "country", "host"}, ) + zoneRequestOriginStatusPathHost = prometheus.NewCounterVec(prometheus.CounterOpts{ + Name: zoneRequestOriginStatusPathHostName.String(), + Help: "Count of requests for zone per edge HTTP status per path per host", + }, []string{"zone", "status", "path", "method", "host"}, + ) + zoneRequestStatusCountryHost = prometheus.NewCounterVec(prometheus.CounterOpts{ Name: zoneRequestStatusCountryHostMetricName.String(), Help: "Count of requests for zone per edge HTTP status per country per host", @@ -256,6 +263,7 @@ func buildAllMetricsSet() MetricsSet { allMetricsSet.Add(zoneRequestBrowserMapMetricName) allMetricsSet.Add(zoneRequestOriginStatusCountryHostMetricName) allMetricsSet.Add(zoneRequestStatusCountryHostMetricName) + allMetricsSet.Add(zoneRequestOriginStatusPathHostName) allMetricsSet.Add(zoneBandwidthTotalMetricName) allMetricsSet.Add(zoneBandwidthCachedMetricName) allMetricsSet.Add(zoneBandwidthSSLEncryptedMetricName) @@ -320,6 +328,9 @@ func mustRegisterMetrics(deniedMetrics MetricsSet) { if !deniedMetrics.Has(zoneRequestStatusCountryHostMetricName) { prometheus.MustRegister(zoneRequestStatusCountryHost) } + if !deniedMetrics.Has(zoneRequestOriginStatusPathHostName) { + prometheus.MustRegister(zoneRequestOriginStatusPathHost) + } if !deniedMetrics.Has(zoneBandwidthTotalMetricName) { prometheus.MustRegister(zoneBandwidthTotal) } @@ -565,6 +576,17 @@ func addHTTPAdaptiveGroups(z *zoneResp, name string) { }).Add(float64(g.Count)) } + for _, g := range z.HTTPRequestsClientRequestPath { + zoneRequestOriginStatusPathHost.With( + prometheus.Labels{ + "zone": name, + "status": strconv.Itoa(int(g.Dimensions.OriginResponseStatus)), + "path": g.Dimensions.ClientRequestPath, + "method": g.Dimensions.ClientRequestHTTPMethodName, + "host": g.Dimensions.ClientRequestHTTPHost, + }).Add(float64(g.Count)) + } + for _, g := range z.HTTPRequestsEdgeCountryHost { zoneRequestStatusCountryHost.With( prometheus.Labels{ From 17702a7ee75becc97086aaea3003e3861279153e Mon Sep 17 00:00:00 2001 From: Yuri Date: Tue, 14 Mar 2023 17:30:56 -0300 Subject: [PATCH 2/5] feat: query formating --- cloudflare.go | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/cloudflare.go b/cloudflare.go index 13ced0e..a160bfa 100644 --- a/cloudflare.go +++ b/cloudflare.go @@ -353,27 +353,14 @@ query ($zoneIDs: [String!], $mintime: Time!, $maxtime: Time!, $limit: Int!, $htt originResponseStatus clientCountryName clientRequestHTTPHost - } } httpRequestsClientRequestPath: httpRequestsAdaptiveGroups( - limit: 100, + limit: $limit, filter: $httpRequestsClientRequestPathFilter, - orderBy: [count_DESC]) - { + orderBy: [count_DESC] + ) { count - avg { - originResponseDurationMs - sampleInterval - } - ratio { - status4xx - status5xx - } - sum { - edgeResponseBytes - visits - } dimensions { originResponseStatus clientRequestPath From 1839809d845e3f35887331136450fc987c8287f3 Mon Sep 17 00:00:00 2001 From: Yuri Date: Tue, 11 Apr 2023 14:19:31 -0300 Subject: [PATCH 3/5] feat: log level --- main.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/main.go b/main.go index 1036fce..3a9d004 100644 --- a/main.go +++ b/main.go @@ -27,6 +27,7 @@ var ( cfgBatchSize = 10 cfgMetricsDenylist = "" cfgClientRequestPathFilters = "" + cfgLogLevel = "info" ) func getTargetZones() []string { @@ -143,6 +144,7 @@ func main() { flag.StringVar(&cfgClientRequestPathFilters, "cf_path_filters", cfgClientRequestPathFilters, "add filters to path query") flag.BoolVar(&cfgFreeTier, "free_tier", cfgFreeTier, "scrape only metrics included in free plan") flag.StringVar(&cfgMetricsDenylist, "metrics_denylist", cfgMetricsDenylist, "metrics to not expose, comma delimited list") + flag.StringVar(&cfgLogLevel, "log_level", cfgLogLevel, "log level, defaults to info") flag.Parse() if !(len(cfgCfAPIToken) > 0 || (len(cfgCfAPIEmail) > 0 && len(cfgCfAPIKey) > 0)) { @@ -156,6 +158,16 @@ func main() { log.SetFormatter(customFormatter) customFormatter.FullTimestamp = true + log_level, err := log.ParseLevel(cfgLogLevel) + if err != nil { + log_level = log.InfoLevel + } + + log.SetLevel(log_level) + log.WithFields(log.Fields{ + "log_level": log_level, + }).Info() + metricsDenylist := []string{} if len(cfgMetricsDenylist) > 0 { metricsDenylist = strings.Split(cfgMetricsDenylist, ",") From 6517c65f3c852a58c9acd972e6f34302e5f44f32 Mon Sep 17 00:00:00 2001 From: Yuri Date: Tue, 11 Apr 2023 14:22:06 -0300 Subject: [PATCH 4/5] feat: log on format error --- cloudflare.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cloudflare.go b/cloudflare.go index a160bfa..16aa417 100644 --- a/cloudflare.go +++ b/cloudflare.go @@ -401,6 +401,7 @@ query ($zoneIDs: [String!], $mintime: Time!, $maxtime: Time!, $limit: Int!, $htt err := json.Unmarshal([]byte(cfgClientRequestPathFilters), &httpRequestsClientRequestPathFilter) if err != nil { + log.Error(err) return nil, err } From 2a74d89273ab5997b853663843be4737b8b20306 Mon Sep 17 00:00:00 2001 From: Yuri Date: Tue, 11 Apr 2023 14:22:30 -0300 Subject: [PATCH 5/5] feat: log query --- cloudflare.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cloudflare.go b/cloudflare.go index 16aa417..38bbe28 100644 --- a/cloudflare.go +++ b/cloudflare.go @@ -388,7 +388,6 @@ query ($zoneIDs: [String!], $mintime: Time!, $maxtime: Time!, $limit: Int!, $htt } } }` - request := graphql.NewRequest(query) if len(cfgCfAPIToken) > 0 { request.Header.Set("Authorization", "Bearer "+cfgCfAPIToken) @@ -413,6 +412,12 @@ query ($zoneIDs: [String!], $mintime: Time!, $maxtime: Time!, $limit: Int!, $htt }, ) + if log.DebugLevel == log.GetLevel() { + log.Debug(httpRequestsClientRequestPathFilter) + f, _ := json.Marshal(httpRequestsClientRequestPathFilter) + log.Debug(string(f)) + } + request.Var("httpRequestsClientRequestPathFilter", httpRequestsClientRequestPathFilter) request.Var("limit", 9999) request.Var("maxtime", now)