Skip to content

Commit

Permalink
feat(opentelemetry):set variable resource attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
SuzyWangIBMer authored and samugi committed Nov 15, 2024
1 parent 8a295b8 commit 77953ff
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 4 deletions.
6 changes: 6 additions & 0 deletions kong/plugins/opentelemetry/handler.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

local otel_traces = require "kong.plugins.opentelemetry.traces"
local otel_logs = require "kong.plugins.opentelemetry.logs"
local otel_utils = require "kong.plugins.opentelemetry.utils"
local dynamic_hook = require "kong.dynamic_hook"
local o11y_logs = require "kong.observability.logs"
local kong_meta = require "kong.meta"
Expand Down Expand Up @@ -30,6 +31,11 @@ function OpenTelemetryHandler:access(conf)
if conf.traces_endpoint then
otel_traces.access(conf)
end

-- Read resource attributes variable
if conf.resource_attributes then
otel_utils.read_resource_attributes(conf.resource_attributes)
end
end


Expand Down
2 changes: 1 addition & 1 deletion kong/plugins/opentelemetry/logs.lua
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ local prepare_logs = otlp.prepare_logs

local function http_export_logs(conf, logs_batch)
local headers = get_headers(conf.headers)
local payload = encode_logs(logs_batch, conf.resource_attributes)
local payload = encode_logs(logs_batch, otel_utils.compiled_resource_attributes)

local ok, err = http_export_request({
connect_timeout = conf.connect_timeout,
Expand Down
11 changes: 11 additions & 0 deletions kong/plugins/opentelemetry/schema.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
local typedefs = require "kong.db.schema.typedefs"
local Schema = require "kong.db.schema"
local pl_template = require "pl.template"

local compile_opts = {
escape = "\xff", -- disable '#' as a valid template escape
}

local function custom_validator(attributes)
for _, v in pairs(attributes) do
Expand All @@ -14,6 +19,12 @@ local function custom_validator(attributes)
if vtype == "string" and #v == 0 then
return nil, "required field missing"
end
local status, res, err = pcall(pl_template.compile, v, compile_opts)
if not status or err then
return false, "value '" .. v ..
"' is not in supported format, error:" ..
(status and res or err)
end
end

return true
Expand Down
2 changes: 1 addition & 1 deletion kong/plugins/opentelemetry/traces.lua
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ end

local function http_export_traces(conf, spans)
local headers = get_headers(conf.headers)
local payload = encode_traces(spans, conf.resource_attributes)
local payload = encode_traces(spans, otel_utils.compiled_resource_attributes)

local ok, err = http_export_request({
connect_timeout = conf.connect_timeout,
Expand Down
123 changes: 121 additions & 2 deletions kong/plugins/opentelemetry/utils.lua
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
local http = require "resty.http"
local clone = require "table.clone"

local sandbox = require "kong.tools.sandbox"
local deep_copy = require("kong.tools.table").deep_copy
local pl_template = require "pl.template"
local lua_enabled = sandbox.configuration.enabled
local sandbox_enabled = sandbox.configuration.sandbox_enabled
local get_request_headers = kong.request.get_headers
local get_uri_args = kong.request.get_query
local rawset = rawset
local str_find = string.find
local tostring = tostring
local null = ngx.null

local EMPTY = require("kong.tools.table").EMPTY

local CONTENT_TYPE_HEADER_NAME = "Content-Type"
local DEFAULT_CONTENT_TYPE_HEADER = "application/x-protobuf"
Expand All @@ -13,6 +21,8 @@ local DEFAULT_HEADERS = {

local _log_prefix = "[otel] "

local compiled_resource_attributes = {}

local function http_export_request(conf, pb_data, headers)
local httpc = http.new()
httpc:set_timeouts(conf.connect_timeout, conf.send_timeout, conf.read_timeout)
Expand Down Expand Up @@ -48,8 +58,117 @@ local function get_headers(conf_headers)
end


local compile_opts = {
escape = "\xff", -- disable '#' as a valid template escape
}

local template_cache = setmetatable( {}, { __mode = "k" })


local function param_value(source_template, resource_attributes, template_env)
if not source_template or source_template == "" then
return nil
end

if not lua_enabled then
-- Detect expressions in the source template
local expr = str_find(source_template, "%$%(.*%)")
if expr then
return nil, "loading of untrusted Lua code disabled because " ..
"'untrusted_lua' config option is set to 'off'"
end
-- Lua is disabled, no need to render the template
return source_template
end

-- find compiled templates for this plugin-configuration array
local compiled_templates = template_cache[resource_attributes]
if not compiled_templates then
compiled_templates = {}
-- store it by `config_array` which is part of the plugin `conf` table
-- it will be GC'ed at the same time as `conf` and hence invalidate the
-- compiled templates here as well as the cache-table has weak-keys
template_cache[resource_attributes] = compiled_templates
end

-- Find or compile the specific template
local compiled_template = compiled_templates[source_template]
if not compiled_template then
compiled_template = assert(pl_template.compile(source_template, compile_opts))
compiled_templates[source_template] = compiled_template
end

return compiled_template:render(template_env)
end

local function read_resource_attributes(resource_attributes)
if not resource_attributes then
return EMPTY
end
local __meta_environment = {
__index = function(self, key)
local lazy_loaders = {
headers = function(self)
return get_request_headers() or EMPTY
end,
query_params = function(self)
return get_uri_args() or EMPTY
end,
uri_captures = function(self)
return (ngx.ctx.router_matches or EMPTY).uri_captures or EMPTY
end,
shared = function(self)
return ((kong or EMPTY).ctx or EMPTY).shared or EMPTY
end,
}
local loader = lazy_loaders[key]
if not loader then
if lua_enabled and not sandbox_enabled then
return _G[key]
end
return
end
-- set the result on the table to not load again
local value = loader()
rawset(self, key, value)
return value
end,
__newindex = function(self)
error("This environment is read-only.")
end,
}

local template_env = {}
if lua_enabled and sandbox_enabled then
-- load the sandbox environment to be used to render the template
template_env = deep_copy(sandbox.configuration.environment)
-- here we can optionally add functions to expose to the sandbox, eg:
-- tostring = tostring,
-- because headers may contain array elements such as duplicated headers
-- type is a useful function in these cases. See issue #25.
template_env.type = type
end
setmetatable(template_env, __meta_environment)
for current_name, current_value in pairs(resource_attributes) do
local res, err = param_value(current_value, resource_attributes, template_env)
if err then
return error(" failed to render the template " ..
current_value .. ", error:" .. err)
end

if res then
current_name = current_name:lower()
compiled_resource_attributes[current_name] = res
end
end
end



return {
http_export_request = http_export_request,
get_headers = get_headers,
_log_prefix = _log_prefix,
read_resource_attributes = read_resource_attributes,
compiled_resource_attributes = compiled_resource_attributes,
}

0 comments on commit 77953ff

Please sign in to comment.