Fastly CDN Logs
Stream Fastly CDN request logs (every edge request, with status, cache state, POP, client and timing) into KubeSense with a Fastly HTTPS logging endpoint.
Fastly records every request its edge serves for your site: the URL, the response status, whether it was a cache hit, which POP served it, the client and how long it took. With a Fastly HTTPS logging endpoint, Fastly pushes these request logs to KubeSense within seconds. They are stored with your other logs, so you can search them on the Logs page, alert on 5xx spikes and put CDN traffic next to your application logs and traces.
Fastly posts straight to your KubeSense endpoint. You don't need to run any relay function or extra service. If you already send Fastly logs to another tool, such as Datadog, add KubeSense as a second endpoint. Both keep receiving the same logs.
How it flows
Fastly service (Logging > HTTPS endpoint)
│ POST https://<KUBESENSE_INGEST_HOST>/fastlylogs (JSON array, gzip, batched)
│ Authorization: Basic <token>
▼
KubeSense
│ checks the token, turns each request into a log
▼
Logs pageFastly sends logs in batches, every few seconds or when a batch fills up. Requests usually show up in KubeSense within a minute.
Before you start
You need:
- Access to the Fastly service that serves your domain, with permission to edit and activate its configuration.
- Two values from KubeSense. Ask your KubeSense contact for them. KubeSense sets up the endpoint on its side.
| Value | Example |
|---|---|
| Endpoint URL | https://<KUBESENSE_INGEST_HOST>/fastlylogs |
| Authorization token | Basic ZmFzdGx5Oi... |
warning: Treat the authorization token like a password. Anyone who has it can send logs to your KubeSense.
1. Create the HTTPS endpoint in Fastly
Fastly control panel
- Open your service and click Edit configuration → Clone version to create a new draft version. An active version can't be edited.
- Go to Logging and, under HTTPS, click Create endpoint.
- Fill in the settings:
| Setting | Value |
|---|---|
| Name | KubeSense |
| Placement | Format Version Default |
| Log format | Your JSON log format, see Log format |
| URL | The endpoint URL from KubeSense. Optionally add the parameters from section 2 |
| Maximum logs | 10000 |
| Maximum bytes | 10485760 |
| Content type | application/json |
| Custom header name | Authorization |
| Custom header value | The authorization token from KubeSense, including the Basic prefix |
| Method | POST |
| JSON log entry format | Array of JSON |
| Select a log line format | Blank |
| Compression | Gzip (recommended) or None |
| TLS / custom CA fields | Leave empty |
- Click Create, then click Activate to activate the new version. The endpoint only starts sending once the version that contains it is active.
warning: Choose Array of JSON as the JSON log entry format and Blank as the log line format. The Classic, Loggly and Logplex line formats add a text prefix in front of every log line, which stops it from being read as JSON.
info: Gzip compresses each batch before Fastly sends it, which cuts the bandwidth Fastly logging uses. KubeSense reads both compressed and uncompressed batches, so you can switch between Gzip and None at any time.
Fastly CLI (alternative)
fastly service logging https create \
--service-id <SERVICE_ID> --version active --autoclone \
--name "KubeSense" \
--url "https://<KUBESENSE_INGEST_HOST>/fastlylogs" \
--content-type "application/json" \
--header-name "Authorization" \
--header-value "Basic <token>" \
--method POST \
--json-format 1 \
--message-type blank \
--compression-codec gzip \
--request-max-entries 10000 \
--request-max-bytes 10485760 \
--format log-format.json
fastly service version activate --service-id <SERVICE_ID> --version latest--autoclone creates a new draft version from the active one. --json-format 1 is Array of JSON. --format takes the path to a file that holds your log format.
Log format
Use any JSON log format. If you already send Fastly logs to Datadog, reuse that exact format: KubeSense reads the Datadog-style fields (http.*, network.*, dd.trace_id) directly, and trace IDs link the CDN request to its trace.
If you don't have a format yet, this one covers the fields KubeSense uses:
{
"date": "%{begin:%Y-%m-%dT%H:%M:%S%Z}t",
"service": "%{req.service_id}V",
"host": "%{Fastly-Orig-Host}i",
"server_datacenter": "%{server.datacenter}V",
"fastly_info_state": "%{fastly_info.state}V",
"http": {
"method": "%m",
"url": "%{cstr_escape(req.url)}V",
"status_code": %s,
"request_time_ms": %D,
"useragent": "%{User-Agent}i",
"referer": "%{Referer}i",
"protocol": "%H"
},
"network": {
"client": { "ip": "%h" },
"geoip": {
"geo_country_code": "%{client.geo.country_code}V",
"geo_city": "%{client.geo.city.utf8}V"
},
"bytes_written": %B
}
}Fastly's own default JSON format works too. Fields KubeSense doesn't recognise are still kept as attributes. Text formats, such as Apache combined, are not supported.
Check that logs arrive
Open a few pages on your site through the Fastly domain. Within a minute, the requests show up on the KubeSense Logs page with Source = fastly.
2. Name the cluster and add tags (optional)
On the Logs page, every log belongs to a cluster. To set the cluster, the environment type or tags such as platform and env, add them to the endpoint URL as query parameters. Each one is optional. Each endpoint can use its own values, so several Fastly services or environments can send to the same KubeSense endpoint and still be told apart.
https://<KUBESENSE_INGEST_HOST>/fastlylogs?kubesense.tags=platform:web,env:prod&kubesense.cluster=cdn-prod| Parameter | Sets | When omitted |
|---|---|---|
kubesense.tags | Tags, as comma-separated key:value pairs. Add as many as you need | No tags |
kubesense.cluster | The log's Cluster | The platform tag, otherwise fastly |
kubesense.env_type | The log's Environment type | cdn |
Tags are also attributes, so you can filter on them like any other field. A tag replaces a log field with the same name: env:prod wins over an env field in your log format.
URL-encode values that contain spaces or &, for example %20 for a space. A change takes effect once the new version is activated.
3. Where the logs show up
Fastly logs appear on the Logs page next to your other logs. Filter on Source = fastly to see only them.
| Logs page field | Comes from |
|---|---|
| Timestamp | The time Fastly received the request, not the time it arrived in KubeSense |
| Source | Always fastly |
| Cluster | kubesense.cluster, otherwise the platform tag, otherwise fastly |
| Environment type | kubesense.env_type, otherwise cdn |
| Workload | The requested domain (host) |
| Instance | The Fastly POP that served the request, for example BOM or FRA |
| Service | The service field of your log format, otherwise fastly |
| Level | Set from the response status, see Levels |
| Body | <method> <url> <status> <time>ms, for example GET /products?id=1 503 42ms |
| Trace ID, Span ID | dd.trace_id and dd.span_id, when your log format includes them |
| Tags | kubesense.tags |
Attributes
Every field of your log format becomes an attribute, so you can filter, group and alert on it, using the field name from your log format. Nested fields are flattened with dots, for example http.status_code or network.geoip.geo_country_code. Number fields, such as status codes, timings and byte counts, can be used in numeric filters and aggregations. Empty values ("", -, (null)) are left out.
With the format from Log format, useful attributes include:
| Attribute | Example |
|---|---|
http.method | GET |
http.url | /products?id=1 |
http.status_code | 503 |
http.request_time_ms | 42 |
host | www.example.com |
server_datacenter | BOM |
fastly_info_state | HIT, MISS, HIT-CLUSTER, ... |
network.client.ip | 203.0.113.10 |
network.geoip.geo_country_code | IN |
platform, env | The tags from kubesense.tags |
With Fastly's default format, the same values are called request_method, url, response_status, response_state and client_ip. Whatever your format calls them, KubeSense still fills in the Body, Level, Workload and Instance from them.
Levels
| Level | Response status |
|---|---|
ERROR | 5xx |
WARN | 4xx |
INFO | Everything else |
Things to try
- All CDN errors: filter Source =
fastlyand Level =ERROR. - Cache hit ratio: group by
fastly_info_state. - Slowest URLs: sort or aggregate on
http.request_time_ms. - Traffic by POP or country: group by Instance or
network.geoip.geo_country_code. - 5xx spike alerts: alert when the count of Source =
fastlyand Level =ERRORexceeds a threshold. See Alerts. - From a slow request to its trace: with
dd.trace_idin your log format, open the linked trace from the log.
4. Troubleshooting
| Symptom | Check |
|---|---|
Fastly reports a failed challenge check or no such host | The URL is wrong or not reachable. It must be the endpoint URL from KubeSense, ending in /fastlylogs |
Fastly reports 401 errors | The custom header doesn't match the token from KubeSense. The header name must be Authorization and the value must include the Basic prefix |
Fastly reports 400 errors | The log format or line format is wrong. Use a JSON log format, Array of JSON and the Blank log line format |
| The endpoint is set up but no logs appear | The version that contains the endpoint isn't active. Click Activate on it |
| Logs appear but the body is a raw JSON object | Your log format has no method, URL or status field KubeSense recognises. Add http.method, http.url and http.status_code to it (Log format) |
| Logs appear under an unexpected cluster | The URL has no kubesense.cluster or platform tag (section 2) |
| Only some requests arrive | The endpoint has a Condition attached that filters requests |
Before sending logs, Fastly checks that the endpoint accepts logs from your service by requesting /.well-known/fastly/logging/challenge on the endpoint's host (Fastly's HTTPS logging guide). KubeSense answers this check for you, so there's nothing to set up on your side. If it fails, Fastly reports a challenge check error and sends nothing until the URL is fixed.
Changing the token
If the token has to be changed, for example after it was shared by mistake:
- Ask your KubeSense contact for a new token.
- Clone the active version, paste the new token into the endpoint's Custom header value, and activate the new version.
Fastly retries batches that fail, so requests logged while the token is being changed are delivered once the new token is in place.