syslogstash 2.2.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/syslogstash.gemspec CHANGED
@@ -1,40 +1,41 @@
1
1
  begin
2
- require 'git-version-bump'
2
+ require 'git-version-bump'
3
3
  rescue LoadError
4
- nil
4
+ nil
5
5
  end
6
6
 
7
7
  Gem::Specification.new do |s|
8
- s.name = "syslogstash"
8
+ s.name = "syslogstash"
9
9
 
10
- s.version = GVB.version rescue "0.0.0.1.NOGVB"
11
- s.date = GVB.date rescue Time.now.strftime("%Y-%m-%d")
10
+ s.version = GVB.version rescue "0.0.0.1.NOGVB"
11
+ s.date = GVB.date rescue Time.now.strftime("%Y-%m-%d")
12
12
 
13
- s.platform = Gem::Platform::RUBY
13
+ s.platform = Gem::Platform::RUBY
14
14
 
15
- s.summary = "Send messages from syslog UNIX sockets to logstash"
15
+ s.summary = "Send messages from syslog UNIX sockets to logstash"
16
16
 
17
- s.authors = ["Matt Palmer"]
18
- s.email = ["matt.palmer@discourse.org"]
19
- s.homepage = "https://github.com/discourse/syslogstash"
17
+ s.authors = ["Matt Palmer"]
18
+ s.email = ["matt.palmer@discourse.org"]
19
+ s.homepage = "https://github.com/discourse/syslogstash"
20
20
 
21
- s.files = `git ls-files -z`.split("\0").reject { |f| f =~ /^(G|spec|Rakefile)/ }
22
- s.executables = ["syslogstash"]
21
+ s.files = `git ls-files -z`.split("\0").reject { |f| f =~ /^(G|spec|Rakefile)/ }
22
+ s.executables = ["syslogstash"]
23
23
 
24
- s.required_ruby_version = ">= 2.1.0"
24
+ s.required_ruby_version = ">= 2.4.0"
25
25
 
26
- s.add_runtime_dependency 'frankenstein'
27
- s.add_runtime_dependency 'rack'
26
+ s.add_runtime_dependency 'config_skeleton'
27
+ s.add_runtime_dependency 'frankenstein'
28
+ s.add_runtime_dependency 'logstash_writer'
29
+ s.add_runtime_dependency 'rack'
28
30
 
29
- s.add_development_dependency 'bundler'
30
- s.add_development_dependency 'github-release'
31
- s.add_development_dependency 'guard-spork'
32
- s.add_development_dependency 'guard-rspec'
33
- s.add_development_dependency 'rake', '~> 10.4', '>= 10.4.2'
34
- # Needed for guard
35
- s.add_development_dependency 'rb-inotify', '~> 0.9'
36
- s.add_development_dependency 'redcarpet'
37
- s.add_development_dependency 'rspec'
38
- s.add_development_dependency 'webmock'
39
- s.add_development_dependency 'yard'
31
+ s.add_development_dependency 'bundler'
32
+ s.add_development_dependency 'github-release'
33
+ s.add_development_dependency 'guard-rspec'
34
+ s.add_development_dependency 'rake', '~> 10.4', '>= 10.4.2'
35
+ # Needed for guard
36
+ s.add_development_dependency 'rb-inotify', '~> 0.9'
37
+ s.add_development_dependency 'redcarpet'
38
+ s.add_development_dependency 'rspec'
39
+ s.add_development_dependency 'webmock'
40
+ s.add_development_dependency 'yard'
40
41
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: syslogstash
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.0
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Palmer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-07-10 00:00:00.000000000 Z
11
+ date: 2019-03-18 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: config_skeleton
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: frankenstein
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -25,7 +39,7 @@ dependencies:
25
39
  - !ruby/object:Gem::Version
26
40
  version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
- name: rack
42
+ name: logstash_writer
29
43
  requirement: !ruby/object:Gem::Requirement
30
44
  requirements:
31
45
  - - ">="
@@ -39,13 +53,13 @@ dependencies:
39
53
  - !ruby/object:Gem::Version
40
54
  version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
- name: bundler
56
+ name: rack
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
59
  - - ">="
46
60
  - !ruby/object:Gem::Version
47
61
  version: '0'
48
- type: :development
62
+ type: :runtime
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
@@ -53,7 +67,7 @@ dependencies:
53
67
  - !ruby/object:Gem::Version
54
68
  version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
- name: github-release
70
+ name: bundler
57
71
  requirement: !ruby/object:Gem::Requirement
58
72
  requirements:
59
73
  - - ">="
@@ -67,7 +81,7 @@ dependencies:
67
81
  - !ruby/object:Gem::Version
68
82
  version: '0'
69
83
  - !ruby/object:Gem::Dependency
70
- name: guard-spork
84
+ name: github-release
71
85
  requirement: !ruby/object:Gem::Requirement
72
86
  requirements:
73
87
  - - ">="
@@ -192,16 +206,14 @@ executables:
192
206
  extensions: []
193
207
  extra_rdoc_files: []
194
208
  files:
209
+ - ".editorconfig"
195
210
  - ".gitignore"
211
+ - ".travis.yml"
196
212
  - Dockerfile
197
213
  - LICENCE
198
- - Makefile
199
214
  - README.md
200
215
  - bin/syslogstash
201
216
  - lib/syslogstash.rb
202
- - lib/syslogstash/config.rb
203
- - lib/syslogstash/logstash_writer.rb
204
- - lib/syslogstash/prometheus_exporter.rb
205
217
  - lib/syslogstash/syslog_reader.rb
206
218
  - syslogstash.gemspec
207
219
  homepage: https://github.com/discourse/syslogstash
@@ -215,15 +227,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
215
227
  requirements:
216
228
  - - ">="
217
229
  - !ruby/object:Gem::Version
218
- version: 2.1.0
230
+ version: 2.4.0
219
231
  required_rubygems_version: !ruby/object:Gem::Requirement
220
232
  requirements:
221
233
  - - ">="
222
234
  - !ruby/object:Gem::Version
223
235
  version: '0'
224
236
  requirements: []
225
- rubyforge_project:
226
- rubygems_version: 2.7.7
237
+ rubygems_version: 3.0.1
227
238
  signing_key:
228
239
  specification_version: 4
229
240
  summary: Send messages from syslog UNIX sockets to logstash
data/Makefile DELETED
@@ -1,14 +0,0 @@
1
- IMAGE := discourse/syslogstash
2
- TAG := $(shell date -u +%Y%m%d.%H%M%S)
3
-
4
- .PHONY: default
5
- default: push
6
- @printf "${IMAGE}:${TAG} ready\n"
7
-
8
- .PHONY: push
9
- push: build
10
- docker push ${IMAGE}:${TAG}
11
-
12
- .PHONY: build
13
- build:
14
- docker build --build-arg=http_proxy=${http_proxy} -t ${IMAGE}:${TAG} .
@@ -1,118 +0,0 @@
1
- require 'logger'
2
-
3
- class Syslogstash::Config
4
- class ConfigurationError < StandardError; end
5
-
6
- # Raised if any problems were found with the config
7
- class InvalidEnvironmentError < StandardError; end
8
-
9
- attr_reader :logstash_server,
10
- :syslog_socket,
11
- :backlog_size,
12
- :stats_server,
13
- :add_fields,
14
- :relay_sockets
15
-
16
- attr_reader :logger
17
-
18
- attr_accessor :relay_to_stdout
19
-
20
- # Create a new syslogstash config based on environment variables.
21
- #
22
- # Examines the environment passed in, and then creates a new config
23
- # object if all is well.
24
- #
25
- # @param env [Hash] the set of environment variables to use.
26
- #
27
- # @param logger [Logger] the logger to which all diagnostic and error
28
- # data will be sent.
29
- #
30
- # @raise [ConfigurationError] if any problems are detected with the
31
- # environment variables found.
32
- #
33
- def initialize(env, logger:)
34
- @logger = logger
35
-
36
- parse_env(env)
37
- end
38
-
39
- private
40
-
41
- def parse_env(env)
42
- @logger.info("config") { "Parsing environment:\n" + env.map { |k, v| "#{k}=#{v.inspect}" }.join("\n") }
43
-
44
- @logstash_server = pluck_string(env, "LOGSTASH_SERVER")
45
- @syslog_socket = pluck_string(env, "SYSLOG_SOCKET")
46
- @relay_to_stdout = pluck_boolean(env, "RELAY_TO_STDOUT", default: false)
47
- @stats_server = pluck_boolean(env, "STATS_SERVER", default: false)
48
- @backlog_size = pluck_integer(env, "BACKLOG_SIZE", valid_range: 0..(2**31 - 1), default: 1_000_000)
49
- @add_fields = pluck_prefix_list(env, "ADD_FIELD_")
50
- @relay_sockets = pluck_path_list(env, "RELAY_SOCKETS", default: [])
51
- end
52
-
53
- def pluck_string(env, key, default: nil)
54
- maybe_default(env, key, default) { env[key] }
55
- end
56
-
57
- def pluck_boolean(env, key, default: nil)
58
- maybe_default(env, key, default) do
59
- case env[key]
60
- when /\A(no|off|0|false)\z/
61
- false
62
- when /\A(yes|on|1|true)\z/
63
- true
64
- else
65
- raise ConfigurationError,
66
- "Value for #{key} (#{env[key].inspect}) is not a valid boolean"
67
- end
68
- end
69
- end
70
-
71
- def pluck_integer(env, key, valid_range: nil, default: nil)
72
- maybe_default(env, key, default) do
73
- if env[key] !~ /\A\d+\z/
74
- raise InvalidEnvironmentError,
75
- "Value for #{key} (#{env[key].inspect}) is not an integer"
76
- end
77
-
78
- env[key].to_i.tap do |v|
79
- unless valid_range.nil? || !valid_range.include?(v)
80
- raise InvalidEnvironmentError,
81
- "Value for #{key} (#{env[key]}) out of range (must be between #{valid_range.first} and #{valid_range.last} inclusive)"
82
- end
83
- end
84
- end
85
- end
86
-
87
- def pluck_prefix_list(env, prefix)
88
- {}.tap do |list|
89
- env.each do |k, v|
90
- next unless k.start_with? prefix
91
- key = k.sub(prefix, '')
92
- list[key] = v
93
- end
94
-
95
- @logger.debug("config") { "Prefix list for #{prefix.inspect} is #{list.inspect}" }
96
- end
97
- end
98
-
99
- def pluck_path_list(env, key, default: nil)
100
- maybe_default(env, key, default) do
101
- env[key].split(":")
102
- end
103
- end
104
-
105
- def maybe_default(env, key, default)
106
- if env[key].nil? || env[key].empty?
107
- if default.nil?
108
- raise ConfigurationError,
109
- "Required environment variable #{key} not specified"
110
- else
111
- @logger.debug("config") { "Using default value #{default.inspect} for config parameter #{key}" }
112
- default
113
- end
114
- else
115
- yield.tap { |v| @logger.debug("config") { "Using plucked value #{v.inspect} for config parameter #{key}" } }
116
- end
117
- end
118
- end
@@ -1,202 +0,0 @@
1
- require 'resolv'
2
- require 'ipaddr'
3
-
4
- # Write messages to a logstash server.
5
- #
6
- class Syslogstash::LogstashWriter
7
- Target = Struct.new(:hostname, :port)
8
-
9
- attr_reader :thread
10
-
11
- # Create a new logstash writer.
12
- #
13
- # Once the object is created, you're ready to give it messages by
14
- # calling #send_entry. No messages will actually be *delivered* to
15
- # logstash, though, until you call #run.
16
- #
17
- def initialize(cfg, stats)
18
- @server_name, @logger, @backlog, @stats = cfg.logstash_server, cfg.logger, cfg.backlog_size, stats
19
-
20
- @entries = []
21
- @entries_mutex = Mutex.new
22
- @cs_mutex = Mutex.new
23
- end
24
-
25
- # Add an entry to the list of messages to be sent to logstash. Actual
26
- # message delivery will happen in a worker thread that is started with
27
- # #run.
28
- #
29
- def send_entry(e)
30
- @entries_mutex.synchronize do
31
- @entries << { content: e, arrival_timestamp: Time.now }
32
- while @entries.length > @backlog
33
- @entries.shift
34
- @stats.dropped
35
- end
36
- end
37
-
38
- @thread.run if @thread
39
- end
40
-
41
- # Start sending messages to logstash servers. This method will return
42
- # almost immediately, and actual message sending will occur in a
43
- # separate thread.
44
- #
45
- def run
46
- @thread = Thread.new { send_messages }
47
- end
48
-
49
- # Cause the writer to disconnect from the currently-active server.
50
- #
51
- def force_disconnect!
52
- @cs_mutex.synchronize do
53
- @logger.info("writer") { "Forced disconnect from #{server_id(@current_server) }" }
54
- @current_server.close if @current_server
55
- @current_server = nil
56
- end
57
- end
58
-
59
- private
60
-
61
- def send_messages
62
- loop do
63
- if @entries_mutex.synchronize { @entries.empty? }
64
- sleep 1
65
- else
66
- begin
67
- entry = @entries_mutex.synchronize { @entries.shift }
68
-
69
- current_server do |s|
70
- s.puts entry[:content]
71
- @stats.sent(server_id(s), entry[:arrival_timestamp])
72
- end
73
-
74
- # If we got here, we sent successfully, so we don't want
75
- # to put the entry back on the queue in the ensure block
76
- entry = nil
77
- rescue StandardError => ex
78
- @logger.error("writer") { (["Unhandled exception while writing entry: #{ex.message} (#{ex.class})"] + ex.backtrace).join("\n ") }
79
- ensure
80
- @entries_mutex.synchronize { @entries.unshift if entry }
81
- end
82
- end
83
- end
84
- end
85
-
86
- # *Yield* a TCPSocket connected to the server we currently believe to
87
- # be accepting log entries, so that something can send log entries to
88
- # it.
89
- #
90
- # The yielding is very deliberate: it allows us to centralise all
91
- # error detection and handling within this one method, and retry
92
- # sending just by calling `yield` again when we've connected to
93
- # another server.
94
- #
95
- def current_server
96
- # I could handle this more cleanly with recursion, but I don't want
97
- # to fill the stack if we have to retry a lot of times
98
- done = false
99
-
100
- until done
101
- @cs_mutex.synchronize do
102
- if @current_server
103
- begin
104
- @logger.debug("writer") { "Using current server #{server_id(@current_server)}" }
105
- yield @current_server
106
- done = true
107
- rescue SystemCallError => ex
108
- # Something went wrong during the send; disconnect from this
109
- # server and recycle
110
- @logger.debug("writer") { "Error while writing to current server: #{ex.message} (#{ex.class})" }
111
- @current_server.close
112
- @current_server = nil
113
- sleep 0.1
114
- end
115
- else
116
- candidates = resolve_server_name
117
- @logger.debug("writer") { "Server candidates: #{candidates.inspect}" }
118
-
119
- begin
120
- next_server = candidates.shift
121
-
122
- if next_server
123
- @logger.debug("writer") { "Trying to connect to #{next_server.to_s}" }
124
- @current_server = TCPSocket.new(next_server.hostname, next_server.port)
125
- else
126
- @logger.debug("writer") { "Could not connect to any server; pausing before trying again" }
127
- @current_server = nil
128
- sleep 5
129
- end
130
- rescue SystemCallError => ex
131
- # Connection failed for any number of reasons; try the next one in the list
132
- @logger.warn("writer") { "Failed to connect to #{next_server.to_s}: #{ex.message} (#{ex.class})" }
133
- sleep 0.1
134
- retry
135
- end
136
- end
137
- end
138
- end
139
- end
140
-
141
- def server_id(s)
142
- pa = s.peeraddr
143
- if pa[0] == "AF_INET6"
144
- "[#{pa[3]}]:#{pa[1]}"
145
- else
146
- "#{pa[3]}:#{pa[1]}"
147
- end
148
- end
149
-
150
- def resolve_server_name
151
- return [static_target] if static_target
152
-
153
- # The IPv6 literal case should have been taken care of by
154
- # static_target, so the only two cases we have to deal with
155
- # here are specified-port (assume A/AAAA) or no port (assume SRV).
156
- if @server_name =~ /:/
157
- host, port = @server_name.split(":", 2)
158
- addrs = Resolv::DNS.new.getaddresses(host)
159
- if addrs.empty?
160
- @logger.warn("writer") { "No addresses resolved for server_name #{host.inspect}" }
161
- end
162
- addrs.sort_by { rand }.map { |a| Target.new(a.to_s, port.to_i) }
163
- else
164
- # SRV records ftw
165
- [].tap do |list|
166
- left = Resolv::DNS.new.getresources(@server_name, Resolv::DNS::Resource::IN::SRV)
167
- if left.empty?
168
- @logger.warn("writer") { "No SRV records found for server_name #{@server_name.inspect}" }
169
- end
170
- until left.empty?
171
- prio = left.map { |rr| rr.priority }.uniq.min
172
- candidates = left.select { |rr| rr.priority == prio }
173
- left -= candidates
174
- candidates.sort_by! { |rr| [rr.weight, rr.target.to_s] }
175
- until candidates.empty?
176
- selector = rand(candidates.inject(1) { |n, rr| n + rr.weight })
177
- chosen = candidates.inject(0) do |n, rr|
178
- break rr if n + rr.weight >= selector
179
- n + rr.weight
180
- end
181
- candidates.delete(chosen)
182
- list << Target.new(chosen.target.to_s, chosen.port)
183
- end
184
- end
185
- end
186
- end
187
- end
188
-
189
- def static_target
190
- @static_target ||= begin
191
- if @server_name =~ /\A(.*):(\d+)\z/
192
- begin
193
- Target.new(IPAddr.new($1).to_s, $2.to_i)
194
- rescue ArgumentError
195
- # Whatever is on the LHS isn't a recognisable address;
196
- # assume hostname and continue
197
- nil
198
- end
199
- end
200
- end
201
- end
202
- end