kong/kong/pdk/log.lua (481 lines of code) (raw):
---
-- This namespace contains an instance of a logging facility, which is a
-- table containing all of the methods described below.
--
-- This instance is namespaced per plugin. Before
-- executing a plugin, Kong swaps this instance with a logging facility
-- dedicated to the plugin. This allows the logs to be prefixed with the
-- plugin's name for debugging purposes.
--
-- @module kong.log
local errlog = require "ngx.errlog"
local ngx_re = require "ngx.re"
local inspect = require "inspect"
local ngx_ssl = require "ngx.ssl"
local phase_checker = require "kong.pdk.private.phases"
local utils = require "kong.tools.utils"
local sub = string.sub
local type = type
local find = string.find
local select = select
local concat = table.concat
local getinfo = debug.getinfo
local reverse = string.reverse
local tostring = tostring
local tonumber = tonumber
local setmetatable = setmetatable
local ngx = ngx
local kong = kong
local check_phase = phase_checker.check
local split = utils.split
local byte = string.byte
local _PREFIX = "[kong] "
local _DEFAULT_FORMAT = "%file_src:%line_src %message"
local _DEFAULT_NAMESPACED_FORMAT = "%file_src:%line_src [%namespace] %message"
local PHASES = phase_checker.phases
local PHASES_LOG = PHASES.log
local QUESTION_MARK = byte("?")
local phases_with_ctx =
phase_checker.new(PHASES.rewrite,
PHASES.access,
PHASES.header_filter,
PHASES.response,
PHASES.body_filter,
PHASES_LOG)
local _LEVELS = {
debug = ngx.DEBUG,
info = ngx.INFO,
notice = ngx.NOTICE,
warn = ngx.WARN,
err = ngx.ERR,
crit = ngx.CRIT,
alert = ngx.ALERT,
emerg = ngx.EMERG,
}
local _MODIFIERS = {
["%file_src"] = {
flag = "S",
info = function(info)
local short_src = info.short_src
if short_src then
local rev_src = reverse(short_src)
local idx = find(rev_src, "/", nil, true)
if idx then
return sub(short_src, #rev_src - idx + 2)
end
return short_src
end
end
},
["%line_src"] = {
flag = "l",
info_key = "currentline",
},
["%func_name"] = {
flag = "n",
info_key = "name",
},
["%message"] = {
message = true,
},
-- %namespace -- precompiled
}
local function parse_modifiers(format)
local buf, err = ngx_re.split(format, [==[(?<!%)(%[a-z_]+)]==], nil, nil, 0)
if not buf then
return nil, "could not parse format: " .. err
end
local buf_len = #buf
for i = 1, buf_len do
local mod = _MODIFIERS[buf[i]]
if mod then
if mod.message then
buf.message_idxs = buf.message_idxs or {}
table.insert(buf.message_idxs, i)
else
buf.debug_flags = (buf.debug_flags or "") .. mod.flag
buf.modifiers = buf.modifiers or {}
table.insert(buf.modifiers, {
idx = i,
info = mod.info,
info_key = mod.info_key,
})
end
end
end
buf.n_modifiers = buf.modifiers and #buf.modifiers or 0
buf.n_messages = buf.message_idxs and #buf.message_idxs or 0
buf.n_len = buf_len
return buf
end
local serializers = {
[1] = function(buf, to_string, ...)
buf[1] = to_string((select(1, ...)))
end,
[2] = function(buf, to_string, ...)
buf[1] = to_string((select(1, ...)))
buf[2] = to_string((select(2, ...)))
end,
[3] = function(buf, to_string, ...)
buf[1] = to_string((select(1, ...)))
buf[2] = to_string((select(2, ...)))
buf[3] = to_string((select(3, ...)))
end,
[4] = function(buf, to_string, ...)
buf[1] = to_string((select(1, ...)))
buf[2] = to_string((select(2, ...)))
buf[3] = to_string((select(3, ...)))
buf[4] = to_string((select(4, ...)))
end,
[5] = function(buf, to_string, ...)
buf[1] = to_string((select(1, ...)))
buf[2] = to_string((select(2, ...)))
buf[3] = to_string((select(3, ...)))
buf[4] = to_string((select(4, ...)))
buf[5] = to_string((select(5, ...)))
end,
}
--- Writes a log line to the location specified by the current Nginx
-- configuration block's `error_log` directive, with the `notice` level (similar
-- to `print()`).
--
-- The Nginx `error_log` directive is set via the `log_level`, `proxy_error_log`
-- and `admin_error_log` Kong configuration properties.
--
-- Arguments given to this function are concatenated similarly to
-- `ngx.log()`, and the log line reports the Lua file and line number from
-- which it was invoked. Unlike `ngx.log()`, this function prefixes error
-- messages with `[kong]` instead of `[lua]`.
--
-- Arguments given to this function can be of any type, but table arguments
-- are converted to strings via `tostring` (thus potentially calling a
-- table's `__tostring` metamethod if set). This behavior differs from
-- `ngx.log()` (which only accepts table arguments if they define the
-- `__tostring` metamethod) with the intent to simplify its usage and be more
-- forgiving and intuitive.
--
-- Produced log lines have the following format when logging is invoked from
-- within the core:
--
-- ``` plain
-- [kong] %file_src:%line_src %message
-- ```
--
-- In comparison, log lines produced by plugins have the following format:
--
-- ``` plain
-- [kong] %file_src:%line_src [%namespace] %message
-- ```
--
-- Where:
--
-- * `%namespace`: The configured namespace (in this case, the plugin name).
-- * `%file_src`: The filename the log was called from.
-- * `%line_src`: The line number the log was called from.
-- * `%message`: The message, made of concatenated arguments given by the caller.
--
-- For example, the following call:
--
-- ``` lua
-- kong.log("hello ", "world")
-- ```
--
-- would, within the core, produce a log line similar to:
--
-- ``` plain
-- 2017/07/09 19:36:25 [notice] 25932#0: *1 [kong] some_file.lua:54 hello world, client: 127.0.0.1, server: localhost, request: "GET /log HTTP/1.1", host: "localhost"
-- ```
--
-- If invoked from within a plugin (for example, `key-auth`) it would include the
-- namespace prefix:
--
-- ``` plain
-- 2017/07/09 19:36:25 [notice] 25932#0: *1 [kong] some_file.lua:54 [key-auth] hello world, client: 127.0.0.1, server: localhost, request: "GET /log HTTP/1.1", host: "localhost"
-- ```
--
-- @function kong.log
-- @phases init_worker, certificate, rewrite, access, header_filter, response, body_filter, log
-- @param ... All params will be concatenated and stringified before being sent to the log.
-- @return Nothing. Throws an error on invalid inputs.
--
-- @usage
-- kong.log("hello ", "world") -- alias to kong.log.notice()
---
-- Similar to `kong.log()`, but the produced log has the severity given by
-- `<level>`, instead of `notice`. The supported levels are:
--
-- * `kong.log.alert()`
-- * `kong.log.crit()`
-- * `kong.log.err()`
-- * `kong.log.warn()`
-- * `kong.log.notice()`
-- * `kong.log.info()`
-- * `kong.log.debug()`
--
-- Logs have the same format as that of `kong.log()`. For
-- example, the following call:
--
-- ``` lua
-- kong.log.err("hello ", "world")
-- ```
--
-- would, within the core, produce a log line similar to:
--
-- ``` plain
-- 2017/07/09 19:36:25 [error] 25932#0: *1 [kong] some_file.lua:54 hello world, client: 127.0.0.1, server: localhost, request: "GET /log HTTP/1.1", host: "localhost"
-- ```
--
-- If invoked from within a plugin (for example, `key-auth`) it would include the
-- namespace prefix:
--
-- ``` plain
-- 2017/07/09 19:36:25 [error] 25932#0: *1 [kong] some_file.lua:54 [key-auth] hello world, client: 127.0.0.1, server: localhost, request: "GET /log HTTP/1.1", host: "localhost"
-- ```
--
-- @function kong.log.LEVEL
-- @phases init_worker, certificate, rewrite, access, header_filter, response, body_filter, log
-- @param ... All params will be concatenated and stringified before being sent to the log.
-- @return Nothing. Throws an error on invalid inputs.
-- @usage
-- kong.log.warn("something require attention")
-- kong.log.err("something failed: ", err)
-- kong.log.alert("something requires immediate action")
local function gen_log_func(lvl_const, imm_buf, to_string, stack_level, sep)
to_string = to_string or tostring
stack_level = stack_level or 2
local sys_log_level
local variadic_buf = {}
return function(...)
if not sys_log_level and ngx.get_phase() ~= "init" then
-- only grab sys_log_level after init_by_lua, where it is
-- hard-coded
sys_log_level = errlog.get_sys_filter_level()
end
if sys_log_level and lvl_const > sys_log_level then
-- early exit if sys_log_level is higher than the current
-- log call
return
end
local n = select("#", ...)
if imm_buf.debug_flags then
local info = getinfo(stack_level, imm_buf.debug_flags)
for i = 1, imm_buf.n_modifiers do
local mod = imm_buf.modifiers[i]
if not info then
imm_buf[mod.idx] = "?"
elseif mod.info then
imm_buf[mod.idx] = mod.info(info) or "?"
else
imm_buf[mod.idx] = info[mod.info_key] or "?"
end
end
end
if serializers[n] then
serializers[n](variadic_buf, to_string, ...)
else
for i = 1, n do
variadic_buf[i] = to_string((select(i, ...)))
end
end
local msg = concat(variadic_buf, sep, 1, n)
for i = 1, imm_buf.n_messages do
imm_buf[imm_buf.message_idxs[i]] = msg
end
local fullmsg = concat(imm_buf, nil, 1, imm_buf.n_len)
if to_string == inspect then
local fullmsg_len = #fullmsg
local WRAP = 120
local i = fullmsg:find("\n") + 1
local header = fullmsg:sub(1, i - 2) .. ("-"):rep(WRAP - i + 3) .. "+"
errlog.raw_log(lvl_const, header)
while i <= fullmsg_len do
local part = string.sub(fullmsg, i, i + WRAP - 1)
local nl = part:match("()\n")
if nl then
part = string.sub(fullmsg, i, i + nl - 2)
i = i + nl
else
i = i + WRAP
end
part = part .. (" "):rep(WRAP - #part)
errlog.raw_log(lvl_const, "|" .. part .. "|")
if i > fullmsg_len then
errlog.raw_log(lvl_const, "+" .. ("-"):rep(WRAP) .. "+")
end
end
return
end
errlog.raw_log(lvl_const, fullmsg)
end
end
--- Write a deprecation log line (similar to `kong.log.warn`).
--
-- Arguments given to this function can be of any type, but table arguments
-- are converted to strings via `tostring` (thus potentially calling a
-- table's `__tostring` metamethod if set). When the last argument is a table,
-- it is considered as a deprecation metadata. The table can include the
-- following properties:
--
-- ``` lua
-- {
-- after = "2.5.0", -- deprecated after Kong version 2.5.0 (defaults to `nil`)
-- removal = "3.0.0", -- about to be removed with Kong version 3.0.0 (defaults to `nil`)
-- trace = true, -- writes stack trace along with the deprecation message (defaults to `nil`)
-- }
-- ```
--
-- For example, the following call:
--
-- ``` lua
-- kong.log.deprecation("hello ", "world")
-- ```
--
-- would, within the core, produce a log line similar to:
--
-- ``` plain
-- 2017/07/09 19:36:25 [warn] 25932#0: *1 [kong] some_file.lua:54 hello world, client: 127.0.0.1, server: localhost, request: "GET /log HTTP/1.1", host: "localhost"
-- ```
--
-- If invoked from within a plugin (for example, `key-auth`) it would include the
-- namespace prefix:
--
-- ``` plain
-- 2017/07/09 19:36:25 [warn] 25932#0: *1 [kong] some_file.lua:54 [key-auth] hello world, client: 127.0.0.1, server: localhost, request: "GET /log HTTP/1.1", host: "localhost"
-- ```
--
-- And with metatable, the following call:
--
-- ``` lua
-- kong.log.deprecation("hello ", "world", { after = "2.5.0", removal = "3.0.0" })
-- ```
--
-- would, within the core, produce a log line similar to:
--
-- ``` plain
-- 2017/07/09 19:36:25 [warn] 25932#0: *1 [kong] some_file.lua:54 hello world (deprecated after 2.5.0, scheduled for removal in 3.0.0), client: 127.0.0.1, server: localhost, request: "GET /log HTTP/1.1", host: "localhost"
-- ```
--
-- @function kong.log.deprecation
-- @phases init_worker, certificate, rewrite, access, header_filter, response, body_filter, log
-- @param ... all params will be concatenated and stringified before being sent to the log
-- (if the last param is a table, it is considered as a deprecation metadata)
-- @return Nothing; throws an error on invalid inputs.
--
-- @usage
-- kong.log.deprecation("hello ", "world")
-- kong.log.deprecation("hello ", "world", { after = "2.5.0" })
-- kong.log.deprecation("hello ", "world", { removal = "3.0.0" })
-- kong.log.deprecation("hello ", "world", { after = "2.5.0", removal = "3.0.0" })
-- kong.log.deprecation("hello ", "world", { trace = true })
local new_deprecation do
local mt = getmetatable(require("kong.deprecation"))
new_deprecation = function(write)
return setmetatable({ write = write }, mt)
end
end
---
-- Like `kong.log()`, this function produces a log with a `notice` level
-- and accepts any number of arguments. If inspect logging is disabled
-- via `kong.log.inspect.off()`, then this function prints nothing, and is
-- aliased to a "NOP" function to save CPU cycles.
--
-- This function differs from `kong.log()` in the sense that arguments will be
-- concatenated with a space(`" "`), and each argument is
-- pretty-printed:
--
-- * Numbers are printed (e.g. `5` -> `"5"`)
-- * Strings are quoted (e.g. `"hi"` -> `'"hi"'`)
-- * Array-like tables are rendered (e.g. `{1,2,3}` -> `"{1, 2, 3}"`)
-- * Dictionary-like tables are rendered on multiple lines
--
-- This function is intended for debugging, and usage
-- in production code paths should be avoided due to the expensive formatting
-- operations it can perform. Existing statements can be left in production code
-- but nopped by calling `kong.log.inspect.off()`.
--
-- When writing logs, `kong.log.inspect()` always uses its own format, defined
-- as:
--
-- ``` plain
-- %file_src:%func_name:%line_src %message
-- ```
--
-- Where:
--
-- * `%file_src`: The filename the log was called from.
-- * `%func_name`: The name of the function the log was called from.
-- * `%line_src`: The line number the log was called from.
-- * `%message`: The message, made of concatenated, pretty-printed arguments
-- given by the caller.
--
-- This function uses the [inspect.lua](https://github.com/kikito/inspect.lua)
-- library to pretty-print its arguments.
--
-- @function kong.log.inspect
-- @phases init_worker, certificate, rewrite, access, header_filter, response, body_filter, log
-- @param ... Parameters are concatenated with spaces between them and
-- rendered as described.
-- @usage
-- kong.log.inspect("some value", a_variable)
local new_inspect
do
local function nop() end
local _inspect_mt = {
__call = function(self, ...)
self.print(...)
end,
}
new_inspect = function(namespace)
local _INSPECT_FORMAT = _PREFIX .. "%file_src:%func_name:%line_src ["..namespace.."]\n%message"
local inspect_buf = assert(parse_modifiers(_INSPECT_FORMAT))
local self = {}
---
-- Enables inspect logs for this logging facility. Calls to
-- `kong.log.inspect` will be writing log lines with the appropriate
-- formatting of arguments.
--
-- @function kong.log.inspect.on
-- @phases init_worker, certificate, rewrite, access, header_filter, response, body_filter, log
-- @usage
-- kong.log.inspect.on()
function self.on()
self.print = gen_log_func(_LEVELS.debug, inspect_buf, inspect, 3, " ")
end
---
-- Disables inspect logs for this logging facility. All calls to
-- `kong.log.inspect()` will be nopped.
--
-- @function kong.log.inspect.off
-- @phases init_worker, certificate, rewrite, access, header_filter, response, body_filter, log
-- @usage
-- kong.log.inspect.off()
function self.off()
self.print = nop
end
self.on()
return setmetatable(self, _inspect_mt)
end
end
local _log_mt = {
__call = function(self, ...)
return self.notice(...)
end,
}
local function get_default_serialize_values()
if ngx.config.subsystem == "http" then
return {
{ key = "request.headers.authorization", value = "REDACTED", mode = "replace" },
{ key = "request.headers.proxy-authorization", value = "REDACTED", mode = "replace" },
}
end
return {}
end
---
-- Sets a value to be used on the `serialize` custom table.
--
-- Logging plugins use the output of `kong.log.serialize()` as a base for their logs.
-- This function lets you customize the log output.
--
-- It can be used to replace existing values in the output, or to delete
-- existing values by passing `nil`.
--
-- **Note:** The type-checking of the `value` parameter can take some time, so
-- it is deferred to the `serialize()` call, which happens in the log
-- phase in most real-usage cases.
--
-- @function kong.log.set_serialize_value
-- @phases certificate, rewrite, access, header_filter, response, body_filter, log
-- @tparam string key The name of the field.
-- @tparam number|string|boolean|table value Value to be set. When a table is used, its keys must be numbers, strings, or booleans, and its values can be numbers, strings, or other tables like itself, recursively.
-- @tparam table options Can contain two entries: options.mode can be `set` (the default, always sets), `add` (only add if entry does not already exist) and `replace` (only change value if it already exists).
-- @treturn table The request information table.
-- @usage
-- -- Adds a new value to the serialized table
-- kong.log.set_serialize_value("my_new_value", 1)
-- assert(kong.log.serialize().my_new_value == 1)
--
-- -- Value can be a table
-- kong.log.set_serialize_value("my", { new = { value = 2 } })
-- assert(kong.log.serialize().my.new.value == 2)
--
-- -- It is possible to change an existing serialized value
-- kong.log.set_serialize_value("my_new_value", 3)
-- assert(kong.log.serialize().my_new_value == 3)
--
-- -- Unset an existing value by setting it to nil
-- kong.log.set_serialize_value("my_new_value", nil)
-- assert(kong.log.serialize().my_new_value == nil)
--
-- -- Dots in the key are interpreted as table accesses
-- kong.log.set_serialize_value("my.new.value", 4)
-- assert(kong.log.serialize().my.new_value == 4)
--
local function set_serialize_value(key, value, options)
check_phase(phases_with_ctx)
options = options or {}
local mode = options.mode or "set"
if type(key) ~= "string" then
error("key must be a string", 2)
end
if mode ~= "set" and mode ~= "add" and mode ~= "replace" then
error("mode must be 'set', 'add' or 'replace'", 2)
end
local ongx = (options or {}).ngx or ngx
local ctx = ongx.ctx
ctx.serialize_values = ctx.serialize_values or get_default_serialize_values()
ctx.serialize_values[#ctx.serialize_values + 1] =
{ key = key, value = value, mode = mode }
end
local serialize
do
local function is_valid_value(v, visited)
local t = type(v)
if v == nil or t == "number" or t == "string" or t == "boolean" then
return true
end
if t ~= "table" then
return false
end
if visited[v] then
return false
end
visited[v] = true
for k, val in pairs(v) do
t = type(k)
if (t ~= "string"
and t ~= "number"
and t ~= "boolean")
or not is_valid_value(val, visited)
then
return false
end
end
return true
end
-- Modify returned table with values set with kong.log.set_serialize_values
local function edit_result(ctx, root)
local serialize_values = ctx.serialize_values or get_default_serialize_values()
local key, mode, new_value, subkeys, node, subkey, last_subkey, existing_value
for _, item in ipairs(serialize_values) do
key, mode, new_value = item.key, item.mode, item.value
if not is_valid_value(new_value, {}) then
error("value must be nil, a number, string, boolean or a non-self-referencial table containing numbers, string and booleans", 2)
end
-- Split key by ., creating subtables when needed
subkeys = setmetatable(split(key, "."), nil)
node = root -- start in root, iterate with each subkey
for i = 1, #subkeys - 1 do -- note that last subkey is treated differently, below
subkey = subkeys[i]
if node[subkey] == nil then
if mode == "set" or mode == "add" then
node[subkey] = {} -- add subtables as needed
else
node = nil
break -- mode == replace; and we have a missing link on the "chain"
end
end
if type(node[subkey]) ~= "table" then
error("The key '" .. key .. "' could not be used as a serialize value. " ..
"Subkey '" .. subkey .. "' is not a table. It's " .. tostring(node[subkey]))
end
node = node[subkey]
end
if type(node) == "table" then
last_subkey = subkeys[#subkeys]
existing_value = node[last_subkey]
if (mode == "set")
or (mode == "add" and existing_value == nil)
or (mode == "replace" and existing_value ~= nil)
then
node[last_subkey] = new_value
end
end
end
return root
end
---
-- Generates a table with useful information for logging.
--
-- This method can be used in the `http` subsystem.
--
-- The following fields are included in the returned table:
-- * `client_ip` - client IP address in textual format.
-- * `latencies` - request/proxy latencies.
-- * `request.headers` - request headers.
-- * `request.method` - request method.
-- * `request.querystring` - request query strings.
-- * `request.size` - size of request.
-- * `request.url` and `request.uri` - URL and URI of request.
-- * `response.headers` - response headers.
-- * `response.size` - size of response.
-- * `response.status` - response HTTP status code.
-- * `route` - route object matched.
-- * `service` - service object used.
-- * `started_at` - timestamp this request came in, in milliseconds.
-- * `tries` - Upstream information; this is an array and if any balancer retries occurred, will contain more than one entry.
-- * `upstream_uri` - request URI sent to Upstream.
--
-- The following fields are only present in an authenticated request (with consumer):
--
-- * `authenticated_entity` - credential used for authentication.
-- * `consumer` - consumer entity accessing the resource.
--
-- The following fields are only present in a TLS/HTTPS request:
-- * `request.tls.version` - TLS/SSL version used by the connection.
-- * `request.tls.cipher` - TLS/SSL cipher used by the connection.
-- * `request.tls.client_verify` - mTLS validation result. Contents are the same as described in [$ssl_client_verify](https://nginx.org/en/docs/http/ngx_http_ssl_module.html#var_ssl_client_verify).
--
-- **Warning:** This function may return sensitive data (e.g., API keys).
-- Consider filtering before writing it to unsecured locations.
--
-- All fields in the returned table may be altered using `kong.log.set_serialize_value`.
--
-- The following HTTP authentication headers are redacted by default, if they appear in the request:
-- * `request.headers.authorization`
-- * `request.headers.proxy-authorization`
--
-- To see what content is present in your setup, enable any of the logging
-- plugins (e.g., `file-log`) and the output written to the log file is the table
-- returned by this function JSON-encoded.
--
-- @function kong.log.serialize
-- @phases log
-- @treturn table the request information table
-- @usage
-- kong.log.serialize()
if ngx.config.subsystem == "http" then
function serialize(options)
check_phase(PHASES_LOG)
local ongx = (options or {}).ngx or ngx
local okong = (options or {}).kong or kong
local ctx = ongx.ctx
local var = ongx.var
local authenticated_entity
if ctx.authenticated_credential ~= nil then
authenticated_entity = {
id = ctx.authenticated_credential.id,
consumer_id = ctx.authenticated_credential.consumer_id
}
end
local request_tls
local request_tls_ver = ngx_ssl.get_tls1_version_str()
if request_tls_ver then
request_tls = {
version = request_tls_ver,
cipher = var.ssl_cipher,
client_verify = ctx.CLIENT_VERIFY_OVERRIDE or var.ssl_client_verify,
}
end
local request_uri = var.request_uri or ""
local host_port = ctx.host_port or var.server_port
local request_size = var.request_length
if tonumber(request_size, 10) then
request_size = tonumber(request_size, 10)
end
local response_size = var.bytes_sent
if tonumber(response_size, 10) then
response_size = tonumber(response_size, 10)
end
local upstream_uri = var.upstream_uri or ""
if upstream_uri ~= "" and not find(upstream_uri, "?", nil, true) then
if byte(ctx.request_uri or var.request_uri, -1) == QUESTION_MARK then
upstream_uri = upstream_uri .. "?"
elseif var.is_args == "?" then
upstream_uri = upstream_uri .. "?" .. (var.args or "")
end
end
return edit_result(ctx, {
request = {
uri = request_uri,
url = var.scheme .. "://" .. var.host .. ":" .. host_port .. request_uri,
querystring = okong.request.get_query(), -- parameters, as a table
method = okong.request.get_method(), -- http method
headers = okong.request.get_headers(),
size = request_size,
tls = request_tls
},
upstream_uri = upstream_uri,
response = {
status = ongx.status,
headers = ongx.resp.get_headers(),
size = response_size,
},
tries = (ctx.balancer_data or {}).tries,
latencies = {
kong = (ctx.KONG_PROXY_LATENCY or ctx.KONG_RESPONSE_LATENCY or 0) +
(ctx.KONG_RECEIVE_TIME or 0),
proxy = ctx.KONG_WAITING_TIME or -1,
request = var.request_time * 1000
},
authenticated_entity = authenticated_entity,
route = ctx.route,
service = ctx.service,
consumer = ctx.authenticated_consumer,
client_ip = var.remote_addr,
started_at = okong.request.get_start_time(),
})
end
else
function serialize(options)
check_phase(PHASES_LOG)
local ongx = (options or {}).ngx or ngx
local okong = (options or {}).kong or kong
local ctx = ongx.ctx
local var = ongx.var
local authenticated_entity
if ctx.authenticated_credential ~= nil then
authenticated_entity = {
id = ctx.authenticated_credential.id,
consumer_id = ctx.authenticated_credential.consumer_id
}
end
local session_tls
local session_tls_ver = ngx_ssl.get_tls1_version_str()
if session_tls_ver then
session_tls = {
version = session_tls_ver,
cipher = var.ssl_cipher,
client_verify = ctx.CLIENT_VERIFY_OVERRIDE or var.ssl_client_verify,
}
end
local host_port = ctx.host_port or var.server_port
return edit_result(ctx, {
session = {
tls = session_tls,
received = tonumber(var.bytes_received, 10),
sent = tonumber(var.bytes_sent, 10),
status = ongx.status,
server_port = tonumber(host_port, 10),
},
upstream = {
received = tonumber(var.upstream_bytes_received, 10),
sent = tonumber(var.upstream_bytes_sent, 10),
},
tries = (ctx.balancer_data or {}).tries,
latencies = {
kong = ctx.KONG_PROXY_LATENCY or ctx.KONG_RESPONSE_LATENCY or 0,
session = var.session_time * 1000,
},
authenticated_entity = authenticated_entity,
route = ctx.route,
service = ctx.service,
consumer = ctx.authenticated_consumer,
client_ip = var.remote_addr,
started_at = okong.request.get_start_time(),
})
end
end
end
local function new_log(namespace, format)
if type(namespace) ~= "string" then
error("namespace must be a string", 2)
end
if namespace == "" then
error("namespace cannot be an empty string", 2)
end
if format then
if type(format) ~= "string" then
error("format must be a string if specified", 2)
end
if format == "" then
error("format cannot be an empty string if specified", 2)
end
end
local self = {}
function self.set_format(fmt)
if fmt and type(fmt) ~= "string" then
error("format must be a string", 2)
elseif not fmt then
fmt = _DEFAULT_NAMESPACED_FORMAT
end
-- pre-compile namespace into format
local format = _PREFIX .. fmt:gsub("([^%%])%%namespace", "%1" .. namespace)
local buf, err = parse_modifiers(format)
if not buf then
error(err, 2)
end
for log_lvl_name, log_lvl in pairs(_LEVELS) do
self[log_lvl_name] = gen_log_func(log_lvl, buf)
end
self.deprecation = new_deprecation(gen_log_func(_LEVELS.warn, buf, nil, 5))
end
self.set_format(format)
self.inspect = new_inspect(namespace)
self.set_serialize_value = set_serialize_value
self.serialize = serialize
return setmetatable(self, _log_mt)
end
_log_mt.__index = _log_mt
_log_mt.new = new_log
return {
new = function()
return new_log("core", _DEFAULT_FORMAT)
end,
}