mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-11 10:43:20 +00:00
[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:
committed by
GitHub
parent
272afa33c8
commit
5a62077f2c
@@ -1,9 +0,0 @@
|
||||
// Settings for the error handler and error viewer
|
||||
|
||||
#define ERROR_COOLDOWN 600 // The "cooldown" time for each occurrence of a unique error
|
||||
#define ERROR_LIMIT 9 // How many occurrences before the next will silence them
|
||||
#define ERROR_MAX_COOLDOWN (ERROR_COOLDOWN * ERROR_LIMIT)
|
||||
#define ERROR_SILENCE_TIME 6000 // How long a unique error will be silenced for
|
||||
|
||||
// How long to wait between messaging admins about occurrences of a unique error
|
||||
#define ERROR_MSG_DELAY 50
|
||||
@@ -1,135 +1,156 @@
|
||||
// error_cooldown items will either be positive (cooldown time) or negative (silenced error)
|
||||
// If negative, starts at -1, and goes down by 1 each time that error gets skipped
|
||||
GLOBAL_VAR_INIT(total_runtimes, 0)
|
||||
GLOBAL_VAR_INIT(total_runtimes, GLOB.total_runtimes || 0)
|
||||
GLOBAL_VAR_INIT(total_runtimes_skipped, 0)
|
||||
|
||||
#ifdef USE_CUSTOM_ERROR_HANDLER
|
||||
#define ERROR_USEFUL_LEN 2
|
||||
|
||||
// The ifdef needs to be down here, since the error viewer references total_runtimes
|
||||
#ifdef DEBUG
|
||||
/world/Error(var/exception/e, var/datum/e_src)
|
||||
GLOB.total_runtimes++ //CHOMPEdit just moving this here to start counting right away
|
||||
if(!istype(e)) // Something threw an unusual exception
|
||||
log_error("\[[time_stamp()]] Uncaught exception: [e]")
|
||||
/world/Error(exception/E, datum/e_src)
|
||||
GLOB.total_runtimes++
|
||||
|
||||
if(!istype(E)) //Something threw an unusual exception
|
||||
log_world("uncaught runtime error: [E]")
|
||||
return ..()
|
||||
|
||||
//CHOMP Edit Stealing this bit from TGStation to try to record OOM issues.
|
||||
//this is snowflake because of a byond bug (ID:2306577), do not attempt to call non-builtin procs in this if
|
||||
if(copytext(e.name,1,32) == "Maximum recursion level reached")
|
||||
if(copytext(E.name, 1, 32) == "Maximum recursion level reached")//32 == length() of that string + 1
|
||||
//log to world while intentionally triggering the byond bug.
|
||||
log_world("runtime error: [e.name]\n[e.desc]")
|
||||
log_world("runtime error: [E.name]\n[E.desc]")
|
||||
//if we got to here without silently ending, the byond bug has been fixed.
|
||||
log_world("The bug with recursion runtimes has been fixed. Please remove the snowflake check from world/Error in [__FILE__]:[__LINE__]")
|
||||
return //this will never happen.
|
||||
|
||||
else if(copytext(e.name,1,18) == "Out of resources!")
|
||||
log_world("BYOND out of memory.")
|
||||
log_game("BYOND out of memory.")
|
||||
return ..()
|
||||
//CHOMP Edit end
|
||||
else if(copytext(E.name, 1, 18) == "Out of resources!")//18 == length() of that string + 1
|
||||
log_world("BYOND out of memory. Restarting ([E?.file]:[E?.line])")
|
||||
TgsEndProcess()
|
||||
. = ..()
|
||||
Reboot(reason = 1)
|
||||
return
|
||||
|
||||
if(!GLOB.error_last_seen) // A runtime is occurring too early in start-up initialization
|
||||
var/static/regex/stack_workaround
|
||||
if(isnull(stack_workaround))
|
||||
stack_workaround = regex("[WORKAROUND_IDENTIFIER](.+?)[WORKAROUND_IDENTIFIER]")
|
||||
var/static/list/error_last_seen = list()
|
||||
var/static/list/error_cooldown = list() /* Error_cooldown items will either be positive(cooldown time) or negative(silenced error)
|
||||
If negative, starts at -1, and goes down by 1 each time that error gets skipped*/
|
||||
|
||||
if(!error_last_seen) // A runtime is occurring too early in start-up initialization
|
||||
return ..()
|
||||
|
||||
if(stack_workaround.Find(E.name))
|
||||
var/list/data = json_decode(stack_workaround.group[1])
|
||||
E.file = data[1]
|
||||
E.line = data[2]
|
||||
E.name = stack_workaround.Replace(E.name, "")
|
||||
|
||||
var/erroruid = "[e.file][e.line]"
|
||||
var/last_seen = GLOB.error_last_seen[erroruid]
|
||||
var/cooldown = GLOB.error_cooldown[erroruid] || 0
|
||||
if(last_seen == null) // A new error!
|
||||
GLOB.error_last_seen[erroruid] = world.time
|
||||
var/erroruid = "[E.file][E.line]"
|
||||
var/last_seen = error_last_seen[erroruid]
|
||||
var/cooldown = error_cooldown[erroruid] || 0
|
||||
|
||||
if(last_seen == null)
|
||||
error_last_seen[erroruid] = world.time
|
||||
last_seen = world.time
|
||||
if(cooldown < 0)
|
||||
GLOB.error_cooldown[erroruid]-- // Used to keep track of skip count for this error
|
||||
GLOB.total_runtimes_skipped++
|
||||
return // Error is currently silenced, skip handling it
|
||||
|
||||
// Handle cooldowns and silencing spammy errors
|
||||
var/silencing = 0
|
||||
// Each occurrence of a unique error adds to its "cooldown" time...
|
||||
cooldown = max(0, cooldown - (world.time - last_seen)) + ERROR_COOLDOWN
|
||||
if(cooldown < 0)
|
||||
error_cooldown[erroruid]-- //Used to keep track of skip count for this error
|
||||
GLOB.total_runtimes_skipped++
|
||||
return //Error is currently silenced, skip handling it
|
||||
//Handle cooldowns and silencing spammy errors
|
||||
var/silencing = FALSE
|
||||
|
||||
// We can runtime before config is initialized because BYOND initialize objs/map before a bunch of other stuff happens.
|
||||
// This is a bunch of workaround code for that. Hooray!
|
||||
var/configured_error_cooldown
|
||||
var/configured_error_limit
|
||||
var/configured_error_silence_time
|
||||
if(config?.entries)
|
||||
configured_error_cooldown = CONFIG_GET(number/error_cooldown)
|
||||
configured_error_limit = CONFIG_GET(number/error_limit)
|
||||
configured_error_silence_time = CONFIG_GET(number/error_silence_time)
|
||||
else
|
||||
var/datum/config_entry/CE = /datum/config_entry/number/error_cooldown
|
||||
configured_error_cooldown = initial(CE.default)
|
||||
CE = /datum/config_entry/number/error_limit
|
||||
configured_error_limit = initial(CE.default)
|
||||
CE = /datum/config_entry/number/error_silence_time
|
||||
configured_error_silence_time = initial(CE.default)
|
||||
|
||||
|
||||
//Each occurence of a unique error adds to its cooldown time...
|
||||
cooldown = max(0, cooldown - (world.time - last_seen)) + configured_error_cooldown
|
||||
// ... which is used to silence an error if it occurs too often, too fast
|
||||
if(cooldown > ERROR_MAX_COOLDOWN)
|
||||
if(cooldown > configured_error_cooldown * configured_error_limit)
|
||||
cooldown = -1
|
||||
silencing = 1
|
||||
silencing = TRUE
|
||||
spawn(0)
|
||||
usr = null
|
||||
sleep(ERROR_SILENCE_TIME)
|
||||
var/skipcount = abs(GLOB.error_cooldown[erroruid]) - 1
|
||||
GLOB.error_cooldown[erroruid] = 0
|
||||
sleep(configured_error_silence_time)
|
||||
var/skipcount = abs(error_cooldown[erroruid]) - 1
|
||||
error_cooldown[erroruid] = 0
|
||||
if(skipcount > 0)
|
||||
log_error("\[[time_stamp()]] Skipped [skipcount] runtimes in [e.file],[e.line].")
|
||||
error_cache.logError(e, skipCount = skipcount)
|
||||
GLOB.error_last_seen[erroruid] = world.time
|
||||
GLOB.error_cooldown[erroruid] = cooldown
|
||||
SEND_TEXT(world.log, "\[[time_stamp()]] Skipped [skipcount] runtimes in [E.file],[E.line].")
|
||||
GLOB.error_cache.log_error(E, skip_count = skipcount)
|
||||
|
||||
error_last_seen[erroruid] = world.time
|
||||
error_cooldown[erroruid] = cooldown
|
||||
|
||||
// The detailed error info needs some tweaking to make it look nice
|
||||
var/list/srcinfo = null
|
||||
var/list/usrinfo = null
|
||||
var/locinfo
|
||||
// First, try to make better src/usr info lines
|
||||
if(istype(e_src))
|
||||
srcinfo = list(" src: [log_info_line(e_src)]")
|
||||
var/atom/atom_e_src = e_src
|
||||
if(istype(atom_e_src))
|
||||
srcinfo += " src.loc: [log_info_line(atom_e_src.loc)]"
|
||||
if(istype(usr))
|
||||
usrinfo = list(" usr: [log_info_line(usr)]")
|
||||
locinfo = log_info_line(usr.loc)
|
||||
usrinfo = list(" usr: [key_name(usr)]")
|
||||
locinfo = loc_name(usr)
|
||||
if(locinfo)
|
||||
usrinfo += " usr.loc: [locinfo]"
|
||||
// The proceeding mess will almost definitely break if error messages are ever changed
|
||||
// I apologize in advance
|
||||
var/list/splitlines = splittext(e.desc, "\n")
|
||||
var/list/splitlines = splittext(E.desc, "\n")
|
||||
var/list/desclines = list()
|
||||
if(splitlines.len > 2) // If there aren't at least three lines, there's no info
|
||||
#ifndef DISABLE_DREAMLUAU
|
||||
var/list/state_stack = GLOB.lua_state_stack
|
||||
var/is_lua_call = length(state_stack)
|
||||
var/list/lua_stacks = list()
|
||||
if(is_lua_call)
|
||||
for(var/level in 1 to state_stack.len)
|
||||
lua_stacks += list(splittext(DREAMLUAU_GET_TRACEBACK(level), "\n"))
|
||||
#endif
|
||||
if(LAZYLEN(splitlines) > ERROR_USEFUL_LEN) // If there aren't at least three lines, there's no info
|
||||
for(var/line in splitlines)
|
||||
if(length(line) < 3)
|
||||
continue // Blank line, skip it
|
||||
if(findtext(line, "source file:"))
|
||||
continue // Redundant, skip it
|
||||
if(findtext(line, "usr.loc:"))
|
||||
continue // Our usr.loc is better, skip it
|
||||
if(LAZYLEN(line) < 3 || findtext(line, "source file:") || findtext(line, "usr.loc:"))
|
||||
continue
|
||||
if(findtext(line, "usr:"))
|
||||
if(usrinfo)
|
||||
desclines.Add(usrinfo)
|
||||
usrinfo = null
|
||||
continue // Our usr info is better, replace it
|
||||
if(srcinfo)
|
||||
if(findtext(line, "src.loc:"))
|
||||
continue
|
||||
if(findtext(line, "src:"))
|
||||
desclines.Add(srcinfo)
|
||||
srcinfo = null
|
||||
continue
|
||||
if(copytext(line, 1, 3) != " ")
|
||||
if(copytext(line, 1, 3) != " ")//3 == length(" ") + 1
|
||||
desclines += (" " + line) // Pad any unpadded lines, so they look pretty
|
||||
else
|
||||
desclines += line
|
||||
if(srcinfo) // If these aren't null, they haven't been added yet
|
||||
desclines.Add(srcinfo)
|
||||
if(usrinfo)
|
||||
if(usrinfo) //If this info isn't null, it hasn't been added yet
|
||||
desclines.Add(usrinfo)
|
||||
#ifndef DISABLE_DREAMLUAU
|
||||
if(is_lua_call)
|
||||
SSlua.log_involved_runtime(E, desclines, lua_stacks)
|
||||
#endif
|
||||
if(silencing)
|
||||
desclines += " (This error will now be silenced for [ERROR_SILENCE_TIME / 600] minutes)"
|
||||
desclines += " (This error will now be silenced for [DisplayTimeText(configured_error_silence_time)])"
|
||||
if(GLOB.error_cache)
|
||||
GLOB.error_cache.log_error(E, desclines)
|
||||
|
||||
// Now to actually output the error info...
|
||||
log_error("\[[time_stamp()]] Runtime in [e.file],[e.line]: [e]")
|
||||
var/main_line = "\[[time_stamp()]] Runtime in [E.file],[E.line]: [E]"
|
||||
SEND_TEXT(world.log, main_line)
|
||||
for(var/line in desclines)
|
||||
log_error(line)
|
||||
if(error_cache)
|
||||
error_cache.logError(e, desclines, e_src = e_src)
|
||||
SEND_TEXT(world.log, line)
|
||||
|
||||
#ifdef UNIT_TESTS
|
||||
if(GLOB.current_test)
|
||||
//good day, sir
|
||||
GLOB.current_test.Fail("[main_line]\n[desclines.Join("\n")]", file = E.file, line = E.line)
|
||||
#endif
|
||||
|
||||
/proc/log_runtime(exception/e, datum/e_src, extra_info)
|
||||
if(!istype(e))
|
||||
world.Error(e, e_src)
|
||||
return
|
||||
//if(Debugger?.enabled)
|
||||
// to_chat(world, span_alertwarning("[main_line]"), type = MESSAGE_TYPE_DEBUG)
|
||||
|
||||
if(extra_info)
|
||||
// Adding extra info adds two newlines, because parsing runtimes is funky
|
||||
if(islist(extra_info))
|
||||
e.desc = " [jointext(extra_info, "\n ")]\n\n" + e.desc
|
||||
else
|
||||
e.desc = " [extra_info]\n\n" + e.desc
|
||||
// This writes the regular format (unwrapping newlines and inserting timestamps as needed).
|
||||
log_runtime("runtime error: [E.name]\n[E.desc]")
|
||||
#endif
|
||||
|
||||
world.Error(e, e_src)
|
||||
#undef ERROR_USEFUL_LEN
|
||||
|
||||
@@ -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><<<</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 += " " + 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 : ..()
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
// Settings for the error handler and error viewer
|
||||
#undef ERROR_COOLDOWN
|
||||
#undef ERROR_LIMIT
|
||||
#undef ERROR_MAX_COOLDOWN
|
||||
#undef ERROR_SILENCE_TIME
|
||||
#undef ERROR_MSG_DELAY
|
||||
Reference in New Issue
Block a user