CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/rex/logging/log_dispatcher.rb
Views: 11777
1
# -*- coding: binary -*-
2
require 'rex/sync'
3
4
module Rex
5
module Logging
6
7
###
8
#
9
# The log dispatcher associates log sources with log sinks. A log source
10
# is a unique identity that is associated with one and only one log sink.
11
# For instance, the framework-core registers the 'core'
12
#
13
###
14
class LogDispatcher
15
16
#
17
# Creates the global log dispatcher instance and initializes it for use.
18
#
19
def initialize()
20
self.log_sinks = {}
21
self.log_levels = {}
22
self.log_sinks_lock = Mutex.new
23
end
24
25
#
26
# Returns the sink that is associated with the supplied source.
27
#
28
def [](src)
29
sink = nil
30
31
log_sinks_lock.synchronize {
32
sink = log_sinks[src]
33
}
34
35
return sink
36
end
37
38
#
39
# Calls the source association routie.
40
#
41
def []=(src, sink)
42
store(src, sink)
43
end
44
45
#
46
# Associates the supplied source with the supplied sink. If a log level
47
# has already been defined for the source, the level argument is ignored.
48
# Use set_log_level to alter it.
49
#
50
def store(src, sink, level = 0)
51
log_sinks_lock.synchronize {
52
if (log_sinks[src] == nil)
53
log_sinks[src] = sink
54
55
set_log_level(src, level) if (log_levels[src] == nil)
56
else
57
raise(
58
RuntimeError,
59
"The supplied log source #{src} is already registered.",
60
caller)
61
end
62
}
63
end
64
65
#
66
# Removes a source association if one exists.
67
#
68
def delete(src)
69
sink = nil
70
71
log_sinks_lock.synchronize {
72
sink = log_sinks[src]
73
74
log_sinks.delete(src)
75
}
76
77
if (sink)
78
sink.cleanup
79
80
return true
81
end
82
83
return false
84
end
85
86
#
87
# Performs the actual log operation against the supplied source
88
#
89
def log(sev, src, level, msg)
90
log_sinks_lock.synchronize {
91
if ((sink = log_sinks[src]))
92
next if (log_levels[src] and level > log_levels[src])
93
94
sink.log(sev, src, level, msg)
95
end
96
}
97
end
98
99
#
100
# This method sets the log level threshold for a given source.
101
#
102
def set_level(src, level)
103
log_levels[src] = level.to_i
104
end
105
106
#
107
# This method returns the log level threshold of a given source.
108
#
109
def get_level(src)
110
log_levels.fetch(src, DEFAULT_LOG_LEVEL)
111
end
112
113
attr_accessor :log_sinks, :log_sinks_lock # :nodoc:
114
attr_accessor :log_levels # :nodoc:
115
end
116
117
end
118
end
119
120
# An instance of the log dispatcher exists in the global namespace, along
121
# with stubs for many of the common logging methods. Various sources can
122
# register themselves as a log sink such that logs can be directed at
123
# various targets depending on where they're sourced from. By doing it
124
# this way, things like sessions can use the global logging stubs and
125
# still be directed at the correct log file.
126
#
127
###
128
ExceptionCallStack = "__EXCEPTCALLSTACK__"
129
130
BACKTRACE_LOG_LEVEL = 3 # Equal to LEV_3
131
DEFAULT_LOG_LEVEL = 0 # Equal to LEV_3
132
133
def dlog(msg, src = 'core', level = 0)
134
$dispatcher.log(LOG_DEBUG, src, level, msg)
135
end
136
137
# Logs errors in a standard format for each Log Level.
138
#
139
# @param msg [String] Contains message from the developer explaining why an error was encountered.
140
# Can also be an +Exception+, in which case a log is built from the +Exception+ with no accompanying message.
141
#
142
# @param src [String] Used to indicate where the error is originating from. Most commonly set to 'core'.
143
#
144
# @param log_level [Integer] Indicates the level of logging the message should be recorded at. If log_level is greater than
145
# the global log level set for +src+, then the log is not recorded.
146
#
147
# @param error [Exception] Exception of an error that needs to be logged. For all log messages, the class and message of
148
# an exception is added to a log message. If the global log level set for +src+ is greater than +BACKTRACE_LOG_LEVEL+,
149
# then the stack trace for an error is also added to the log message.
150
#
151
# (Eg Loop Iterations, Variables, Function Calls).
152
#
153
# @return [NilClass].
154
def elog(msg, src = 'core', log_level = 0, error: nil)
155
error = msg.is_a?(Exception) ? msg : error
156
157
if error.nil? || !error.is_a?(Exception)
158
$dispatcher.log(LOG_ERROR, src, log_level, msg)
159
else
160
error_details = "#{error.class} #{error.message}"
161
if get_log_level(src) >= BACKTRACE_LOG_LEVEL
162
if error.backtrace
163
error_details << "\nCall stack:\n#{error.backtrace.join("\n")}"
164
else
165
error_details << "\nCall stack:\nNone"
166
end
167
end
168
169
if msg.is_a?(Exception)
170
$dispatcher.log(LOG_ERROR, src, log_level,"#{error_details}")
171
else
172
$dispatcher.log(LOG_ERROR, src, log_level,"#{msg} - #{error_details}")
173
end
174
end
175
end
176
177
def wlog(msg, src = 'core', level = 0)
178
$dispatcher.log(LOG_WARN, src, level, msg)
179
end
180
181
def ilog(msg, src = 'core', level = 0)
182
$dispatcher.log(LOG_INFO, src, level, msg)
183
end
184
185
def rlog(msg, src = 'core', level = 0)
186
if (msg == ExceptionCallStack)
187
msg = "\nCall stack:\n" + $@.join("\n") + "\n"
188
end
189
190
$dispatcher.log(LOG_RAW, src, level, msg)
191
end
192
193
def log_source_registered?(src)
194
($dispatcher[src] != nil)
195
end
196
197
def register_log_source(src, sink, level = nil)
198
$dispatcher[src] = sink
199
200
set_log_level(src, level) if (level)
201
end
202
203
def deregister_log_source(src)
204
$dispatcher.delete(src)
205
end
206
207
def set_log_level(src, level)
208
$dispatcher.set_level(src, level)
209
end
210
211
def get_log_level(src)
212
$dispatcher.get_level(src)
213
end
214
215
# Creates the global log dispatcher
216
$dispatcher = Rex::Logging::LogDispatcher.new
217
218