« Back to Index

Fastly JSON Logging

View original Gist on GitHub

Tags: #fastly #cdn #logs #json

Alernative (not tested).vcl

// this is what's possible in vcl_error but might not be possible in our own subroutines...
synthetic {"{
  "geo": {
    "gmt_offset":"} json.escape(client.geo.gmt_offset) {",
    "city": ""} json.escape(client.geo.city) {"",
    "country_name": ""} json.escape(client.geo.country_name) {"",
    "ip_override": ""} json.escape(client.geo.ip_override) {""
  },
  "as": {
    "name": ""} json.escape(client.as.name) {""
  }
}"};

Fastly JSON Logging.md

Note: original log format that Fastly translates into VCL: %h [%{%Y-%m-%d %H:%M:%S}t.%{msec_frac}t] "%r" %>s %B %{tls.client.protocol}V %{fastly_info.state}V

Simple

# double quoted strings support percent escapes
# https://docs.fastly.com/vcl/types/string/

declare local var.json STRING;

set var.json = "{" + 
  "%22foo%22: %22bar%22," + 
  "%22boop%22: %22beep%22," + 
  "%22city%22: %22"+ json.escape(client.geo.city.utf8) + "%22" + 
"}";

set req.http.X-JSON = var.json;

# output:
# x-json: {"foo": "bar","boop": "beep","city": "bristol"}

Note: %22 is the HTML encoding for " (see docs).

Trying without encoding works, but is complicated to get working across multiple lines:

# what it would be nice to use...
{
  "timestamp":"%{begin:%Y-%m-%dT%H:%M:%S}t",
  "time_elapsed":%{time.elapsed.usec}V,
  "is_tls":%{if(req.is_ssl, "true", "false")}V,
  "client_ip":"%{req.http.Fastly-Client-IP}V",
  "geo_city":"%{client.geo.city}V",
  "geo_country_code":"%{client.geo.country_code}V",
  "request":"%{req.method}V",
  "host":"%{req.http.Fastly-Orig-Host}V",
  "url":"%{json.escape(req.url)}V",
  "request_referer":"%{json.escape(req.http.Referer)}V",
  "request_user_agent":"%{json.escape(req.http.User-Agent)}V",
  "request_accept_language":"%{json.escape(req.http.Accept-Language)}V",
  "request_accept_charset":"%{json.escape(req.http.Accept-Charset)}V",
  "cache_status":"%{regsub(fastly_info.state, "^(HIT-(SYNTH)|(HITPASS|HIT|MISS|PASS|ERROR|PIPE)).*", "\\2\\3") }V"
}

# what we end up using...
# this is done via the long string format {"..."}
#
# simple example:
# {"{  "foo":""} req.http.X-Foo {"",  "bar":"} req.http.X-Bar {" }"};
#
# which gets fugly quick, when made longer, as in real-world examples (also has to be all on one line!)
#
# https://docs.fastly.com/vcl/types/string/
set req.http.X-JSON2 = {"{   "timestamp":""} strftime({"%Y-%m-%dT%H:%M:%S"}, time.start) {"",   "time_elapsed":"} time.elapsed.usec {",   "is_tls":"} if(req.is_ssl, "true", "false") {",   "client_ip":""} req.http.Fastly-Client-IP {"",   "geo_city":""} client.geo.city {"",   "geo_country_code":""} client.geo.country_code {"",   "request":""} req.method {"",   "host":""} req.http.Fastly-Orig-Host {"",   "url":""} json.escape(req.url) {"",   "request_referer":""} json.escape(req.http.Referer) {"",   "request_user_agent":""} json.escape(req.http.User-Agent) {"",   "request_accept_language":""} json.escape(req.http.Accept-Language) {"",   "request_accept_charset":""} json.escape(req.http.Accept-Charset) {"",   "cache_status":""} regsub(fastly_info.state, "^(HIT-(SYNTH)|(HITPASS|HIT|MISS|PASS|ERROR|PIPE)).*", "\2\3")  {"" }"};

Note: both approaches will require blank to be set


## Advanced (and performance costly)

[https://github.com/fastly/vcl-json-generate](https://github.com/fastly/vcl-json-generate)

## interpolate fastly values.md

> note: variables such as beresp are only available in vcl_fetch

```vcl
set var.json = "{" + 
  "%22host%22: %22" + req.http.host + "%22," + 
  "%22backend%22: %22" + req.backend + "%22," + 
  "%22contentType%22: %22" + beresp.http.Content-Type + "%22," + 
  "%22cacheType%22: %22" + fastly_info.state + "%22," + 
  "%22pageSize%22: %22" + beresp.http.Content-Length + "%22," + 
  "%22bot%22: %22" + req.http.User-Agent + "%22" + 
"}";