remote_syslog_sender 1.1.0

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9badc24ba1d4b6b484a7e5395c0ae68eb162e6c1
4
+ data.tar.gz: 68952f70023a41578e0778aebfcc687b6ac38e3d
5
+ SHA512:
6
+ metadata.gz: 0704cd78a41574090d1cd3a8304c8f8321acefa6eedd2874f8639926eae8459dd94bebb2d3e16305b9547faf8a667c685ad8f156345eecf24e7aa585a64e0e13
7
+ data.tar.gz: fe35a63e7c0979457eb57955afdb1248f656146f87a728776999e3eacddd2d828aaefe00b5ffb4420725015b0c635a83fde82144cb003e03dfee5ffd2231ca6b
@@ -0,0 +1,3 @@
1
+ Gemfile.lock
2
+ .bundle
3
+ pkg
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2017 joker1007
4
+ Original Copyright (c) 2011 Eric Lindvall
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in
14
+ all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ THE SOFTWARE.
@@ -0,0 +1,102 @@
1
+ # Remote Syslog Sender
2
+
3
+ This gem is syslog sender that is extracted from (papertrail/remote_syslog_logger)[https://github.com/papertrail/remote_syslog_logger]
4
+
5
+ This can send message to remote syslog server via UDP, TCP, TCP+TLS.
6
+ (Original does not support TCP, TCP+TLS protocol).
7
+
8
+ ## Installation
9
+
10
+ The easiest way to install `remote_syslog_sender` is with Bundler. Add
11
+ `remote_syslog_sender` to your `Gemfile`.
12
+
13
+ If you are not using a `Gemfile`, run:
14
+
15
+ $ [sudo] gem install remote_syslog_sender
16
+
17
+
18
+ ## Usage
19
+
20
+ ```ruby
21
+ sender = RemoteSyslogSender.new('syslog.domain.com', 514) # default protocol is UDP
22
+ sender.transmit("message body")
23
+ # or
24
+ sender.write("message body")
25
+
26
+
27
+ ## TCP
28
+ sender = RemoteSyslogSender.new('syslog.domain.com', 514, protocol: :tcp)
29
+ sender.transmit("message body")
30
+
31
+ ## TCP+TLS
32
+ sender = RemoteSyslogSender.new('syslog.domain.com', 514, protocol: :tcp, tls: true, ca_file: "custom_ca.pem")
33
+ sender.transmit("message body")
34
+ ```
35
+
36
+ To point the logs to your local system, use `localhost` and ensure that
37
+ the system's syslog daemon is bound to `127.0.0.1`.
38
+
39
+
40
+ ## Limitations
41
+
42
+ If the specified host cannot be resolved, `syslog.domain.com` in the
43
+ example under the usage section above, `remote_syslog_sender` will block
44
+ for approximately 20 seconds before displaying an error. This could
45
+ result in the application failing to start or even stopping responding.
46
+
47
+ Workarounds for this include:
48
+
49
+ * use an IP address instead of a hostname.
50
+ * put a hosts entry in `/etc/hosts` or equivalent, so that DNS is not
51
+ actually consulted
52
+ * instead of logging directly to the network, write to a file and
53
+ transmit new entries with a standalone daemon like
54
+ [remote_syslog](https://github.com/papertrail/remote_syslog),
55
+
56
+ ## Message length
57
+
58
+ All log lines are truncated to a maximum of 1024 characters. This restriction
59
+ comes from [RFC 3164 section 4.1][rfc-limit]:
60
+
61
+ > The total length of the packet MUST be 1024 bytes or less.
62
+
63
+ Additionally, the generally-accepted [MTU][] of the Internet is 1500 bytes, so
64
+ regardless of the RFC, UDP syslog packets longer than 1500 bytes would not
65
+ arrive. For details or to use TCP syslog for longer messages, see
66
+ [help.papertrailapp.com][troubleshoot].
67
+
68
+ [rfc-limit]: https://tools.ietf.org/html/rfc3164#section-4.1
69
+ [MTU]: (https://en.wikipedia.org/wiki/Maximum_transmission_unit)
70
+ [troubleshoot]: http://help.papertrailapp.com/kb/configuration/troubleshooting-remote-syslog-reachability/#message-length
71
+
72
+
73
+ ## Default program name
74
+
75
+ By default, the `program` value is set to the name and ID of the invoking
76
+ process. For example, `puma[12345]` or `rack[3456]`.
77
+
78
+ The `program` value is used to populate the syslog "tag" field, must be 32
79
+ or fewer characters. In a few cases, an artifact of how the app is launched
80
+ may lead to a default `program` value longer than 32 characters. For example,
81
+ the `thin` Web server may generate a default `program` value such
82
+ as:
83
+
84
+ thin server (0.0.0.0:3001)[11179]
85
+
86
+ If this occurs, the following exception will be raised when a
87
+ `RemoteSyslogSender` is instantiated:
88
+
89
+ Tag must not be longer than 32 characters (ArgumentError)
90
+
91
+ To remedy this, explicitly provide a `program` argument which is shorter than
92
+ 32 characters. See [Usage](#usage).
93
+
94
+
95
+ ## Contributing
96
+
97
+ Bug reports and pull requests are welcome on GitHub at https://github.com/reproio/remote_syslog_sender.
98
+
99
+
100
+ ## License
101
+
102
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rake/testtask'
4
+ Rake::TestTask.new(:test) do |test|
5
+ test.libs << 'lib' << 'test'
6
+ test.pattern = 'test/**/test_*.rb'
7
+ test.verbose = true
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,16 @@
1
+
2
+ require 'remote_syslog_sender/udp_sender'
3
+ require 'remote_syslog_sender/tcp_sender'
4
+
5
+ module RemoteSyslogSender
6
+ VERSION = '1.0.3'
7
+
8
+ def self.new(remote_hostname, remote_port, options = {})
9
+ protocol = options.delete(:protocol)
10
+ if protocol && protocol.to_sym == :tcp
11
+ TcpSender.new(remote_hostname, remote_port, options)
12
+ else
13
+ UdpSender.new(remote_hostname, remote_port, options)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,60 @@
1
+ require 'socket'
2
+ require 'syslog_protocol'
3
+
4
+ module RemoteSyslogSender
5
+ class Sender
6
+ # To suppress initialize warning
7
+ class Packet < SyslogProtocol::Packet
8
+ def initialize(*)
9
+ super
10
+ @time = nil
11
+ end
12
+ end
13
+
14
+ def initialize(remote_hostname, remote_port, options = {})
15
+ @remote_hostname = remote_hostname
16
+ @remote_port = remote_port
17
+ @whinyerrors = options[:whinyerrors]
18
+ @packet_size = options[:packet_size] || 1024
19
+
20
+ @packet = Packet.new
21
+
22
+ local_hostname = options[:local_hostname] || (Socket.gethostname rescue `hostname`.chomp)
23
+ local_hostname = 'localhost' if local_hostname.nil? || local_hostname.empty?
24
+ @packet.hostname = local_hostname
25
+
26
+ @packet.facility = options[:facility] || 'user'
27
+ @packet.severity = options[:severity] || 'notice'
28
+ @packet.tag = options[:program] || "#{File.basename($0)}[#{$$}]"
29
+
30
+ @socket = nil
31
+ end
32
+
33
+ def transmit(message)
34
+ message.split(/\r?\n/).each do |line|
35
+ begin
36
+ next if line =~ /^\s*$/
37
+ packet = @packet.dup
38
+ packet.content = line
39
+ send_msg(packet.assemble(@packet_size))
40
+ rescue
41
+ $stderr.puts "#{self.class} error: #{$!.class}: #{$!}\nOriginal message: #{line}"
42
+ raise if @whinyerrors
43
+ end
44
+ end
45
+ end
46
+
47
+ # Make this act a little bit like an `IO` object
48
+ alias_method :write, :transmit
49
+
50
+ def close
51
+ @socket.close
52
+ end
53
+
54
+ private
55
+
56
+ def send_msg(payload)
57
+ raise NotImplementedError, "please override"
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,122 @@
1
+ require 'socket'
2
+ require 'syslog_protocol'
3
+ require 'remote_syslog_sender/sender'
4
+
5
+ module RemoteSyslogSender
6
+ class TcpSender < Sender
7
+ class NonBlockingTimeout < StandardError; end
8
+
9
+ def initialize(remote_hostname, remote_port, options = {})
10
+ super
11
+ @tls = options[:tls]
12
+ @retry_limit = options[:retry_limit] || 3
13
+ @retry_interval = options[:retry_interval] || 0.5
14
+ @remote_hostname = remote_hostname
15
+ @remote_port = remote_port
16
+ @ssl_method = options[:ssl_method] || 'TLSv1_2'
17
+ @ca_file = options[:ca_file]
18
+ @verify_mode = options[:verify_mode]
19
+ @timeout = options[:timeout]
20
+ @timeout_exception = !!options[:timeout_exception]
21
+ @exponential_backoff = !!options[:exponential_backoff]
22
+
23
+ if [:SOL_SOCKET, :SO_KEEPALIVE, :IPPROTO_TCP, :TCP_KEEPIDLE].all? {|c| Socket.const_defined? c}
24
+ @keep_alive = options[:keep_alive]
25
+ end
26
+ if Socket.const_defined?(:TCP_KEEPIDLE)
27
+ @keep_alive_idle = options[:keep_alive_idle]
28
+ end
29
+ if Socket.const_defined?(:TCP_KEEPCNT)
30
+ @keep_alive_cnt = options[:keep_alive_cnt]
31
+ end
32
+ if Socket.const_defined?(:TCP_KEEPINTVL)
33
+ @keep_alive_intvl = options[:keep_alive_intvl]
34
+ end
35
+ connect
36
+ end
37
+
38
+ private
39
+
40
+ def connect
41
+ sock = TCPSocket.new(@remote_hostname, @remote_port)
42
+ if @keep_alive
43
+ sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true)
44
+ sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_KEEPIDLE, @keep_alive_idle) if @keep_alive_idle
45
+ sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_KEEPCNT, @keep_alive_cnt) if @keep_alive_cnt
46
+ sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_KEEPINTVL, @keep_alive_intvl) if @keep_alive_intvl
47
+ end
48
+ if @tls
49
+ require 'openssl'
50
+ context = OpenSSL::SSL::SSLContext.new(@ssl_method)
51
+ context.ca_file = @ca_file if @ca_file
52
+ context.verify_mode = @verify_mode if @verify_mode
53
+ @socket = OpenSSL::SSL::SSLSocket.new(sock, context)
54
+ @socket.connect
55
+ @socket.post_connection_check(@remote_hostname)
56
+ raise "verification error" if @socket.verify_result != OpenSSL::X509::V_OK
57
+ else
58
+ @socket = sock
59
+ end
60
+ end
61
+
62
+ def send_msg(payload)
63
+ if @timeout && @timeout >= 0
64
+ method = :write_nonblock
65
+ else
66
+ method = :write
67
+ end
68
+
69
+ retry_limit = @retry_limit.to_i
70
+ retry_interval = @retry_interval.to_f
71
+ retry_count = 0
72
+
73
+ payload << "\n"
74
+ payload.force_encoding(Encoding::ASCII_8BIT)
75
+ payload_size = payload.bytesize
76
+
77
+ until payload_size <= 0
78
+ start = get_time
79
+ begin
80
+ result = @socket.__send__(method, payload)
81
+ payload_size -= result
82
+ payload.slice!(0, result) if payload_size > 0
83
+ rescue IO::WaitWritable
84
+ timeout_wait = @timeout - (get_time - start)
85
+ retry if IO.select(nil, [@socket], nil, timeout_wait)
86
+
87
+ raise NonBlockingTimeout if @timeout_exception
88
+ break
89
+ rescue
90
+ if retry_count < retry_limit
91
+ sleep retry_interval
92
+ retry_count += 1
93
+ retry_interval *= 2 if @exponential_backoff
94
+ connect
95
+ retry
96
+ else
97
+ raise
98
+ end
99
+ end
100
+ end
101
+ end
102
+
103
+ POSIX_CLOCK =
104
+ if defined?(Process::CLOCK_MONOTONIC_COARSE)
105
+ Process::CLOCK_MONOTONIC_COARSE
106
+ elsif defined?(Process::CLOCK_MONOTONIC)
107
+ Process::CLOCK_MONOTONIC_COARSE
108
+ elsif defined?(Process::CLOCK_REALTIME_COARSE)
109
+ Process::CLOCK_REALTIME_COARSE
110
+ elsif defined?(Process::CLOCK_REALTIME)
111
+ Process::CLOCK_REALTIME
112
+ end
113
+
114
+ def get_time
115
+ if POSIX_CLOCK
116
+ Process.clock_gettime(POSIX_CLOCK)
117
+ else
118
+ Time.now.to_f
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,18 @@
1
+ require 'socket'
2
+ require 'syslog_protocol'
3
+ require 'remote_syslog_sender/sender'
4
+
5
+ module RemoteSyslogSender
6
+ class UdpSender < Sender
7
+ def initialize(remote_hostname, remote_port, options = {})
8
+ super
9
+ @socket = UDPSocket.new
10
+ end
11
+
12
+ private
13
+
14
+ def send_msg(payload)
15
+ @socket.send(payload, 0, @remote_hostname, @remote_port)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,21 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'remote_syslog_sender'
3
+ s.version = '1.1.0'
4
+ s.summary = "Message sender that sends directly to a remote syslog endpoint"
5
+ s.description = "Message sender that sends directly to a remote syslog endpoint (Support UDP, TCP, TCP+TLS)"
6
+
7
+ s.authors = ["Tomohiro Hashidate", "Eric Lindvall"]
8
+ s.email = 'kakyoin.hierophant@gmail.com'
9
+ s.homepage = 'https://github.com/reproio/remote_syslog_logger'
10
+
11
+ s.files = `git ls-files -z`.split("\x0")
12
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
13
+ s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
14
+ s.require_paths = %w[lib]
15
+
16
+ s.add_runtime_dependency 'syslog_protocol'
17
+
18
+ s.add_development_dependency "bundler", "~> 1.6"
19
+ s.add_development_dependency "rake"
20
+ s.add_development_dependency "test-unit"
21
+ end
@@ -0,0 +1,12 @@
1
+ $:.unshift File.expand_path('../../lib', __FILE__)
2
+
3
+ unless ENV['BUNDLE_GEMFILE']
4
+ require 'rubygems'
5
+ require 'bundler'
6
+ Bundler.setup
7
+ Bundler.require
8
+ end
9
+
10
+ require 'remote_syslog_sender'
11
+
12
+ require 'test/unit'
@@ -0,0 +1,66 @@
1
+ require File.expand_path('../helper', __FILE__)
2
+
3
+ class TestRemoteSyslogSender < Test::Unit::TestCase
4
+ def setup
5
+ @server_port = rand(50000) + 1024
6
+ @socket = UDPSocket.new
7
+ @socket.bind('127.0.0.1', @server_port)
8
+
9
+ @tcp_server = TCPServer.open('127.0.0.1', 0)
10
+ @tcp_server_port = @tcp_server.addr[1]
11
+
12
+ @tcp_server_wait_thread = Thread.start do
13
+ @tcp_server.accept
14
+ end
15
+ end
16
+
17
+ def teardown
18
+ @socket.close
19
+ @tcp_server.close
20
+ end
21
+
22
+ def test_sender
23
+ @sender = RemoteSyslogSender.new('127.0.0.1', @server_port)
24
+ @sender.write "This is a test"
25
+
26
+ message, _ = *@socket.recvfrom(1024)
27
+ assert_match(/This is a test/, message)
28
+ end
29
+
30
+ def test_sender_long_payload
31
+ @sender = RemoteSyslogSender.new('127.0.0.1', @server_port, packet_size: 10240)
32
+ @sender.write "abcdefgh" * 1000
33
+
34
+ message, _ = *@socket.recvfrom(10240)
35
+ assert_match(/#{"abcdefgh" * 1000}/, message)
36
+ end
37
+
38
+ def test_sender_tcp
39
+ @sender = RemoteSyslogSender.new('127.0.0.1', @tcp_server_port, protocol: :tcp)
40
+ @sender.write "This is a test"
41
+ sock = @tcp_server_wait_thread.value
42
+
43
+ message, _ = *sock.recvfrom(1024)
44
+ assert_match(/This is a test/, message)
45
+ end
46
+
47
+ def test_sender_tcp_nonblock
48
+ @sender = RemoteSyslogSender.new('127.0.0.1', @tcp_server_port, protocol: :tcp, timeout: 20)
49
+ @sender.write "This is a test"
50
+ sock = @tcp_server_wait_thread.value
51
+
52
+ message, _ = *sock.recvfrom(1024)
53
+ assert_match(/This is a test/, message)
54
+ end
55
+
56
+ def test_sender_multiline
57
+ @sender = RemoteSyslogSender.new('127.0.0.1', @server_port)
58
+ @sender.write "This is a test\nThis is the second line"
59
+
60
+ message, _ = *@socket.recvfrom(1024)
61
+ assert_match(/This is a test/, message)
62
+
63
+ message, _ = *@socket.recvfrom(1024)
64
+ assert_match(/This is the second line/, message)
65
+ end
66
+ end
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: remote_syslog_sender
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Tomohiro Hashidate
8
+ - Eric Lindvall
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2017-10-03 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: syslog_protocol
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: bundler
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '1.6'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '1.6'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rake
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: test-unit
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ description: Message sender that sends directly to a remote syslog endpoint (Support
71
+ UDP, TCP, TCP+TLS)
72
+ email: kakyoin.hierophant@gmail.com
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - Gemfile
79
+ - LICENSE
80
+ - README.md
81
+ - Rakefile
82
+ - lib/remote_syslog_sender.rb
83
+ - lib/remote_syslog_sender/sender.rb
84
+ - lib/remote_syslog_sender/tcp_sender.rb
85
+ - lib/remote_syslog_sender/udp_sender.rb
86
+ - remote_syslog_sender.gemspec
87
+ - test/helper.rb
88
+ - test/test_remote_syslog_logger.rb
89
+ homepage: https://github.com/reproio/remote_syslog_logger
90
+ licenses: []
91
+ metadata: {}
92
+ post_install_message:
93
+ rdoc_options: []
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ requirements: []
107
+ rubyforge_project:
108
+ rubygems_version: 2.6.13
109
+ signing_key:
110
+ specification_version: 4
111
+ summary: Message sender that sends directly to a remote syslog endpoint
112
+ test_files:
113
+ - test/helper.rb
114
+ - test/test_remote_syslog_logger.rb