Skip to content

Instantly share code, notes, and snippets.

@erikcw
Last active August 22, 2023 10:58
Show Gist options
  • Save erikcw/e999e1fb438dbbb91533 to your computer and use it in GitHub Desktop.
Save erikcw/e999e1fb438dbbb91533 to your computer and use it in GitHub Desktop.
Simple nginx lua script to add UUID to each request for end to end request tracking.
# Dependencies
# nginx_lua
# lua uuid module (luarocks install uuid)
http {
# this will be the request id
map $host $request_uuid {
default '';
}
log_format log_with_request_id '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'request_id: $request_uuid';
server {
# your server block here ...
access_log /var/log/nginx.log log_with_request_id;
set_by_lua $request_uuid '
if ngx.var.http_x_request_id == nil then
return uuid()
else
return ngx.var.http_x_request_id
end
';
}
}
@designermonkey
Copy link

I need behaviour like this. Thanks for this.

Question before I change loads of stuff... Is uuid a function in lua then?

@erikcw
Copy link
Author

erikcw commented Sep 5, 2019

@designermonkey You need to install the uuid library luarocks install uuid. See comments at the top of the gist.

I need behaviour like this. Thanks for this.

Question before I change loads of stuff... Is uuid a function in lua then?

@designermonkey
Copy link

I completely missed that. Thanks for a great utility!

@mjpieters
Copy link

mjpieters commented Mar 3, 2021

Note that NGINX 1.11.0 and up include a unique request id: $request_id. It's a 16-byte random value expressed in hex (so has a few bits more entropy than the UUID4 value produced by the LUA library). See the NGINX source code if you want to compare the implementation to the LUA version. 1.11.0 was released in May 2016, after this gist was created, but you no longer need LUA to provide you with a request tracking ID.

@omasseau
Copy link

omasseau commented Nov 5, 2021

Beware that $request_id generated by nginx is not a real UUID (it is not the same format).

@mjpieters
Copy link

Beware that $request_id generated by nginx is not a real UUID (it is not the same format).

That should not make a difference. UUID4 has 122 random bits (the remaining 6 are used to record the UUID version and variant, at least for the variant that LUA produces). The dashes between the hex digits are a convention, not a requirement for UUIDs; they simply delimit the 16 bytes into a groups of lengths 4, 2, 2, 2 and 6. You can 'transform' a $random_id value into a UUID with simple text transformations (replace the 13th hex digit with 4, update the 17th hex digit (0, 4, C -> 8; 1, 5, D -> 9; 2, 6, E -> A; 3, 7, F -> B), insert the dashes).

If all you need is a unique identifier for a request, $random_id is going to be way faster, don't get too hung up on the difference.

@raiden-dev
Copy link

Beware that $request_id generated by nginx is not a real UUID (it is not the same format).

That should not make a difference. UUID4 has 122 random bits (the remaining 6 are used to record the UUID version and variant, at least for the variant that LUA produces). The dashes between the hex digits are a convention, not a requirement for UUIDs; they simply delimit the 16 bytes into a groups of lengths 4, 2, 2, 2 and 6. You can 'transform' a $random_id value into a UUID with simple text transformations (replace the 13th hex digit with 4, update the 17th hex digit (0, 4, C -> 8; 1, 5, D -> 9; 2, 6, E -> A; 3, 7, F -> B), insert the dashes).

If all you need is a unique identifier for a request, $random_id is going to be way faster, don't get too hung up on the difference.

Looks like it could be implemented just with plain nginx's if and set statements:

# Sets $uuid variable as UUIDv4 (variant 1) converted from $request_id

set $uuid_m 4;

if ($request_id ~ '^(?<uuid_g1>[0-9a-f]{8})(?<uuid_g2>[0-9a-f]{4})(?<uuid_g3>[0-9a-f]{3})(?<uuid_n>[0-9a-f]{1})(?<uuid_g4>[0-9a-f]{3})(?<uuid_g5>[0-9a-f]{12})') {
}

if ($uuid_n = 0) {
  set $uuid_n 8;
}

if ($uuid_n = 4) {
  set $uuid_n 8;
}

if ($uuid_n = c) {
  set $uuid_n 8;
}

if ($uuid_n = 1) {
  set $uuid_n 9;
}

if ($uuid_n = 5) {
  set $uuid_n 9;
}

if ($uuid_n = d) {
  set $uuid_n 9;
}

if ($uuid_n = 2) {
  set $uuid_n a;
}

if ($uuid_n = 6) {
  set $uuid_n a;
}

if ($uuid_n = e) {
  set $uuid_n a;
}

if ($uuid_n = 3) {
  set $uuid_n b;
}

if ($uuid_n = 7) {
  set $uuid_n b;
}

if ($uuid_n = f) {
  set $uuid_n b;
}

set $uuid $uuid_g1-$uuid_g2-$uuid_m$uuid_g3-$uuid_n$uuid_g4-$uuid_g5;

@rmn-lux
Copy link

rmn-lux commented Jul 26, 2023

map would be nice for previous answer

map $uuid_n $uuid_n {
    0   8;
    1   9;
    2   a;
    3   b;
    4   8;
    5   9;
    6   a;
    7   b;
    c   8;
    d   9;
    e   a;
    f   b;
}


@mjpieters
Copy link

mjpieters commented Aug 9, 2023

Looks like it could be implemented just with plain nginx's if and set statements:

Your regex doesn't replace hex digit 13, you shifted digit 13-31 to positions 14-32, dropping off the 32nd hex digit. Probably not a big deal if your random values are properly random.

You can use a single map to do all the work with 4 regexes (one for each group of digit 17 values):

# map $request_id, a 32 (lowercase) hex digit random value, to a valid UUID4 value, formatted in 8h-4h-4h-4h-12h format.
# replaces hex digit 13 with '4', and the upper two bits of hex digit 17 with binary '10'
map $request_id $request_uuid4 {
    # <8h><4h><ignored h><3h><h digit for 0b??00><3h><12h>
    "~^(?<uuid_g1>[0-9a-f]{8})(?<uuid_g2>[0-9a-f]{4})[0-9a-f](?<uuid_g3>[0-9a-f]{3})[048c](?<uuid_g4>[0-9a-f]{3})(?<uuid_g5>[0-9a-f]{12})$" "${uuid_g1}-${uuid_g2}-4${uuid_g3}-8${uuid_g4}-${uuid_g5}";
    # <8h><4h><ignored h><3h><h digit for 0b??01><3h><12h>
    "~^(?<uuid_g1>[0-9a-f]{8})(?<uuid_g2>[0-9a-f]{4})[0-9a-f](?<uuid_g3>[0-9a-f]{3})[159d](?<uuid_g4>[0-9a-f]{3})(?<uuid_g5>[0-9a-f]{12})$" "${uuid_g1}-${uuid_g2}-4${uuid_g3}-9${uuid_g4}-${uuid_g5}";
    # <8h><4h><ignored h><3h><h digit for 0b??10><3h><12h>
    "~^(?<uuid_g1>[0-9a-f]{8})(?<uuid_g2>[0-9a-f]{4})[0-9a-f](?<uuid_g3>[0-9a-f]{3})[26ae](?<uuid_g4>[0-9a-f]{3})(?<uuid_g5>[0-9a-f]{12})$" "${uuid_g1}-${uuid_g2}-4${uuid_g3}-a${uuid_g4}-${uuid_g5}";
    # <8h><4h><ignored h><3h><h digit for 0b??11><3h><12h>
    "~^(?<uuid_g1>[0-9a-f]{8})(?<uuid_g2>[0-9a-f]{4})[0-9a-f](?<uuid_g3>[0-9a-f]{3})[37bf](?<uuid_g4>[0-9a-f]{3})(?<uuid_g5>[0-9a-f]{12})$" "${uuid_g1}-${uuid_g2}-4${uuid_g3}-b${uuid_g4}-${uuid_g5}";
}

# use $request_uuid4 in your log directive, or in a addheader directive to send the value back in responses.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment