fix RPT spam, allow re-init of capture, change to spawn loop, fix setvar

- add readme too
This commit is contained in:
2023-04-18 17:14:04 -07:00
parent a209843caa
commit b7b9c89dc2
11 changed files with 3476 additions and 202 deletions

View File

@@ -1,68 +0,0 @@
Jarvis — Today at 8:23 PM
@EagleTrooper let's use this instead
to save blowing up the recruiters notifications
on recruitment
EagleTrooper — Today at 8:23 PM
Yeah keep the recruitment clean. I understand Im a recruiter / NCO / admin for the community in involved in.
Alright,
I am working on developing a dashboard that gives historical stats with our server as well as our missions that run Performance / FPS / Memory Usage / etc. During my research i came across your groups Repo and it appears that you are doing something very similar to what I wanted to do. I did read through just to get an idea unfortunately there was some gaps in the implementation and i was hoping to speak to someone about how it was done with your group as well as if they would be so kind as to provide pointers and insight so that I may implement something very similar.
Jarvis — Today at 8:26 PM
Do you know how to setup a Grafana/InfluxDB stack?
EagleTrooper — Today at 8:27 PM
Already have it setup (Background in IT and Cloud Network Engineering).
Sweetwater.I — Today at 8:27 PM
u can see our dashboard here if u wanna see what cavmetrics extension is outputting.
https://metrics.7cav.us/d/NED-gV3Mz/arma3?orgId=1&refresh=10s
Jarvis — Today at 8:28 PM
It's basically just arma -> influx -> grafana
Sweetwater.I — Today at 8:28 PM
but yeah, influxdb+grafana is what we do
Jarvis — Today at 8:28 PM
The cavmetrics addon you see is the arma extension
EagleTrooper — Today at 8:28 PM
Yep understood the workflow and that was very clear. I believe the piece im missing or dont understand is the dll creation / implementation
Jarvis — Today at 8:28 PM
If you know influx/grafana, you're 2/3rds of the way there
Just git clone the cavmetrics repo
EagleTrooper — Today at 8:29 PM
I believe it was the Arma-Influx?
That was the piece that i was not clear on. Unless i was misunderstanding it still required a compiled DLL
Jarvis — Today at 8:29 PM
the a3_influx dll is inside of the git repo
so == a DLL but linux
we run our servers on linux
EagleTrooper — Today at 8:30 PM
Unfortuantly it looks like its a .so
Sweetwater.I — Today at 8:30 PM
use the arma-influx repo to build the extension
EagleTrooper — Today at 8:30 PM
Yeah we have windows :/
Sweetwater.I — Today at 8:30 PM
cavmetrics is what we called the packaged extension.
on the repo
Jarvis — Today at 8:30 PM
https://github.com/7Cav/Arma-Influx
GitHub
GitHub - 7Cav/Arma-Influx: Arma Extension for sending metrics to In...
Arma Extension for sending metrics to InfluxDB. Contribute to 7Cav/Arma-Influx development by creating an account on GitHub.
Just compile that for dll
it's an arma ext written in go
Liber.N — Today at 8:31 PM
Cavmetrics is an arma addon thats calls to arma influx extension
Jarvis — Today at 8:31 PM
you'll need to change the makefile on that repo tho
EagleTrooper — Today at 8:32 PM
does it need to be labeld armago? or a3influx?
If that is what your referring to then it appears i do have an understanding of it and something else is missing outside of my imediate understanding and may not have anything you can provide input on. I will review the implementation again and see if I can get any further.
I do appreciate the input and help.
Jarvis — Today at 8:35 PM
no probs, it's a long road but found it worth it in the end
https://metrics.7cav.us/
Specifically - https://metrics.7cav.us/d/NED-gV3Mz/arma3?orgId=1&refresh=10s
EagleTrooper — Today at 8:36 PM
I will take a look! Keep on Keeping on and again I appreciate the friendly response!
Jarvis — Today at 8:36 PM
You'll need to update these for your influx db host - https://github.com/7Cav/cav_metrics/blob/dev/%40CavMetrics/addons/CavMetrics/functions/fn_send.sqf#L9-L12

View File

@@ -31,7 +31,6 @@ try {
switch (_function) do { switch (_function) do {
case "deinitExtension": { case "deinitExtension": {
diag_log format ["RangerMetrics: deinitExtension: %1", _response];
// Our first call is deinitExtension. When we received a single "true" value, we can then run init processes for the extension connections. // Our first call is deinitExtension. When we received a single "true" value, we can then run init processes for the extension connections.
if ((_response select 0) isEqualTo true) then { if ((_response select 0) isEqualTo true) then {
"RangerMetrics" callExtension "initExtension"; "RangerMetrics" callExtension "initExtension";
@@ -46,6 +45,39 @@ switch (_function) do {
// Load settings // Load settings
[_function, _response] call RangerMetrics_callback_fnc_loadSettings; [_function, _response] call RangerMetrics_callback_fnc_loadSettings;
}; };
case "extensionReady": {
// deinitialize existing captures
if (!isNil "RangerMetrics_allMEH") then {
{
private _handle = missionNamespace getVariable _x;
if (isNil "_handle") then {continue};
private _EHName = (_x splitString "_") select 2;
removeMissionEventHandler [_EHName, _handle];
missionNamespace setVariable [_x, nil];
} forEach RangerMetrics_allMEH;
};
if (!isNil "RangerMetrics_allCBA") then {
{
private _handle = missionNamespace getVariable _x;
if (isNil "_handle") then {continue};
private _EHName = (_x splitString "_") select 2;
[_EHName, _handle] call CBA_fnc_removeEventHandler;
missionNamespace setVariable [_x, nil];
} forEach RangerMetrics_allCBA;
};
if (!isNil "RangerMetrics_allServerPoll") then {
{
private _handle = missionNamespace getVariable _x;
if (isNil "_handle") then {continue};
terminate _handle;
missionNamespace setVariable [_x, nil];
} forEach RangerMetrics_allServerPoll;
};
call RangerMetrics_fnc_initCapture;
};
default { default {
_response call RangerMetrics_fnc_log; _response call RangerMetrics_fnc_log;
}; };

View File

@@ -16,10 +16,12 @@ if (_function isEqualTo "loadSettingsJSON") exitWith {
] call RangerMetrics_fnc_log; ] call RangerMetrics_fnc_log;
if (isServer) then { if (isServer) then {
RangerMetrics_serverProfileName = profileName; missionNamespace setVariable [
publicVariable "RangerMetrics_serverProfileName"; "RangerMetrics_serverProfileName",
profileName,
true
];
}; };
call RangerMetrics_fnc_initCapture;
}; };
switch (_data select 0) do { switch (_data select 0) do {

View File

@@ -69,7 +69,7 @@
false; false;
} else { } else {
missionNamespace setVariable [ missionNamespace setVariable [
("RangerMetrics" + "_CBAEH_" + _settingName), ("RangerMetrics" + "_CBAEH_" + _handleName),
_handle _handle
]; ];
true; true;
@@ -81,24 +81,24 @@
private _meh = allVariables missionNamespace select { RangerMetrics_allMEH = allVariables missionNamespace select {
_x find (toLower "RangerMetrics_MEH_") == 0 _x find (toLower "RangerMetrics_MEH_") == 0
}; };
private _cba = allVariables missionNamespace select { RangerMetrics_allCBA = allVariables missionNamespace select {
_x find (toLower "RangerMetrics_CBAEH_") == 0 _x find (toLower "RangerMetrics_CBAEH_") == 0
}; };
private _serverPoll = allVariables missionNamespace select { RangerMetrics_allServerPoll = allVariables missionNamespace select {
_x find (toLower "RangerMetrics_serverPoll_") == 0 _x find (toLower "RangerMetrics_serverPoll_") == 0
}; };
[format ["Mission event handlers: %1", _meh]] call RangerMetrics_fnc_log; [format ["Mission event handlers: %1", RangerMetrics_allMEH]] call RangerMetrics_fnc_log;
[format ["CBA event handlers: %1", _cba]] call RangerMetrics_fnc_log; [format ["CBA event handlers: %1", RangerMetrics_allCBA]] call RangerMetrics_fnc_log;
[format ["Server poll handles: %1", _serverPoll]] call RangerMetrics_fnc_log; [format ["Server poll handles: %1", RangerMetrics_allServerPoll]] call RangerMetrics_fnc_log;
RangerMetrics_initialized = true; missionNamespace setVariable ["RangerMetrics_initialized", true, true];
RangerMetrics_run = true; missionNamespace setVariable ["RangerMetrics_run", true, true];
["RangerMetrics_run", true] remoteExecCall ["setVariable", 0];
// start sending // start sending

View File

@@ -1,4 +1,4 @@
params [["_text","Log text invalid"], ["_type","INFO"]]; params [["_text","Log text invalid"], ["_logType","DEBUG"]];
if (typeName _this != "ARRAY") exitWith { if (typeName _this != "ARRAY") exitWith {
diag_log format ["RangerMetrics: Invalid log params: %1", _this]; diag_log format ["RangerMetrics: Invalid log params: %1", _this];
@@ -6,18 +6,19 @@ if (typeName _this != "ARRAY") exitWith {
if (typeName _text != "STRING") exitWith { if (typeName _text != "STRING") exitWith {
diag_log format ["RangerMetrics: Invalid log text: %1", _this]; diag_log format ["RangerMetrics: Invalid log text: %1", _this];
}; };
if (typeName _type != "STRING") exitWith { if (typeName _logType != "STRING") exitWith {
diag_log format ["RangerMetrics: Invalid log type: %1", _this]; diag_log format ["RangerMetrics: Invalid log type: %1", _this];
}; };
if (_type isEqualTo "DEBUG") then { if (
if (!RangerMetrics_debug) exitWith {}; _logType == "DEBUG" &&
}; !(missionNamespace getVariable ["RangerMetrics_debug", false])
) exitWith {};
private _textFormatted = format [ private _textFormatted = format [
"[%1] %2: %3", "[%1] %2: %3",
RangerMetrics_logPrefix, RangerMetrics_logPrefix,
_type, _logType,
_text]; _text];
if(isServer) then { if(isServer) then {

View File

@@ -20,7 +20,7 @@ RangerMetrics_recordingSettings = createHashMap;
[format ["Instance name: %1", profileName]] call RangerMetrics_fnc_log; [format ["Instance name: %1", profileName]] call RangerMetrics_fnc_log;
[format ["CBA detected: %1", RangerMetrics_cbaPresent]] call RangerMetrics_fnc_log; [format ["CBA detected: %1", RangerMetrics_cbaPresent]] call RangerMetrics_fnc_log;
["Initializing v0.1"] call RangerMetrics_fnc_log; ["Initializing v0.0.2"] call RangerMetrics_fnc_log;
// Create listener - extension calls are async, so we need to listen for the response // Create listener - extension calls are async, so we need to listen for the response
addMissionEventHandler [ addMissionEventHandler [

View File

@@ -21,7 +21,17 @@ if (_tagContext find "world" > -1) then {
_tags pushBack ["string", "world", toLower worldName]; _tags pushBack ["string", "world", toLower worldName];
}; };
if (_tagContext find "server" > -1) then { if (_tagContext find "server" > -1) then {
_tags pushBack ["string", "connectedServer", RangerMetrics_serverProfileName]; private _serverProfile = missionNamespace getVariable [
"RangerMetrics_serverProfileName",
""
];
if (_serverProfile isNotEqualTo "") then {
_tags pushBack [
"string",
"connectedServer",
_serverProfile
];
};
}; };
private _outTags = _tags apply { private _outTags = _tags apply {

View File

@@ -12,16 +12,41 @@ if (_interval == 0) exitWith {
[_code] call CBA_fnc_execNextFrame; [_code] call CBA_fnc_execNextFrame;
}; };
// run a constant scheduled loop
private _handle = [{ private _runnerVar = "RangerMetrics" + "_serverPollRunner_" + _refName;
params ["_args", "_idPFH"]; missionNamespace setVariable [_runnerVar, scriptNull];
_args params ["_refName", "_code"]; private _spawnParams = [_refName, _code, _interval, _runnerVar];
private _handle = _spawnParams spawn {
[_code] call CBA_fnc_execNextFrame; params ["_refName", "_code", "_interval", "_runnerVar"];
while {true} do {
}, _interval, _this] call CBA_fnc_addPerFrameHandler; if (scriptDone (
missionNamespace getVariable _runnerVar
)) then {
private _handle = [] spawn _code;
missionNamespace setVariable [
_runnerVar,
_handle
];
};
// sleep _interval;
sleep 2;
};
};
missionNamespace setVariable [ missionNamespace setVariable [
"RangerMetrics" + "_serverPoll_" + _refName, "RangerMetrics" + "_serverPoll_" + _refName,
_handle _handle
]; ];
// USE PFH
// private _handle = [{
// params ["_args", "_idPFH"];
// _args params ["_refName", "_code"];
// [_code] call CBA_fnc_execNextFrame;
// }, _interval, _this] call CBA_fnc_addPerFrameHandler;
// missionNamespace setVariable [
// "RangerMetrics" + "_serverPoll_" + _refName,
// _handle
// ];

View File

@@ -5,18 +5,19 @@
"token": "XXXXXXXXXXXXXXXXXXXXXXXXXXXX_AUTH_TOKEN_XXXXXXXXXXXXXXXXXXXXXXXXXXX", "token": "XXXXXXXXXXXXXXXXXXXXXXXXXXXX_AUTH_TOKEN_XXXXXXXXXXXXXXXXXXXXXXXXXXX",
"org": "ORG_NAME" "org": "ORG_NAME"
}, },
"arma3": {
"refreshRateMs": 1000,
"debug": false
},
"timescaledb": { "timescaledb": {
"enabled": false, "enabled": false,
"connectionUrl": "postgresql://user:pass@host.com:5432", "connectionUrl": "postgresql://user:pass@host.com:5432",
"databaseName": "ranger_metrics", "databaseName": "ranger_metrics",
"description": "TimescaleDB is an open-source time-series database built on Postgres. The extension will connect to the maintenance database (postgres) first and create the database it will control with desired name, then shift connections to the newly created database to create the schema and conduct write operations." "description": "TimescaleDB is an open-source time-series database built on Postgres. The extension will connect to the maintenance database (postgres) first and create the database it will control with desired name, then shift connections to the newly created database to create the schema and conduct write operations."
}, },
"arma3": {
"refreshRateMs": 1000,
"debug": false
},
"recordingSettings": { "recordingSettings": {
"serverPerformance": { "serverPolls": [
{
"name": "serverPerformance", "name": "serverPerformance",
"enabled": true, "enabled": true,
"serverOnly": false, "serverOnly": false,
@@ -25,7 +26,7 @@
"measurement": "fps", "measurement": "fps",
"description": "SERVER/HC POLL. Tracks average and minimum FPS. Polled on server (and HC) at specified interval." "description": "SERVER/HC POLL. Tracks average and minimum FPS. Polled on server (and HC) at specified interval."
}, },
"runningScripts": { {
"name": "runningScripts", "name": "runningScripts",
"enabled": true, "enabled": true,
"serverOnly": false, "serverOnly": false,
@@ -34,7 +35,7 @@
"measurement": "running_scripts", "measurement": "running_scripts",
"description": "SERVER/HC POLL. Tracks the number of scripts running on the server (and HC). Polled on specified interval." "description": "SERVER/HC POLL. Tracks the number of scripts running on the server (and HC). Polled on specified interval."
}, },
"entityCount": { {
"name": "entityCount", "name": "entityCount",
"enabled": true, "enabled": true,
"serverOnly": false, "serverOnly": false,
@@ -43,16 +44,16 @@
"measurement": "n/a", "measurement": "n/a",
"description": "SERVER/HC POLL. Tracks the number of entities on the server. Polled on server (and HC) at specified interval as well as on EntityCreated/EntityRespawned/EntityKilled events. Measurement is static, [entities_local, entities_remote, entities_global]." "description": "SERVER/HC POLL. Tracks the number of entities on the server. Polled on server (and HC) at specified interval as well as on EntityCreated/EntityRespawned/EntityKilled events. Measurement is static, [entities_local, entities_remote, entities_global]."
}, },
"playerPerformance": { {
"name": "playerPerformance", "name": "playerPerformance",
"enabled": true, "enabled": true,
"serverOnly": true, "serverOnly": true,
"intervalMs": 3000, "intervalMs": 1000,
"bucket": "player_performance", "bucket": "player_performance",
"measurement": "network", "measurement": "network",
"description": "SERVER POLL. User network performance. Tracks average ping, average bandwidth, and desync. Polled by the server at specified interval. Not tracked from headless client." "description": "SERVER POLL. User network performance. Tracks average ping, average bandwidth, and desync. Polled by the server at specified interval. Not tracked from headless client."
}, },
"serverTime": { {
"name": "serverTime", "name": "serverTime",
"enabled": true, "enabled": true,
"serverOnly": true, "serverOnly": true,
@@ -61,25 +62,25 @@
"measurement": "server_time", "measurement": "server_time",
"description": "SERVER POLL. Tracks the server time. Always server-only. Runs at specified interval." "description": "SERVER POLL. Tracks the server time. Always server-only. Runs at specified interval."
}, },
"weather": { {
"name": "weather", "name": "weather",
"enabled": true, "enabled": true,
"serverOnly": true, "serverOnly": true,
"intervalMs": 300000, "intervalMs": 120000,
"bucket": "mission_data", "bucket": "mission_data",
"measurement": "weather", "measurement": "weather",
"description": "SERVER POLL. Tracks the weather on the server. Always server-only. Runs at specified interval." "description": "SERVER POLL. Tracks the weather on the server. Always server-only. Runs at specified interval."
}, },
"viewDistance": { {
"name": "viewDistance", "name": "viewDistance",
"enabled": true, "enabled": true,
"serverOnly": true, "serverOnly": true,
"intervalMs": 300000, "intervalMs": 120000,
"bucket": "mission_data", "bucket": "mission_data",
"measurement": "view_distance", "measurement": "view_distance",
"description": "SERVER POSTINIT, MPEnded. Tracks the view distance on the server. Always server-only. Runs at specified interval." "description": "SERVER POSTINIT, MPEnded. Tracks the view distance on the server. Always server-only. Runs at specified interval."
}, },
"runningMission": { {
"name": "runningMission", "name": "runningMission",
"enabled": true, "enabled": true,
"serverOnly": true, "serverOnly": true,
@@ -87,34 +88,14 @@
"bucket": "mission_data", "bucket": "mission_data",
"measurement": "running_mission", "measurement": "running_mission",
"description": "SERVER POSTINIT, MPEnded. Tracks the name of the mission running on the server. Always server-only. Runs at the start (and tries at end) of the mission." "description": "SERVER POSTINIT, MPEnded. Tracks the name of the mission running on the server. Always server-only. Runs at the start (and tries at end) of the mission."
}, }
"playerStatus": { ],
"name": "playerStatus", "CBAEventHandlers": [
"enabled": true, {
"serverOnly": true,
"intervalMs": 0,
"bucket": "player_data",
"measurement": "client_state",
"description": "EH. Tracks players' clientstateNumber, adminState, and profileName on connect/disconnect to server and to mission, as well as when these values change."
},
"playerIdentity": {
"name": "playerIdentity",
"enabled": true,
"serverOnly": true,
"intervalMs": 0,
"bucket": "player_data",
"measurement": "player_identity",
"description": "EH. Tracks players' identity on connect/disconnect to server and to mission, as well as when these values change."
},
"CBAEventHandlers": {
"milsimServerEfficiency": {
"name": "milsimServerEfficiency", "name": "milsimServerEfficiency",
"enabled": true, "enabled": true,
"serverOnly": true,
"bucket": "server_performance",
"measurement": "milsim_server_efficiency",
"description": "EVENTHANDLER. Tracks the efficiency of the server." "description": "EVENTHANDLER. Tracks the efficiency of the server."
} }
} ]
} }
} }

View File

@@ -1,3 +1,57 @@
# RangerMetrics based off of CAV-Metrics # RangerMetrics - Arma3 InfluxDB Metrics
Ranger Metrics is used to submit information from the Arma3 Instance to the Influx Database. This uses the ArmaInflux Version complied to a DLL to communicate. Ranger Metrics is used to submit information from the Arma3 Instance to the Influx Database. This uses the ArmaInflux Version complied to a DLL to communicate.
---
## Setup
### Settings.json
Configure the options in settings.json.
As of v0.0.2, metrics are captured on a recurring loop in the scheduled environment with a two second pause to allow time. Whether to use CBA Per Frame Handlers that run metric collection less often and in the unscheduled environment has yet to be decided on, as it does lead to longer intervals that are more difficult to graph precisely.
### InfluxDB
InfluxDB is a time series database. It is used to store data points with a timestamp.
#### Required Buckets
- mission_data
- player_data
- player_performance
- server_events
- server_performance
### Grafana
Grafana is a dashboarding tool. It is used to display the data from InfluxDB. Import the dashboard from the json file in the root of this addon and set up your datasources appropriately.
---
## Usage
### Ingame
#### Toggle Capture On/Off
Running the following commands in Server Exec will toggle the capture on or off for the server and any Headless Clients. Capture loops will still occur but will exit almost immediately.
Change the last parameter to false to ONLY target the server when run as Server Exec.
*This may not apply to raw Event Handler data which goes under `server_events`*
```sqf
// ON
missionNamespace setVariable ["RangerMetrics_run", true, true];
// OFF
missionNamespace setVariable ["RangerMetrics_run", false, true];
```
#### Reload Settings.json and recreate all capture loops
To reload everything while in a game, run `"RangerMetrics" callExtension "deinitExtension";` in Global Exec. This will disconnect any database connections and reset state. Running it Global Exec will cause any client with the addon to run it, which includes Headless Clients.
When the extension is finished, it will notify Arma via a callback. The addon will then __automatically__ run `"RangerMetrics" callExtension "initExtension";` to reinitialize the extension, to include fetching the new settings, tearing down existing captures, and re-setting up captures with the new settings.

3237
grafana_dashboard.json Normal file

File diff suppressed because it is too large Load Diff