r7insight 2.7.6 → 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: 99fdb5e8d382903830d4fe7a900265680ffe2043
4
- data.tar.gz: 23aa014dea96d8c561bcea19df816e1b8aacc688
3
+ metadata.gz: 1a2c177685e8729322134332c2600df2f39128bc
4
+ data.tar.gz: 60f673c54746ce17be74ed6519b60940646d11d0
5
5
  SHA512:
6
- metadata.gz: dde6bee24ffa5e36aad3c0ef2116fa03b9750bf4de2e116243cf2b2fbcd684eff878d1fa7df95c2560f81c5044ed39159c45a3e17772435e27b23be43d387775
7
- data.tar.gz: 7965e50f8d28da7ff37a55d9bafa2e0e4f8338043ce4e05a2fc196b7ae0640242c6b617301097e00b08f00a3de829b7ae213dad9fbd4f248bfcea4622bfefa4d
6
+ metadata.gz: bc374752238269cf845c8d624389c9e37b37323bb6ceab803afe3ade0246c5194333aeec9b68da18e5d57b3c25dc0a93db7714c438a0579aed00fe8581c78838
7
+ data.tar.gz: 13b808f2ada9e6e8385cb9331febba3aa496956261a9854833c7d4dbbf2ad16a890fab590ff2e225feef23d692bb1b60ed70b0a261578f4d38cd96c5e70444b3
data/.gitignore CHANGED
@@ -50,3 +50,10 @@ build-iPhoneSimulator/
50
50
 
51
51
  # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
52
52
  .rvmrc
53
+
54
+ # IDEs
55
+ .vscode
56
+ .idea
57
+
58
+ # ruby plugins
59
+ .rakeTasks
data/.travis.yml ADDED
@@ -0,0 +1,13 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - 2.6.5
5
+ - 2.5
6
+
7
+ before_install:
8
+ - gem update --system
9
+ - gem install bundler
10
+
11
+ script:
12
+ - make test
13
+
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,31 @@
1
+ # Contributing to r7insight_ruby
2
+
3
+ :+1::tada: Thanks for taking the time to contribute! :tada::+1:
4
+
5
+ ## Requirements
6
+
7
+ In order to work on this repository you will require:
8
+ - ruby version `2.6.5`
9
+ - bundle version `2.0.2`
10
+ - gem version `3.0.6`
11
+ - rvm version `1.29.9`
12
+
13
+ ## Workflow
14
+
15
+ - Fork repository in GitHub
16
+ - Clone your repository fork
17
+ - Implement functionality
18
+ - `make test` for testing
19
+ - Add extra tests and documentation if required
20
+ - Push into your fork and create a pull request into the main Rapid7 repository
21
+ - Once pull request is approved `make bump-(major|minor|patch)` for bumping versions (use [SemVer](https://semver.org/))
22
+ - Push again into the branch
23
+ - Pull request should get approved and merged
24
+
25
+ ### Deployment information for Rapid7 developers
26
+ - Pull down the merged master which includes the Pull request changes
27
+ - `make build`
28
+ - `gem push r7insight<VERSION>.gem`
29
+ - Create a new release in GitHub with the correct version tag
30
+
31
+ JetBrains RubyMine was used to develop this gem.
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
+ ruby '>= 2.5.0'
4
+
3
5
  gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,36 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ r7insight (3.0.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ activesupport (6.0.0)
10
+ concurrent-ruby (~> 1.0, >= 1.0.2)
11
+ i18n (>= 0.7, < 2)
12
+ minitest (~> 5.1)
13
+ tzinfo (~> 1.1)
14
+ zeitwerk (~> 2.1, >= 2.1.8)
15
+ concurrent-ruby (1.1.5)
16
+ i18n (1.7.0)
17
+ concurrent-ruby (~> 1.0)
18
+ minitest (5.12.2)
19
+ rake (13.0.0)
20
+ thread_safe (0.3.6)
21
+ tzinfo (1.2.5)
22
+ thread_safe (~> 0.1)
23
+ zeitwerk (2.2.0)
24
+
25
+ PLATFORMS
26
+ ruby
27
+
28
+ DEPENDENCIES
29
+ activesupport (~> 6.0.0)
30
+ bundler (~> 2.0.0)
31
+ minitest (~> 5.12.2)
32
+ r7insight!
33
+ rake (~> 13.0.0)
34
+
35
+ BUNDLED WITH
36
+ 2.0.2
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License
2
2
 
3
- Copyright (c) 2014 Logentries
3
+ Copyright (c) 2019 Rapid7
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
18
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- THE SOFTWARE.
21
+ THE SOFTWARE.
data/Makefile ADDED
@@ -0,0 +1,34 @@
1
+ .PHONY: test build bump-major bump-minor bump-patch help
2
+ .DEFAULT_GOAL := help
3
+
4
+ test: ## Run the unit tests
5
+ bundle exec rake --trace
6
+
7
+ build: ## Build the ruby gem
8
+ gem build r7insight.gemspec
9
+
10
+ bump-major: ## Bump the major version (1.0.0 -> 2.0.0)
11
+ @which bump || (echo "You do not have 'bump' installed or in your PATH.\n" \
12
+ "Please run 'gem install bump'\n" "GitHub: https://github.com/gregorym/bump" && false)
13
+ @bump major
14
+
15
+ bump-minor: ## Bump the minor version (0.1.0 -> 0.2.0)
16
+ @which bump || (echo "You do not have 'bump' installed or in your PATH.\n" \
17
+ "Please run 'gem install bump'\n" "GitHub: https://github.com/gregorym/bump" && false)
18
+ @bump minor
19
+
20
+ bump-patch: ## Bump the patch version (0.0.1 -> 0.0.2)
21
+ @which bump || (echo "You do not have 'bump' installed or in your PATH.\n" \
22
+ "Please run 'gem install bump'\n" "GitHub: https://github.com/gregorym/bump" && false)
23
+ @bump patch
24
+
25
+ help: ## Shows help
26
+ @IFS=$$'\n' ; \
27
+ help_lines=(`fgrep -h "##" ${MAKEFILE_LIST} | fgrep -v fgrep | sed -e 's/\\$$//'`); \
28
+ for help_line in $${help_lines[@]}; do \
29
+ IFS=$$'#' ; \
30
+ help_split=($$help_line) ; \
31
+ help_command=`echo $${help_split[0]} | sed -e 's/^ *//' -e 's/ *$$//'` ; \
32
+ help_info=`echo $${help_split[2]} | sed -e 's/^ *//' -e 's/ *$$//'` ; \
33
+ printf "%-30s %s\n" $$help_command $$help_info ; \
34
+ done
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
- Logging to InsightOps in Ruby
2
- =============================
1
+ Logging to Rapid7 Insight Platform in Ruby
2
+ ==========================================
3
3
 
4
4
  [![Build Status](https://travis-ci.org/rapid7/r7insight_ruby.svg?branch=master)](https://travis-ci.org/rapid7/r7insight_ruby)
5
- This is a Rapid7 library for logging from Ruby platforms to InsightOps, including Heroku.
5
+ This is a library for logging from Ruby platforms to the Rapid7 Insight Platform, including Heroku.
6
6
 
7
7
  It is available on github <https://github.com/rapid7/r7insight_ruby/> and rubygems
8
8
  <http://rubygems.org/>.
@@ -15,13 +15,12 @@ Example
15
15
  Rails.logger.warn("warning message")
16
16
  Rails.logger.debug("debug message")
17
17
 
18
-
19
18
  Howto
20
19
  -----
21
20
 
22
- You must first register your account details with Rapid7 InsightOps.
21
+ You must first register your account on the Rapid7 Insight Platform.
23
22
 
24
- Once you have logged in to InsightOps, create a new host with a name of your choice.
23
+ Once you have logged into the platform, create a new host with a name of your choice.
25
24
  Inside this host, create a new logfile, selecting `Token TCP` (or `Plain TCP/UDP` if using UDP)
26
25
  as the source type.
27
26
 
@@ -40,43 +39,51 @@ Then from the cmd line run the following command:
40
39
 
41
40
  This will install the gem on your local environment.
42
41
 
43
- The next step is to configure the default rails logger to use the insightops logger.
42
+ The next step is to configure the default rails logger to use the Rapid7 Insight Platform logger.
43
+ Ensure you add a `require` to load in the package:
44
44
 
45
+ require 'r7_insight.rb'
45
46
 
46
47
  In your environment configuration file ( for production : `config/environments/production.rb`), add the following:
47
48
 
48
- Rails.logger = Le.new('LOG_TOKEN', 'REGION')
49
+ Rails.logger = R7Insight.new('LOG_TOKEN', 'REGION')
49
50
 
50
51
  If you want to keep logging locally in addition to sending logs to in, just add local parameter after the key.
51
52
  By default, this will write to the standard Rails log or to STDOUT if not using Rails:
52
53
 
53
- Rails.logger = Le.new('LOG_TOKEN', 'REGION', :local => true)
54
+ Rails.logger = R7Insight.new('LOG_TOKEN', 'REGION', :local => true)
54
55
 
55
56
  You may specify the local log device by providing a filename (String) or IO object (typically STDOUT, STDERR, or an open file):
56
57
 
57
- Rails.logger = Le.new('LOG_TOKEN', 'REGION', :local => 'log/my_custom_log.log')
58
+ Rails.logger = R7Insight.new('LOG_TOKEN', 'REGION', :local => 'log/my_custom_log.log')
58
59
 
59
- If you want the gem to use SSL when streaming logs to insightops, add the ssl parameter and set it to true:
60
+ If you want the gem to use SSL when streaming logs to the Rapid7 Insight Platform, add the ssl parameter and set it to true:
60
61
 
61
- Rails.logger = Le.new('LOG_TOKEN', 'REGION', :ssl => true)
62
+ Rails.logger = R7Insight.new('LOG_TOKEN', 'REGION', :ssl => true)
62
63
 
63
64
  If you want to print debug messages for the gem to a file called r7insightGem.log, add this:
64
65
 
65
- Rails.logger = Le.new('LOG_TOKEN', 'REGION', :debug => true)
66
+ Rails.logger = R7Insight.new('LOG_TOKEN', 'REGION', :debug => true)
66
67
 
67
68
  If you want to use ActiveSupport::TaggedLogging logging, add this:
68
69
 
69
- Rails.logger = Le.new('LOG_TOKEN', 'REGION', :tag => true)
70
+ Rails.logger = R7Insight.new('LOG_TOKEN', 'REGION', :tag => true)
70
71
 
71
72
  You can also specify the default level of the logger by adding a :
72
73
 
73
- Rails.logger = Le.new('LOG_TOKEN', 'REGION', :log_level => Logger::<level>)
74
+ Rails.logger = R7Insight.new('LOG_TOKEN', 'REGION', :log_level => Logger::<level>)
74
75
 
75
- For the `LOG_TOKEN` argument, paste the token for the logfile you created earlier in the InsightOps UI or empty string for
76
+ For the `LOG_TOKEN` argument, paste the token for the logfile you created earlier in the Rapid7 Insight UI or empty string for
76
77
  a UDP connection.
77
78
 
78
- For the `REGION` argument, provide the region of your account, e.g: 'eu', 'us'.
79
+ For the `REGION` argument, provide the region of your account, e.g: 'eu', 'us' etc.
79
80
 
80
81
  Additionally, when connecting via UDP, be sure to specify a port using the udp_port parameter:
81
82
 
82
- Rails.logger = Le.new('', 'REGION' :udp_port => 13287)
83
+ Rails.logger = R7Insight.new('', 'REGION' :udp_port => 13287)
84
+
85
+
86
+ Contact Support
87
+ ------
88
+
89
+ Please email our support team at support@rapid7.com if you need any help.
data/Rakefile CHANGED
@@ -1,16 +1,18 @@
1
- require "bundler/gem_tasks"
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
2
4
  require 'rake/testtask'
3
5
 
4
6
  Rake::TestTask.new do |t|
5
- t.libs = ['lib','test']
6
- t.test_files = Dir.glob("test/**/*_spec.rb").sort
7
+ t.libs = %w[lib test]
8
+ t.test_files = Dir.glob('test/**/*_spec.rb').sort
7
9
  t.verbose = true
8
10
  end
9
11
 
10
- task :default => [:test]
11
- task :spec => [:test]
12
+ task default: [:test]
13
+ task spec: [:test]
12
14
 
13
- desc "Open an irb session preloaded with this library"
15
+ desc 'Open an irb session preloaded with this library'
14
16
  task :console do
15
- sh "irb -rubygems -I lib -r le.rb"
17
+ sh 'irb -rubygems -I lib -r r7_insight.rb'
16
18
  end
@@ -1,11 +1,12 @@
1
- require File.join(File.dirname(__FILE__), 'le', 'host')
1
+ # frozen_string_literal: true
2
2
 
3
- require 'logger'
4
-
5
- module Le
3
+ require File.join(File.dirname(__FILE__), 'r7_insight', 'host')
6
4
 
7
- def self.new(token, region, options={})
5
+ require 'logger'
8
6
 
7
+ # Rapid7 Insight Platform Ruby Logging functionality
8
+ module R7Insight
9
+ def self.new(token, region, options = {})
9
10
  opt_local = options[:local] || false
10
11
  opt_debug = options[:debug] || false
11
12
  opt_ssl = !options.include?(:ssl) ? true : options[:ssl]
@@ -13,21 +14,18 @@ module Le
13
14
  opt_log_level = options[:log_level] || Logger::DEBUG
14
15
 
15
16
  opt_datahub_enabled = options[:datahub_enabled] || false
16
- opt_datahub_endpoint = options[:datahub_endpoint] || ['', 10000]
17
- opt_datahub_ip = options[:datahub_ip] || ''
18
- opt_datahub_port = options[:datahub_port] || 10000
17
+ opt_datahub_endpoint = options[:datahub_endpoint] || ['', 10_000]
19
18
  opt_host_id = options[:host_id] || ''
20
- opt_host_name_enabled = options[:host_name_enabled] || false
21
- opt_host_name = options[:host_name] || ''
22
19
  opt_custom_host = options[:custom_host] || [false, '']
23
20
 
24
21
  opt_udp_port = options[:udp_port] || nil
25
22
  opt_use_data_endpoint = options[:data_endpoint] || false
26
23
 
27
- self.checkParams(token, region, opt_datahub_enabled, opt_udp_port)
24
+ check_params(token, region, opt_datahub_enabled, opt_udp_port)
28
25
 
29
-
30
- host = Le::Host.new(token, region, opt_local, opt_debug, opt_ssl, opt_datahub_endpoint, opt_host_id, opt_custom_host, opt_udp_port, opt_use_data_endpoint)
26
+ host = R7Insight::Host.new(token, region, opt_local, opt_debug, opt_ssl,
27
+ opt_datahub_endpoint, opt_host_id, opt_custom_host,
28
+ opt_udp_port, opt_use_data_endpoint)
31
29
 
32
30
  if defined?(ActiveSupport::TaggedLogging) && opt_tag
33
31
  logger = ActiveSupport::TaggedLogging.new(Logger.new(host))
@@ -44,22 +42,15 @@ module Le
44
42
  logger
45
43
  end
46
44
 
47
- def self.checkParams(token, region, opt_datahub_enabled, opt_udp_port)
48
- # Check if the key is valid UUID format
45
+ def self.check_params(token, region, opt_datahub_enabled, opt_udp_port)
46
+ # test Token only when DataHub and UDP are not enabled
47
+ return unless !opt_datahub_enabled && !opt_udp_port
49
48
 
50
- if (!opt_datahub_enabled && !opt_udp_port) # test Token only when DataHub and UDP are not enabled
51
- if (token =~ /\A(urn:uuid:)?[\da-f]{8}-([\da-f]{4}-){3}[\da-f]{12}\z/i) == nil
52
- puts "\nLE: It appears the LOGENTRIES_TOKEN you entered is invalid!\n"
53
- else
54
- (token="")
55
- end
56
-
57
- # This checks if region is valid. So far we have Europe and US
58
- if !region
59
- puts "\nLE: You need to specify region. Options: eu, us"
60
- end
49
+ # Check if the key is valid UUID format
50
+ if (token =~ /\A(urn:uuid:)?[\da-f]{8}-([\da-f]{4}-){3}[\da-f]{12}\z/i).nil?
51
+ puts "\nLE: It appears the R7INSIGHT_TOKEN you entered is invalid!\n"
52
+ end
61
53
 
62
- end
54
+ puts "\nLE: You need to specify region, such as 'eu'" unless region
63
55
  end
64
-
65
56
  end
@@ -1,15 +1,20 @@
1
- module Le
2
- module Host
1
+ # frozen_string_literal: true
3
2
 
4
- def self.new(token, region, local, debug, ssl, datahub_endpoint, host_id, custom_host, udp_port, use_data_endpoint)
5
- Le::Host::CONNECTION.new(token, region, local, debug, ssl, datahub_endpoint, host_id, custom_host, udp_port, use_data_endpoint)
3
+ module R7Insight
4
+ # Rapid7 Insight Platform Logging Host
5
+ module Host
6
+ def self.new(token, region, local, debug, ssl, datahub_endpoint, host_id,
7
+ custom_host, udp_port, use_data_endpoint)
8
+ R7Insight::Host::CONNECTION.new(token, region, local, debug, ssl,
9
+ datahub_endpoint, host_id, custom_host,
10
+ udp_port, use_data_endpoint)
6
11
  end
7
12
 
13
+ # Log formatter
8
14
  module InstanceMethods
9
15
  def formatter
10
16
  proc do |severity, datetime, _, msg|
11
- message = "#{datetime} "
12
- message << format_message(msg, severity)
17
+ "#{datetime} #{format_message(msg, severity)}"
13
18
  end
14
19
  end
15
20
 
@@ -19,7 +24,6 @@ module Le
19
24
  "severity=#{severity}, #{message_in.lstrip}"
20
25
  end
21
26
  end
22
-
23
27
  end
24
28
  end
25
29
 
@@ -0,0 +1,300 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'socket'
4
+ require 'openssl'
5
+ require 'timeout'
6
+ require 'uri'
7
+
8
+ module R7Insight
9
+ module Host
10
+ # Class for connecting to the Rapid7 Insight Platform and handling the
11
+ # connection
12
+ class CONNECTION
13
+ DATA_ENDPOINT = '.data.logs.insight.rapid7.com'
14
+ DATA_PORT_UNSECURE = 80
15
+ DATA_PORT_SECURE = 443
16
+ API_SSL_PORT = 20_000
17
+ SHUTDOWN_COMMAND = 'DIE!DIE!' # magic shutdown command string for worker
18
+ SHUTDOWN_MAX_WAIT = 10 # max seconds to wait for queue shutdown clearing
19
+ SHUTDOWN_WAIT_STEP = 0.2 # sleep duration (seconds) while shutting down
20
+ CONNECTION_EXCEPTIONS = [
21
+ Timeout::Error,
22
+ Errno::EHOSTUNREACH,
23
+ Errno::ECONNREFUSED,
24
+ Errno::ECONNRESET,
25
+ Errno::ETIMEDOUT,
26
+ EOFError,
27
+ Errno::EPIPE
28
+ ].freeze
29
+
30
+ include R7Insight::Host::InstanceMethods
31
+ attr_accessor :token, :region, :queue, :started, :thread, :conn, :local,
32
+ :debug, :ssl, :datahub_enabled, :datahub_ip, :datahub_port,
33
+ :datahub_endpoint, :host_id, :host_name_enabled, :host_name,
34
+ :custom_host, :udp_port, :use_data_endpoint
35
+
36
+ def initialize(token, region, local, debug, ssl, datahub_endpoint,
37
+ host_id, custom_host, udp_port, use_data_endpoint)
38
+ if local
39
+ device = if local.class <= TrueClass
40
+ if defined?(Rails)
41
+ Rails.root.join('log', "#{Rails.env}.log")
42
+ else
43
+ STDOUT
44
+ end
45
+ else
46
+ local
47
+ end
48
+ @logger_console = Logger.new(device)
49
+ end
50
+
51
+ @region = region
52
+ @local = local.nil? || local == false ? false : true # Replace "!!"
53
+ @debug = debug
54
+ @ssl = ssl
55
+ @udp_port = udp_port
56
+ @use_data_endpoint = use_data_endpoint
57
+
58
+ @datahub_endpoint = datahub_endpoint
59
+ if !@datahub_endpoint[0].empty?
60
+ @datahub_enabled = true
61
+ @datahub_ip = (@datahub_endpoint[0]).to_s
62
+ @datahub_port = @datahub_endpoint[1]
63
+ else
64
+ @datahub_enabled = false
65
+ end
66
+
67
+ if @datahub_enabled && @ssl
68
+ puts("\n\nYou Cannot have DataHub and SSL enabled at the same time.
69
+ Please set SSL value to false in your environment.rb file or used Token-Based
70
+ logging by leaving the Datahub IP address blank. Exiting application. \n\n")
71
+ exit
72
+ end
73
+
74
+ # Check if region was specified
75
+ puts("\n\nYou need to specify a region, such as 'eu'") if region.empty?
76
+
77
+ # Check if DataHub is enabled
78
+ # If datahub is not enabled, set the token to the token's parameter
79
+ # If DH is enabled, make the token empty.
80
+ if !datahub_enabled
81
+ @token = token
82
+ else
83
+ @token = ''
84
+
85
+ # !NOTE THIS @datahub_port conditional MAY NEED TO BE CHANGED IF SSL
86
+ # CAN'T WORK WITH DH
87
+ @datahub_port = @datahub_port.empty? ? API_SSL_PORT : datahub_port
88
+ @datahub_ip = datahub_ip
89
+ end
90
+
91
+ @host_name_enabled = custom_host[0]
92
+ @host_name = custom_host[1]
93
+
94
+ if !host_id.empty?
95
+ @host_id = host_id
96
+ @host_id = "host_id=#{host_id}"
97
+ else
98
+ @host_id = ''
99
+ end
100
+
101
+ # If no host name is given but required, assign the machine name
102
+ if @host_name_enabled
103
+ @host_name = Socket.gethostname if host_name.empty?
104
+
105
+ @host_name = "host_name=#{@host_name}"
106
+ end
107
+
108
+ @queue = Queue.new
109
+ @started = false
110
+ @thread = nil
111
+
112
+ init_debug if @debug
113
+ at_exit { shutdown! }
114
+ end
115
+
116
+ def init_debug
117
+ file_path = 'r7insightGem.log'
118
+ file_path = 'log/r7insightGem.log' if File.exist?('log/')
119
+ @debug_logger = Logger.new(file_path)
120
+ end
121
+
122
+ def dbg(message)
123
+ @debug_logger.add(Logger::Severity::DEBUG, message) if @debug
124
+ end
125
+
126
+ def write(message)
127
+ message = "#{message} #{host_id}" unless host_id.empty?
128
+ message = "#{message} #{host_name}" if host_name_enabled
129
+
130
+ @logger_console.add(Logger::Severity::UNKNOWN, message) if @local
131
+
132
+ @queue << if message.scan(/\n/).empty?
133
+ "#{@token} #{message} \n"
134
+ else
135
+ "#{message.gsub(/^/, "#{@token} [#{random_message_id}]")}\n"
136
+ end
137
+
138
+ if @started
139
+ check_async_thread
140
+ else
141
+ start_async_thread
142
+ end
143
+ end
144
+
145
+ def start_async_thread
146
+ @thread = Thread.new { run }
147
+ dbg 'R7Insight: Asynchronous socket writer started'
148
+ @started = true
149
+ end
150
+
151
+ def check_async_thread
152
+ @thread = Thread.new { run } unless @thread&.alive?
153
+ end
154
+
155
+ def close
156
+ dbg 'R7Insight: Closing asynchronous socket writer'
157
+ @started = false
158
+ end
159
+
160
+ def open_connection
161
+ dbg 'R7Insight: Reopening connection to R7Insight API server'
162
+
163
+ if @use_data_endpoint
164
+ host = @region + DATA_ENDPOINT
165
+
166
+ port = @ssl ? DATA_PORT_SECURE : DATA_PORT_UNSECURE
167
+ elsif @udp_port
168
+ host = @region + DATA_ENDPOINT
169
+ port = @udp_port
170
+ elsif @datahub_enabled
171
+ host = @datahub_ip
172
+ port = @datahub_port
173
+ else
174
+ host = @region + DATA_ENDPOINT
175
+ port = @ssl ? DATA_PORT_SECURE : DATA_PORT_UNSECURE
176
+ end
177
+
178
+ if @udp_port
179
+ @conn = UDPSocket.new
180
+ @conn.connect(host, port)
181
+ else
182
+ socket = TCPSocket.new(host, port)
183
+
184
+ if @ssl
185
+ cert_store = OpenSSL::X509::Store.new
186
+ cert_store.set_default_paths
187
+
188
+ ssl_context = OpenSSL::SSL::SSLContext.new
189
+ ssl_context.cert_store = cert_store
190
+
191
+ ssl_context.min_version = OpenSSL::SSL::TLS1_1_VERSION
192
+ ssl_context.max_version = OpenSSL::SSL::TLS1_3_VERSION
193
+ ssl_context.verify_mode = OpenSSL::SSL::VERIFY_PEER
194
+ ssl_socket = OpenSSL::SSL::SSLSocket.new(socket, ssl_context)
195
+ ssl_socket.hostname = host if ssl_socket.respond_to?(:hostname=)
196
+ ssl_socket.sync_close = true
197
+ Timeout.timeout(10) do
198
+ ssl_socket.connect
199
+ end
200
+ @conn = ssl_socket
201
+ else
202
+ @conn = socket
203
+ end
204
+ end
205
+
206
+ dbg 'R7Insight: Connection established'
207
+ end
208
+
209
+ def reopen_connection
210
+ close_connection
211
+ root_delay = 0.1
212
+ loop do
213
+ begin
214
+ open_connection
215
+ break
216
+ rescue *CONNECTION_EXCEPTIONS
217
+ dbg "R7Insight: Unable to connect to R7Insight due to timeout
218
+ (#{$ERROR_INFO})"
219
+ rescue StandardError
220
+ dbg "R7Insight: Got exception in reopenConnection - #{$ERROR_INFO}"
221
+ raise
222
+ end
223
+ root_delay *= 2
224
+ root_delay = 10 if root_delay >= 10
225
+ wait_for = (root_delay + rand(root_delay)).to_i
226
+ dbg "R7Insight: Waiting for #{wait_for}ms"
227
+ sleep(wait_for)
228
+ end
229
+ end
230
+
231
+ def close_connection
232
+ begin
233
+ if @conn.respond_to?(:sysclose)
234
+ @conn.sysclose
235
+ elsif @conn.respond_to?(:close)
236
+ @conn.close
237
+ end
238
+ rescue StandardError
239
+ dbg "R7Insight: couldn't close connection, close with exception -
240
+ #{$ERROR_INFO}"
241
+ ensure
242
+ @conn = nil
243
+ end
244
+ end
245
+
246
+ def run
247
+ reopen_connection
248
+
249
+ loop do
250
+ data = @queue.pop
251
+ break if data == SHUTDOWN_COMMAND
252
+
253
+ loop do
254
+ begin
255
+ @conn.write(data)
256
+ rescue *CONNECTION_EXCEPTIONS
257
+ dbg "R7Insight: Connection timeout(#{$ERROR_INFO}), try to reopen
258
+ connection"
259
+ reopen_connection
260
+ next
261
+ rescue StandardError
262
+ dbg "R7Insight: Got exception in run loop - #{$ERROR_INFO}"
263
+ raise
264
+ end
265
+ break
266
+ end
267
+ end
268
+
269
+ dbg 'R7Insight: Closing Asynchronous socket writer'
270
+
271
+ close_connection
272
+ end
273
+
274
+ private
275
+
276
+ def random_message_id
277
+ @random_message_id_sample_space ||= ('0'..'9').to_a.concat(('A'..'Z')
278
+ .to_a)
279
+ (0..5).map { @random_message_id_sample_space.sample }.join
280
+ end
281
+
282
+ # at_exit handler.
283
+ # Attempts to clear the queue and terminate the async worker cleanly
284
+ # before process ends.
285
+ def shutdown!
286
+ return unless @started
287
+
288
+ dbg "R7Insight: commencing shutdown, queue has #{queue.size} entries to clear"
289
+ queue << SHUTDOWN_COMMAND
290
+ SHUTDOWN_MAX_WAIT.div(SHUTDOWN_WAIT_STEP).times do
291
+ break if queue.empty?
292
+
293
+ sleep SHUTDOWN_WAIT_STEP
294
+ end
295
+ dbg "R7Insight: shutdown complete, queue is #{queue.empty? ? '' : 'not '}
296
+ empty with #{queue.size} entries"
297
+ end
298
+ end
299
+ end
300
+ end