# -------------------------------------------------------- # ▼ Exception Logger [VX Ace] - v1.0.1 # by Krosk # -------------------------------------------------------- # # This script will read the backtrace of any exception # occurring within the game, and write it back in a file # for an easier debugging experience. # # Any exception triggered by a script error within a script, # or an event will be caught. # # If a script error happens when reading a file # (e.g. external script), this script can also be used # to catch and log the exception. In this case, # catch the exception with the following block. # begin # file = File.open("path/to/file.rb", r) # ... # rescue Exception => exception # EXCLOG::error_handler(exception, file) # end # # The default log file is Log.txt, and can be edited with # the EXCLOG_FILE parameter. # # -------------------------------------------------------- module EXCLOG EXCLOG_FILE = "Log.txt" #-------------------------------------------------------------------------- # ▼ No need to edit below this line #-------------------------------------------------------------------------- Call = "" #-------------------------------------------------------------------------- # Auto detect script name #-------------------------------------------------------------------------- EXCLOG_NAME = "" $RGSS_SCRIPTS.each do |script_entry| break unless script_entry[3].lines.each do |line| if line[/EXCLOG_NAME/] EXCLOG_NAME.replace(script_entry[1]) break end end end #-------------------------------------------------------------------------- # Backtrace logging #-------------------------------------------------------------------------- def self.error_handler(exception, file_arg = nil) source = $RGSS_SCRIPTS[exception.backtrace[0].split(':')[0][1..-2].to_i][1] source_line = exception.backtrace[0].split(":")[1] if file_arg != nil file = file_arg source = file.path end if source == EXCLOG_NAME # Probably an event interruption source = "event" end logfile = File.open(EXCLOG_FILE, "w") logfile.write("---------- Script error : #{source} ----------\n") logfile.write("----- Error class\n") logfile.write("#{exception.class}\n\n") logfile.write("----- Message\n") if exception.class == NoMethodError logfile.write("- ARGS : #{exception.args.inspect}\n") end logfile.write(exception.message + "\n\n") if file_arg != nil logfile.write("----- Error location in #{file.path}\n") logfile.write("Line #{file.lineno}\n") logfile.write(IO.readlines(file.path)[file.lineno-1] + "\n") elsif source == "event" logfile.write("----- Event Script error location\n") logfile.write(Call + "\n\n") else logfile.write("----- Common script error location at '#{source}'\n") logfile.write("Line #{source_line}\n\n") end logfile.write("----- Backtrace\n") for trace in exception.backtrace location = trace.split(":") script_id = location[0][1..-2].to_i script_name = $RGSS_SCRIPTS[script_id][1] logfile.write("Script : #{script_name} | Line : #{location[1]}") if location[2] != nil logfile.write(" | Method : #{location[2]}") end logfile.write("\n>\t") logfile.write($RGSS_SCRIPTS[script_id][3].split("\n")[location[1].to_i-1]) logfile.write("\n") end logfile.close raise end def self.register_call(arg, map_id, event_id, depth) string = "" if map_id == 0 # very likely to be in a battle string << "BATTLE: #{$game_troop.troop.id}\n" if $game_party.in_battle string << "COMMONT_EVENT (?) - " if depth > 0 string << "SCRIPT:\n#{arg}" else # common map event = $game_map.events[event_id] string << "MAP:#{map_id} EVENT:#{event_id} X:#{event.x},Y:#{event.y}\n" string << "COMMONT_EVENT (?) - " if depth > 0 string << "SCRIPT:\n#{arg}" end Call.replace(string) end end #-------------------------------------------------------------------------- # Game_Interpreter hook, logging last script call # If an in-game exception points here, the error # comes from a script inside an event (map, common or battle) #-------------------------------------------------------------------------- class Game_Interpreter alias exc_eval eval def eval(arg) EXCLOG::register_call(arg, map_id, event_id, @depth) exc_eval(arg) end end #-------------------------------------------------------------------------- # Main hook #-------------------------------------------------------------------------- alias exc_rgss_main rgss_main def rgss_main(*args, &block) exc_rgss_main(*args, &block) rescue Exception => exception EXCLOG::error_handler(exception) end #========================================================================== # End of file #==========================================================================