Common Ruby Logger

The ‘mintpress-logger’ ruby gem provides the MintLogger module to include into any ruby class that requires logging. By default it creates a STDOUT/STDERR logger, with logging enabled at Logger::INFO level.

Setting Up the Logger

set_log_level

The set_log_level method should only be called from an entry point into the ruby code. Common utilities / gems should refrain from calling it is preferrable that they inherit the current log settings from the code that is calling them.

The first parameter can be * a String (debug/info/warn/error/fatal) * a Symbol (:debug/:info/:warn/:error/:fatal) * an Integer (Logger::DEBUG/Logger::INFO/Logger::WARN/Logger::ERROR/Logger::FATAL)

The second parameter can be * nil (if changing the default log level), or * the file name of the entry point into the code (if changing the default log level for all code called from this entry point).

set_log_prefix

The set_log_prefix method allows control over the text that prefixes the log messages produced by the logger. It accepts a LogPrefixElements object, that describes the required format. The message prefix can contain zero or more of the following elements:

element description
:date log date and time
:severity log message severity (DEBUG/INFO/WARN/ERROR/FATAL)
:progname the name of the class & method that generated the log message
:other allows text constants to be included in the prefix

The order the elements appear in the elements: array passed to the LogPrefixElements object determines the order they appear in the prefix. The date_format: string passed to the LogPrefixElements object determines the format of the :date element. The DateTime documentation describes the values that can be used to format the date. The default date format is "%Y-%m-%d %H:%M:%S".

The log prefix call below is the default used by MintLogger

set_log_prefix(
  LogPrefixElements.new(
    elements: [
      LogPrefix.new(
        prefix: "[",
        suffix: "] ",
        element: :date
      ),
      LogPrefix.new(
        prefix: nil,
        suffix: nil,
        element: :severity
      ),
      LogPrefix.new(
        prefix: " (",
        suffix: ") ",
        element: :progname
      )
    ],
    date_format: "%Y-%m-%d %H:%M:%S"
  )
)

This generates log messages with a prefix: [2018-11-21 18:00:08] DEBUG (MintPress::OracleEBS::DBHost.get_patch_level)

enable_debug_file_log

Calling the enable_debug_file_log method will cause the MintLogger to log all messages (regardless of level) into a local debug log file. Logging into the debug log file is independent of the STDOUT/STDERR logger controlled by the set_log_level and set_log_prefix methods. Eg the STDOUT/STDERR logger can be configured to log Logger::ERROR -> Logger::FATAL messages, whilst the debug log file will receive Logger::DEBUG -> Logger::FATAL messages.

The enable_debug_file_log method accepts three parameters:

parameter description
file_name The name of the file to create on the local file system. The default file is /tmp/mp_debug.log
mode Valid values are (w)rite and (a)ppend. In write mode, the file is replaced each time the enable_debug_file_log is called. In append mode it appends to the existing file (or creates a new one)
debug_log_prefix LogPrefixElements object describing the prefix to use in the log file. If not provided, the default described above is used.

disable_debug_file_log

Calling the disable_debug_file_log method will cause the MintLogger to no logger log messages into the debug log file. This has no affect on the STDOUT/STDERR logger.

For further details on the CLI, please refer to docs.oracle.com/en/database/oracle/oracle-database/12.2/dgbkr/oracle-data-guard-broker-commands.html

Utilities - Common

check_result

The check_result method is used with the LASRpcUtils::ConsoleTuple result values returned from the las_rpc_utils execute command. It accepts the following parameters

Parameter Default Description
command: The command that was passed to las-rpc-utils execute.
result: The LASRpcUtils::ConsoleTuple returned from las-rpc-utils execute.
success_msg: A message to print if the result.exit_status = 0. nil if no success message required.
failure_msg: A message to print if the result.exit_status != 0. nil if no failure message required.
success_out: :debug Logger target for the success messages (anything that translate_log_level can translate).
failure_out: :error Logger target for the failure messages (anything that translate_log_level can translate).
abort_on_failure: false true/false value, if set to true check_result will raise the failure_msg if result.exit_status != 0.
print_command: false true/false value, if true, prints the command that was successful prior to printing the success_msg. (applies to exit_status = 0 only).

With the default parameters: * if the result.exit_status passed in is 0, it will print the success_msg passed in as a debug message * if the result.exit_status passed in is not 0, it will print the failure_msg passed in as an error message along with the exit status, stdout and stderr.

fmt_variable

The fmt_variable returns a string based on the following parameters:

Parameter Default Description
prompt Text to print prior to the variable’s contents
variable Variable to print the contents of (must be a String)
dont_pad false true/false, whether to right pad the prompt text to prompt_length or not.
prompt_length: PROMPT_LENGTH The width to pad the prompt string with (PROMPT_LENGTH=20)

A sample output from fmt_variable("surname",surname_var,true) is surname: Anderson

fmt_hash

The fmt_hash method loops through the elements in a hash (or array) and, using fmt_variable builds a debug string of the hash/array’s contents. It derives the prompt_length to call fmt_variable with as the longest hash key + 4 or the array length + 3.

Utilities - Logging

translate_log_level

The translate_log_level method translates the log_level passed in into an integer that reflects the Logger constants for the various log levels. The parameter value can be:

Object Type Debug Info Warn Error Fatal
a string “debug” “info” “warn” “error” “fatal”
a symbol :debug :info :warn :error :fatal
an integer Logger::DEBUG Logger::INFO Logger::WARN Logger::ERROR Logger::FATAL

If the log_level passed is a String, translate_log_level will downcase the value passed.

current_log_level

The current_log_level method returns the log level configured for the current call stack. (as one of the Logger::XXX constants)

logging_at?

The logging_at? method returns true if the log level passed in is greater than or equal to the current log level, false otherwise

debug_logging_enabled?

The debug_logging_enabled? method returns true if the current log level is Logger::DEBUG, false otherwise

info_logging_enabled?

The debug_logging_enabled? method returns true if the current log level is Logger::DEBUG or Logger::INFO, false otherwise

Logging Messages

debug, info, warn, error, fatal

The debug, info, warn, error and fatal methods all accept either * a bracketed string (eg. info("Here is a test message") or * a lambda/proc info { "here is a test message" }.

The proc will only be executed if logging is enabled at the level requested. It is recommended that all calls to debug use the proc call method to reduce processing overhead.

def my_test
  debug { "here is a debug message" }
  info { "here is an information message" }
  warn { "here is a warning message" }
  error { "here is an error message" }
  fatal { "here is a fatal message" }
end

throw_error

Similar to the methods above, abort will accept a string or lambda/proc as a parameter. As abort messages eventually get ‘raise’d, the proc will always be processed so there is little benefit in using a proc to pass the message content. It is provided more for compatibility with the other methods above.

The throw_error method accepts an optional second parameter use_fail:. By default, abort will use raise to produce the message. If the use_fail: parameter is set to true, abort will use fail to produce the message.

NB. As raise and fail are aliases of each other, this has zero effect AFAIK)

def my_test
  throw_error("Major exception occurred")
end

log_exception

The log_exception method accepts a StandardError exception and builds a backtrace stack (like the one that ruby generates when an un-rescued raise is encountered) then logs it to the Logger::ERROR output. If the optional raise_error: parameter is set to true, log_exception will use throw_error to output the backtrace.

start_method

Logs the string --> ENTER <-- as a debug message

end_method

Logs the string --> EXIT <-- as a debug message

Configuration File

If, when the MintLogger is first activated, the current user’s home directory includes a .limepoint subdirectory, MintLogger will listen for changes in that directory. If a MintLogger configuration file (mlConfig.toml) is created, modified or deleted from the directory this will impact the current run-time logging environment.

The configuration file has the following structure:

[levelOverrides]
  DifferentClass = "info"
  SampleClass = "debug"

[excludeMethods]
  DifferentClass = []
  SampleClass = ["differentTest"]

[includeMethods]
  DifferentClass = []
  SampleClass = ["/.*Test/", "testDebug"]

Configuration File Sections

levelOverrides

This section allows the log level assigned to each class to be dynamically overridden. Each key value is a ruby class (case sensitive) and the value for each key is the debug log level to use when checking if messages should be output from that class. Valid values are “debug”, “info”, “warn”, “error” or “fatal”.

excludeMethods

This section allows configuration of methods that should not produce log output. Each key value is a ruby class (case sensitive) and the value for each key is an array of method names or regex’s to “match” methods to exclude from logging. NB. toml does not allow a native ruby regex as an array value so it must be enclosed in double quotes per the SampleClass example in the includeMethods above.

includeMethods

This section allows configuration of methods that should produce log output. Each key value is a ruby class (case sensitive) and the value for each key is an array of method names or regex’s to “match” methods to include in the logging. NB. toml does not allow a native ruby regex as an array value so it must be enclosed in double quotes per the SampleClass example in the includeMethods above.

Configuration File Processing

When deciding to log (or not log) a message from a class, the configuration file’s contents are processed in the following sequence outlined below. Each step in the sequence is only performed if the previous steps are successful. 1. levelOverrides - the message must be being logged from the class at a level greater than or equal to the override level for that class (Logger::DEBUG = 0, Logger::INFO = 1, Logger::WARN = 2, Logger::ERROR = 3, Logger::FATAL = 4). If no override level exists in the toml file, then the message log level will be compared to the default level provided by the class itself (or one of its parents) 2. includeMethods - the message must be one of the following : * logged from a class that is not listed in the includeMethods section of the toml file * logged from a method that matches one of the patterns in the includeMethods array for this class * logged from a class with no elements in its includeMethods array (eg differentClass above) 3. excludeMethods - the message must be one of the following: * logged from a class that is not listed in the excludeMethods section of the toml file * logged from a method that does not match one of the patterns in the excludeMethods array for this class * logged from a class with no elements in its excludeMethods array (eg differentClass above)

Example Configuration File and Logger Usage

NB. This example assumes the configuration file above has been saved into ~/.limepoint for the current user.

irb

require 'mintpress-logger'

class SampleClass
  include MintLogger
  attr_accessor :sampleVar
  
  def initialize
    @sampleVar = "Fred"
  end

  def testDebug
    debug { "Here's a log message from testDebug" }
    debug { fmt_variable("sampleVar", @sampleVar) }
  end

  def anotherTest
    info { "Here's a log message from anotherTest" }
    info { fmt_variable("sampleVar", @sampleVar) }
  end

  def differentTest
    debug { "Here's a log message from anotherTest" }
    debug { fmt_variable("sampleVar", @sampleVar) }
  end

  def finalTest
    info { "Here's a log message from finalTest" }
    info { fmt_variable("sampleVar", @sampleVar) }
  end
end

class DifferentClass
  include MintLogger
  attr_accessor :mySample

  def initialize
    set_log_level("debug", parent_path: __FILE__)
    @mySample = SampleClass.new
    info "Printing sampleVar in DifferentClass.initialize"
    info "#{@mySample.sampleVar}"
    info "Calling @mySample.testDebug in DifferentClass.initialize"
    @mySample.testDebug
    info "End of initialize"
  end

  def test
    debug "Calling @mySample.testDebug in test - debug" # this won't appear due to level override
    info "Calling @mySample.testDebug in test - info"
    @mySample.testDebug
    debug "Creating a new SampleClass object"
    newtest = SampleClass.new
    info "Calling finalTest"
    newtest.finalTest
    info "Calling differentTest - mlConfig.toml override will block output"
    newtest.differentTest
    info "End of Fred.test"
  end
end

logTest = DifferentClass.new
logTest.test

License & Authors

  • Author:: LimePoint (support@limepoint.com)

# MintPress® - Automation and Configuration Management
#
# Copyright © 2019 LimePoint. All rights reserved.
#
# This program and its contents are confidential and owned by LimePoint.
# Only licenced users are permitted to access and use of this file.
# This program (or any part of it) may not be disclosed, copied or used
# except as expressly permitted in LimePoint’s End User Licence Agreement.
#
# LimePoint® and MintPress® are Registered Trademarks of LimePoint IP Limited.
# For more information contact LimePoint at http://www.limepoint.com