mirror of
https://github.com/CommunityOx/ox_lib.git
synced 2026-08-16 22:46:03 +01:00
feat(logger): support grafana loki (#115)
This commit is contained in:
@@ -7,6 +7,15 @@ local function badResponse(endpoint, response)
|
||||
print(('unable to submit logs to %s\n%s'):format(endpoint, json.encode(response, { indent = true })))
|
||||
end
|
||||
|
||||
-- idk where to put this?
|
||||
|
||||
local function split(str,pat)
|
||||
local tbl = {}
|
||||
str:gsub(pat, function(x) tbl[#tbl+1]=x end)
|
||||
return tbl
|
||||
end
|
||||
|
||||
|
||||
local playerData = {}
|
||||
|
||||
AddEventHandler('playerDropped', function()
|
||||
@@ -84,4 +93,133 @@ if service == 'datadog' then
|
||||
end
|
||||
end
|
||||
|
||||
if service == 'loki' then
|
||||
local lokiUser = GetConvar('loki:user', '')
|
||||
local lokiKey = GetConvar('loki:key', '')
|
||||
local lokiEndpoint = GetConvar('loki:endpoint', '')
|
||||
local endpoint = ('https://%s:%s@%s/loki/api/v1/push'):format(lokiUser, lokiKey, lokiEndpoint)
|
||||
local resourceName = GetCurrentResourceName()
|
||||
|
||||
function lib.logger(source, event, message, ...)
|
||||
if not buffer then
|
||||
buffer = {}
|
||||
|
||||
SetTimeout(500, function()
|
||||
-- Strip string keys from buffer
|
||||
local tempBuffer = {}
|
||||
for _,v in pairs(buffer) do
|
||||
tempBuffer[#tempBuffer+1] = v
|
||||
end
|
||||
|
||||
local postBody = json.encode({streams = tempBuffer})
|
||||
PerformHttpRequest(endpoint, function(status, _, _, _)
|
||||
if status ~= 204 then
|
||||
badResponse(endpoint, ("Error Code: %s\n%s"):format(status, postBody))
|
||||
end
|
||||
end, 'POST', postBody, {
|
||||
['Content-Type'] = 'application/json',
|
||||
})
|
||||
|
||||
buffer = nil
|
||||
end)
|
||||
end
|
||||
|
||||
-- Generates a nanosecond unix timestamp
|
||||
local timestamp = ('%s000000000'):format(os.time(os.date('*t')))
|
||||
|
||||
-- Initializes values table with the message
|
||||
local values = {message = message}
|
||||
|
||||
-- Formats arguments into kvp comma seperated string and adds user data
|
||||
local function formatTags(source, tags)
|
||||
if type(source) == 'number' and source > 0 then
|
||||
local data = playerData[source]
|
||||
|
||||
if not data then
|
||||
local _data = {
|
||||
('username:%s'):format(GetPlayerName(source))
|
||||
}
|
||||
|
||||
local num = 1
|
||||
|
||||
---@cast source string
|
||||
for i = 0, GetNumPlayerIdentifiers(source) - 1 do
|
||||
local identifier = GetPlayerIdentifier(source, i)
|
||||
|
||||
if not identifier:find('ip') then
|
||||
num += 1
|
||||
_data[num] = identifier
|
||||
end
|
||||
end
|
||||
|
||||
data = table.concat(_data, ',')
|
||||
playerData[source] = data
|
||||
end
|
||||
|
||||
tags = tags and ('%s,%s'):format(tags, data) or data
|
||||
end
|
||||
|
||||
return tags
|
||||
end
|
||||
|
||||
-- Converts a string of comma seperated kvp string to a table of kvps
|
||||
-- example `discord:blahblah,fivem:blahblah,license:blahblah` -> `{discord="blahblah",fivem="blahblah",license="blahblah"}`
|
||||
local function convertDDTagsToKVP(tags)
|
||||
local tempTable = split(tags, "[^,]*") -- outputs a number index table wth k:v strings as values
|
||||
local bTable = {} -- buffer table
|
||||
|
||||
-- Loop through table and grab only values
|
||||
for _,v in pairs(tempTable) do
|
||||
local tempTable2 = split(v, "[^:]*") -- splits string on ':' character
|
||||
local key = tempTable2[1] -- generate key from first half of string
|
||||
local value = tempTable2[2] -- store the value from the second half of the string
|
||||
bTable[key] = value
|
||||
end
|
||||
return bTable -- Return the new table of kvps
|
||||
end
|
||||
|
||||
-- Format the args into strings
|
||||
local tags = formatTags(source, ... and string.strjoin(',', string.tostringall(...)) or nil)
|
||||
local tagsTable = convertDDTagsToKVP(tags)
|
||||
|
||||
-- Concatenates tags kvp table to the values table
|
||||
for k,v in pairs(tagsTable) do
|
||||
values[k] = v -- Store the tags in the values table ready for logging
|
||||
end
|
||||
|
||||
-- initialise stream payload
|
||||
local payload = {
|
||||
stream = {
|
||||
hostname = resourceName,
|
||||
service = event
|
||||
},
|
||||
values = {
|
||||
{
|
||||
timestamp,
|
||||
json.encode(values)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-- Safety check incase it throws index issue
|
||||
if not buffer then
|
||||
buffer = {}
|
||||
end
|
||||
|
||||
-- Checks if the event exists in the buffer and adds to the values if found
|
||||
-- else initialises the stream
|
||||
if not buffer[event] then
|
||||
buffer[event] = payload
|
||||
else
|
||||
local lastIndex = #buffer[event].values
|
||||
lastIndex += 1
|
||||
|
||||
buffer[event].values[lastIndex] = {
|
||||
timestamp,
|
||||
json.encode(values)
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return lib.logger
|
||||
|
||||
Reference in New Issue
Block a user