termite 0.0.10 → 0.0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +91 -37
- data/TODO +8 -2
- data/lib/termite.rb +266 -91
- data/lib/termite/hastur_logger.rb +41 -0
- data/lib/termite/syslog_logger.rb +47 -0
- data/lib/termite/version.rb +1 -1
- data/lib/termite/version.rb~ +3 -0
- data/termite.gemspec +2 -1
- data/test/ecology_log_test.rb +4 -3
- data/test/extra_logger_test.rb +5 -4
- data/test/hastur_logger_test.rb +58 -0
- data/test/level_test.rb +53 -0
- data/test/rescue_test.rb +8 -10
- data/test/sinks_test.rb +183 -0
- data/test/stderr_log_test.rb +11 -6
- data/test/termite_logger_test.rb +24 -15
- data/test/termite_subclass_test.rb +1 -1
- data/test/test_helper.rb +14 -9
- metadata +123 -74
- data/Gemfile.lock +0 -29
data/README.md
CHANGED
@@ -20,9 +20,10 @@ your Gemfile or on your gem command line.
|
|
20
20
|
Logging Dynamically
|
21
21
|
===================
|
22
22
|
|
23
|
-
Create a logger with something like:
|
23
|
+
Create a logger with something like one of these:
|
24
24
|
|
25
25
|
@logger = Termite::Logger.new
|
26
|
+
@logger = Termite::Logger.new :component => "MyLibrary"
|
26
27
|
|
27
28
|
Then use it like a regular logger, possibly with options:
|
28
29
|
|
@@ -34,38 +35,54 @@ You can also pass in JSON data after your message and before your options:
|
|
34
35
|
@logger.fatal("Pain and misery!", { :where_it_hurts => "elbow" }, :component => "WhinyLib")
|
35
36
|
@logger.info("I ache", {:where_it_hurts => "kidney"}, :application => "NotMe", :component => "StoicLib")
|
36
37
|
|
37
|
-
Termite also supports full Ruby Logger initialize parameters for
|
38
|
+
Termite also supports full Ruby Logger initialize parameters for
|
39
|
+
backward compatibility:
|
38
40
|
|
39
41
|
@logger = Termite::Logger.new("/var/lib/daily_termite_logs", "daily")
|
40
42
|
@logger = Termite::Logger.new("/tmp/rotatable.txt", 15, 1024000) # Up to 15 logs of size 1024000
|
41
43
|
|
44
|
+
You can also use all the standard methods of Ruby logging:
|
45
|
+
|
46
|
+
@logger.log(Logger::INFO, "I feel Ruby-compatible")
|
47
|
+
@logger << "This message gets logged as INFO"
|
48
|
+
|
49
|
+
Similarly, when using a file logger, the output should be very similar
|
50
|
+
to what you'd get from a Ruby logger. So a Termite logger is nearly a
|
51
|
+
drop-in replacement for the Ruby logger, plus you get Syslog output
|
52
|
+
and console output *in addition* to your existing to-file output.
|
53
|
+
|
42
54
|
Log Level
|
43
55
|
=========
|
44
56
|
|
45
|
-
Termite loggers, like Ruby loggers, allow you to set the logger's
|
46
|
-
level will be silently discarded, and
|
47
|
-
|
57
|
+
Termite loggers, like Ruby loggers, allow you to set the logger's
|
58
|
+
level. All events below that level will be silently discarded, and
|
59
|
+
will also not be sent to non-syslog loggers. You can change the level
|
60
|
+
with "logger.level = Logger::WARN" and similar.
|
48
61
|
|
49
|
-
By default, Termite will log events of all severities to standard
|
50
|
-
severity :error to standard error. You
|
51
|
-
|
52
|
-
|
62
|
+
By default, Termite will log events of all severities to standard
|
63
|
+
output, and events of at least severity :error to standard error. You
|
64
|
+
can set .stdout_level and .stderr_level just like setting .level
|
65
|
+
above. Level takes precedence over the other two and stderr_level
|
66
|
+
takes precedence over stdout_level.
|
53
67
|
|
54
68
|
Non-Syslog Logging
|
55
69
|
==================
|
56
70
|
|
57
|
-
If a filename or handle is specified in the constructor, Termite will
|
58
|
-
with the same parameters and mirror all
|
71
|
+
If a filename or handle is specified in the constructor, Termite will
|
72
|
+
instantiate a Ruby Logger with the same parameters and mirror all
|
73
|
+
events to it.
|
59
74
|
|
60
|
-
You can pass in objects with an :add method to a Termite logger's
|
61
|
-
from then on all events will be
|
62
|
-
|
75
|
+
You can pass in objects with an :add method to a Termite logger's
|
76
|
+
"add_extra_logger" method, and from then on all events will be
|
77
|
+
mirrored to that object. This can be useful for chaining loggers if
|
78
|
+
you need to. Events below the Termite logger's level (see above)
|
79
|
+
won't be mirrored.
|
63
80
|
|
64
81
|
Translating to SysLog
|
65
82
|
=====================
|
66
83
|
|
67
|
-
When writing to SysLog, Termite translates Ruby Logger severities into
|
68
|
-
default, this is the mapping:
|
84
|
+
When writing to SysLog, Termite translates Ruby Logger severities into
|
85
|
+
SysLog severities. By default, this is the mapping:
|
69
86
|
|
70
87
|
Logger => SysLog
|
71
88
|
:unknown => :alert
|
@@ -78,8 +95,9 @@ Logger => SysLog
|
|
78
95
|
Configuring with an Ecology
|
79
96
|
===========================
|
80
97
|
|
81
|
-
Termite supports a standard Ecology file. By default, it will look at
|
82
|
-
executable ($0) with extension .ecology.
|
98
|
+
Termite supports a standard Ecology file. By default, it will look at
|
99
|
+
the location of the current executable ($0) with extension .ecology.
|
100
|
+
So "bob.rb" would have "bob.ecology" next to it.
|
83
101
|
|
84
102
|
An Ecology is a JSON file of roughly this structure:
|
85
103
|
|
@@ -91,41 +109,77 @@ An Ecology is a JSON file of roughly this structure:
|
|
91
109
|
"app_group": "SuperSpiffyGroup",
|
92
110
|
"precedence": 7
|
93
111
|
},
|
94
|
-
"
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
112
|
+
"sinks": [
|
113
|
+
{
|
114
|
+
"type": "stdout",
|
115
|
+
"color": "green",
|
116
|
+
"min_level": "debug",
|
117
|
+
"max_level": "warning"
|
118
|
+
},
|
119
|
+
{
|
120
|
+
"type": "stderr",
|
121
|
+
"color": "red",
|
122
|
+
"min_level": "error"
|
123
|
+
},
|
124
|
+
{
|
125
|
+
"type": "file",
|
126
|
+
"filename": "/tmp/bobo.txt",
|
127
|
+
"min_level": "warning",
|
128
|
+
"shift_age": 10,
|
129
|
+
"shift_size": 1024000
|
130
|
+
},
|
131
|
+
{
|
132
|
+
"type": "syslog",
|
133
|
+
"transport": "UDP"
|
134
|
+
},
|
135
|
+
{
|
136
|
+
"type": "hastur",
|
137
|
+
"udp_port": 9199,
|
138
|
+
"labels": {
|
139
|
+
"app_flavor": "vanilla",
|
140
|
+
"track_for": "jbhat"
|
141
|
+
}
|
142
|
+
}
|
143
|
+
]
|
99
144
|
}
|
100
145
|
}
|
101
146
|
|
102
|
-
Absolutely every part of it is optional, including the presence of the
|
147
|
+
Absolutely every part of it is optional, including the presence of the
|
148
|
+
file at all.
|
103
149
|
|
104
|
-
You can override the application name, as shown above. Other than the
|
105
|
-
all Termite-specific parameters are under the
|
150
|
+
You can override the application name, as shown above. Other than the
|
151
|
+
application name, all Termite-specific parameters are under the
|
152
|
+
"logging" field, as above.
|
106
153
|
|
107
|
-
The default_component is what application component is given for an
|
108
|
-
If set, it can be removed with ":component =>
|
154
|
+
The default_component is what application component is given for an
|
155
|
+
add() call by default. If set, it can be removed with ":component =>
|
156
|
+
nil" for a given add() call.
|
109
157
|
|
110
158
|
Extra JSON fields are added to the JSON data of every add() call.
|
111
159
|
|
112
|
-
Console_print can be set to "off" (or "no" or "0") to turn off Termite
|
113
|
-
and stdout by default at different log levels.
|
160
|
+
Console_print can be set to "off" (or "no" or "0") to turn off Termite
|
161
|
+
printing to stderr and stdout by default at different log levels.
|
162
|
+
|
163
|
+
Filename, shift_age and shift_size are the same as Ruby Logger's
|
164
|
+
normal initialize parameters. The first is the filename to log to,
|
165
|
+
the second is how many total log files to keep, and the third is how
|
166
|
+
large each log file can grow. Or the second can be set to a value
|
167
|
+
like "daily" or "monthly", and then the third is irrelevant.
|
114
168
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
a value like "daily" or "monthly", and then the third is irrelevant.
|
169
|
+
You can also set level, stdout_level and stderr_level explained above.
|
170
|
+
We allow numerical (syslog) values, or standard names of Ruby Logger
|
171
|
+
severities.
|
119
172
|
|
120
|
-
|
121
|
-
|
173
|
+
If you reset your Ecology (usually used for testing), you should
|
174
|
+
recreate all your Termite::Logger objects. The old ones will still
|
175
|
+
have stale settings from the old Ecology's defaults and data.
|
122
176
|
|
123
177
|
Releasing within Ooyala
|
124
178
|
=======================
|
125
179
|
|
126
180
|
Ooyalans, to release Termite to gems.sv2, use the following:
|
127
181
|
|
128
|
-
|
182
|
+
rake build
|
129
183
|
rake _0.8.7_ -f ../ooyala_gems.rake gem:push termite-0.0.1.gem
|
130
184
|
|
131
185
|
Change the version to the actual version you'd like to push.
|
data/TODO
CHANGED
@@ -1,3 +1,9 @@
|
|
1
1
|
Ecology configuration:
|
2
|
-
*
|
3
|
-
*
|
2
|
+
* Load extra loggers from Ecology
|
3
|
+
* Load which transport protocol from Ecology
|
4
|
+
|
5
|
+
When logging:
|
6
|
+
* Send call-site with data (configurable depth?)
|
7
|
+
|
8
|
+
Environment variables:
|
9
|
+
* Add variable(s) to set console output when debugging
|
data/lib/termite.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
require "rubygems"
|
2
|
+
require "termite/hastur_logger"
|
3
|
+
require "termite/syslog_logger"
|
2
4
|
require "termite/version"
|
3
5
|
|
4
6
|
require "ecology"
|
5
7
|
require "multi_json"
|
8
|
+
require "rainbow"
|
6
9
|
|
7
10
|
require "thread"
|
8
11
|
require "syslog"
|
@@ -14,7 +17,7 @@ module Termite
|
|
14
17
|
##
|
15
18
|
# Maps Logger severities to syslog(3) severities.
|
16
19
|
|
17
|
-
|
20
|
+
LOGGER_SYSLOG_MAP = {
|
18
21
|
:unknown => :alert,
|
19
22
|
:fatal => :crit,
|
20
23
|
:error => :err,
|
@@ -23,27 +26,33 @@ module Termite
|
|
23
26
|
:debug => :debug,
|
24
27
|
}
|
25
28
|
|
29
|
+
# Get Ruby Logger labels for Logger-compatible output
|
30
|
+
RUBY_LOGGER_SEV_LABELS = ::Logger::SEV_LABEL
|
31
|
+
|
26
32
|
##
|
27
|
-
# Maps Ruby Logger log levels to their values
|
33
|
+
# Maps Ruby Logger log levels to their numerical values.
|
28
34
|
|
29
35
|
LOGGER_LEVEL_MAP = {}
|
30
36
|
|
31
|
-
|
37
|
+
LOGGER_SYSLOG_MAP.each_key do |key|
|
32
38
|
LOGGER_LEVEL_MAP[key] = ::Logger.const_get key.to_s.upcase
|
33
39
|
end
|
34
40
|
|
35
41
|
##
|
36
|
-
# Maps Logger numerical log level values to syslog
|
42
|
+
# Maps Logger numerical log level values to syslog level names.
|
37
43
|
|
38
|
-
|
44
|
+
LEVEL_SYSLOG_MAP = {}
|
39
45
|
|
40
46
|
LOGGER_LEVEL_MAP.invert.each do |level, severity|
|
41
|
-
|
47
|
+
LEVEL_SYSLOG_MAP[level] = LOGGER_SYSLOG_MAP[severity]
|
42
48
|
end
|
43
49
|
|
50
|
+
##
|
51
|
+
# Maps Syslog level names to their numerical severity levels
|
52
|
+
|
44
53
|
SYSLOG_SEVERITY_MAP = {}
|
45
54
|
|
46
|
-
|
55
|
+
LOGGER_SYSLOG_MAP.values.each do |syslog_severity|
|
47
56
|
SYSLOG_SEVERITY_MAP[syslog_severity] = ::Syslog.const_get("LOG_" + syslog_severity.to_s.upcase)
|
48
57
|
end
|
49
58
|
|
@@ -63,7 +72,7 @@ module Termite
|
|
63
72
|
EOM
|
64
73
|
end
|
65
74
|
|
66
|
-
|
75
|
+
LOGGER_SYSLOG_MAP.each_key do |level|
|
67
76
|
make_methods level
|
68
77
|
end
|
69
78
|
|
@@ -73,32 +82,156 @@ module Termite
|
|
73
82
|
attr_accessor :level
|
74
83
|
attr_reader :stdout_level
|
75
84
|
attr_reader :stderr_level
|
76
|
-
attr_reader :file_logger
|
77
85
|
|
78
|
-
def initialize(logdev = nil, shift_age =
|
79
|
-
|
86
|
+
def initialize(logdev = nil, shift_age = nil, shift_size = nil, options = {})
|
87
|
+
if logdev.is_a?(Hash)
|
88
|
+
options = logdev
|
89
|
+
logdev = nil
|
90
|
+
end
|
91
|
+
|
92
|
+
@loggers ||= []
|
93
|
+
@log_filename ||= logdev
|
94
|
+
@shift_age ||= shift_age
|
95
|
+
@shift_size ||= shift_size
|
80
96
|
|
81
97
|
Ecology.read
|
98
|
+
read_ecology_data(options)
|
99
|
+
end
|
82
100
|
|
83
|
-
|
84
|
-
@log_filename ||= logdev
|
101
|
+
private
|
85
102
|
|
86
|
-
|
87
|
-
@
|
103
|
+
def read_ecology_data(options = {})
|
104
|
+
@application = Ecology.application
|
105
|
+
@default_component = options[:component] || Ecology.property("logging::default_component") ||
|
106
|
+
options[:default_component]
|
107
|
+
# @console_print defaults to "yes", but can be nil if "no", "off" or "0" is specified
|
108
|
+
@console_print = Ecology.property("logging::console_print") || options[:console_print] || "yes"
|
109
|
+
@console_print = nil if ["no", "off", "0"].include?(@console_print)
|
110
|
+
@default_fields = Ecology.property("logging::extra_json_fields") || options[:extra_json_fields] || {}
|
111
|
+
@use_logger_prefix = Ecology.property("logging::use_logger_prefix") || options[:use_logger_prefix]
|
112
|
+
@level = Ecology.property("logging::level") || options[:level]
|
113
|
+
@level = string_to_severity(@level) if @level
|
114
|
+
@level = ::Logger::DEBUG if @level.nil? || ENV["TERMITE_DEBUG"]
|
88
115
|
|
89
|
-
|
90
|
-
|
91
|
-
|
116
|
+
sinks = Ecology.property("logging::sinks")
|
117
|
+
sinks ? instantiate_sinks(sinks, options) : read_unsinked_ecology(options)
|
118
|
+
end
|
92
119
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
120
|
+
# This is the codepath for ecology files that do not have a 'sinks' section defined
|
121
|
+
def read_unsinked_ecology(options)
|
122
|
+
sinks = []
|
123
|
+
# File Sink
|
124
|
+
file_sink = {"type" => "file", "min_level" => @level}
|
125
|
+
file_sink["filename"] = Ecology.property("logging::filename", :as => :path) ||
|
126
|
+
options[:logging_filename]
|
127
|
+
file_sink["shift_age"] = Ecology.property("logging::shift_age") || options[:shift_age]
|
128
|
+
file_sink["shift_size"] = Ecology.property("logging::shift_size") || options[:shift_size]
|
129
|
+
file_sink["logger_prefix?"] = @use_logger_prefix if @use_logger_prefix
|
130
|
+
sinks << file_sink if file_sink["filename"]
|
131
|
+
|
132
|
+
# STDERR Sink
|
133
|
+
stderr_sink = {"type" => "file", "filename" => STDERR}
|
134
|
+
@stderr_level = Ecology.property("logging::stderr_level") || options[:stderr_level] || ::Logger::ERROR
|
135
|
+
stderr_sink["min_level"] = string_to_severity(@stderr_level) if @stderr_level
|
136
|
+
if @use_logger_prefix
|
137
|
+
stderr_sink["logger_prefix?"] = @use_logger_prefix
|
138
|
+
else
|
139
|
+
stderr_prefix = Ecology.property("logging::stderr_logger_prefix") || options[:stderr_logger_prefix]
|
140
|
+
stderr_sink["logger_prefix?"] = stderr_prefix if stderr_prefix
|
141
|
+
end
|
142
|
+
stderr_sink["color"] = Ecology.property("logging::stderr_color") || options[:stderr_color]
|
143
|
+
stderr_sink["color"] ||= "red" if STDERR.tty?
|
144
|
+
sinks << stderr_sink if @console_print
|
145
|
+
|
146
|
+
# STDOUT Sink
|
147
|
+
stdout_sink = {"type" => "file", "filename" => STDOUT}
|
148
|
+
@stdout_level = Ecology.property("logging::stdout_level") || options[:stdout_level] || ::Logger::INFO
|
149
|
+
stdout_sink["min_level"] = string_to_severity(@stdout_level) if @stdout_level
|
150
|
+
stdout_sink["max_level"] = string_to_severity(@stderr_level) - 1
|
151
|
+
if @use_logger_prefix
|
152
|
+
stdout_sink["logger_prefix?"] = @use_logger_prefix
|
153
|
+
else
|
154
|
+
stdout_prefix = Ecology.property("logging::stdout_logger_prefix") || options[:stdout_logger_prefix]
|
155
|
+
stdout_sink["logger_prefix?"] = stdout_prefix if stdout_prefix
|
156
|
+
end
|
157
|
+
stdout_sink["color"] = Ecology.property("logging:stdout_color") || options[:stdout_color]
|
158
|
+
stdout_sink["color"] ||= "blue" if STDOUT.tty?
|
159
|
+
sinks << stdout_sink if @console_print
|
160
|
+
|
161
|
+
# Syslog Sink
|
162
|
+
syslog_sink = {"type" => "syslog"}
|
163
|
+
syslog_sink["transport"] = Ecology.property("logging::transport") || options[:transport]
|
164
|
+
sinks << syslog_sink
|
165
|
+
|
166
|
+
|
167
|
+
# Set up sinks
|
168
|
+
instantiate_sinks(sinks, options)
|
97
169
|
end
|
98
170
|
|
99
|
-
|
171
|
+
# For each sink, and to loggers
|
172
|
+
def instantiate_sinks(sinks, options)
|
173
|
+
sinks = sinks.dup
|
174
|
+
syslog = false
|
175
|
+
# Maximum Level
|
176
|
+
min_level = 5
|
177
|
+
sinks.each do |sink|
|
178
|
+
# Set min level if lower than current (@level if not defined)
|
179
|
+
if sink["min_level"]
|
180
|
+
sink_level = string_to_severity(sink["min_level"])
|
181
|
+
min_level = sink_level if sink_level < min_level
|
182
|
+
else min_level = @level
|
183
|
+
end
|
184
|
+
cur_logger = case sink["type"]
|
185
|
+
when "file"
|
186
|
+
sink["newline?"] = true unless sink.has_key? "newline?"
|
187
|
+
case sink["filename"]
|
188
|
+
when STDOUT
|
189
|
+
::Logger.new(STDOUT) if @console_print
|
190
|
+
when STDERR
|
191
|
+
::Logger.new(STDERR) if @console_print
|
192
|
+
else
|
193
|
+
::Logger.new(sink["filename"], sink["shift_age"] || 0, sink["shift_size"] || 1048576)
|
194
|
+
end
|
195
|
+
when "stdout"
|
196
|
+
sink["newline?"] = true unless sink.has_key? "newline?"
|
197
|
+
::Logger.new(STDOUT) if @console_print
|
198
|
+
when "stderr"
|
199
|
+
sink["newline?"] = true unless sink.has_key? "newline?"
|
200
|
+
::Logger.new(STDERR) if @console_print
|
201
|
+
when "syslog"
|
202
|
+
syslog = true
|
203
|
+
setup_syslog_logger(sink, options)
|
204
|
+
when "hastur"
|
205
|
+
setup_hastur_logger(sink, options)
|
206
|
+
end
|
207
|
+
sink["logger"] = cur_logger
|
208
|
+
end
|
209
|
+
@loggers = sinks
|
210
|
+
|
211
|
+
# Create syslog logger if not defined in sinks
|
212
|
+
unless syslog
|
213
|
+
min_level = @level
|
214
|
+
add_logger(setup_syslog_logger(options), "type" => "syslog", "min_level" => min_level)
|
215
|
+
end
|
216
|
+
|
217
|
+
# Constructor params logger
|
218
|
+
if @log_filename
|
219
|
+
min_level = @level
|
220
|
+
add_logger(::Logger.new(@log_filename, @shift_age || 0, @shift_size || 1048576), "type" => "file",
|
221
|
+
"filename" => @log_filename,
|
222
|
+
"shift_age" => @shift_age || 0,
|
223
|
+
"shift_size" => @shift_size || 1048576,
|
224
|
+
"logger_prefix?" => @use_logger_prefix,
|
225
|
+
"min_level" => @level,
|
226
|
+
"newline?" => true
|
227
|
+
)
|
228
|
+
end
|
229
|
+
# If the min level of all loggers is greater than @level, use that
|
230
|
+
@level = [@level, min_level].max
|
231
|
+
end
|
100
232
|
|
101
233
|
def string_to_severity(str)
|
234
|
+
return str if str.is_a? Numeric
|
102
235
|
orig_string = str
|
103
236
|
str = str.strip.downcase
|
104
237
|
return str.to_i if str =~ /\d+/
|
@@ -107,61 +240,60 @@ module Termite
|
|
107
240
|
ret
|
108
241
|
end
|
109
242
|
|
110
|
-
def
|
111
|
-
|
112
|
-
|
113
|
-
@
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
@console_print = nil if ["no", "off", "0"].include?(@console_print)
|
118
|
-
|
119
|
-
@log_filename = Ecology.property("logging::filename", :as => :path)
|
120
|
-
@shift_age = Ecology.property("logging::shift_age")
|
121
|
-
@shift_size = Ecology.property("logging::shift_size")
|
122
|
-
@default_component = Ecology.property("logging::default_component")
|
123
|
-
@level = Ecology.property("logging::level")
|
124
|
-
@level = string_to_severity(@level) if @level
|
125
|
-
@stderr_level = Ecology.property("logging::stderr_level")
|
126
|
-
@stderr_level = string_to_severity(@stderr_level) if @stderr_level
|
127
|
-
@stdout_level = Ecology.property("logging::stdout_level")
|
128
|
-
@stdout_level = string_to_severity(@stdout_level) if @stdout_level
|
243
|
+
def setup_syslog_logger(sink, options)
|
244
|
+
# For UDP socket
|
245
|
+
@server_addr = options[:address] || "0.0.0.0"
|
246
|
+
@server_port = options[:port] ? options[:port].to_i : 514
|
247
|
+
@socket = find_or_create_socket(@server_addr, @server_port)
|
248
|
+
SyslogLogger.new(@socket, @server_addr, @server_port, sink["transport"])
|
249
|
+
end
|
129
250
|
|
130
|
-
|
251
|
+
def setup_hastur_logger(sink, options)
|
252
|
+
@hastur_addr = options[:hastur_address] || "127.0.0.1"
|
253
|
+
@hastur_port = sink["udp_port"] || options[:hastur_port] || 8125
|
254
|
+
@hastur_socket = find_or_create_socket(@hastur_addr, @hastur_port)
|
255
|
+
HasturLogger.new(@hastur_socket, @hastur_addr, @hastur_port, sink["labels"])
|
256
|
+
end
|
131
257
|
|
132
|
-
|
133
|
-
|
134
|
-
|
258
|
+
def find_or_create_socket(addr, port)
|
259
|
+
@@sockets ||= {}
|
260
|
+
key = "#{addr}:#{port}"
|
261
|
+
@@sockets[key] ||= UDPSocket.new
|
135
262
|
end
|
136
263
|
|
137
264
|
public
|
138
265
|
|
139
|
-
def add_logger(logger)
|
140
|
-
|
266
|
+
def add_logger(logger, options={})
|
267
|
+
if logger.is_a? Hash
|
268
|
+
@loggers << logger
|
269
|
+
else
|
270
|
+
options["logger"] = logger
|
271
|
+
@loggers << options
|
272
|
+
end
|
141
273
|
end
|
142
274
|
|
143
275
|
def socket
|
144
276
|
@socket
|
145
277
|
end
|
146
278
|
|
147
|
-
|
148
|
-
|
279
|
+
COLORS = [:black, :red, :green, :yellow, :blue, :magenta, :cyan, :white, :default]
|
280
|
+
|
281
|
+
def raw_add(severity, raw_message = nil, data = nil, options = {}, &block)
|
149
282
|
# Severity is a numerical severity using Ruby Logger's scale
|
150
283
|
severity ||= ::Logger::UNKNOWN
|
151
284
|
return true if severity < @level
|
152
285
|
|
153
|
-
application =
|
154
|
-
|
155
|
-
component ||= @default_component
|
286
|
+
application = options[:application] || @application
|
287
|
+
component = @default_component
|
156
288
|
component = options[:component] if options.has_key?(:component)
|
157
|
-
|
158
|
-
|
159
|
-
|
289
|
+
|
290
|
+
combined_app = application + ":" + component if component
|
291
|
+
app_data = {:combined => combined_app, :app => application, :component => component}
|
160
292
|
|
161
293
|
data ||= {}
|
162
294
|
if data.is_a?(Hash)
|
163
295
|
data = @default_fields.merge(data)
|
164
|
-
data = MultiJson.
|
296
|
+
data = MultiJson.dump(data)
|
165
297
|
elsif data.is_a?(String)
|
166
298
|
# Can't merge a JSON string with default data
|
167
299
|
raise "Can't merge a JSON string with extra fields!" unless @default_fields.empty?
|
@@ -169,42 +301,37 @@ module Termite
|
|
169
301
|
raise "Unknown data object passed as JSON!"
|
170
302
|
end
|
171
303
|
|
172
|
-
tid = Ecology.thread_id(Thread.current)
|
173
304
|
time = Time.now
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
full_message = clean(message || block.call)
|
181
|
-
|
182
|
-
# ruby_severity is the Ruby Logger severity as a symbol
|
183
|
-
ruby_severity = LOGGER_LEVEL_MAP.invert[severity]
|
184
|
-
|
185
|
-
full_message.split("\n").each do |line|
|
186
|
-
message = syslog_string + "#{line} #{data}"
|
187
|
-
|
188
|
-
begin
|
189
|
-
@socket.send(message, 0, @server_addr, @server_port)
|
190
|
-
rescue Exception
|
191
|
-
# Didn't work. Try built-in Ruby syslog
|
192
|
-
require "syslog"
|
193
|
-
Syslog.open(application, Syslog::LOG_PID | Syslog::LOG_CONS) do |s|
|
194
|
-
s.error("UDP syslog failed! Falling back to libc syslog!") rescue nil
|
195
|
-
s.send(LEVEL_LOGGER_MAP[severity], "#{line} #{data}") rescue nil
|
196
|
-
end
|
197
|
-
end
|
198
|
-
end
|
199
|
-
|
200
|
-
if @console_print && severity >= @stderr_level
|
201
|
-
STDERR.puts raw_message
|
202
|
-
elsif @console_print && severity >= @stdout_level
|
203
|
-
STDOUT.puts raw_message
|
305
|
+
full_message = if raw_message
|
306
|
+
raw_message
|
307
|
+
elsif block
|
308
|
+
block.call
|
309
|
+
else
|
310
|
+
"Error! Logger called with no message or block to execute! Trace: #{caller.join(", ")}"
|
204
311
|
end
|
205
|
-
|
206
|
-
|
207
|
-
|
312
|
+
full_message = clean(full_message)
|
313
|
+
# Lifted from Logger::Formatter
|
314
|
+
ruby_logger_severity = RUBY_LOGGER_SEV_LABELS[severity]
|
315
|
+
ruby_logger_message = "%s, [%s#%d] %5s -- %s: %s" % [ruby_logger_severity[0..0],
|
316
|
+
(time.strftime("%Y-%m-%dT%H:%M:%S.") << "%06d " % time.usec),
|
317
|
+
$$, ruby_logger_severity, "", full_message]
|
318
|
+
|
319
|
+
@loggers.each do |sink|
|
320
|
+
next if (sink["min_level"] && severity < string_to_severity(sink["min_level"])) ||
|
321
|
+
(sink["max_level"] && severity > string_to_severity(sink["max_level"])) ||
|
322
|
+
sink["logger"].nil?
|
323
|
+
message = sink["logger_prefix?"] ? ruby_logger_message : full_message
|
324
|
+
message += " #{data}" if sink["logger_data?"]
|
325
|
+
if sink["logger"].respond_to?(:send_message)
|
326
|
+
sink["logger"].send_message(severity, message, app_data, time, data)
|
327
|
+
else
|
328
|
+
message += "\n" if sink["newline?"] && sink["newline?"] != "false"
|
329
|
+
if sink["color"]
|
330
|
+
color = (COLORS.include? sink["color"].to_sym) ? sink["color"].to_sym : sink["color"]
|
331
|
+
message = message.color(color)
|
332
|
+
end
|
333
|
+
sink["logger"] << message
|
334
|
+
end rescue nil
|
208
335
|
end
|
209
336
|
|
210
337
|
true
|
@@ -256,4 +383,52 @@ module Termite
|
|
256
383
|
return message
|
257
384
|
end
|
258
385
|
end
|
386
|
+
|
387
|
+
def FakeLogger
|
388
|
+
Termite::LOGGER_SYSLOG_MAP.each_key do |key|
|
389
|
+
define_method(LOGGER_LEVEL_MAP[key]) do
|
390
|
+
# Do nothing
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
# Alias the other methods to a do-nothing method
|
395
|
+
alias :add :error
|
396
|
+
alias :<< :error
|
397
|
+
alias :log :error
|
398
|
+
alias :silence :error
|
399
|
+
alias :add_logger :error
|
400
|
+
|
401
|
+
# Read and write level
|
402
|
+
attr_accessor :level
|
403
|
+
|
404
|
+
# For now, don't read an Ecology, just mock out these accessors.
|
405
|
+
|
406
|
+
def stdout_level
|
407
|
+
4
|
408
|
+
end
|
409
|
+
|
410
|
+
def stderr_level
|
411
|
+
2
|
412
|
+
end
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
module Termite
|
417
|
+
module Thread
|
418
|
+
def self.new(*args, &block)
|
419
|
+
::Thread.new do
|
420
|
+
begin
|
421
|
+
block.call
|
422
|
+
rescue ::Exception
|
423
|
+
if args[0].respond_to?(:warn)
|
424
|
+
logger = args[0]
|
425
|
+
else
|
426
|
+
logger = ::Termite::Logger.new(*args)
|
427
|
+
end
|
428
|
+
logger.warn "Exception in thread: #{$!.message}"
|
429
|
+
logger.warn " Backtrace:\n#{$!.backtrace.join("\n")}"
|
430
|
+
end
|
431
|
+
end
|
432
|
+
end
|
433
|
+
end
|
259
434
|
end
|