forked from rapid7/rex-core
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Migrate rex/logging into Core from Msf namespace
Using Rex' various gems without Msf will result in errors when the logging subsystem is undefined (as that remained in Msf during the great Rex excision). This manifests in rex-socket as noted by @zeroSteiner in rapid7/rex-socket#38. Address the dependency problem by moving rex/logging into this gem which is already required by rex-socket and other descendants. Testing: None - this is a quick-n-dirty subdirectory move. If this works, someone with real git skill should migrate the relevant history of the code; as losing that stuff results in people not knowing whom to ask when the time comes to fix some deeply-bored bug.
- Loading branch information
RageLtMan
committed
Dec 4, 2022
1 parent
1e43b17
commit cbd267a
Showing
10 changed files
with
553 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
# -*- coding: binary -*- | ||
module Rex::Logging | ||
|
||
|
||
# | ||
# Log severities | ||
# | ||
LOG_ERROR = :error | ||
LOG_DEBUG = :debug | ||
LOG_INFO = :info | ||
LOG_WARN = :warn | ||
LOG_RAW = :raw | ||
|
||
## | ||
# | ||
# Log levels | ||
# | ||
## | ||
|
||
# | ||
# LEV_0 - Default | ||
# | ||
# This log level is the default log level if none is specified. It should be | ||
# used when a log message should always be displayed when logging is enabled. | ||
# Very few log messages should occur at this level aside from necessary | ||
# information logging and error/warning logging. Debug logging at level zero | ||
# is not advised. | ||
# | ||
LEV_0 = 0 | ||
|
||
# | ||
# LEV_1 - Extra | ||
# | ||
# This log level should be used when extra information may be needed to | ||
# understand the cause of an error or warning message or to get debugging | ||
# information that might give clues as to why something is happening. This | ||
# log level should be used only when information may be useful to understanding | ||
# the behavior of something at a basic level. This log level should not be | ||
# used in an exhaustively verbose fashion. | ||
# | ||
LEV_1 = 1 | ||
|
||
# | ||
# LEV_2 - Verbose | ||
# | ||
# This log level should be used when verbose information may be needed to | ||
# analyze the behavior of the framework. This should be the default log | ||
# level for all detailed information not falling into LEV_0 or LEV_1. | ||
# It is recommended that this log level be used by default if you are | ||
# unsure. | ||
# | ||
LEV_2 = 2 | ||
|
||
# | ||
# LEV_3 - Insanity | ||
# | ||
# This log level should contain very verbose information about the | ||
# behavior of the framework, such as detailed information about variable | ||
# states at certain phases including, but not limited to, loop iterations, | ||
# function calls, and so on. This log level will rarely be displayed, | ||
# but when it is the information provided should make it easy to analyze | ||
# any problem. | ||
# | ||
LEV_3 = 3 | ||
|
||
require 'rex/logging/log_dispatcher' | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,216 @@ | ||
# -*- coding: binary -*- | ||
require 'rex/sync' | ||
|
||
module Rex | ||
module Logging | ||
|
||
### | ||
# | ||
# The log dispatcher associates log sources with log sinks. A log source | ||
# is a unique identity that is associated with one and only one log sink. | ||
# For instance, the framework-core registers the 'core' | ||
# | ||
### | ||
class LogDispatcher | ||
|
||
# | ||
# Creates the global log dispatcher instance and initializes it for use. | ||
# | ||
def initialize() | ||
self.log_sinks = {} | ||
self.log_levels = {} | ||
self.log_sinks_lock = Mutex.new | ||
end | ||
|
||
# | ||
# Returns the sink that is associated with the supplied source. | ||
# | ||
def [](src) | ||
sink = nil | ||
|
||
log_sinks_lock.synchronize { | ||
sink = log_sinks[src] | ||
} | ||
|
||
return sink | ||
end | ||
|
||
# | ||
# Calls the source association routie. | ||
# | ||
def []=(src, sink) | ||
store(src, sink) | ||
end | ||
|
||
# | ||
# Associates the supplied source with the supplied sink. If a log level | ||
# has already been defined for the source, the level argument is ignored. | ||
# Use set_log_level to alter it. | ||
# | ||
def store(src, sink, level = 0) | ||
log_sinks_lock.synchronize { | ||
if (log_sinks[src] == nil) | ||
log_sinks[src] = sink | ||
|
||
set_log_level(src, level) if (log_levels[src] == nil) | ||
else | ||
raise( | ||
RuntimeError, | ||
"The supplied log source #{src} is already registered.", | ||
caller) | ||
end | ||
} | ||
end | ||
|
||
# | ||
# Removes a source association if one exists. | ||
# | ||
def delete(src) | ||
sink = nil | ||
|
||
log_sinks_lock.synchronize { | ||
sink = log_sinks[src] | ||
|
||
log_sinks.delete(src) | ||
} | ||
|
||
if (sink) | ||
sink.cleanup | ||
|
||
return true | ||
end | ||
|
||
return false | ||
end | ||
|
||
# | ||
# Performs the actual log operation against the supplied source | ||
# | ||
def log(sev, src, level, msg) | ||
log_sinks_lock.synchronize { | ||
if ((sink = log_sinks[src])) | ||
next if (log_levels[src] and level > log_levels[src]) | ||
|
||
sink.log(sev, src, level, msg) | ||
end | ||
} | ||
end | ||
|
||
# | ||
# This method sets the log level threshold for a given source. | ||
# | ||
def set_level(src, level) | ||
log_levels[src] = level.to_i | ||
end | ||
|
||
# | ||
# This method returns the log level threshold of a given source. | ||
# | ||
def get_level(src) | ||
log_levels.fetch(src, DEFAULT_LOG_LEVEL) | ||
end | ||
|
||
attr_accessor :log_sinks, :log_sinks_lock # :nodoc: | ||
attr_accessor :log_levels # :nodoc: | ||
end | ||
|
||
end | ||
end | ||
|
||
# An instance of the log dispatcher exists in the global namespace, along | ||
# with stubs for many of the common logging methods. Various sources can | ||
# register themselves as a log sink such that logs can be directed at | ||
# various targets depending on where they're sourced from. By doing it | ||
# this way, things like sessions can use the global logging stubs and | ||
# still be directed at the correct log file. | ||
# | ||
### | ||
ExceptionCallStack = "__EXCEPTCALLSTACK__" | ||
|
||
BACKTRACE_LOG_LEVEL = 3 # Equal to LEV_3 | ||
DEFAULT_LOG_LEVEL = 0 # Equal to LEV_3 | ||
|
||
def dlog(msg, src = 'core', level = 0) | ||
$dispatcher.log(LOG_DEBUG, src, level, msg) | ||
end | ||
|
||
# Logs errors in a standard format for each Log Level. | ||
# | ||
# @param msg [String] Contains message from the developer explaining why an error was encountered. | ||
# Can also be an +Exception+, in which case a log is built from the +Exception+ with no accompanying message. | ||
# | ||
# @param src [String] Used to indicate where the error is originating from. Most commonly set to 'core'. | ||
# | ||
# @param log_level [Integer] Indicates the level of logging the message should be recorded at. If log_level is greater than | ||
# the global log level set for +src+, then the log is not recorded. | ||
# | ||
# @param error [Exception] Exception of an error that needs to be logged. For all log messages, the class and message of | ||
# an exception is added to a log message. If the global log level set for +src+ is greater than +BACKTRACE_LOG_LEVEL+, | ||
# then the stack trace for an error is also added to the log message. | ||
# | ||
# (Eg Loop Iterations, Variables, Function Calls). | ||
# | ||
# @return [NilClass]. | ||
def elog(msg, src = 'core', log_level = 0, error: nil) | ||
error = msg.is_a?(Exception) ? msg : error | ||
|
||
if error.nil? || !error.is_a?(Exception) | ||
$dispatcher.log(LOG_ERROR, src, log_level, msg) | ||
else | ||
error_details = "#{error.class} #{error.message}" | ||
if get_log_level(src) >= BACKTRACE_LOG_LEVEL | ||
if error.backtrace | ||
error_details << "\nCall stack:\n#{error.backtrace.join("\n")}" | ||
else | ||
error_details << "\nCall stack:\nNone" | ||
end | ||
end | ||
|
||
if msg.is_a?(Exception) | ||
$dispatcher.log(LOG_ERROR, src, log_level,"#{error_details}") | ||
else | ||
$dispatcher.log(LOG_ERROR, src, log_level,"#{msg} - #{error_details}") | ||
end | ||
end | ||
end | ||
|
||
def wlog(msg, src = 'core', level = 0) | ||
$dispatcher.log(LOG_WARN, src, level, msg) | ||
end | ||
|
||
def ilog(msg, src = 'core', level = 0) | ||
$dispatcher.log(LOG_INFO, src, level, msg) | ||
end | ||
|
||
def rlog(msg, src = 'core', level = 0) | ||
if (msg == ExceptionCallStack) | ||
msg = "\nCall stack:\n" + $@.join("\n") + "\n" | ||
end | ||
|
||
$dispatcher.log(LOG_RAW, src, level, msg) | ||
end | ||
|
||
def log_source_registered?(src) | ||
($dispatcher[src] != nil) | ||
end | ||
|
||
def register_log_source(src, sink, level = nil) | ||
$dispatcher[src] = sink | ||
|
||
set_log_level(src, level) if (level) | ||
end | ||
|
||
def deregister_log_source(src) | ||
$dispatcher.delete(src) | ||
end | ||
|
||
def set_log_level(src, level) | ||
$dispatcher.set_level(src, level) | ||
end | ||
|
||
def get_log_level(src) | ||
$dispatcher.get_level(src) | ||
end | ||
|
||
# Creates the global log dispatcher | ||
$dispatcher = Rex::Logging::LogDispatcher.new |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
# -*- coding: binary -*- | ||
|
||
module Rex | ||
module Logging | ||
|
||
### | ||
# | ||
# This abstract interface is what must be implemented by any class | ||
# that would like to register as a log sink on a given LogDispatcher | ||
# instance, such as the Framework object. | ||
# | ||
### | ||
module LogSink | ||
|
||
def cleanup | ||
end | ||
|
||
# | ||
# This method must be implemented by any derived log sink classes and is | ||
# intended to take the supplied parameters and persist them to an arbitrary | ||
# medium. | ||
# | ||
def log(sev, src, level, msg) | ||
raise NotImplementedError | ||
end | ||
|
||
protected | ||
|
||
# | ||
# This method returns the current timestamp in MM/DD/YYYY HH:Mi:SS format. | ||
# | ||
def get_current_timestamp | ||
return ::Time.now.strftime("%m/%d/%Y %H:%M:%S") | ||
end | ||
|
||
end | ||
|
||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# -*- coding: binary -*- | ||
|
||
|
||
module Rex | ||
module Logging | ||
|
||
### | ||
# | ||
# LogSinkFactory can instantiate a LogSink based on the given name. | ||
# | ||
### | ||
module LogSinkFactory | ||
# Creates a new log sink of the given name. If no name is provided, a default | ||
# Flatfile log sink is chosen | ||
# | ||
# @param [String] name The name of the required log sink within Rex::Logging::Sinks | ||
# @param [Array] attrs The attributes to use with the given log sink | ||
# @return [Rex::Logging::LogSink] The newly created log sink | ||
def self.new(name = nil, *attrs) | ||
name ||= Rex::Logging::Sinks::Flatfile.name.demodulize | ||
raise NameError unless available_sinks.include?(name.to_sym) | ||
|
||
log_sink = Rex::Logging::Sinks.const_get(name) | ||
log_sink.new(*attrs) | ||
rescue NameError | ||
raise Rex::ArgumentError, "Could not find logger #{name}, expected one of #{available_sinks.join(', ')}" | ||
end | ||
|
||
# Returns a list of the available sinks that can be created by this factory | ||
# | ||
# @return [Array<Sym>] The available sinks that can be created by this factory | ||
def self.available_sinks | ||
Rex::Logging::Sinks.constants - [Rex::Logging::Sinks::Stream.name.demodulize.to_sym] | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# -*- coding: binary -*- | ||
module Rex | ||
module Logging | ||
module Sinks | ||
|
||
### | ||
# | ||
# This class implements the LogSink interface and backs it against a | ||
# file on disk. | ||
# | ||
### | ||
class Flatfile < Rex::Logging::Sinks::Stream | ||
|
||
# | ||
# Creates a flatfile log sink instance that will be configured to log to | ||
# the supplied file path. | ||
# | ||
def initialize(file) | ||
super(File.new(file, 'a')) | ||
end | ||
|
||
end | ||
|
||
end end end |
Oops, something went wrong.