termite 0.0.10 → 0.0.20
Sign up to get free protection for your applications and to get access to all the features.
- 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
|