Files
CHOMPStation2/code/_helpers/logging/_logging.dm
2025-09-14 20:05:26 +02:00

275 lines
7.3 KiB
Plaintext

//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)
#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]")
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
#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_TRACKING_LOG_APART)
#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
/**
* Generic logging helper
*
* reads the type of the log
* and writes it to the respective log file
* unless log_globally is FALSE
* Arguments:
* * message - The message being logged
* * message_type - the type of log the message is(ATTACK, SAY, etc)
* * color - color of the log text
* * log_globally - boolean checking whether or not we write this log to the log file
*/
/atom/proc/log_message(message, message_type, color = null, log_globally = TRUE, list/data)
if(!log_globally)
return
var/log_text = "[key_name_and_tag(src)] [message] [loc_name(src)]"
switch(message_type)
/// ship both attack logs and victim logs to the end of round attack.log just to ensure we don't lose information
if(LOG_ATTACK, LOG_VICTIM)
log_attack(log_text, data)
if(LOG_SAY)
log_say(log_text, data)
if(LOG_WHISPER)
log_whisper(log_text, data)
if(LOG_EMOTE)
log_emote(log_text, data)
if(LOG_PDA)
log_pda(log_text, data)
if(LOG_OOC)
log_ooc(log_text, data)
if(LOG_LOOC)
log_looc(log_text, data)
if(LOG_ADMIN)
log_admin(log_text, data)
if(LOG_ADMIN_PRIVATE)
log_admin_private(log_text, data)
if(LOG_ASAY)
log_adminsay(log_text, data)
if(LOG_GAME)
log_game(log_text, data)
else
stack_trace("Invalid individual logging type: [message_type]. Defaulting to [LOG_GAME] (LOG_GAME).")
log_game(log_text, data)
/* 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()
logger.shutdown_logging()
/* 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(isdatum(whom))
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='byond://?priv_msg=[C.getStealthKey()]'>"
. += "Administrator"
else
if(include_link)
. += "<a href='byond://?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/key_name_and_tag(whom, include_link = null, include_name = TRUE)
var/tag = "!tagless!" // whom can be null in key_name() so lets set this as a safety
if(isatom(whom))
var/atom/subject = whom
tag = subject.tag
return "[key_name(whom, include_link, include_name)] ([tag])"
/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 (?, ?, ?))"
// Virgo specific Helper procs for building detailed log lines
//
// These procs must not fail under ANY CIRCUMSTANCES!
// Are these even still required?
/proc/report_progress(var/progress_message)
admin_notice(span_boldannounce("[progress_message]"), R_DEBUG)
log_world(progress_message)
/datum/proc/log_info_line()
return "[src] ([type])"
/atom/log_info_line()
. = ..()
var/turf/t = get_turf(src)
if(istype(t))
return "[.] @ [t.log_info_line()]"
else if(loc)
return "[.] @ ([loc]) (0,0,0) ([loc.type])"
else
return "[.] @ (NULL) (0,0,0) (NULL)"
/turf/log_info_line()
return "([src]) ([x],[y],[z]) ([type])"
/mob/log_info_line()
return "[..()] (ckey=[ckey])"
/proc/log_info_line(var/datum/d)
if(!d)
return "*null*"
if(!istype(d))
return json_encode(d)
return d.log_info_line()
/mob/proc/simple_info_line()
return "[key_name(src)] ([x],[y],[z])"
/client/proc/simple_info_line()
return "[key_name(src)] ([mob.x],[mob.y],[mob.z])"