gelf 2.0.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 204c030f01e64d6980fe343a5d53144c3f31c894
4
- data.tar.gz: 7ae8e4d33e55764f191692a396f950e87118f359
3
+ metadata.gz: 7e1c02912c99651ae9b78b3d80a254b39e44b880
4
+ data.tar.gz: 0723b767126b56dbe6d86bcb2ae77da7024eb128
5
5
  SHA512:
6
- metadata.gz: d3dfbe2d3d722a53de17c4a46edf93ffb4d17105b1f481f07bfaad598d9a29a070d1cdc5c8160e05c11bc1d373ee95c1de7997b676981826169a191f507c324e
7
- data.tar.gz: c4d5d7fbf18e55096ae640af049c7e508a718a88c8e321b901bf7765f5da85c421ca3bb76d037534c5844cb1ae85534adda4648b3c9a601228246c7b54f21e17
6
+ metadata.gz: fa809d0a268591b148822d58bf1f32d703b18ba8c1149e0c70d160cd30e49b834ddb2ef4d9bc206bd0650a6432e9021823d299bb760bfaa126dfd34d6960dc42
7
+ data.tar.gz: 99f27da7b54e6a0e19713ae4c926a2005a7c94236c0b4d28a322cf78db81361e3cb4504a93c138cce9bcbc0025e71956adc86b3eb5b1e341f028c79f07703509
@@ -1,12 +1,16 @@
1
1
  rvm:
2
- - 1.8.7
3
- - ree
4
- - 1.9.2
5
- - 1.9.3
6
- - 2.0.0
7
- - 2.1.0
8
- - 2.2.0
9
- - jruby-19mode
10
-
11
- notifications:
12
- irc: "irc.freenode.org#graylog2"
2
+ - 1.9.3
3
+ - 2.0.0
4
+ - 2.1.0
5
+ - 2.2.0
6
+ - 2.3.0
7
+ - jruby-19mode
8
+ deploy:
9
+ provider: rubygems
10
+ api_key:
11
+ secure: Pm1yAqo2ldet/Fd2jK5dKZkDNmyx5KkYtViL+eOR4Rxs82dwqOcCSyrBL6BOzuRfJtpjgXUX6FDapKgLDYW/2EDW8mmiciOL0WLzxYQsmJXcerJeUl2tTeFKpyEf+8aaDQpAlsK0m1dPmQjCJF9MWsa7m3oshm3mJ1VITY90chM=
12
+ gem: gelf
13
+ gemspec: gelf.gemspec
14
+ on:
15
+ tags: true
16
+ repo: Graylog2/gelf-rb
data/CHANGELOG CHANGED
@@ -1,5 +1,13 @@
1
+ 3.0.0, 2016-08-21
2
+ + Overhaul TCP support
3
+ + Include automatic support for Celluloid::IO if available
4
+ + Add TLS support to TCP transport
5
+ - Remove support for ancient Rubygems versions
6
+ - Remove support for Ruby 1.9.2 (1.9.3 works!)
7
+ - Remove already-deprecated `host` and `port` methods on GELF::Notifier
8
+
1
9
  2.0.0, 2016-02-02
2
- * Added GELF support
10
+ + Added GELF TCP support.
3
11
 
4
12
  1.3.2, 2011-12-02:
5
13
  * support for rubygems-test.
data/Gemfile CHANGED
@@ -2,7 +2,10 @@ source "https://rubygems.org"
2
2
 
3
3
  group :development do
4
4
  gem "shoulda", "~> 2.11.3"
5
- gem "jeweler", "~> 1.8.7"
6
- gem "mocha", "~> 0.14.0"
7
- gem "test-unit", "~> 3.0.8"
5
+ gem "jeweler", "~> 2.1.1"
6
+ # Because of a dependency chain jeweler->github_api->oauth2->rack,
7
+ # pin the version: Rack 2.0.x doesn't work on < Ruby 2.2
8
+ gem 'rack', '< 2.0'
9
+ gem "mocha", "~> 1.1.0"
10
+ gem "test-unit", "~> 3.2.0"
8
11
  end
@@ -1,61 +1,70 @@
1
1
  GEM
2
2
  remote: https://rubygems.org/
3
3
  specs:
4
- addressable (2.3.5)
4
+ addressable (2.4.0)
5
5
  builder (3.2.2)
6
- faraday (0.8.8)
7
- multipart-post (~> 1.2.0)
8
- git (1.2.6)
9
- github_api (0.10.1)
10
- addressable
11
- faraday (~> 0.8.1)
12
- hashie (>= 1.2)
13
- multi_json (~> 1.4)
14
- nokogiri (~> 1.5.2)
15
- oauth2
16
- hashie (2.0.5)
17
- highline (1.6.19)
18
- httpauth (0.2.0)
19
- jeweler (1.8.7)
6
+ descendants_tracker (0.0.4)
7
+ thread_safe (~> 0.3, >= 0.3.1)
8
+ faraday (0.9.2)
9
+ multipart-post (>= 1.2, < 3)
10
+ git (1.3.0)
11
+ github_api (0.14.5)
12
+ addressable (~> 2.4.0)
13
+ descendants_tracker (~> 0.0.4)
14
+ faraday (~> 0.8, < 0.10)
15
+ hashie (>= 3.4)
16
+ oauth2 (~> 1.0)
17
+ hashie (3.4.4)
18
+ highline (1.7.8)
19
+ jeweler (2.1.1)
20
20
  builder
21
- bundler (~> 1.0)
21
+ bundler (>= 1.0)
22
22
  git (>= 1.2.5)
23
- github_api (= 0.10.1)
23
+ github_api
24
24
  highline (>= 1.6.15)
25
- nokogiri (= 1.5.10)
25
+ nokogiri (>= 1.5.10)
26
26
  rake
27
27
  rdoc
28
- json (1.8.2)
29
- jwt (0.1.8)
30
- multi_json (>= 1.5)
31
- metaclass (0.0.1)
32
- mocha (0.14.0)
28
+ semver
29
+ json (1.8.3)
30
+ jwt (1.5.4)
31
+ metaclass (0.0.4)
32
+ mini_portile2 (2.1.0)
33
+ mocha (1.1.0)
33
34
  metaclass (~> 0.0.1)
34
- multi_json (1.7.9)
35
+ multi_json (1.12.1)
35
36
  multi_xml (0.5.5)
36
- multipart-post (1.2.0)
37
- nokogiri (1.5.10)
38
- oauth2 (0.9.2)
39
- faraday (~> 0.8)
40
- httpauth (~> 0.2)
41
- jwt (~> 0.1.4)
42
- multi_json (~> 1.0)
37
+ multipart-post (2.0.0)
38
+ nokogiri (1.6.8)
39
+ mini_portile2 (~> 2.1.0)
40
+ pkg-config (~> 1.1.7)
41
+ oauth2 (1.2.0)
42
+ faraday (>= 0.8, < 0.10)
43
+ jwt (~> 1.0)
44
+ multi_json (~> 1.3)
43
45
  multi_xml (~> 0.5)
44
- rack (~> 1.2)
45
- power_assert (0.2.3)
46
- rack (1.5.2)
47
- rake (10.1.0)
48
- rdoc (4.0.1)
46
+ rack (>= 1.2, < 3)
47
+ pkg-config (1.1.7)
48
+ power_assert (0.3.0)
49
+ rack (1.6.4)
50
+ rake (11.2.2)
51
+ rdoc (4.2.2)
49
52
  json (~> 1.4)
53
+ semver (1.0.1)
50
54
  shoulda (2.11.3)
51
- test-unit (3.0.9)
55
+ test-unit (3.2.1)
52
56
  power_assert
57
+ thread_safe (0.3.5)
53
58
 
54
59
  PLATFORMS
55
60
  ruby
56
61
 
57
62
  DEPENDENCIES
58
- jeweler (~> 1.8.7)
59
- mocha (~> 0.14.0)
63
+ jeweler (~> 2.1.1)
64
+ mocha (~> 1.1.0)
65
+ rack (< 2.0)
60
66
  shoulda (~> 2.11.3)
61
- test-unit (~> 3.0.8)
67
+ test-unit (~> 3.2.0)
68
+
69
+ BUNDLED WITH
70
+ 1.12.5
data/README.md CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  This is the new GELF gem written by Alexey Palazhchenko. It is based on the old gem by Lennart Koopmann and allows you to send GELF messages to Graylog2 server instances. See [http://www.graylog2.org/about/gelf](http://www.graylog2.org/about/gelf) for more information about GELF and [http://rdoc.info/github/Graylog2/gelf-rb/master/frames](http://rdoc.info/github/Graylog2/gelf-rb/master/frames) for API documentation.
4
4
 
5
- Tested with Ruby 1.8.7, 1.9.x. and 2.0.x.
5
+ Tested with Ruby 1.9, 2.0, 2.1, 2.2 and 2.3.
6
6
 
7
- ![](https://travis-ci.org/Graylog2/gelf-rb.png?branch=master)
7
+ ![](https://travis-ci.org/Graylog2/gelf-rb.png?branch=master) [![Code Climate](https://codeclimate.com/github/Graylog2/gelf-rb/badges/gpa.svg)](https://codeclimate.com/github/Graylog2/gelf-rb)
8
8
 
9
9
  ## Usage
10
10
  ### Gelf::Notifier
data/Rakefile CHANGED
@@ -13,9 +13,9 @@ begin
13
13
  gem.name = "gelf"
14
14
  gem.summary = 'Library to send GELF messages to Graylog logging server.'
15
15
  gem.description = 'Library to send GELF messages to Graylog logging server. Supports plain-text, GELF messages and exceptions via UDP and TCP.'
16
- gem.email = "lennart@graylog.com"
16
+ gem.email = "mail@marcusilgner.com"
17
17
  gem.homepage = "http://github.com/Graylog2/gelf-rb"
18
- gem.authors = ["Alexey Palazhchenko", "Lennart Koopmann", "Zac Sprackett"]
18
+ gem.authors = ["Alexey Palazhchenko", "Lennart Koopmann", "Zac Sprackett", "Marcus Ilgner"]
19
19
  gem.add_dependency "json"
20
20
  gem.licenses = ["MIT"]
21
21
  # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.0.0
1
+ 3.0.0
@@ -2,20 +2,19 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: gelf 2.0.0 ruby lib
5
+ # stub: gelf 3.0.0 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "gelf"
9
- s.version = "2.0.0"
9
+ s.version = "3.0.0"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib"]
13
- s.authors = ["Alexey Palazhchenko", "Lennart Koopmann", "Zac Sprackett"]
14
- s.date = "2016-02-03"
13
+ s.authors = ["Alexey Palazhchenko", "Lennart Koopmann", "Zac Sprackett", "Marcus Ilgner"]
14
+ s.date = "2016-08-21"
15
15
  s.description = "Library to send GELF messages to Graylog logging server. Supports plain-text, GELF messages and exceptions via UDP and TCP."
16
- s.email = "lennart@graylog.com"
16
+ s.email = "mail@marcusilgner.com"
17
17
  s.extra_rdoc_files = [
18
- "CHANGELOG",
19
18
  "LICENSE",
20
19
  "README.md"
21
20
  ]
@@ -35,8 +34,10 @@ Gem::Specification.new do |s|
35
34
  "lib/gelf.rb",
36
35
  "lib/gelf/logger.rb",
37
36
  "lib/gelf/notifier.rb",
38
- "lib/gelf/ruby_sender.rb",
39
37
  "lib/gelf/severity.rb",
38
+ "lib/gelf/transport/tcp.rb",
39
+ "lib/gelf/transport/tcp_tls.rb",
40
+ "lib/gelf/transport/udp.rb",
40
41
  "test/helper.rb",
41
42
  "test/test_logger.rb",
42
43
  "test/test_notifier.rb",
@@ -45,7 +46,7 @@ Gem::Specification.new do |s|
45
46
  ]
46
47
  s.homepage = "http://github.com/Graylog2/gelf-rb"
47
48
  s.licenses = ["MIT"]
48
- s.rubygems_version = "2.4.5.1"
49
+ s.rubygems_version = "2.5.1"
49
50
  s.summary = "Library to send GELF messages to Graylog logging server."
50
51
 
51
52
  if s.respond_to? :specification_version then
@@ -53,22 +54,25 @@ Gem::Specification.new do |s|
53
54
 
54
55
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
55
56
  s.add_development_dependency(%q<shoulda>, ["~> 2.11.3"])
56
- s.add_development_dependency(%q<jeweler>, ["~> 1.8.7"])
57
- s.add_development_dependency(%q<mocha>, ["~> 0.14.0"])
58
- s.add_development_dependency(%q<test-unit>, ["~> 3.0.8"])
57
+ s.add_development_dependency(%q<jeweler>, ["~> 2.1.1"])
58
+ s.add_development_dependency(%q<rack>, ["< 2.0"])
59
+ s.add_development_dependency(%q<mocha>, ["~> 1.1.0"])
60
+ s.add_development_dependency(%q<test-unit>, ["~> 3.2.0"])
59
61
  s.add_runtime_dependency(%q<json>, [">= 0"])
60
62
  else
61
63
  s.add_dependency(%q<shoulda>, ["~> 2.11.3"])
62
- s.add_dependency(%q<jeweler>, ["~> 1.8.7"])
63
- s.add_dependency(%q<mocha>, ["~> 0.14.0"])
64
- s.add_dependency(%q<test-unit>, ["~> 3.0.8"])
64
+ s.add_dependency(%q<jeweler>, ["~> 2.1.1"])
65
+ s.add_dependency(%q<rack>, ["< 2.0"])
66
+ s.add_dependency(%q<mocha>, ["~> 1.1.0"])
67
+ s.add_dependency(%q<test-unit>, ["~> 3.2.0"])
65
68
  s.add_dependency(%q<json>, [">= 0"])
66
69
  end
67
70
  else
68
71
  s.add_dependency(%q<shoulda>, ["~> 2.11.3"])
69
- s.add_dependency(%q<jeweler>, ["~> 1.8.7"])
70
- s.add_dependency(%q<mocha>, ["~> 0.14.0"])
71
- s.add_dependency(%q<test-unit>, ["~> 3.0.8"])
72
+ s.add_dependency(%q<jeweler>, ["~> 2.1.1"])
73
+ s.add_dependency(%q<rack>, ["< 2.0"])
74
+ s.add_dependency(%q<mocha>, ["~> 1.1.0"])
75
+ s.add_dependency(%q<test-unit>, ["~> 3.2.0"])
72
76
  s.add_dependency(%q<json>, [">= 0"])
73
77
  end
74
78
  end
@@ -12,6 +12,5 @@ module GELF
12
12
  end
13
13
 
14
14
  require 'gelf/severity'
15
- require 'gelf/ruby_sender'
16
15
  require 'gelf/notifier'
17
16
  require 'gelf/logger'
@@ -5,7 +5,7 @@ module GELF
5
5
  attr_accessor :formatter
6
6
 
7
7
  # Use it like Logger#add... or better not to use at all.
8
- def add(level, message = nil, progname = nil, &block)
8
+ def add(level, message = nil, progname = nil)
9
9
  progname ||= default_options['facility']
10
10
 
11
11
  if message.nil?
@@ -21,11 +21,11 @@ module GELF
21
21
 
22
22
  if message.is_a?(Hash)
23
23
  # Stringify keys.
24
- message.each do |k,v|
25
- message_hash[k.to_s] = message[k]
24
+ message.each do |key, value|
25
+ message_hash[key.to_s] = value
26
26
  end
27
27
  else
28
- message_hash['short_message'] = message
28
+ message_hash['short_message'] = message.to_s
29
29
  end
30
30
 
31
31
  if message.is_a?(Exception)
@@ -37,15 +37,17 @@ module GELF
37
37
 
38
38
  # Redefines methods in +Notifier+.
39
39
  GELF::Levels.constants.each do |const|
40
- class_eval <<-EOT, __FILE__, __LINE__ + 1
41
- def #{const.downcase}(progname = nil, &block) # def debug(progname = nil, &block)
42
- add(GELF::#{const}, nil, progname, &block) # add(GELF::DEBUG, nil, progname, &block)
43
- end # end
40
+ method_name = const.downcase
44
41
 
45
- def #{const.downcase}? # def debug?
46
- GELF::#{const} >= level # GELF::DEBUG >= level
47
- end # end
48
- EOT
42
+ define_method(method_name) do |progname=nil, &block|
43
+ const_level = GELF.const_get(const)
44
+ add(const_level, nil, progname, &block)
45
+ end
46
+
47
+ define_method("#{method_name}?") do
48
+ const_level = GELF.const_get(const)
49
+ const_level >= level
50
+ end
49
51
  end
50
52
 
51
53
  def <<(message)
@@ -1,10 +1,14 @@
1
+ require 'gelf/transport/udp'
2
+ require 'gelf/transport/tcp'
3
+ require 'gelf/transport/tcp_tls'
4
+
1
5
  module GELF
2
6
  # Graylog2 notifier.
3
7
  class Notifier
4
- @last_chunk_id = 0
5
- class << self
6
- attr_accessor :last_chunk_id
7
- end
8
+ # Maximum number of GELF chunks as per GELF spec
9
+ MAX_CHUNKS = 128
10
+ MAX_CHUNK_SIZE_WAN = 1420
11
+ MAX_CHUNK_SIZE_LAN = 8154
8
12
 
9
13
  attr_accessor :enabled, :collect_file_and_line, :rescue_network_errors
10
14
  attr_reader :max_chunk_size, :level, :default_options, :level_mapping
@@ -15,24 +19,21 @@ module GELF
15
19
  def initialize(host = 'localhost', port = 12201, max_size = 'WAN', default_options = {})
16
20
  @enabled = true
17
21
  @collect_file_and_line = true
22
+ @random = Random.new
18
23
 
19
24
  self.level = GELF::DEBUG
20
25
  self.max_chunk_size = max_size
21
26
  self.rescue_network_errors = false
22
27
 
23
- self.default_options = default_options
28
+ self.default_options = default_options.dup
24
29
  self.default_options['version'] = SPEC_VERSION
25
30
  self.default_options['host'] ||= Socket.gethostname
26
31
  self.default_options['level'] ||= GELF::UNKNOWN
27
32
  self.default_options['facility'] ||= 'gelf-rb'
28
33
  self.default_options['protocol'] ||= GELF::Protocol::UDP
29
34
 
30
- if self.default_options['protocol'] == GELF::Protocol::TCP
31
- @sender = RubyTcpSender.new([[host, port]])
32
- else
33
- @sender = RubyUdpSender.new([[host, port]])
34
- end
35
35
  self.level_mapping = :logger
36
+ @sender = create_sender(host, port)
36
37
  end
37
38
 
38
39
  # Get a list of receivers.
@@ -47,24 +48,14 @@ module GELF
47
48
  @sender.addresses = addrs
48
49
  end
49
50
 
50
- def host
51
- warn "GELF::Notifier#host is deprecated. Use #addresses instead."
52
- self.addresses.first[0]
53
- end
54
-
55
- def port
56
- warn "GELF::Notifier#port is deprecated. Use #addresses instead."
57
- self.addresses.first[1]
58
- end
59
-
60
51
  # +size+ may be a number of bytes, 'WAN' (1420 bytes) or 'LAN' (8154).
61
52
  # Default (safe) value is 'WAN'.
62
53
  def max_chunk_size=(size)
63
54
  case size.to_s.downcase
64
55
  when 'wan'
65
- @max_chunk_size = 1420
56
+ @max_chunk_size = MAX_CHUNK_SIZE_WAN
66
57
  when 'lan'
67
- @max_chunk_size = 8154
58
+ @max_chunk_size = MAX_CHUNK_SIZE_LAN
68
59
  else
69
60
  @max_chunk_size = size.to_int
70
61
  end
@@ -129,18 +120,32 @@ module GELF
129
120
  end
130
121
 
131
122
  GELF::Levels.constants.each do |const|
132
- class_eval <<-EOT, __FILE__, __LINE__ + 1
133
- def #{const.downcase}(*args) # def debug(*args)
134
- notify_with_level(GELF::#{const}, *args) # notify_with_level(GELF::DEBUG, *args)
135
- end # end
136
- EOT
123
+ define_method(const.downcase) do |*args|
124
+ level = GELF.const_get(const)
125
+ notify_with_level(level, *args)
126
+ end
137
127
  end
138
128
 
139
129
  private
130
+
131
+ def create_sender(host, port)
132
+ addresses = [[host, port]]
133
+ if default_options['protocol'] == GELF::Protocol::TCP
134
+ if default_options.key?('tls')
135
+ tls_options = default_options.delete('tls')
136
+ GELF::Transport::TCPTLS.new(addresses, tls_options)
137
+ else
138
+ GELF::Transport::TCP.new(addresses)
139
+ end
140
+ else
141
+ GELF::Transport::UDP.new(addresses)
142
+ end
143
+ end
144
+
140
145
  def notify_with_level(message_level, *args)
141
146
  notify_with_level!(message_level, *args)
142
147
  rescue SocketError, SystemCallError
143
- raise unless self.rescue_network_errors
148
+ raise unless rescue_network_errors
144
149
  rescue Exception => exception
145
150
  notify_with_level!(GELF::UNKNOWN, exception)
146
151
  end
@@ -150,7 +155,7 @@ module GELF
150
155
  extract_hash(*args)
151
156
  @hash['level'] = message_level unless message_level.nil?
152
157
  if @hash['level'] >= level
153
- if self.default_options['protocol'] == GELF::Protocol::TCP
158
+ if default_options['protocol'] == GELF::Protocol::TCP
154
159
  validate_hash
155
160
  @sender.send(@hash.to_json + "\0")
156
161
  else
@@ -180,7 +185,10 @@ module GELF
180
185
 
181
186
  def self.extract_hash_from_exception(exception)
182
187
  bt = exception.backtrace || ["Backtrace is not available."]
183
- { 'short_message' => "#{exception.class}: #{exception.message}", 'full_message' => "Backtrace:\n" + bt.join("\n") }
188
+ {
189
+ 'short_message' => "#{exception.class}: #{exception.message}",
190
+ 'full_message' => "Backtrace:\n" + bt.join("\n")
191
+ }
184
192
  end
185
193
 
186
194
  # Converts Hoptoad-specific keys in +@hash+ to Graylog2-specific.
@@ -197,9 +205,7 @@ module GELF
197
205
 
198
206
  def set_file_and_line
199
207
  stack = caller
200
- begin
201
- frame = stack.shift
202
- end while frame.include?(LIB_GELF_PATTERN)
208
+ frame = stack.find { |f| !f.include?(LIB_GELF_PATTERN) }
203
209
  match = CALLER_REGEXP.match(frame)
204
210
  @hash['file'] = match[1]
205
211
  @hash['line'] = match[2].to_i
@@ -223,9 +229,12 @@ module GELF
223
229
 
224
230
  # Maximum total size is 8192 byte for UDP datagram. Split to chunks if bigger. (GELF v1.0 supports chunking)
225
231
  if data.count > @max_chunk_size
226
- id = GELF::Notifier.last_chunk_id += 1
232
+ id = @random.bytes(8)
227
233
  msg_id = Digest::MD5.digest("#{Time.now.to_f}-#{id}")[0, 8]
228
234
  num, count = 0, (data.count.to_f / @max_chunk_size).ceil
235
+ if count > MAX_CHUNKS
236
+ raise ArgumentError, "Data too big (#{data.count} bytes), would create more than #{MAX_CHUNKS} chunks!"
237
+ end
229
238
  data.each_slice(@max_chunk_size) do |slice|
230
239
  datagrams << "\x1e\x0f" + msg_id + [num, count, *slice].pack('C*')
231
240
  num += 1
@@ -239,7 +248,6 @@ module GELF
239
248
 
240
249
  def validate_hash
241
250
  raise ArgumentError.new("Hash is empty.") if @hash.nil? || @hash.empty?
242
-
243
251
  @hash['level'] = @level_mapping[@hash['level']]
244
252
  end
245
253
 
@@ -252,7 +260,8 @@ module GELF
252
260
  def self.stringify_keys(hash)
253
261
  hash.keys.each do |key|
254
262
  value, key_s = hash.delete(key), key.to_s
255
- raise ArgumentError.new("Both #{key.inspect} and #{key_s} are present.") if hash.has_key?(key_s)
263
+ raise ArgumentError.new("Both #{key.inspect} and #{key_s} are present.") if hash.key?(key_s)
264
+ value = stringify_keys(value) if value.is_a?(Hash)
256
265
  hash[key_s] = value
257
266
  end
258
267
  hash
@@ -0,0 +1,63 @@
1
+ module GELF
2
+ module Transport
3
+ class TCP
4
+ attr_reader :addresses
5
+
6
+ # `addresses` Array of [host, port] pairs
7
+ def initialize(addresses)
8
+ @sockets = []
9
+ self.addresses = addresses
10
+ end
11
+
12
+ def addresses=(addresses)
13
+ @addresses = addresses.dup.freeze.tap do |addrs|
14
+ @sockets.each(&:close)
15
+ @sockets = addrs.map { |peer| connect(*peer) }
16
+ end
17
+ end
18
+
19
+ def send(message)
20
+ return if @addresses.empty?
21
+ loop do
22
+ connected = @sockets.reject(&:closed?)
23
+ reconnect_all if connected.empty?
24
+ break if write_any(connected, message)
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def connect(host, port)
31
+ socket_class.new(host, port)
32
+ end
33
+
34
+ def reconnect_all
35
+ @sockets = @sockets.each_with_index.map do |old_socket, index|
36
+ old_socket.closed? ? connect(*@addresses[index]) : old_socket
37
+ end
38
+ end
39
+
40
+ def socket_class
41
+ if defined?(Celluloid::IO::TCPSocket)
42
+ Celluloid::IO::TCPSocket
43
+ else
44
+ ::TCPSocket
45
+ end
46
+ end
47
+
48
+ def write_any(sockets, message)
49
+ sockets.shuffle.each do |socket|
50
+ return true if write_socket(socket, message)
51
+ end
52
+ false
53
+ end
54
+
55
+ def write_socket(socket, message)
56
+ socket.write(message) > 0
57
+ rescue IOError, SystemCallError
58
+ socket.close unless socket.closed?
59
+ false
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,112 @@
1
+ require 'openssl'
2
+
3
+ module GELF
4
+ module Transport
5
+ # Provides encryption capabilities for TCP connections
6
+ class TCPTLS < TCP
7
+ # Supported tls_options:
8
+ # 'no_default_ca' [Boolean] prevents OpenSSL from using the systems CA store.
9
+ # 'tls_version' [Symbol] any of :TLSv1, :TLSv1_1, :TLSv1_2 (default)
10
+ # 'cert' [String, IO] the client certificate file
11
+ # 'key' [String, IO] the key for the client certificate
12
+ # 'all_ciphers' [Boolean] allows any ciphers to be used, may be insecure
13
+ def initialize(addresses, tls_options={})
14
+ @tls_options = tls_options
15
+ super(addresses)
16
+ end
17
+
18
+ protected
19
+
20
+ def write_socket(socket, message)
21
+ super(socket, message)
22
+ rescue OpenSSL::SSL::SSLError
23
+ socket.close unless socket.closed?
24
+ false
25
+ end
26
+
27
+ def connect(host, port)
28
+ plain_socket = super(host, port)
29
+ start_tls(plain_socket)
30
+ rescue OpenSSL::SSL::SSLError
31
+ plain_socket.close unless plain_socket.closed?
32
+ nil
33
+ end
34
+
35
+ # Initiates TLS communication on the socket
36
+ def start_tls(plain_socket)
37
+ ssl_socket_class.new(plain_socket, ssl_context).tap do |ssl_socket|
38
+ ssl_socket.sync_close = true
39
+ ssl_socket.connect
40
+ end
41
+ end
42
+
43
+ def ssl_socket_class
44
+ if defined?(Celluloid::IO::SSLSocket)
45
+ Celluloid::IO::SSLSocket
46
+ else
47
+ OpenSSL::SSL::SSLSocket
48
+ end
49
+ end
50
+
51
+ def ssl_context
52
+ @ssl_context ||= OpenSSL::SSL::SSLContext.new.tap do |ctx|
53
+ ctx.cert_store = ssl_cert_store
54
+ ctx.ssl_version = tls_version
55
+ ctx.verify_mode = verify_mode
56
+ set_certificate_and_key(ctx)
57
+ restrict_ciphers(ctx) unless @tls_options['all_ciphers']
58
+ end
59
+ end
60
+
61
+ def set_certificate_and_key(context)
62
+ return unless @tls_options['cert'] && @tls_options['key']
63
+ context.cert = OpenSSL::X509::Certificate.new(resource(@tls_options['cert']))
64
+ context.key = OpenSSL::PKey::RSA.new(resource(@tls_options['key']))
65
+ end
66
+
67
+ # checks whether {resource} is a filename and tries to read it
68
+ # otherwise treats it as if it already contains certificate/key data
69
+ def resource(data)
70
+ if data.is_a?(String) && File.exist?(data)
71
+ File.read(data)
72
+ else
73
+ data
74
+ end
75
+ end
76
+
77
+ # These are A-level ciphers as reported from Graylog 2.0.1
78
+ # which were also available on Ruby using OpenSSL 1.0.2h
79
+ # A lot of AES-128-CBC based ciphers were not available
80
+ SECURE_CIPHERS = %w(
81
+ AES128-GCM-SHA256
82
+ ECDHE-RSA-AES128-GCM-SHA256
83
+ DHE-RSA-AES128-GCM-SHA256
84
+ ).freeze
85
+ def restrict_ciphers(ctx)
86
+ ctx.ciphers = SECURE_CIPHERS
87
+ end
88
+
89
+ def verify_mode
90
+ @tls_options['no_verify'] ? OpenSSL::SSL::VERIFY_NONE : OpenSSL::SSL::VERIFY_PEER
91
+ end
92
+
93
+ # SSL v2&3 are insecure, forces at least TLS v1.0 and defaults to v1.2
94
+ def tls_version
95
+ if @tls_options.key?('version') &&
96
+ OpenSSL::SSL::SSLContext::METHODS.include?(@tls_options['version']) &&
97
+ @tls_options['version'] =~ /\ATLSv/
98
+ @tls_options['version']
99
+ else
100
+ :TLSv1_2
101
+ end
102
+ end
103
+
104
+ def ssl_cert_store
105
+ OpenSSL::X509::Store.new.tap do |store|
106
+ # TODO: allow passing in expected server certificate and disabling system CAs
107
+ store.set_default_paths
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,41 @@
1
+ module GELF
2
+ module Transport
3
+ class UDP
4
+ attr_accessor :addresses
5
+
6
+ def initialize(addresses)
7
+ @addresses = addresses
8
+ end
9
+
10
+ def send_datagrams(datagrams)
11
+ socket = get_socket
12
+ idx = get_address_index
13
+
14
+ host, port = @addresses[idx]
15
+ set_address_index((idx + 1) % @addresses.length)
16
+ datagrams.each do |datagram|
17
+ socket.send(datagram, 0, host, port)
18
+ end
19
+ end
20
+
21
+ def close
22
+ socket = get_socket
23
+ socket.close if socket
24
+ end
25
+
26
+ private
27
+
28
+ def get_socket
29
+ Thread.current[:gelf_udp_socket] ||= UDPSocket.open
30
+ end
31
+
32
+ def get_address_index
33
+ Thread.current[:gelf_udp_address_idx] ||= 0
34
+ end
35
+
36
+ def set_address_index(value)
37
+ Thread.current[:gelf_udp_address_idx] = value
38
+ end
39
+ end
40
+ end
41
+ end
@@ -1,7 +1,7 @@
1
1
  require 'rubygems'
2
2
  require 'test/unit'
3
3
  require 'shoulda'
4
- require 'mocha'
4
+ require 'mocha/setup'
5
5
 
6
6
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
7
7
  $LOAD_PATH.unshift(File.dirname(__FILE__))
@@ -7,7 +7,7 @@ class TestNotifier < Test::Unit::TestCase
7
7
  Socket.expects(:gethostname).returns('default_hostname')
8
8
  n = GELF::Notifier.new
9
9
  assert_equal [[['localhost', 12201]], 1420], [n.addresses, n.max_chunk_size]
10
- assert_equal( { 'version' => '1.0', 'level' => GELF::UNKNOWN,
10
+ assert_equal( { 'version' => '1.0', 'level' => GELF::UNKNOWN, 'protocol' => 0,
11
11
  'host' => 'default_hostname', 'facility' => 'gelf-rb' },
12
12
  n.default_options )
13
13
  n.addresses, n.max_chunk_size, n.default_options = [['graylog2.org', 7777]], :lan, {:host => 'grayhost'}
@@ -52,14 +52,14 @@ class TestNotifier < Test::Unit::TestCase
52
52
  e.set_backtrace(caller)
53
53
  hash = @notifier.__send__(:extract_hash, e)
54
54
  assert_equal 'RuntimeError: message', hash['short_message']
55
- assert_match /Backtrace/, hash['full_message']
55
+ assert_match(/Backtrace/, hash['full_message'])
56
56
  assert_equal GELF::ERROR, hash['level']
57
57
  end
58
58
 
59
59
  should "work with exception without backtrace" do
60
60
  e = RuntimeError.new('message')
61
61
  hash = @notifier.__send__(:extract_hash, e)
62
- assert_match /Backtrace is not available/, hash['full_message']
62
+ assert_match(/Backtrace is not available/, hash['full_message'])
63
63
  end
64
64
 
65
65
  should "work with exception and hash" do
@@ -108,7 +108,7 @@ class TestNotifier < Test::Unit::TestCase
108
108
  should "set file and line" do
109
109
  line = __LINE__
110
110
  hash = @notifier.__send__(:extract_hash, { 'version' => '1.0', 'short_message' => 'message' })
111
- assert_match /test_notifier.rb/, hash['file']
111
+ assert_match(/test_notifier.rb/, hash['file'])
112
112
  assert_equal line + 1, hash['line']
113
113
  end
114
114
 
@@ -153,7 +153,7 @@ class TestNotifier < Test::Unit::TestCase
153
153
  asserted = "\x78\x9c"
154
154
  if RUBY_VERSION[0,1].to_i >= 2
155
155
  puts "I'm a Ruby > 2.0.0. Enforcing ASCII-8BIT. (#{RUBY_VERSION}/#{RUBY_VERSION[0,1].to_i})"
156
- # lol well yeah, Rubby.
156
+ # lol well yeah, Rubby.
157
157
  # http://stackoverflow.com/questions/15843684/binary-string-literals-in-ruby-2-0
158
158
  asserted = asserted.b
159
159
  end
@@ -200,6 +200,17 @@ class TestNotifier < Test::Unit::TestCase
200
200
  assert_equal datagrams.count, datagram[11].ord
201
201
  end
202
202
  end
203
+
204
+ should "throw an error if more than MAX_CHUNKS will be created" do
205
+ srand(1) # for stable tests
206
+ hash = { 'version' => '1.0', 'short_message' => 'message' }
207
+ hash.merge!('something' => (0..3000).map { RANDOM_DATA[rand(RANDOM_DATA.count)] }.join) # or it will be compressed too good
208
+ @notifier.max_chunk_size = 10
209
+ @notifier.instance_variable_set('@hash', hash)
210
+ assert_raise(ArgumentError) do
211
+ @notifier.__send__(:datagrams_from_hash)
212
+ end
213
+ end
203
214
  end
204
215
 
205
216
  context "level threshold" do
@@ -4,7 +4,7 @@ class TestRubyUdpSender < Test::Unit::TestCase
4
4
  context "with ruby sender" do
5
5
  setup do
6
6
  @addresses = [['localhost', 12201], ['localhost', 12202]]
7
- @sender = GELF::RubyUdpSender.new(@addresses)
7
+ @sender = GELF::Transport::UDP.new(@addresses)
8
8
  @datagrams1 = %w(d1 d2 d3)
9
9
  @datagrams2 = %w(e1 e2 e3)
10
10
  end
metadata CHANGED
@@ -1,16 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gelf
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexey Palazhchenko
8
8
  - Lennart Koopmann
9
9
  - Zac Sprackett
10
+ - Marcus Ilgner
10
11
  autorequire:
11
12
  bindir: bin
12
13
  cert_chain: []
13
- date: 2016-02-03 00:00:00.000000000 Z
14
+ date: 2016-08-21 00:00:00.000000000 Z
14
15
  dependencies:
15
16
  - !ruby/object:Gem::Dependency
16
17
  name: shoulda
@@ -32,42 +33,56 @@ dependencies:
32
33
  requirements:
33
34
  - - "~>"
34
35
  - !ruby/object:Gem::Version
35
- version: 1.8.7
36
+ version: 2.1.1
36
37
  type: :development
37
38
  prerelease: false
38
39
  version_requirements: !ruby/object:Gem::Requirement
39
40
  requirements:
40
41
  - - "~>"
41
42
  - !ruby/object:Gem::Version
42
- version: 1.8.7
43
+ version: 2.1.1
44
+ - !ruby/object:Gem::Dependency
45
+ name: rack
46
+ requirement: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - "<"
49
+ - !ruby/object:Gem::Version
50
+ version: '2.0'
51
+ type: :development
52
+ prerelease: false
53
+ version_requirements: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - "<"
56
+ - !ruby/object:Gem::Version
57
+ version: '2.0'
43
58
  - !ruby/object:Gem::Dependency
44
59
  name: mocha
45
60
  requirement: !ruby/object:Gem::Requirement
46
61
  requirements:
47
62
  - - "~>"
48
63
  - !ruby/object:Gem::Version
49
- version: 0.14.0
64
+ version: 1.1.0
50
65
  type: :development
51
66
  prerelease: false
52
67
  version_requirements: !ruby/object:Gem::Requirement
53
68
  requirements:
54
69
  - - "~>"
55
70
  - !ruby/object:Gem::Version
56
- version: 0.14.0
71
+ version: 1.1.0
57
72
  - !ruby/object:Gem::Dependency
58
73
  name: test-unit
59
74
  requirement: !ruby/object:Gem::Requirement
60
75
  requirements:
61
76
  - - "~>"
62
77
  - !ruby/object:Gem::Version
63
- version: 3.0.8
78
+ version: 3.2.0
64
79
  type: :development
65
80
  prerelease: false
66
81
  version_requirements: !ruby/object:Gem::Requirement
67
82
  requirements:
68
83
  - - "~>"
69
84
  - !ruby/object:Gem::Version
70
- version: 3.0.8
85
+ version: 3.2.0
71
86
  - !ruby/object:Gem::Dependency
72
87
  name: json
73
88
  requirement: !ruby/object:Gem::Requirement
@@ -84,11 +99,10 @@ dependencies:
84
99
  version: '0'
85
100
  description: Library to send GELF messages to Graylog logging server. Supports plain-text,
86
101
  GELF messages and exceptions via UDP and TCP.
87
- email: lennart@graylog.com
102
+ email: mail@marcusilgner.com
88
103
  executables: []
89
104
  extensions: []
90
105
  extra_rdoc_files:
91
- - CHANGELOG
92
106
  - LICENSE
93
107
  - README.md
94
108
  files:
@@ -107,8 +121,10 @@ files:
107
121
  - lib/gelf.rb
108
122
  - lib/gelf/logger.rb
109
123
  - lib/gelf/notifier.rb
110
- - lib/gelf/ruby_sender.rb
111
124
  - lib/gelf/severity.rb
125
+ - lib/gelf/transport/tcp.rb
126
+ - lib/gelf/transport/tcp_tls.rb
127
+ - lib/gelf/transport/udp.rb
112
128
  - test/helper.rb
113
129
  - test/test_logger.rb
114
130
  - test/test_notifier.rb
@@ -134,7 +150,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
134
150
  version: '0'
135
151
  requirements: []
136
152
  rubyforge_project:
137
- rubygems_version: 2.4.5.1
153
+ rubygems_version: 2.4.8
138
154
  signing_key:
139
155
  specification_version: 4
140
156
  summary: Library to send GELF messages to Graylog logging server.
@@ -1,147 +0,0 @@
1
- module GELF
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_datagrams(datagrams)
13
- host, port = @addresses[@i]
14
- @i = (@i + 1) % @addresses.length
15
- datagrams.each do |datagram|
16
- @socket.send(datagram, 0, host, port)
17
- end
18
- end
19
-
20
- def close
21
- @socket.close
22
- end
23
- end
24
-
25
- class RubyTcpSocket
26
- attr_accessor :socket
27
-
28
- def initialize(host, port)
29
- @host = host
30
- @port = port
31
- connect
32
- end
33
-
34
- def connected?
35
- if not @connected
36
- begin
37
- if @socket.nil?
38
- @socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
39
- end
40
- sockaddr = Socket.sockaddr_in(@port, @host)
41
- @socket.connect_nonblock(sockaddr)
42
- rescue Errno::EISCONN
43
- @connected = true
44
- rescue Errno::EINPROGRESS, Errno::EALREADY
45
- @connected = false
46
- rescue SystemCallError
47
- @socket = nil
48
- @connected = false
49
- end
50
- end
51
- return @connected
52
- end
53
-
54
- def connect
55
- @connected = false
56
- socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
57
- sockaddr = Socket.sockaddr_in(@port, @host)
58
- begin
59
- socket.connect_nonblock(sockaddr)
60
- rescue Errno::EISCONN
61
- @connected = true
62
- rescue SystemCallError
63
- return false
64
- end
65
- @socket = socket
66
- return true
67
- end
68
-
69
- def matches?(host, port)
70
- if @host == host and @port == port
71
- true
72
- else
73
- false
74
- end
75
- end
76
- end
77
-
78
- class RubyTcpSender
79
- attr_reader :addresses
80
-
81
- def initialize(addresses)
82
- @sockets = []
83
- addresses.each do |address|
84
- s = RubyTcpSocket.new(address[0], address[1])
85
- @sockets.push(s)
86
- end
87
- end
88
-
89
- def addresses=(addresses)
90
- addresses.each do |address|
91
- found = false
92
- # handle pre existing sockets
93
- @sockets.each do |socket|
94
- if socket.matches?(address[0], address[1])
95
- found = true
96
- break
97
- end
98
- end
99
- if not found
100
- s = RubyTcpSocket.new(address[0], address[1])
101
- @sockets.push(s)
102
- end
103
- end
104
- end
105
-
106
- def send(message)
107
- while true do
108
- sent = false
109
- sockets = @sockets.map { |s|
110
- if s.connected?
111
- s.socket
112
- end
113
- }
114
- sockets.compact!
115
- next unless not sockets.empty?
116
- begin
117
- result = select( nil, sockets, nil, 1)
118
- if result
119
- writers = result[1]
120
- sent = write_any(writers, message)
121
- end
122
- break if sent
123
- rescue SystemCallError, IOError
124
- end
125
- end
126
- end
127
-
128
- private
129
- def write_any(writers, message)
130
- writers.shuffle.each do |w|
131
- begin
132
- w.write(message)
133
- return true
134
- rescue Errno::EPIPE
135
- @sockets.each do |s|
136
- if s.socket == w
137
- s.socket.close
138
- s.socket = nil
139
- s.connect
140
- end
141
- end
142
- end
143
- end
144
- return false
145
- end
146
- end
147
- end