loggability 0.15.0.pre20190714094638 → 0.18.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -9,10 +9,7 @@ require 'date'
9
9
  module Loggability
10
10
 
11
11
  # Package version constant
12
- VERSION = '0.14.0'
13
-
14
- # VCS revision
15
- REVISION = %q$Revision$
12
+ VERSION = '0.18.1'
16
13
 
17
14
  # The key for the global logger (Loggability's own logger)
18
15
  GLOBAL_KEY = :__global__
@@ -29,22 +26,28 @@ module Loggability
29
26
  LOGSPEC_PATTERN = %r{
30
27
  ^
31
28
  \s*
32
- ((?i:debug|info|warn|error|fatal)) # severity
29
+ (?<severity>(?i:debug|info|warn|error|fatal))
33
30
  (?:
34
31
  \s+
35
- ((?:[\w\-/:\.]|\\[ ])+)
32
+ (?<target>(?:[\w\-/:\.\[\]]|\\[ ])+)
36
33
  )?
37
34
  (?: \s+\(
38
- (\w+)
35
+ (?<format>\w+)
39
36
  \) )?
40
37
  \s*
41
38
  $
42
39
  }x
43
40
 
44
- require 'loggability/constants'
45
- include Loggability::Constants
46
41
 
47
- require 'loggability/logger'
42
+ # Automatically load subordinate classes/modules
43
+ autoload :Constants, 'loggability/constants'
44
+ autoload :LogDevice, 'loggability/log_device'
45
+ autoload :Logger, 'loggability/logger'
46
+ autoload :LogHost, 'loggability/loghost'
47
+ autoload :LogClient, 'loggability/logclient'
48
+ autoload :Override, 'loggability/override'
49
+
50
+ include Loggability::Constants
48
51
 
49
52
 
50
53
  ##
@@ -58,18 +61,6 @@ module Loggability
58
61
  @config = CONFIG_DEFAULTS.dup.freeze
59
62
 
60
63
 
61
- # Automatically log the log host and log client mixins when they're referenced
62
- autoload :LogHost, 'loggability/loghost'
63
- autoload :LogClient, 'loggability/logclient'
64
- autoload :Override, 'loggability/override'
65
-
66
-
67
- ### Return the library's version string
68
- def self::version_string( include_buildnum=false )
69
- vstring = "%s %s" % [ self.name, VERSION ]
70
- vstring << " (build %s)" % [ REVISION[/: ([[:xdigit:]]+)/, 1] || '0' ] if include_buildnum
71
- return vstring
72
- end
73
64
 
74
65
 
75
66
  ### Cast the given +device+ to a Loggability::Logger, if possible, and return it. If
@@ -155,12 +146,12 @@ module Loggability
155
146
 
156
147
  ### Call the method with the given +methodname+ across the loggers of all loghosts with
157
148
  ### the given +arg+ and/or +block+.
158
- def self::aggregate( methodname, arg, &block )
149
+ def self::aggregate( methodname, *args, &block )
159
150
  # self.log.debug "Aggregating a call to %p with %p to %d log hosts" %
160
151
  # [ methodname, arg, Loggability.log_hosts.length ]
161
152
  Loggability.log_hosts.values.each do |loghost|
162
153
  # self.log.debug " %p.logger.%s( %p )" % [ loghost, methodname, arg ]
163
- loghost.logger.send( methodname, arg, &block )
154
+ loghost.logger.send( methodname, *args, &block )
164
155
  end
165
156
  end
166
157
 
@@ -200,8 +191,8 @@ module Loggability
200
191
  #
201
192
  # Aggregate method: set all loggers to log to +destination+. See Loggability::Logger#output_to
202
193
  # for more info.
203
- def self::output_to( newdevice )
204
- self.aggregate( :output_to, newdevice )
194
+ def self::output_to( newdevice, *args )
195
+ self.aggregate( :output_to, newdevice, *args )
205
196
  end
206
197
  class << self
207
198
  alias_method :write_to, :output_to
@@ -332,6 +323,7 @@ module Loggability
332
323
  target = case target
333
324
  when 'STDOUT' then $stdout
334
325
  when 'STDERR' then $stderr
326
+ when /:/ then Loggability::LogDevice.parse_device_spec( target )
335
327
  else
336
328
  target
337
329
  end
@@ -0,0 +1,35 @@
1
+ # -*- ruby -*-
2
+ # vim: set nosta noet ts=4 sw=4:
3
+ # frozen_string_literal: true
4
+
5
+ require 'time'
6
+ require 'json'
7
+
8
+ require 'loggability/formatter' unless defined?( Loggability::Formatter )
9
+
10
+
11
+ # Output logs as JSON.
12
+ class Loggability::Formatter::Structured < Loggability::Formatter
13
+
14
+ # The version of the format output
15
+ LOG_FORMAT_VERSION = 1
16
+
17
+
18
+ ### Format a message of the specified +severity+ using the given +time+,
19
+ ### +progname+, and +message+.
20
+ def call( severity, time, progname, message )
21
+ severity ||= 'DEBUG'
22
+ time ||= Time.now
23
+ entry = {
24
+ '@version' => LOG_FORMAT_VERSION,
25
+ '@timestamp' => time.iso8601( 3 ),
26
+ 'level' => severity,
27
+ 'progname' => progname,
28
+ 'message' => message,
29
+ }
30
+
31
+ return JSON.generate( entry )
32
+ end
33
+
34
+ end # class Loggability::Formatter::Default
35
+
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env ruby
2
+ # vim: set nosta noet ts=4 sw=4:
3
+ # encoding: utf-8
4
+
5
+ require 'loggability' unless defined?( Loggability )
6
+
7
+
8
+ # An abstract base class for logging devices. A device manages the actual writing of messages
9
+ # to whatever destination logs are supposed to be shipped to, along with any buffering,
10
+ # encoding, or serialization that needs to be done.
11
+ #
12
+ # Log devices are loadable by name via the ::create method if they are declared in a
13
+ # directory named `loggability/log_device/` in the gem path.
14
+ #
15
+ # Concrete log devices are required to implement two methods: #write and #close.
16
+ #
17
+ # [write]
18
+ # Takes one argument, which is the message that needs to be written.
19
+ #
20
+ # [close]
21
+ # Close any open filehandles or connections established by the device.
22
+ class Loggability::LogDevice
23
+
24
+
25
+ # Regexp used to split up logging devices in config lines
26
+ DEVICE_TARGET_REGEX = /^([\s*a-z]\w*)(?:\[(.*)\])?/
27
+
28
+
29
+ ### Parses out the target class name and its arguments from the +target_spec+
30
+ ### then requires the subclass and instantiates it by passing the arguments.
31
+ ### The +target_spec+ comes from a config file in the format of:
32
+ ###
33
+ ### logging:
34
+ ### datadog[data_dog_api_key]
35
+ ###
36
+ ### In the above example:
37
+ ### * "datadog" is the log device to send logs to
38
+ ### * "data_dog_api_key" is the argument that will be passed onto the datadog
39
+ ### log device's constructor
40
+ def self::parse_device_spec( target_spec )
41
+ targets = target_spec.split( ';' ).compact
42
+ return targets.map do |t|
43
+ target_subclass = t[ DEVICE_TARGET_REGEX, 1 ]&.strip.to_sym
44
+ target_subclass_args = t[ DEVICE_TARGET_REGEX, 2 ]
45
+
46
+ self.create( target_subclass, target_subclass_args )
47
+ end
48
+ end
49
+
50
+
51
+ ### Requires the subclass and instantiates it with the passed-in arguments and
52
+ ### then returns an instance of it.
53
+ def self::create( target, *target_args )
54
+ modname = target.to_s.capitalize
55
+
56
+ self.load_device_type( target ) unless self.const_defined?( modname, false )
57
+ subclass = self.const_get( modname, false )
58
+
59
+ return subclass.new( *target_args )
60
+ rescue NameError => err
61
+ raise LoadError, "failed to load %s LogDevice: %s" % [ target, err.message ]
62
+ end
63
+
64
+
65
+ ### Attempt to load a LogDevice of the given +type+.
66
+ def self::load_device_type( type )
67
+ require_path = "loggability/log_device/%s" % [ type.to_s.downcase ]
68
+ require( require_path )
69
+ end
70
+
71
+
72
+ ### Write a +message+ to the device. This needs to be overridden by concrete
73
+ ### subclasses; calling this implementation will raise an NotImplementedError.
74
+ def write( message )
75
+ raise NotImplementedError, "%s is not implemented by %s" % [ __callee__, self.class.name ]
76
+ end
77
+
78
+
79
+ ### Close the device. This needs to be overridden by concrete subclasses;
80
+ ### calling this implementation will raise an NotImplementedError.
81
+ def close
82
+ raise NotImplementedError, "%s is not implemented by %s" % [ __callee__, self.class.name ]
83
+ end
84
+
85
+ end # class Loggability::LogDevice
86
+
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env ruby
2
+ # vim: set nosta noet ts=4 sw=4:
3
+ # encoding: utf-8
4
+
5
+ require 'loggability/logger' unless defined?( Loggability::Logger )
6
+
7
+ # A log device that appends to the object it's constructed with instead of writing
8
+ # to a file descriptor or a file.
9
+ class Loggability::LogDevice::Appending < Loggability::LogDevice
10
+
11
+ ### Create a new +Appending+ log device that will append content to +array+.
12
+ def initialize( target )
13
+ @target = target || []
14
+ end
15
+
16
+
17
+ ######
18
+ public
19
+ ######
20
+
21
+ # The target of the log device
22
+ attr_reader :target
23
+
24
+
25
+ ### Append the specified +message+ to the target.
26
+ def write( message )
27
+ @target << message
28
+ end
29
+
30
+
31
+ ### No-op -- this is here just so Logger doesn't complain
32
+ def close; end
33
+
34
+ end # class Loggability::LogDevice::Appending
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env ruby
2
+ # vim: set nosta noet ts=4 sw=4:
3
+ # encoding: utf-8
4
+
5
+ require 'uri'
6
+ require 'socket'
7
+ require 'net/https'
8
+ require 'json'
9
+ require 'concurrent'
10
+ require 'loggability/logger' unless defined?( Loggability::Logger )
11
+
12
+ require 'loggability/log_device/http'
13
+
14
+
15
+ # A log device that sends logs to Datadog's HTTP endpoint
16
+ # for receiving logs
17
+ class Loggability::LogDevice::Datadog < Loggability::LogDevice::Http
18
+
19
+ ### Datadog's HTTP endpoint URL for sending logs to
20
+ DEFAULT_ENDPOINT = URI( "https://http-intake.logs.datadoghq.com/v1/input" )
21
+
22
+ ### The max number of messages that can be sent to datadog in a single payload
23
+ MAX_BATCH_SIZE = 480
24
+
25
+ ### The max size in bytes for a single message.
26
+ ### Limiting the message size to 200kB to leave room for other info such as
27
+ ### tags, metadata, etc.
28
+ ### DataDog's max size for a single log entry is 256kB
29
+ MAX_MESSAGE_BYTESIZE = 204_800
30
+
31
+ ### The max size in bytes of all messages in the batch.
32
+ ### Limiting the total messages size to 4MB to leave room for other info such as
33
+ ### tags, metadata, etc.
34
+ ### Datadog's max size for the entire payload is 5MB
35
+ MAX_BATCH_BYTESIZE = 4_194_304
36
+
37
+ # Override the default HTTP device options for sending logs to DD
38
+ DEFAULT_OPTIONS = {
39
+ max_batch_size: MAX_BATCH_SIZE,
40
+ max_message_bytesize: MAX_MESSAGE_BYTESIZE,
41
+ max_batch_bytesize: MAX_BATCH_BYTESIZE,
42
+ }
43
+
44
+
45
+ ### Create a new Datadog
46
+ def initialize( api_key, endpoint=DEFAULT_ENDPOINT, options={} )
47
+ if endpoint.is_a?( Hash )
48
+ options = endpoint
49
+ endpoint = DEFAULT_ENDPOINT
50
+ end
51
+
52
+ super( endpoint, options )
53
+
54
+ @api_key = api_key
55
+ @hostname = Socket.gethostname
56
+ end
57
+
58
+
59
+ ######
60
+ public
61
+ ######
62
+
63
+ ##
64
+ # The name of the current host
65
+ attr_reader :hostname
66
+
67
+ ##
68
+ # The configured Datadog API key
69
+ attr_reader :api_key
70
+
71
+
72
+ ### Format an individual log +message+ for Datadog.
73
+ def format_log_message( message )
74
+ return {
75
+ hostname: self.hostname,
76
+ message: message
77
+ }.to_json
78
+ end
79
+
80
+
81
+ ### Overridden to add the configured API key to the headers of each request.
82
+ def make_batch_request
83
+ request = super
84
+
85
+ request[ 'DD-API-KEY' ] = self.api_key
86
+
87
+ return request
88
+ end
89
+
90
+ end # class Loggability::LogDevice::Datadog
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env ruby
2
+ # vim: set nosta noet ts=4 sw=4:
3
+ # encoding: utf-8
4
+
5
+ require 'logger'
6
+ require 'loggability/logger' unless defined?( Loggability::Logger )
7
+
8
+ # A log device that delegates to the ruby's default Logger's file device and writes
9
+ # to a file descriptor or a file.
10
+ class Loggability::LogDevice::File < Loggability::LogDevice
11
+
12
+ ### Create a new +File+ device that will write to the file using the built-in ruby's +File+ log device
13
+ def initialize( target )
14
+ @target = ::Logger::LogDevice.new( target )
15
+ end
16
+
17
+
18
+ ######
19
+ public
20
+ ######
21
+
22
+ # The target of the log device
23
+ attr_reader :target
24
+
25
+
26
+ ### Append the specified +message+ to the target.
27
+ def write( message )
28
+ self.target.write( message )
29
+ end
30
+
31
+
32
+ ### close the file
33
+ def close
34
+ self.target.close
35
+ end
36
+
37
+ end # class Loggability::LogDevice::File
@@ -0,0 +1,310 @@
1
+ #!/usr/bin/env ruby
2
+ # vim: set nosta noet ts=4 sw=4:
3
+ # encoding: utf-8
4
+
5
+ require 'socket'
6
+ require 'uri'
7
+ require 'net/https'
8
+ require 'json'
9
+ require 'concurrent'
10
+ require 'loggability/logger' unless defined?( Loggability::Logger )
11
+
12
+ require 'loggability/log_device'
13
+
14
+ # This is the a generalized class that allows its subclasses to send log
15
+ # messages to HTTP endpoints asynchronously on a separate thread.
16
+ class Loggability::LogDevice::Http < Loggability::LogDevice
17
+
18
+ # The default HTTP endpoint URL to send logs to
19
+ DEFAULT_ENDPOINT = "http://localhost:12775/v1/logs"
20
+
21
+ # The default maximum number of messages that can be sent to the server in a single payload
22
+ DEFAULT_MAX_BATCH_SIZE = 100
23
+
24
+ # The default max size in bytes for a single message.
25
+ DEFAULT_MAX_MESSAGE_BYTESIZE = 2 * 16
26
+
27
+ # The default number of seconds between batches
28
+ DEFAULT_BATCH_INTERVAL = 60
29
+
30
+ # The default number of seconds to wait for data to be written before timing out
31
+ DEFAULT_WRITE_TIMEOUT = 15
32
+
33
+ # The default Executor class to use for asynchronous tasks
34
+ DEFAULT_EXECUTOR_CLASS = Concurrent::SingleThreadExecutor
35
+
36
+ # The default for the maximum bytesize of the queue (1 GB)
37
+ DEFAULT_MAX_QUEUE_BYTESIZE = ( 2 ** 10 ) * ( 2 ** 10 ) * ( 2 ** 10 )
38
+
39
+ # The default options for new instances
40
+ DEFAULT_OPTIONS = {
41
+ execution_interval: DEFAULT_BATCH_INTERVAL,
42
+ write_timeout: DEFAULT_WRITE_TIMEOUT,
43
+ max_batch_size: DEFAULT_MAX_BATCH_SIZE,
44
+ max_message_bytesize: DEFAULT_MAX_MESSAGE_BYTESIZE,
45
+ executor_class: DEFAULT_EXECUTOR_CLASS,
46
+ }
47
+
48
+
49
+ ### Initialize the HTTP log device to send to the specified +endpoint+ with the
50
+ ### given +options+. Valid options are:
51
+ ###
52
+ ### [:batch_interval]
53
+ ### Maximum number of seconds between batches
54
+ ### [:write_timeout]
55
+ ### How many seconds to wait for data to be written while sending a batch
56
+ ### [:max_batch_size]
57
+ ### The maximum number of messages that can be in a single batch
58
+ ### [:max_batch_bytesize]
59
+ ### The maximum number of bytes that can be in the payload of a single batch
60
+ ### [:max_message_bytesize]
61
+ ### The maximum number of bytes that can be in a single message
62
+ ### [:executor_class]
63
+ ### The Concurrent executor class to use for asynchronous tasks.
64
+ def initialize( endpoint=DEFAULT_ENDPOINT, opts={} )
65
+ if endpoint.is_a?( Hash )
66
+ opts = endpoint
67
+ endpoint = DEFAULT_ENDPOINT
68
+ end
69
+
70
+ opts = DEFAULT_OPTIONS.merge( opts )
71
+
72
+ @endpoint = URI( endpoint ).freeze
73
+ @logs_queue = Queue.new
74
+
75
+ @logs_queue_bytesize = 0
76
+ @max_queue_bytesize = opts[:max_queue_bytesize] || DEFAULT_MAX_QUEUE_BYTESIZE
77
+ @batch_interval = opts[:batch_interval] || DEFAULT_BATCH_INTERVAL
78
+ @write_timeout = opts[:write_timeout] || DEFAULT_WRITE_TIMEOUT
79
+ @max_batch_size = opts[:max_batch_size] || DEFAULT_MAX_BATCH_SIZE
80
+ @max_message_bytesize = opts[:max_message_bytesize] || DEFAULT_MAX_MESSAGE_BYTESIZE
81
+ @executor_class = opts[:executor_class] || DEFAULT_EXECUTOR_CLASS
82
+
83
+ @max_batch_bytesize = opts[:max_batch_bytesize] || @max_batch_size * @max_message_bytesize
84
+ @last_send_time = Concurrent.monotonic_time
85
+ end
86
+
87
+
88
+ ######
89
+ public
90
+ ######
91
+
92
+ ##
93
+ # The single thread pool executor
94
+ attr_reader :executor
95
+
96
+ ##
97
+ # The URI of the endpoint to send messages to
98
+ attr_reader :endpoint
99
+
100
+ ##
101
+ # The Queue that contains any log messages which have not yet been sent to the
102
+ # logging service.
103
+ attr_reader :logs_queue
104
+
105
+ ##
106
+ # The max bytesize of the queue. Will not queue more messages if this threshold is hit
107
+ attr_reader :max_queue_bytesize
108
+
109
+ ##
110
+ # The size of +logs_queue+ in bytes
111
+ attr_accessor :logs_queue_bytesize
112
+
113
+ ##
114
+ # The monotonic clock time when the last batch of logs were sent
115
+ attr_accessor :last_send_time
116
+
117
+ ##
118
+ # Number of seconds after the task completes before the task is performed again.
119
+ attr_reader :batch_interval
120
+
121
+ ##
122
+ # How many seconds to wait for data to be written while sending a batch
123
+ attr_reader :write_timeout
124
+
125
+ ##
126
+ # The maximum number of messages to post at one time
127
+ attr_reader :max_batch_size
128
+
129
+ ##
130
+ # The maximum number of bytes of a single message to include in a batch
131
+ attr_reader :max_message_bytesize
132
+
133
+ ##
134
+ # The maximum number of bytes that will be included in a single POST
135
+ attr_reader :max_batch_bytesize
136
+
137
+ ##
138
+ # The Concurrent executor class to use for asynchronous tasks
139
+ attr_reader :executor_class
140
+
141
+ ##
142
+ # The timer task thread
143
+ attr_reader :timer_task
144
+
145
+
146
+ ### LogDevice API -- write a message to the HTTP device.
147
+ def write( message )
148
+ self.start unless self.running?
149
+ if message.is_a?( Hash )
150
+ message_size = message.to_json.bytesize
151
+ else
152
+ message_size = message.bytesize
153
+ end
154
+ return if ( self.logs_queue_bytesize + message_size ) >= self.max_queue_bytesize
155
+ self.logs_queue_bytesize += message_size
156
+ self.logs_queue.enq( message )
157
+ self.send_logs
158
+ end
159
+
160
+
161
+ ### LogDevice API -- stop the batch thread and close the http connection
162
+ def close
163
+ self.stop
164
+ self.http_client.finish
165
+ rescue IOError
166
+ # ignore it since http session has not yet started.
167
+ end
168
+
169
+
170
+ ### Starts a thread pool with a single thread.
171
+ def start
172
+ self.start_executor
173
+ self.start_timer_task
174
+ end
175
+
176
+
177
+ ### Returns +true+ if the device has started sending messages to the logging endpoint.
178
+ def running?
179
+ return self.executor&.running?
180
+ end
181
+
182
+
183
+ ### Shutdown the executor, which is a pool of single thread
184
+ ### waits 3 seconds for shutdown to complete
185
+ def stop
186
+ return unless self.running?
187
+
188
+ self.timer_task.shutdown if self.timer_task&.running?
189
+ self.executor.shutdown
190
+
191
+ unless self.executor.wait_for_termination( 3 )
192
+ self.executor.halt
193
+ self.executor.wait_for_termination( 3 )
194
+ end
195
+ end
196
+
197
+
198
+ ### Start the background thread that sends messages.
199
+ def start_executor
200
+ @executor = self.executor_class.new
201
+ @executor.auto_terminate = true unless @executor.serialized?
202
+ end
203
+
204
+
205
+ ### Create a timer task that calls that sends logs at regular interval
206
+ def start_timer_task
207
+ @timer_task = Concurrent::TimerTask.execute( execution_interval: self.batch_interval ) do
208
+ self.send_logs
209
+ end
210
+ end
211
+
212
+
213
+ ### Sends a batch of log messages to the logging service. This executes inside
214
+ ### the sending thread.
215
+ def send_logs
216
+ self.executor.post do
217
+ if self.batch_ready?
218
+ # p "Batch ready; sending."
219
+ request = self.make_batch_request
220
+ request.body = self.get_next_log_payload
221
+
222
+ # p "Sending request", request
223
+
224
+ self.http_client.request( request ) do |res|
225
+ p( res ) if $DEBUG
226
+ end
227
+
228
+ self.last_send_time = Concurrent.monotonic_time
229
+ else
230
+ # p "Batch not ready yet."
231
+ end
232
+ end
233
+ end
234
+
235
+
236
+ ### Returns +true+ if a batch of logs is ready to be sent.
237
+ def batch_ready?
238
+ seconds_since_last_send = Concurrent.monotonic_time - self.last_send_time
239
+
240
+ return self.logs_queue.size >= self.max_batch_size ||
241
+ seconds_since_last_send >= self.batch_interval
242
+ end
243
+ alias_method :has_batch_ready?, :batch_ready?
244
+
245
+
246
+ ### Returns a new HTTP request (a subclass of Net::HTTPRequest) suitable for
247
+ ### sending the next batch of logs to the service. Defaults to a POST of JSON data. This
248
+ ### executes inside the sending thread.
249
+ def make_batch_request
250
+ request = Net::HTTP::Post.new( self.endpoint.path )
251
+ request[ 'Content-Type' ] = 'application/json'
252
+
253
+ return request
254
+ end
255
+
256
+
257
+ ### Dequeue pending log messages to send to the service and return them as a
258
+ ### suitably-encoded String. The default is a JSON Array. This executes inside
259
+ ### the sending thread.
260
+ def get_next_log_payload
261
+ buf = []
262
+ count = 0
263
+ bytes = 0
264
+
265
+ # Be conservative so as not to overflow
266
+ max_size = self.max_batch_bytesize - self.max_message_bytesize - 2 # for the outer Array
267
+
268
+ while count < self.max_batch_size && bytes < max_size && !self.logs_queue.empty?
269
+ message = self.logs_queue.deq
270
+ formatted_message = self.format_log_message( message )
271
+ self.logs_queue_bytesize -= message.bytesize
272
+
273
+ count += 1
274
+ bytes += formatted_message.bytesize + 3 # comma and delimiters
275
+
276
+ buf << formatted_message
277
+ end
278
+
279
+ return '[' + buf.join(',') + ']'
280
+ end
281
+
282
+
283
+ ### Returns the given +message+ in whatever format individual log messages are
284
+ ### expected to be in by the service. The default just returns the stringified
285
+ ### +message+. This executes inside the sending thread.
286
+ def format_log_message( message )
287
+ return message.to_s[ 0 ... self.max_message_bytesize ].dump
288
+ end
289
+
290
+
291
+ ### sets up a configured http object ready to instantiate connections
292
+ def http_client
293
+ return @http_client ||= begin
294
+ uri = URI( self.endpoint )
295
+
296
+ http = Net::HTTP.new( uri.host, uri.port )
297
+ http.write_timeout = self.write_timeout
298
+
299
+ if uri.scheme == 'https'
300
+ http.use_ssl = true
301
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
302
+ end
303
+
304
+ http
305
+ end
306
+ end
307
+
308
+
309
+ end # class Loggability::LogDevice::Http
310
+