termite 0.0.2 → 0.0.10
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +29 -0
- data/LICENSE +19 -0
- data/README.md +131 -0
- data/Rakefile +20 -0
- data/TODO +3 -0
- data/lib/termite/version.rb +3 -0
- data/lib/termite.rb +259 -0
- data/termite.gemspec +35 -0
- data/test/ecology_log_test.rb +59 -0
- data/test/extra_logger_test.rb +27 -0
- data/test/log_locally.rb +6 -0
- data/test/rescue_test.rb +49 -0
- data/test/stderr_log_test.rb +38 -0
- data/test/termite_logger_test.rb +85 -0
- data/test/termite_subclass_test.rb +29 -0
- data/test/test_helper.rb +51 -0
- metadata +128 -26
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
*.gem
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
termite (0.0.10)
|
5
|
+
ecology (~> 0.0.6)
|
6
|
+
multi_json
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: http://rubygems.org/
|
10
|
+
remote: http://gems.sv2/
|
11
|
+
specs:
|
12
|
+
ecology (0.0.7)
|
13
|
+
multi_json
|
14
|
+
minitest (2.3.0)
|
15
|
+
mocha (0.9.12)
|
16
|
+
multi_json (1.0.3)
|
17
|
+
rake (0.9.2)
|
18
|
+
scope (0.2.1)
|
19
|
+
minitest
|
20
|
+
|
21
|
+
PLATFORMS
|
22
|
+
ruby
|
23
|
+
|
24
|
+
DEPENDENCIES
|
25
|
+
bundler (~> 1.0.10)
|
26
|
+
mocha
|
27
|
+
rake
|
28
|
+
scope (~> 0.2.1)
|
29
|
+
termite!
|
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2011 Ooyala, Inc.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
Termite
|
2
|
+
=======
|
3
|
+
|
4
|
+
Termite is a gem to handle local logs. Specifically, it logs to
|
5
|
+
Syslog in a simple component-based manner, records thread IDs and
|
6
|
+
serializes JSON data.
|
7
|
+
|
8
|
+
It uses, but doesn't depend on, an Ecology file to specify things
|
9
|
+
like the default application name and other logging settings.
|
10
|
+
|
11
|
+
Installing
|
12
|
+
==========
|
13
|
+
|
14
|
+
"gem install termite" works pretty well. You can also specify Termite
|
15
|
+
from a Gemfile if you're using Bundler (and you should).
|
16
|
+
|
17
|
+
Ooyalans should make sure that "gems.sv2" is listed as a gem source in
|
18
|
+
your Gemfile or on your gem command line.
|
19
|
+
|
20
|
+
Logging Dynamically
|
21
|
+
===================
|
22
|
+
|
23
|
+
Create a logger with something like:
|
24
|
+
|
25
|
+
@logger = Termite::Logger.new
|
26
|
+
|
27
|
+
Then use it like a regular logger, possibly with options:
|
28
|
+
|
29
|
+
@logger.add(Logger::DEBUG, "so, like, this thing happened, right?", {}, :component => :ValleyGirl)
|
30
|
+
@logger.warn { "And I was like, whoah!" }
|
31
|
+
|
32
|
+
You can also pass in JSON data after your message and before your options:
|
33
|
+
|
34
|
+
@logger.fatal("Pain and misery!", { :where_it_hurts => "elbow" }, :component => "WhinyLib")
|
35
|
+
@logger.info("I ache", {:where_it_hurts => "kidney"}, :application => "NotMe", :component => "StoicLib")
|
36
|
+
|
37
|
+
Termite also supports full Ruby Logger initialize parameters for backward compatibility:
|
38
|
+
|
39
|
+
@logger = Termite::Logger.new("/var/lib/daily_termite_logs", "daily")
|
40
|
+
@logger = Termite::Logger.new("/tmp/rotatable.txt", 15, 1024000) # Up to 15 logs of size 1024000
|
41
|
+
|
42
|
+
Log Level
|
43
|
+
=========
|
44
|
+
|
45
|
+
Termite loggers, like Ruby loggers, allow you to set the logger's level. All events below that
|
46
|
+
level will be silently discarded, and will also not be sent to non-syslog loggers. You can change
|
47
|
+
the level with "logger.level = Logger::WARN" and similar.
|
48
|
+
|
49
|
+
By default, Termite will log events of all severities to standard output, and events of at least
|
50
|
+
severity :error to standard error. You can set .stdout_level and .stderr_level just like setting
|
51
|
+
.level above. Level takes precedence over the other two and stderr_level takes precedence over
|
52
|
+
stdout_level.
|
53
|
+
|
54
|
+
Non-Syslog Logging
|
55
|
+
==================
|
56
|
+
|
57
|
+
If a filename or handle is specified in the constructor, Termite will instantiate a Ruby Logger
|
58
|
+
with the same parameters and mirror all events to it.
|
59
|
+
|
60
|
+
You can pass in objects with an :add method to a Termite logger's "add_extra_logger" method, and
|
61
|
+
from then on all events will be mirrored to that object. This can be useful for chaining loggers
|
62
|
+
if you need to. Events below the Termite logger's level (see above) won't be mirrored.
|
63
|
+
|
64
|
+
Translating to SysLog
|
65
|
+
=====================
|
66
|
+
|
67
|
+
When writing to SysLog, Termite translates Ruby Logger severities into SysLog severities. By
|
68
|
+
default, this is the mapping:
|
69
|
+
|
70
|
+
Logger => SysLog
|
71
|
+
:unknown => :alert
|
72
|
+
:fatal => :crit
|
73
|
+
:error => :err
|
74
|
+
:warn => :warning
|
75
|
+
:info => :info
|
76
|
+
:debug => :debug
|
77
|
+
|
78
|
+
Configuring with an Ecology
|
79
|
+
===========================
|
80
|
+
|
81
|
+
Termite supports a standard Ecology file. By default, it will look at the location of the current
|
82
|
+
executable ($0) with extension .ecology. So "bob.rb" would have "bob.ecology" next to it.
|
83
|
+
|
84
|
+
An Ecology is a JSON file of roughly this structure:
|
85
|
+
|
86
|
+
{
|
87
|
+
"application": "MyApp",
|
88
|
+
"logging": {
|
89
|
+
"default_component": "SplodgingLib",
|
90
|
+
"extra_json_fields": {
|
91
|
+
"app_group": "SuperSpiffyGroup",
|
92
|
+
"precedence": 7
|
93
|
+
},
|
94
|
+
"console_print": "off",
|
95
|
+
"filename": "/tmp/bobo.txt",
|
96
|
+
"shift_age": 10,
|
97
|
+
"shift_size": 1024000,
|
98
|
+
"stderr_level": "fatal"
|
99
|
+
}
|
100
|
+
}
|
101
|
+
|
102
|
+
Absolutely every part of it is optional, including the presence of the file at all.
|
103
|
+
|
104
|
+
You can override the application name, as shown above. Other than the application name,
|
105
|
+
all Termite-specific parameters are under the "logging" field, as above.
|
106
|
+
|
107
|
+
The default_component is what application component is given for an add() call by default.
|
108
|
+
If set, it can be removed with ":component => nil" for a given add() call.
|
109
|
+
|
110
|
+
Extra JSON fields are added to the JSON data of every add() call.
|
111
|
+
|
112
|
+
Console_print can be set to "off" (or "no" or "0") to turn off Termite printing to stderr
|
113
|
+
and stdout by default at different log levels.
|
114
|
+
|
115
|
+
Filename, shift_age and shift_size are the same as Ruby Logger's normal initialize
|
116
|
+
parameters. The first is the filename to log to, the second is how many total log files
|
117
|
+
to keep, and the third is how large each log file can grow. Or the second can be set to
|
118
|
+
a value like "daily" or "monthly", and then the third is irrelevant.
|
119
|
+
|
120
|
+
You can also set level, stdout_level and stderr_level explained above. We allow numerical
|
121
|
+
(syslog) values, or standard names of Ruby Logger severities.
|
122
|
+
|
123
|
+
Releasing within Ooyala
|
124
|
+
=======================
|
125
|
+
|
126
|
+
Ooyalans, to release Termite to gems.sv2, use the following:
|
127
|
+
|
128
|
+
gem build
|
129
|
+
rake _0.8.7_ -f ../ooyala_gems.rake gem:push termite-0.0.1.gem
|
130
|
+
|
131
|
+
Change the version to the actual version you'd like to push.
|
data/Rakefile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require "bundler"
|
2
|
+
require "rake/testtask"
|
3
|
+
|
4
|
+
require File.join(File.dirname(__FILE__), "lib", "termite", "version")
|
5
|
+
|
6
|
+
Rake::TestTask.new do |t|
|
7
|
+
t.libs << "test"
|
8
|
+
t.test_files = Dir.glob("test/**/*test.rb")
|
9
|
+
t.verbose = true
|
10
|
+
end
|
11
|
+
|
12
|
+
desc 'Builds the gem'
|
13
|
+
task :build do
|
14
|
+
sh "gem build termite.gemspec"
|
15
|
+
end
|
16
|
+
|
17
|
+
desc 'Builds and installs the gem'
|
18
|
+
task :install => :build do
|
19
|
+
sh "gem install termite-#{Termite::VERSION}"
|
20
|
+
end
|
data/TODO
ADDED
data/lib/termite.rb
ADDED
@@ -0,0 +1,259 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "termite/version"
|
3
|
+
|
4
|
+
require "ecology"
|
5
|
+
require "multi_json"
|
6
|
+
|
7
|
+
require "thread"
|
8
|
+
require "syslog"
|
9
|
+
require "logger"
|
10
|
+
require "socket"
|
11
|
+
|
12
|
+
module Termite
|
13
|
+
class Logger
|
14
|
+
##
|
15
|
+
# Maps Logger severities to syslog(3) severities.
|
16
|
+
|
17
|
+
LOGGER_MAP = {
|
18
|
+
:unknown => :alert,
|
19
|
+
:fatal => :crit,
|
20
|
+
:error => :err,
|
21
|
+
:warn => :warning,
|
22
|
+
:info => :info,
|
23
|
+
:debug => :debug,
|
24
|
+
}
|
25
|
+
|
26
|
+
##
|
27
|
+
# Maps Ruby Logger log levels to their values so we can silence.
|
28
|
+
|
29
|
+
LOGGER_LEVEL_MAP = {}
|
30
|
+
|
31
|
+
LOGGER_MAP.each_key do |key|
|
32
|
+
LOGGER_LEVEL_MAP[key] = ::Logger.const_get key.to_s.upcase
|
33
|
+
end
|
34
|
+
|
35
|
+
##
|
36
|
+
# Maps Logger numerical log level values to syslog log levels.
|
37
|
+
|
38
|
+
LEVEL_LOGGER_MAP = {}
|
39
|
+
|
40
|
+
LOGGER_LEVEL_MAP.invert.each do |level, severity|
|
41
|
+
LEVEL_LOGGER_MAP[level] = LOGGER_MAP[severity]
|
42
|
+
end
|
43
|
+
|
44
|
+
SYSLOG_SEVERITY_MAP = {}
|
45
|
+
|
46
|
+
LOGGER_MAP.values.each do |syslog_severity|
|
47
|
+
SYSLOG_SEVERITY_MAP[syslog_severity] = ::Syslog.const_get("LOG_" + syslog_severity.to_s.upcase)
|
48
|
+
end
|
49
|
+
|
50
|
+
##
|
51
|
+
# Builds a methods for level +meth+.
|
52
|
+
|
53
|
+
def self.make_methods(meth)
|
54
|
+
eval <<-EOM, nil, __FILE__, __LINE__ + 1
|
55
|
+
def #{meth}(message = nil, data = {}, options = {}, &block)
|
56
|
+
return true if #{LOGGER_LEVEL_MAP[meth]} < @level
|
57
|
+
add(::Logger::#{meth.to_s.upcase}, message, data, options, &block)
|
58
|
+
end
|
59
|
+
|
60
|
+
def #{meth}?
|
61
|
+
@level <= ::Logger::#{meth.to_s.upcase}
|
62
|
+
end
|
63
|
+
EOM
|
64
|
+
end
|
65
|
+
|
66
|
+
LOGGER_MAP.each_key do |level|
|
67
|
+
make_methods level
|
68
|
+
end
|
69
|
+
|
70
|
+
##
|
71
|
+
# Log level for Logger compatibility.
|
72
|
+
|
73
|
+
attr_accessor :level
|
74
|
+
attr_reader :stdout_level
|
75
|
+
attr_reader :stderr_level
|
76
|
+
attr_reader :file_logger
|
77
|
+
|
78
|
+
def initialize(logdev = nil, shift_age = 0, shift_size = 1048576, options = {})
|
79
|
+
options = logdev if(logdev.is_a?(Hash))
|
80
|
+
|
81
|
+
Ecology.read
|
82
|
+
|
83
|
+
read_ecology_data
|
84
|
+
@log_filename ||= logdev
|
85
|
+
|
86
|
+
@file_logger = ::Logger.new(@log_filename, @shift_age || shift_age, @shift_size || shift_size) if @log_filename
|
87
|
+
@extra_loggers << @file_logger if @file_logger
|
88
|
+
|
89
|
+
# For UDP socket
|
90
|
+
@server_addr = options[:address] || "0.0.0.0"
|
91
|
+
@server_port = options[:port] ? options[:port].to_i : 514
|
92
|
+
|
93
|
+
@@sockets ||= {}
|
94
|
+
key = "#{@server_addr}:#{@server_port}"
|
95
|
+
@@sockets[key] ||= UDPSocket.new
|
96
|
+
@socket = @@sockets[key]
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
def string_to_severity(str)
|
102
|
+
orig_string = str
|
103
|
+
str = str.strip.downcase
|
104
|
+
return str.to_i if str =~ /\d+/
|
105
|
+
ret = LOGGER_LEVEL_MAP[str.to_sym]
|
106
|
+
raise "Unknown logger severity #{orig_string}" unless ret
|
107
|
+
ret
|
108
|
+
end
|
109
|
+
|
110
|
+
def read_ecology_data
|
111
|
+
@application = Ecology.application
|
112
|
+
|
113
|
+
@extra_loggers = []
|
114
|
+
|
115
|
+
# @console_print defaults to "yes", but can be nil if "no", "off" or "0" is specified
|
116
|
+
@console_print = Ecology.property("logging::console_print") || "yes"
|
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
|
129
|
+
|
130
|
+
@default_fields = Ecology.property("logging::extra_json_fields") || {}
|
131
|
+
|
132
|
+
@level ||= ::Logger::DEBUG
|
133
|
+
@stdout_level ||= ::Logger::INFO
|
134
|
+
@stderr_level ||= ::Logger::ERROR
|
135
|
+
end
|
136
|
+
|
137
|
+
public
|
138
|
+
|
139
|
+
def add_logger(logger)
|
140
|
+
@extra_loggers << logger
|
141
|
+
end
|
142
|
+
|
143
|
+
def socket
|
144
|
+
@socket
|
145
|
+
end
|
146
|
+
|
147
|
+
def raw_add(severity, message = nil, data = nil, options = {}, &block)
|
148
|
+
raw_message = message
|
149
|
+
# Severity is a numerical severity using Ruby Logger's scale
|
150
|
+
severity ||= ::Logger::UNKNOWN
|
151
|
+
return true if severity < @level
|
152
|
+
|
153
|
+
application = options[:application] || @application
|
154
|
+
|
155
|
+
component ||= @default_component
|
156
|
+
component = options[:component] if options.has_key?(:component)
|
157
|
+
if component
|
158
|
+
application += ":" + component
|
159
|
+
end
|
160
|
+
|
161
|
+
data ||= {}
|
162
|
+
if data.is_a?(Hash)
|
163
|
+
data = @default_fields.merge(data)
|
164
|
+
data = MultiJson.encode(data)
|
165
|
+
elsif data.is_a?(String)
|
166
|
+
# Can't merge a JSON string with default data
|
167
|
+
raise "Can't merge a JSON string with extra fields!" unless @default_fields.empty?
|
168
|
+
else
|
169
|
+
raise "Unknown data object passed as JSON!"
|
170
|
+
end
|
171
|
+
|
172
|
+
tid = Ecology.thread_id(Thread.current)
|
173
|
+
time = Time.now
|
174
|
+
day = time.strftime("%b %d").sub(/0(\d)/, ' \\1')
|
175
|
+
time_of_day = time.strftime("%T")
|
176
|
+
hostname = Socket.gethostname
|
177
|
+
tag = Syslog::LOG_LOCAL6 + SYSLOG_SEVERITY_MAP[LEVEL_LOGGER_MAP[severity]]
|
178
|
+
|
179
|
+
syslog_string = "<#{tag}>#{day} #{time_of_day} #{hostname} #{application} [#{Process.pid}]: [#{tid}] "
|
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
|
204
|
+
end
|
205
|
+
|
206
|
+
@extra_loggers.each do |logger|
|
207
|
+
logger.send(ruby_severity, raw_message) rescue nil
|
208
|
+
end
|
209
|
+
|
210
|
+
true
|
211
|
+
end
|
212
|
+
|
213
|
+
def add(*args)
|
214
|
+
begin
|
215
|
+
raw_add(*args)
|
216
|
+
rescue Exception => e
|
217
|
+
STDERR.puts("Couldn't log via Termite! Failing! Arguments:")
|
218
|
+
STDERR.puts(args.inspect)
|
219
|
+
STDERR.puts("Exception: #{e.message}")
|
220
|
+
STDERR.puts("Backtrace: #{e.backtrace.inspect}")
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
alias :log :add
|
225
|
+
|
226
|
+
# This isn't perfect. "Real" Ruby loggers use << to mean
|
227
|
+
# "write directly to the underlying store without adding
|
228
|
+
# headers or other cruft". That's meaningful for file
|
229
|
+
# loggers, but not for syslog.
|
230
|
+
def << (message)
|
231
|
+
add(::Logger::INFO, message)
|
232
|
+
end
|
233
|
+
|
234
|
+
##
|
235
|
+
# Allows messages of a particular log level to be ignored temporarily.
|
236
|
+
#
|
237
|
+
# Can you say "Broken Windows"?
|
238
|
+
|
239
|
+
def silence(temporary_level = ::Logger::ERROR)
|
240
|
+
old_logger_level = @level
|
241
|
+
@level = temporary_level
|
242
|
+
yield
|
243
|
+
ensure
|
244
|
+
@level = old_logger_level
|
245
|
+
end
|
246
|
+
|
247
|
+
private
|
248
|
+
|
249
|
+
##
|
250
|
+
# Clean up messages so they're nice and pretty.
|
251
|
+
|
252
|
+
def clean(message)
|
253
|
+
message = message.to_s.dup
|
254
|
+
message.strip!
|
255
|
+
message.gsub!(/\e\[[^m]*m/, '') # remove useless ansi color codes
|
256
|
+
return message
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
data/termite.gemspec
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
|
4
|
+
require "termite/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "termite"
|
8
|
+
s.version = Termite::VERSION
|
9
|
+
s.platform = Gem::Platform::RUBY
|
10
|
+
s.authors = ["Noah Gibbs"]
|
11
|
+
s.email = ["noah@ooyala.com"]
|
12
|
+
s.homepage = "http://www.ooyala.com"
|
13
|
+
s.summary = %q{Ruby logging based on Syslog}
|
14
|
+
s.description = <<EOS
|
15
|
+
Termite wraps syslog with a format for extra data, and for
|
16
|
+
what you wish it would send automatically.
|
17
|
+
EOS
|
18
|
+
|
19
|
+
s.rubyforge_project = "termite"
|
20
|
+
|
21
|
+
ignores = File.readlines(".gitignore").grep(/\S+/).map {|pattern| pattern.chomp }
|
22
|
+
dotfiles = Dir[".*"]
|
23
|
+
s.files = Dir["**/*"].reject {|f| File.directory?(f) || ignores.any? {|i| File.fnmatch(i, f) } } + dotfiles
|
24
|
+
s.test_files = s.files.grep(/^test\//)
|
25
|
+
|
26
|
+
s.require_paths = ["lib"]
|
27
|
+
|
28
|
+
s.add_dependency "multi_json"
|
29
|
+
s.add_dependency "ecology", "~>0.0.6"
|
30
|
+
|
31
|
+
s.add_development_dependency "bundler", "~> 1.0.10"
|
32
|
+
s.add_development_dependency "scope", "~> 0.2.1"
|
33
|
+
s.add_development_dependency "mocha"
|
34
|
+
s.add_development_dependency "rake"
|
35
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "test_helper.rb")
|
2
|
+
|
3
|
+
class EcologyLogTest < Scope::TestCase
|
4
|
+
context "with a custom ecology" do
|
5
|
+
setup do
|
6
|
+
Ecology.reset
|
7
|
+
|
8
|
+
set_up_ecology <<ECOLOGY_CONTENTS
|
9
|
+
{
|
10
|
+
"application": "MyApp",
|
11
|
+
"logging": {
|
12
|
+
"default_component": "SplodgingLib",
|
13
|
+
"extra_json_fields": {
|
14
|
+
"app_group": "SuperSpiffyGroup",
|
15
|
+
"precedence": 7
|
16
|
+
},
|
17
|
+
"console_print": "off",
|
18
|
+
"filename": "/tmp/bobo.txt",
|
19
|
+
"shift_age": 10,
|
20
|
+
"shift_size": 1024000
|
21
|
+
}
|
22
|
+
}
|
23
|
+
ECOLOGY_CONTENTS
|
24
|
+
end
|
25
|
+
|
26
|
+
context "with a default termite logger" do
|
27
|
+
setup do
|
28
|
+
@logger = Termite::Logger.new
|
29
|
+
end
|
30
|
+
|
31
|
+
should "send back extra JSON data and a default component when specified" do
|
32
|
+
expect_add(@logger.socket, 2, 'oh no! {"app_group":"SuperSpiffyGroup","precedence":7}', :application => "MyApp:SplodgingLib")
|
33
|
+
@logger.fatal("oh no!")
|
34
|
+
end
|
35
|
+
|
36
|
+
should "allow overriding the default component" do
|
37
|
+
expect_add(@logger.socket, 2, 'oh no! {"app_group":"SuperSpiffyGroup","precedence":7}', :application => "MyApp:SpliyingLib")
|
38
|
+
@logger.fatal("oh no!", {}, :component => "SpliyingLib")
|
39
|
+
end
|
40
|
+
|
41
|
+
should "allow overriding the default component with nothing" do
|
42
|
+
expect_add(@logger.socket, 2, 'oh no! {"app_group":"SuperSpiffyGroup","precedence":7}', :application => "MyApp")
|
43
|
+
@logger.fatal("oh no!", {}, :component => nil)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
should "pass initialize parameters to Ruby Logger" do
|
48
|
+
log_mock = mock("Ruby Logger")
|
49
|
+
::Logger.expects(:new).with("/tmp/bobo.txt", 10, 1024000).returns(log_mock)
|
50
|
+
Termite::Logger.new
|
51
|
+
end
|
52
|
+
|
53
|
+
should "override parameters passed to Termite Logger" do
|
54
|
+
log_mock = mock("Ruby Logger")
|
55
|
+
::Logger.expects(:new).with("/tmp/bobo.txt", 10, 1024000).returns(log_mock)
|
56
|
+
Termite::Logger.new("/var/lib/sam.log", "daily")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "test_helper.rb")
|
2
|
+
|
3
|
+
class TermiteExtraLoggerTest < Scope::TestCase
|
4
|
+
context "with termite" do
|
5
|
+
setup do
|
6
|
+
$0 = "MyApp"
|
7
|
+
Ecology.reset
|
8
|
+
end
|
9
|
+
|
10
|
+
context "and two extra loggers added" do
|
11
|
+
setup do
|
12
|
+
@logger = Termite::Logger.new("/tmp/test_log_output.txt") # Test with output file
|
13
|
+
@logger.level = Logger::DEBUG
|
14
|
+
@logger.socket.expects(:send)
|
15
|
+
@mock_logger_1 = mock()
|
16
|
+
@logger.add_logger(@mock_logger_1)
|
17
|
+
@logger.add_logger(@mock_logger_2)
|
18
|
+
end
|
19
|
+
|
20
|
+
should "correctly send logs to Syslog" do
|
21
|
+
@mock_logger_1.expects(:fatal)
|
22
|
+
@mock_logger_2.expects(:fatal)
|
23
|
+
@logger.add(Logger::FATAL, "foo!", {})
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/test/log_locally.rb
ADDED
data/test/rescue_test.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "test_helper.rb")
|
2
|
+
require "syslog"
|
3
|
+
|
4
|
+
class RescueTest < Scope::TestCase
|
5
|
+
context "with termite" do
|
6
|
+
setup do
|
7
|
+
Ecology.reset
|
8
|
+
@logger = Termite::Logger.new("/tmp/termite_test.log")
|
9
|
+
end
|
10
|
+
|
11
|
+
should "continue even if all loggers raise errors" do
|
12
|
+
# First, UDP will raise an error
|
13
|
+
@logger.socket.expects(:send).raises(StandardError, "You suck!")
|
14
|
+
|
15
|
+
# Termite should fall back to trying Ruby Syslog...
|
16
|
+
syslog_mock = mock("Syslog connection")
|
17
|
+
Syslog.expects(:open).yields(syslog_mock)
|
18
|
+
syslog_mock.expects(:error).with("UDP syslog failed! Falling back to libc syslog!")
|
19
|
+
syslog_mock.expects(:crit).raises(StandardError, "You suck even more than that!")
|
20
|
+
|
21
|
+
# And it should still try to write to a file logger
|
22
|
+
@logger.file_logger.expects(:fatal).raises(StandardError, "You suck lots!")
|
23
|
+
|
24
|
+
extra_logger = mock("Additional logger")
|
25
|
+
@logger.add_logger(extra_logger)
|
26
|
+
# And it should try to write to any extra loggers
|
27
|
+
extra_logger.expects(:fatal).raises(StandardError, "You suck constantly!")
|
28
|
+
|
29
|
+
# And yet, nothing should be raised to the outside world
|
30
|
+
begin
|
31
|
+
@logger.fatal("Woe is me!")
|
32
|
+
assert true, "Nothing was raised! Yay!"
|
33
|
+
rescue Exception
|
34
|
+
flunk "Logging an event raised an assertion outside the logger!"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
should "continue even if internal logic gives an error" do
|
39
|
+
#Time.expects(:now).raises(Exception.new "You suck!")
|
40
|
+
Ecology.expects(:thread_id).raises(Exception.new "Ecology thread_id dies!")
|
41
|
+
begin
|
42
|
+
@logger.fatal("Woe is me!")
|
43
|
+
assert true, "Nothing was raised! Yay!"
|
44
|
+
rescue Exception
|
45
|
+
flunk "Logging an event raised an assertion outside the logger!"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "test_helper.rb")
|
2
|
+
|
3
|
+
class EcologyLogTest < Scope::TestCase
|
4
|
+
context "with a custom ecology" do
|
5
|
+
setup do
|
6
|
+
Ecology.reset
|
7
|
+
|
8
|
+
set_up_ecology <<ECOLOGY_CONTENTS
|
9
|
+
{
|
10
|
+
"application": "MyApp",
|
11
|
+
"logging": {
|
12
|
+
"stdout_level": "warn",
|
13
|
+
"stderr_level": "fatal"
|
14
|
+
}
|
15
|
+
}
|
16
|
+
ECOLOGY_CONTENTS
|
17
|
+
end
|
18
|
+
|
19
|
+
context "with a default termite logger" do
|
20
|
+
setup do
|
21
|
+
@logger = Termite::Logger.new
|
22
|
+
end
|
23
|
+
|
24
|
+
should "log fatal errors to STDERR" do
|
25
|
+
expect_console_add(STDERR, 2, 'oh no!', :application => "MyApp", :method => :puts, :extra_args => [])
|
26
|
+
STDOUT.expects(:puts).never
|
27
|
+
@logger.fatal("oh no!")
|
28
|
+
end
|
29
|
+
|
30
|
+
should "log warnings to STDOUT" do
|
31
|
+
expect_console_add(STDOUT, 4, 'oh no!', :application => "MyApp", :method => :puts, :extra_args => [])
|
32
|
+
STDERR.expects(:puts).never
|
33
|
+
@logger.warn("oh no!")
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "test_helper.rb")
|
2
|
+
require "syslog"
|
3
|
+
|
4
|
+
class TermiteLoggerTest < Scope::TestCase
|
5
|
+
context "with termite ecology" do
|
6
|
+
setup do
|
7
|
+
Ecology.reset
|
8
|
+
|
9
|
+
set_up_ecology <<ECOLOGY_TEXT
|
10
|
+
{
|
11
|
+
"application": "foo_app"
|
12
|
+
}
|
13
|
+
ECOLOGY_TEXT
|
14
|
+
end
|
15
|
+
|
16
|
+
context "and fully permissive logging levels set" do
|
17
|
+
setup do
|
18
|
+
@logger = Termite::Logger.new("/tmp/test_log_output.txt") # Test with output file
|
19
|
+
@logger.level = Logger::DEBUG
|
20
|
+
end
|
21
|
+
|
22
|
+
should "correctly send logs to Syslog" do
|
23
|
+
expect_add(@logger.socket, 2, "foo! {}")
|
24
|
+
@logger.add(Logger::FATAL, "foo!", {})
|
25
|
+
end
|
26
|
+
|
27
|
+
should "correctly alias log to add" do
|
28
|
+
expect_add(@logger.socket, 2, "foo! {}")
|
29
|
+
@logger.log(Logger::FATAL, "foo!", {})
|
30
|
+
end
|
31
|
+
|
32
|
+
should "treat << as add-with-info" do
|
33
|
+
expect_add(@logger.socket, 6, "foo! {}")
|
34
|
+
@logger << "foo!"
|
35
|
+
end
|
36
|
+
|
37
|
+
should "correctly send an alert to Syslog" do
|
38
|
+
expect_add(@logger.socket, 1, "foo! {}")
|
39
|
+
@logger.add(Logger::UNKNOWN, "foo!", {})
|
40
|
+
end
|
41
|
+
|
42
|
+
should "correctly send a critical event to Syslog" do
|
43
|
+
expect_add(@logger.socket, 2, "foo! {}")
|
44
|
+
@logger.fatal("foo!")
|
45
|
+
end
|
46
|
+
|
47
|
+
should "correctly send an error event to Syslog" do
|
48
|
+
expect_add(@logger.socket, 3, "foo! {}")
|
49
|
+
@logger.error("foo!")
|
50
|
+
end
|
51
|
+
|
52
|
+
should "correctly send a warning event to Syslog" do
|
53
|
+
expect_add(@logger.socket, 4, "foo! {}")
|
54
|
+
@logger.warn("foo!")
|
55
|
+
end
|
56
|
+
|
57
|
+
should "correctly send an info event to Syslog" do
|
58
|
+
expect_add(@logger.socket, 6, "foo! {}")
|
59
|
+
@logger.info("foo!")
|
60
|
+
end
|
61
|
+
|
62
|
+
should "correctly send a debug event to Syslog" do
|
63
|
+
expect_add(@logger.socket, 7, "foo! {}")
|
64
|
+
@logger.debug("foo!")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context "and default setup for components" do
|
69
|
+
setup do
|
70
|
+
@logger = Termite::Logger.new("/tmp/test_log_output.txt") # Test with output file
|
71
|
+
end
|
72
|
+
|
73
|
+
should "allow overriding application name" do
|
74
|
+
expect_add(@logger.socket, 2, "foo! {}", :application => "bar_app")
|
75
|
+
@logger.fatal("foo!", {}, :application => "bar_app")
|
76
|
+
end
|
77
|
+
|
78
|
+
should "allow setting a component" do
|
79
|
+
expect_add(@logger.socket, 2, "foo! {}", :application => "foo_app:whatcomponent")
|
80
|
+
@logger.fatal("foo!", {}, :component => "whatcomponent")
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "test_helper.rb")
|
2
|
+
|
3
|
+
class SubLogger < Termite::Logger
|
4
|
+
end
|
5
|
+
|
6
|
+
class TermiteSubclassTest < Scope::TestCase
|
7
|
+
context "with subclassed termite" do
|
8
|
+
setup do
|
9
|
+
Ecology.reset
|
10
|
+
|
11
|
+
set_up_ecology <<ECOLOGY_TEXT
|
12
|
+
{
|
13
|
+
"application": "foo_app"
|
14
|
+
}
|
15
|
+
ECOLOGY_TEXT
|
16
|
+
end
|
17
|
+
|
18
|
+
context "and only default logging levels set" do
|
19
|
+
setup do
|
20
|
+
@logger = SubLogger.new
|
21
|
+
end
|
22
|
+
|
23
|
+
should "correctly send logs to Syslog" do
|
24
|
+
@logger.socket.expects(:send)
|
25
|
+
@logger.add(Logger::FATAL, "foo!", {})
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "bundler"
|
3
|
+
Bundler.require(:default, :development)
|
4
|
+
require "minitest/autorun"
|
5
|
+
|
6
|
+
# For testing Termite itself, use the local version *first*.
|
7
|
+
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), "..", "lib")
|
8
|
+
|
9
|
+
require "termite"
|
10
|
+
|
11
|
+
class Scope::TestCase
|
12
|
+
def set_up_ecology(file_contents, filename = "some.ecology")
|
13
|
+
ENV["ECOLOGY_SPEC"] = filename
|
14
|
+
File.expects(:exist?).with(filename).returns(true)
|
15
|
+
File.expects(:read).with(filename).returns(file_contents)
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize_environment
|
19
|
+
unless @initialized_test_env
|
20
|
+
Time.stubs(:now).returns(Time.at(1315433360))
|
21
|
+
Socket.stubs(:gethostname).returns("samplehost")
|
22
|
+
Process.stubs(:pid).returns("1234")
|
23
|
+
Ecology.stubs(:thread_id).returns("main")
|
24
|
+
|
25
|
+
@initialized_test_env = true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# This adds the Mocha expectation for this call. Technically it also
|
30
|
+
# returns the expectation, so you could modify it later if you wanted.
|
31
|
+
def expect_add(socket, severity_num, message, options = {})
|
32
|
+
initialize_environment
|
33
|
+
|
34
|
+
app = options[:application] || "foo_app"
|
35
|
+
string = "<#{Syslog::LOG_LOCAL6 + severity_num}>Sep 7 15:09:20 samplehost #{app} [1234]: [main] #{message}"
|
36
|
+
|
37
|
+
options[:method] ||= :send
|
38
|
+
options[:extra_args] ||= [0, "0.0.0.0", 514]
|
39
|
+
socket.expects(options[:method]).with(string, *options[:extra_args])
|
40
|
+
end
|
41
|
+
|
42
|
+
def expect_console_add(socket, severity_num, message, options = {})
|
43
|
+
initialize_environment
|
44
|
+
|
45
|
+
app = options[:application] || "foo_app"
|
46
|
+
|
47
|
+
options[:method] ||= :send
|
48
|
+
options[:extra_args] ||= [0, "0.0.0.0", 514]
|
49
|
+
socket.expects(options[:method]).with(message, *options[:extra_args])
|
50
|
+
end
|
51
|
+
end
|
metadata
CHANGED
@@ -1,45 +1,147 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: termite
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.2
|
3
|
+
version: !ruby/object:Gem::Version
|
5
4
|
prerelease:
|
5
|
+
version: 0.0.10
|
6
6
|
platform: ruby
|
7
|
-
authors:
|
8
|
-
-
|
7
|
+
authors:
|
8
|
+
- Noah Gibbs
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
12
|
+
|
13
|
+
date: 2011-09-30 00:00:00 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: multi_json
|
17
|
+
prerelease: false
|
18
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
type: :runtime
|
25
|
+
version_requirements: *id001
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: ecology
|
28
|
+
prerelease: false
|
29
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
30
|
+
none: false
|
31
|
+
requirements:
|
32
|
+
- - ~>
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 0.0.6
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id002
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: bundler
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 1.0.10
|
46
|
+
type: :development
|
47
|
+
version_requirements: *id003
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: scope
|
50
|
+
prerelease: false
|
51
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ~>
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: 0.2.1
|
57
|
+
type: :development
|
58
|
+
version_requirements: *id004
|
59
|
+
- !ruby/object:Gem::Dependency
|
60
|
+
name: mocha
|
61
|
+
prerelease: false
|
62
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: "0"
|
68
|
+
type: :development
|
69
|
+
version_requirements: *id005
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: rake
|
72
|
+
prerelease: false
|
73
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: "0"
|
79
|
+
type: :development
|
80
|
+
version_requirements: *id006
|
81
|
+
description: |
|
82
|
+
Termite wraps syslog with a format for extra data, and for
|
83
|
+
what you wish it would send automatically.
|
84
|
+
|
85
|
+
email:
|
86
|
+
- noah@ooyala.com
|
17
87
|
executables: []
|
88
|
+
|
18
89
|
extensions: []
|
90
|
+
|
19
91
|
extra_rdoc_files: []
|
20
|
-
|
21
|
-
|
92
|
+
|
93
|
+
files:
|
94
|
+
- Gemfile
|
95
|
+
- Gemfile.lock
|
96
|
+
- lib/termite/version.rb
|
97
|
+
- lib/termite.rb
|
98
|
+
- LICENSE
|
99
|
+
- Rakefile
|
100
|
+
- README.md
|
101
|
+
- termite.gemspec
|
102
|
+
- test/ecology_log_test.rb
|
103
|
+
- test/extra_logger_test.rb
|
104
|
+
- test/log_locally.rb
|
105
|
+
- test/rescue_test.rb
|
106
|
+
- test/stderr_log_test.rb
|
107
|
+
- test/termite_logger_test.rb
|
108
|
+
- test/termite_subclass_test.rb
|
109
|
+
- test/test_helper.rb
|
110
|
+
- TODO
|
111
|
+
- .gitignore
|
112
|
+
homepage: http://www.ooyala.com
|
22
113
|
licenses: []
|
114
|
+
|
23
115
|
post_install_message:
|
24
116
|
rdoc_options: []
|
25
|
-
|
117
|
+
|
118
|
+
require_paths:
|
26
119
|
- lib
|
27
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
120
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
28
121
|
none: false
|
29
|
-
requirements:
|
30
|
-
- -
|
31
|
-
- !ruby/object:Gem::Version
|
32
|
-
version:
|
33
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - ">="
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: "0"
|
126
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
34
127
|
none: false
|
35
|
-
requirements:
|
36
|
-
- -
|
37
|
-
- !ruby/object:Gem::Version
|
38
|
-
version:
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: "0"
|
39
132
|
requirements: []
|
133
|
+
|
40
134
|
rubyforge_project: termite
|
41
|
-
rubygems_version: 1.8.
|
135
|
+
rubygems_version: 1.8.10
|
42
136
|
signing_key:
|
43
137
|
specification_version: 3
|
44
|
-
summary:
|
45
|
-
test_files:
|
138
|
+
summary: Ruby logging based on Syslog
|
139
|
+
test_files:
|
140
|
+
- test/ecology_log_test.rb
|
141
|
+
- test/extra_logger_test.rb
|
142
|
+
- test/log_locally.rb
|
143
|
+
- test/rescue_test.rb
|
144
|
+
- test/stderr_log_test.rb
|
145
|
+
- test/termite_logger_test.rb
|
146
|
+
- test/termite_subclass_test.rb
|
147
|
+
- test/test_helper.rb
|