Complete refactor, includes loki logging
This commit is contained in:
parent
7e81d1f0ea
commit
c404d28fb1
@ -1,26 +1,66 @@
|
|||||||
local ClientConfig = require "config.client"
|
--! Import Configs and Modules
|
||||||
local _trace = Citizen.Trace
|
local ClientConfig <const> = require "config.client"
|
||||||
|
local SharedConfig <const> = require "config.shared"
|
||||||
|
local originalTrace <const> = Citizen.Trace
|
||||||
|
local tablePack <const> = table.pack
|
||||||
|
local tableUnpack <const> = table.unpack
|
||||||
|
|
||||||
|
--! Set ERROR_EVENT as local constant
|
||||||
|
local ERROR_EVENT <const> = SharedConfig.ERROR_EVENT
|
||||||
|
|
||||||
function error(...)
|
local keywords = {}
|
||||||
local resource = GetCurrentResourceName()
|
for _, word in ipairs(ClientConfig.errorWords or {}) do
|
||||||
print(string.format("----- RESOURCE ERROR -----"))
|
if type(word) == "string" then
|
||||||
print(...)
|
keywords[#keywords + 1] = string.lower(word)
|
||||||
print(string.format("------ PLEASE REPORT THIS TO STAFF ------"))
|
end
|
||||||
print(string.format("------ IDEALLY WITH CLIPS & SCREENSHOTS OF WHAT YOU'RE DOING ------"))
|
end
|
||||||
TriggerServerEvent("Error:Server:Report", resource, ...)
|
|
||||||
|
local suppressTraceIntercept = false
|
||||||
|
|
||||||
|
local function traceLine(message)
|
||||||
|
originalTrace(tostring(message))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function shouldReportError(message)
|
||||||
|
local lowered = string.lower(message)
|
||||||
|
for i = 1, #keywords do
|
||||||
|
if lowered:find(keywords[i], 1, true) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local function reportClientError(args)
|
||||||
|
local resourceName = GetCurrentResourceName()
|
||||||
|
|
||||||
|
suppressTraceIntercept = true
|
||||||
|
--? Trace/Print the error message to client F8 console.
|
||||||
|
traceLine("----- RESOURCE ERROR -----")
|
||||||
|
for i = 1, args.n do
|
||||||
|
traceLine(args[i])
|
||||||
|
end
|
||||||
|
traceLine("------ PLEASE REPORT THIS TO STAFF ------")
|
||||||
|
traceLine("------ IDEALLY WITH CLIPS & SCREENSHOTS OF WHAT YOU'RE DOING ------")
|
||||||
|
suppressTraceIntercept = false
|
||||||
|
|
||||||
|
TriggerServerEvent(ERROR_EVENT, resourceName, tableUnpack(args, 1, args.n))
|
||||||
end
|
end
|
||||||
|
|
||||||
---@diagnostic disable-next-line: duplicate-set-field
|
---@diagnostic disable-next-line: duplicate-set-field
|
||||||
function Citizen.Trace(...)
|
function Citizen.Trace(...)
|
||||||
if type(...) == "string" then
|
local args = tablePack(...)
|
||||||
local args = string.lower(...)
|
|
||||||
for _, word in ipairs(ClientConfig.errorWords) do
|
if suppressTraceIntercept then
|
||||||
if string.find(args, word) then
|
originalTrace(tableUnpack(args, 1, args.n))
|
||||||
error(...)
|
return
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
_trace(...)
|
|
||||||
|
local message = args[1]
|
||||||
|
if type(message) == "string" and shouldReportError(message) then
|
||||||
|
reportClientError(args)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
originalTrace(tableUnpack(args, 1, args.n))
|
||||||
end
|
end
|
||||||
@ -2,8 +2,8 @@ return {
|
|||||||
errorWords = {
|
errorWords = {
|
||||||
"failure",
|
"failure",
|
||||||
"error",
|
"error",
|
||||||
"not",
|
|
||||||
"failed",
|
"failed",
|
||||||
|
"not",
|
||||||
"not safe",
|
"not safe",
|
||||||
"invalid",
|
"invalid",
|
||||||
"cannot",
|
"cannot",
|
||||||
@ -13,6 +13,23 @@ return {
|
|||||||
"attempt",
|
"attempt",
|
||||||
"traceback",
|
"traceback",
|
||||||
"stack",
|
"stack",
|
||||||
"function"
|
"function",
|
||||||
}
|
--? Possible false positives below, use with caution
|
||||||
|
"nil",
|
||||||
|
"no such",
|
||||||
|
"bad argument",
|
||||||
|
"expected",
|
||||||
|
"script",
|
||||||
|
"exception",
|
||||||
|
"panic",
|
||||||
|
"crash",
|
||||||
|
"unhandled",
|
||||||
|
"timeout",
|
||||||
|
"denied",
|
||||||
|
"refused",
|
||||||
|
"missing",
|
||||||
|
"unknown",
|
||||||
|
"syntax",
|
||||||
|
"warning"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -1,3 +1,16 @@
|
|||||||
return {
|
return {
|
||||||
Webhook = 'https://discord.com/api/'
|
Logging = {
|
||||||
|
--! Discord is not a logging service, please switch to loki/grafana, datadog, fivemanage, etc for production.
|
||||||
|
backend = 'discord', -- options: 'discord', 'loki'
|
||||||
|
discord = {
|
||||||
|
webhook = 'https://discord.com/api/'
|
||||||
|
},
|
||||||
|
--! Loki configuration uses ox_lib logger and it's configuration for endpoint/auth (https://coxdocs.dev/ox_lib/Modules/Logger/Server#grafana-loki)
|
||||||
|
loki = {
|
||||||
|
enabled = false,
|
||||||
|
labels = {
|
||||||
|
app = GetCurrentResourceName()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
3
config/shared.lua
Normal file
3
config/shared.lua
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
return {
|
||||||
|
ERROR_EVENT = "Error:Server:Report",
|
||||||
|
}
|
||||||
@ -1,41 +1,91 @@
|
|||||||
---@diagnostic disable: param-type-mismatch
|
--! Import Configs and Modules
|
||||||
local ServerConfig = require "config.server"
|
local ServerConfig <const> = require "config.server"
|
||||||
local ServerFunctions = require "server.sv_functions"
|
local SharedConfig <const> = require "config.shared"
|
||||||
local QBX = exports.qbx_core
|
local ServerFunctions <const> = require "server.sv_functions"
|
||||||
local LOGGER = require '@qbx_core.modules.logger'
|
local DiscordLogger <const> = require "server.sv_logger"
|
||||||
|
local LokiLogger <const> = require "server.sv_loki"
|
||||||
|
|
||||||
RegisterServerEvent("Error:Server:Report", function(resource, ...)
|
--! Set ERROR_EVENT as local constant
|
||||||
|
local ERROR_EVENT <const> = SharedConfig.ERROR_EVENT
|
||||||
|
|
||||||
|
--! Logging setup
|
||||||
|
local LoggingConfig <const> = ServerConfig.Logging or {}
|
||||||
|
|
||||||
|
local function selectLogger()
|
||||||
|
local backend = type(LoggingConfig.backend) == "string" and LoggingConfig.backend:lower() or "discord"
|
||||||
|
|
||||||
|
if backend == "loki" then
|
||||||
|
local config = LoggingConfig.loki or {}
|
||||||
|
if config.enabled then
|
||||||
|
return function(entry)
|
||||||
|
LokiLogger.log(config, entry)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local config = LoggingConfig.discord or {}
|
||||||
|
return function(entry)
|
||||||
|
DiscordLogger.log(config, entry)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local ActiveLogger <const> = selectLogger()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
RegisterNetEvent(ERROR_EVENT, function(resource, ...)
|
||||||
local src = source
|
local src = source
|
||||||
local errorMessage = ...
|
local args = table.pack(...)
|
||||||
errorMessage = errorMessage:gsub("%^%d+", "")
|
local playerData = ServerFunctions.GetPlayerData(src)
|
||||||
|
local citizenId = playerData and playerData.citizenid or "UNKNOWN"
|
||||||
local userName = GetPlayerName(src)
|
|
||||||
local steamIdent = GetPlayerIdentifierByType(src, 'steam')
|
|
||||||
local steamID = (steamIdent and steamIdent:gsub('steam:',"")) or 'ERROR: STEAM-NOT-FOUND'
|
|
||||||
local discordIdent = GetPlayerIdentifierByType(src, 'discord')
|
|
||||||
local discordID = (discordIdent and discordIdent:gsub('discord:',"")) or 'ERROR: DISCORD-NOT-FOUND'
|
|
||||||
local citizenid = QBX:GetPlayer(src).PlayerData.citizenid
|
|
||||||
local ped = GetPlayerPed(src)
|
local ped = GetPlayerPed(src)
|
||||||
local x, y, z = table.unpack(GetEntityCoords(ped))
|
|
||||||
local heading = GetEntityHeading(ped)
|
local x, y, z, heading = 0.0, 0.0, 0.0, 0.0
|
||||||
LOGGER.log({
|
if ped and ped ~= 0 then
|
||||||
source = citizenid,
|
x, y, z = table.unpack(GetEntityCoords(ped))
|
||||||
webhook = ServerConfig.Webhook,
|
heading = GetEntityHeading(ped)
|
||||||
event = 'Error:Server:Report',
|
end
|
||||||
color = 'red',
|
|
||||||
message = string.format("**__Script Error In %s__**", resource) ..'\n'..
|
local errorMessage = ServerFunctions.SanitizeErrorArgs(args)
|
||||||
'---------------------------------------\n'..
|
if errorMessage == "" then
|
||||||
'**__Triggered By:__** \n'..
|
errorMessage = "No error message supplied."
|
||||||
'**Username:** '.. userName ..'\n'..
|
end
|
||||||
'**Steam Account:** '.. 'https://steamcommunity.com/profiles/'.. tonumber(steamID, 16) ..'\n'..
|
|
||||||
'**Discord:** <@'.. discordID ..'> \n'..
|
local steamProfile = ServerFunctions.SteamProfile(GetPlayerIdentifierByType(src, 'steam'))
|
||||||
'**Source(ID):** '.. src ..'\n'..
|
local discordMention = ServerFunctions.DiscordMention(GetPlayerIdentifierByType(src, 'discord'))
|
||||||
'**CitizenID:** '.. citizenid ..'\n'..
|
local status = ServerFunctions.DeadOrLastStand(src)
|
||||||
'**Coords:** '.. ServerFunctions.FormatCoords(x,y,z,heading) ..'\n'..
|
local identifiers = ServerFunctions.FetchIdentifiers(src)
|
||||||
'**Status:** '.. ServerFunctions.DeadOrLastStand(src) ..'\n'..
|
local coords = ServerFunctions.FormatCoords(x, y, z, heading)
|
||||||
'**Identifiers:** '.. ServerFunctions.FetchIdentifiers(src) .. '\n'..
|
|
||||||
'---------------------------------------\n'..
|
local message = table.concat({
|
||||||
errorMessage..
|
string.format("**__Script Error In %s__**", resource or "unknown"),
|
||||||
'---------------------------------------\n'
|
"---------------------------------------",
|
||||||
|
"**__Triggered By:__**",
|
||||||
|
string.format("**Username:** %s", GetPlayerName(src) or "UNKNOWN"),
|
||||||
|
string.format("**Steam Account:** %s", steamProfile),
|
||||||
|
string.format("**Discord:** %s", discordMention),
|
||||||
|
string.format("**Source(ID):** %s", src),
|
||||||
|
string.format("**CitizenID:** %s", citizenId),
|
||||||
|
string.format("**Coords:** %s", coords),
|
||||||
|
string.format("**Status:** %s", status),
|
||||||
|
string.format("**Identifiers:** %s", identifiers),
|
||||||
|
"---------------------------------------",
|
||||||
|
errorMessage,
|
||||||
|
"---------------------------------------"
|
||||||
|
}, "\n")
|
||||||
|
|
||||||
|
ActiveLogger({
|
||||||
|
source = citizenId,
|
||||||
|
event = ERROR_EVENT,
|
||||||
|
color = 16711680,
|
||||||
|
message = message,
|
||||||
|
level = "error",
|
||||||
|
--? Grafana Tags
|
||||||
|
tags = {
|
||||||
|
resource = resource or "unknown",
|
||||||
|
citizenid = citizenId,
|
||||||
|
source_id = tostring(src),
|
||||||
|
username = GetPlayerName(src) or "UNKNOWN",
|
||||||
|
},
|
||||||
})
|
})
|
||||||
end)
|
end)
|
||||||
@ -1,37 +1,96 @@
|
|||||||
local QBX = exports.qbx_core
|
local QBX = exports.qbx_core
|
||||||
|
|
||||||
local function FormatCoords(x,y,z,heading)
|
local Functions = {}
|
||||||
local formattedX = math.floor(x * 100) / 100
|
|
||||||
local formattedY = math.floor(y * 100) / 100
|
|
||||||
local formattedZ = math.floor(z * 100) / 100
|
|
||||||
local formattedHeading = math.floor(heading * 100) / 100
|
|
||||||
local formattedString = formattedX .. ', '.. formattedY .. ', '.. formattedZ .. ', H: '..formattedHeading
|
|
||||||
return formattedString
|
|
||||||
end
|
|
||||||
|
|
||||||
local function FetchIdentifiers (source)
|
function Functions.FormatCoords(x, y, z, heading)
|
||||||
|
if not x or not y or not z then
|
||||||
local identifiersString = ''
|
return "Coords unavailable"
|
||||||
for _, identifier in ipairs(GetPlayerIdentifiers(source)) do
|
|
||||||
identifiersString = identifiersString .. '\n'..identifier
|
|
||||||
end
|
end
|
||||||
identifiersString = identifiersString .. '\n'
|
|
||||||
|
|
||||||
return identifiersString
|
return string.format("%.2f, %.2f, %.2f, H: %.2f", x, y, z, heading or 0.0)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function DeadOrLastStand (source)
|
function Functions.FetchIdentifiers(source)
|
||||||
local _source = source
|
if type(source) ~= "number" then
|
||||||
local Player = QBX:GetPlayer(_source)
|
return "\nIdentifiers unavailable"
|
||||||
local dead = Player.PlayerData.metadata.isDead
|
end
|
||||||
local laststand = Player.PlayerData.metadata.inLaststand
|
|
||||||
if dead then return "Player Dead" end
|
local identifiers = GetPlayerIdentifiers(source)
|
||||||
if laststand then return "Player in Laststand" end
|
if type(identifiers) ~= "table" or #identifiers == 0 then
|
||||||
|
return "\nIdentifiers unavailable"
|
||||||
|
end
|
||||||
|
|
||||||
|
return "\n" .. table.concat(identifiers, "\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
function Functions.DeadOrLastStand(source)
|
||||||
|
local player = QBX:GetPlayer(source)
|
||||||
|
if not player or not player.PlayerData then
|
||||||
|
return "Player state unavailable"
|
||||||
|
end
|
||||||
|
|
||||||
|
local metadata = player.PlayerData.metadata or {}
|
||||||
|
if metadata.isDead then
|
||||||
|
return "Player Dead"
|
||||||
|
end
|
||||||
|
|
||||||
|
if metadata.inLaststand then
|
||||||
|
return "Player In Laststand"
|
||||||
|
end
|
||||||
|
|
||||||
return "Player Alive"
|
return "Player Alive"
|
||||||
end
|
end
|
||||||
|
|
||||||
return {
|
function Functions.SanitizeErrorArgs(args)
|
||||||
FormatCoords = FormatCoords,
|
if type(args) ~= "table" then
|
||||||
FetchIdentifiers = FetchIdentifiers,
|
return ""
|
||||||
DeadOrLastStand = DeadOrLastStand
|
end
|
||||||
}
|
|
||||||
|
local count = args.n or #args
|
||||||
|
local messages = {}
|
||||||
|
|
||||||
|
for index = 1, count do
|
||||||
|
local value = args[index]
|
||||||
|
if type(value) == "string" then
|
||||||
|
messages[#messages + 1] = value:gsub("%^%d+", "")
|
||||||
|
else
|
||||||
|
messages[#messages + 1] = tostring(value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return table.concat(messages, "\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
function Functions.SteamProfile(identifier)
|
||||||
|
if type(identifier) ~= "string" then
|
||||||
|
return "ERROR: STEAM-NOT-FOUND"
|
||||||
|
end
|
||||||
|
|
||||||
|
local hexId = identifier:gsub("steam:", "")
|
||||||
|
local decimalId = tonumber(hexId, 16)
|
||||||
|
if not decimalId then
|
||||||
|
return "ERROR: STEAM-CONVERSION-FAILED"
|
||||||
|
end
|
||||||
|
|
||||||
|
return string.format("https://steamcommunity.com/profiles/%d", decimalId)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Functions.DiscordMention(identifier)
|
||||||
|
if type(identifier) ~= "string" then
|
||||||
|
return "ERROR: DISCORD-NOT-FOUND"
|
||||||
|
end
|
||||||
|
|
||||||
|
local discordId = identifier:gsub("discord:", "")
|
||||||
|
return string.format("<@%s>", discordId)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Functions.GetPlayerData(sourceId)
|
||||||
|
local player = QBX:GetPlayer(sourceId)
|
||||||
|
if not player or not player.PlayerData then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return player.PlayerData
|
||||||
|
end
|
||||||
|
|
||||||
|
return Functions
|
||||||
105
server/sv_logger.lua
Normal file
105
server/sv_logger.lua
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
---@diagnostic disable: undefined-global
|
||||||
|
|
||||||
|
local Logger = {}
|
||||||
|
|
||||||
|
local queue = {}
|
||||||
|
local processing = false
|
||||||
|
local rateLimitMs = 2000
|
||||||
|
|
||||||
|
|
||||||
|
local function jsonEncode(payload)
|
||||||
|
local ok, encoded = pcall(json.encode, payload)
|
||||||
|
if ok and encoded then
|
||||||
|
return encoded
|
||||||
|
end
|
||||||
|
|
||||||
|
return "{}"
|
||||||
|
end
|
||||||
|
|
||||||
|
local function processQueue()
|
||||||
|
local entry = table.remove(queue, 1)
|
||||||
|
if not entry then
|
||||||
|
processing = false
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(entry.webhook) ~= "string" or entry.webhook == "" then
|
||||||
|
processQueue()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
processing = true
|
||||||
|
|
||||||
|
local body = jsonEncode(entry.payload)
|
||||||
|
|
||||||
|
PerformHttpRequest(entry.webhook, function()
|
||||||
|
if #queue == 0 then
|
||||||
|
processing = false
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
SetTimeout(rateLimitMs, processQueue)
|
||||||
|
end, 'POST', body, {
|
||||||
|
['Content-Type'] = 'application/json'
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
function Logger.log(config, entry)
|
||||||
|
if type(entry) ~= "table" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
config = config or {}
|
||||||
|
|
||||||
|
if type(config.rateLimitMs) == "number" and config.rateLimitMs > 0 then
|
||||||
|
rateLimitMs = math.floor(config.rateLimitMs)
|
||||||
|
end
|
||||||
|
|
||||||
|
local webhook = entry.webhook or config.webhook
|
||||||
|
if type(webhook) ~= "string" or webhook == "" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local embed = entry.embed or {
|
||||||
|
title = entry.event or "Server Log",
|
||||||
|
description = entry.message or "",
|
||||||
|
color = entry.color,
|
||||||
|
timestamp = os.date('!%Y-%m-%dT%H:%M:%SZ')
|
||||||
|
}
|
||||||
|
|
||||||
|
if entry.source then
|
||||||
|
embed.footer = embed.footer or {
|
||||||
|
text = string.format("Source: %s", entry.source)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local payload = {
|
||||||
|
username = entry.username or config.username or "Logger",
|
||||||
|
avatar_url = entry.avatarUrl or config.avatarUrl,
|
||||||
|
content = entry.content,
|
||||||
|
embeds = entry.embeds or { embed }
|
||||||
|
}
|
||||||
|
|
||||||
|
queue[#queue + 1] = {
|
||||||
|
webhook = webhook,
|
||||||
|
payload = payload
|
||||||
|
}
|
||||||
|
|
||||||
|
if not processing then
|
||||||
|
processQueue()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Logger.setRateLimit(ms)
|
||||||
|
if type(ms) ~= "number" or ms <= 0 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
rateLimitMs = math.floor(ms)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Logger.queueSize()
|
||||||
|
return #queue
|
||||||
|
end
|
||||||
|
|
||||||
|
return Logger
|
||||||
80
server/sv_loki.lua
Normal file
80
server/sv_loki.lua
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
---@diagnostic disable: undefined-global
|
||||||
|
|
||||||
|
local LokiLogger = {}
|
||||||
|
|
||||||
|
|
||||||
|
--? Shotout ox_lib for the following code.
|
||||||
|
-- ? Function to format tags into a string suitable for Loki logging
|
||||||
|
-- ? Intakes a table of key-value pairs and returns a formatted loki label string i.e key:value,key2:value2
|
||||||
|
local function formatTags(tagData)
|
||||||
|
if type(tagData) ~= "table" then
|
||||||
|
return ""
|
||||||
|
end
|
||||||
|
|
||||||
|
local keys = {}
|
||||||
|
for key in pairs(tagData) do
|
||||||
|
keys[#keys + 1] = key
|
||||||
|
end
|
||||||
|
|
||||||
|
table.sort(keys)
|
||||||
|
|
||||||
|
local segments = {}
|
||||||
|
|
||||||
|
for index = 1, #keys do
|
||||||
|
local key = keys[index]
|
||||||
|
local value = tagData[key]
|
||||||
|
if value ~= nil then
|
||||||
|
local specifier = type(value) == "number" and "%d" or "%s"
|
||||||
|
segments[#segments + 1] = string.format("%s:" .. specifier, key, value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
table.sort(segments)
|
||||||
|
|
||||||
|
return table.concat(segments, ",")
|
||||||
|
end
|
||||||
|
|
||||||
|
local function mergeTags(base, extra)
|
||||||
|
local merged = {}
|
||||||
|
|
||||||
|
if type(base) == "table" then
|
||||||
|
for key, value in pairs(base) do
|
||||||
|
merged[key] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(extra) == "table" then
|
||||||
|
for key, value in pairs(extra) do
|
||||||
|
merged[key] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return merged
|
||||||
|
end
|
||||||
|
|
||||||
|
function LokiLogger.log(config, entry)
|
||||||
|
if type(config) ~= "table" or config.enabled == false then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(entry) ~= "table" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local tags = mergeTags(config.labels, entry.tags)
|
||||||
|
tags.level = entry.level or tags.level or "error"
|
||||||
|
|
||||||
|
local formattedTags = formatTags(tags)
|
||||||
|
|
||||||
|
local source = entry.source or config.source or "hof-errors"
|
||||||
|
local event = entry.event or "ErrorEvent"
|
||||||
|
local message = entry.message or ""
|
||||||
|
|
||||||
|
if formattedTags == "" then
|
||||||
|
formattedTags = "level:error"
|
||||||
|
end
|
||||||
|
|
||||||
|
lib.logger(source, event, message, formattedTags)
|
||||||
|
end
|
||||||
|
|
||||||
|
return LokiLogger
|
||||||
Loading…
Reference in New Issue
Block a user