syslog-sd 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +2 -0
- data/LICENSE +20 -0
- data/README.rdoc +21 -0
- data/Rakefile +98 -0
- data/VERSION +1 -0
- data/benchmarks/notifier.rb +36 -0
- data/lib/syslog-sd.rb +7 -0
- data/lib/syslog-sd/logger.rb +55 -0
- data/lib/syslog-sd/notifier.rb +214 -0
- data/lib/syslog-sd/ruby_sender.rb +18 -0
- data/lib/syslog-sd/severity.rb +37 -0
- data/syslog-sd.gemspec +61 -0
- data/test/helper.rb +12 -0
- data/test/test_logger.rb +139 -0
- data/test/test_notifier.rb +214 -0
- data/test/test_ruby_sender.rb +28 -0
- data/test/test_severity.rb +9 -0
- metadata +124 -0
data/CHANGELOG
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010-2011 Lennart Koopmann, Alexey Palazhchenko
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
= syslog-sd
|
2
|
+
|
3
|
+
Super-Duper library to send syslog messages to logging server such as Graylog2 (http://graylog2.org).
|
4
|
+
Supports Structured Data elements as defined by RFC 5424.
|
5
|
+
|
6
|
+
Based on GELF gem (https://github.com/graylog2/gelf-rb).
|
7
|
+
|
8
|
+
Works with Ruby 1.8.7 and 1.9.2. 1.8.6 is not supported.
|
9
|
+
|
10
|
+
== Note on Patches/Pull Requests
|
11
|
+
|
12
|
+
* Fork the project.
|
13
|
+
* Make your feature addition or bug fix.
|
14
|
+
* Add tests for it. This is important so I don't break it in a future version unintentionally.
|
15
|
+
* Commit, do not mess with rakefile, version, or history.
|
16
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
17
|
+
* Send me a pull request. Bonus points for topic branches.
|
18
|
+
|
19
|
+
== Copyright
|
20
|
+
|
21
|
+
Copyright (c) 2010-2011 Lennart Koopmann and Alexey Palazhchenko. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'rake'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'ci/reporter/rake/test_unit'
|
5
|
+
rescue LoadError
|
6
|
+
# nothing
|
7
|
+
end
|
8
|
+
|
9
|
+
begin
|
10
|
+
require 'jeweler'
|
11
|
+
|
12
|
+
Jeweler::Tasks.new do |gem|
|
13
|
+
gem.name = "syslog-sd"
|
14
|
+
gem.summary = 'Library to send syslog messages to logging server such as Graylog2. Supports Structured Data elements as defined by RFC 5424.'
|
15
|
+
gem.description = 'Super-Duper library to send syslog messages to logging server such as Graylog2. ' +
|
16
|
+
'Supports Structured Data elements as defined by RFC 5424.'
|
17
|
+
gem.email = "alexey.palazhchenko@gmail.com"
|
18
|
+
gem.homepage = "http://github.com/AlekSi/syslog-sd-rb"
|
19
|
+
gem.authors = ["Alexey Palazhchenko", "Lennart Koopmann"]
|
20
|
+
gem.add_development_dependency "shoulda"
|
21
|
+
gem.add_development_dependency "mocha"
|
22
|
+
gem.add_development_dependency "timecop"
|
23
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
24
|
+
end
|
25
|
+
rescue LoadError => e
|
26
|
+
puts e
|
27
|
+
abort "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
28
|
+
end
|
29
|
+
|
30
|
+
require 'rake/testtask'
|
31
|
+
Rake::TestTask.new(:test) do |test|
|
32
|
+
test.libs << 'lib' << 'test'
|
33
|
+
test.pattern = 'test/**/test_*.rb'
|
34
|
+
test.verbose = true
|
35
|
+
end
|
36
|
+
|
37
|
+
task :test => :check_dependencies
|
38
|
+
task :default => :test
|
39
|
+
|
40
|
+
begin
|
41
|
+
require 'rcov/rcovtask'
|
42
|
+
Rcov::RcovTask.new do |test|
|
43
|
+
test.libs << 'test'
|
44
|
+
test.pattern = 'test/**/test_*.rb'
|
45
|
+
test.rcov_opts << '--exclude gem'
|
46
|
+
test.verbose = true
|
47
|
+
end
|
48
|
+
rescue LoadError => e
|
49
|
+
task :rcov do
|
50
|
+
puts e
|
51
|
+
abort "rcov is not available. Run: gem install rcov"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
begin
|
56
|
+
gem 'ruby_parser', '~> 2.0.6'
|
57
|
+
gem 'activesupport', '~> 3.0.0'
|
58
|
+
gem 'metric_fu', '~> 2.1.1'
|
59
|
+
require 'metric_fu'
|
60
|
+
|
61
|
+
MetricFu::Configuration.run do |config|
|
62
|
+
# Saikuro is useless
|
63
|
+
config.metrics -= [:saikuro]
|
64
|
+
|
65
|
+
config.flay = { :dirs_to_flay => ['lib'],
|
66
|
+
:minimum_score => 10 }
|
67
|
+
config.flog = { :dirs_to_flog => ['lib'] }
|
68
|
+
config.reek = { :dirs_to_reek => ['lib'] }
|
69
|
+
config.roodi = { :dirs_to_roodi => ['lib'] }
|
70
|
+
config.rcov = { :environment => 'test',
|
71
|
+
:test_files => ['test/test_*.rb'],
|
72
|
+
:rcov_opts => ["-I 'lib:test'",
|
73
|
+
"--sort coverage",
|
74
|
+
"--no-html",
|
75
|
+
"--text-coverage",
|
76
|
+
"--no-color",
|
77
|
+
"--exclude /test/,/gems/"]}
|
78
|
+
config.graph_engine = :gchart
|
79
|
+
end
|
80
|
+
|
81
|
+
rescue LoadError, NameError => e
|
82
|
+
desc 'Generate all metrics reports'
|
83
|
+
task :'metrics:all' do
|
84
|
+
puts e.inspect
|
85
|
+
# puts e.backtrace
|
86
|
+
abort "metric_fu is not available. Run: gem install metric_fu"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
require 'rake/rdoctask'
|
91
|
+
Rake::RDocTask.new do |rdoc|
|
92
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
93
|
+
|
94
|
+
rdoc.rdoc_dir = 'rdoc'
|
95
|
+
rdoc.title = "syslog-sd #{version}"
|
96
|
+
rdoc.rdoc_files.include('README*')
|
97
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
98
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.2.0
|
@@ -0,0 +1,36 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
puts "Loading..."
|
4
|
+
|
5
|
+
require 'benchmark'
|
6
|
+
require 'rubygems'
|
7
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
8
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
9
|
+
require 'syslog-sd'
|
10
|
+
|
11
|
+
puts "Generating random data..."
|
12
|
+
srand(1)
|
13
|
+
RANDOM_DATA = ('A'..'z').to_a
|
14
|
+
k3_message = (1..3*1024).map { RANDOM_DATA[rand(RANDOM_DATA.count)] }.join
|
15
|
+
|
16
|
+
TARGET_HOST = 'localhost'
|
17
|
+
TARGET_PORT = 12201
|
18
|
+
DEFAULT_OPTIONS = { '_host' => 'localhost' }
|
19
|
+
TIMES = 5000
|
20
|
+
|
21
|
+
SHORT_HASH = { 'short_message' => 'message' }
|
22
|
+
LONG_HASH = { 'short_message' => 'message', 'long_message' => k3_message }
|
23
|
+
|
24
|
+
|
25
|
+
notifier = SyslogSD::Notifier.new(TARGET_HOST, TARGET_PORT, DEFAULT_OPTIONS)
|
26
|
+
|
27
|
+
# to create mongo collections, etc.
|
28
|
+
notifier.notify!(LONG_HASH)
|
29
|
+
sleep(5)
|
30
|
+
|
31
|
+
puts "Sending #{TIMES} notifications...\n"
|
32
|
+
tms = Benchmark.bm(25) do |b|
|
33
|
+
b.report('short data') { TIMES.times { notifier.notify!(SHORT_HASH) } }
|
34
|
+
sleep(5)
|
35
|
+
b.report('long data ') { TIMES.times { notifier.notify!(LONG_HASH) } }
|
36
|
+
end
|
data/lib/syslog-sd.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
module SyslogSD
|
2
|
+
# Methods for compatibility with Ruby Logger.
|
3
|
+
module LoggerCompatibility
|
4
|
+
# Does nothing.
|
5
|
+
def close
|
6
|
+
end
|
7
|
+
|
8
|
+
# Use it like Logger#add... or better not to use at all.
|
9
|
+
def add(level, *args)
|
10
|
+
raise ArgumentError.new('Wrong arguments.') unless (0..2).include?(args.count)
|
11
|
+
|
12
|
+
# Ruby Logger's author is a maniac.
|
13
|
+
message, progname = if args.count == 2
|
14
|
+
[args[0], args[1]]
|
15
|
+
elsif args.count == 0
|
16
|
+
[yield, default_options['facility']]
|
17
|
+
elsif block_given?
|
18
|
+
[yield, args[0]]
|
19
|
+
else
|
20
|
+
[args[0], default_options['facility']]
|
21
|
+
end
|
22
|
+
|
23
|
+
hash = {'short_message' => message, 'facility' => progname}
|
24
|
+
hash.merge!(self.class.extract_hash_from_exception(message)) if message.is_a?(Exception)
|
25
|
+
notify_with_level(level, hash)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Redefines methods in +Notifier+.
|
29
|
+
SyslogSD::Levels.constants.each do |const|
|
30
|
+
class_eval <<-EOT, __FILE__, __LINE__ + 1
|
31
|
+
def #{const.downcase}(*args) # def debug(*args)
|
32
|
+
args.unshift(yield) if block_given? # args.unshift(yield) if block_given?
|
33
|
+
add(SyslogSD::#{const}, *args) # add(SyslogSD::DEBUG, *args)
|
34
|
+
end # end
|
35
|
+
|
36
|
+
def #{const.downcase}? # def debug?
|
37
|
+
SyslogSD::#{const} >= level # SyslogSD::DEBUG >= level
|
38
|
+
end # end
|
39
|
+
EOT
|
40
|
+
end
|
41
|
+
|
42
|
+
def <<(message)
|
43
|
+
notify_with_level(SyslogSD::UNKNOWN, 'short_message' => message)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Graylog2 notifier, compatible with Ruby Logger.
|
48
|
+
# You can use it with Rails like this:
|
49
|
+
# config.logger = SyslogSD::Logger.new("localhost", 12201, "WAN", { :facility => "appname" })
|
50
|
+
# config.colorize_logging = false
|
51
|
+
class Logger < Notifier
|
52
|
+
include LoggerCompatibility
|
53
|
+
@last_chunk_id = 0
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,214 @@
|
|
1
|
+
module SyslogSD
|
2
|
+
# syslog notifier.
|
3
|
+
class Notifier
|
4
|
+
attr_accessor :enabled, :collect_file_and_line
|
5
|
+
attr_reader :level, :default_options, :level_mapping
|
6
|
+
|
7
|
+
# +host+ and +port+ are host/ip and port of syslog server.
|
8
|
+
# +default_options+ is used in notify!
|
9
|
+
def initialize(host = 'localhost', port = 514, default_options = {})
|
10
|
+
@enabled = true
|
11
|
+
@collect_file_and_line = true
|
12
|
+
@sd_id = "_@37797"
|
13
|
+
|
14
|
+
self.level = SyslogSD::DEBUG
|
15
|
+
|
16
|
+
self.default_options = default_options
|
17
|
+
self.default_options['host'] ||= Socket.gethostname
|
18
|
+
self.default_options['level'] ||= SyslogSD::UNKNOWN
|
19
|
+
self.default_options['facility'] ||= 'syslog-sd-rb'
|
20
|
+
self.default_options['procid'] ||= Process.pid
|
21
|
+
|
22
|
+
@sender = RubyUdpSender.new([[host, port]])
|
23
|
+
self.level_mapping = :logger
|
24
|
+
end
|
25
|
+
|
26
|
+
# Get a list of receivers.
|
27
|
+
# notifier.addresses # => [['localhost', 12201], ['localhost', 12202]]
|
28
|
+
def addresses
|
29
|
+
@sender.addresses
|
30
|
+
end
|
31
|
+
|
32
|
+
# Set a list of receivers.
|
33
|
+
# notifier.addresses = [['localhost', 12201], ['localhost', 12202]]
|
34
|
+
def addresses=(addrs)
|
35
|
+
@sender.addresses = addrs
|
36
|
+
end
|
37
|
+
|
38
|
+
def host
|
39
|
+
warn "SyslogSD::Notifier#host is deprecated. Use #addresses instead."
|
40
|
+
self.addresses.first[0]
|
41
|
+
end
|
42
|
+
|
43
|
+
def port
|
44
|
+
warn "SyslogSD::Notifier#port is deprecated. Use #addresses instead."
|
45
|
+
self.addresses.first[1]
|
46
|
+
end
|
47
|
+
|
48
|
+
def level=(new_level)
|
49
|
+
@level = if new_level.is_a?(Fixnum)
|
50
|
+
new_level
|
51
|
+
else
|
52
|
+
SyslogSD.const_get(new_level.to_s.upcase)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def default_options=(options)
|
57
|
+
@default_options = self.class.stringify_keys(options)
|
58
|
+
end
|
59
|
+
|
60
|
+
# +mapping+ may be a hash, 'logger' (SyslogSD::LOGGER_MAPPING) or 'direct' (SyslogSD::DIRECT_MAPPING).
|
61
|
+
# Default (compatible) value is 'logger'.
|
62
|
+
def level_mapping=(mapping)
|
63
|
+
case mapping.to_s.downcase
|
64
|
+
when 'logger'
|
65
|
+
@level_mapping = SyslogSD::LOGGER_MAPPING
|
66
|
+
when 'direct'
|
67
|
+
@level_mapping = SyslogSD::DIRECT_MAPPING
|
68
|
+
else
|
69
|
+
@level_mapping = mapping
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def disable
|
74
|
+
@enabled = false
|
75
|
+
end
|
76
|
+
|
77
|
+
def enable
|
78
|
+
@enabled = true
|
79
|
+
end
|
80
|
+
|
81
|
+
# Same as notify!, but rescues all exceptions (including +ArgumentError+)
|
82
|
+
# and sends them instead.
|
83
|
+
def notify(*args)
|
84
|
+
notify_with_level(nil, *args)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Sends message to Graylog2 server.
|
88
|
+
# +args+ can be:
|
89
|
+
# - hash-like object (any object which responds to +to_hash+, including +Hash+ instance):
|
90
|
+
# notify!(:short_message => 'All your rebase are belong to us', :user => 'AlekSi')
|
91
|
+
# - exception with optional hash-like object:
|
92
|
+
# notify!(SecurityError.new('ALARM!'), :trespasser => 'AlekSi')
|
93
|
+
# - string-like object (anything which responds to +to_s+) with optional hash-like object:
|
94
|
+
# notify!('Plain olde text message', :scribe => 'AlekSi')
|
95
|
+
# Resulted fields are merged with +default_options+, the latter will never overwrite the former.
|
96
|
+
# This method will raise +ArgumentError+ if arguments are wrong. Consider using notify instead.
|
97
|
+
def notify!(*args)
|
98
|
+
notify_with_level!(nil, *args)
|
99
|
+
end
|
100
|
+
|
101
|
+
SyslogSD::Levels.constants.each do |const|
|
102
|
+
class_eval <<-EOT, __FILE__, __LINE__ + 1
|
103
|
+
def #{const.downcase}(*args) # def debug(*args)
|
104
|
+
notify_with_level(SyslogSD::#{const}, *args) # notify_with_level(SyslogSD::DEBUG, *args)
|
105
|
+
end # end
|
106
|
+
EOT
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
def notify_with_level(message_level, *args)
|
111
|
+
notify_with_level!(message_level, *args)
|
112
|
+
rescue Exception => exception
|
113
|
+
notify_with_level!(SyslogSD::UNKNOWN, exception)
|
114
|
+
end
|
115
|
+
|
116
|
+
def notify_with_level!(message_level, *args)
|
117
|
+
return unless @enabled
|
118
|
+
extract_hash(*args)
|
119
|
+
@hash['level'] = message_level unless message_level.nil?
|
120
|
+
if @hash['level'] >= level
|
121
|
+
@sender.send_datagram(serialize_hash)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def extract_hash(object = nil, args = {})
|
126
|
+
primary_data = if object.respond_to?(:to_hash)
|
127
|
+
object.to_hash
|
128
|
+
elsif object.is_a?(Exception)
|
129
|
+
args['level'] ||= SyslogSD::ERROR
|
130
|
+
self.class.extract_hash_from_exception(object)
|
131
|
+
else
|
132
|
+
args['level'] ||= SyslogSD::INFO
|
133
|
+
{ 'short_message' => object.to_s }
|
134
|
+
end
|
135
|
+
|
136
|
+
@hash = default_options.merge(self.class.stringify_keys(args.merge(primary_data)))
|
137
|
+
convert_hoptoad_keys_to_graylog2
|
138
|
+
set_file_and_line if @collect_file_and_line
|
139
|
+
check_presence_of_mandatory_attributes
|
140
|
+
@hash
|
141
|
+
end
|
142
|
+
|
143
|
+
def self.extract_hash_from_exception(exception)
|
144
|
+
bt = exception.backtrace || ["Backtrace is not available."]
|
145
|
+
{ 'short_message' => "#{exception.class}: #{exception.message}", 'full_message' => "Backtrace:\n" + bt.join("\n") }
|
146
|
+
end
|
147
|
+
|
148
|
+
# Converts Hoptoad-specific keys in +@hash+ to Graylog2-specific.
|
149
|
+
def convert_hoptoad_keys_to_graylog2
|
150
|
+
if @hash['short_message'].to_s.empty?
|
151
|
+
if @hash.has_key?('error_class') && @hash.has_key?('error_message')
|
152
|
+
@hash['short_message'] = @hash.delete('error_class') + ': ' + @hash.delete('error_message')
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
CALLER_REGEXP = /^(.*):(\d+).*/
|
158
|
+
LIB_PATTERN = File.join('lib', 'syslog-sd')
|
159
|
+
|
160
|
+
def set_file_and_line
|
161
|
+
stack = caller
|
162
|
+
begin
|
163
|
+
frame = stack.shift
|
164
|
+
end while frame.include?(LIB_PATTERN)
|
165
|
+
match = CALLER_REGEXP.match(frame)
|
166
|
+
@hash['file'] = match[1]
|
167
|
+
@hash['line'] = match[2].to_i
|
168
|
+
end
|
169
|
+
|
170
|
+
def check_presence_of_mandatory_attributes
|
171
|
+
%w(short_message host).each do |attribute|
|
172
|
+
if @hash[attribute].to_s.empty?
|
173
|
+
raise ArgumentError.new("#{attribute} is missing. Options short_message and host must be set.")
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def serialize_hash
|
179
|
+
raise ArgumentError.new("Hash is empty.") if @hash.nil? || @hash.empty?
|
180
|
+
|
181
|
+
@hash['level'] = @level_mapping[@hash['level']]
|
182
|
+
|
183
|
+
prival = 128 + @hash.delete('level') # 128 = 16(local0) * 8
|
184
|
+
t = Time.now.utc
|
185
|
+
timestamp = t.strftime("%Y-%m-%dT%H:%M:%S.#{t.usec.to_s[0,3]}Z")
|
186
|
+
host = @hash.delete('host')
|
187
|
+
facility = @hash.delete('facility') || '-'
|
188
|
+
procid = @hash.delete('procid')
|
189
|
+
msgid = @hash.delete('msgid') || '-'
|
190
|
+
short_message = @hash.delete('short_message')
|
191
|
+
"<#{prival}>1 #{timestamp} #{host} #{facility} #{procid} #{msgid} #{serialize_sd} #{short_message}"
|
192
|
+
end
|
193
|
+
|
194
|
+
def serialize_sd
|
195
|
+
return '-' if @hash.empty?
|
196
|
+
|
197
|
+
res = "@sd_id"
|
198
|
+
@hash.each_pair do |key, value|
|
199
|
+
value = value.to_s.gsub(/([\\\]\"])/) { "\\#{$1}" }
|
200
|
+
res += " #{key}=\"#{value}\""
|
201
|
+
end
|
202
|
+
'[' + res + ']'
|
203
|
+
end
|
204
|
+
|
205
|
+
def self.stringify_keys(hash)
|
206
|
+
hash.keys.each do |key|
|
207
|
+
value, key_s = hash.delete(key), key.to_s
|
208
|
+
raise ArgumentError.new("Both #{key.inspect} and #{key_s} are present.") if hash.has_key?(key_s)
|
209
|
+
hash[key_s] = value
|
210
|
+
end
|
211
|
+
hash
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module SyslogSD
|
2
|
+
# Plain Ruby UDP sender.
|
3
|
+
class RubyUdpSender
|
4
|
+
attr_accessor :addresses
|
5
|
+
|
6
|
+
def initialize(addresses)
|
7
|
+
@addresses = addresses
|
8
|
+
@i = 0
|
9
|
+
@socket = UDPSocket.open
|
10
|
+
end
|
11
|
+
|
12
|
+
def send_datagram(datagram)
|
13
|
+
host, port = @addresses[@i]
|
14
|
+
@i = (@i + 1) % @addresses.length
|
15
|
+
@socket.send(datagram, 0, host, port)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module SyslogSD
|
2
|
+
# There are two things you should know about log levels/severity:
|
3
|
+
# - syslog defines levels from 0 (Emergency) to 7 (Debug).
|
4
|
+
# 0 (Emergency) and 1 (Alert) levels are reserved for OS kernel.
|
5
|
+
# - Ruby default Logger defines levels from 0 (DEBUG) to 4 (FATAL) and 5 (UNKNOWN).
|
6
|
+
# Note that order is inverted.
|
7
|
+
# For compatibility we define our constants as Ruby Logger, and convert values before
|
8
|
+
# generating syslog message, using defined mapping.
|
9
|
+
|
10
|
+
module Levels
|
11
|
+
DEBUG = 0
|
12
|
+
INFO = 1
|
13
|
+
WARN = 2
|
14
|
+
ERROR = 3
|
15
|
+
FATAL = 4
|
16
|
+
UNKNOWN = 5
|
17
|
+
end
|
18
|
+
|
19
|
+
include Levels
|
20
|
+
|
21
|
+
# Maps Ruby Logger levels to syslog levels as SyslogLogger and syslogger gems. This one is default.
|
22
|
+
LOGGER_MAPPING = {DEBUG => 7, # Debug
|
23
|
+
INFO => 6, # Info
|
24
|
+
WARN => 5, # Notice
|
25
|
+
ERROR => 4, # Warning
|
26
|
+
FATAL => 3, # Error
|
27
|
+
UNKNOWN => 1} # Alert – shouldn't be used
|
28
|
+
|
29
|
+
# Maps Ruby Logger levels to syslog levels as is.
|
30
|
+
DIRECT_MAPPING = {DEBUG => 7, # Debug
|
31
|
+
INFO => 6, # Info
|
32
|
+
# skip 5 Notice
|
33
|
+
WARN => 4, # Warning
|
34
|
+
ERROR => 3, # Error
|
35
|
+
FATAL => 2, # Critical
|
36
|
+
UNKNOWN => 1} # Alert – shouldn't be used
|
37
|
+
end
|
data/syslog-sd.gemspec
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{syslog-sd}
|
8
|
+
s.version = "1.2.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = [%q{Alexey Palazhchenko}, %q{Lennart Koopmann}]
|
12
|
+
s.date = %q{2011-05-17}
|
13
|
+
s.description = %q{Super-Duper library to send syslog messages to logging server such as Graylog2. Supports Structured Data elements as defined by RFC 5424.}
|
14
|
+
s.email = %q{alexey.palazhchenko@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
"CHANGELOG",
|
21
|
+
"LICENSE",
|
22
|
+
"README.rdoc",
|
23
|
+
"Rakefile",
|
24
|
+
"VERSION",
|
25
|
+
"benchmarks/notifier.rb",
|
26
|
+
"lib/syslog-sd.rb",
|
27
|
+
"lib/syslog-sd/logger.rb",
|
28
|
+
"lib/syslog-sd/notifier.rb",
|
29
|
+
"lib/syslog-sd/ruby_sender.rb",
|
30
|
+
"lib/syslog-sd/severity.rb",
|
31
|
+
"syslog-sd.gemspec",
|
32
|
+
"test/helper.rb",
|
33
|
+
"test/test_logger.rb",
|
34
|
+
"test/test_notifier.rb",
|
35
|
+
"test/test_ruby_sender.rb",
|
36
|
+
"test/test_severity.rb"
|
37
|
+
]
|
38
|
+
s.homepage = %q{http://github.com/AlekSi/syslog-sd-rb}
|
39
|
+
s.require_paths = [%q{lib}]
|
40
|
+
s.rubygems_version = %q{1.8.2}
|
41
|
+
s.summary = %q{Library to send syslog messages to logging server such as Graylog2. Supports Structured Data elements as defined by RFC 5424.}
|
42
|
+
|
43
|
+
if s.respond_to? :specification_version then
|
44
|
+
s.specification_version = 3
|
45
|
+
|
46
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
47
|
+
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
48
|
+
s.add_development_dependency(%q<mocha>, [">= 0"])
|
49
|
+
s.add_development_dependency(%q<timecop>, [">= 0"])
|
50
|
+
else
|
51
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
52
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
53
|
+
s.add_dependency(%q<timecop>, [">= 0"])
|
54
|
+
end
|
55
|
+
else
|
56
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
57
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
58
|
+
s.add_dependency(%q<timecop>, [">= 0"])
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
data/test/helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'shoulda'
|
4
|
+
require 'mocha'
|
5
|
+
require 'timecop'
|
6
|
+
|
7
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
8
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
9
|
+
require 'syslog-sd'
|
10
|
+
|
11
|
+
class Test::Unit::TestCase
|
12
|
+
end
|
data/test/test_logger.rb
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestLogger < Test::Unit::TestCase
|
4
|
+
context "with logger with mocked sender" do
|
5
|
+
setup do
|
6
|
+
Socket.stubs(:gethostname).returns('stubbed_hostname')
|
7
|
+
@logger = SyslogSD::Logger.new
|
8
|
+
@sender = mock
|
9
|
+
@logger.instance_variable_set('@sender', @sender)
|
10
|
+
end
|
11
|
+
|
12
|
+
should "respond to #close" do
|
13
|
+
assert @logger.respond_to?(:close)
|
14
|
+
end
|
15
|
+
|
16
|
+
context "#add" do
|
17
|
+
# logger.add(Logger::INFO, 'Message')
|
18
|
+
should "implement add method with level and message from parameters, facility from defaults" do
|
19
|
+
@logger.expects(:notify_with_level!).with do |level, hash|
|
20
|
+
level == SyslogSD::INFO &&
|
21
|
+
hash['short_message'] == 'Message' &&
|
22
|
+
hash['facility'] == 'syslog-sd-rb'
|
23
|
+
end
|
24
|
+
@logger.add(SyslogSD::INFO, 'Message')
|
25
|
+
end
|
26
|
+
|
27
|
+
# logger.add(Logger::INFO, RuntimeError.new('Boom!'))
|
28
|
+
should "implement add method with level and exception from parameters, facility from defaults" do
|
29
|
+
@logger.expects(:notify_with_level!).with do |level, hash|
|
30
|
+
level == SyslogSD::INFO &&
|
31
|
+
hash['short_message'] == 'RuntimeError: Boom!' &&
|
32
|
+
hash['full_message'] =~ /^Backtrace/ &&
|
33
|
+
hash['facility'] == 'syslog-sd-rb'
|
34
|
+
end
|
35
|
+
@logger.add(SyslogSD::INFO, RuntimeError.new('Boom!'))
|
36
|
+
end
|
37
|
+
|
38
|
+
# logger.add(Logger::INFO) { 'Message' }
|
39
|
+
should "implement add method with level from parameter, message from block, facility from defaults" do
|
40
|
+
@logger.expects(:notify_with_level!).with do |level, hash|
|
41
|
+
level == SyslogSD::INFO &&
|
42
|
+
hash['short_message'] == 'Message' &&
|
43
|
+
hash['facility'] == 'syslog-sd-rb'
|
44
|
+
end
|
45
|
+
@logger.add(SyslogSD::INFO) { 'Message' }
|
46
|
+
end
|
47
|
+
|
48
|
+
# logger.add(Logger::INFO) { RuntimeError.new('Boom!') }
|
49
|
+
should "implement add method with level from parameter, exception from block, facility from defaults" do
|
50
|
+
@logger.expects(:notify_with_level!).with do |level, hash|
|
51
|
+
level == SyslogSD::INFO &&
|
52
|
+
hash['short_message'] == 'RuntimeError: Boom!' &&
|
53
|
+
hash['full_message'] =~ /^Backtrace/ &&
|
54
|
+
hash['facility'] == 'syslog-sd-rb'
|
55
|
+
end
|
56
|
+
@logger.add(SyslogSD::INFO) { RuntimeError.new('Boom!') }
|
57
|
+
end
|
58
|
+
|
59
|
+
# logger.add(Logger::INFO, 'Message', 'Facility')
|
60
|
+
should "implement add method with level, message and facility from parameters" do
|
61
|
+
@logger.expects(:notify_with_level!).with do |level, hash|
|
62
|
+
level == SyslogSD::INFO &&
|
63
|
+
hash['short_message'] == 'Message' &&
|
64
|
+
hash['facility'] == 'Facility'
|
65
|
+
end
|
66
|
+
@logger.add(SyslogSD::INFO, 'Message', 'Facility')
|
67
|
+
end
|
68
|
+
|
69
|
+
# logger.add(Logger::INFO, RuntimeError.new('Boom!'), 'Facility')
|
70
|
+
should "implement add method with level, exception and facility from parameters" do
|
71
|
+
@logger.expects(:notify_with_level!).with do |level, hash|
|
72
|
+
level == SyslogSD::INFO &&
|
73
|
+
hash['short_message'] == 'RuntimeError: Boom!' &&
|
74
|
+
hash['full_message'] =~ /^Backtrace/ &&
|
75
|
+
hash['facility'] == 'Facility'
|
76
|
+
end
|
77
|
+
@logger.add(SyslogSD::INFO, RuntimeError.new('Boom!'), 'Facility')
|
78
|
+
end
|
79
|
+
|
80
|
+
# logger.add(Logger::INFO, 'Facility') { 'Message' }
|
81
|
+
should "implement add method with level and facility from parameters, message from block" do
|
82
|
+
@logger.expects(:notify_with_level!).with do |level, hash|
|
83
|
+
level == SyslogSD::INFO &&
|
84
|
+
hash['short_message'] == 'Message' &&
|
85
|
+
hash['facility'] == 'Facility'
|
86
|
+
end
|
87
|
+
@logger.add(SyslogSD::INFO, 'Facility') { 'Message' }
|
88
|
+
end
|
89
|
+
|
90
|
+
# logger.add(Logger::INFO, 'Facility') { RuntimeError.new('Boom!') }
|
91
|
+
should "implement add method with level and facility from parameters, exception from block" do
|
92
|
+
@logger.expects(:notify_with_level!).with do |level, hash|
|
93
|
+
level == SyslogSD::INFO &&
|
94
|
+
hash['short_message'] == 'RuntimeError: Boom!' &&
|
95
|
+
hash['full_message'] =~ /^Backtrace/ &&
|
96
|
+
hash['facility'] == 'Facility'
|
97
|
+
end
|
98
|
+
@logger.add(SyslogSD::INFO, 'Facility') { RuntimeError.new('Boom!') }
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
SyslogSD::Levels.constants.each do |const|
|
103
|
+
# logger.error "Argument #{ @foo } mismatch."
|
104
|
+
should "call add with level #{const} from method name, message from parameter" do
|
105
|
+
@logger.expects(:add).with(SyslogSD.const_get(const), 'message')
|
106
|
+
@logger.__send__(const.downcase, 'message')
|
107
|
+
end
|
108
|
+
|
109
|
+
# logger.fatal { "Argument 'foo' not given." }
|
110
|
+
should "call add with level #{const} from method name, message from block" do
|
111
|
+
@logger.expects(:add).with(SyslogSD.const_get(const), 'message')
|
112
|
+
@logger.__send__(const.downcase) { 'message' }
|
113
|
+
end
|
114
|
+
|
115
|
+
# logger.info('initialize') { "Initializing..." }
|
116
|
+
should "call add with level #{const} from method name, facility from parameter, message from block" do
|
117
|
+
@logger.expects(:add).with(SyslogSD.const_get(const), 'message', 'facility')
|
118
|
+
@logger.__send__(const.downcase, 'facility') { 'message' }
|
119
|
+
end
|
120
|
+
|
121
|
+
should "respond to #{const.downcase}?" do
|
122
|
+
@logger.level = SyslogSD.const_get(const) - 1
|
123
|
+
assert @logger.__send__(const.to_s.downcase + '?')
|
124
|
+
@logger.level = SyslogSD.const_get(const)
|
125
|
+
assert @logger.__send__(const.to_s.downcase + '?')
|
126
|
+
@logger.level = SyslogSD.const_get(const) + 1
|
127
|
+
assert !@logger.__send__(const.to_s.downcase + '?')
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
should "support Logger#<<" do
|
132
|
+
@logger.expects(:notify_with_level!).with do |level, hash|
|
133
|
+
level == SyslogSD::UNKNOWN &&
|
134
|
+
hash['short_message'] == "Message"
|
135
|
+
end
|
136
|
+
@logger << "Message"
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,214 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
RANDOM_DATA = ('A'..'Z').to_a
|
4
|
+
|
5
|
+
class TestNotifier < Test::Unit::TestCase
|
6
|
+
should "allow access to host, port and default_options" do
|
7
|
+
Socket.expects(:gethostname).returns('default_hostname')
|
8
|
+
n = SyslogSD::Notifier.new
|
9
|
+
assert_equal [['localhost', 514]], n.addresses
|
10
|
+
assert_equal( { 'level' => SyslogSD::UNKNOWN,
|
11
|
+
'host' => 'default_hostname', 'facility' => 'syslog-sd-rb',
|
12
|
+
'procid' => Process.pid },
|
13
|
+
n.default_options )
|
14
|
+
n.addresses, n.default_options = [['graylog2.org', 7777]], {:host => 'grayhost'}
|
15
|
+
assert_equal [['graylog2.org', 7777]], n.addresses
|
16
|
+
assert_equal({'host' => 'grayhost'}, n.default_options)
|
17
|
+
end
|
18
|
+
|
19
|
+
context "with notifier with mocked sender" do
|
20
|
+
setup do
|
21
|
+
Socket.stubs(:gethostname).returns('stubbed_hostname')
|
22
|
+
@notifier = SyslogSD::Notifier.new('host', 12345)
|
23
|
+
@sender = mock
|
24
|
+
@notifier.instance_variable_set('@sender', @sender)
|
25
|
+
end
|
26
|
+
|
27
|
+
context "extract_hash" do
|
28
|
+
should "check arguments" do
|
29
|
+
assert_raise(ArgumentError) { @notifier.__send__(:extract_hash) }
|
30
|
+
assert_raise(ArgumentError) { @notifier.__send__(:extract_hash, 1, 2, 3) }
|
31
|
+
end
|
32
|
+
|
33
|
+
should "work with hash" do
|
34
|
+
hash = @notifier.__send__(:extract_hash, { 'version' => '1.0', 'short_message' => 'message' })
|
35
|
+
assert_equal '1.0', hash['version']
|
36
|
+
assert_equal 'message', hash['short_message']
|
37
|
+
end
|
38
|
+
|
39
|
+
should "work with any object which responds to #to_hash" do
|
40
|
+
o = Object.new
|
41
|
+
o.expects(:to_hash).returns({ 'version' => '1.0', 'short_message' => 'message' })
|
42
|
+
hash = @notifier.__send__(:extract_hash, o)
|
43
|
+
assert_equal '1.0', hash['version']
|
44
|
+
assert_equal 'message', hash['short_message']
|
45
|
+
end
|
46
|
+
|
47
|
+
should "work with exception with backtrace" do
|
48
|
+
e = RuntimeError.new('message')
|
49
|
+
e.set_backtrace(caller)
|
50
|
+
hash = @notifier.__send__(:extract_hash, e)
|
51
|
+
assert_equal 'RuntimeError: message', hash['short_message']
|
52
|
+
assert_match /Backtrace/, hash['full_message']
|
53
|
+
assert_equal SyslogSD::ERROR, hash['level']
|
54
|
+
end
|
55
|
+
|
56
|
+
should "work with exception without backtrace" do
|
57
|
+
e = RuntimeError.new('message')
|
58
|
+
hash = @notifier.__send__(:extract_hash, e)
|
59
|
+
assert_match /Backtrace is not available/, hash['full_message']
|
60
|
+
end
|
61
|
+
|
62
|
+
should "work with exception and hash" do
|
63
|
+
e, h = RuntimeError.new('message'), {'param' => 1, 'level' => SyslogSD::FATAL, 'short_message' => 'will be hidden by exception'}
|
64
|
+
hash = @notifier.__send__(:extract_hash, e, h)
|
65
|
+
assert_equal 'RuntimeError: message', hash['short_message']
|
66
|
+
assert_equal SyslogSD::FATAL, hash['level']
|
67
|
+
assert_equal 1, hash['param']
|
68
|
+
end
|
69
|
+
|
70
|
+
should "work with plain text" do
|
71
|
+
hash = @notifier.__send__(:extract_hash, 'message')
|
72
|
+
assert_equal 'message', hash['short_message']
|
73
|
+
assert_equal SyslogSD::INFO, hash['level']
|
74
|
+
end
|
75
|
+
|
76
|
+
should "work with plain text and hash" do
|
77
|
+
hash = @notifier.__send__(:extract_hash, 'message', 'level' => SyslogSD::WARN)
|
78
|
+
assert_equal 'message', hash['short_message']
|
79
|
+
assert_equal SyslogSD::WARN, hash['level']
|
80
|
+
end
|
81
|
+
|
82
|
+
should "covert hash keys to strings" do
|
83
|
+
hash = @notifier.__send__(:extract_hash, :short_message => :message)
|
84
|
+
assert hash.has_key?('short_message')
|
85
|
+
assert !hash.has_key?(:short_message)
|
86
|
+
end
|
87
|
+
|
88
|
+
should "not overwrite keys on convert" do
|
89
|
+
assert_raise(ArgumentError) { @notifier.__send__(:extract_hash, :short_message => :message1, 'short_message' => 'message2') }
|
90
|
+
end
|
91
|
+
|
92
|
+
should "use default_options" do
|
93
|
+
@notifier.default_options = {:foo => 'bar', 'short_message' => 'will be hidden by explicit argument', 'host' => 'some_host'}
|
94
|
+
hash = @notifier.__send__(:extract_hash, { 'version' => '1.0', 'short_message' => 'message' })
|
95
|
+
assert_equal 'bar', hash['foo']
|
96
|
+
assert_equal 'message', hash['short_message']
|
97
|
+
end
|
98
|
+
|
99
|
+
should "be compatible with HoptoadNotifier" do
|
100
|
+
# https://github.com/thoughtbot/hoptoad_notifier/blob/master/README.rdoc, section Going beyond exceptions
|
101
|
+
hash = @notifier.__send__(:extract_hash, :error_class => 'Class', :error_message => 'Message')
|
102
|
+
assert_equal 'Class: Message', hash['short_message']
|
103
|
+
end
|
104
|
+
|
105
|
+
should "set file and line" do
|
106
|
+
line = __LINE__
|
107
|
+
hash = @notifier.__send__(:extract_hash, { 'version' => '1.0', 'short_message' => 'message' })
|
108
|
+
assert_match /test_notifier.rb/, hash['file']
|
109
|
+
assert_equal line + 1, hash['line']
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
context "serialize_hash" do
|
114
|
+
setup do
|
115
|
+
@notifier.level_mapping = :direct
|
116
|
+
Timecop.freeze(Time.utc(2010, 5, 16, 12, 13, 14))
|
117
|
+
end
|
118
|
+
|
119
|
+
expected = {
|
120
|
+
{'short_message' => 'message', 'level' => SyslogSD::WARN,
|
121
|
+
'host' => 'host', 'facility' => 'facility', 'procid' => 123,
|
122
|
+
'msgid' => 'msgid'} => '<132>1 2010-05-16T12:13:14.0Z host facility 123 msgid - message',
|
123
|
+
{'short_message' => 'message', 'level' => SyslogSD::WARN,
|
124
|
+
'host' => 'host', 'facility' => 'facility', 'procid' => 123,
|
125
|
+
'msgid' => 'msgid', 'user_id' => 123} => '<132>1 2010-05-16T12:13:14.0Z host facility 123 msgid [@sd_id user_id="123"] message',
|
126
|
+
{'short_message' => 'message', 'level' => SyslogSD::WARN,
|
127
|
+
'host' => 'host', 'facility' => 'facility', 'procid' => 123,
|
128
|
+
'msgid' => 'msgid', 'user_id' => '\\"]'} => '<132>1 2010-05-16T12:13:14.0Z host facility 123 msgid [@sd_id user_id="\\\\\"\]"] message'
|
129
|
+
}
|
130
|
+
|
131
|
+
expected.each_pair do |key, value|
|
132
|
+
should "be as #{value}" do
|
133
|
+
@notifier.instance_variable_set('@hash', key)
|
134
|
+
assert_equal value, @notifier.__send__(:serialize_hash)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
teardown do
|
139
|
+
Timecop.return
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
context "level threshold" do
|
144
|
+
setup do
|
145
|
+
@notifier.level = SyslogSD::WARN
|
146
|
+
@hash = { 'version' => '1.0', 'short_message' => 'message' }
|
147
|
+
end
|
148
|
+
|
149
|
+
['debug', 'DEBUG', :debug].each do |l|
|
150
|
+
should "allow to set threshold as #{l.inspect}" do
|
151
|
+
@notifier.level = l
|
152
|
+
assert_equal SyslogSD::DEBUG, @notifier.level
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
should "not send notifications with level below threshold" do
|
157
|
+
@sender.expects(:send_datagram).never
|
158
|
+
@notifier.notify!(@hash.merge('level' => SyslogSD::DEBUG))
|
159
|
+
end
|
160
|
+
|
161
|
+
should "not notifications with level equal or above threshold" do
|
162
|
+
@sender.expects(:send_datagram).once
|
163
|
+
@notifier.notify!(@hash.merge('level' => SyslogSD::WARN))
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
context "when disabled" do
|
168
|
+
setup do
|
169
|
+
@notifier.disable
|
170
|
+
end
|
171
|
+
|
172
|
+
should "not send datagram" do
|
173
|
+
@sender.expects(:send_datagram).never
|
174
|
+
@notifier.expects(:extract_hash).never
|
175
|
+
@notifier.notify!({ 'version' => '1.0', 'short_message' => 'message' })
|
176
|
+
end
|
177
|
+
|
178
|
+
context "and enabled again" do
|
179
|
+
setup do
|
180
|
+
@notifier.enable
|
181
|
+
end
|
182
|
+
|
183
|
+
should "send datagram" do
|
184
|
+
@sender.expects(:send_datagram)
|
185
|
+
@notifier.notify!({ 'version' => '1.0', 'short_message' => 'message' })
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
should "pass valid data to sender" do
|
191
|
+
@sender.expects(:send_datagram).with do |datagram|
|
192
|
+
datagram.is_a?(String)
|
193
|
+
end
|
194
|
+
@notifier.notify!({ 'version' => '1.0', 'short_message' => 'message' })
|
195
|
+
end
|
196
|
+
|
197
|
+
SyslogSD::Levels.constants.each do |const|
|
198
|
+
should "call notify with level #{const} from method name" do
|
199
|
+
@notifier.expects(:notify_with_level).with(SyslogSD.const_get(const), { 'version' => '1.0', 'short_message' => 'message' })
|
200
|
+
@notifier.__send__(const.downcase, { 'version' => '1.0', 'short_message' => 'message' })
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
should "not rescue from invalid invocation of #notify!" do
|
205
|
+
assert_raise(ArgumentError) { @notifier.notify!(:no_short_message => 'too bad') }
|
206
|
+
end
|
207
|
+
|
208
|
+
should "rescue from invalid invocation of #notify" do
|
209
|
+
@notifier.expects(:notify_with_level!).with(nil, instance_of(Hash)).raises(ArgumentError)
|
210
|
+
@notifier.expects(:notify_with_level!).with(SyslogSD::UNKNOWN, instance_of(ArgumentError))
|
211
|
+
assert_nothing_raised { @notifier.notify(:no_short_message => 'too bad') }
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestRubyUdpSender < Test::Unit::TestCase
|
4
|
+
context "with ruby sender" do
|
5
|
+
setup do
|
6
|
+
@addresses = [['localhost', 12201], ['localhost', 12202]]
|
7
|
+
@sender = SyslogSD::RubyUdpSender.new(@addresses)
|
8
|
+
end
|
9
|
+
|
10
|
+
context "send_datagram" do
|
11
|
+
setup do
|
12
|
+
@sender.send_datagram("d1")
|
13
|
+
@sender.send_datagram("e1")
|
14
|
+
@sender.send_datagram("d2")
|
15
|
+
@sender.send_datagram("e2")
|
16
|
+
end
|
17
|
+
|
18
|
+
before_should "be called 2 times with 1st and 2nd address" do
|
19
|
+
UDPSocket.any_instance.expects(:send).times(2).with do |datagram, _, host, port|
|
20
|
+
datagram.start_with?('d') && host == 'localhost' && port == 12201
|
21
|
+
end
|
22
|
+
UDPSocket.any_instance.expects(:send).times(2).with do |datagram, _, host, port|
|
23
|
+
datagram.start_with?('e') && host == 'localhost' && port == 12202
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: syslog-sd
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 31
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 2
|
9
|
+
- 0
|
10
|
+
version: 1.2.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Alexey Palazhchenko
|
14
|
+
- Lennart Koopmann
|
15
|
+
autorequire:
|
16
|
+
bindir: bin
|
17
|
+
cert_chain: []
|
18
|
+
|
19
|
+
date: 2011-05-17 00:00:00 Z
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: shoulda
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :development
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: mocha
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :development
|
48
|
+
version_requirements: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: timecop
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
type: :development
|
62
|
+
version_requirements: *id003
|
63
|
+
description: Super-Duper library to send syslog messages to logging server such as Graylog2. Supports Structured Data elements as defined by RFC 5424.
|
64
|
+
email: alexey.palazhchenko@gmail.com
|
65
|
+
executables: []
|
66
|
+
|
67
|
+
extensions: []
|
68
|
+
|
69
|
+
extra_rdoc_files:
|
70
|
+
- LICENSE
|
71
|
+
- README.rdoc
|
72
|
+
files:
|
73
|
+
- CHANGELOG
|
74
|
+
- LICENSE
|
75
|
+
- README.rdoc
|
76
|
+
- Rakefile
|
77
|
+
- VERSION
|
78
|
+
- benchmarks/notifier.rb
|
79
|
+
- lib/syslog-sd.rb
|
80
|
+
- lib/syslog-sd/logger.rb
|
81
|
+
- lib/syslog-sd/notifier.rb
|
82
|
+
- lib/syslog-sd/ruby_sender.rb
|
83
|
+
- lib/syslog-sd/severity.rb
|
84
|
+
- syslog-sd.gemspec
|
85
|
+
- test/helper.rb
|
86
|
+
- test/test_logger.rb
|
87
|
+
- test/test_notifier.rb
|
88
|
+
- test/test_ruby_sender.rb
|
89
|
+
- test/test_severity.rb
|
90
|
+
homepage: http://github.com/AlekSi/syslog-sd-rb
|
91
|
+
licenses: []
|
92
|
+
|
93
|
+
post_install_message:
|
94
|
+
rdoc_options: []
|
95
|
+
|
96
|
+
require_paths:
|
97
|
+
- lib
|
98
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
99
|
+
none: false
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
hash: 3
|
104
|
+
segments:
|
105
|
+
- 0
|
106
|
+
version: "0"
|
107
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
108
|
+
none: false
|
109
|
+
requirements:
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
hash: 3
|
113
|
+
segments:
|
114
|
+
- 0
|
115
|
+
version: "0"
|
116
|
+
requirements: []
|
117
|
+
|
118
|
+
rubyforge_project:
|
119
|
+
rubygems_version: 1.8.2
|
120
|
+
signing_key:
|
121
|
+
specification_version: 3
|
122
|
+
summary: Library to send syslog messages to logging server such as Graylog2. Supports Structured Data elements as defined by RFC 5424.
|
123
|
+
test_files: []
|
124
|
+
|