forked from yangxikun/opentelemetry-lua
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3 from plantfansam/tracestate
Tracestate
- Loading branch information
Showing
6 changed files
with
217 additions
and
71 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
local _M = { | ||
MAX_KEY_LEN = 256, | ||
MAX_VAL_LEN = 256, | ||
MAX_ENTRIES = 32, | ||
} | ||
|
||
local mt = { | ||
__index = _M | ||
} | ||
|
||
local function validate_member_key(key) | ||
if #key > _M.MAX_KEY_LEN then | ||
return nil | ||
end | ||
|
||
local valid_key = string.match(key, [[^%s*([a-z][_0-9a-z%-*/]*)$]]) | ||
if not valid_key then | ||
local tenant_id, system_id = string.match(key, [[^%s*([a-z0-9][_0-9a-z%-*/]*)@([a-z][_0-9a-z%-*/]*)$]]) | ||
if not tenant_id or not system_id then | ||
return nil | ||
end | ||
if #tenant_id > 241 or #system_id > 14 then | ||
return nil | ||
end | ||
return tenant_id .. "@" .. system_id | ||
end | ||
|
||
return valid_key | ||
end | ||
|
||
local function validate_member_value(value) | ||
if #value > _M.MAX_VAL_LEN then | ||
return nil | ||
end | ||
return string.match(value, | ||
[[^([ !"#$%%&'()*+%-./0-9:;<>?@A-Z[\%]^_`a-z{|}~]*[!"#$%%&'()*+%-./0-9:;<>?@A-Z[\%]^_`a-z{|}~])%s*$]]) | ||
end | ||
|
||
function _M.new(values) | ||
local self = { values = values } | ||
return setmetatable(self, mt) | ||
end | ||
|
||
-------------------------------------------------------------------------------- | ||
-- Parse tracestate header into a tracestate | ||
-- | ||
-- @return tracestate | ||
-------------------------------------------------------------------------------- | ||
function _M.parse_tracestate(tracestate) | ||
if not tracestate then | ||
return _M.new({}) | ||
end | ||
if type(tracestate) == "string" then | ||
tracestate = { tracestate } | ||
end | ||
|
||
local new_tracestate = {} | ||
local members_count = 0 | ||
local error_message = "failed to parse tracestate" | ||
for _, item in ipairs(tracestate) do | ||
for member in string.gmatch(item, "([^,]+)") do | ||
if member ~= "" then | ||
local start_pos, end_pos = string.find(member, "=", 1, true) | ||
if not start_pos or start_pos == 1 then | ||
ngx.log(ngx.WARN, error_message) | ||
return _M.new({}) | ||
end | ||
local key = validate_member_key(string.sub(member, 1, start_pos - 1)) | ||
if not key then | ||
ngx.log(ngx.WARN, error_message) | ||
return _M.new({}) | ||
end | ||
local value = validate_member_value(string.sub(member, end_pos + 1)) | ||
if not value then | ||
ngx.log(ngx.WARN, error_message) | ||
return _M.new({}) | ||
end | ||
members_count = members_count + 1 | ||
if members_count > _M.MAX_ENTRIES then | ||
ngx.log(ngx.WARN, error_message) | ||
return _M.new({}) | ||
end | ||
table.insert(new_tracestate, {key, value}) | ||
end | ||
end | ||
end | ||
|
||
return _M.new(new_tracestate) | ||
end | ||
|
||
-------------------------------------------------------------------------------- | ||
-- Set the key value pair for the tracestate | ||
-- | ||
-- @return tracestate | ||
-------------------------------------------------------------------------------- | ||
function _M.set(self, key, value) | ||
if not validate_member_key(key) then | ||
return self | ||
end | ||
if not validate_member_value(value) then | ||
return self | ||
end | ||
self:del(key) | ||
if #self.values >= _M.MAX_ENTRIES then | ||
table.remove(self.values) | ||
ngx.log(ngx.WARN, "tracestate max values exceeded, removing rightmost entry") | ||
end | ||
table.insert(self.values, 1, {key, value}) | ||
return self | ||
end | ||
|
||
-------------------------------------------------------------------------------- | ||
-- Get the value for the current key from the tracestate | ||
-- | ||
-- @return value | ||
-------------------------------------------------------------------------------- | ||
function _M.get(self, key) | ||
for _, item in ipairs(self.values) do | ||
local ckey = item[1] | ||
if ckey == key then | ||
return item[2] | ||
end | ||
end | ||
return "" | ||
end | ||
|
||
-------------------------------------------------------------------------------- | ||
-- Delete the key from the tracestate | ||
-- | ||
-- @return tracestate | ||
-------------------------------------------------------------------------------- | ||
function _M.del(self, key) | ||
local index = 0 | ||
for i, item in ipairs(self.values) do | ||
local ckey = item[1] | ||
if ckey == key then | ||
index = i | ||
break | ||
end | ||
end | ||
if index ~= 0 then | ||
table.remove(self.values, index) | ||
end | ||
return self | ||
end | ||
|
||
-------------------------------------------------------------------------------- | ||
-- Return the header value of the tracestate | ||
-- | ||
-- @return string | ||
-------------------------------------------------------------------------------- | ||
function _M.as_string(self) | ||
local output = {} | ||
for _, item in ipairs(self.values) do | ||
table.insert(output, item[1] .. "=" .. item[2]) | ||
end | ||
return table.concat(output, ",") | ||
end | ||
|
||
return _M |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
local tracestate = require("opentelemetry.trace.tracestate") | ||
|
||
describe("is_valid", function() | ||
it("parse, get works", function() | ||
local ts = tracestate.parse_tracestate("foo=bar,baz=lehrman") | ||
assert.is_true(#ts.values == 2) | ||
assert.is_true(ts:get("foo") == "bar") | ||
assert.is_true(ts:get("baz") == "lehrman") | ||
end) | ||
it("set works", function() | ||
local ts = tracestate.parse_tracestate("foo=bar,baz=lehrman") | ||
assert.is_true(#ts.values == 2) | ||
ts:set("foo", "fun") | ||
assert.is_true(#ts.values == 2) | ||
assert.is_true(ts:get("foo") == "fun") | ||
ts:set("family", "values") | ||
assert.is_true(#ts.values == 3) | ||
assert.is_true(ts:get("family") == "values") | ||
-- setting an invalid value leaves the old kv pair | ||
ts:set("foo", "v=l") | ||
assert.is_true(ts:get("foo") == "fun") | ||
end) | ||
it("del works", function() | ||
local ts = tracestate.parse_tracestate("foo=bar,baz=lehrman") | ||
ts:del("foo") | ||
assert.is_true(#ts.values == 1) | ||
assert.is_true(ts:get("foo") == "") | ||
end) | ||
it("as_string works", function() | ||
local ts = tracestate.parse_tracestate("foo=bar,baz=lehrman") | ||
assert.is_true(ts:as_string() == "foo=bar,baz=lehrman") | ||
ts:set("bing", "bong") | ||
assert.is_true(ts:as_string() == "bing=bong,foo=bar,baz=lehrman") | ||
end) | ||
it("max len is respected", function() | ||
local ts = tracestate.parse_tracestate("") | ||
for i=1,tracestate.MAX_ENTRIES,1 do | ||
ts:set("a" .. tostring(i), "b" .. tostring(i)) | ||
end | ||
assert.is_true(#ts.values == tracestate.MAX_ENTRIES) | ||
ts:set("one", "more") | ||
assert.is_true(#ts.values == tracestate.MAX_ENTRIES) | ||
-- First elem added is the first one lost when we add over max entries | ||
assert.is_true(ts:get("a1") == "") | ||
assert.is_true(ts:get("one") == "more") | ||
-- Newest elem is prepended | ||
assert.is_true(ts.values[1][1] == "one") | ||
|
||
end) | ||
end) |