Skip to main content

πŸ“Š Global Audit Logging System

XR-MDT includes a robust, centralized logging system designed to track every critical action performed within the tablet. This ensures accountability and provides a clear audit trail for server administrators.

πŸ” How it Works

The logging system operates on a dual-path architecture:
  1. Database Persistence: Every log is stored in the mdt_logs table for long-term historical review.
  2. Real-time Webhooks: Logs are immediately forwarded to configured Discord channels based on the action type and faction, separated into administrative and operational webhooks.
The AddLog function is implemented in server/sv_logs.lua and is exposed as a server-side export.

πŸ› οΈ Usage for Developers

Trigger logs from any server-side script using the AddLog export.

Export Signature

exports['xr-mdt']:AddLog(source, job, action_type, details, debugType)
ParameterTypeRequiredDescription
sourcenumberβœ…Server ID of the player who performed the action.
jobstringβœ…Faction/Job name (e.g., 'police', 'ems', 'doj'). Used for webhook routing.
action_typestringβœ…The title/type of the action (e.g., 'Duty: Started', 'Case Created').
detailsstringβœ…Full descriptive details of the action.
debugTypestring?❌Optional category for webhook routing overrides (e.g., 'Money', 'Items').

What Gets Logged Automatically

The AddLog function automatically resolves and stores:
  • citizenid β€” from Bridge.GetCitizenIdFromObject(player).
  • name β€” "firstname lastname" from Bridge.GetCharInfo(player).
  • rank β€” player’s current job grade name.

Database Schema (mdt_logs)

ColumnTypeDescription
idINTAuto-increment primary key
jobVARCHARFaction name
citizenidVARCHARCitizen unique identifier
nameVARCHARPlayer full name
rankVARCHARJob grade name
action_typeVARCHARAction title
detailsTEXTFull details
created_atTIMESTAMPTimestamp

Examples

-- Log a fine being issued
exports['xr-mdt']:AddLog(
    source,
    "police",
    "Fine Issued",
    string.format("Officer fined %s $%s for: %s", citizenName, amount, reason),
    "Money"
)

-- Log an armory withdrawal
exports['xr-mdt']:AddLog(
    source,
    "police",
    "Armory: Weapon Taken",
    "Took Glock 17 (Serial: #SN-A1B2-C3D4)",
    "Items"
)

-- Log a report creation
exports['xr-mdt']:AddLog(
    source,
    "police",
    "Report: Created",
    "Officer created report #105 for Citizen John Doe"
)

βš™οΈ Configuration

Webhooks are configured in configs/config.webhook.lua.

Webhook Routing Priority

The system routes logs based on the action type and its destination mapping:
  1. Routing Mapping: The action type is resolved against Config.Webhooks.Routing. It returns:
    • "admin" β€” Administrative events (hiring, firing, code changes, deletions).
    • "faction" β€” Operational/faction events (fines, reports, armory).
    • "both" β€” Dispatched to both administrative and operational channels.
    • If not zmapowany, default is "faction" (defined as ["Default"] = "faction").
  2. Override Checks (debugType): Checks if a specific override webhook exists under Config.Webhooks.Types[debugType]. If set, it will override the faction webhook.
  3. Specific Faction Webhooks:
    • Operational logs: routes to Config.Webhooks.Jobs[JOB_UPPERCASE].Faction, then falls back to Config.Webhooks.FactionMain.
    • Administrative logs: routes to Config.Webhooks.Jobs[JOB_UPPERCASE].Admin, then falls back to Config.Webhooks.AdminMain.
  4. Main Webhook: Final fallback to Config.Webhooks.Main if any of the above paths are empty or not configured.

Example Webhook Configuration Structure

Config.Webhooks = {
    -- Primary global fallback webhook if others are not set
    Main = "https://discord.com/api/webhooks/...",

    -- Global fallback webhooks for faction logs and administrative logs
    AdminMain = "https://discord.com/api/webhooks/...",
    FactionMain = "https://discord.com/api/webhooks/...",

    -- Faction-specific webhooks.
    Jobs = {
        LSPD = {
            Faction = "https://discord.com/api/webhooks/...",
            Admin = "https://discord.com/api/webhooks/...",
        },
        EMS = {
            Faction = "",
            Admin = "",
        },
        DOJ = {
            Faction = "",
            Admin = "",
        },
        Business = {
            Faction = "",
            Admin = "",
        },
    },

    -- Webhooks for specific action types (overrides Jobs faction webhooks if set)
    Types = {
        Database = "",
        Money = "",
        Items = "",
        JobChange = "",
        Duty = "",
        Commands = "",
    },

    Routing = {
        -- Fallback routing for any log type not explicitly specified below
        ["Default"] = "faction",

        -- Administrative events (personnel, access, code changes, deletions)
        ["Hired"] = "both",
        ["Fired"] = "both",
        ["Promotion/Demotion"] = "both",
        ["Badge Changed"] = "both",
        ["Suspension"] = "both",
        ["License Issued"] = "both",
        ["Code Changed"] = "both",
        ["Report: Deleted"] = "both",
        ["Armory: Delete Kit"] = "both",
        ["Employee Note Created"] = "both",
        ["Employee Note Deleted"] = "both",

        -- Faction operational events
        ["Armory: Return"] = "faction",
        ["Armory: Collect"] = "faction",
        ["Armory: Save Kit"] = "faction",
        ["Armory: Collect Kit"] = "faction",
        ["Armory: Weapon Taken"] = "faction",
        ["Invoice Issued"] = "faction",
        ["Invoice Paid"] = "faction",
        ["Document: Created"] = "faction",
        ["Document: Updated"] = "faction",
        ["Document: Printed"] = "faction",
        ["Document: Received"] = "faction",
        ["Document: Signed"] = "faction",
        ["Document: Profile Printed"] = "faction",
        ["Duty: Started (CODE 1)"] = "faction",
        ["Duty: Break (CODE 2)"] = "faction",
        ["Duty: Ended (CODE 3)"] = "faction",
        ["Duty: Started"] = "faction",
        ["Duty: Break"] = "faction",
        ["Duty: Ended"] = "faction",
        ["Case Created"] = "faction",
        ["Medical Record Added"] = "faction",
        ["Patient Note Added"] = "faction",
        ["Citizen Lookup"] = "faction",
        ["Citizen Note Added"] = "faction",
        ["Warrant Issued"] = "faction",
        ["Jail Sentence"] = "faction",
        ["Fine Issued"] = "faction",
        ["Incident Report Created"] = "faction",
        ["Report: Created"] = "faction",
        ["Report: Updated"] = "faction",
    },

    -- Appearance & Colors for Discord Embeds
    Categories = {
        LSPD = { Color = 2123412, Title = "πŸš“ LSPD LOGS" },
        EMS = { Color = 15158332, Title = "πŸš‘ EMS LOGS" },
        DOJ = { Color = 15844367, Title = "βš–οΈ DOJ LOGS" },
        Business = { Color = 3066993, Title = "πŸ’Ό BUSINESS LOGS" },
        System = { Color = 8359053, Title = "βš™οΈ SYSTEM LOGS" },
    }
}

πŸ–₯️ NUI Log Viewer

Authorized employees can view logs directly within the MDT:
  • Navigate to the Logs tab in any tablet.
  • Logs are fetched via a server callback returning the last 200 entries for the job.
  • Search by name, action type, or details.
  • Filter results in real-time.
[!NOTE] By default, the Logs tab is visible to higher ranks. Permissions are controlled via logs_view = true in the respective Grades tables.

Β© XR-Core Systems | Professional FiveM Resources