termite 0.0.2 → 0.0.10
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/.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
|