Pastey Logging tools
- Posted by ghaberek (admin) May 13, 2015
--**** -- == Logging tools -- -- <<LEVELTOC level=2 depth=4>> namespace logging include std/datetime.e include std/filesys.e include std/io.e include std/search.e include std/text.e ifdef EU4_1 then include euphoria/debug/debug.e end ifdef --**** -- === Log level constants -- Description: -- See [[:set_log_level]] for how to use these constants. public enum --** do not log any messages LOG_SILENT = 0, --** log severe messages only LOG_SEVERE, --** log errors and severe messages LOG_ERRORS, --** log warnings, errors, and severe LOG_WARNING, --** log all messages LOG_VERBOSE, $ --**** -- === Log output macros -- Description: -- See [[:set_log_header]] for how to use these macros. public constant --** displays the current date, see [[:set_date_format]] __DATE__ = "__DATE__", --** displays the current time, see [[:set_time_format]] __TIME__ = "__TIME__", $ ifdef EU4_1 then public constant --** displays the current file name (Euphoria 4.1+ only) __FILE__ = "__FILE__", --** displays the current file path (Euphoria 4.1+ only) __PATH__ = "__PATH__", --** displays the current line number (Euphoria 4.1+ only) __LINE__ = "__LINE__", --** displays the current routine name (Euphoria 4.1+ only) __ROUTINE__ = "__ROUTINE__", $ end ifdef public constant --** displays the log level of the message __LEVEL__ = "__LEVEL__", $ constant VALID_LOG_LEVELS = { LOG_SILENT, LOG_SEVERE, LOG_ERRORS, LOG_WARNING, LOG_VERBOSE } constant LOG_LEVEL_NAMES = { "LOG_SILENT", "LOG_SEVERE", "LOG_ERRORS", "LOG_WARNING", "LOG_VERBOSE" } integer log_level = LOG_WARNING sequence log_mode = "a" sequence log_output = {STDOUT} sequence log_header = "__DATE__ __TIME__ " sequence date_format = "%Y/%m/%d" sequence time_format = "%H:%M:%S" ifdef LOG_VERBOSE then log_level = LOG_VERBOSE elsifdef LOG_WARNING then log_level = LOG_WARNING elsifdef LOG_ERRORS then log_level = LOG_ERRORS elsifdef LOG_SEVERE then log_level = LOG_SEVERE elsifdef LOG_SILENT then log_level = LOG_SILENT end ifdef function expand_macros( sequence msg, datetime dt, sequence cs, integer level ) if match( "__", msg ) then sequence dt_date = datetime:format( dt, date_format ) sequence dt_time = datetime:format( dt, time_format ) msg = match_replace( __DATE__, msg, dt_date ) msg = match_replace( __TIME__, msg, dt_time ) ifdef EU4_1 then sequence cs_file_name = filename( cs[CS_FILE_NAME] ) sequence cs_path_name = canonical_path( cs[CS_FILE_NAME] ) sequence cs_line_no = sprint( cs[CS_LINE_NO] ) sequence cs_routine_name = cs[CS_ROUTINE_NAME] msg = match_replace( __FILE__, msg, cs_file_name ) msg = match_replace( __PATH__, msg, cs_path_name ) msg = match_replace( __LINE__, msg, cs_line_no ) msg = match_replace( __ROUTINE__, msg, cs_routine_name ) end ifdef integer level_id = find( level, VALID_LOG_LEVELS ) sequence level_name = LOG_LEVEL_NAMES[level_id] msg = match_replace( __LEVEL__, msg, level_name ) end if return msg end function --**** -- === Logging routines --** -- Gets the current log header. -- -- Returns: -- The value previously set by [[:set_log_header]]. -- -- See Also: -- [[:set_log_header]], [[:write_log]] public function get_log_header() return log_header end function --** -- Gets the current log level. -- -- Returns: -- The log level set by [[:set_log_level]]. -- -- See Also: -- [[:set_log_level]], [[:write_log]] public function get_log_level() return log_level end function --** -- Gets the log output destination. -- -- Returns: -- The log output destination set by [[:set_log_output]]. -- -- Comments: -- This function always returns a sequence of log destinations, -- even if [[:set_log_output]] was called with a single file number. -- -- See Also: -- [[:set_log_output]], [[:write_log]] public function get_log_output() return log_output end function --** -- Sets the log header, which is prepended to each line output by [[:write_log]]. -- -- Parameters: -- # ##header## : a sequence in ##printf()## format -- # ##data## : //(optional)// values passed to the format in ##header## -- -- Comments: -- The log header can contain one or more macros that are expanded with each call to [[:write_log]]. -- -- * ##~__DATE__## : current date -- * ##~__TIME__## : current time -- * ##~__FILE__## : current file name* -- * ##~__PATH__## : current file path* -- * ##~__LINE__## : current line number* -- * ##~__ROUTINE__## : current routine name* -- * ##~__LEVEL__## : the log level of the message\\ --\\ -- -- ~*these macros are only available with Euphoria 4.1 and up -- -- The default log header is ##"~__DATE~__ ~__TIME~__ "## -- -- See Also: -- [[:get_log_header]], [[:set_date_format]], [[:set_time_format]], [[:write_log]] public procedure set_log_header( sequence header, object data = {} ) log_header = sprintf( header, data ) end procedure --** -- Sets the log level, which determines the output of future [[:write_log]] calls. -- -- Parameters: -- # ##level## : one of the available [[:Log level constants]] -- -- Comments: -- The default log level is ##LOG_WARNING##. -- -- Log levels can be set via the command line using ##-D##, e.g. ##eui -D LOG_WARNING myapp.ex## -- -- See Also: -- [[:get_log_level]], [[:write_log]] public procedure set_log_level( integer level ) if find( level, VALID_LOG_LEVELS ) then log_level = level end if end procedure --** -- Sets the log output destination. -- -- Parameters: -- # ##output## : an object, either: -- ** an **atom**, which is an open file number, or ##STDOUT##/##STDERR## -- ** a **sequence**, which is a //list// of file numbers or file names (not a single file name) -- # ##mode## : //(optional)// the mode used for ##open()## with file names -- -- Comment: -- The default ##mode## for ##open()## is "a". -- -- See Also: -- [[:get_log_output]], [[:write_log]] public procedure set_log_output( object output, sequence mode = "a" ) if atom( output ) then output = {output} end if log_mode = mode log_output = output end procedure --** -- Sets the format used by the ~__DATE~__ macro. -- -- Parameters: -- # ##format## : the format string to use -- -- Comments: -- This is the same format used by ##datetime:format()##. -- -- See Also: -- [[:set_time_format]] public procedure set_date_format( sequence format ) date_format = format end procedure --** -- Sets the format used by the ~__TIME~__ macro. -- -- Parameters: -- # ##format## : the format string to use -- -- Comments: -- This is the same format used by ##datetime:format()##. -- -- See Also: -- [[:set_date_format]] public procedure set_time_format( sequence format ) time_format = format end procedure --** -- Writes a message to the current log outputs. -- -- Parameters: -- # ##level## : the logging level of this message, see [[:Log level constants]] -- # ##message## : //(optional)// the message to write out, using ##printf()## format -- # ##data## : //(optional)// the data passed to ##printf()## when writing ##message## -- -- Comments: -- The ##message## parameter may contain [[:Log header macros]], -- which will be expanded automatically during output. -- -- See Also: -- [[:Log header macros]], [[:Log level constants]] public procedure write_log( integer level, sequence message = "", object data = {} ) if log_level >= level then -- get the required macro values datetime dt = datetime:now() ifdef EU4_1 then sequence cs = call_stack() cs = cs[$-1] -- the stack item before this one elsedef cs = {} -- call_stack() not supported end ifdef -- expand macros in the header and message -- and combine them into a new message message = expand_macros( log_header, dt, cs, level ) & expand_macros( message, dt, cs, level ) -- verify that the message ends with a new line if not search:ends( EOL, message ) then message &= EOL end if -- send the message to the provided outputs for i = 1 to length( log_output ) do object fn = log_output[i] integer close_fn = 0 if sequence( fn ) then -- open the file temporarily fn = open( fn, log_mode ) close_fn = 1 end if printf( fn, message, data ) if close_fn then close( fn ) end if end for end if end procedure
1. Comment by ghaberek May 18, 2015
There is a bug on line 359. The variable cs is not declared as a sequence in non-4.1 versions.
/* 355 */ ifdef EU4_1 then /* 356 */ sequence cs = call_stack() /* 357 */ cs = cs[$-1] -- the stack item before this one /* 358 */ elsedef /* 359 */ sequence cs = {} -- call_stack() not supported (!! add 'sequence' to this line !!) /* 360 */ end ifdef
-Greg