diff --git a/code/__DEFINES/_protect.dm b/code/__DEFINES/_protect.dm index dbe25203c0b..22fad2f6385 100644 --- a/code/__DEFINES/_protect.dm +++ b/code/__DEFINES/_protect.dm @@ -1,4 +1,4 @@ -///Protects a datum from being VV'd +///Protects a datum from being VV'd or spawned through admin manipulation #define GENERAL_PROTECT_DATUM(Path)\ ##Path/can_vv_get(var_name){\ return FALSE;\ @@ -8,4 +8,10 @@ }\ ##Path/CanProcCall(procname){\ return FALSE;\ +}\ +##Path/Read(savefile/savefile){\ + qdel(src);\ +}\ +##Path/Write(savefile/savefile){\ + return;\ } diff --git a/code/__DEFINES/logging.dm b/code/__DEFINES/logging.dm index d086fa2ae62..9cfe197c0ca 100644 --- a/code/__DEFINES/logging.dm +++ b/code/__DEFINES/logging.dm @@ -55,24 +55,95 @@ #define LOGSRC_MOB "Mob" // Log header keys -#define LOG_HEADER_CATEGORY "category" -#define LOG_HEADER_INIT_TIMESTAMP "timestamp" -#define LOG_HEADER_ROUND_ID "round_id" - -// Log data keys -#define LOG_ENTRY_MESSAGE "message" -#define LOG_ENTRY_TIMESTAMP "timestamp" -#define LOG_ENTRY_DATA "data" +#define LOG_HEADER_CATEGORY "cat" +#define LOG_HEADER_CATEGORY_LIST "cat-list" +#define LOG_HEADER_INIT_TIMESTAMP "ts" +#define LOG_HEADER_ROUND_ID "round-id" +#define LOG_HEADER_SECRET "secret" // Log json keys -#define LOG_JSON_CATEGORY "category" +#define LOG_JSON_CATEGORY "cat" #define LOG_JSON_ENTRIES "entries" -#define LOG_JSON_LOGGING_START "log_start" +#define LOG_JSON_LOGGING_START "log-start" -// Log categories -#define LOG_CATEGORY_NOT_FOUND "invalid_category" -#define LOG_CATEGORY_TARGET_ZONE_SWITCH "target_zone_switch" +// Category for invalid/missing categories +#define LOG_CATEGORY_NOT_FOUND "invalid-category" + +// Misc categories +#define LOG_CATEGORY_ATTACK "attack" +#define LOG_CATEGORY_CONFIG "config" +#define LOG_CATEGORY_DYNAMIC "dynamic" +#define LOG_CATEGORY_ECONOMY "economy" +#define LOG_CATEGORY_FILTER "filter" +#define LOG_CATEGORY_MANIFEST "manifest" +#define LOG_CATEGORY_MECHA "mecha" +#define LOG_CATEGORY_PAPER "paper" +#define LOG_CATEGORY_RUNTIME "runtime" +#define LOG_CATEGORY_SHUTTLE "shuttle" +#define LOG_CATEGORY_SILICON "silicon" #define LOG_CATEGORY_SILO "silo" +#define LOG_CATEGORY_SIGNAL "signal" +#define LOG_CATEGORY_SPEECH_INDICATOR "speech-indiciator" +#define LOG_CATEGORY_SUSPICIOUS_LOGIN "supicious-login" +#define LOG_CATEGORY_TARGET_ZONE_SWITCH "target-zone-switch" +#define LOG_CATEGORY_TELECOMMS "telecomms" +#define LOG_CATEGORY_TOOL "tool" +#define LOG_CATEGORY_VIRUS "virus" + +// Admin categories +#define LOG_CATEGORY_ADMIN "admin" +#define LOG_CATEGORY_ADMIN_CIRCUIT "admin-circuit" +#define LOG_CATEGORY_ADMIN_DSAY "admin-dsay" + +// Admin private categories +#define LOG_CATEGORY_ADMIN_PRIVATE "adminprivate" +#define LOG_CATEGORY_ADMIN_PRIVATE_ASAY "adminprivate-asay" + +// Debug categories +#define LOG_CATEGORY_DEBUG "debug" +#define LOG_CATEGORY_DEBUG_ASSET "debug-asset" +#define LOG_CATEGORY_DEBUG_HREF "debug-href" +#define LOG_CATEGORY_DEBUG_JOB "debug-job" +#define LOG_CATEGORY_DEBUG_LUA "debug-lua" +#define LOG_CATEGORY_DEBUG_MAPPING "debug-mapping" +#define LOG_CATEGORY_DEBUG_MOBTAG "debug-mobtag" +#define LOG_CATEGORY_DEBUG_QDEL "debug-qdel" +#define LOG_CATEGORY_DEBUG_SQL "debug-sql" +#define LOG_CATEGORY_DEBUG_TGUI "debug-tgui" + +// Compatibility categories, for when stuff is changed and you need existing functionality to work +#define LOG_CATEGORY_COMPAT_GAME "game-compat" + +// Game categories +#define LOG_CATEGORY_GAME "game" +#define LOG_CATEGORY_GAME_ACCESS "game-access" +#define LOG_CATEGORY_GAME_EMOTE "game-emote" +#define LOG_CATEGORY_GAME_INTERNET_REQUEST "game-internet-request" +#define LOG_CATEGORY_GAME_OOC "game-ooc" +#define LOG_CATEGORY_GAME_PRAYER "game-prayer" +#define LOG_CATEGORY_GAME_RADIO_EMOTE "game-radio-emote" +#define LOG_CATEGORY_GAME_SAY "game-say" +#define LOG_CATEGORY_GAME_TOPIC "game-topic" +#define LOG_CATEGORY_GAME_TRAITOR "game-traitor" +#define LOG_CATEGORY_GAME_VOTE "game-vote" +#define LOG_CATEGORY_GAME_WHISPER "game-whisper" + +// Uplink categories +#define LOG_CATEGORY_UPLINK "uplink" +#define LOG_CATEGORY_UPLINK_CHANGELING "uplink-changeling" +#define LOG_CATEGORY_UPLINK_HERETIC "uplink-heretic" +#define LOG_CATEGORY_UPLINK_MALF "uplink-malf" +#define LOG_CATEGORY_UPLINK_SPELL "uplink-spell" + +// PDA categories +#define LOG_CATEGORY_PDA "pda" +#define LOG_CATEGORY_PDA_CHAT "pda-chat" +#define LOG_CATEGORY_PDA_COMMENT "pda-comment" + +#define SCHEMA_VERSION "schema-version" + +// Default log schema version +#define LOG_CATEGORY_SCHEMA_VERSION_NOT_SET "0.0.1" //wrapper macros for easier grepping #define DIRECT_OUTPUT(A, B) A << B diff --git a/code/__HELPERS/files.dm b/code/__HELPERS/files.dm index 12ae584db5f..c773b8e9b76 100644 --- a/code/__HELPERS/files.dm +++ b/code/__HELPERS/files.dm @@ -5,7 +5,7 @@ */ GLOBAL_VAR_INIT(fileaccess_timer, 0) -/client/proc/browse_files(root_type=BROWSE_ROOT_ALL_LOGS, max_iterations=10, list/valid_extensions=list("txt","log","htm", "html", "gz")) +/client/proc/browse_files(root_type=BROWSE_ROOT_ALL_LOGS, max_iterations=10, list/valid_extensions=list("txt","log","htm", "html", "gz", "json")) // wow why was this ever a parameter var/root = "data/logs/" switch(root_type) diff --git a/code/__HELPERS/logging/_logging.dm b/code/__HELPERS/logging/_logging.dm index c1e65aad4d2..6e2f7cd0773 100644 --- a/code/__HELPERS/logging/_logging.dm +++ b/code/__HELPERS/logging/_logging.dm @@ -10,6 +10,31 @@ msg = "## NOTICE: [msg]" log_world(msg) +#define SET_SERIALIZATION_SEMVER(semver_list, semver) semver_list[type] = semver +#define CHECK_SERIALIZATION_SEMVER(wanted, actual) (__check_serialization_semver(wanted, actual)) + +/// Checks if the actual semver is equal or later than the wanted semver +/// Must be passed as TEXT; you're probably looking for CHECK_SERIALIZATION_SEMVER, look right above +/proc/__check_serialization_semver(wanted, actual) + if(wanted == actual) + return TRUE + + var/list/wanted_versions = semver_to_list(wanted) + var/list/actual_versions = semver_to_list(actual) + + if(!wanted_versions || !actual_versions) + stack_trace("Invalid semver string(s) passed to __check_serialization_semver: '[wanted]' and '[actual]'") + return FALSE + + if(wanted_versions[1] != actual_versions[1]) + return FALSE // major must always + + if(wanted_versions[2] > actual_versions[2]) + return FALSE // actual must be later than wanted + + // patch is not checked + return TRUE + //print a testing-mode debug message to world.log and world #ifdef TESTING #define testing(msg) log_world("## TESTING: [msg]"); to_chat(world, "## TESTING: [msg]") @@ -128,7 +153,7 @@ GLOBAL_LIST_INIT(testing_global_profiler, list("_PROFILE_NAME" = "Global")) /* Close open log handles. This should be called as late as possible, and no logging should hapen after. */ /proc/shutdown_logging() rustg_log_close_all() - GLOB.logger.shutdown_logging() + logger.shutdown_logging() /* Helper procs for building detailed log lines */ /proc/key_name(whom, include_link = null, include_name = TRUE) diff --git a/code/__HELPERS/logging/admin.dm b/code/__HELPERS/logging/admin.dm index 6c659e4ea01..91f774c5346 100644 --- a/code/__HELPERS/logging/admin.dm +++ b/code/__HELPERS/logging/admin.dm @@ -1,33 +1,36 @@ /* Items with ADMINPRIVATE prefixed are stripped from public logs. */ +// For backwards compatibility these are currently also added to LOG_CATEGORY_GAME with their respective prefix +// This is to avoid breaking any existing tools that rely on the old format, but should be removed in the future + /// General logging for admin actions -/proc/log_admin(text) +/proc/log_admin(text, list/data) GLOB.admin_activities.Add(text) - if (CONFIG_GET(flag/log_admin)) - WRITE_LOG(GLOB.world_game_log, "ADMIN: [text]") + logger.Log(LOG_CATEGORY_ADMIN, text, data) + logger.Log(LOG_CATEGORY_COMPAT_GAME, "ADMIN: [text]") /// Logging for admin actions on or with circuits -/proc/log_admin_circuit(text) +/proc/log_admin_circuit(text, list/data) GLOB.admin_activities.Add(text) - if(CONFIG_GET(flag/log_admin)) - WRITE_LOG(GLOB.world_game_log, "ADMIN: CIRCUIT: [text]") + logger.Log(LOG_CATEGORY_ADMIN_CIRCUIT, text, data) + logger.Log(LOG_CATEGORY_COMPAT_GAME, "ADMIN: CIRCUIT: [text]") /// General logging for admin actions -/proc/log_admin_private(text) +/proc/log_admin_private(text, list/data) GLOB.admin_activities.Add(text) - if (CONFIG_GET(flag/log_admin)) - WRITE_LOG(GLOB.world_game_log, "ADMINPRIVATE: [text]") + logger.Log(LOG_CATEGORY_ADMIN_PRIVATE, text, data) + logger.Log(LOG_CATEGORY_COMPAT_GAME, "ADMINPRIVATE: [text]") /// Logging for AdminSay (ASAY) messages -/proc/log_adminsay(text) +/proc/log_adminsay(text, list/data) GLOB.admin_activities.Add(text) - if (CONFIG_GET(flag/log_adminchat)) - WRITE_LOG(GLOB.world_game_log, "ADMINPRIVATE: ASAY: [text]") + logger.Log(LOG_CATEGORY_ADMIN_PRIVATE_ASAY, text, data) + logger.Log(LOG_CATEGORY_COMPAT_GAME, "ADMINPRIVATE: ASAY: [text]") /// Logging for DeachatSay (DSAY) messages -/proc/log_dsay(text) - if (CONFIG_GET(flag/log_adminchat)) - WRITE_LOG(GLOB.world_game_log, "ADMIN: DSAY: [text]") +/proc/log_dsay(text, list/data) + logger.Log(LOG_CATEGORY_ADMIN_DSAY, text, data) + logger.Log(LOG_CATEGORY_COMPAT_GAME, "ADMIN: DSAY: [text]") /** * Writes to a special log file if the log_suspicious_login config flag is set, @@ -36,8 +39,7 @@ * Mirrors this log entry to log_access when access_log_mirror is TRUE, so this proc * doesn't need to be used alongside log_access and can replace it where appropriate. */ -/proc/log_suspicious_login(text, access_log_mirror = TRUE) - if (CONFIG_GET(flag/log_suspicious_login)) - WRITE_LOG(GLOB.world_suspicious_login_log, "SUSPICIOUS_ACCESS: [text]") +/proc/log_suspicious_login(text, list/data, access_log_mirror = TRUE) + logger.Log(LOG_CATEGORY_SUSPICIOUS_LOGIN, text) if(access_log_mirror) log_access(text) diff --git a/code/__HELPERS/logging/antagonists.dm b/code/__HELPERS/logging/antagonists.dm index 38f88b3b4d6..3d06bb325ec 100644 --- a/code/__HELPERS/logging/antagonists.dm +++ b/code/__HELPERS/logging/antagonists.dm @@ -1,29 +1,23 @@ /// Logging for traitor objectives -/proc/log_traitor(text) - if (CONFIG_GET(flag/log_traitor)) - WRITE_LOG(GLOB.world_game_log, "TRAITOR: [text]") +/proc/log_traitor(text, list/data) + logger.Log(LOG_CATEGORY_GAME_TRAITOR, text, data) /// Logging for items purchased from a traitor uplink -/proc/log_uplink(text) - if (CONFIG_GET(flag/log_uplink)) - WRITE_LOG(GLOB.world_uplink_log, "UPLINK: [text]") +/proc/log_uplink(text, list/data) + logger.Log(LOG_CATEGORY_UPLINK, text, data) /// Logging for upgrades purchased by a malfunctioning (or combat upgraded) AI -/proc/log_malf_upgrades(text) - if (CONFIG_GET(flag/log_uplink)) - WRITE_LOG(GLOB.world_uplink_log, "MALF UPGRADE: [text]") +/proc/log_malf_upgrades(text, list/data) + logger.Log(LOG_CATEGORY_UPLINK_MALF, text, data) /// Logging for changeling powers purchased -/proc/log_changeling_power(text) - if (CONFIG_GET(flag/log_uplink)) - WRITE_LOG(GLOB.world_uplink_log, "CHANGELING: [text]") +/proc/log_changeling_power(text, list/data) + logger.Log(LOG_CATEGORY_UPLINK_CHANGELING, text, data) /// Logging for heretic powers learned -/proc/log_heretic_knowledge(text) - if (CONFIG_GET(flag/log_uplink)) - WRITE_LOG(GLOB.world_uplink_log, "HERETIC RESEARCH: [text]") +/proc/log_heretic_knowledge(text, list/data) + logger.Log(LOG_CATEGORY_UPLINK_HERETIC, text, data) /// Logging for wizard powers learned -/proc/log_spellbook(text) - if (CONFIG_GET(flag/log_uplink)) - WRITE_LOG(GLOB.world_uplink_log, "SPELLBOOK: [text]") +/proc/log_spellbook(text, list/data) + logger.Log(LOG_CATEGORY_UPLINK_SPELL, text, data) diff --git a/code/__HELPERS/logging/attack.dm b/code/__HELPERS/logging/attack.dm index e804c07e9f0..598882a9ac1 100644 --- a/code/__HELPERS/logging/attack.dm +++ b/code/__HELPERS/logging/attack.dm @@ -1,7 +1,6 @@ /// Generic attack logging -/proc/log_attack(text) - if (CONFIG_GET(flag/log_attack)) - WRITE_LOG(GLOB.world_attack_log, "ATTACK: [text]") +/proc/log_attack(text, list/data) + logger.Log(LOG_CATEGORY_ATTACK, text) /** * Log a combat message in the attack log diff --git a/code/__HELPERS/logging/debug.dm b/code/__HELPERS/logging/debug.dm index e7ba69b1eb1..61b686811c8 100644 --- a/code/__HELPERS/logging/debug.dm +++ b/code/__HELPERS/logging/debug.dm @@ -1,32 +1,30 @@ /// Logging for loading and caching assets -/proc/log_asset(text) - if(CONFIG_GET(flag/log_asset)) - WRITE_LOG(GLOB.world_asset_log, "ASSET: [text]") +/proc/log_asset(text, list/data) + logger.Log(LOG_CATEGORY_DEBUG_ASSET, text, data) /// Logging for config errors /// Rarely gets called; just here in case the config breaks. -/proc/log_config(text) - WRITE_LOG(GLOB.config_error_log, text) +/proc/log_config(text, list/data) + logger.Log(LOG_CATEGORY_CONFIG, text, data) SEND_TEXT(world.log, text) -/proc/log_filter_raw(text) - WRITE_LOG(GLOB.filter_log, "FILTER: [text]") +/proc/log_filter_raw(text, list/data) + logger.Log(LOG_CATEGORY_FILTER, text, data) /// Logging for job slot changes -/proc/log_job_debug(text) - if (CONFIG_GET(flag/log_job_debug)) - WRITE_LOG(GLOB.world_job_debug_log, "JOB: [text]") +/proc/log_job_debug(text, list/data) + logger.Log(LOG_CATEGORY_DEBUG_JOB, text, data) /// Logging for lua scripting -/proc/log_lua(text) - WRITE_LOG(GLOB.lua_log, text) +/proc/log_lua(text, list/data) + logger.Log(LOG_CATEGORY_DEBUG_LUA, text, data) /// Logging for mapping errors /proc/log_mapping(text, skip_world_log) #ifdef UNIT_TESTS GLOB.unit_test_mapping_logs += text #endif - WRITE_LOG(GLOB.world_map_error_log, text) + logger.Log(LOG_CATEGORY_DEBUG_MAPPING, text) if(skip_world_log) return SEND_TEXT(world.log, text) @@ -37,31 +35,27 @@ WRITE_LOG_NO_FORMAT(GLOB.perf_log, .) /// Logging for hard deletes -/proc/log_qdel(text) - WRITE_LOG(GLOB.world_qdel_log, "QDEL: [text]") - -/// Logging for SQL errors -/proc/log_query_debug(text) - WRITE_LOG(GLOB.query_debug_log, "SQL: [text]") +/proc/log_qdel(text, list/data) + logger.Log(LOG_CATEGORY_DEBUG_QDEL, text, data) /* Log to the logfile only. */ -/proc/log_runtime(text) - WRITE_LOG(GLOB.world_runtime_log, text) +/proc/log_runtime(text, list/data) + logger.Log(LOG_CATEGORY_RUNTIME, text, data) -/proc/log_signal(text) - WRITE_LOG(GLOB.signals_log, text) +/proc/log_signal(text, list/data) + logger.Log(LOG_CATEGORY_SIGNAL, text, data) /// Logging for DB errors -/proc/log_sql(text) - WRITE_LOG(GLOB.sql_error_log, "SQL: [text]") +/proc/log_sql(text, list/data) + logger.Log(LOG_CATEGORY_DEBUG_SQL, text, data) /// Logging for world/Topic -/proc/log_topic(text) - WRITE_LOG(GLOB.world_game_log, "TOPIC: [text]") +/proc/log_topic(text, list/data) + logger.Log(LOG_CATEGORY_GAME_TOPIC, text, data) /// Log to both DD and the logfile. -/proc/log_world(text) +/proc/log_world(text, list/data) #ifdef USE_CUSTOM_ERROR_HANDLER - WRITE_LOG(GLOB.world_runtime_log, text) + logger.Log(LOG_CATEGORY_RUNTIME, text, data) #endif SEND_TEXT(world.log, text) diff --git a/code/__HELPERS/logging/dynamic.dm b/code/__HELPERS/logging/dynamic.dm index ff1f4fd1f7a..51bba86ca05 100644 --- a/code/__HELPERS/logging/dynamic.dm +++ b/code/__HELPERS/logging/dynamic.dm @@ -4,5 +4,5 @@ log_dynamic("[text]") /// Logging for dynamic procs -/proc/log_dynamic(text) - WRITE_LOG(GLOB.dynamic_log, "DYNAMIC: [text]") +/proc/log_dynamic(text, list/data) + logger.Log(LOG_CATEGORY_DYNAMIC, text, data) diff --git a/code/__HELPERS/logging/economy.dm b/code/__HELPERS/logging/economy.dm index 8f190cb934c..59fb0eee3ed 100644 --- a/code/__HELPERS/logging/economy.dm +++ b/code/__HELPERS/logging/economy.dm @@ -1,3 +1,2 @@ -/proc/log_econ(text) - if (CONFIG_GET(flag/log_econ)) - WRITE_LOG(GLOB.world_econ_log, "MONEY: [text]") +/proc/log_econ(text, list/data) + logger.Log(LOG_CATEGORY_ECONOMY, text, data) diff --git a/code/__HELPERS/logging/game.dm b/code/__HELPERS/logging/game.dm index 40fd458d1a9..fd0fb533d31 100644 --- a/code/__HELPERS/logging/game.dm +++ b/code/__HELPERS/logging/game.dm @@ -1,40 +1,32 @@ /// Logging for generic/unsorted game messages -/proc/log_game(text) - if (CONFIG_GET(flag/log_game)) - WRITE_LOG(GLOB.world_game_log, "GAME: [text]") +/proc/log_game(text, list/data) + logger.Log(LOG_CATEGORY_GAME, text, data) /// Logging for emotes -/proc/log_emote(text) - if (CONFIG_GET(flag/log_emote)) - WRITE_LOG(GLOB.world_game_log, "EMOTE: [text]") +/proc/log_emote(text, list/data) + logger.Log(LOG_CATEGORY_GAME_EMOTE, text, data) /// Logging for emotes sent over the radio -/proc/log_radio_emote(text) - if (CONFIG_GET(flag/log_emote)) - WRITE_LOG(GLOB.world_game_log, "RADIOEMOTE: [text]") +/proc/log_radio_emote(text, list/data) + logger.Log(LOG_CATEGORY_GAME_RADIO_EMOTE, text, data) /// Logging for messages sent in OOC -/proc/log_ooc(text) - if (CONFIG_GET(flag/log_ooc)) - WRITE_LOG(GLOB.world_game_log, "OOC: [text]") +/proc/log_ooc(text, list/data) + logger.Log(LOG_CATEGORY_GAME_OOC, text, data) /// Logging for prayed messages -/proc/log_prayer(text) - if (CONFIG_GET(flag/log_prayer)) - WRITE_LOG(GLOB.world_game_log, "PRAY: [text]") +/proc/log_prayer(text, list/data) + logger.Log(LOG_CATEGORY_GAME_PRAYER, text, data) /// Logging for music requests -/proc/log_internet_request(text) - if (CONFIG_GET(flag/log_internet_request)) - WRITE_LOG(GLOB.world_game_log, "INTERNET REQUEST: [text]") +/proc/log_internet_request(text, list/data) + logger.Log(LOG_CATEGORY_GAME_INTERNET_REQUEST, text, data) /// Logging for logging in & out of the game, with error messages. -/proc/log_access(text) - if (CONFIG_GET(flag/log_access)) - WRITE_LOG(GLOB.world_game_log, "ACCESS: [text]") +/proc/log_access(text, list/data) + logger.Log(LOG_CATEGORY_GAME_ACCESS, text, data) /// Logging for OOC votes -/proc/log_vote(text) - if (CONFIG_GET(flag/log_vote)) - WRITE_LOG(GLOB.world_game_log, "VOTE: [text]") +/proc/log_vote(text, list/data) + logger.Log(LOG_CATEGORY_GAME_VOTE, text, data) diff --git a/code/__HELPERS/logging/manifest.dm b/code/__HELPERS/logging/manifest.dm index 7275b1cc73d..6009195e411 100644 --- a/code/__HELPERS/logging/manifest.dm +++ b/code/__HELPERS/logging/manifest.dm @@ -1,4 +1,5 @@ /// Logging for player manifest (ckey, name, job, special role, roundstart/latejoin) /proc/log_manifest(ckey, datum/mind/mind, mob/body, latejoin = FALSE) - if (CONFIG_GET(flag/log_manifest)) - WRITE_LOG(GLOB.world_manifest_log, "[ckey] \\ [body.real_name] \\ [mind.assigned_role.title] \\ [mind.special_role ? mind.special_role : "NONE"] \\ [latejoin ? "LATEJOIN":"ROUNDSTART"]") + logger.Log(LOG_CATEGORY_MANIFEST, "manifest entry added", list( + "mind" = mind, "body" = body, "latejoin" = latejoin + )) diff --git a/code/__HELPERS/logging/mecha.dm b/code/__HELPERS/logging/mecha.dm index 45c6a445dd2..68c62bad8af 100644 --- a/code/__HELPERS/logging/mecha.dm +++ b/code/__HELPERS/logging/mecha.dm @@ -1,7 +1,6 @@ /// Logging for mech actions -/proc/log_mecha(text) - if (CONFIG_GET(flag/log_mecha)) - WRITE_LOG(GLOB.world_mecha_log, "MECHA: [text]") +/proc/log_mecha(text, list/data) + logger.Log(LOG_CATEGORY_MECHA, text, data) /// Logging for equipment installed in a mecha /obj/item/mecha_parts/mecha_equipment/log_message(message, message_type = LOG_MECHA, color = null, log_globally) diff --git a/code/__HELPERS/logging/mob.dm b/code/__HELPERS/logging/mob.dm index 02281ab54e1..a5951a9a524 100644 --- a/code/__HELPERS/logging/mob.dm +++ b/code/__HELPERS/logging/mob.dm @@ -3,12 +3,11 @@ * Arguments: * * text - text to log. */ -/mob/proc/log_mob_tag(text) - WRITE_LOG(GLOB.world_mob_tag_log, "TAG: \[[tag]\] [text]") +/mob/proc/log_mob_tag(text, list/data) + logger.Log(LOG_CATEGORY_DEBUG_MOBTAG, text, data) -/proc/log_silicon(text) - if (CONFIG_GET(flag/log_silicon)) - WRITE_LOG(GLOB.world_silicon_log, "SILICON: [text]") +/proc/log_silicon(text, list/data) + logger.Log(LOG_CATEGORY_SILICON, text, data) /// Logs a message in a mob's individual log, and in the global logs as well if log_globally is true diff --git a/code/__HELPERS/logging/paper.dm b/code/__HELPERS/logging/paper.dm index b748faf217e..edd2835772b 100644 --- a/code/__HELPERS/logging/paper.dm +++ b/code/__HELPERS/logging/paper.dm @@ -1,3 +1,3 @@ /// Logging for writing made on paper -/proc/log_paper(text) - WRITE_LOG(GLOB.world_paper_log, "PAPER: [text]") +/proc/log_paper(text, list/data) + logger.Log(LOG_CATEGORY_PAPER, text, data) diff --git a/code/__HELPERS/logging/pda.dm b/code/__HELPERS/logging/pda.dm index 01c8d98d95a..acd5b611183 100644 --- a/code/__HELPERS/logging/pda.dm +++ b/code/__HELPERS/logging/pda.dm @@ -1,16 +1,11 @@ /// Logging for PDA messages sent -/proc/log_pda(text) - if (CONFIG_GET(flag/log_pda)) - WRITE_LOG(GLOB.world_pda_log, "PDA: [text]") +/proc/log_pda(text, list/data) + logger.Log(LOG_CATEGORY_PDA, text, data) /// Logging for newscaster comments -/proc/log_comment(text) - //reusing the PDA option because I really don't think news comments are worth a config option - if (CONFIG_GET(flag/log_pda)) - WRITE_LOG(GLOB.world_pda_log, "COMMENT: [text]") +/proc/log_comment(text, list/data) + logger.Log(LOG_CATEGORY_PDA_COMMENT, text, data) /// Logging for chatting on modular computer channels -/proc/log_chat(text) - //same thing here - if (CONFIG_GET(flag/log_pda)) - WRITE_LOG(GLOB.world_pda_log, "CHAT: [text]") +/proc/log_chat(text, list/data) + logger.Log(LOG_CATEGORY_PDA_CHAT, text, data) diff --git a/code/__HELPERS/logging/shuttle.dm b/code/__HELPERS/logging/shuttle.dm index 62a5fcd3e6e..605ed903cfc 100644 --- a/code/__HELPERS/logging/shuttle.dm +++ b/code/__HELPERS/logging/shuttle.dm @@ -1,4 +1,3 @@ /// Logging for shuttle actions -/proc/log_shuttle(text) - if (CONFIG_GET(flag/log_shuttle)) - WRITE_LOG(GLOB.world_shuttle_log, "SHUTTLE: [text]") +/proc/log_shuttle(text, list/data) + logger.Log(LOG_CATEGORY_SHUTTLE, text, data) diff --git a/code/__HELPERS/logging/talk.dm b/code/__HELPERS/logging/talk.dm index bed13b6c350..6cfae068cb6 100644 --- a/code/__HELPERS/logging/talk.dm +++ b/code/__HELPERS/logging/talk.dm @@ -17,14 +17,12 @@ log_message("[prefix][custom_say_emote ? "*[custom_say_emote]*, " : ""]\"[message]\"[suffix]", message_type, log_globally = log_globally) /// Logging for generic spoken messages -/proc/log_say(text) - if (CONFIG_GET(flag/log_say)) - WRITE_LOG(GLOB.world_game_log, "SAY: [text]") +/proc/log_say(text, list/data) + logger.Log(LOG_CATEGORY_GAME_SAY, text, data) /// Logging for whispered messages -/proc/log_whisper(text) - if (CONFIG_GET(flag/log_whisper)) - WRITE_LOG(GLOB.world_game_log, "WHISPER: [text]") +/proc/log_whisper(text, list/data) + logger.Log(LOG_CATEGORY_GAME_WHISPER, text, data) /// Helper for logging of messages with only one sender and receiver (i.e. mind links) /proc/log_directed_talk(atom/source, atom/target, message, message_type, tag) @@ -37,11 +35,9 @@ target.log_talk(message, LOG_VICTIM, tag = "[tag] from [key_name(source)]", log_globally = FALSE) /// Logging for speech taking place over comms, as well as tcomms equipment -/proc/log_telecomms(text) - if (CONFIG_GET(flag/log_telecomms)) - WRITE_LOG(GLOB.world_telecomms_log, "TCOMMS: [text]") +/proc/log_telecomms(text, list/data) + logger.Log(LOG_CATEGORY_TELECOMMS, text, data) /// Logging for speech indicators. -/proc/log_speech_indicators(text) - if (CONFIG_GET(flag/log_speech_indicators)) - WRITE_LOG(GLOB.world_speech_indicators_log, "SPEECH INDICATOR: [text]") +/proc/log_speech_indicators(text, list/data) + logger.Log(LOG_CATEGORY_SPEECH_INDICATOR, text, data) diff --git a/code/__HELPERS/logging/tool.dm b/code/__HELPERS/logging/tool.dm index 4fd354eb58d..b43d8a4b704 100644 --- a/code/__HELPERS/logging/tool.dm +++ b/code/__HELPERS/logging/tool.dm @@ -1,4 +1,3 @@ /// Logging for tool usage /proc/log_tool(text, mob/initiator) - if(CONFIG_GET(flag/log_tools)) - WRITE_LOG(GLOB.world_tool_log, "TOOL: [text]") + logger.Log(LOG_CATEGORY_TOOL, text, initiator ? list("initiator" = initiator) : null) diff --git a/code/__HELPERS/logging/ui.dm b/code/__HELPERS/logging/ui.dm index 3a0432d35f7..607f22a507b 100644 --- a/code/__HELPERS/logging/ui.dm +++ b/code/__HELPERS/logging/ui.dm @@ -1,5 +1,5 @@ -/proc/log_href(text) - WRITE_LOG(GLOB.world_href_log, "HREF: [text]") +/proc/log_href(text, list/data) + logger.Log(LOG_CATEGORY_DEBUG_HREF, text, data) /** * Appends a tgui-related log entry. All arguments are optional. @@ -31,4 +31,4 @@ // Insert message if(message) entry += "\n[message]" - WRITE_LOG(GLOB.tgui_log, entry) + logger.Log(LOG_CATEGORY_DEBUG_TGUI, entry) diff --git a/code/__HELPERS/logging/virus.dm b/code/__HELPERS/logging/virus.dm index 26d0d374e02..050ae9434e4 100644 --- a/code/__HELPERS/logging/virus.dm +++ b/code/__HELPERS/logging/virus.dm @@ -1,7 +1,6 @@ /// Logging for the creation and contraction of viruses -/proc/log_virus(text) - if (CONFIG_GET(flag/log_virus)) - WRITE_LOG(GLOB.world_virus_log, "VIRUS: [text]") +/proc/log_virus(text, list/data) + logger.Log(LOG_CATEGORY_VIRUS, text, data) /// Returns a string for admin logging uses, should describe the disease in detail /datum/disease/proc/admin_details() diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index 91837dfb3ea..ed430a86708 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -1193,3 +1193,15 @@ GLOBAL_LIST_INIT(binary, list("0","1")) /proc/sanitize_css_class_name(name) var/static/regex/regex = new(@"[^a-zA-Z0-9]","g") return replacetext(name, regex, "") + +/// Converts a semver string into a list of numbers +/proc/semver_to_list(semver_string) + var/static/regex/semver_regex = regex(@"(\d+)\.(\d+)\.(\d+)", "") + if(!semver_regex.Find(semver_string)) + return null + + return list( + text2num(semver_regex.group[1]), + text2num(semver_regex.group[2]), + text2num(semver_regex.group[3]), + ) diff --git a/code/_globalvars/logging.dm b/code/_globalvars/logging.dm index a930f75a3e5..4d0b0698679 100644 --- a/code/_globalvars/logging.dm +++ b/code/_globalvars/logging.dm @@ -28,47 +28,18 @@ GLOBAL_PROTECT(##log_var_name);\ SHOULD_CALL_PARENT(TRUE) return -// All individual log files +// All individual log files. +// These should be used where the log category cannot easily be a json log file. DECLARE_LOG(config_error_log, DONT_START_LOG) -DECLARE_LOG(dynamic_log, DONT_START_LOG) -DECLARE_LOG(lua_log, DONT_START_LOG) DECLARE_LOG(perf_log, DONT_START_LOG) // Declared here but name is set in time_track subsystem -DECLARE_LOG(query_debug_log, DONT_START_LOG) -DECLARE_LOG(signals_log, DONT_START_LOG) -DECLARE_LOG(tgui_log, START_LOG) + #ifdef REFERENCE_DOING_IT_LIVE DECLARE_LOG_NAMED(harddel_log, "harddels", START_LOG) #endif + #if defined(UNIT_TESTS) || defined(SPACEMAN_DMM) DECLARE_LOG_NAMED(test_log, "tests", START_LOG) #endif -DECLARE_LOG_NAMED(filter_log, "filters", DONT_START_LOG) -DECLARE_LOG_NAMED(sql_error_log, "sql", DONT_START_LOG) -DECLARE_LOG_NAMED(world_asset_log, "asset", DONT_START_LOG) -DECLARE_LOG_NAMED(world_attack_log, "attack", START_LOG) -DECLARE_LOG_NAMED(world_econ_log, "econ", START_LOG) -DECLARE_LOG_NAMED(world_game_log, "game", START_LOG) -DECLARE_LOG_NAMED(world_href_log, "hrefs", START_LOG) -DECLARE_LOG_NAMED(world_job_debug_log, "job_debug", START_LOG) -DECLARE_LOG_NAMED(world_manifest_log, "manifest", START_LOG) -DECLARE_LOG_NAMED(world_map_error_log, "map_errors", DONT_START_LOG) -DECLARE_LOG_NAMED(world_mecha_log, "mecha", DONT_START_LOG) -DECLARE_LOG_NAMED(world_mob_tag_log, "mob_tags", START_LOG) -DECLARE_LOG_NAMED(world_paper_log, "paper", DONT_START_LOG) -DECLARE_LOG_NAMED(world_pda_log, "pda", START_LOG) -DECLARE_LOG_NAMED(world_qdel_log, "qdel", START_LOG) -DECLARE_LOG_NAMED(world_runtime_log, "runtime", START_LOG) -DECLARE_LOG_NAMED(world_shuttle_log, "shuttle", START_LOG) -DECLARE_LOG_NAMED(world_silicon_log, "silicon", DONT_START_LOG) -DECLARE_LOG_NAMED(world_speech_indicators_log, "speech_indicators", DONT_START_LOG) -DECLARE_LOG_NAMED(world_telecomms_log, "telecomms", START_LOG) -DECLARE_LOG_NAMED(world_tool_log, "tools", DONT_START_LOG) -DECLARE_LOG_NAMED(world_uplink_log, "uplink", START_LOG) -DECLARE_LOG_NAMED(world_virus_log, "virus", DONT_START_LOG) -/// Log associated with [/proc/log_suspicious_login()] -/// Intended to hold all logins that failed due to suspicious circumstances such as ban detection, CID randomisation etc. -DECLARE_LOG_NAMED(world_suspicious_login_log, "suspicious_logins", DONT_START_LOG) - /// Picture logging diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm index 752918e3c63..cac60fadec8 100644 --- a/code/controllers/configuration/entries/general.dm +++ b/code/controllers/configuration/entries/general.dm @@ -158,6 +158,10 @@ /// logs all timers in buckets on automatic bucket reset (Useful for timer debugging) /datum/config_entry/flag/log_timers_on_bucket_reset +/// Log human readable versions of json log entries +/datum/config_entry/flag/log_as_human_readable + default = TRUE + /// allows admins with relevant permissions to have their own ooc colour /datum/config_entry/flag/allow_admin_ooccolor diff --git a/code/controllers/subsystem/dbcore.dm b/code/controllers/subsystem/dbcore.dm index 7afd62fb87f..909d72bb85e 100644 --- a/code/controllers/subsystem/dbcore.dm +++ b/code/controllers/subsystem/dbcore.dm @@ -569,12 +569,18 @@ Delayed insert mode was removed in mysql 7 and only works with MyISAM type table . = (status != DB_QUERY_BROKEN) var/timed_out = !. && findtext(last_error, "Operation timed out") if(!. && log_error) - log_sql("[last_error] | Query used: [sql] | Arguments: [json_encode(arguments)]") + logger.Log(LOG_CATEGORY_DEBUG_SQL, "sql query failed", list( + "query" = sql, + "arguments" = json_encode(arguments), + "error" = last_error, + )) + if(!async && timed_out) - log_query_debug("Query execution started at [start_time]") - log_query_debug("Query execution ended at [REALTIMEOFDAY]") - log_query_debug("Slow query timeout detected.") - log_query_debug("Query used: [sql]") + logger.Log(LOG_CATEGORY_DEBUG_SQL, "slow query timeout", list( + "query" = sql, + "start_time" = start_time, + "end_time" = REALTIMEOFDAY, + )) slow_query_check() /// Sleeps until execution of the query has finished. diff --git a/code/datums/datum.dm b/code/datums/datum.dm index 8b4dd37aa7b..97d5f83f0d8 100644 --- a/code/datums/datum.dm +++ b/code/datums/datum.dm @@ -183,13 +183,20 @@ to_chat(target, txt_changed_vars()) #endif -///Return a LIST for serialize_datum to encode! Not the actual json! -/datum/proc/serialize_list(list/options) - CRASH("Attempted to serialize datum [src] of type [type] without serialize_list being implemented!") +/// Return a list of data which can be used to investigate the datum, also ensure that you set the semver in the options list +/datum/proc/serialize_list(list/options, list/semvers) + SHOULD_CALL_PARENT(TRUE) -///Accepts a LIST from deserialize_datum. Should return src or another datum. + . = list() + .["tag"] = tag + + SET_SERIALIZATION_SEMVER(semvers, "1.0.0") + return . + +///Accepts a LIST from deserialize_datum. Should return whether or not the deserialization was successful. /datum/proc/deserialize_list(json, list/options) - CRASH("Attempted to deserialize datum [src] of type [type] without deserialize_list being implemented!") + SHOULD_CALL_PARENT(TRUE) + return TRUE ///Serializes into JSON. Does not encode type. /datum/proc/serialize_json(list/options) @@ -241,11 +248,10 @@ return var/typeofdatum = jsonlist["DATUM_TYPE"] //BYOND won't directly read if this is just put in the line below, and will instead runtime because it thinks you're trying to make a new list? var/datum/D = new typeofdatum - var/datum/returned = D.deserialize_list(jsonlist, options) - if(!isdatum(returned)) + if(!D.deserialize_list(jsonlist, options)) qdel(D) else - return returned + return D /** * Callback called by a timer to end an associative-list-indexed cooldown. diff --git a/code/datums/json_savefile.dm b/code/datums/json_savefile.dm index 83054258aeb..69e5a2ef7cf 100644 --- a/code/datums/json_savefile.dm +++ b/code/datums/json_savefile.dm @@ -59,7 +59,9 @@ GENERAL_PROTECT_DATUM(/datum/json_savefile) if(path) rustg_file_write(json_encode(tree), path) -/datum/json_savefile/serialize_list(list/options) +/datum/json_savefile/serialize_list(list/options, list/semvers) + SHOULD_CALL_PARENT(FALSE) + SET_SERIALIZATION_SEMVER(semvers, "1.0.0") return tree.Copy() /// Traverses the entire dir tree of the given savefile and dynamically assembles the tree from it diff --git a/code/datums/martial/_martial.dm b/code/datums/martial/_martial.dm index a6cfbb3d725..a3c62f5ed3f 100644 --- a/code/datums/martial/_martial.dm +++ b/code/datums/martial/_martial.dm @@ -16,6 +16,16 @@ /// If set to true this style allows you to punch people despite being a pacifist (for instance Boxing, which does no damage) var/pacifist_style = FALSE +/datum/martial_art/serialize_list(list/options, list/semvers) + . = ..() + + .["name"] = name + .["id"] = id + .["pacifist_style"] = pacifist_style + + SET_SERIALIZATION_SEMVER(semvers, "1.0.0") + return . + /datum/martial_art/proc/help_act(mob/living/attacker, mob/living/defender) return MARTIAL_ATTACK_INVALID diff --git a/code/datums/memory/_memory.dm b/code/datums/memory/_memory.dm index 88d8f4a6247..45710f07067 100644 --- a/code/datums/memory/_memory.dm +++ b/code/datums/memory/_memory.dm @@ -75,6 +75,22 @@ memorizer_mind = null return ..() +/datum/memory/serialize_list(list/options, list/semvers) + . = ..() + + .["name"] = name + .["memorizer"] = memorizer + .["story_value"] = story_value + .["memory_flags"] = memory_flags + .["mood_verb"] = mood_verb + .["protagonist_name"] = protagonist_name + .["deuteragonist_name"] = deuteragonist_name + .["antagonist_name"] = antagonist_name + .["where"] = where + + SET_SERIALIZATION_SEMVER(semvers, "1.0.0") + return . + /** * Generates a name for the memory. */ diff --git a/code/datums/mind/_mind.dm b/code/datums/mind/_mind.dm index 5407513712a..2e74de07a51 100644 --- a/code/datums/mind/_mind.dm +++ b/code/datums/mind/_mind.dm @@ -122,6 +122,25 @@ set_current(null) return ..() +/datum/mind/serialize_list(list/options, list/semvers) + . = ..() + + .["key"] = key + .["name"] = name + .["ghostname"] = ghostname + .["memories"] = memories + .["martial_art"] = martial_art + .["antag_datums"] = antag_datums + .["holy_role"] = holy_role + .["special_role"] = special_role + .["assigned_role"] = assigned_role.title + .["current"] = current + + var/mob/enslaved_to = src.enslaved_to?.resolve() + .["enslaved_to"] = enslaved_to + + SET_SERIALIZATION_SEMVER(semvers, "1.0.0") + return . /datum/mind/vv_edit_var(var_name, var_value) switch(var_name) diff --git a/code/game/world.dm b/code/game/world.dm index e2f380c2cfe..181ff3c9098 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -76,6 +76,9 @@ GLOBAL_VAR(restart_counter) // Init the debugger first so we can debug Master init_debugger() + // Create the logger + logger = new + // THAT'S IT, WE'RE DONE, THE. FUCKING. END. Master = new @@ -176,6 +179,14 @@ GLOBAL_VAR(restart_counter) #endif SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(_addtimer), cb, 10 SECONDS)) +/// Returns a list of data about the world state, don't clutter +/world/proc/get_world_state_for_logging() + var/data = list() + data["tick_usage"] = world.tick_usage + data["tick_lag"] = world.tick_lag + data["time"] = world.time + data["timestamp"] = logger.unix_timestamp_string() + return data /world/proc/SetupLogs() var/override_dir = params[OVERRIDE_LOG_DIRECTORY_PARAMETER] @@ -199,7 +210,7 @@ GLOBAL_VAR(restart_counter) GLOB.picture_logging_prefix = "O_[override_dir]_" GLOB.picture_log_directory = "data/picture_logs/[override_dir]" - GLOB.logger.init_logging() + logger.init_logging() var/latest_changelog = file("[global.config.directory]/../html/changelogs/archive/" + time2text(world.timeofday, "YYYY-MM") + ".yml") GLOB.changelog_hash = fexists(latest_changelog) ? md5(latest_changelog) : 0 //for telling if the changelog has changed recently diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 17fbc1adc69..6491135b5fd 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -1474,15 +1474,17 @@ else if(href_list["slowquery"]) if(!check_rights(R_ADMIN)) return + + var/data = list("key" = usr.key) var/answer = href_list["slowquery"] if(answer == "yes") - log_query_debug("[usr.key] | Reported a server hang") if(tgui_alert(usr, "Did you just press any admin buttons?", "Query server hang report", list("Yes", "No")) == "Yes") var/response = input(usr,"What were you just doing?","Query server hang report") as null|text if(response) - log_query_debug("[usr.key] | [response]") + data["response"] = response + logger.Log(LOG_CATEGORY_DEBUG_SQL, "server hang", data) else if(answer == "no") - log_query_debug("[usr.key] | Reported no server hang") + logger.Log(LOG_CATEGORY_DEBUG_SQL, "no server hang", data) else if(href_list["ctf_toggle"]) if(!check_rights(R_ADMIN)) diff --git a/code/modules/logging/categories/log_category_admin.dm b/code/modules/logging/categories/log_category_admin.dm new file mode 100644 index 00000000000..6bc4cbc827d --- /dev/null +++ b/code/modules/logging/categories/log_category_admin.dm @@ -0,0 +1,26 @@ +/datum/log_category/admin + category = LOG_CATEGORY_ADMIN + config_flag = /datum/config_entry/flag/log_admin + +/datum/log_category/admin_dsay + category = LOG_CATEGORY_ADMIN_DSAY + master_category = /datum/log_category/admin + config_flag = /datum/config_entry/flag/log_admin + +/datum/log_category/admin_circuit + category = LOG_CATEGORY_ADMIN_CIRCUIT + master_category = /datum/log_category/admin + config_flag = /datum/config_entry/flag/log_admin + +// private categories // + +/datum/log_category/admin_private + category = LOG_CATEGORY_ADMIN_PRIVATE + config_flag = /datum/config_entry/flag/log_admin + secret = TRUE + +/datum/log_category/admin_asay + category = LOG_CATEGORY_ADMIN_PRIVATE_ASAY + master_category = /datum/log_category/admin_private + config_flag = /datum/config_entry/flag/log_adminchat + secret = TRUE diff --git a/code/modules/logging/categories/log_category_compats.dm b/code/modules/logging/categories/log_category_compats.dm new file mode 100644 index 00000000000..2cf7343f01e --- /dev/null +++ b/code/modules/logging/categories/log_category_compats.dm @@ -0,0 +1,7 @@ +/datum/log_category/game_compat + category = LOG_CATEGORY_COMPAT_GAME + master_category = /datum/log_category/game + config_flag = /datum/config_entry/flag/logging_compat_adminprivate + +/datum/config_entry/flag/logging_compat_adminprivate + default = FALSE diff --git a/code/modules/logging/categories/log_category_debug.dm b/code/modules/logging/categories/log_category_debug.dm new file mode 100644 index 00000000000..417a56773d6 --- /dev/null +++ b/code/modules/logging/categories/log_category_debug.dm @@ -0,0 +1,45 @@ +/datum/log_category/debug + category = LOG_CATEGORY_DEBUG + +/datum/log_category/debug_tgui + category = LOG_CATEGORY_DEBUG_TGUI + master_category = /datum/log_category/debug + +/datum/log_category/debug_sql + category = LOG_CATEGORY_DEBUG_SQL + master_category = /datum/log_category/debug + +/datum/log_category/debug_lua + category = LOG_CATEGORY_DEBUG_LUA + master_category = /datum/log_category/debug + +/datum/log_category/debug_href + category = LOG_CATEGORY_DEBUG_HREF + master_category = /datum/log_category/debug + +// This is not in the debug master category on purpose, do not add it +/datum/log_category/debug_runtime + category = LOG_CATEGORY_RUNTIME + internal_formatting = FALSE + +/datum/log_category/debug_mapping + category = LOG_CATEGORY_DEBUG_MAPPING + master_category = /datum/log_category/debug + +/datum/log_category/debug_job + category = LOG_CATEGORY_DEBUG_JOB + config_flag = /datum/config_entry/flag/log_job_debug + master_category = /datum/log_category/debug + +/datum/log_category/debug_mobtag + category = LOG_CATEGORY_DEBUG_MOBTAG + master_category = /datum/log_category/debug + +/datum/log_category/debug_asset + category = LOG_CATEGORY_DEBUG_ASSET + config_flag = /datum/config_entry/flag/log_asset + master_category = /datum/log_category/debug + +/datum/log_category/debug_qdel + category = LOG_CATEGORY_DEBUG_QDEL + master_category = /datum/log_category/debug diff --git a/code/modules/logging/categories/log_category_game.dm b/code/modules/logging/categories/log_category_game.dm new file mode 100644 index 00000000000..1d29f288984 --- /dev/null +++ b/code/modules/logging/categories/log_category_game.dm @@ -0,0 +1,58 @@ +/datum/log_category/game + category = LOG_CATEGORY_GAME + config_flag = /datum/config_entry/flag/log_game + +/datum/log_category/game_vote + category = LOG_CATEGORY_GAME_VOTE + config_flag = /datum/config_entry/flag/log_vote + master_category = /datum/log_category/game + +/datum/log_category/game_emote + category = LOG_CATEGORY_GAME_EMOTE + config_flag = /datum/config_entry/flag/log_emote + master_category = /datum/log_category/game + +/datum/log_category/game_internet_request + category = LOG_CATEGORY_GAME_INTERNET_REQUEST + config_flag = /datum/config_entry/flag/log_internet_request + master_category = /datum/log_category/game + +/datum/log_category/game_radio_emote + category = LOG_CATEGORY_GAME_RADIO_EMOTE + config_flag = /datum/config_entry/flag/log_emote + master_category = /datum/log_category/game + +/datum/log_category/game_traitor + category = LOG_CATEGORY_GAME_TRAITOR + config_flag = /datum/config_entry/flag/log_traitor + master_category = /datum/log_category/game + +/datum/log_category/game_topic + category = LOG_CATEGORY_GAME_TOPIC + config_flag = /datum/config_entry/flag/log_world_topic + master_category = /datum/log_category/game + +/datum/log_category/game_say + category = LOG_CATEGORY_GAME_SAY + config_flag = /datum/config_entry/flag/log_say + master_category = /datum/log_category/game + +/datum/log_category/game_whisper + category = LOG_CATEGORY_GAME_WHISPER + config_flag = /datum/config_entry/flag/log_whisper + master_category = /datum/log_category/game + +/datum/log_category/game_ooc + category = LOG_CATEGORY_GAME_OOC + config_flag = /datum/config_entry/flag/log_ooc + master_category = /datum/log_category/game + +/datum/log_category/game_prayer + category = LOG_CATEGORY_GAME_PRAYER + config_flag = /datum/config_entry/flag/log_prayer + master_category = /datum/log_category/game + +/datum/log_category/game_access + category = LOG_CATEGORY_GAME_ACCESS + config_flag = /datum/config_entry/flag/log_access + master_category = /datum/log_category/game diff --git a/code/modules/logging/categories/log_category_misc.dm b/code/modules/logging/categories/log_category_misc.dm new file mode 100644 index 00000000000..5cc77fed203 --- /dev/null +++ b/code/modules/logging/categories/log_category_misc.dm @@ -0,0 +1,58 @@ +/datum/log_category/virus + category = LOG_CATEGORY_VIRUS + config_flag = /datum/config_entry/flag/log_virus + +/datum/log_category/tool + category = LOG_CATEGORY_TOOL + config_flag = /datum/config_entry/flag/log_tools + +/datum/log_category/mecha + category = LOG_CATEGORY_MECHA + config_flag = /datum/config_entry/flag/log_mecha + +/datum/log_category/silicon + category = LOG_CATEGORY_SILICON + config_flag = /datum/config_entry/flag/log_silicon + +/datum/log_category/paper + category = LOG_CATEGORY_PAPER + +/datum/log_category/attack + category = LOG_CATEGORY_ATTACK + config_flag = /datum/config_entry/flag/log_attack + +/datum/log_category/economy + category = LOG_CATEGORY_ECONOMY + config_flag = /datum/config_entry/flag/log_econ + +/datum/log_category/manifest + category = LOG_CATEGORY_MANIFEST + config_flag = /datum/config_entry/flag/log_manifest + +/datum/log_category/shuttle + category = LOG_CATEGORY_SHUTTLE + config_flag = /datum/config_entry/flag/log_shuttle + +/datum/log_category/supicious_login + category = LOG_CATEGORY_SUSPICIOUS_LOGIN + config_flag = /datum/config_entry/flag/log_suspicious_login + +/datum/log_category/config + category = LOG_CATEGORY_CONFIG + +/datum/log_category/filter + category = LOG_CATEGORY_FILTER + +/datum/log_category/signal + category = LOG_CATEGORY_SIGNAL + +/datum/log_category/dynamic + category = LOG_CATEGORY_DYNAMIC + +/datum/log_category/telecomms + category = LOG_CATEGORY_TELECOMMS + config_flag = /datum/config_entry/flag/log_telecomms + +/datum/log_category/speech_indicator + category = LOG_CATEGORY_SPEECH_INDICATOR + config_flag = /datum/config_entry/flag/log_speech_indicators diff --git a/code/modules/logging/categories/log_category_pda.dm b/code/modules/logging/categories/log_category_pda.dm new file mode 100644 index 00000000000..aefb8776f20 --- /dev/null +++ b/code/modules/logging/categories/log_category_pda.dm @@ -0,0 +1,11 @@ +/datum/log_category/pda + category = LOG_CATEGORY_PDA + config_flag = /datum/config_entry/flag/log_pda + +/datum/log_category/pda_chat + category = LOG_CATEGORY_PDA_CHAT + master_category = /datum/log_category/pda + +/datum/log_category/pda_comment + category = LOG_CATEGORY_PDA_COMMENT + master_category = /datum/log_category/pda diff --git a/code/modules/logging/categories/log_category_uplink.dm b/code/modules/logging/categories/log_category_uplink.dm new file mode 100644 index 00000000000..f88d224ad3b --- /dev/null +++ b/code/modules/logging/categories/log_category_uplink.dm @@ -0,0 +1,23 @@ +/datum/log_category/uplink + category = LOG_CATEGORY_UPLINK + config_flag = /datum/config_entry/flag/log_uplink + +/datum/log_category/uplink_malf + category = LOG_CATEGORY_UPLINK_MALF + config_flag = /datum/config_entry/flag/log_uplink + master_category = /datum/log_category/uplink + +/datum/log_category/uplink_heretic + category = LOG_CATEGORY_UPLINK_HERETIC + config_flag = /datum/config_entry/flag/log_uplink + master_category = /datum/log_category/uplink + +/datum/log_category/uplink_changeling + category = LOG_CATEGORY_UPLINK_CHANGELING + config_flag = /datum/config_entry/flag/log_uplink + master_category = /datum/log_category/uplink + +/datum/log_category/uplink_spell + category = LOG_CATEGORY_UPLINK_SPELL + config_flag = /datum/config_entry/flag/log_uplink + master_category = /datum/log_category/uplink diff --git a/code/modules/logging/log_category.dm b/code/modules/logging/log_category.dm index 943ebabf3d3..82dcc57d0fe 100644 --- a/code/modules/logging/log_category.dm +++ b/code/modules/logging/log_category.dm @@ -1,32 +1,56 @@ /// The main datum that contains all log entries for a category /datum/log_category - /// The category this datum contains + /// The category name var/category + + /// The schema version of this log category. + /// Expected format of "Major.Minor.Patch" + var/schema_version = LOG_CATEGORY_SCHEMA_VERSION_NOT_SET + + /// The master category that contains this category + var/datum/log_category/master_category + /// If set this config flag is checked to enable this log category var/config_flag - /// List of all entries, in chronological order of when they were added - var/list/entries = list() + + /// Whether or not this log should not be publically visible + var/secret = FALSE + + /// Whether the readable version of the log message is formatted internally instead of by rustg + var/internal_formatting = TRUE + +GENERAL_PROTECT_DATUM(/datum/log_category) /// Backup log category to catch attempts to log to a category that doesn't exist /datum/log_category/backup_category_not_found category = LOG_CATEGORY_NOT_FOUND /// Add an entry to this category. It is very important that any data you provide doesn't hold references to anything! -/datum/log_category/proc/add_entry(message, list/data) - var/list/entry = list( - LOG_ENTRY_MESSAGE = message, - LOG_ENTRY_TIMESTAMP = big_number_to_text(rustg_unix_timestamp()), +/datum/log_category/proc/create_entry(message, list/data, list/semver_store) + var/datum/log_entry/entry = new( + // world state contains raw timestamp + timestamp = logger.human_readable_timestamp(), + category = category, + message = message, + data = data, + semver_store = semver_store, ) - if(data) - entry[LOG_ENTRY_DATA] = data - entries += list(entry) write_entry(entry) /// Allows for category specific file splitting. Needs to accept a null entry for the default file. -/datum/log_category/proc/get_output_file(list/entry) - return "[GLOB.log_directory]/[category].json" +/// If master_category it will always return the output of master_category.get_output_file(entry) +/datum/log_category/proc/get_output_file(list/entry, extension = "log.json") + if(master_category) + return master_category.get_output_file(entry, extension) + if(secret) + return "[GLOB.log_directory]/secret/[category].[extension]" + return "[GLOB.log_directory]/[category].[extension]" -/// Writes an entry to the output file for the category -/datum/log_category/proc/write_entry(list/entry) - rustg_file_append("[json_encode(entry)]\n", get_output_file(entry)) +/// Writes an entry to the output file(s) for the category +/datum/log_category/proc/write_entry(datum/log_entry/entry) + // config isn't loaded? assume we want human readable logs + if(isnull(config) || CONFIG_GET(flag/log_as_human_readable)) + entry.write_readable_entry_to_file(get_output_file(entry, "log"), format_internally = internal_formatting) + + entry.write_entry_to_file(get_output_file(entry)) diff --git a/code/modules/logging/log_entry.dm b/code/modules/logging/log_entry.dm new file mode 100644 index 00000000000..0f47cef6796 --- /dev/null +++ b/code/modules/logging/log_entry.dm @@ -0,0 +1,114 @@ + +// Schema version must always be the very last element in the array. + +// Current Schema: 1.0.0 +// [timestamp, category, message, data, world_state, semver_store, id, schema_version] + +/// A datum which contains log information. +/datum/log_entry + /// Next id to assign to a log entry. + var/static/next_id = 0 + + /// Unique id of the log entry. + var/id + + /// Schema version of the log entry. + var/schema_version = "1.0.0" + + /// Unix timestamp of the log entry. + var/timestamp + + /// Category of the log entry. + var/category + + /// Message of the log entry. + var/message + + /// Data of the log entry; optional. + var/list/data + + /// Semver store of the log entry, used to store the schema of data entries + var/list/semver_store + +GENERAL_PROTECT_DATUM(/datum/log_entry) + +/datum/log_entry/New(timestamp, category, message, list/data, list/semver_store) + ..() + + src.id = next_id++ + src.timestamp = timestamp + src.category = category + src.message = message + with_data(data) + with_semver_store(semver_store) + +/datum/log_entry/proc/with_data(list/data) + if(!isnull(data)) + if(!islist(data)) + src.data = list("data" = data) + stack_trace("Log entry data was not a list, it was [data.type].") + else + src.data = data + return src + +/datum/log_entry/proc/with_semver_store(list/semver_store) + if(isnull(semver_store)) + return + if(!islist(semver_store)) + stack_trace("Log entry semver store was not a list, it was [semver_store.type]. We cannot reliably convert it to a list.") + else + src.semver_store = semver_store + return src + +/// Converts the log entry to a human-readable string. +/datum/log_entry/proc/to_readable_text(format = TRUE) + if(format) + return "\[[timestamp]\] [uppertext(category)]: [message]" + else + return "[message]" + +/// Converts the log entry to a JSON string. +/datum/log_entry/proc/to_json_text() + // I do not trust byond's json encoder, so we're doing it manually + var/list/json_strings = list() + + json_strings += json_encode(timestamp) + + json_strings += json_encode(category) + + json_strings += json_encode(message) + + if(length(data)) + json_strings += json_encode(data) + else + json_strings += "null" + + json_strings += json_encode(world.get_world_state_for_logging()) + + if(length(semver_store)) + json_strings += json_encode(semver_store) + else + json_strings += "null" + + json_strings += "[id]" + + json_strings += json_encode(schema_version) + + return "\[[json_strings.Join(",")]\]" + +/// Writes the log entry to a file. +/datum/log_entry/proc/write_entry_to_file(file) + if(!fexists(file)) + CRASH("Attempted to log to an uninitialized file: [file]") + WRITE_LOG_NO_FORMAT(file, "[to_json_text()]\n") + +/// Writes the log entry to a file as a human-readable string. +/datum/log_entry/proc/write_readable_entry_to_file(file, format_internally = TRUE) + if(!fexists(file)) + CRASH("Attempted to log to an uninitialized file: [file]") + + // If it's being formatted internally we need to manually add a newline + if(format_internally) + WRITE_LOG_NO_FORMAT(file, "[to_readable_text(format = TRUE)]\n") + else + WRITE_LOG(file, "[to_readable_text(format = FALSE)]") diff --git a/code/modules/logging/log_holder.dm b/code/modules/logging/log_holder.dm index 0ab25ccb218..526cf1f9ccb 100644 --- a/code/modules/logging/log_holder.dm +++ b/code/modules/logging/log_holder.dm @@ -1,6 +1,5 @@ -GLOBAL_DATUM_INIT(logger, /datum/log_holder, new) -GLOBAL_PROTECT(logger) +GLOBAL_REAL(logger, /datum/log_holder) /** * Main datum to manage logging actions */ @@ -14,40 +13,58 @@ GLOBAL_PROTECT(logger) var/list/datum/log_category/log_categories /// typecache list for categories that exist but are disabled var/list/disabled_categories + /// category nesting tree for ui purposes + var/list/category_group_tree + + /// list of Log args waiting for processing pending log initialization + var/list/waiting_log_calls + + /// Whether or not logging as human readable text is enabled + var/human_readable_enabled = FALSE var/initialized = FALSE var/shutdown = FALSE +GENERAL_PROTECT_DATUM(/datum/log_holder) + /// Assembles basic information for logging, creating the log category datums and checking for config flags as required /datum/log_holder/proc/init_logging() if(initialized) CRASH("Attempted to call init_logging twice!") - initialized = TRUE round_id = GLOB.round_id - logging_start_timestamp = rustg_unix_timestamp() + logging_start_timestamp = unix_timestamp_string() log_categories = list() disabled_categories = list() - for(var/datum/log_category/category_type as anything in subtypesof(/datum/log_category)) - var/category = initial(category_type.category) - if(!category) - continue - if(category in log_categories) - stack_trace("Found two identical log category type definitions! [category_type]") - continue + human_readable_enabled = CONFIG_GET(flag/log_as_human_readable) - var/config_flag = initial(category_type.config_flag) + category_group_tree = assemble_log_category_tree() + var/config_flag + for(var/datum/log_category/master_category as anything in category_group_tree) + var/list/sub_categories = category_group_tree[master_category] + sub_categories = sub_categories.Copy() + for(var/datum/log_category/sub_category as anything in sub_categories) + config_flag = initial(sub_category.config_flag) + if(config_flag && !config.Get(config_flag)) + disabled_categories[initial(sub_category.category)] = TRUE + sub_categories -= sub_category + continue + + config_flag = initial(master_category.config_flag) if(config_flag && !config.Get(config_flag)) - disabled_categories[category] = TRUE - continue - category_type = log_categories[category] = new category_type - var/list/log_start_entry = list( - LOG_HEADER_CATEGORY = category, - LOG_HEADER_INIT_TIMESTAMP = big_number_to_text(logging_start_timestamp), - LOG_HEADER_ROUND_ID = big_number_to_text(GLOB.round_id), - ) - rustg_file_write("[json_encode(log_start_entry)]\n", category_type.get_output_file(null)) + disabled_categories[initial(master_category.category)] = TRUE + if(!length(sub_categories)) + continue + // enabled, or any of the sub categories are enabled + init_log_category(master_category, sub_categories) + + initialized = TRUE + + // process any waiting log calls and then cut the list + for(var/list/arg_list as anything in waiting_log_calls) + Log(arglist(arg_list)) + waiting_log_calls?.Cut() if(fexists(GLOB.config_error_log)) fcopy(GLOB.config_error_log, "[GLOB.log_directory]/config_error.log") @@ -61,10 +78,120 @@ GLOBAL_PROTECT(logger) CRASH("Attempted to call shutdown_logging twice!") shutdown = TRUE -/// This is Log because log is a byond internal proc +/// Iterates over all log category types to assemble them into a tree of main category -> (sub category)[] while also checking for loops and sanity errors +/datum/log_holder/proc/assemble_log_category_tree() + var/static/list/category_tree + if(category_tree) + return category_tree + + category_tree = list() + var/list/all_types = subtypesof(/datum/log_category) + var/list/known_categories = list() + var/list/sub_categories = list() + + // Assemble the master categories + for(var/datum/log_category/category_type as anything in all_types) + var/category = initial(category_type.category) + if(category in known_categories) + stack_trace("log category type '[category_type]' has duplicate category '[category]', skipping") + continue + + if(!initial(category_type.schema_version)) + stack_trace("log category type '[category_type]' does not have a valid schema version, skipping") + continue + + var/master_category = initial(category_type.master_category) + if(master_category) + sub_categories[master_category] += list(category_type) + continue + category_tree[category_type] = list() + + // Sort the sub categories + for(var/datum/log_category/master as anything in sub_categories) + if(!(master in category_tree)) + stack_trace("log category [master] is an invalid master category as it's a sub category") + continue + for(var/datum/log_category/sub_category as anything in sub_categories[master]) + if(initial(sub_category.secret) != initial(master.secret)) + stack_trace("log category [sub_category] has a secret status that differs from its master category [master]") + category_tree[master] += list(sub_category) + + return category_tree + +/// Initializes the given log category and populates the list of contained categories based on the sub category list +/datum/log_holder/proc/init_log_category(datum/log_category/category_type, list/datum/log_category/sub_categories) + var/datum/log_category/category_instance = new category_type + + var/list/contained_categories = list() + for(var/datum/log_category/sub_category as anything in sub_categories) + sub_category = new sub_category + var/sub_category_actual = sub_category.category + sub_category.master_category = category_instance + log_categories[sub_category_actual] = sub_category + + if(!semver_to_list(sub_category.schema_version)) + stack_trace("log category [sub_category_actual] has an invalid schema version '[sub_category.schema_version]'") + sub_category.schema_version = LOG_CATEGORY_SCHEMA_VERSION_NOT_SET + + contained_categories += sub_category_actual + + log_categories[category_instance.category] = category_instance + + if(!semver_to_list(category_instance.schema_version)) + stack_trace("log category [category_instance.category] has an invalid schema version '[category_instance.schema_version]'") + category_instance.schema_version = LOG_CATEGORY_SCHEMA_VERSION_NOT_SET + + contained_categories += category_instance.category + + var/list/category_header = list( + LOG_HEADER_INIT_TIMESTAMP = logging_start_timestamp, + LOG_HEADER_ROUND_ID = big_number_to_text(GLOB.round_id), + LOG_HEADER_SECRET = category_instance.secret, + LOG_HEADER_CATEGORY_LIST = contained_categories, + LOG_HEADER_CATEGORY = category_instance.category, + ) + + rustg_file_write("[json_encode(category_header)]\n", category_instance.get_output_file(null)) + if(human_readable_enabled) + rustg_file_write("\[[human_readable_timestamp()]\] Starting up round ID [round_id].\n - -------------------------\n", category_instance.get_output_file(null, "log")) + +/datum/log_holder/proc/unix_timestamp_string() // pending change to rust-g + return RUSTG_CALL(RUST_G, "unix_timestamp")() + +/datum/log_holder/proc/human_readable_timestamp(precision = 3) + var/start = time2text(world.timeofday, "YYYY-MM-DD hh:mm:ss") + // now we grab the millis from the rustg timestamp + var/list/timestamp = splittext(unix_timestamp_string(), ".") + var/millis = timestamp[2] + if(length(millis) > precision) + millis = copytext(millis, 1, precision + 1) + return "[start].[millis]" + +/// Adds an entry to the given category, if the category is disabled it will not be logged. +/// If the category does not exist, we will CRASH and log to the error category. +/// the data list is optional and will be recursively json serialized. /datum/log_holder/proc/Log(category, message, list/data) - if(!initialized || shutdown) - CRASH("Attempted to perform logging before initializion or after shutdown!") + // This is Log because log is a byond internal proc + if(shutdown) + stack_trace("Performing logging after shutdown! This might not be functional in the future!") + // but for right now it's fine + + // do not include the message because these go into the runtime log and we might be secret! + if(!istext(message)) + message = "[message]" + stack_trace("Logging with a non-text message") + + if(!category) + category = LOG_CATEGORY_NOT_FOUND + stack_trace("Logging with a null or empty category") + + if(data && !islist(data)) + data = list("data" = data) + stack_trace("Logging with data this is not a list, it will be converted to a list with a single key 'data'") + + if(!initialized) // we are initialized during /world/proc/SetupLogging which is called in /world/New + waiting_log_calls += list(list(category, message, data)) + return if(disabled_categories[category]) return @@ -73,4 +200,49 @@ GLOBAL_PROTECT(logger) if(!log_category) Log(LOG_CATEGORY_NOT_FOUND, message, data) CRASH("Attempted to log to a category that doesn't exist! [category]") - log_category.add_entry(message, data) + + var/list/semver_store = null + if(length(data)) + semver_store = list() + data = recursive_jsonify(data, semver_store) + log_category.create_entry(message, data, semver_store) + +/// Recursively converts an associative list of datums into their jsonified(list) form +/datum/log_holder/proc/recursive_jsonify(list/data_list, list/semvers) + if(isnull(data_list)) + return null + + var/list/jsonified_list = list() + for(var/key in data_list) + var/datum/data = data_list[key] + + if(isnull(data)) + // do nothing - nulls are allowed + + else if(islist(data)) + data = recursive_jsonify(data, semvers) + + else if(isdatum(data)) + var/list/options_list = list( + SCHEMA_VERSION = LOG_CATEGORY_SCHEMA_VERSION_NOT_SET, + ) + + var/list/serialization_data = data.serialize_list(options_list, semvers) + var/current_semver = semvers[data.type] + if(!semver_to_list(current_semver)) + stack_trace("serialization of data had an invalid semver") + semvers[data.type] = LOG_CATEGORY_SCHEMA_VERSION_NOT_SET + + if(!length(serialization_data)) // serialize_list wasn't implemented, and errored + stack_trace("serialization data was empty") + continue + + data = recursive_jsonify(serialization_data, semvers) + + if(islist(data) && !length(data)) + stack_trace("recursive_jsonify got an empty list after serialization") + continue + + jsonified_list[key] = data + + return jsonified_list diff --git a/code/modules/mining/machine_silo.dm b/code/modules/mining/machine_silo.dm index 2077428bd34..f76cdcde796 100644 --- a/code/modules/mining/machine_silo.dm +++ b/code/modules/mining/machine_silo.dm @@ -241,7 +241,7 @@ GLOBAL_LIST_EMPTY(silo_access_logs) "raw_materials" = get_raw_materials(""), "direction" = amount < 0 ? "withdrawn" : "deposited", ) - GLOB.logger.Log( + logger.Log( LOG_CATEGORY_SILO, "[machine_name] in \[[AREACOORD(M)]\] [action] [abs(amount)]x [noun] | [get_raw_materials("")]", data, diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 14b2e758c68..02de4d93ae0 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -109,6 +109,17 @@ . = ..() tag = "mob_[next_mob_id++]" +/mob/serialize_list(list/options, list/semvers) + . = ..() + + .["tag"] = tag + .["name"] = name + .["ckey"] = ckey + .["key"] = key + + SET_SERIALIZATION_SEMVER(semvers, "1.0.0") + return . + /** * set every hud image in the given category active so other people with the given hud can see it. * Arguments: diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 66e2e18350b..1be76be1f35 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -513,7 +513,7 @@ "name" = offhand.name, ) - GLOB.logger.Log( + logger.Log( LOG_CATEGORY_TARGET_ZONE_SWITCH, "[key_name(src)] manually changed selected zone", data, diff --git a/code/modules/photography/_pictures.dm b/code/modules/photography/_pictures.dm index e6df79d0b6c..8c949892dbf 100644 --- a/code/modules/photography/_pictures.dm +++ b/code/modules/photography/_pictures.dm @@ -58,8 +58,9 @@ ic.Blend(small_img,ICON_OVERLAY, 13, 13) picture_icon = ic -/datum/picture/serialize_list(list/options) - . = list() +/datum/picture/serialize_list(list/options, list/semvers) + . = ..() + .["id"] = id .["desc"] = picture_desc .["name"] = picture_name @@ -69,9 +70,19 @@ .["blueprints"] = has_blueprints .["logpath"] = logpath + SET_SERIALIZATION_SEMVER(semvers, "1.0.0") + return . + /datum/picture/deserialize_list(list/input, list/options) + if((SCHEMA_VERSION in options) && (options[SCHEMA_VERSION] != "1.0.0")) + CRASH("Invalid schema version for datum/picture: [options[SCHEMA_VERSION]] (expected 1.0.0)") + . = ..() + if(!.) + return . + if(!input["logpath"] || !fexists(input["logpath"]) || !input["id"] || !input["pixel_size_x"] || !input["pixel_size_y"]) - return + return FALSE + picture_image = icon(file(input["logpath"])) logpath = input["logpath"] id = input["id"] @@ -85,7 +96,6 @@ picture_desc = input["desc"] if(input["name"]) picture_name = input["name"] - return src /proc/load_photo_from_disk(id, location) var/datum/picture/P = load_picture_from_disk(id) diff --git a/config/config.txt b/config/config.txt index cfb9391ae96..629da4ad8d7 100644 --- a/config/config.txt +++ b/config/config.txt @@ -3,6 +3,7 @@ $include game_options.txt $include dbconfig.txt $include comms.txt +$include logging.txt $include resources.txt $include interviews.txt $include lua.txt @@ -80,103 +81,6 @@ ENABLE_LOCALHOST_RANK ## If USE_LOW_LIVING_HOUR_INTERN is unhashed, players under this number of living hours have [Intern] added to their ID card. If this isn't set, uses USE_EXP_RESTRICTIONS_HEADS_HOURS instead. If that isn't set, it finally defaults to a hardcoded fallback of 15 hours. #USE_LOW_LIVING_HOUR_INTERN_HOURS 50 -## log OOC channel -LOG_OOC - -## log client Say -LOG_SAY - -## log admin actions -LOG_ADMIN - -## log admin chat -LOG_ADMINCHAT - -## log asset debug logs -#LOG_ASSET - -## log client access (logon/logoff) -LOG_ACCESS - -## Enables log entries for logins that failed due to suspicious circumstances (banned player, CID randomiser, spoofed BYOND version, etc.) to a dedicated file. -LOG_SUSPICIOUS_LOGIN - -## log game actions (start of round, results, etc.) -LOG_GAME - -## log player votes -LOG_VOTE - -## log manual target zone switching -LOG_ZONE_SWITCH - -## log client Whisper -LOG_WHISPER - -## log emotes -LOG_EMOTE - -## log attack messages -LOG_ATTACK - -## log pda messages -LOG_PDA - -## log uplink purchases -LOG_UPLINK - -## log telecomms messages -LOG_TELECOMMS - -## log prayers -LOG_PRAYER - -## Log Music requests -LOG_INTERNET_REQUEST - -## log silicons -LOG_SILICON - -## log economy actions -LOG_ECON - -## log traitor objectives -LOG_TRAITOR - -## log crew manifest to separate file -LOG_MANIFEST - -## log job divide debugging information -#LOG_JOB_DEBUG - -## log all world.Topic() calls -# LOG_WORLD_TOPIC - -## enables use of the proc twitterize() that lets you take a large list of strings and turn it into a JSON file of tweet sized strings. -## As an example of how this could be """useful""" look towards Poly (https://twitter.com/Poly_the_Parrot) -# LOG_TWITTER - -## Enable logging pictures -LOG_PICTURES - -## log mecha actions -# LOG_MECHA - -## log virus and actions -LOG_VIRUS - -## log shuttle actions -LOG_SHUTTLE - -## log tool interactions [/atom/proc/tool_act] -LOG_TOOLS - -## Log all timers on timer auto reset -# LOG_TIMERS_ON_BUCKET_RESET - -## log speech indicators -LOG_SPEECH_INDICATORS - ##Log camera pictures - Must have picture logging enabled PICTURE_LOGGING_CAMERA diff --git a/config/logging.txt b/config/logging.txt new file mode 100644 index 00000000000..2635197a796 --- /dev/null +++ b/config/logging.txt @@ -0,0 +1,103 @@ +### Config Flags for logging, compatibility flags are at the top + +## Whether or not admin/adminprivate logging is also sent to LOG_CATEGORY_GAME for legacy log parsers +LOGGING_COMPAT_ADMINPRIVATE + +## Should we also log entries as human readable +LOG_AS_HUMAN_READABLE + +### Actual logging flags + +## log client access (logon/logoff) +LOG_ACCESS + +## log admin actions +LOG_ADMIN + +## log admin chat +LOG_ADMINCHAT + +## log asset debug logs +# LOG_ASSET + +## log attack messages +LOG_ATTACK + +## log economy actions +LOG_ECON + +## log emotes +LOG_EMOTE + +## log game actions (start of round, results, etc.) +LOG_GAME + +## log job divide debugging information +# LOG_JOB_DEBUG + +## log crew manifest to separate file +LOG_MANIFEST + +## log mecha actions +# LOG_MECHA + +## log OOC channel +LOG_OOC + +## log pda messages +LOG_PDA + +## Enable logging pictures +LOG_PICTURES + +## log prayers +LOG_PRAYER + +## log client Say +LOG_SAY + +## log shuttle actions +LOG_SHUTTLE + +## log silicons +LOG_SILICON + +## log speech indicators +LOG_SPEECH_INDICATORS + +## Enables log entries for logins that failed due to suspicious circumstances (banned player, CID randomiser, spoofed BYOND version, etc.) to a dedicated file. +LOG_SUSPICIOUS_LOGIN + +## log telecomms messages +LOG_TELECOMMS + +## Log all timers on timer auto reset +# LOG_TIMERS_ON_BUCKET_RESET + +## log tool interactions [/atom/proc/tool_act] +LOG_TOOLS + +## log traitor objectives +LOG_TRAITOR + +## enables use of the proc twitterize() that lets you take a large list of strings and turn it into a JSON file of tweet sized strings. +## As an example of how this could be """useful""" look towards Poly (https://twitter.com/Poly_the_Parrot) +# LOG_TWITTER + +## log uplink purchases +LOG_UPLINK + +## log virus and actions +LOG_VIRUS + +## log player votes +LOG_VOTE + +## log client Whisper +LOG_WHISPER + +## log all world.Topic() calls +# LOG_WORLD_TOPIC + +## log manual target zone switching +LOG_ZONE_SWITCH diff --git a/tgstation.dme b/tgstation.dme index 9520e54f474..2be4a8edcc7 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -3734,9 +3734,17 @@ #include "code\modules\lighting\lighting_turf.dm" #include "code\modules\lighting\static_lighting_area.dm" #include "code\modules\logging\log_category.dm" +#include "code\modules\logging\log_entry.dm" #include "code\modules\logging\log_holder.dm" +#include "code\modules\logging\categories\log_category_admin.dm" +#include "code\modules\logging\categories\log_category_compats.dm" +#include "code\modules\logging\categories\log_category_debug.dm" +#include "code\modules\logging\categories\log_category_game.dm" +#include "code\modules\logging\categories\log_category_misc.dm" +#include "code\modules\logging\categories\log_category_pda.dm" #include "code\modules\logging\categories\log_category_silo.dm" #include "code\modules\logging\categories\log_category_target_zone_switch.dm" +#include "code\modules\logging\categories\log_category_uplink.dm" #include "code\modules\mafia\_defines.dm" #include "code\modules\mafia\controller.dm" #include "code\modules\mafia\map_pieces.dm"