gelf 1.0.2 → 1.1.0.beta1
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/CHANGELOG +19 -4
- data/LICENSE +1 -1
- data/README.rdoc +1 -1
- data/Rakefile +6 -1
- data/VERSION +1 -1
- data/benchmarks/notifier.rb +32 -0
- data/gelf.gemspec +16 -5
- data/lib/gelf.rb +3 -0
- data/lib/gelf/deprecations.rb +7 -7
- data/lib/gelf/em_sender.rb +10 -0
- data/lib/gelf/logger.rb +50 -0
- data/lib/gelf/notifier.rb +106 -55
- data/lib/gelf/ruby_sender.rb +15 -0
- data/lib/gelf/severity.rb +28 -0
- data/test/helper.rb +5 -0
- data/test/test_logger.rb +135 -0
- data/test/test_notifier.rb +73 -57
- data/test/test_ruby_sender.rb +21 -0
- data/test/test_severity.rb +9 -0
- metadata +27 -7
data/CHANGELOG
CHANGED
@@ -1,6 +1,21 @@
|
|
1
|
-
1.0
|
2
|
-
|
1
|
+
1.1.0 (not yet released), 2010-11-xx:
|
2
|
+
+ Notifier#default_options;
|
3
|
+
+ severity (level) threshold;
|
4
|
+
+ wrappers for GELF::Notifier#notify with severity:
|
5
|
+
- GELF::Notifier.debug
|
6
|
+
- GELF::Notifier.info
|
7
|
+
- GELF::Notifier.warn
|
8
|
+
- GELF::Notifier.error
|
9
|
+
- GELF::Notifier.fatal
|
10
|
+
- GELF::Notifier.unknown
|
11
|
+
+ full compatibility with Ruby Logger and other loggers:
|
12
|
+
- GELF::Logger#fatal { "Argument 'foo' not given." }
|
13
|
+
- GELF::Logger#error "Argument #{ @foo } mismatch."
|
14
|
+
- GELF::Logger#info('initialize') { "Initializing..." }
|
15
|
+
- GELF::Logger#add(GELF::FATAL) { 'Fatal error!' }
|
16
|
+
- GELF::Logger#close
|
17
|
+
- GELF::Logger#level = GELF::INFO
|
3
18
|
|
4
19
|
1.0.0, 2010-11-10:
|
5
|
-
|
6
|
-
|
20
|
+
+ initial stable version;
|
21
|
+
* deprecated Gelf class is still there.
|
data/LICENSE
CHANGED
data/README.rdoc
CHANGED
data/Rakefile
CHANGED
@@ -2,7 +2,7 @@ require 'rubygems'
|
|
2
2
|
require 'rake'
|
3
3
|
|
4
4
|
begin
|
5
|
-
gem 'jeweler', '1.4.0'
|
5
|
+
gem 'jeweler', '~> 1.4.0' # https://github.com/technicalpickles/jeweler/issues/issue/150
|
6
6
|
require 'jeweler'
|
7
7
|
Jeweler::Tasks.new do |gem|
|
8
8
|
gem.name = "gelf"
|
@@ -42,6 +42,11 @@ rescue LoadError
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
+
begin
|
46
|
+
require 'metric_fu'
|
47
|
+
rescue LoadError
|
48
|
+
end
|
49
|
+
|
45
50
|
task :test => :check_dependencies
|
46
51
|
|
47
52
|
task :default => :test
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.0.
|
1
|
+
1.1.0.beta1
|
@@ -0,0 +1,32 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'benchmark'
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
7
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
8
|
+
require 'gelf'
|
9
|
+
|
10
|
+
srand(1)
|
11
|
+
RANDOM_DATA = ('A'..'z').to_a
|
12
|
+
k1_message = (0..1024).map { RANDOM_DATA[rand(RANDOM_DATA.count)] }.join
|
13
|
+
|
14
|
+
TARGET_HOST = 'localhost'
|
15
|
+
TARGET_PORT = 12201
|
16
|
+
DEFAULT_OPTIONS = { 'host' => 'localhost' }
|
17
|
+
TIMES = 5000
|
18
|
+
|
19
|
+
SHORT_HASH = { 'short_message' => 'message' }
|
20
|
+
LONG_HASH = { 'short_message' => 'short message', 'long_message' => k1_message, 'user_id' => rand(10000)}
|
21
|
+
|
22
|
+
|
23
|
+
notifier_lan = GELF::Notifier.new(TARGET_HOST, TARGET_PORT, 'LAN', DEFAULT_OPTIONS)
|
24
|
+
notifier_wan = GELF::Notifier.new(TARGET_HOST, TARGET_PORT, 'WAN', DEFAULT_OPTIONS)
|
25
|
+
|
26
|
+
puts "Sending #{TIMES} notifications..."
|
27
|
+
tms = Benchmark.bmbm do |b|
|
28
|
+
b.report('lan, short data') { TIMES.times { notifier_lan.notify!(SHORT_HASH) } }
|
29
|
+
b.report('wan, short data') { TIMES.times { notifier_wan.notify!(SHORT_HASH) } }
|
30
|
+
b.report('lan, long data') { TIMES.times { notifier_lan.notify!(LONG_HASH) } }
|
31
|
+
b.report('wan, long data') { TIMES.times { notifier_wan.notify!(LONG_HASH) } }
|
32
|
+
end
|
data/gelf.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{gelf}
|
8
|
-
s.version = "1.0.
|
8
|
+
s.version = "1.1.0.beta1"
|
9
9
|
|
10
|
-
s.required_rubygems_version = Gem::Requirement.new("
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Alexey Palazhchenko", "Lennart Koopmann"]
|
12
|
-
s.date = %q{2010-11-
|
12
|
+
s.date = %q{2010-11-25}
|
13
13
|
s.description = %q{Suports plain-text, GELF messages and exceptions.}
|
14
14
|
s.email = %q{lennart@socketfeed.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -23,13 +23,21 @@ Gem::Specification.new do |s|
|
|
23
23
|
"README.rdoc",
|
24
24
|
"Rakefile",
|
25
25
|
"VERSION",
|
26
|
+
"benchmarks/notifier.rb",
|
26
27
|
"gelf.gemspec",
|
27
28
|
"lib/gelf.rb",
|
28
29
|
"lib/gelf/deprecations.rb",
|
30
|
+
"lib/gelf/em_sender.rb",
|
31
|
+
"lib/gelf/logger.rb",
|
29
32
|
"lib/gelf/notifier.rb",
|
33
|
+
"lib/gelf/ruby_sender.rb",
|
34
|
+
"lib/gelf/severity.rb",
|
30
35
|
"test/helper.rb",
|
31
36
|
"test/test_deprecations.rb",
|
32
|
-
"test/
|
37
|
+
"test/test_logger.rb",
|
38
|
+
"test/test_notifier.rb",
|
39
|
+
"test/test_ruby_sender.rb",
|
40
|
+
"test/test_severity.rb"
|
33
41
|
]
|
34
42
|
s.homepage = %q{http://github.com/Graylog2/gelf-rb}
|
35
43
|
s.rdoc_options = ["--charset=UTF-8"]
|
@@ -39,7 +47,10 @@ Gem::Specification.new do |s|
|
|
39
47
|
s.test_files = [
|
40
48
|
"test/helper.rb",
|
41
49
|
"test/test_deprecations.rb",
|
42
|
-
"test/
|
50
|
+
"test/test_logger.rb",
|
51
|
+
"test/test_notifier.rb",
|
52
|
+
"test/test_ruby_sender.rb",
|
53
|
+
"test/test_severity.rb"
|
43
54
|
]
|
44
55
|
|
45
56
|
if s.respond_to? :specification_version then
|
data/lib/gelf.rb
CHANGED
data/lib/gelf/deprecations.rb
CHANGED
@@ -19,15 +19,15 @@ class Gelf
|
|
19
19
|
@notifier.notify(@message)
|
20
20
|
end
|
21
21
|
|
22
|
-
[:short_message, :full_message, :level, :host, :line, :file].each do |
|
23
|
-
define_method
|
24
|
-
deprecate("GELF::Message##{
|
25
|
-
@message[
|
22
|
+
[:short_message, :full_message, :level, :host, :line, :file].each do |attribute|
|
23
|
+
define_method attribute do
|
24
|
+
deprecate("GELF::Message##{attribute}")
|
25
|
+
@message[attribute]
|
26
26
|
end
|
27
27
|
|
28
|
-
define_method "#{
|
29
|
-
deprecate("GELF::Message##{
|
30
|
-
@message[
|
28
|
+
define_method "#{attribute}=" do |value|
|
29
|
+
deprecate("GELF::Message##{attribute} = value")
|
30
|
+
@message[attribute] = value
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
data/lib/gelf/logger.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
module GELF
|
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, facility = if args.count == 2
|
14
|
+
[args[0], args[1]]
|
15
|
+
elsif args.count == 0
|
16
|
+
[yield, nil]
|
17
|
+
elsif block_given?
|
18
|
+
[yield, args[0]]
|
19
|
+
else
|
20
|
+
[args[0], nil]
|
21
|
+
end
|
22
|
+
|
23
|
+
hash = {'short_message' => message, 'facility' => facility}
|
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
|
+
GELF::Levels.constants.each do |const|
|
29
|
+
class_eval <<-EOT, __FILE__, __LINE__ + 1
|
30
|
+
def #{const.downcase}(*args) # def debug(*args)
|
31
|
+
args.unshift(yield) if block_given? # args.unshift(yield) if block_given?
|
32
|
+
add(GELF::#{const}, *args) # add(GELF::DEBUG, *args)
|
33
|
+
end # end
|
34
|
+
|
35
|
+
def #{const.downcase}? # def debug?
|
36
|
+
GELF::#{const} >= level # GELF::DEBUG >= level
|
37
|
+
end # end
|
38
|
+
EOT
|
39
|
+
end
|
40
|
+
|
41
|
+
def <<(message)
|
42
|
+
notify('short_message' => message, 'level' => GELF::UNKNOWN)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Graylog2 notifier, compatible with Ruby Logger.
|
47
|
+
class Logger < Notifier
|
48
|
+
include LoggerCompatibility
|
49
|
+
end
|
50
|
+
end
|
data/lib/gelf/notifier.rb
CHANGED
@@ -1,117 +1,168 @@
|
|
1
1
|
module GELF
|
2
|
+
# Graylog2 notifier.
|
2
3
|
class Notifier
|
3
|
-
|
4
|
+
@last_chunk_id = 0
|
5
|
+
class << self
|
6
|
+
attr_accessor :last_chunk_id
|
7
|
+
end
|
4
8
|
|
5
|
-
attr_accessor :host, :port
|
6
|
-
attr_reader :max_chunk_size
|
9
|
+
attr_accessor :host, :port, :default_options
|
10
|
+
attr_reader :max_chunk_size, :level
|
7
11
|
|
8
12
|
# +host+ and +port+ are host/ip and port of graylog2-server.
|
9
|
-
|
10
|
-
|
13
|
+
# +max_size+ is passed to max_chunk_size=.
|
14
|
+
# +default_options+ is used in notify!
|
15
|
+
def initialize(host = 'localhost', port = 12201, max_size = 'WAN', default_options = {})
|
16
|
+
self.level = GELF::DEBUG
|
17
|
+
|
18
|
+
self.host, self.port, self.max_chunk_size = host, port, max_size
|
19
|
+
|
20
|
+
@default_options = {}
|
21
|
+
self.default_options = default_options
|
22
|
+
self.default_options['host'] ||= Socket.gethostname
|
23
|
+
self.default_options['level'] ||= GELF::DEBUG
|
24
|
+
self.default_options['facility'] ||= 'gelf-rb'
|
25
|
+
|
26
|
+
@sender = RubySender.new(host, port)
|
11
27
|
end
|
12
28
|
|
13
29
|
# +size+ may be a number of bytes, 'WAN' (1420 bytes) or 'LAN' (8154).
|
14
30
|
# Default (safe) value is 'WAN'.
|
15
31
|
def max_chunk_size=(size)
|
16
|
-
|
17
|
-
if
|
32
|
+
size_s = size.to_s.downcase
|
33
|
+
if size_s == 'wan'
|
18
34
|
@max_chunk_size = 1420
|
19
|
-
elsif
|
35
|
+
elsif size_s == 'lan'
|
20
36
|
@max_chunk_size = 8154
|
21
37
|
else
|
22
38
|
@max_chunk_size = size.to_int
|
23
39
|
end
|
24
40
|
end
|
25
41
|
|
42
|
+
def level=(new_level)
|
43
|
+
@level = new_level
|
44
|
+
end
|
45
|
+
|
26
46
|
# Same as notify!, but rescues all exceptions (including +ArgumentError+)
|
27
47
|
# and sends them instead.
|
28
48
|
def notify(*args)
|
29
|
-
|
30
|
-
rescue Exception => e
|
31
|
-
notify!(e)
|
49
|
+
notify_with_level(nil, *args)
|
32
50
|
end
|
33
51
|
|
34
52
|
# Sends message to Graylog2 server.
|
35
53
|
# +args+ can be:
|
36
|
-
# - hash-like object (any object which responds to +to_hash+, including +Hash+ instance)
|
54
|
+
# - hash-like object (any object which responds to +to_hash+, including +Hash+ instance):
|
37
55
|
# notify!(:short_message => 'All your rebase are belong to us', :user => 'AlekSi')
|
38
|
-
# - exception with optional hash-like object
|
56
|
+
# - exception with optional hash-like object:
|
39
57
|
# notify!(SecurityError.new('ALARM!'), :trespasser => 'AlekSi')
|
40
|
-
# - string-like object (anything which responds to +to_s+) with optional hash-like object
|
58
|
+
# - string-like object (anything which responds to +to_s+) with optional hash-like object:
|
41
59
|
# notify!('Plain olde text message', :scribe => 'AlekSi')
|
60
|
+
# Resulted fields are merged with +default_options+, the latter will never overwrite the former.
|
42
61
|
# This method will raise +ArgumentError+ if arguments are wrong. Consider using notify instead.
|
43
62
|
def notify!(*args)
|
44
|
-
|
63
|
+
notify_with_level!(nil, *args)
|
64
|
+
end
|
65
|
+
|
66
|
+
GELF::Levels.constants.each do |const|
|
67
|
+
class_eval <<-EOT, __FILE__, __LINE__ + 1
|
68
|
+
def #{const.downcase}(*args) # def debug(*args)
|
69
|
+
notify_with_level(GELF::#{const}, *args) # notify_with_level(GELF::DEBUG, *args)
|
70
|
+
end # end
|
71
|
+
EOT
|
45
72
|
end
|
46
73
|
|
47
74
|
private
|
48
|
-
def
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
75
|
+
def notify_with_level(message_level, *args)
|
76
|
+
notify_with_level!(message_level, *args)
|
77
|
+
rescue Exception => exception
|
78
|
+
notify_with_level!(GELF::UNKNOWN, exception)
|
79
|
+
end
|
80
|
+
|
81
|
+
def notify_with_level!(message_level, *args)
|
82
|
+
extract_hash(*args)
|
83
|
+
@hash['level'] = message_level unless message_level.nil?
|
84
|
+
if @hash['level'] >= level
|
85
|
+
@sender.send_datagrams(datagrams_from_hash)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def extract_hash(object = nil, args = {})
|
90
|
+
primary_data = if object.respond_to?(:to_hash)
|
91
|
+
object.to_hash
|
92
|
+
elsif object.is_a?(Exception)
|
93
|
+
args['level'] ||= GELF::ERROR
|
94
|
+
self.class.extract_hash_from_exception(object)
|
55
95
|
else
|
56
|
-
|
96
|
+
args['level'] ||= GELF::INFO
|
97
|
+
{ 'short_message' => object.to_s }
|
57
98
|
end
|
58
99
|
|
59
|
-
hash = args.merge(primary_data)
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
end
|
100
|
+
@hash = default_options.merge(args.merge(primary_data))
|
101
|
+
stringify_hash_keys
|
102
|
+
convert_hoptoad_keys_to_graylog2
|
103
|
+
check_presence_of_mandatory_attributes
|
104
|
+
@hash
|
105
|
+
end
|
66
106
|
|
67
|
-
|
107
|
+
def self.extract_hash_from_exception(exception)
|
108
|
+
bt = exception.backtrace || ["Backtrace is not available."]
|
109
|
+
{ 'short_message' => "#{exception.class}: #{exception.message}", 'full_message' => "Backtrace:\n" + bt.join("\n") }
|
110
|
+
end
|
68
111
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
hash
|
74
|
-
hash.delete('
|
112
|
+
# Converts Hoptoad-specific keys in +@hash+ to Graylog2-specific.
|
113
|
+
def convert_hoptoad_keys_to_graylog2
|
114
|
+
if @hash['short_message'].to_s.empty?
|
115
|
+
if @hash.has_key?('error_class') && @hash.has_key?('error_message')
|
116
|
+
@hash['short_message'] = "#{@hash['error_class']}: #{@hash['error_message']}"
|
117
|
+
@hash.delete('error_class')
|
118
|
+
@hash.delete('error_message')
|
75
119
|
end
|
76
120
|
end
|
121
|
+
end
|
77
122
|
|
78
|
-
|
79
|
-
|
80
|
-
|
123
|
+
def check_presence_of_mandatory_attributes
|
124
|
+
%w(short_message host).each do |attribute|
|
125
|
+
if @hash[attribute].to_s.empty?
|
126
|
+
raise ArgumentError.new("Options short_message and host must be set.")
|
81
127
|
end
|
82
128
|
end
|
83
|
-
|
84
|
-
hash
|
85
129
|
end
|
86
130
|
|
87
|
-
def
|
88
|
-
|
89
|
-
|
131
|
+
def datagrams_from_hash
|
132
|
+
raise ArgumentError.new("Hash is empty.") if @hash.nil? || @hash.empty?
|
133
|
+
|
134
|
+
@hash['level'] = GELF::LEVELS_MAPPING[@hash['level']]
|
135
|
+
|
136
|
+
data = Zlib::Deflate.deflate(@hash.to_json).bytes
|
90
137
|
datagrams = []
|
91
138
|
|
92
139
|
# Maximum total size is 8192 byte for UDP datagram. Split to chunks if bigger. (GELFv2 supports chunking)
|
93
140
|
if data.count > @max_chunk_size
|
94
|
-
|
95
|
-
msg_id = Digest::SHA256.digest("#{Time.now.to_f}-#{
|
96
|
-
|
141
|
+
id = self.class.last_chunk_id += 1
|
142
|
+
msg_id = Digest::SHA256.digest("#{Time.now.to_f}-#{id}")
|
143
|
+
num, count = 0, (data.count.to_f / @max_chunk_size).ceil
|
97
144
|
data.each_slice(@max_chunk_size) do |slice|
|
98
|
-
datagrams << chunk_data(slice, msg_id,
|
99
|
-
|
145
|
+
datagrams << self.class.chunk_data(slice, msg_id, num, count)
|
146
|
+
num += 1
|
100
147
|
end
|
101
148
|
else
|
102
149
|
datagrams = [data.map(&:chr).join]
|
103
150
|
end
|
104
151
|
|
105
|
-
datagrams.each { |d| sock.send(d, 0, @host, @port) }
|
106
152
|
datagrams
|
107
153
|
end
|
108
154
|
|
109
|
-
def chunk_data(data, msg_id,
|
110
|
-
|
155
|
+
def self.chunk_data(data, msg_id, num, count)
|
156
|
+
# [30, 15].pack('CC') => "\036\017"
|
157
|
+
return "\036\017" + msg_id + [num, count].pack('nn') + data.map(&:chr).join
|
111
158
|
end
|
112
159
|
|
113
|
-
def
|
114
|
-
@
|
160
|
+
def stringify_hash_keys
|
161
|
+
@hash.keys.each do |key|
|
162
|
+
value, key_s = @hash.delete(key), key.to_s
|
163
|
+
raise ArgumentError.new("Both #{key.inspect} and #{key_s} are present.") if @hash.has_key?(key_s)
|
164
|
+
@hash[key_s] = value
|
165
|
+
end
|
115
166
|
end
|
116
167
|
end
|
117
168
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module GELF
|
2
|
+
# Plain Ruby sender.
|
3
|
+
class RubySender
|
4
|
+
def initialize(host, port)
|
5
|
+
@host, @port = host, port
|
6
|
+
@socket = UDPSocket.open
|
7
|
+
end
|
8
|
+
|
9
|
+
def send_datagrams(datagrams)
|
10
|
+
datagrams.each do |datagram|
|
11
|
+
@socket.send(datagram, 0, @host, @port)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module GELF
|
2
|
+
# There are two things you should know about log leves/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 GELF message.
|
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.
|
22
|
+
LEVELS_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
|
+
end
|
data/test/helper.rb
CHANGED
data/test/test_logger.rb
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestLogger < Test::Unit::TestCase
|
4
|
+
context "with notifier with mocked sender" do
|
5
|
+
setup do
|
6
|
+
Socket.stubs(:gethostname).returns('stubbed_hostname')
|
7
|
+
@notifier = GELF::Logger.new('host', 12345)
|
8
|
+
@sender = mock
|
9
|
+
@notifier.instance_variable_set('@sender', @sender)
|
10
|
+
end
|
11
|
+
|
12
|
+
should "respond to #close" do
|
13
|
+
assert @notifier.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" do
|
19
|
+
@notifier.expects(:notify_with_level!).with do |level, hash|
|
20
|
+
level == GELF::INFO &&
|
21
|
+
hash['short_message'] == 'Message'
|
22
|
+
end
|
23
|
+
@notifier.add(GELF::INFO, 'Message')
|
24
|
+
end
|
25
|
+
|
26
|
+
# logger.add(Logger::INFO, RuntimeError.new('Boom!'))
|
27
|
+
should "implement add method with level and exception from parameters" do
|
28
|
+
@notifier.expects(:notify_with_level!).with do |level, hash|
|
29
|
+
level == GELF::INFO &&
|
30
|
+
hash['short_message'] == 'RuntimeError: Boom!' &&
|
31
|
+
hash['full_message'] =~ /^Backtrace/
|
32
|
+
end
|
33
|
+
@notifier.add(GELF::INFO, RuntimeError.new('Boom!'))
|
34
|
+
end
|
35
|
+
|
36
|
+
# logger.add(Logger::INFO) { 'Message' }
|
37
|
+
should "implement add method with level from parameter and message from block" do
|
38
|
+
@notifier.expects(:notify_with_level!).with do |level, hash|
|
39
|
+
level == GELF::INFO &&
|
40
|
+
hash['short_message'] == 'Message'
|
41
|
+
end
|
42
|
+
@notifier.add(GELF::INFO) { 'Message' }
|
43
|
+
end
|
44
|
+
|
45
|
+
# logger.add(Logger::INFO) { RuntimeError.new('Boom!') }
|
46
|
+
should "implement add method with level from parameter and exception from block" do
|
47
|
+
@notifier.expects(:notify_with_level!).with do |level, hash|
|
48
|
+
level == GELF::INFO &&
|
49
|
+
hash['short_message'] == 'RuntimeError: Boom!' &&
|
50
|
+
hash['full_message'] =~ /^Backtrace/
|
51
|
+
end
|
52
|
+
@notifier.add(GELF::INFO) { RuntimeError.new('Boom!') }
|
53
|
+
end
|
54
|
+
|
55
|
+
# logger.add(Logger::INFO, 'Message', 'Facility')
|
56
|
+
should "implement add method with level, message and facility from parameters" do
|
57
|
+
@notifier.expects(:notify_with_level!).with do |level, hash|
|
58
|
+
level == GELF::INFO &&
|
59
|
+
hash['short_message'] == 'Message' &&
|
60
|
+
hash['facility'] == 'Facility'
|
61
|
+
end
|
62
|
+
@notifier.add(GELF::INFO, 'Message', 'Facility')
|
63
|
+
end
|
64
|
+
|
65
|
+
# logger.add(Logger::INFO, RuntimeError.new('Boom!'), 'Facility')
|
66
|
+
should "implement add method with level, exception and facility from parameters" do
|
67
|
+
@notifier.expects(:notify_with_level!).with do |level, hash|
|
68
|
+
level == GELF::INFO &&
|
69
|
+
hash['short_message'] == 'RuntimeError: Boom!' &&
|
70
|
+
hash['full_message'] =~ /^Backtrace/ &&
|
71
|
+
hash['facility'] == 'Facility'
|
72
|
+
end
|
73
|
+
@notifier.add(GELF::INFO, RuntimeError.new('Boom!'), 'Facility')
|
74
|
+
end
|
75
|
+
|
76
|
+
# logger.add(Logger::INFO, 'Facility') { 'Message' }
|
77
|
+
should "implement add method with level and facility from parameters and message from block" do
|
78
|
+
@notifier.expects(:notify_with_level!).with do |level, hash|
|
79
|
+
level == GELF::INFO &&
|
80
|
+
hash['short_message'] == 'Message' &&
|
81
|
+
hash['facility'] == 'Facility'
|
82
|
+
end
|
83
|
+
@notifier.add(GELF::INFO, 'Facility') { 'Message' }
|
84
|
+
end
|
85
|
+
|
86
|
+
# logger.add(Logger::INFO, 'Facility') { RuntimeError.new('Boom!') }
|
87
|
+
should "implement add method with level and facility from parameters and exception from block" do
|
88
|
+
@notifier.expects(:notify_with_level!).with do |level, hash|
|
89
|
+
level == GELF::INFO &&
|
90
|
+
hash['short_message'] == 'RuntimeError: Boom!' &&
|
91
|
+
hash['full_message'] =~ /^Backtrace/ &&
|
92
|
+
hash['facility'] == 'Facility'
|
93
|
+
end
|
94
|
+
@notifier.add(GELF::INFO, 'Facility') { RuntimeError.new('Boom!') }
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
GELF::Levels.constants.each do |const|
|
99
|
+
# logger.error "Argument #{ @foo } mismatch."
|
100
|
+
should "call notify with level #{const} from method name and message from parameter" do
|
101
|
+
@notifier.expects(:add).with(GELF.const_get(const), 'message')
|
102
|
+
@notifier.__send__(const.downcase, 'message')
|
103
|
+
end
|
104
|
+
|
105
|
+
# logger.fatal { "Argument 'foo' not given." }
|
106
|
+
should "call notify with level #{const} from method name and message from block" do
|
107
|
+
@notifier.expects(:add).with(GELF.const_get(const), 'message')
|
108
|
+
@notifier.__send__(const.downcase) { 'message' }
|
109
|
+
end
|
110
|
+
|
111
|
+
# logger.info('initialize') { "Initializing..." }
|
112
|
+
should "call notify with level #{const} from method name, facility from parameter and message from block" do
|
113
|
+
@notifier.expects(:add).with(GELF.const_get(const), 'message', 'facility')
|
114
|
+
@notifier.__send__(const.downcase, 'facility') { 'message' }
|
115
|
+
end
|
116
|
+
|
117
|
+
should "respond to #{const.downcase}?" do
|
118
|
+
@notifier.level = GELF.const_get(const) - 1
|
119
|
+
assert @notifier.__send__(const.to_s.downcase + '?')
|
120
|
+
@notifier.level = GELF.const_get(const)
|
121
|
+
assert @notifier.__send__(const.to_s.downcase + '?')
|
122
|
+
@notifier.level = GELF.const_get(const) + 1
|
123
|
+
assert !@notifier.__send__(const.to_s.downcase + '?')
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
should "support Notifier#<<" do
|
128
|
+
@notifier.expects(:notify_with_level!).with do |nil_, hash|
|
129
|
+
hash['short_message'] == "Message" &&
|
130
|
+
hash['level'] == GELF::UNKNOWN
|
131
|
+
end
|
132
|
+
@notifier << "Message"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
data/test/test_notifier.rb
CHANGED
@@ -1,24 +1,33 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
|
-
HASH = {'short_message' => 'message', 'host' => '
|
3
|
+
HASH = {'short_message' => 'message', 'host' => 'somehost', 'level' => GELF::WARN, 'facility' => 'test'}
|
4
4
|
|
5
5
|
RANDOM_DATA = ('A'..'Z').to_a
|
6
6
|
|
7
7
|
class TestNotifier < Test::Unit::TestCase
|
8
|
-
should "allow access to host, port and
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
assert_equal
|
8
|
+
should "allow access to host, port, max_chunk_size and default_options" do
|
9
|
+
Socket.expects(:gethostname).returns('default_hostname')
|
10
|
+
n = GELF::Notifier.new
|
11
|
+
assert_equal ['localhost', 12201, 1420], [n.host, n.port, n.max_chunk_size]
|
12
|
+
assert_equal({'level' => 0, 'host' => 'default_hostname', 'facility' => 'gelf-rb'}, n.default_options)
|
13
|
+
n.host, n.port, n.max_chunk_size, n.default_options = 'graylog2.org', 7777, :lan, {'host' => 'grayhost'}
|
14
|
+
assert_equal ['graylog2.org', 7777, 8154], [n.host, n.port, n.max_chunk_size]
|
15
|
+
assert_equal({'host' => 'grayhost'}, n.default_options)
|
16
|
+
|
17
|
+
n.max_chunk_size = 1337.1
|
18
|
+
assert_equal 1337, n.max_chunk_size
|
13
19
|
end
|
14
20
|
|
15
|
-
context "with notifier" do
|
21
|
+
context "with notifier with mocked sender" do
|
16
22
|
setup do
|
23
|
+
Socket.stubs(:gethostname).returns('stubbed_hostname')
|
17
24
|
@notifier = GELF::Notifier.new('host', 12345)
|
25
|
+
@sender = mock
|
26
|
+
@notifier.instance_variable_set('@sender', @sender)
|
18
27
|
end
|
19
28
|
|
20
29
|
context "extract_hash" do
|
21
|
-
should "check
|
30
|
+
should "check arguments" do
|
22
31
|
assert_raise(ArgumentError) { @notifier.__send__(:extract_hash) }
|
23
32
|
assert_raise(ArgumentError) { @notifier.__send__(:extract_hash, 1, 2, 3) }
|
24
33
|
end
|
@@ -39,6 +48,7 @@ class TestNotifier < Test::Unit::TestCase
|
|
39
48
|
hash = @notifier.__send__(:extract_hash, e)
|
40
49
|
assert_equal 'RuntimeError: message', hash['short_message']
|
41
50
|
assert_match /Backtrace/, hash['full_message']
|
51
|
+
assert_equal GELF::ERROR, hash['level']
|
42
52
|
end
|
43
53
|
|
44
54
|
should "work with exception without backtrace" do
|
@@ -48,18 +58,23 @@ class TestNotifier < Test::Unit::TestCase
|
|
48
58
|
end
|
49
59
|
|
50
60
|
should "work with exception and hash" do
|
51
|
-
e, h = RuntimeError.new('message'), {'param' => 1, 'short_message' => 'will be hidden by exception'}
|
61
|
+
e, h = RuntimeError.new('message'), {'param' => 1, 'level' => GELF::FATAL, 'short_message' => 'will be hidden by exception'}
|
52
62
|
hash = @notifier.__send__(:extract_hash, e, h)
|
53
63
|
assert_equal 'RuntimeError: message', hash['short_message']
|
64
|
+
assert_equal GELF::FATAL, hash['level']
|
54
65
|
assert_equal 1, hash['param']
|
55
66
|
end
|
56
67
|
|
57
68
|
should "work with plain text" do
|
58
|
-
|
69
|
+
hash = @notifier.__send__(:extract_hash, 'message')
|
70
|
+
assert_equal 'message', hash['short_message']
|
71
|
+
assert_equal GELF::INFO, hash['level']
|
59
72
|
end
|
60
73
|
|
61
74
|
should "work with plain text and hash" do
|
62
|
-
|
75
|
+
hash = @notifier.__send__(:extract_hash, 'message', 'level' => GELF::WARN)
|
76
|
+
assert_equal 'message', hash['short_message']
|
77
|
+
assert_equal GELF::WARN, hash['level']
|
63
78
|
end
|
64
79
|
|
65
80
|
should "covert hash keys to strings" do
|
@@ -72,6 +87,13 @@ class TestNotifier < Test::Unit::TestCase
|
|
72
87
|
assert_raise(ArgumentError) { @notifier.__send__(:extract_hash, :short_message => :message1, 'short_message' => 'message2') }
|
73
88
|
end
|
74
89
|
|
90
|
+
should "use default_options" do
|
91
|
+
@notifier.default_options = {:file => 'somefile.rb', 'short_message' => 'will be hidden by explicit argument'}
|
92
|
+
hash = @notifier.__send__(:extract_hash, HASH)
|
93
|
+
assert_equal 'somefile.rb', hash['file']
|
94
|
+
assert_not_equal 'will be hidden by explicit argument', hash['short_message']
|
95
|
+
end
|
96
|
+
|
75
97
|
should "be compatible with HoptoadNotifier" do
|
76
98
|
# https://github.com/thoughtbot/hoptoad_notifier/blob/master/README.rdoc, section Going beyond exceptions
|
77
99
|
hash = @notifier.__send__(:extract_hash, :error_class => 'Class', :error_message => 'Message')
|
@@ -79,41 +101,52 @@ class TestNotifier < Test::Unit::TestCase
|
|
79
101
|
end
|
80
102
|
end
|
81
103
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
104
|
+
context "datagrams_from_hash" do
|
105
|
+
should "not split short data" do
|
106
|
+
@notifier.instance_variable_set('@hash', HASH)
|
107
|
+
datagrams = @notifier.__send__(:datagrams_from_hash)
|
108
|
+
assert_equal 1, datagrams.count
|
109
|
+
assert_equal "\170\234", datagrams[0][0..1]
|
110
|
+
end
|
111
|
+
|
112
|
+
should "split long data" do
|
113
|
+
srand(1) # for stable tests
|
114
|
+
hash = HASH.merge('something' => (0..3000).map { RANDOM_DATA[rand(RANDOM_DATA.count)] }.join) # or it will be compressed too good
|
115
|
+
@notifier.instance_variable_set('@hash', hash)
|
116
|
+
datagrams = @notifier.__send__(:datagrams_from_hash)
|
117
|
+
assert_equal 2, datagrams.count
|
118
|
+
assert_equal "\036\017", datagrams[0][0..1]
|
119
|
+
assert_equal "\036\017", datagrams[1][0..1]
|
120
|
+
end
|
86
121
|
end
|
87
122
|
|
88
|
-
context "
|
89
|
-
|
90
|
-
|
91
|
-
@notifier.notify!(HASH)
|
123
|
+
context "level threshold" do
|
124
|
+
setup do
|
125
|
+
@notifier.level = GELF::WARN
|
92
126
|
end
|
93
127
|
|
94
|
-
should "
|
95
|
-
|
96
|
-
|
97
|
-
@notifier.notify!(HASH.merge('something' => (0..3000).map { RANDOM_DATA[rand(RANDOM_DATA.count)] }.join)) # or it will be compressed too good
|
128
|
+
should "not send notifications with level below threshold" do
|
129
|
+
@sender.expects(:send_datagrams).never
|
130
|
+
@notifier.notify!(HASH.merge('level' => GELF::DEBUG))
|
98
131
|
end
|
99
132
|
|
100
|
-
should "
|
101
|
-
|
102
|
-
|
103
|
-
port == @notifier.port &&
|
104
|
-
data[0..1] == "\170\234"
|
105
|
-
end
|
106
|
-
@notifier.notify!(HASH)
|
133
|
+
should "not notifications with level equal or above threshold" do
|
134
|
+
@sender.expects(:send_datagrams).once
|
135
|
+
@notifier.notify!(HASH.merge('level' => GELF::WARN))
|
107
136
|
end
|
137
|
+
end
|
108
138
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
139
|
+
should "pass valid data to sender" do
|
140
|
+
@sender.expects(:send_datagrams).with do |datagrams|
|
141
|
+
datagrams.is_a?(Array) && datagrams[0].is_a?(String)
|
142
|
+
end
|
143
|
+
@notifier.notify!(HASH)
|
144
|
+
end
|
145
|
+
|
146
|
+
GELF::Levels.constants.each do |const|
|
147
|
+
should "call notify with level #{const} from method name" do
|
148
|
+
@notifier.expects(:notify_with_level).with(GELF.const_get(const), HASH)
|
149
|
+
@notifier.__send__(const.downcase, HASH)
|
117
150
|
end
|
118
151
|
end
|
119
152
|
|
@@ -122,26 +155,9 @@ class TestNotifier < Test::Unit::TestCase
|
|
122
155
|
end
|
123
156
|
|
124
157
|
should "rescue from invalid invocation of #notify" do
|
125
|
-
@notifier.expects(:
|
126
|
-
@notifier.expects(:
|
158
|
+
@notifier.expects(:notify_with_level!).with(nil, instance_of(Hash)).raises(ArgumentError)
|
159
|
+
@notifier.expects(:notify_with_level!).with(GELF::UNKNOWN, instance_of(ArgumentError))
|
127
160
|
assert_nothing_raised { @notifier.notify(:no_short_message => 'too bad') }
|
128
161
|
end
|
129
|
-
|
130
|
-
should "chunk data" do
|
131
|
-
UDPSocket.any_instance.stubs(:send)
|
132
|
-
srand(1) # for stable tests
|
133
|
-
data = (0..3000).map { RANDOM_DATA[rand(RANDOM_DATA.count)] }.join
|
134
|
-
datagrams = @notifier.__send__(:do_notify, {'short_message' => data, 'host' => 'localhost'})
|
135
|
-
assert_equal 2, datagrams.count
|
136
|
-
datagrams.each_index do |i|
|
137
|
-
datagram = datagrams[i]
|
138
|
-
assert datagram[0..1] == "\x1e\x0f"
|
139
|
-
# datagram[2..33] is a message id
|
140
|
-
assert_equal 0, datagram[34].ord
|
141
|
-
assert_equal i, datagram[35].ord
|
142
|
-
assert_equal 0, datagram[36].ord
|
143
|
-
assert_equal datagrams.count, datagram[37].ord
|
144
|
-
end
|
145
|
-
end
|
146
162
|
end
|
147
163
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestRubySender < Test::Unit::TestCase
|
4
|
+
context "with ruby sender" do
|
5
|
+
setup do
|
6
|
+
@host, @port = 'localhost', 12201
|
7
|
+
@sender = GELF::RubySender.new('localhost', 12201)
|
8
|
+
@datagrams = %w(d1 d2 d3)
|
9
|
+
end
|
10
|
+
|
11
|
+
context "send_datagrams" do
|
12
|
+
setup do
|
13
|
+
@sender.send_datagrams(@datagrams)
|
14
|
+
end
|
15
|
+
|
16
|
+
before_should "be called with 3 times correct parameters" do
|
17
|
+
UDPSocket.any_instance.expects(:send).times(3).with(instance_of(String), 0, @host, @port).returns(@datagrams)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
metadata
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gelf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
hash: -1848230059
|
5
|
+
prerelease: true
|
5
6
|
segments:
|
6
7
|
- 1
|
8
|
+
- 1
|
7
9
|
- 0
|
8
|
-
-
|
9
|
-
version: 1.0.
|
10
|
+
- beta1
|
11
|
+
version: 1.1.0.beta1
|
10
12
|
platform: ruby
|
11
13
|
authors:
|
12
14
|
- Alexey Palazhchenko
|
@@ -15,7 +17,7 @@ autorequire:
|
|
15
17
|
bindir: bin
|
16
18
|
cert_chain: []
|
17
19
|
|
18
|
-
date: 2010-11-
|
20
|
+
date: 2010-11-25 00:00:00 +03:00
|
19
21
|
default_executable:
|
20
22
|
dependencies:
|
21
23
|
- !ruby/object:Gem::Dependency
|
@@ -26,6 +28,7 @@ dependencies:
|
|
26
28
|
requirements:
|
27
29
|
- - ">="
|
28
30
|
- !ruby/object:Gem::Version
|
31
|
+
hash: 3
|
29
32
|
segments:
|
30
33
|
- 0
|
31
34
|
version: "0"
|
@@ -39,6 +42,7 @@ dependencies:
|
|
39
42
|
requirements:
|
40
43
|
- - ">="
|
41
44
|
- !ruby/object:Gem::Version
|
45
|
+
hash: 3
|
42
46
|
segments:
|
43
47
|
- 0
|
44
48
|
version: "0"
|
@@ -52,6 +56,7 @@ dependencies:
|
|
52
56
|
requirements:
|
53
57
|
- - ">="
|
54
58
|
- !ruby/object:Gem::Version
|
59
|
+
hash: 3
|
55
60
|
segments:
|
56
61
|
- 0
|
57
62
|
version: "0"
|
@@ -73,13 +78,21 @@ files:
|
|
73
78
|
- README.rdoc
|
74
79
|
- Rakefile
|
75
80
|
- VERSION
|
81
|
+
- benchmarks/notifier.rb
|
76
82
|
- gelf.gemspec
|
77
83
|
- lib/gelf.rb
|
78
84
|
- lib/gelf/deprecations.rb
|
85
|
+
- lib/gelf/em_sender.rb
|
86
|
+
- lib/gelf/logger.rb
|
79
87
|
- lib/gelf/notifier.rb
|
88
|
+
- lib/gelf/ruby_sender.rb
|
89
|
+
- lib/gelf/severity.rb
|
80
90
|
- test/helper.rb
|
81
91
|
- test/test_deprecations.rb
|
92
|
+
- test/test_logger.rb
|
82
93
|
- test/test_notifier.rb
|
94
|
+
- test/test_ruby_sender.rb
|
95
|
+
- test/test_severity.rb
|
83
96
|
has_rdoc: true
|
84
97
|
homepage: http://github.com/Graylog2/gelf-rb
|
85
98
|
licenses: []
|
@@ -94,17 +107,21 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
94
107
|
requirements:
|
95
108
|
- - ">="
|
96
109
|
- !ruby/object:Gem::Version
|
110
|
+
hash: 3
|
97
111
|
segments:
|
98
112
|
- 0
|
99
113
|
version: "0"
|
100
114
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
101
115
|
none: false
|
102
116
|
requirements:
|
103
|
-
- - "
|
117
|
+
- - ">"
|
104
118
|
- !ruby/object:Gem::Version
|
119
|
+
hash: 25
|
105
120
|
segments:
|
106
|
-
-
|
107
|
-
|
121
|
+
- 1
|
122
|
+
- 3
|
123
|
+
- 1
|
124
|
+
version: 1.3.1
|
108
125
|
requirements: []
|
109
126
|
|
110
127
|
rubyforge_project:
|
@@ -115,4 +132,7 @@ summary: Library to send GELF messages to Graylog2 logging server
|
|
115
132
|
test_files:
|
116
133
|
- test/helper.rb
|
117
134
|
- test/test_deprecations.rb
|
135
|
+
- test/test_logger.rb
|
118
136
|
- test/test_notifier.rb
|
137
|
+
- test/test_ruby_sender.rb
|
138
|
+
- test/test_severity.rb
|