remote_syslog_sender 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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