Search code examples
dockernginxredisluaopenresty

how to access value of ngx.var.bytes_sent in access_by_lua_file


my lua script

local redis = require "resty.redis"
local red = redis:new()

red:set_timeout(1000)

local ok, err = red:connect("172.20.0.2", 6379)
    if not ok then
    ngx.log(ngx.CRIT, "Failed to connect to redis: ", err)
    return
end

function save_bytes_sent()
    local bytes_sent = ngx.var.bytes_sent
    local res, err = red:set("bytes_sent_" .. ngx.var.uri, bytes_sent)
        if not res then
        ngx.log(ngx.CRIT, "Failed to save bytes_sent in redis: ", err)
        return
    end

    local ok, err = red:close()
        if not ok then
        ngx.log(ngx.CRIT, "Failed to close the connection to redis: ", err)
        return
    end

    ngx.log(ngx.CRIT, "Saved bytes_sent in redis successfully")
end

save_bytes_sent()

and my nginx.conf

location / {
    access_by_lua_file /test.lua;
}

i use openresty docker image and all config is ok such as volumes, network and ...

I want to get the ngx.var.bytes_sent value from the response of each request and insert it in redis, but my problem is that in access_by_lua_file, its value is zero, and if I use the log_by_lua_file, I can get its value, but I cannot use Redis because there is no access to Redis in this phase.

What is the solution for this problem?


Solution

  • Since Rewrite/Access Phase precedes Content Phase, it is too early to use $bytes_sent (ngx.var.bytes_sent) in access_by_lua_* directives (the value will be zero). See the figure below.

    Log Phase (log_by_lua_*) is the right place, but, as you mentioned, it is not possible to use the Redis client in this phase (Cosocket API is disabled in this context).

    The common trick is to wrap your code in a callback and run it via zero-delay timer. See this section of the documentation: Cosockets Not Available Everywhere.

    A rough example:

    log_by_lua_block {
        local redis = require "resty.redis"
    
        local REDIS_HOST = "127.20.0.2"
        local REDIS_PORT = 6379
    
    
        local function get_redis_connection(host, port)
            local red = redis:new()
            red:set_timeout(1000)
            local ok, err = red:connect(host, port)
            if not ok then
                return nil, err
            end
            return red
        end
    
        local function save_bytes_sent(_, uri, bytes_sent)
            local red, err = get_redis_connection(REDIS_HOST, REDIS_PORT)
            if not red then
                ngx.log(ngx.CRIT, "Failed to connect to redis: ", err)
                return
            end
    
            local res, err = red:set("bytes_sent_" .. uri, bytes_sent)
            if not res then
                ngx.log(ngx.CRIT, "Failed to save bytes_sent in redis: ", err)
                return
            end
    
            local ok, err = red:close()
            if not ok then
                ngx.log(ngx.CRIT, "Failed to close the connection to redis: ", err)
                return
            end
    
            ngx.log(ngx.CRIT, "Saved bytes_sent in redis successfully")
        end
    
        ngx.timer.at(0, save_bytes_sent, ngx.var.uri, ngx.var.bytes_sent)
    }
    

    a diagram showing the order in which Lua Nginx Module Directives are executed