MAJOR rework

- improves a lot on the Go side including better config and logging libraries (including log rotation), better internal package distribution, and new a3go functionality to make data transfer more performant
- SQF side preprocessing of capture data is now minimal - arrays in hashmap format are sent directly to the extension and parsed there to minimize game impact
- CBA custom events are implemented in a better fashion
- README update
- license change
- with performance improvements, the deep customization of integrated metric gathering is removed in return to a single refreshRateMs, defining the interval at which core metrics are captured
- peeled back the list of core metrics to the core information used in troubleshooting and benchmarking
This commit is contained in:
2023-10-10 00:44:50 -07:00
parent cf45d6b263
commit dc822c4c93
75 changed files with 4335 additions and 3347 deletions

1
addons/main/$PBOPREFIX$ Normal file
View File

@@ -0,0 +1 @@
x\ifxmetrics\addons\main

27
addons/main/config.cpp Normal file
View File

@@ -0,0 +1,27 @@
#include "script_component.hpp"
class CfgPatches {
class ADDON {
units[] = {};
weapons[] = {};
requiredVersion = 2.10;
requiredAddons[] = {};
author[] = {"IndigoFox"};
authorUrl = "https://github.com/indig0fox/IFXMetrics";
};
};
class CfgFunctions {
class ADDON {
class functions {
class postInit {
file = QPATHTOF(DOUBLES(fnc,postInit).sqf);
postInit = 1;
};
PATHTO_FNC(callbackHandler);
PATHTO_FNC(captureLoop);
PATHTO_FNC(log);
PATHTO_FNC(onExtLoad);
};
};
};

View File

@@ -0,0 +1,37 @@
#include "script_component.hpp"
params ["_name", "_function", "_data"];
if !(_name == GVARMAIN(extensionName)) exitWith {};
// Validate data param
if (isNil "_data") then {_data = ""};
if (_data isEqualTo "") exitWith {
[
"WARN",
format ["Callback empty data: %1", _function]
] call FUNC(log);
false;
};
private _dataArr = parseSimpleArray _data;
if (
(count _dataArr isEqualTo 0) &&
(_function isNotEqualTo ":LOG:")
) exitWith {
[
"WARN",
format ["Callback invalid data for function %1: %2", _function, _data]
] call FUNC(log);
false;
};
switch (_function) do {
case ":LOG:": {
diag_log formatText ["[%1] %2", GVARMAIN(logPrefix), _dataArr#0];
};
default {
["INFO", _data] call FUNC(log);
};
};

View File

@@ -0,0 +1,65 @@
#include "script_component.hpp"
/*
Parameters
_function The function you wish to execute. <CODE>
_delay The amount of time in seconds between executions, 0 for every frame. (optional, default: 0) <NUMBER>
_args Parameters passed to the function executing. (optional) <ANY>
_start Function that is executed when the PFH is added. (optional) <CODE>
_end Function that is executed when the PFH is removed. (optional) <CODE>
_runCondition Condition that has to return true for the PFH to be executed. (optional, default {true}) <CODE>
_exitCondition Condition that has to return true to delete the PFH object. (optional, default {false}) <CODE>
_private List of local variables that are serialized between executions. (optional) <CODE>
*/
GVARMAIN(captureLoop) = [
{
private _startTime = diag_tickTime;
GVARMAIN(extensionName) callExtension [
":INFLUX:WRITE:",
[
call EFUNC(capture,server_performance),
call EFUNC(capture,running_scripts),
call EFUNC(capture,server_time),
call EFUNC(capture,weather)
]
];
// entity_count returns an array of hashMap
{
GVARMAIN(extensionName) callExtension [
":INFLUX:WRITE:",
[_x]
];
} forEach (call EFUNC(capture,entity_count));
{
GVARMAIN(extensionName) callExtension [
":INFLUX:WRITE:",
[_x]
];
} forEach (call EFUNC(capture,player_performance));
["DEBUG", format[
"Processed primary data loop in %1 ms",
(diag_tickTime - _startTime) * 1000
]] call FUNC(log);
},
(GVARMAIN(refreshRateMs) / 1000), // delay in seconds,
[], // args
{
// start
["DEBUG", "Starting server performance capture"] call FUNC(log);
},
{
// end
["DEBUG", "Stopping server performance capture"] call FUNC(log);
},
{
// runCondition
true
},
{
// exitCondition
false
},
[] // private
] call CBA_fnc_createPerFrameHandlerObject;

27
addons/main/fnc_log.sqf Normal file
View File

@@ -0,0 +1,27 @@
#include "script_component.hpp"
if (!isServer) exitWith {};
if (typeName _this != "ARRAY") exitWith {
diag_log format ["[%1]: Invalid log params: %2", GVARMAIN(logPrefix), _this];
};
params [
["_level", "INFO", [""]],
["_text", "", [""]]
];
if (
_level == "DEBUG" &&
!GVARMAIN(debug)
) exitWith {};
if (_text isEqualTo "") exitWith {};
diag_log formatText [
"[%1] %2: %3",
GVARMAIN(logPrefix),
_level,
_text
];

View File

@@ -0,0 +1,121 @@
#include "script_component.hpp"
// wait until READY
waitUntil {
sleep 2;
(GVARMAIN(extensionName) callExtension ":READY:") isEqualTo "true";
};
// load settings from extension
private _settings = GVARMAIN(extensionName) callExtension ":SETTINGS:";
if (isNil "_settings") exitWith {
diag_log formatText[
"[%1] (ERROR): IFXMetrics extension settings not loaded. IFXMetrics will not be available.",
GVARMAIN(logPrefix)
];
};
_settingsArr = parseSimpleArray _settings;
GVARMAIN(enabled) = _settingsArr select 0;
GVARMAIN(debug) = _settingsArr select 1;
GVARMAIN(refreshRateMs) = _settingsArr select 2;
if (!GVARMAIN(enabled)) exitWith {
diag_log formatText[
"[%1] (WARN): IFXMetrics config entry influxdb.enabled is false. IFXMetrics will not be available.",
GVARMAIN(logPrefix)
];
};
// get custom CBA handlers
private _handlersFromExtension = GVARMAIN(extensionName) callExtension ":CUSTOM:CBA:EVENTS:";
_handlersFromExtension = parseSimpleArray _handlersFromExtension;
if (count _handlersFromExtension isEqualTo 0) then {
diag_log formatText[
"[%1] (WARN): IFXMetrics custom CBA handlers failed to parse. Custom events will not be logged.",
GVARMAIN(logPrefix)
];
} else {
diag_log formatText[
"[%1] (INFO): IFXMetrics custom CBA handlers loaded: %2",
GVARMAIN(logPrefix),
_handlersFromExtension
];
// data is a keyed HashMap
GVARMAIN(cbaHandlers) = createHashMapFromArray _handlersFromExtension;
};
GVARMAIN(standardTags) = [
["profile", profileName],
["world", worldName],
["server", serverName]
];
// Connect to InfluxDB
GVARMAIN(extensionName) callExtension ":INFLUX:CONNECT:";
waitUntil {
sleep 2;
(GVARMAIN(extensionName) callExtension ":INFLUX:CONNECTED:") isEqualTo "true";
};
// Send initial mission info
GVARMAIN(extensionName) callExtension [
":INFLUX:WRITE:",
[(call EFUNC(capture,running_mission))]
];
// Set up CBA event listeners
/* example usage:
["milsimServerEfficiency", [
["tags", [ // tags must be string values!
["missionPhase", "init"]
]],
["fields", [ // fields can be any type
["value", 0.5]
["numberOfShinyObjects", 3]
]]
]] call CBA_fnc_serverEvent;
*/
{
_key = _x;
_hash = _y;
[
_hash get "eventName", // event name
{ // function
_thisArgs params [
"_enabled",
"_bucket",
"_measurement",
"_description"
];
private _data = [
["bucket", _bucket],
["measurement", _measurement],
["tags", GVARMAIN(standardTags)],
["fields", _this]
];
GVARMAIN(extensionName) callExtension [
":INFLUX:WRITE:",
[_data]
];
},
[ // args
_hash get "enabled",
_hash get "bucket",
_hash get "measurement",
_hash get "description"
]
] call CBA_fnc_addEventHandlerArgs;
} forEach GVARMAIN(cbaHandlers);
// wait five seconds, then start the loop
[{call FUNC(captureLoop);}, 5] call CBA_fnc_waitAndExecute;

View File

@@ -0,0 +1,159 @@
#include "script_component.hpp"
// if (!isServer) exitWith {};
if (is3DEN || !isMultiplayer) exitWith {};
if (!isServer && hasInterface) exitWith {};
GVARMAIN(cbaLoaded) = isClass(configFile >> "CfgPatches" >> "cba_main");
GVARMAIN(logPrefix) = QUOTE(PREFIX_BEAUTIFIED);
GVARMAIN(extensionName) = QUOTE(PREFIX);
// Create listener for async callbacks
addMissionEventHandler [
"ExtensionCallback",
FUNC(callbackHandler)
];
// start loading the extension
[(parseSimpleArray (GVARMAIN(extensionName) callExtension ":START:")) select 0] remoteExec ["systemChat", 0];
// load settings from extension
private _settings = GVARMAIN(extensionName) callExtension ":SETTINGS:";
if (isNil "_settings") exitWith {
diag_log formatText[
"[%1] (ERROR): IFXMetrics extension settings not loaded. IFXMetrics will not be available.",
GVARMAIN(logPrefix)
];
};
_settingsArr = parseSimpleArray _settings;
GVARMAIN(enabled) = _settingsArr select 0;
GVARMAIN(debug) = _settingsArr select 1;
GVARMAIN(refreshRateMs) = _settingsArr select 2;
if (!GVARMAIN(enabled)) exitWith {
diag_log formatText[
"[%1] (WARN): IFXMetrics config entry influxdb.enabled is false. IFXMetrics will not be available.",
GVARMAIN(logPrefix)
];
};
// get custom CBA handlers
private _handlersFromExtension = GVARMAIN(extensionName) callExtension ":CUSTOM:CBA:EVENTS:";
_handlersFromExtension = parseSimpleArray _handlersFromExtension;
if (count _handlersFromExtension isEqualTo 0) then {
diag_log formatText[
"[%1] (WARN): IFXMetrics custom CBA handlers failed to parse. Custom events will not be logged.",
GVARMAIN(logPrefix)
];
} else {
diag_log formatText[
"[%1] (INFO): IFXMetrics custom CBA handlers loaded: %2",
GVARMAIN(logPrefix),
_handlersFromExtension
];
// data is a keyed HashMap
GVARMAIN(cbaHandlers) = createHashMapFromArray _handlersFromExtension;
};
GVARMAIN(standardTags) = [
["profile", profileName],
["world", worldName],
["server", serverName]
];
// Connect to InfluxDB
private _connectResult = parseSimpleArray (GVARMAIN(extensionName) callExtension ":INFLUX:CONNECT:");
if (count _connectResult isEqualTo 0) exitWith {
diag_log formatText[
"[%1] (ERROR): IFXMetrics failed to connect to InfluxDB. IFXMetrics will not be available.",
GVARMAIN(logPrefix)
];
};
if (_connectResult select 0 isNotEqualTo "OK") exitWith {
diag_log formatText[
"[%1] (ERROR): IFXMetrics failed to connect to InfluxDB. IFXMetrics will not be available.",
GVARMAIN(logPrefix)
];
};
[format ["%1", (_connectResult select 1)]] remoteExec ["systemChat", 0];
// Send initial mission info
[
"DEBUG",
str (call EFUNC(capture,running_mission))
] call FUNC(log);
GVARMAIN(extensionName) callExtension [
":INFLUX:WRITE:",
[(call EFUNC(capture,running_mission))]
];
// Set up CBA event listeners
/* example usage:
["milsimServerEfficiency", [
[ // tags in hash format. must be string values!
["missionPhase", "init"]
],
[ // fields in hash format. can be any type
["value", 0.5]
["numberOfShinyObjects", 3]
]
]] call CBA_fnc_serverEvent;
*/
{
_key = _x;
_hash = createHashMapFromArray _y;
[
_hash get "eventName", // event name
{ // function
if (count _this isNotEqualTo 2) exitWith {
diag_log formatText[
"[%1] (ERROR): IFXMetrics CBA handler %2 received invalid number of arguments. Expected 2, got %3.",
GVARMAIN(logPrefix),
_thisType,
count _this
];
};
_thisArgs params [
"_enabled",
"_bucket",
"_measurement",
"_description"
];
private _data = [
["bucket", _bucket],
["measurement", _measurement],
["tags", _this#0],
["fields", _this#1]
];
GVARMAIN(extensionName) callExtension [
":INFLUX:WRITE:",
[_data]
];
},
[ // args
_key,
_hash get "enabled",
_hash get "bucket",
_hash get "measurement",
_hash get "description"
]
] call CBA_fnc_addEventHandlerArgs;
} forEach GVARMAIN(cbaHandlers);
// wait five seconds, then start the loop
call FUNC(captureLoop);

View File

@@ -0,0 +1,4 @@
#define COMPONENT main
#define COMPONENT_BEAUTIFIED Main
#include "\x\ifxmetrics\addons\main\script_mod.hpp"

View File

@@ -0,0 +1,8 @@
#include "script_version.hpp"
#define MAINPREFIX x
#define PREFIX ifxmetrics
#define PREFIX_BEAUTIFIED IFXMetrics
#define SUBPREFIX addons
#include "\x\cba\addons\main\script_macros_common.hpp"

View File

@@ -0,0 +1,8 @@
#define MAJOR 2
#define MINOR 0
#define PATCH 0
#define BUILD 20231009
#define VERSION 2.0
#define VERSION_STR MAJOR##.##MINOR##.##PATCH##.##BUILD
#define VERSION_AR MAJOR,MINOR,PATCH,BUILD