diff --git a/code/__DEFINES/MC.dm b/code/__DEFINES/MC.dm index e2068cecb89..26631eab0e4 100644 --- a/code/__DEFINES/MC.dm +++ b/code/__DEFINES/MC.dm @@ -66,6 +66,7 @@ /datum/controller/subsystem/##X/New(){\ NEW_SS_GLOBAL(SS##X);\ PreInit();\ + ss_id=#X;\ }\ /datum/controller/subsystem/##X @@ -73,5 +74,6 @@ /datum/controller/subsystem/processing/##X/New(){\ NEW_SS_GLOBAL(SS##X);\ PreInit();\ + ss_id="processing_[#X]";\ }\ /datum/controller/subsystem/processing/##X diff --git a/code/controllers/subsystem.dm b/code/controllers/subsystem.dm index f187fb7a9b9..4bb18701110 100644 --- a/code/controllers/subsystem.dm +++ b/code/controllers/subsystem.dm @@ -2,6 +2,8 @@ /datum/controller/subsystem // Metadata; you should define these. name = "fire codertrain" //name of the subsystem + /// Subsystem ID. Used for when we need a technical name for the SS + var/ss_id = "fire_codertrain_again" var/init_order = INIT_ORDER_DEFAULT //order of initialization. Higher numbers are initialized first, lower numbers later. Use defines in __DEFINES/subsystems.dm for easy understanding of order. var/wait = 20 //time to wait (in deciseconds) between each call to fire(). Must be a positive integer. var/priority = FIRE_PRIORITY_DEFAULT //When mutiple subsystems need to run in the same tick, higher priority subsystems will run first and be given a higher share of the tick before MC_TICK_CHECK triggers a sleep @@ -227,3 +229,14 @@ if("queued_priority") //editing this breaks things. return 0 . = ..() + +// Returns the metrics for the subsystem. +// This can be overriden on subtypes for variables that could affect tick usage +// Example: ATs on SSair +/datum/controller/subsystem/proc/get_metrics() + SHOULD_CALL_PARENT(TRUE) + var/list/out = list() + out["cost"] = cost + out["tick_usage"] = tick_usage + out["custom"] = list() // Override as needed on child + return out diff --git a/code/controllers/subsystem/air.dm b/code/controllers/subsystem/air.dm index 324df11b8d1..9cb8371d816 100644 --- a/code/controllers/subsystem/air.dm +++ b/code/controllers/subsystem/air.dm @@ -66,6 +66,11 @@ SUBSYSTEM_DEF(air) msg += "AT/MS:[round((cost ? active_turfs.len/cost : 0),0.1)]" ..(msg) +/datum/controller/subsystem/air/get_metrics() + . = ..() + var/list/cust = list() + cust["active_turfs"] = length(active_turfs) + .["custom"] = cust /datum/controller/subsystem/air/Initialize(timeofday) setup_overlays() // Assign icons and such for gas-turf-overlays diff --git a/code/controllers/subsystem/metrics.dm b/code/controllers/subsystem/metrics.dm new file mode 100644 index 00000000000..f2a7824dcb8 --- /dev/null +++ b/code/controllers/subsystem/metrics.dm @@ -0,0 +1,30 @@ +SUBSYSTEM_DEF(metrics) + name = "Metrics" + flags = SS_NO_INIT + wait = 1 MINUTES + offline_implications = "Server metrics will no longer be ingested into monitoring systems. No immediate action is needed." + runlevels = RUNLEVEL_LOBBY | RUNLEVEL_SETUP | RUNLEVEL_GAME | RUNLEVEL_POSTGAME // ALL THE LEVELS + /// The real time of day the server started. Used to calculate time drift + var/world_init_time = 0 // Not set in here. Set in world/New() + +/datum/controller/subsystem/metrics/fire(resumed) + // AA TODO + return + +/datum/controller/subsystem/metrics/proc/get_metrics_json() + var/list/out = list() + out["cpu"] = world.cpu + // out["maptick"] = world.map_cpu // TODO: 514 + out["elapsed_processed"] = world.time + out["elapsed_real"] = (REALTIMEOFDAY - world_init_time) + out["client_count"] = length(GLOB.clients) + out["round_id"] = text2num(GLOB.round_id) // This is so we can filter the metrics by a single round ID + + // Funnel in all SS metrics + var/list/ss_data = list() + for(var/datum/controller/subsystem/SS in Master.subsystems) + ss_data[SS.ss_id] = SS.get_metrics() + + out["subsystems"] = ss_data + // And send it all + return json_encode(out) diff --git a/code/controllers/subsystem/mobs.dm b/code/controllers/subsystem/mobs.dm index e024a1f3d4e..def8a807853 100644 --- a/code/controllers/subsystem/mobs.dm +++ b/code/controllers/subsystem/mobs.dm @@ -12,6 +12,12 @@ SUBSYSTEM_DEF(mobs) /// The amount of giant spiders that exist in the world. Used for mob capping. var/giant_spiders = 0 +/datum/controller/subsystem/mobs/get_metrics() + . = ..() + var/list/cust = list() + cust["processing"] = length(GLOB.mob_living_list) + .["custom"] = cust + /datum/controller/subsystem/mobs/stat_entry() ..("P:[GLOB.mob_living_list.len]") diff --git a/code/controllers/subsystem/processing/processing.dm b/code/controllers/subsystem/processing/processing.dm index 5302314589d..b08aae88a23 100644 --- a/code/controllers/subsystem/processing/processing.dm +++ b/code/controllers/subsystem/processing/processing.dm @@ -14,6 +14,12 @@ SUBSYSTEM_DEF(processing) /datum/controller/subsystem/processing/stat_entry() ..("[stat_tag]:[processing.len]") +/datum/controller/subsystem/processing/get_metrics() + . = ..() + var/list/cust = list() + cust["processing"] = length(processing) + .["custom"] = cust + /datum/controller/subsystem/processing/fire(resumed = 0) if(!resumed) currentrun = processing.Copy() diff --git a/code/controllers/subsystem/tickets/tickets.dm b/code/controllers/subsystem/tickets/tickets.dm index d5eb31117e2..0954c39a032 100644 --- a/code/controllers/subsystem/tickets/tickets.dm +++ b/code/controllers/subsystem/tickets/tickets.dm @@ -41,6 +41,12 @@ SUBSYSTEM_DEF(tickets) var/ticketCounter = 1 +/datum/controller/subsystem/tickets/get_metrics() + . = ..() + var/list/cust = list() + cust["tickets"] = length(allTickets) // Not a perf metric but I want to see a graph where SSair usage spikes and 20 tickets come in + .["custom"] = cust + /datum/controller/subsystem/tickets/Initialize() if(!close_messages) close_messages = list("- [ticket_name] Rejected! -", diff --git a/code/controllers/subsystem/timer.dm b/code/controllers/subsystem/timer.dm index 53d77e8fad6..0a0eade55d7 100644 --- a/code/controllers/subsystem/timer.dm +++ b/code/controllers/subsystem/timer.dm @@ -37,6 +37,12 @@ SUBSYSTEM_DEF(timer) /datum/controller/subsystem/timer/stat_entry(msg) ..("B:[bucket_count] P:[length(second_queue)] H:[length(hashes)] C:[length(clienttime_timers)] S:[length(timer_id_dict)]") +/datum/controller/subsystem/timer/get_metrics() + . = ..() + var/list/cust = list() + cust["bucket_count"] = bucket_count + .["custom"] = cust + /datum/controller/subsystem/timer/fire(resumed = FALSE) var/lit = last_invoke_tick var/last_check = world.time - TICKS2DS(BUCKET_LEN*1.5) diff --git a/code/game/world.dm b/code/game/world.dm index a5ccc80fe7b..a2c5dfa746a 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -8,6 +8,8 @@ GLOBAL_LIST_INIT(map_transition_config, list(CC_TRANSITION_CONFIG)) // Right off the bat enable_auxtools_debugger() + SSmetrics.world_init_time = REALTIMEOFDAY + // Do sanity checks to ensure RUST actually exists if(!fexists(RUST_G)) DIRECT_OUTPUT(world.log, "ERROR: RUSTG was not found and is required for the game to function. Server will now exit.") diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index d929acd58ca..fb0878dee9c 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -12,6 +12,7 @@ #include "spawn_humans.dm" #include "sql.dm" #include "subsystem_init.dm" +#include "subsystem_metric_sanity.dm" #include "timer_sanity.dm" #include "unit_test.dm" #endif diff --git a/code/modules/unit_tests/subsystem_metric_sanity.dm b/code/modules/unit_tests/subsystem_metric_sanity.dm new file mode 100644 index 00000000000..ad5bb254a60 --- /dev/null +++ b/code/modules/unit_tests/subsystem_metric_sanity.dm @@ -0,0 +1,19 @@ +// Unit test to ensure SS metrics are valid +/datum/unit_test/subsystem_metric_sanity/Run() + for(var/datum/controller/subsystem/SS in Master.subsystems) + var/list/data = SS.get_metrics() + if(length(data) != 3) + Fail("SS[SS.ss_id] has invalid metrics data!") + continue + if(isnull(data["cost"])) + Fail("SS[SS.ss_id] has invalid metrics data! No 'cost' found in [json_encode(data)]") + continue + if(isnull(data["tick_usage"])) + Fail("SS[SS.ss_id] has invalid metrics data! No 'tick_usage' found in [json_encode(data)]") + continue + if(isnull(data["custom"])) + Fail("SS[SS.ss_id] has invalid metrics data! No 'custom' found in [json_encode(data)]") + continue + if(!islist(data["custom"])) + Fail("SS[SS.ss_id] has invalid metrics data! 'custom' is not a list in [json_encode(data)]") + continue diff --git a/paradise.dme b/paradise.dme index 11176b1befe..d531c3e6719 100644 --- a/paradise.dme +++ b/paradise.dme @@ -235,6 +235,7 @@ #include "code\controllers\subsystem\machinery.dm" #include "code\controllers\subsystem\mapping.dm" #include "code\controllers\subsystem\medals.dm" +#include "code\controllers\subsystem\metrics.dm" #include "code\controllers\subsystem\mobs.dm" #include "code\controllers\subsystem\nano_mob_hunter.dm" #include "code\controllers\subsystem\nightshift.dm"