Files
Bubberstation/code/__HELPERS/_logging.dm
SkyratBot 1d2d7d2b6c [MIRROR] Fixes an issue with tags for mobs not properly being handled correctly and adds logging to admin circuits [MDB IGNORE] (#11441)
* Fixes an issue with tags for mobs not properly being handled correctly and adds logging to admin circuits (#64821)

Tags would bug out due to how the 'Save Shell' component would copy all the variables on an object except a few restricted ones, though this proved to be very buggy.
The duplicator part has been removed and more proper logging has been added.
To compensate for the duplicator part being removed, admin circuit display names will now replace the entire name of the shell.

* Fixes an issue with tags for mobs not properly being handled correctly and adds logging to admin circuits

Co-authored-by: Watermelon914 <37270891+Watermelon914@users.noreply.github.com>
2022-02-11 10:25:58 +00:00

436 lines
13 KiB
Plaintext

//wrapper macros for easier grepping
#define DIRECT_OUTPUT(A, B) A << B
#define DIRECT_INPUT(A, B) A >> B
#define SEND_IMAGE(target, image) DIRECT_OUTPUT(target, image)
#define SEND_SOUND(target, sound) DIRECT_OUTPUT(target, sound)
#define SEND_TEXT(target, text) DIRECT_OUTPUT(target, text)
#define WRITE_FILE(file, text) DIRECT_OUTPUT(file, text)
#define READ_FILE(file, text) DIRECT_INPUT(file, text)
//This is an external call, "true" and "false" are how rust parses out booleans
#define WRITE_LOG(log, text) rustg_log_write(log, text, "true")
#define WRITE_LOG_NO_FORMAT(log, text) rustg_log_write(log, text, "false")
//print a warning message to world.log
#define WARNING(MSG) warning("[MSG] in [__FILE__] at line [__LINE__] src: [UNLINT(src)] usr: [usr].")
/proc/warning(msg)
msg = "## WARNING: [msg]"
log_world(msg)
//not an error or a warning, but worth to mention on the world log, just in case.
#define NOTICE(MSG) notice(MSG)
/proc/notice(msg)
msg = "## NOTICE: [msg]"
log_world(msg)
//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]")
GLOBAL_LIST_INIT(testing_global_profiler, list("_PROFILE_NAME" = "Global"))
// we don't really check if a word or name is used twice, be aware of that
#define testing_profile_start(NAME, LIST) LIST[NAME] = world.timeofday
#define testing_profile_current(NAME, LIST) round((world.timeofday - LIST[NAME])/10,0.1)
#define testing_profile_output(NAME, LIST) testing("[LIST["_PROFILE_NAME"]] profile of [NAME] is [testing_profile_current(NAME,LIST)]s")
#define testing_profile_output_all(LIST) { for(var/_NAME in LIST) { testing_profile_current(,_NAME,LIST); }; };
#else
#define testing(msg)
#define testing_profile_start(NAME, LIST)
#define testing_profile_current(NAME, LIST)
#define testing_profile_output(NAME, LIST)
#define testing_profile_output_all(LIST)
#endif
#define testing_profile_global_start(NAME) testing_profile_start(NAME,GLOB.testing_global_profiler)
#define testing_profile_global_current(NAME) testing_profile_current(NAME, GLOB.testing_global_profiler)
#define testing_profile_global_output(NAME) testing_profile_output(NAME, GLOB.testing_global_profiler)
#define testing_profile_global_output_all testing_profile_output_all(GLOB.testing_global_profiler)
#define testing_profile_local_init(PROFILE_NAME) var/list/_timer_system = list( "_PROFILE_NAME" = PROFILE_NAME, "_start_of_proc" = world.timeofday )
#define testing_profile_local_start(NAME) testing_profile_start(NAME, _timer_system)
#define testing_profile_local_current(NAME) testing_profile_current(NAME, _timer_system)
#define testing_profile_local_output(NAME) testing_profile_output(NAME, _timer_system)
#define testing_profile_local_output_all testing_profile_output_all(_timer_system)
#if defined(UNIT_TESTS) || defined(SPACEMAN_DMM)
/proc/log_test(text)
WRITE_LOG(GLOB.test_log, text)
SEND_TEXT(world.log, text)
#endif
#if defined(REFERENCE_DOING_IT_LIVE)
#define log_reftracker(msg) log_harddel("## REF SEARCH [msg]")
/proc/log_harddel(text)
WRITE_LOG(GLOB.harddel_log, text)
#elif defined(REFERENCE_TRACKING) // Doing it locally
#define log_reftracker(msg) log_world("## REF SEARCH [msg]")
#else //Not tracking at all
#define log_reftracker(msg)
#endif
/* Items with ADMINPRIVATE prefixed are stripped from public logs. */
/proc/log_admin(text)
GLOB.admin_log.Add(text)
if (CONFIG_GET(flag/log_admin))
WRITE_LOG(GLOB.world_game_log, "ADMIN: [text]")
/proc/log_admin_circuit(text)
GLOB.admin_log.Add(text)
if(CONFIG_GET(flag/log_admin))
WRITE_LOG(GLOB.world_game_log, "ADMIN: CIRCUIT: [text]")
/proc/log_admin_private(text)
GLOB.admin_log.Add(text)
if (CONFIG_GET(flag/log_admin))
WRITE_LOG(GLOB.world_game_log, "ADMINPRIVATE: [text]")
/proc/log_adminsay(text)
GLOB.admin_log.Add(text)
if (CONFIG_GET(flag/log_adminchat))
WRITE_LOG(GLOB.world_game_log, "ADMINPRIVATE: ASAY: [text]")
/proc/log_dsay(text)
if (CONFIG_GET(flag/log_adminchat))
WRITE_LOG(GLOB.world_game_log, "ADMIN: DSAY: [text]")
/* All other items are public. */
/proc/log_game(text)
if (CONFIG_GET(flag/log_game))
WRITE_LOG(GLOB.world_game_log, "GAME: [text]")
/proc/log_mecha(text)
if (CONFIG_GET(flag/log_mecha))
WRITE_LOG(GLOB.world_mecha_log, "MECHA: [text]")
/proc/log_virus(text)
if (CONFIG_GET(flag/log_virus))
WRITE_LOG(GLOB.world_virus_log, "VIRUS: [text]")
/proc/log_cloning(text, mob/initiator)
if(CONFIG_GET(flag/log_cloning))
WRITE_LOG(GLOB.world_cloning_log, "CLONING: [text]")
/proc/log_paper(text)
WRITE_LOG(GLOB.world_paper_log, "PAPER: [text]")
/proc/log_asset(text)
if(CONFIG_GET(flag/log_asset))
WRITE_LOG(GLOB.world_asset_log, "ASSET: [text]")
/proc/log_access(text)
if (CONFIG_GET(flag/log_access))
WRITE_LOG(GLOB.world_game_log, "ACCESS: [text]")
/proc/log_silicon(text)
if (CONFIG_GET(flag/log_silicon))
WRITE_LOG(GLOB.world_silicon_log, "SILICON: [text]")
/proc/log_tool(text, mob/initiator)
if(CONFIG_GET(flag/log_tools))
WRITE_LOG(GLOB.world_tool_log, "TOOL: [text]")
/**
* Writes to a special log file if the log_suspicious_login config flag is set,
* which is intended to contain all logins that failed under suspicious circumstances.
*
* 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]")
if(access_log_mirror)
log_access(text)
/proc/log_attack(text)
if (CONFIG_GET(flag/log_attack))
WRITE_LOG(GLOB.world_attack_log, "ATTACK: [text]")
/proc/log_econ(text)
if (CONFIG_GET(flag/log_econ))
WRITE_LOG(GLOB.world_econ_log, "MONEY: [text]")
/proc/log_traitor(text)
if (CONFIG_GET(flag/log_econ))
WRITE_LOG(GLOB.world_game_log, "TRAITOR: [text]")
/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"]")
/proc/log_bomber(atom/user, details, atom/bomb, additional_details, message_admins = TRUE)
var/bomb_message = "[details][bomb ? " [bomb.name] at [AREACOORD(bomb)]": ""][additional_details ? " [additional_details]" : ""]."
if(user)
user.log_message(bomb_message, LOG_ATTACK) //let it go to individual logs as well as the game log
bomb_message = "[key_name(user)] at [AREACOORD(user)] [bomb_message]"
else
log_game(bomb_message)
GLOB.bombers += bomb_message
if(message_admins)
message_admins("[user ? "[ADMIN_LOOKUPFLW(user)] at [ADMIN_VERBOSEJMP(user)] " : ""][details][bomb ? " [bomb.name] at [ADMIN_VERBOSEJMP(bomb)]": ""][additional_details ? " [additional_details]" : ""].")
/// Logs the contents of the gasmix to the game log, prefixed by text
/proc/log_atmos(text, datum/gas_mixture/mix)
var/message = text
message += "TEMP=[mix.temperature],MOL=[mix.total_moles()],VOL=[mix.volume]"
for(var/key in mix.gases)
var/list/gaslist = mix.gases[key]
message += "[gaslist[GAS_META][META_GAS_ID]]=[gaslist[MOLES]];"
log_game(message)
/proc/log_say(text)
if (CONFIG_GET(flag/log_say))
WRITE_LOG(GLOB.world_game_log, "SAY: [text]")
/proc/log_ooc(text)
if (CONFIG_GET(flag/log_ooc))
WRITE_LOG(GLOB.world_game_log, "OOC: [text]")
/proc/log_whisper(text)
if (CONFIG_GET(flag/log_whisper))
WRITE_LOG(GLOB.world_game_log, "WHISPER: [text]")
/proc/log_emote(text)
if (CONFIG_GET(flag/log_emote))
WRITE_LOG(GLOB.world_game_log, "EMOTE: [text]")
/proc/log_radio_emote(text)
if (CONFIG_GET(flag/log_emote))
WRITE_LOG(GLOB.world_game_log, "RADIOEMOTE: [text]")
/proc/log_prayer(text)
if (CONFIG_GET(flag/log_prayer))
WRITE_LOG(GLOB.world_game_log, "PRAY: [text]")
/proc/log_pda(text)
if (CONFIG_GET(flag/log_pda))
WRITE_LOG(GLOB.world_pda_log, "PDA: [text]")
/proc/log_comment(text)
if (CONFIG_GET(flag/log_pda))
//reusing the PDA option because I really don't think news comments are worth a config option
WRITE_LOG(GLOB.world_pda_log, "COMMENT: [text]")
/proc/log_uplink(text)
if (CONFIG_GET(flag/log_uplink))
WRITE_LOG(GLOB.world_uplink_log, "UPLINK: [text]")
/proc/log_spellbook(text)
if (CONFIG_GET(flag/log_uplink))
WRITE_LOG(GLOB.world_uplink_log, "SPELLBOOK: [text]")
/proc/log_heretic_knowledge(text)
if (CONFIG_GET(flag/log_uplink))
WRITE_LOG(GLOB.world_uplink_log, "HERETIC RESEARCH: [text]")
/proc/log_telecomms(text)
if (CONFIG_GET(flag/log_telecomms))
WRITE_LOG(GLOB.world_telecomms_log, "TCOMMS: [text]")
/proc/log_chat(text)
if (CONFIG_GET(flag/log_pda))
//same thing here
WRITE_LOG(GLOB.world_pda_log, "CHAT: [text]")
/proc/log_vote(text)
if (CONFIG_GET(flag/log_vote))
WRITE_LOG(GLOB.world_game_log, "VOTE: [text]")
/proc/log_shuttle(text)
if (CONFIG_GET(flag/log_shuttle))
WRITE_LOG(GLOB.world_shuttle_log, "SHUTTLE: [text]")
/proc/log_topic(text)
WRITE_LOG(GLOB.world_game_log, "TOPIC: [text]")
/proc/log_href(text)
WRITE_LOG(GLOB.world_href_log, "HREF: [text]")
/proc/log_mob_tag(text)
WRITE_LOG(GLOB.world_mob_tag_log, "TAG: [text]")
/proc/log_sql(text)
WRITE_LOG(GLOB.sql_error_log, "SQL: [text]")
/proc/log_qdel(text)
WRITE_LOG(GLOB.world_qdel_log, "QDEL: [text]")
/proc/log_query_debug(text)
WRITE_LOG(GLOB.query_debug_log, "SQL: [text]")
/proc/log_job_debug(text)
if (CONFIG_GET(flag/log_job_debug))
WRITE_LOG(GLOB.world_job_debug_log, "JOB: [text]")
/* Log to both DD and the logfile. */
/proc/log_world(text)
#ifdef USE_CUSTOM_ERROR_HANDLER
WRITE_LOG(GLOB.world_runtime_log, text)
#endif
SEND_TEXT(world.log, text)
/* Log to the logfile only. */
/proc/log_runtime(text)
WRITE_LOG(GLOB.world_runtime_log, text)
/* Rarely gets called; just here in case the config breaks. */
/proc/log_config(text)
WRITE_LOG(GLOB.config_error_log, text)
SEND_TEXT(world.log, text)
/proc/log_mapping(text, skip_world_log)
WRITE_LOG(GLOB.world_map_error_log, text)
if(skip_world_log)
return
SEND_TEXT(world.log, text)
/proc/log_perf(list/perf_info)
. = "[perf_info.Join(",")]\n"
WRITE_LOG_NO_FORMAT(GLOB.perf_log, .)
/**
* Appends a tgui-related log entry. All arguments are optional.
*/
/proc/log_tgui(user, message, context,
datum/tgui_window/window,
datum/src_object)
var/entry = ""
// Insert user info
if(!user)
entry += "<nobody>"
else if(istype(user, /mob))
var/mob/mob = user
entry += "[mob.ckey] (as [mob] at [mob.x],[mob.y],[mob.z])"
else if(istype(user, /client))
var/client/client = user
entry += "[client.ckey]"
// Insert context
if(context)
entry += " in [context]"
else if(window)
entry += " in [window.id]"
// Resolve src_object
if(!src_object && window?.locked_by)
src_object = window.locked_by.src_object
// Insert src_object info
if(src_object)
entry += "\nUsing: [src_object.type] [REF(src_object)]"
// Insert message
if(message)
entry += "\n[message]"
WRITE_LOG(GLOB.tgui_log, entry)
/* For logging round startup. */
/proc/start_log(log)
WRITE_LOG(log, "Starting up round ID [GLOB.round_id].\n-------------------------")
/* 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()
/* Helper procs for building detailed log lines */
/proc/key_name(whom, include_link = null, include_name = TRUE)
var/mob/M
var/client/C
var/key
var/ckey
var/fallback_name
if(!whom)
return "*null*"
if(istype(whom, /client))
C = whom
M = C.mob
key = C.key
ckey = C.ckey
else if(ismob(whom))
M = whom
C = M.client
key = M.key
ckey = M.ckey
else if(istext(whom))
key = whom
ckey = ckey(whom)
C = GLOB.directory[ckey]
if(C)
M = C.mob
else if(istype(whom,/datum/mind))
var/datum/mind/mind = whom
key = mind.key
ckey = ckey(key)
if(mind.current)
M = mind.current
if(M.client)
C = M.client
else
fallback_name = mind.name
else // Catch-all cases if none of the types above match
var/swhom = null
if(istype(whom, /atom))
var/atom/A = whom
swhom = "[A.name]"
else if(istype(whom, /datum))
swhom = "[whom]"
if(!swhom)
swhom = "*invalid*"
return "\[[swhom]\]"
. = ""
if(!ckey)
include_link = FALSE
if(key)
if(C?.holder && C.holder.fakekey && !include_name)
if(include_link)
. += "<a href='?priv_msg=[C.findStealthKey()]'>"
. += "Administrator"
else
if(include_link)
. += "<a href='?priv_msg=[ckey]'>"
. += key
if(!C)
. += "\[DC\]"
if(include_link)
. += "</a>"
else
. += "*no key*"
if(include_name)
if(M)
if(M.real_name)
. += "/([M.real_name])"
else if(M.name)
. += "/([M.name])"
else if(fallback_name)
. += "/([fallback_name])"
return .
/proc/key_name_admin(whom, include_name = TRUE)
return key_name(whom, TRUE, include_name)
/proc/loc_name(atom/A)
if(!istype(A))
return "(INVALID LOCATION)"
var/turf/T = A
if (!istype(T))
T = get_turf(A)
if(istype(T))
return "([AREACOORD(T)])"
else if(A.loc)
return "(UNKNOWN (?, ?, ?))"