204 lines
4.8 KiB
Lua
204 lines
4.8 KiB
Lua
-- This opentelemetry library was inspired by https://github.com/yangxikun/opentelemetry-lua,
|
|
-- but simplified and adapted to the Prosody ecosystem.
|
|
|
|
local json = require "util.json"
|
|
local array = require "util.array"
|
|
local time = require "util.time"
|
|
local random = require "util.random"
|
|
local hex = require "util.hex"
|
|
|
|
local m = {}
|
|
|
|
--------------------------------------------------
|
|
-- Util --
|
|
--------------------------------------------------
|
|
|
|
local function random_hex(n)
|
|
return hex.to(random.bytes(n))
|
|
end
|
|
local function to_nanoseconds(t)
|
|
return tostring(math.floor(t * 1e9))
|
|
end
|
|
|
|
|
|
--------------------------------------------------
|
|
-- Attribute --
|
|
--------------------------------------------------
|
|
|
|
local Attribute = {}
|
|
m.Attribute = Attribute
|
|
|
|
function Attribute.string(value)
|
|
return {
|
|
string_value = value
|
|
}
|
|
end
|
|
|
|
--------------------------------------------------
|
|
-- Span --
|
|
--------------------------------------------------
|
|
|
|
local SpanStatus = {
|
|
UNSET = 0,
|
|
OK = 1,
|
|
ERROR = 2,
|
|
}
|
|
|
|
local SpanKind = {
|
|
UNSPECIFIED = 0,
|
|
INTERNAL = 1,
|
|
SERVER = 2,
|
|
CLIENT = 3,
|
|
PRODUCER = 4,
|
|
CONSUMER = 5,
|
|
}
|
|
|
|
local Span = {}
|
|
Span.__index = Span
|
|
m.Span = Span
|
|
|
|
function Span.new(name, tracer, opts)
|
|
local self = {
|
|
name = name,
|
|
tracer = tracer,
|
|
parent_context = opts.context,
|
|
trace_id = opts.context and opts.context.trace_id or random_hex(16),
|
|
span_id = random_hex(8),
|
|
start_time = time.now(),
|
|
attributes = array {},
|
|
}
|
|
|
|
return setmetatable(self, Span)
|
|
end
|
|
|
|
function Span:set_attribute(k, v)
|
|
table.insert(self.attributes, { key = k, value = v })
|
|
return self
|
|
end
|
|
|
|
function Span:end_span()
|
|
self.end_time = time.now()
|
|
self.tracer.processor:add(self)
|
|
end
|
|
|
|
function Span:encode()
|
|
return {
|
|
trace_id = self.trace_id,
|
|
span_id = self.span_id,
|
|
parent_span_id = self.parent_context and self.parent_context.span_id,
|
|
name = self.name,
|
|
start_time_unix_nano = to_nanoseconds(self.start_time),
|
|
end_time_unix_nano = to_nanoseconds(self.end_time),
|
|
attributes = self.attributes,
|
|
kind = self.kind or SpanKind.INTERNAL,
|
|
status = {
|
|
code = self.status or SpanStatus.OK
|
|
}
|
|
}
|
|
end
|
|
|
|
--------------------------------------------------
|
|
-- Processor --
|
|
--------------------------------------------------
|
|
|
|
local Processor = {}
|
|
Processor.__index = Processor
|
|
m.Processor = Processor
|
|
|
|
function Processor.new(exporter)
|
|
local self = {
|
|
exporter = exporter
|
|
}
|
|
return setmetatable(self, Processor)
|
|
end
|
|
|
|
function Processor:add(span)
|
|
self.exporter:export({ span })
|
|
end
|
|
|
|
--------------------------------------------------
|
|
-- Exporter --
|
|
--------------------------------------------------
|
|
|
|
local Exporter = {}
|
|
Exporter.__index = Exporter
|
|
m.Exporter = Exporter
|
|
|
|
function Exporter.new(request_fn, endpoint)
|
|
local self = {
|
|
request_fn = request_fn,
|
|
endpoint = endpoint,
|
|
}
|
|
return setmetatable(self, Exporter)
|
|
end
|
|
|
|
function Exporter:export(spans)
|
|
local body = {
|
|
resource_spans = {
|
|
{
|
|
resource = spans[1].tracer.resource,
|
|
scope_spans = {
|
|
{
|
|
scope = {
|
|
name = spans[1].tracer.name,
|
|
},
|
|
spans = {}
|
|
},
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
for _, span in ipairs(spans) do
|
|
table.insert(
|
|
body.resource_spans[1].scope_spans[1].spans,
|
|
span:encode()
|
|
)
|
|
end
|
|
|
|
self.request_fn(
|
|
self.endpoint, {
|
|
method = "POST",
|
|
headers = {
|
|
["Content-Type"] = "application/json",
|
|
},
|
|
body = json.encode(body),
|
|
},
|
|
function(body, code, response)
|
|
if code ~= 200 then
|
|
module:log("error", json.encode(body))
|
|
end
|
|
end
|
|
)
|
|
end
|
|
|
|
--------------------------------------------------
|
|
-- Tracer --
|
|
--------------------------------------------------
|
|
|
|
local Tracer = {}
|
|
Tracer.__index = Tracer
|
|
m.Tracer = Tracer
|
|
|
|
function Tracer.new(processor, service, name)
|
|
return setmetatable({
|
|
processor = processor,
|
|
resource = {
|
|
attributes = {
|
|
{
|
|
key = "service.name",
|
|
value = Attribute.string(service)
|
|
}
|
|
}
|
|
},
|
|
name = name,
|
|
}, Tracer)
|
|
end
|
|
|
|
function Tracer:start_span(name, context)
|
|
return Span.new(name, self, { context = context })
|
|
end
|
|
|
|
return m
|