Tags: #fastly #cdn #logs #json
// 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) {""
}
}"};
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
# 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" + "}";