parsing.go (220 lines of code) (raw):
package main
import (
"encoding/json"
"fmt"
"io"
)
func parseZoneIDs(apiRespBody io.Reader, zonesFilter []string) (map[string]string, error) {
var zoneList zonesResp
if err := json.NewDecoder(apiRespBody).Decode(&zoneList); err != nil {
return nil, err
}
zones := map[string]string{}
for _, zone := range zoneList.Result {
if zone.Status != "pending" && (len(zonesFilter) == 0 || contains(zonesFilter, zone.Name)) {
zones[zone.ID] = zone.Name
}
}
return zones, nil
}
type extractFunc func(zoneResp, map[string]string, *TimeBucket) (int, error)
func extractZoneNetworkErrorLogs(zone zoneResp, zoneNames map[string]string, scrapeBucket *TimeBucket) (int, error) {
// NELs have different sampling rates.
//
// According to https://w3c.github.io/network-error-logging/#generate-a-network-error-report
// "ok" is the only accepted code for a successful response, so only those would have a different
// sampling rate compared to all others.
//
// Currently, Cloudflare uses:
// - the default of 1 as a `failure_fraction`
// - 0.01 as a `success_fraction`
//
// So for every single failure and 100 successful connections the browser is instructed to report a success.
// Thus, we multiply successes by 100.
// This *might* be a dynamic value, but it seems constant across zones and is not exposed with its value in the
// API, so for now, this is hard-coded.
const failureFraction float64 = 1
const successFraction float64 = 0.01
for _, nel := range zone.NetworkErrorLogs {
var currentFraction float64 = failureFraction
if nel.Dimensions.Type == "ok" {
currentFraction = successFraction
}
networkErrorLogs.WithLabelValues(
zoneNames[zone.ZoneTag],
nel.Dimensions.ClientIPCountryCode,
toString(nel.Dimensions.ClientIPVersion),
nel.Dimensions.LastKnownGoodColoCode,
nel.Dimensions.Protocol,
nel.Dimensions.Phase,
nel.Dimensions.Type,
).
Add(float64(nel.Count)/currentFraction, scrapeBucket.getEndTime())
}
return len(zone.NetworkErrorLogs), nil
}
func extractZoneHTTPCountry(zone zoneResp, zoneNames map[string]string, scrapeBucket *TimeBucket) (int, error) {
for _, countryTraffic := range zone.TrafficCountry {
httpCountryRequests.WithLabelValues(zoneNames[zone.ZoneTag], countryTraffic.Dimensions.ClientCountryName).
Add(float64(countryTraffic.Count), scrapeBucket.getEndTime())
httpCountryBytes.WithLabelValues(zoneNames[zone.ZoneTag], countryTraffic.Dimensions.ClientCountryName).
Add(float64(countryTraffic.Sum.EdgeResponseBytes), scrapeBucket.getEndTime())
}
return len(zone.TrafficCountry), nil
}
func extractZoneHTTPColo(zone zoneResp, zoneNames map[string]string, scrapeBucket *TimeBucket) (int, error) {
for _, coloTraffic := range zone.TrafficColo {
httpColoRequests.WithLabelValues(zoneNames[zone.ZoneTag], coloTraffic.Dimensions.ColoCode).
Add(float64(coloTraffic.Count), scrapeBucket.getEndTime())
httpColoBytes.WithLabelValues(zoneNames[zone.ZoneTag], coloTraffic.Dimensions.ColoCode).
Add(float64(coloTraffic.Sum.EdgeResponseBytes), scrapeBucket.getEndTime())
}
return len(zone.TrafficColo), nil
}
func extractZoneHTTPCached(zone zoneResp, zoneNames map[string]string, scrapeBucket *TimeBucket) (int, error) {
for _, cachedTraffic := range zone.TrafficCached {
httpCachedRequests.WithLabelValues(zoneNames[zone.ZoneTag], cachedTraffic.Dimensions.CacheStatus).
Add(float64(cachedTraffic.Count), scrapeBucket.getEndTime())
httpCachedBytes.WithLabelValues(zoneNames[zone.ZoneTag], cachedTraffic.Dimensions.CacheStatus).
Add(float64(cachedTraffic.Sum.EdgeResponseBytes), scrapeBucket.getEndTime())
}
return len(zone.TrafficCached), nil
}
func extractZoneHTTPDetailed(zone zoneResp, zoneNames map[string]string, scrapeBucket *TimeBucket) (int, error) {
for _, detailedTraffic := range zone.TrafficDetails {
httpTLSRequests.WithLabelValues(zoneNames[zone.ZoneTag], detailedTraffic.Dimensions.ClientSSLProtocol).
Add(float64(detailedTraffic.Count), scrapeBucket.getEndTime())
httpProtocolRequests.WithLabelValues(zoneNames[zone.ZoneTag], detailedTraffic.Dimensions.ClientRequestHTTPProtocol).
Add(float64(detailedTraffic.Count), scrapeBucket.getEndTime())
httpResponses.WithLabelValues(
zoneNames[zone.ZoneTag],
toString(detailedTraffic.Dimensions.EdgeResponseStatus),
toString(detailedTraffic.Dimensions.OriginResponseStatus),
).
Add(float64(detailedTraffic.Count), scrapeBucket.getEndTime())
}
return len(zone.TrafficDetails), nil
}
func extractZoneFirewallEvents(zone zoneResp, zoneNames map[string]string, scrapeBucket *TimeBucket) (int, error) {
for _, firewallEventGroup := range zone.FirewallEventsAdaptiveGroups {
firewallEvents.WithLabelValues(
zoneNames[zone.ZoneTag], firewallEventGroup.Dimensions.Action,
firewallEventGroup.Dimensions.Source,
firewallEventGroup.Dimensions.RuleID,
toString(firewallEventGroup.Dimensions.EdgeResponseStatus), toString(firewallEventGroup.Dimensions.OriginResponseStatus),
).Add(float64(firewallEventGroup.Count), scrapeBucket.getEndTime())
}
return len(zone.FirewallEventsAdaptiveGroups), nil
}
func extractZoneIPReputation(zone zoneResp, zoneNames map[string]string, scrapeBucket *TimeBucket) (int, error) {
for _, ipRep := range zone.IPReputation {
reputation.WithLabelValues(
zoneNames[zone.ZoneTag],
ipRep.Dimensions.Source,
ipRep.Dimensions.Repuation,
ipRep.Dimensions.ClientCountryName,
).Add(float64(ipRep.Count), scrapeBucket.getEndTime())
}
return len(zone.IPReputation), nil
}
func extractZoneHealthCheckEvents(zone zoneResp, zoneNames map[string]string, scrapeBucket *TimeBucket) (int, error) {
for _, healthCheckEventsGroup := range zone.HealthCheckEventsGroups {
healthCheckEvents.WithLabelValues(
zoneNames[zone.ZoneTag], healthCheckEventsGroup.Dimensions.FailureReason,
healthCheckEventsGroup.Dimensions.HealthCheckName, healthCheckEventsGroup.Dimensions.HealthStatus,
toString(healthCheckEventsGroup.Dimensions.OriginResponseStatus),
healthCheckEventsGroup.Dimensions.Region, healthCheckEventsGroup.Dimensions.Scope,
).Add(float64(healthCheckEventsGroup.Count), scrapeBucket.getEndTime())
}
return len(zone.HealthCheckEventsGroups), nil
}
type cloudflareResp struct {
Viewer struct {
Zones []zoneResp `json:"zones"`
} `json:"viewer"`
}
type zoneResp struct {
TrafficCached []struct {
Count uint64 `json:"count"`
Dimensions struct {
CacheStatus string `json:"cacheStatus"`
SampleInterval uint64 `json:"sampleInterval"`
} `json:"dimensions"`
Sum struct {
EdgeResponseBytes uint64 `json:"edgeResponseBytes"`
} `json:"sum"`
} `json:"trafficCached"`
TrafficColo []struct {
Count uint64 `json:"count"`
Dimensions struct {
ColoCode string `json:"coloCode"`
SampleInterval uint64 `json:"sampleInterval"`
} `json:"dimensions"`
Sum struct {
EdgeResponseBytes uint64 `json:"edgeResponseBytes"`
} `json:"sum"`
} `json:"trafficColo"`
NetworkErrorLogs []struct {
Count uint64 `json:"count"`
Dimensions struct {
ClientIPCountryCode string `json:"clientIPCountryCode"`
ClientIPVersion int `json:"clientIPVersion"`
LastKnownGoodColoCode string `json:"lastKnownGoodColoCode"`
Protocol string `json:"protocol"`
Phase string `json:"phase"`
Type string `json:"type"`
} `json:"dimensions"`
} `json:"networkErrorLogs"`
TrafficCountry []struct {
Count uint64 `json:"count"`
Dimensions struct {
ClientCountryName string `json:"clientCountryName"`
SampleInterval uint64 `json:"sampleInterval"`
} `json:"dimensions"`
Sum struct {
EdgeResponseBytes uint64 `json:"edgeResponseBytes"`
} `json:"sum"`
} `json:"trafficCountry"`
TrafficDetails []struct {
Count uint64 `json:"count"`
Dimensions struct {
ClientRequestHTTPProtocol string `json:"clientRequestHTTPProtocol"`
ClientSSLProtocol string `json:"clientSSLProtocol"`
EdgeResponseStatus int `json:"edgeResponseStatus"`
OriginResponseStatus int `json:"originResponseStatus"`
SampleInterval uint64 `json:"sampleInterval"`
} `json:"dimensions"`
Sum struct {
EdgeResponseBytes uint64 `json:"edgeResponseBytes"`
} `json:"sum"`
} `json:"trafficDetails"`
FirewallEventsAdaptiveGroups []struct {
Count uint64 `json:"count"`
Dimensions struct {
Action string `json:"action"`
EdgeResponseStatus int `json:"edgeResponseStatus"`
OriginResponseStatus int `json:"originResponseStatus"`
SampleInterval int `json:"sampleInterval,omitempty"`
RuleID string `json:"ruleId"`
Source string `json:"source"`
} `json:"dimensions"`
} `json:"firewallEventsAdaptiveGroups,omitempty"`
IPReputation []struct {
Count uint64 `json:"count"`
Dimensions struct {
Repuation string `json:"clientIPClass"`
ClientCountryName string `json:"clientCountryName"`
SampleInterval int `json:"sampleInterval,omitempty"`
Source string `json:"source"`
} `json:"dimensions"`
} `json:"reputation,omitempty"`
HealthCheckEventsGroups []struct {
Count uint64 `json:"count"`
Dimensions struct {
Datetime string `json:"datetime"`
FailureReason string `json:"failureReason"`
HealthCheckName string `json:"healthCheckName"`
HealthStatus string `json:"healthStatus"`
OriginResponseStatus int `json:"originResponseStatus"`
Region string `json:"region"`
Scope string `json:"scope"`
} `json:"dimensions"`
} `json:"healthCheckEventsGroups,omitempty"`
ZoneTag string `json:"zoneTag"`
}
type zonesResp struct {
Result []struct {
ID string `json:"id"`
Name string `json:"name"`
Status string `json:"status"`
} `json:"result"`
}
func toString(i int) string {
return fmt.Sprintf("%d", i)
}