[MIRROR] JSON Logging Refactor (#11623)

Co-authored-by: Selis <12716288+ItsSelis@users.noreply.github.com>
Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com>
This commit is contained in:
CHOMPStation2StaffMirrorBot
2025-09-14 11:05:26 -07:00
committed by GitHub
parent 272afa33c8
commit 5a62077f2c
425 changed files with 4081 additions and 2568 deletions
+129 -141
View File
@@ -3,205 +3,193 @@
// There are 3 different types used here:
//
// - ErrorCache keeps track of all error sources, as well as all individually
// - error_cache keeps track of all error sources, as well as all individually
// logged errors. Only one instance of this datum should ever exist, and it's
// right here:
#ifdef DEBUG
var/global/datum/ErrorViewer/ErrorCache/error_cache = new()
#ifdef USE_CUSTOM_ERROR_HANDLER
GLOBAL_DATUM_INIT(error_cache, /datum/error_viewer/error_cache, new)
#else
// If debugging is disabled, there's nothing useful to log, so don't bother.
var/global/datum/ErrorViewer/ErrorCache/error_cache = null
GLOBAL_DATUM(error_cache, /datum/error_viewer/error_cache)
#endif
// - ErrorSource datums exist for each line (of code) that generates an error,
// - error_source datums exist for each line (of code) that generates an error,
// and keep track of all errors generated by that line.
//
// - ErrorEntry datums exist for each logged error, and keep track of all
// - error_entry datums exist for each logged error, and keep track of all
// relevant info about that error.
// Common vars and procs are kept at the ErrorViewer level
/datum/ErrorViewer/
// Common vars and procs are kept at the error_viewer level
/datum/error_viewer
var/name = ""
/datum/ErrorViewer/proc/browseTo(var/user, var/html)
if(user)
var/datum/browser/popup = new(user, "error_viewer", "Runtime Viewer", 900, 500)
popup.add_head_content({"<style>
.runtime{
background-color: #171717;
border: solid 1px #202020;
font-family:'Courier New',monospace;
font-size:9pt;
color: #DDDDDD;
}
p.runtime_list{
font-family:'Courier New',monospace;
font-size:9pt;
margin: 0;
padding: 0;
text-indent:-13ch;
margin-left:13ch;
white-space:nowrap;
}
</style>"})
popup.set_content(html)
popup.open(0)
/datum/error_viewer/proc/browse_to(client/user, html)
var/datum/browser/browser = new(user.mob, "error_viewer", null, 600, 400)
browser.set_content(html)
browser.set_head_content({"
<style>
.runtime
{
background-color: #171717;
border: solid 1px #202020;
font-family: "Courier New";
padding-left: 10px;
color: #CCCCCC;
}
.runtime_line
{
margin-bottom: 10px;
display: inline-block;
}
</style>
"})
browser.open()
/datum/ErrorViewer/proc/buildHeader(var/datum/ErrorViewer/back_to, var/linear, var/refreshable)
// Common starter HTML for showTo
var/html = ""
/datum/error_viewer/proc/build_header(datum/error_viewer/back_to, linear)
// Common starter HTML for show_to
if(istype(back_to))
html += "[back_to.makeLink("<<<", null, linear)] "
if(refreshable)
html += "[makeLink("Refresh", null, linear)]"
if(html)
html += "<br><br>"
return html
. = ""
/datum/ErrorViewer/proc/showTo(var/user, var/datum/ErrorViewer/back_to, var/linear)
if (istype(back_to))
. += back_to.make_link("<b>&lt;&lt;&lt;</b>", null, linear)
. += "[make_link("Refresh")]<br><br>"
/datum/error_viewer/proc/show_to(user, datum/error_viewer/back_to, linear)
// Specific to each child type
return
/datum/ErrorViewer/proc/makeLink(var/linktext, var/datum/ErrorViewer/back_to, var/linear)
/datum/error_viewer/proc/make_link(linktext, datum/error_viewer/back_to, linear)
var/back_to_param = ""
if(!linktext)
if (!linktext)
linktext = name
if(istype(back_to))
back_to_param = ";viewruntime_backto=\ref[back_to]"
if(linear)
if (istype(back_to))
back_to_param = ";viewruntime_backto=[REF(back_to)]"
if (linear)
back_to_param += ";viewruntime_linear=1"
return "<A href='byond://?src=\ref[src];[HrefToken()];viewruntime=\ref[src][back_to_param]'>[html_encode(linktext)]</A>"
/datum/ErrorViewer/Topic(href, href_list)
if(..())
return 1
if(href_list["viewruntime_backto"])
showTo(usr, locate(href_list["viewruntime_backto"]), href_list["viewruntime_linear"])
else
showTo(usr, null, href_list["viewruntime_linear"])
return "<a href='byond://?_src_=holder;[HrefToken()];viewruntime=[REF(src)][back_to_param]'>[linktext]</a>"
/datum/ErrorViewer/ErrorCache
/datum/error_viewer/error_cache
var/list/errors = list()
var/list/error_sources = list()
var/list/errors_silenced = list()
/datum/ErrorViewer/ErrorCache/showTo(var/user, var/datum/ErrorViewer/back_to, var/linear)
var/html = buildHeader(null, linear, refreshable=1)
html += "[GLOB.total_runtimes] runtimes, [GLOB.total_runtimes_skipped] skipped<br><br>"
if(!linear)
html += "organized | [makeLink("linear", null, 1)]<hr>"
var/datum/ErrorViewer/ErrorSource/error_source
for(var/erroruid in error_sources)
/datum/error_viewer/error_cache/show_to(user, datum/error_viewer/back_to, linear)
var/html = build_header()
html += "<b>[GLOB.total_runtimes]</b> runtimes, <b>[GLOB.total_runtimes_skipped]</b> skipped<br><br>"
if (!linear)
html += "organized | [make_link("linear", null, 1)]<hr>"
var/datum/error_viewer/error_source/error_source
for (var/erroruid in error_sources)
error_source = error_sources[erroruid]
html += "<p class='runtime_list'>[error_source.makeLink(null, src)]<br></p>"
else
html += "[makeLink("organized", null)] | linear<hr>"
for(var/datum/ErrorViewer/ErrorEntry/error_entry in errors)
html += "<p class='runtime_list'>[error_entry.makeLink(null, src, 1)]<br></p>"
browseTo(user, html)
html += "[error_source.make_link(null, src)]<br>"
/datum/ErrorViewer/ErrorCache/proc/logError(var/exception/e, var/list/desclines, var/skipCount, var/datum/e_src)
if(!istype(e))
else
html += "[make_link("organized", null)] | linear<hr>"
for (var/datum/error_viewer/error_entry/error_entry in errors)
html += "[error_entry.make_link(null, src, 1)]<br>"
browse_to(user, html)
/datum/error_viewer/error_cache/proc/log_error(exception/e, list/desclines, skip_count)
if (!istype(e))
return // Abnormal exception, don't even bother
var/erroruid = "[e.file][e.line]"
var/datum/ErrorViewer/ErrorSource/error_source = error_sources[erroruid]
if(!error_source)
var/datum/error_viewer/error_source/error_source = error_sources[erroruid]
if (!error_source)
error_source = new(e)
error_sources[erroruid] = error_source
var/datum/ErrorViewer/ErrorEntry/error_entry = new(e, desclines, skipCount, e_src)
var/datum/error_viewer/error_entry/error_entry = new(e, desclines, skip_count)
error_entry.error_source = error_source
errors += error_entry
error_source.errors += error_entry
if(skipCount)
return // Skip notifying admins about skipped errors
if (skip_count)
return // Skip notifying admins about skipped errors.
// Show the error to admins with debug messages turned on, but only if one
// from the same source hasn't been shown too recently
// (Also, make sure config is initialized, or log_debug will runtime)
if(config && error_source.next_message_at <= world.time)
if (error_source.next_message_at <= world.time)
var/const/viewtext = "\[view]" // Nesting these in other brackets went poorly
log_debug("Runtime in [e.file],[e.line]: [html_encode(e.name)] [error_entry.makeLink(viewtext)]")
error_source.next_message_at = world.time + ERROR_MSG_DELAY
//to_chat(world, "Runtime in <b>[e.file]</b>, line <b>[e.line]</b>: <b>[html_encode(e.name)]</b> [error_entry.make_link(viewtext)]")
var/err_msg_delay
if(config?.loaded)
err_msg_delay = CONFIG_GET(number/error_msg_delay)
else
var/datum/config_entry/CE = /datum/config_entry/number/error_msg_delay
err_msg_delay = initial(CE.default)
error_source.next_message_at = world.time + err_msg_delay
/datum/ErrorViewer/ErrorSource
/datum/error_viewer/error_source
var/list/errors = list()
var/next_message_at = 0
/datum/ErrorViewer/ErrorSource/New(var/exception/e)
if(!istype(e))
/datum/error_viewer/error_source/New(exception/e)
if (!istype(e))
name = "\[[time_stamp()]] Uncaught exceptions"
return
name = "\[[time_stamp()]] Runtime in [e.file],[e.line]: [e]"
/datum/ErrorViewer/ErrorSource/showTo(var/user, var/datum/ErrorViewer/back_to, var/linear)
if(!istype(back_to))
back_to = error_cache
var/html = buildHeader(back_to, refreshable=1)
for(var/datum/ErrorViewer/ErrorEntry/error_entry in errors)
html += "<p class='runtime_list'>[error_entry.makeLink(null, src)]<br></p>"
browseTo(user, html)
name = "<b>\[[time_stamp()]]</b> Runtime in <b>[e.file]</b>, line <b>[e.line]</b>: <b>[html_encode(e.name)]</b>"
/datum/ErrorViewer/ErrorEntry
var/datum/ErrorViewer/ErrorSource/error_source
/datum/error_viewer/error_source/show_to(user, datum/error_viewer/back_to, linear)
if (!istype(back_to))
back_to = GLOB.error_cache
var/html = build_header(back_to)
for (var/datum/error_viewer/error_entry/error_entry in errors)
html += "[error_entry.make_link(null, src)]<br>"
browse_to(user, html)
/datum/error_viewer/error_entry
var/datum/error_viewer/error_source/error_source
var/exception/exc
var/desc = ""
var/srcRef
var/srcType
var/turf/srcLoc
var/usrRef
var/turf/usrLoc
var/isSkipCount
var/usr_ref
var/turf/usr_loc
var/is_skip_count
/datum/ErrorViewer/ErrorEntry/New(var/exception/e, var/list/desclines, var/skipCount, var/datum/e_src)
if(!istype(e))
name = "\[[time_stamp()]] Uncaught exception: [e]"
/datum/error_viewer/error_entry/New(exception/e, list/desclines, skip_count)
if (!istype(e))
name = "<b>\[[time_stamp()]]</b> Uncaught exception: <b>[html_encode(e.name)]</b>"
return
if(skipCount)
name = "\[[time_stamp()]] Skipped [skipCount] runtimes in [e.file],[e.line]."
isSkipCount = TRUE
if(skip_count)
name = "\[[time_stamp()]] Skipped [skip_count] runtimes in [e.file],[e.line]."
is_skip_count = TRUE
return
name = "\[[time_stamp()]] Runtime in [e.file],[e.line]: [e]"
name = "<b>\[[time_stamp()]]</b> Runtime in <b>[e.file]</b>, line <b>[e.line]</b>: <b>[html_encode(e.name)]</b>"
exc = e
if(istype(desclines))
for(var/line in desclines)
if (istype(desclines))
for (var/line in desclines)
// There's probably a better way to do this than non-breaking spaces...
desc += "&nbsp;&nbsp;" + html_encode(line) + "<br>"
if(istype(e_src))
srcRef = "\ref[e_src]"
srcType = e_src.type
srcLoc = get_turf(e_src)
if(usr)
usrRef = "\ref[usr]"
usrLoc = get_turf(usr)
desc += "<span class='runtime_line'>[html_encode(line)]</span><br>"
/datum/ErrorViewer/ErrorEntry/showTo(var/user, var/datum/ErrorViewer/back_to, var/linear)
if(!istype(back_to))
if (usr)
usr_ref = "[REF(usr)]"
usr_loc = get_turf(usr)
/datum/error_viewer/error_entry/show_to(user, datum/error_viewer/back_to, linear)
if (!istype(back_to))
back_to = error_source
var/html = buildHeader(back_to, linear)
html += "<div class='runtime'>[html_encode(name)]<br>[desc]</div>"
if(srcRef)
html += "<br>src: <a href='byond://?_src_=vars;[HrefToken()];Vars=[srcRef]'>VV</a>"
if(ispath(srcType, /mob))
html += " <a href='byond://?_src_=holder;[HrefToken()];adminplayeropts=[srcRef]'>PP</a>"
html += " <a href='byond://?_src_=holder;[HrefToken()];adminplayerobservefollow=[srcRef]'>Follow</a>"
if(istype(srcLoc))
html += "<br>src.loc: <a href='byond://?_src_=vars;[HrefToken()];Vars=\ref[srcLoc]'>VV</a>"
html += " <a href='byond://?_src_=holder;[HrefToken()];adminplayerobservecoodjump=1;X=[srcLoc.x];Y=[srcLoc.y];Z=[srcLoc.z]'>JMP</a>"
if(usrRef)
html += "<br>usr: <a href='byond://?_src_=vars;[HrefToken()];Vars=[usrRef]'>VV</a>"
html += " <a href='byond://?_src_=holder;[HrefToken()];adminplayeropts=[usrRef]'>PP</a>"
html += " <a href='byond://?_src_=holder;[HrefToken()];adminplayerobservefollow=[usrRef]'>Follow</a>"
if(istype(usrLoc))
html += "<br>usr.loc: <a href='byond://?_src_=vars;[HrefToken()];Vars=\ref[usrLoc]'>VV</a>"
html += " <a href='byond://?_src_=holder;[HrefToken()];adminplayerobservecoodjump=1;X=[usrLoc.x];Y=[usrLoc.y];Z=[usrLoc.z]'>JMP</a>"
browseTo(user, html)
/datum/ErrorViewer/ErrorEntry/makeLink(var/linktext, var/datum/ErrorViewer/back_to, var/linear)
if(isSkipCount)
return html_encode(name)
else
return ..()
var/html = build_header(back_to, linear)
html += "[name]<div class='runtime'>[desc]</div>"
if (usr_ref)
html += "<br><b>usr</b>: <a href='byond://?_src_=vars;[HrefToken()];Vars=[usr_ref]'>VV</a>"
html += " <a href='byond://?_src_=holder;[HrefToken()];adminplayeropts=[usr_ref]'>PP</a>"
html += " <a href='byond://?_src_=holder;[HrefToken()];adminplayerobservefollow=[usr_ref]'>Follow</a>"
if (istype(usr_loc))
html += "<br><b>usr.loc</b>: <a href='byond://?_src_=vars;[HrefToken()];Vars=[REF(usr_loc)]'>VV</a>"
html += " <a href='byond://?_src_=holder;[HrefToken()];adminplayerobservecoodjump=1;X=[usr_loc.x];Y=[usr_loc.y];Z=[usr_loc.z]'>JMP</a>"
browse_to(user, html)
/datum/error_viewer/error_entry/make_link(linktext, datum/error_viewer/back_to, linear)
return is_skip_count ? name : ..()