syslog-sd 1.3.2 → 1.4.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.3.2
1
+ 1.4.0.beta1
@@ -0,0 +1,17 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
6
+ require 'syslog-sd'
7
+
8
+ TARGET_HOST = 'localhost'
9
+ TARGET_PORT = 5140
10
+ RANGE = 35000...36000
11
+
12
+ n = SyslogSD::Notifier.new(TARGET_HOST, TARGET_PORT)
13
+ RANGE.each do |size|
14
+ n.notify!('a' * size)
15
+ puts size if (size % 100) == 0
16
+ sleep 0.01
17
+ end
@@ -1,36 +1,48 @@
1
1
  #! /usr/bin/env ruby
2
2
 
3
- puts "Loading..."
4
-
5
3
  require 'benchmark'
4
+ require 'thread'
6
5
  require 'rubygems'
7
6
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
8
7
  $LOAD_PATH.unshift(File.dirname(__FILE__))
9
8
  require 'syslog-sd'
10
9
 
10
+ Thread.abort_on_exception = true
11
+
12
+ puts "Running on #{RUBY_DESCRIPTION}"
11
13
  puts "Generating random data..."
12
14
  srand(1)
13
15
  RANDOM_DATA = ('A'..'z').to_a
14
16
  k3_message = (1..3*1024).map { RANDOM_DATA[rand(RANDOM_DATA.count)] }.join
15
17
 
16
18
  TARGET_HOST = 'localhost'
17
- TARGET_PORT = 12201
19
+ TARGET_PORT = 5140
18
20
  DEFAULT_OPTIONS = { '_host' => 'localhost' }
19
- TIMES = 5000
21
+ MESSAGES_COUNT = 5000
22
+ THREADS_COUNT = 50
20
23
 
21
24
  SHORT_HASH = { 'short_message' => 'message' }
22
25
  LONG_HASH = { 'short_message' => 'message', 'long_message' => k3_message }
23
26
 
27
+ def notify(n, hash)
28
+ threads = []
29
+ THREADS_COUNT.times do
30
+ threads << Thread.new do
31
+ (MESSAGES_COUNT / THREADS_COUNT).times { n.notify!(hash) }
32
+ end
33
+ end
34
+ threads.each { |t| t.join }
35
+ end
24
36
 
25
- notifier = SyslogSD::Notifier.new(TARGET_HOST, TARGET_PORT, DEFAULT_OPTIONS)
37
+ n = SyslogSD::Notifier.new(TARGET_HOST, TARGET_PORT, DEFAULT_OPTIONS)
26
38
 
27
39
  # to create mongo collections, etc.
28
- notifier.notify!(LONG_HASH)
40
+ n.notify!(LONG_HASH)
29
41
  sleep(5)
30
42
 
31
- puts "Sending #{TIMES} notifications...\n"
43
+ puts "Sending #{MESSAGES_COUNT} notifications...\n"
32
44
  tms = Benchmark.bm(25) do |b|
33
- b.report('short data') { TIMES.times { notifier.notify!(SHORT_HASH) } }
45
+ b.report('short data') { notify(n, SHORT_HASH) }
34
46
  sleep(5)
35
- b.report('long data ') { TIMES.times { notifier.notify!(LONG_HASH) } }
47
+ b.report(' long data') { notify(n, LONG_HASH) }
36
48
  end
@@ -5,7 +5,7 @@ module SyslogSD
5
5
  def close
6
6
  end
7
7
 
8
- # Use it like Logger#add... or better not to use at all.
8
+ # Use it like Logger#add... or better not use directly at all.
9
9
  def add(level, *args)
10
10
  raise ArgumentError.new('Wrong arguments.') unless (0..2).include?(args.count)
11
11
 
@@ -50,6 +50,5 @@ module SyslogSD
50
50
  # config.colorize_logging = false
51
51
  class Logger < Notifier
52
52
  include LoggerCompatibility
53
- @last_chunk_id = 0
54
53
  end
55
54
  end
@@ -120,16 +120,18 @@ module SyslogSD
120
120
 
121
121
  def notify_with_level!(message_level, *args)
122
122
  return unless @enabled
123
- extract_hash(*args)
124
- @hash['level'] = message_level unless message_level.nil?
125
- if @hash['level'] >= level
126
- str = serialize_hash
123
+ hash = extract_hash(*args)
124
+ hash['level'] = message_level unless message_level.nil?
125
+ if hash['level'] >= level
126
+ str = serialize_hash(hash)
127
127
  @sender.send_datagram(str)
128
128
  str
129
129
  end
130
130
  end
131
131
 
132
132
  def extract_hash(object = nil, args = {})
133
+ args = self.class.stringify_keys(args)
134
+
133
135
  primary_data = if object.respond_to?(:to_hash)
134
136
  object.to_hash
135
137
  elsif object.is_a?(Exception)
@@ -140,74 +142,108 @@ module SyslogSD
140
142
  { 'short_message' => object.to_s }
141
143
  end
142
144
 
143
- @hash = default_options.merge(self.class.stringify_keys(args.merge(primary_data)))
144
- convert_hoptoad_keys_to_graylog2
145
- set_file_and_line if @collect_file_and_line
146
- set_timestamp
147
- check_presence_of_mandatory_attributes
148
- @hash
145
+ hash = self.class.stringify_keys(primary_data)
146
+ hash = default_options.merge(args.merge(hash))
147
+ hash = convert_airbrake_keys_to_graylog2(hash)
148
+ hash = set_file_and_line(hash)
149
+ hash = set_timestamp(hash)
150
+ check_presence_of_mandatory_attributes(hash)
151
+ hash
149
152
  end
150
153
 
154
+ CALLER_REGEXP = /^(.*):(\d+).*/
155
+
151
156
  def self.extract_hash_from_exception(exception)
152
- bt = exception.backtrace || ["Backtrace is not available."]
153
- { 'short_message' => "#{exception.class}: #{exception.message}", 'full_message' => "Backtrace:\n" + bt.join("\n") }
157
+ error_class = exception.class.name
158
+ error_message = exception.message
159
+
160
+ # always collect file and line there (ignore @collect_file_and_line)
161
+ # since we already know them, no need to call `caller`
162
+ file, line = nil, nil
163
+ bt = exception.backtrace
164
+ if bt
165
+ match = CALLER_REGEXP.match(bt[0])
166
+ if match
167
+ file = match[1]
168
+ line = match[2].to_i
169
+ end
170
+ else
171
+ bt = ["Backtrace is not available."]
172
+ end
173
+
174
+ { 'short_message' => "#{error_class}: #{error_message}", 'full_message' => "Backtrace:\n" + bt.join("\n"),
175
+ 'error_class' => error_class, 'error_message' => error_message,
176
+ 'file' => file, 'line' => line }
154
177
  end
155
178
 
156
- # Converts Hoptoad-specific keys in +@hash+ to Graylog2-specific.
157
- def convert_hoptoad_keys_to_graylog2
158
- if @hash['short_message'].to_s.empty?
159
- if @hash.has_key?('error_class') && @hash.has_key?('error_message')
160
- @hash['short_message'] = @hash.delete('error_class') + ': ' + @hash.delete('error_message')
179
+ # Converts Airbrake-specific keys in +hash+ to Graylog2-specific.
180
+ def convert_airbrake_keys_to_graylog2(hash)
181
+ if hash['short_message'].to_s.empty?
182
+ if hash.has_key?('error_class') && hash.has_key?('error_message')
183
+ hash['short_message'] = hash['error_class'] + ': ' + hash['error_message']
161
184
  end
162
185
  end
186
+ hash
163
187
  end
164
188
 
165
- CALLER_REGEXP = /^(.*):(\d+).*/
166
189
  LIB_PATTERN = File.join('lib', 'syslog-sd')
167
190
 
168
- def set_file_and_line
169
- stack = caller
170
- begin
171
- frame = stack.shift
172
- end while frame.include?(LIB_PATTERN)
173
- match = CALLER_REGEXP.match(frame)
174
- @hash['file'] = match[1]
175
- @hash['line'] = match[2].to_i
191
+ def set_file_and_line(hash)
192
+ return hash unless hash['file'].nil? || hash['line'].nil?
193
+
194
+ if @collect_file_and_line
195
+ stack = caller
196
+ begin
197
+ frame = stack.shift
198
+ end while frame.include?(LIB_PATTERN)
199
+
200
+ match = CALLER_REGEXP.match(frame)
201
+ if match
202
+ hash['file'] = match[1]
203
+ hash['line'] = match[2].to_i
204
+ else
205
+ hash['file'] = 'unknown'
206
+ hash['line'] = -1
207
+ end
208
+ end
209
+
210
+ hash
176
211
  end
177
212
 
178
- def set_timestamp
179
- @hash['timestamp'] = Time.now.utc.to_f if @hash['timestamp'].nil?
213
+ def set_timestamp(hash)
214
+ hash['timestamp'] ||= Time.now.utc.to_f
215
+ hash
180
216
  end
181
217
 
182
- def check_presence_of_mandatory_attributes
218
+ def check_presence_of_mandatory_attributes(hash)
183
219
  %w(short_message host).each do |attribute|
184
- if @hash[attribute].to_s.empty?
220
+ if hash[attribute].to_s.empty?
185
221
  raise ArgumentError.new("#{attribute} is missing. Options short_message and host must be set.")
186
222
  end
187
223
  end
188
224
  end
189
225
 
190
- def serialize_hash
191
- raise ArgumentError.new("Hash is empty.") if @hash.nil? || @hash.empty?
226
+ def serialize_hash(hash)
227
+ raise ArgumentError.new("Hash is empty.") if hash.nil? || hash.empty?
192
228
 
193
- @hash['level'] = @level_mapping[@hash['level']]
229
+ hash['level'] = @level_mapping[hash['level']]
194
230
 
195
- prival = 128 + @hash.delete('level') # 128 = 16(local0) * 8
231
+ prival = 128 + hash.delete('level') # 128 = 16(local0) * 8
196
232
  t = Time.now.utc
197
233
  timestamp = timestamp_as_float ? t.to_f : t.strftime("%Y-%m-%dT%H:%M:%S.#{t.usec.to_s[0,3]}Z")
198
- host = @hash.delete('host')
199
- facility = @hash.delete('facility') || '-'
200
- procid = @hash.delete('procid')
201
- msgid = @hash.delete('msgid') || '-'
202
- short_message = @hash.delete('short_message')
203
- "<#{prival}>1 #{timestamp} #{host} #{facility} #{procid} #{msgid} #{serialize_sd} #{short_message}"
234
+ host = hash.delete('host')
235
+ facility = hash.delete('facility') || '-'
236
+ procid = hash.delete('procid')
237
+ msgid = hash.delete('msgid') || '-'
238
+ short_message = hash.delete('short_message')
239
+ "<#{prival}>1 #{timestamp} #{host} #{facility} #{procid} #{msgid} #{serialize_sd(hash)} #{short_message}"
204
240
  end
205
241
 
206
- def serialize_sd
207
- return '-' if @hash.empty?
242
+ def serialize_sd(hash)
243
+ return '-' if hash.empty?
208
244
 
209
245
  res = @sd_id
210
- @hash.each_pair do |key, value|
246
+ hash.each_pair do |key, value|
211
247
  value = value.to_s.gsub(/([\\\]\"])/) { "\\#{$1}" }
212
248
  res += " #{key}=\"#{value}\""
213
249
  end
@@ -1,15 +1,29 @@
1
1
  module SyslogSD
2
2
  # Plain Ruby UDP sender.
3
3
  class RubyUdpSender
4
- attr_accessor :addresses
5
-
6
- def initialize(addresses)
7
- @addresses = addresses
8
- @i = 0
4
+ def initialize(addrs)
5
+ @mutex = ::Mutex.new
9
6
  @socket = UDPSocket.open
7
+ @socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, 65507) # 65535 - 20 (ip header) - 8 (udp header)
8
+ self.addresses = addrs
9
+ end
10
+
11
+ def addresses
12
+ @mutex.synchronize do
13
+ @i = 0
14
+ @addresses
15
+ end
16
+ end
17
+
18
+ def addresses=(addrs)
19
+ @mutex.synchronize do
20
+ @i = 0
21
+ @addresses = addrs
22
+ end
10
23
  end
11
24
 
12
25
  def send_datagram(datagram)
26
+ # not thread-safe, but we don't care if round-robin algo fails sometimes
13
27
  host, port = @addresses[@i]
14
28
  @i = (@i + 1) % @addresses.length
15
29
  @socket.send(datagram, 0, host, port)
data/lib/syslog-sd.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'socket'
2
2
  require 'digest/md5'
3
+ require 'thread'
3
4
 
4
5
  require 'syslog-sd/severity'
5
6
  require 'syslog-sd/ruby_sender'
data/syslog-sd.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "syslog-sd"
8
- s.version = "1.3.2"
8
+ s.version = "1.4.0.beta1"
9
9
 
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
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 = "2011-12-02"
12
+ s.date = "2012-01-17"
13
13
  s.description = "Super-Duper library to send syslog messages over UDP to logging server such as Graylog2. Supports Structured Data elements as defined by RFC 5424."
14
14
  s.email = "alexey.palazhchenko@gmail.com"
15
15
  s.extra_rdoc_files = [
@@ -24,6 +24,7 @@ Gem::Specification.new do |s|
24
24
  "README.rdoc",
25
25
  "Rakefile",
26
26
  "VERSION",
27
+ "benchmarks/incremental.rb",
27
28
  "benchmarks/notifier.rb",
28
29
  "lib/syslog-sd.rb",
29
30
  "lib/syslog-sd/logger.rb",
@@ -39,7 +40,7 @@ Gem::Specification.new do |s|
39
40
  ]
40
41
  s.homepage = "http://github.com/Graylog2/syslog-sd-rb"
41
42
  s.require_paths = ["lib"]
42
- s.rubygems_version = "1.8.11"
43
+ s.rubygems_version = "1.8.15"
43
44
  s.summary = "Library to send syslog messages over UDP to logging server such as Graylog2. Supports Structured Data elements as defined by RFC 5424."
44
45
 
45
46
  if s.respond_to? :specification_version then
@@ -49,20 +49,30 @@ class TestNotifier < Test::Unit::TestCase
49
49
  e.set_backtrace(caller)
50
50
  hash = @notifier.__send__(:extract_hash, e)
51
51
  assert_equal 'RuntimeError: message', hash['short_message']
52
+ assert_equal 'RuntimeError', hash['error_class']
53
+ assert_equal 'message', hash['error_message']
54
+ assert_match /shoulda/, hash['file']
55
+ assert hash['line'] > 300 # 382 in shoulda 2.11.3
52
56
  assert_match /Backtrace/, hash['full_message']
53
57
  assert_equal SyslogSD::ERROR, hash['level']
54
58
  end
55
59
 
56
60
  should "work with exception without backtrace" do
57
61
  e = RuntimeError.new('message')
58
- hash = @notifier.__send__(:extract_hash, e)
62
+ hash, line = @notifier.__send__(:extract_hash, e), __LINE__
63
+ assert_equal __FILE__, hash['file']
64
+ assert_equal line, hash['line']
59
65
  assert_match /Backtrace is not available/, hash['full_message']
60
66
  end
61
67
 
62
68
  should "work with exception and hash" do
63
69
  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)
70
+ hash, line = @notifier.__send__(:extract_hash, e, h), __LINE__
65
71
  assert_equal 'RuntimeError: message', hash['short_message']
72
+ assert_equal 'RuntimeError', hash['error_class']
73
+ assert_equal 'message', hash['error_message']
74
+ assert_equal __FILE__, hash['file']
75
+ assert_equal line, hash['line']
66
76
  assert_equal SyslogSD::FATAL, hash['level']
67
77
  assert_equal 1, hash['param']
68
78
  end
@@ -77,6 +87,10 @@ class TestNotifier < Test::Unit::TestCase
77
87
  hash = @notifier.__send__(:extract_hash, 'message', 'level' => SyslogSD::WARN)
78
88
  assert_equal 'message', hash['short_message']
79
89
  assert_equal SyslogSD::WARN, hash['level']
90
+
91
+ hash = @notifier.__send__(:extract_hash, 'message', :level => SyslogSD::WARN)
92
+ assert_equal 'message', hash['short_message']
93
+ assert_equal SyslogSD::WARN, hash['level']
80
94
  end
81
95
 
82
96
  should "covert hash keys to strings" do
@@ -96,10 +110,12 @@ class TestNotifier < Test::Unit::TestCase
96
110
  assert_equal 'message', hash['short_message']
97
111
  end
98
112
 
99
- should "be compatible with HoptoadNotifier" do
100
- # https://github.com/thoughtbot/hoptoad_notifier/blob/master/README.rdoc, section Going beyond exceptions
113
+ should "be compatible with Airbrake" do
114
+ # https://github.com/airbrake/airbrake/blob/master/README.md, section Going beyond exceptions
101
115
  hash = @notifier.__send__(:extract_hash, :error_class => 'Class', :error_message => 'Message')
102
116
  assert_equal 'Class: Message', hash['short_message']
117
+ assert_equal 'Class', hash['error_class']
118
+ assert_equal 'Message', hash['error_message']
103
119
  end
104
120
 
105
121
  should "set file and line" do
@@ -143,8 +159,7 @@ class TestNotifier < Test::Unit::TestCase
143
159
 
144
160
  expected.each_pair do |key, value|
145
161
  should "be as #{value}" do
146
- @notifier.instance_variable_set('@hash', key)
147
- assert_equal value, @notifier.__send__(:serialize_hash)
162
+ assert_equal value, @notifier.__send__(:serialize_hash, key)
148
163
  end
149
164
  end
150
165
 
@@ -153,8 +168,7 @@ class TestNotifier < Test::Unit::TestCase
153
168
  hash = { 'short_message' => 'message', 'level' => SyslogSD::WARN, 'host' => 'host', 'facility' => 'facility', 'procid' => 123,
154
169
  'msgid' => 'msgid', 'user_id' => 123 }
155
170
  expected = '<132>1 1274011994.0 host facility 123 msgid [_@37797 user_id="123"] message'
156
- @notifier.instance_variable_set('@hash', hash)
157
- assert_equal expected, @notifier.__send__(:serialize_hash)
171
+ assert_equal expected, @notifier.__send__(:serialize_hash, hash)
158
172
  end
159
173
 
160
174
  teardown do
metadata CHANGED
@@ -1,13 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: syslog-sd
3
3
  version: !ruby/object:Gem::Version
4
- hash: 31
5
- prerelease:
4
+ hash: -3060412182
5
+ prerelease: 6
6
6
  segments:
7
7
  - 1
8
- - 3
9
- - 2
10
- version: 1.3.2
8
+ - 4
9
+ - 0
10
+ - beta
11
+ - 1
12
+ version: 1.4.0.beta1
11
13
  platform: ruby
12
14
  authors:
13
15
  - Alexey Palazhchenko
@@ -16,7 +18,7 @@ autorequire:
16
18
  bindir: bin
17
19
  cert_chain: []
18
20
 
19
- date: 2011-12-02 00:00:00 Z
21
+ date: 2012-01-17 00:00:00 Z
20
22
  dependencies:
21
23
  - !ruby/object:Gem::Dependency
22
24
  name: shoulda
@@ -77,6 +79,7 @@ files:
77
79
  - README.rdoc
78
80
  - Rakefile
79
81
  - VERSION
82
+ - benchmarks/incremental.rb
80
83
  - benchmarks/notifier.rb
81
84
  - lib/syslog-sd.rb
82
85
  - lib/syslog-sd/logger.rb
@@ -109,16 +112,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
109
112
  required_rubygems_version: !ruby/object:Gem::Requirement
110
113
  none: false
111
114
  requirements:
112
- - - ">="
115
+ - - ">"
113
116
  - !ruby/object:Gem::Version
114
- hash: 3
117
+ hash: 25
115
118
  segments:
116
- - 0
117
- version: "0"
119
+ - 1
120
+ - 3
121
+ - 1
122
+ version: 1.3.1
118
123
  requirements: []
119
124
 
120
125
  rubyforge_project:
121
- rubygems_version: 1.8.11
126
+ rubygems_version: 1.8.15
122
127
  signing_key:
123
128
  specification_version: 3
124
129
  summary: Library to send syslog messages over UDP to logging server such as Graylog2. Supports Structured Data elements as defined by RFC 5424.