scout_apm_logging 0.0.13 → 1.0.1

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.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -4
  3. data/.rubocop.yml +1 -1
  4. data/CHANGELOG.md +6 -0
  5. data/NOTICE +4 -0
  6. data/lib/scout_apm/logging/config.rb +5 -131
  7. data/lib/scout_apm/logging/loggers/capture.rb +3 -2
  8. data/lib/scout_apm/logging/loggers/formatter.rb +21 -2
  9. data/lib/scout_apm/logging/loggers/opentelemetry/LICENSE +201 -0
  10. data/lib/scout_apm/logging/loggers/opentelemetry/NOTICE +9 -0
  11. data/lib/scout_apm/logging/loggers/opentelemetry/api/logs/log_record.rb +18 -0
  12. data/lib/scout_apm/logging/loggers/opentelemetry/api/logs/logger.rb +64 -0
  13. data/lib/scout_apm/logging/loggers/opentelemetry/api/logs/logger_provider.rb +31 -0
  14. data/lib/scout_apm/logging/loggers/opentelemetry/api/logs/severity_number.rb +43 -0
  15. data/lib/scout_apm/logging/loggers/opentelemetry/api/logs/version.rb +18 -0
  16. data/lib/scout_apm/logging/loggers/opentelemetry/api/logs.rb +28 -0
  17. data/lib/scout_apm/logging/loggers/opentelemetry/exporter/exporter/otlp/logs_exporter.rb +389 -0
  18. data/lib/scout_apm/logging/loggers/opentelemetry/exporter/exporter/otlp/version.rb +20 -0
  19. data/lib/scout_apm/logging/loggers/opentelemetry/exporter/proto/collector/logs/v1/logs_service_pb.rb +43 -0
  20. data/lib/scout_apm/logging/loggers/opentelemetry/exporter/proto/common/v1/common_pb.rb +58 -0
  21. data/lib/scout_apm/logging/loggers/opentelemetry/exporter/proto/logs/v1/logs_pb.rb +91 -0
  22. data/lib/scout_apm/logging/loggers/opentelemetry/exporter/proto/resource/v1/resource_pb.rb +33 -0
  23. data/lib/scout_apm/logging/loggers/opentelemetry/opentelemetry.rb +62 -0
  24. data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs/export/batch_log_record_processor.rb +225 -0
  25. data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs/export/log_record_exporter.rb +64 -0
  26. data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs/export.rb +34 -0
  27. data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs/log_record.rb +115 -0
  28. data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs/log_record_data.rb +31 -0
  29. data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs/log_record_processor.rb +53 -0
  30. data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs/logger.rb +94 -0
  31. data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs/logger_provider.rb +158 -0
  32. data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs/version.rb +20 -0
  33. data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs.rb +28 -0
  34. data/lib/scout_apm/logging/utils.rb +0 -69
  35. data/lib/scout_apm/logging/version.rb +1 -1
  36. data/lib/scout_apm_logging.rb +2 -11
  37. data/scout_apm_logging.gemspec +7 -0
  38. data/spec/data/config_test_1.yml +0 -1
  39. data/spec/data/mock_config.yml +0 -3
  40. data/spec/integration/rails/lifecycle_spec.rb +57 -23
  41. data/spec/spec_helper.rb +0 -12
  42. data/spec/unit/config_spec.rb +0 -12
  43. data/spec/unit/loggers/capture_spec.rb +0 -6
  44. metadata +126 -39
  45. data/bin/scout_apm_logging_monitor +0 -6
  46. data/lib/scout_apm/logging/monitor/_rails.rb +0 -22
  47. data/lib/scout_apm/logging/monitor/collector/checksum.rb +0 -51
  48. data/lib/scout_apm/logging/monitor/collector/configuration.rb +0 -150
  49. data/lib/scout_apm/logging/monitor/collector/downloader.rb +0 -78
  50. data/lib/scout_apm/logging/monitor/collector/extractor.rb +0 -37
  51. data/lib/scout_apm/logging/monitor/collector/manager.rb +0 -57
  52. data/lib/scout_apm/logging/monitor/monitor.rb +0 -216
  53. data/lib/scout_apm/logging/monitor_manager/manager.rb +0 -162
  54. data/lib/scout_apm/logging/state.rb +0 -69
  55. data/spec/data/empty_logs_config.yml +0 -0
  56. data/spec/data/logs_config.yml +0 -3
  57. data/spec/data/state_file.json +0 -3
  58. data/spec/integration/loggers/capture_spec.rb +0 -68
  59. data/spec/integration/monitor/collector/downloader/will_verify_checksum.rb +0 -49
  60. data/spec/integration/monitor/collector_healthcheck_spec.rb +0 -29
  61. data/spec/integration/monitor/continuous_state_collector_spec.rb +0 -31
  62. data/spec/integration/monitor/previous_collector_setup_spec.rb +0 -45
  63. data/spec/integration/monitor_manager/disable_agent_spec.rb +0 -30
  64. data/spec/integration/monitor_manager/monitor_pid_file_spec.rb +0 -38
  65. data/spec/integration/monitor_manager/single_monitor_spec.rb +0 -53
  66. data/spec/unit/monitor/collector/configuration_spec.rb +0 -64
  67. data/spec/unit/state_spec.rb +0 -20
  68. data/tooling/checksums.rb +0 -106
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright The OpenTelemetry Authors
4
+ #
5
+ # SPDX-License-Identifier: Apache-2.0
6
+
7
+ module ScoutApm
8
+ module Logging
9
+ module Loggers
10
+ module OpenTelemetry
11
+ module Logs
12
+ ## Current OpenTelemetry logs version
13
+ VERSION = '0.1.0'
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright The OpenTelemetry Authors
4
+ #
5
+ # SPDX-License-Identifier: Apache-2.0
6
+
7
+ require_relative 'logs/log_record'
8
+ require_relative 'logs/logger'
9
+ require_relative 'logs/logger_provider'
10
+ require_relative 'logs/severity_number'
11
+
12
+ module ScoutApm
13
+ module Logging
14
+ module Loggers
15
+ module OpenTelemetry
16
+ # The Logs API records a timestamped record with metadata.
17
+ # In OpenTelemetry, any data that is not part of a distributed trace or a
18
+ # metric is a log. For example, events are a specific type of log.
19
+ #
20
+ # This API is provided for logging library authors to build log
21
+ # appenders/bridges. It should NOT be used directly by application
22
+ # developers.
23
+ module Logs
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,389 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright The OpenTelemetry Authors
4
+ #
5
+ # SPDX-License-Identifier: Apache-2.0
6
+
7
+ require 'opentelemetry/common'
8
+ require 'opentelemetry/sdk'
9
+ require 'net/http'
10
+ require 'zlib'
11
+
12
+ require 'google/rpc/status_pb'
13
+
14
+ require_relative '../../proto/common/v1/common_pb'
15
+ require_relative '../../proto/resource/v1/resource_pb'
16
+ require_relative '../../proto/logs/v1/logs_pb'
17
+ require_relative '../../proto/collector/logs/v1/logs_service_pb'
18
+
19
+ module ScoutApm
20
+ module Logging
21
+ module Loggers
22
+ module OpenTelemetry
23
+ module Exporter
24
+ module OTLP
25
+ # An OpenTelemetry log exporter that sends log records over HTTP as Protobuf encoded OTLP ExportLogsServiceRequests.
26
+ class LogsExporter # rubocop:disable Metrics/ClassLength
27
+ SUCCESS = OpenTelemetry::SDK::Logs::Export::SUCCESS
28
+ FAILURE = OpenTelemetry::SDK::Logs::Export::FAILURE
29
+ private_constant(:SUCCESS, :FAILURE)
30
+
31
+ # Default timeouts in seconds.
32
+ KEEP_ALIVE_TIMEOUT = 30
33
+ RETRY_COUNT = 5
34
+ WRITE_TIMEOUT_SUPPORTED = Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.6')
35
+ private_constant(:KEEP_ALIVE_TIMEOUT, :RETRY_COUNT, :WRITE_TIMEOUT_SUPPORTED)
36
+
37
+ ERROR_MESSAGE_INVALID_HEADERS = 'headers must be a String with comma-separated URL Encoded UTF-8 k=v pairs or a Hash'
38
+ private_constant(:ERROR_MESSAGE_INVALID_HEADERS)
39
+
40
+ DEFAULT_USER_AGENT = "OTel-OTLP-Exporter-Ruby/#{OpenTelemetry::Exporter::OTLP::VERSION} Ruby/#{RUBY_VERSION} (#{RUBY_PLATFORM}; #{RUBY_ENGINE}/#{RUBY_ENGINE_VERSION})".freeze
41
+
42
+ def self.ssl_verify_mode
43
+ if ENV.key?('OTEL_RUBY_EXPORTER_OTLP_SSL_VERIFY_PEER')
44
+ OpenSSL::SSL::VERIFY_PEER
45
+ elsif ENV.key?('OTEL_RUBY_EXPORTER_OTLP_SSL_VERIFY_NONE')
46
+ OpenSSL::SSL::VERIFY_NONE
47
+ else
48
+ OpenSSL::SSL::VERIFY_PEER
49
+ end
50
+ end
51
+
52
+ def initialize(endpoint: ::OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_LOGS_ENDPOINT', 'OTEL_EXPORTER_OTLP_ENDPOINT', default: 'http://localhost:4318/v1/logs'),
53
+ certificate_file: ::OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE', 'OTEL_EXPORTER_OTLP_CERTIFICATE'),
54
+ ssl_verify_mode: LogsExporter.ssl_verify_mode,
55
+ headers: ::OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_LOGS_HEADERS', 'OTEL_EXPORTER_OTLP_HEADERS', default: {}),
56
+ compression: ::OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_LOGS_COMPRESSION', 'OTEL_EXPORTER_OTLP_COMPRESSION', default: 'gzip'),
57
+ timeout: ::OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_LOGS_TIMEOUT', 'OTEL_EXPORTER_OTLP_TIMEOUT', default: 10))
58
+ raise ArgumentError, "invalid url for OTLP::Exporter #{endpoint}" unless ::OpenTelemetry::Common::Utilities.valid_url?(endpoint)
59
+ raise ArgumentError, "unsupported compression key #{compression}" unless compression.nil? || %w[gzip none].include?(compression)
60
+
61
+ @uri = if endpoint == ENV['OTEL_EXPORTER_OTLP_ENDPOINT']
62
+ URI.join(endpoint, 'v1/logs')
63
+ else
64
+ URI(endpoint)
65
+ end
66
+
67
+ @http = http_connection(@uri, ssl_verify_mode, certificate_file)
68
+
69
+ @path = @uri.path
70
+ @headers = prepare_headers(headers)
71
+ @timeout = timeout.to_f
72
+ @compression = compression
73
+ @shutdown = false
74
+ end
75
+
76
+ # Called to export sampled {OpenTelemetry::SDK::Logs::LogRecordData} structs.
77
+ #
78
+ # @param [Enumerable<OpenTelemetry::SDK::Logs::LogRecordData>] log_record_data the
79
+ # list of recorded {OpenTelemetry::SDK::Logs::LogRecordData} structs to be
80
+ # exported.
81
+ # @param [optional Numeric] timeout An optional timeout in seconds.
82
+ # @return [Integer] the result of the export.
83
+ def export(log_record_data, timeout: nil)
84
+ OpenTelemetry.logger.error('Logs Exporter tried to export, but it has already shut down') if @shutdown
85
+ return FAILURE if @shutdown
86
+
87
+ send_bytes(encode(log_record_data), timeout: timeout)
88
+ end
89
+
90
+ # Called when {OpenTelemetry::SDK::Logs::LoggerProvider#force_flush} is called, if
91
+ # this exporter is registered to a {OpenTelemetry::SDK::Logs::LoggerProvider}
92
+ # object.
93
+ #
94
+ # @param [optional Numeric] timeout An optional timeout in seconds.
95
+ def force_flush(timeout: nil)
96
+ SUCCESS
97
+ end
98
+
99
+ # Called when {OpenTelemetry::SDK::Logs::LoggerProvider#shutdown} is called, if
100
+ # this exporter is registered to a {OpenTelemetry::SDK::Logs::LoggerProvider}
101
+ # object.
102
+ #
103
+ # @param [optional Numeric] timeout An optional timeout in seconds.
104
+ def shutdown(timeout: nil)
105
+ @shutdown = true
106
+ @http.finish if @http.started?
107
+ SUCCESS
108
+ end
109
+
110
+ private
111
+
112
+ def http_connection(uri, ssl_verify_mode, certificate_file)
113
+ http = Net::HTTP.new(uri.host, uri.port)
114
+ http.use_ssl = uri.scheme == 'https'
115
+ http.verify_mode = ssl_verify_mode
116
+ http.ca_file = certificate_file unless certificate_file.nil?
117
+ http.keep_alive_timeout = KEEP_ALIVE_TIMEOUT
118
+ http
119
+ end
120
+
121
+ # The around_request is a private method that provides an extension
122
+ # point for the exporters network calls. The default behaviour
123
+ # is to not record these operations.
124
+ #
125
+ # An example use case would be to prepend a patch, or extend this class
126
+ # and override this method's behaviour to explicitly record the HTTP request.
127
+ # This would allow you to create log records for your export pipeline.
128
+ def around_request
129
+ ::OpenTelemetry::Common::Utilities.untraced { yield } # rubocop:disable Style/ExplicitBlockArgument
130
+ end
131
+
132
+ def send_bytes(bytes, timeout:) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
133
+ return FAILURE if bytes.nil?
134
+
135
+ request = Net::HTTP::Post.new(@path)
136
+ if @compression == 'gzip'
137
+ request.add_field('Content-Encoding', 'gzip')
138
+ body = Zlib.gzip(bytes)
139
+ else
140
+ body = bytes
141
+ end
142
+
143
+ request.body = body
144
+ request.add_field('Content-Type', 'application/x-protobuf')
145
+ @headers.each { |key, value| request.add_field(key, value) }
146
+
147
+ retry_count = 0
148
+ timeout ||= @timeout
149
+ start_time = ::OpenTelemetry::Common::Utilities.timeout_timestamp
150
+
151
+ around_request do
152
+ remaining_timeout = ::OpenTelemetry::Common::Utilities.maybe_timeout(timeout, start_time)
153
+ return FAILURE if remaining_timeout.zero?
154
+
155
+ @http.open_timeout = remaining_timeout
156
+ @http.read_timeout = remaining_timeout
157
+ @http.write_timeout = remaining_timeout if WRITE_TIMEOUT_SUPPORTED
158
+ @http.start unless @http.started?
159
+ response = measure_request_duration { @http.request(request) }
160
+
161
+ case response
162
+ when Net::HTTPOK
163
+ response.body # Read and discard body
164
+ SUCCESS
165
+ when Net::HTTPServiceUnavailable, Net::HTTPTooManyRequests
166
+ response.body # Read and discard body
167
+ redo if backoff?(retry_after: response['Retry-After'], retry_count: retry_count += 1, reason: response.code)
168
+ FAILURE
169
+ when Net::HTTPRequestTimeOut, Net::HTTPGatewayTimeOut, Net::HTTPBadGateway
170
+ response.body # Read and discard body
171
+ redo if backoff?(retry_count: retry_count += 1, reason: response.code)
172
+ FAILURE
173
+ when Net::HTTPNotFound
174
+ OpenTelemetry.handle_error(message: "OTLP exporter received http.code=404 for uri: '#{@path}'")
175
+ FAILURE
176
+ when Net::HTTPBadRequest, Net::HTTPClientError, Net::HTTPServerError
177
+ log_status(response.body)
178
+ FAILURE
179
+ when Net::HTTPRedirection
180
+ @http.finish
181
+ handle_redirect(response['location'])
182
+ redo if backoff?(retry_after: 0, retry_count: retry_count += 1, reason: response.code)
183
+ else
184
+ @http.finish
185
+ FAILURE
186
+ end
187
+ rescue Net::OpenTimeout, Net::ReadTimeout
188
+ retry if backoff?(retry_count: retry_count += 1, reason: 'timeout')
189
+ return FAILURE
190
+ rescue OpenSSL::SSL::SSLError
191
+ retry if backoff?(retry_count: retry_count += 1, reason: 'openssl_error')
192
+ return FAILURE
193
+ rescue SocketError
194
+ retry if backoff?(retry_count: retry_count += 1, reason: 'socket_error')
195
+ return FAILURE
196
+ rescue SystemCallError => e
197
+ retry if backoff?(retry_count: retry_count += 1, reason: e.class.name)
198
+ return FAILURE
199
+ rescue EOFError
200
+ retry if backoff?(retry_count: retry_count += 1, reason: 'eof_error')
201
+ return FAILURE
202
+ rescue Zlib::DataError
203
+ retry if backoff?(retry_count: retry_count += 1, reason: 'zlib_error')
204
+ return FAILURE
205
+ rescue StandardError => e
206
+ OpenTelemetry.handle_error(exception: e, message: 'unexpected error in OTLP::Exporter#send_bytes')
207
+ return FAILURE
208
+ end
209
+ ensure
210
+ # Reset timeouts to defaults for the next call.
211
+ @http.open_timeout = @timeout
212
+ @http.read_timeout = @timeout
213
+ @http.write_timeout = @timeout if WRITE_TIMEOUT_SUPPORTED
214
+ end
215
+
216
+ def handle_redirect(location)
217
+ # TODO: figure out destination and reinitialize @http and @path
218
+ end
219
+
220
+ def log_status(body)
221
+ status = Google::Rpc::Status.decode(body)
222
+ details = status.details.map do |detail|
223
+ klass_or_nil = ::Google::Protobuf::DescriptorPool.generated_pool.lookup(detail.type_name).msgclass
224
+ detail.unpack(klass_or_nil) if klass_or_nil
225
+ end.compact
226
+ OpenTelemetry.handle_error(message: "OTLP exporter received rpc.Status{message=#{status.message}, details=#{details}}")
227
+ rescue StandardError => e
228
+ OpenTelemetry.handle_error(exception: e, message: 'unexpected error decoding rpc.Status in OTLP::Exporter#log_status')
229
+ end
230
+
231
+ def measure_request_duration
232
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
233
+ begin
234
+ yield
235
+ ensure
236
+ stop = Process.clock_gettime(Process::CLOCK_MONOTONIC)
237
+ 1000.0 * (stop - start)
238
+ end
239
+ end
240
+
241
+ def backoff?(retry_count:, reason:, retry_after: nil) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
242
+ return false if retry_count > RETRY_COUNT
243
+
244
+ sleep_interval = nil
245
+ unless retry_after.nil?
246
+ sleep_interval =
247
+ begin
248
+ Integer(retry_after)
249
+ rescue ArgumentError
250
+ nil
251
+ end
252
+ sleep_interval ||=
253
+ begin
254
+ Time.httpdate(retry_after) - Time.now
255
+ rescue # rubocop:disable Style/RescueStandardError
256
+ nil
257
+ end
258
+ sleep_interval = nil unless sleep_interval&.positive?
259
+ end
260
+ sleep_interval ||= rand(2**retry_count)
261
+
262
+ sleep(sleep_interval)
263
+ true
264
+ end
265
+
266
+ def encode(log_record_data) # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
267
+ Opentelemetry::Proto::Collector::Logs::V1::ExportLogsServiceRequest.encode(
268
+ Opentelemetry::Proto::Collector::Logs::V1::ExportLogsServiceRequest.new(
269
+ resource_logs: log_record_data
270
+ .group_by(&:resource)
271
+ .map do |resource, log_record_datas|
272
+ Opentelemetry::Proto::Logs::V1::ResourceLogs.new(
273
+ resource: Opentelemetry::Proto::Resource::V1::Resource.new(
274
+ attributes: resource.attribute_enumerator.map { |key, value| as_otlp_key_value(key, value) }
275
+ ),
276
+ scope_logs: log_record_datas
277
+ .group_by(&:instrumentation_scope)
278
+ .map do |il, lrd|
279
+ Opentelemetry::Proto::Logs::V1::ScopeLogs.new(
280
+ scope: Opentelemetry::Proto::Common::V1::InstrumentationScope.new(
281
+ name: il.name,
282
+ version: il.version
283
+ ),
284
+ log_records: lrd.map { |lr| as_otlp_log_record(lr) }
285
+ )
286
+ end
287
+ )
288
+ end
289
+ )
290
+ )
291
+ rescue StandardError => e
292
+ OpenTelemetry.handle_error(exception: e, message: 'unexpected error in OTLP::Exporter#encode')
293
+ nil
294
+ end
295
+
296
+ def as_otlp_log_record(log_record_data)
297
+ Opentelemetry::Proto::Logs::V1::LogRecord.new(
298
+ time_unix_nano: log_record_data.timestamp,
299
+ observed_time_unix_nano: log_record_data.observed_timestamp,
300
+ severity_number: as_otlp_severity_number(log_record_data.severity_number),
301
+ severity_text: log_record_data.severity_text,
302
+ body: as_otlp_any_value(log_record_data.body),
303
+ attributes: log_record_data.attributes&.map { |k, v| as_otlp_key_value(k, v) },
304
+ dropped_attributes_count: log_record_data.total_recorded_attributes - log_record_data.attributes&.size.to_i,
305
+ flags: log_record_data.trace_flags.instance_variable_get(:@flags),
306
+ trace_id: log_record_data.trace_id,
307
+ span_id: log_record_data.span_id
308
+ )
309
+ end
310
+
311
+ def as_otlp_key_value(key, value)
312
+ Opentelemetry::Proto::Common::V1::KeyValue.new(key: key, value: as_otlp_any_value(value))
313
+ rescue Encoding::UndefinedConversionError => e
314
+ encoded_value = value.encode('UTF-8', invalid: :replace, undef: :replace, replace: '�')
315
+ OpenTelemetry.handle_error(exception: e, message: "encoding error for key #{key} and value #{encoded_value}")
316
+ Opentelemetry::Proto::Common::V1::KeyValue.new(key: key, value: as_otlp_any_value('Encoding Error'))
317
+ end
318
+
319
+ def as_otlp_any_value(value)
320
+ result = Opentelemetry::Proto::Common::V1::AnyValue.new
321
+ case value
322
+ when String
323
+ result.string_value = value
324
+ when Integer
325
+ result.int_value = value
326
+ when Float
327
+ result.double_value = value
328
+ when true, false
329
+ result.bool_value = value
330
+ when Array
331
+ values = value.map { |element| as_otlp_any_value(element) }
332
+ result.array_value = Opentelemetry::Proto::Common::V1::ArrayValue.new(values: values)
333
+ end
334
+ result
335
+ end
336
+
337
+ # TODO: maybe don't translate the severity number, but translate the severity text into
338
+ # the number if the number is nil? Poss. change to allow for adding your own
339
+ # otel values?
340
+ def as_otlp_severity_number(severity_number)
341
+ case severity_number
342
+ when 0 then Opentelemetry::Proto::Logs::V1::SeverityNumber::SEVERITY_NUMBER_DEBUG
343
+ when 1 then Opentelemetry::Proto::Logs::V1::SeverityNumber::SEVERITY_NUMBER_INFO
344
+ when 2 then Opentelemetry::Proto::Logs::V1::SeverityNumber::SEVERITY_NUMBER_WARN
345
+ when 3 then Opentelemetry::Proto::Logs::V1::SeverityNumber::SEVERITY_NUMBER_ERROR
346
+ when 4 then Opentelemetry::Proto::Logs::V1::SeverityNumber::SEVERITY_NUMBER_FATAL
347
+ when 5 then Opentelemetry::Proto::Logs::V1::SeverityNumber::SEVERITY_NUMBER_UNSPECIFIED
348
+ end
349
+ end
350
+
351
+ def prepare_headers(config_headers)
352
+ headers = case config_headers
353
+ when String then parse_headers(config_headers)
354
+ when Hash then config_headers.dup
355
+ else
356
+ raise ArgumentError, ERROR_MESSAGE_INVALID_HEADERS
357
+ end
358
+
359
+ headers['User-Agent'] = "#{headers.fetch('User-Agent', '')} #{DEFAULT_USER_AGENT}".strip
360
+
361
+ headers
362
+ end
363
+
364
+ def parse_headers(raw)
365
+ entries = raw.split(',')
366
+ raise ArgumentError, ERROR_MESSAGE_INVALID_HEADERS if entries.empty?
367
+
368
+ entries.each_with_object({}) do |entry, headers|
369
+ k, v = entry.split('=', 2).map(&CGI.method(:unescape))
370
+ begin
371
+ k = k.to_s.strip
372
+ v = v.to_s.strip
373
+ rescue Encoding::CompatibilityError
374
+ raise ArgumentError, ERROR_MESSAGE_INVALID_HEADERS
375
+ rescue ArgumentError => e
376
+ raise e, ERROR_MESSAGE_INVALID_HEADERS
377
+ end
378
+ raise ArgumentError, ERROR_MESSAGE_INVALID_HEADERS if k.empty? || v.empty?
379
+
380
+ headers[k] = v
381
+ end
382
+ end
383
+ end
384
+ end
385
+ end
386
+ end
387
+ end
388
+ end
389
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright The OpenTelemetry Authors
4
+ #
5
+ # SPDX-License-Identifier: Apache-2.0
6
+
7
+ module ScoutApm
8
+ module Logging
9
+ module Loggers
10
+ module OpenTelemetry
11
+ module Exporter
12
+ module OTLP
13
+ ## Current OpenTelemetry OTLP logs exporter version
14
+ VERSION = '0.26.3'
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
4
+ # source: opentelemetry/proto/collector/logs/v1/logs_service.proto
5
+
6
+ require 'google/protobuf'
7
+
8
+ require_relative '../../../logs/v1/logs_pb'
9
+
10
+ Google::Protobuf::DescriptorPool.generated_pool.build do
11
+ add_file("scout/opentelemetry/proto/collector/logs/v1/logs_service.proto", :syntax => :proto3) do
12
+ add_message "scout_apm.logging.loggers.opentelemetry.proto.collector.logs.v1.ExportLogsServiceRequest" do
13
+ repeated :resource_logs, :message, 1, "scout_apm.logging.loggers.opentelemetry.proto.logs.v1.ResourceLogs"
14
+ end
15
+ add_message "scout_apm.logging.loggers.opentelemetry.proto.collector.logs.v1.ExportLogsServiceResponse" do
16
+ optional :partial_success, :message, 1, "scout_apm.logging.loggers.opentelemetry.proto.collector.logs.v1.ExportLogsPartialSuccess"
17
+ end
18
+ add_message "scout_apm.logging.loggers.opentelemetry.proto.collector.logs.v1.ExportLogsPartialSuccess" do
19
+ optional :rejected_log_records, :int64, 1
20
+ optional :error_message, :string, 2
21
+ end
22
+ end
23
+ end
24
+
25
+ module ScoutApm
26
+ module Logging
27
+ module Loggers
28
+ module Opentelemetry
29
+ module Proto
30
+ module Collector
31
+ module Logs
32
+ module V1
33
+ ExportLogsServiceRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("scout_apm.logging.loggers.opentelemetry.proto.collector.logs.v1.ExportLogsServiceRequest").msgclass
34
+ ExportLogsServiceResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("scout_apm.logging.loggers.opentelemetry.proto.collector.logs.v1.ExportLogsServiceResponse").msgclass
35
+ ExportLogsPartialSuccess = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("scout_apm.logging.loggers.opentelemetry.proto.collector.logs.v1.ExportLogsPartialSuccess").msgclass
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
4
+ # source: opentelemetry/proto/common/v1/common.proto
5
+
6
+ require 'google/protobuf'
7
+
8
+ Google::Protobuf::DescriptorPool.generated_pool.build do
9
+ add_file("scout/opentelemetry/proto/common/v1/common.proto", :syntax => :proto3) do
10
+ add_message "scout_apm.logging.loggers.opentelemetry.proto.common.v1.AnyValue" do
11
+ oneof :value do
12
+ optional :string_value, :string, 1
13
+ optional :bool_value, :bool, 2
14
+ optional :int_value, :int64, 3
15
+ optional :double_value, :double, 4
16
+ optional :array_value, :message, 5, "scout_apm.logging.loggers.opentelemetry.proto.common.v1.ArrayValue"
17
+ optional :kvlist_value, :message, 6, "scout_apm.logging.loggers.opentelemetry.proto.common.v1.KeyValueList"
18
+ optional :bytes_value, :bytes, 7
19
+ end
20
+ end
21
+ add_message "scout_apm.logging.loggers.opentelemetry.proto.common.v1.ArrayValue" do
22
+ repeated :values, :message, 1, "scout_apm.logging.loggers.opentelemetry.proto.common.v1.AnyValue"
23
+ end
24
+ add_message "scout_apm.logging.loggers.opentelemetry.proto.common.v1.KeyValueList" do
25
+ repeated :values, :message, 1, "scout_apm.logging.loggers.opentelemetry.proto.common.v1.KeyValue"
26
+ end
27
+ add_message "scout_apm.logging.loggers.opentelemetry.proto.common.v1.KeyValue" do
28
+ optional :key, :string, 1
29
+ optional :value, :message, 2, "scout_apm.logging.loggers.opentelemetry.proto.common.v1.AnyValue"
30
+ end
31
+ add_message "scout_apm.logging.loggers.opentelemetry.proto.common.v1.InstrumentationScope" do
32
+ optional :name, :string, 1
33
+ optional :version, :string, 2
34
+ repeated :attributes, :message, 3, "scout_apm.logging.loggers.opentelemetry.proto.common.v1.KeyValue"
35
+ optional :dropped_attributes_count, :uint32, 4
36
+ end
37
+ end
38
+ end
39
+
40
+ module ScoutApm
41
+ module Logging
42
+ module Loggers
43
+ module Opentelemetry
44
+ module Proto
45
+ module Common
46
+ module V1
47
+ AnyValue = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("scout_apm.logging.loggers.opentelemetry.proto.common.v1.AnyValue").msgclass
48
+ ArrayValue = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("scout_apm.logging.loggers.opentelemetry.proto.common.v1.ArrayValue").msgclass
49
+ KeyValueList = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("scout_apm.logging.loggers.opentelemetry.proto.common.v1.KeyValueList").msgclass
50
+ KeyValue = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("scout_apm.logging.loggers.opentelemetry.proto.common.v1.KeyValue").msgclass
51
+ InstrumentationScope = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("scout_apm.logging.loggers.opentelemetry.proto.common.v1.InstrumentationScope").msgclass
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
4
+ # source: opentelemetry/proto/logs/v1/logs.proto
5
+
6
+ require 'google/protobuf'
7
+
8
+ require_relative '../../common/v1/common_pb'
9
+ require_relative '../../resource/v1/resource_pb'
10
+
11
+ Google::Protobuf::DescriptorPool.generated_pool.build do
12
+ add_file("scout/opentelemetry/proto/logs/v1/logs.proto", :syntax => :proto3) do
13
+ add_message "scout_apm.logging.loggers.opentelemetry.proto.logs.v1.LogsData" do
14
+ repeated :resource_logs, :message, 1, "scout_apm.logging.loggers.opentelemetry.proto.logs.v1.ResourceLogs"
15
+ end
16
+ add_message "scout_apm.logging.loggers.opentelemetry.proto.logs.v1.ResourceLogs" do
17
+ optional :resource, :message, 1, "scout_apm.logging.loggers.opentelemetry.proto.resource.v1.Resource"
18
+ repeated :scope_logs, :message, 2, "scout_apm.logging.loggers.opentelemetry.proto.logs.v1.ScopeLogs"
19
+ optional :schema_url, :string, 3
20
+ end
21
+ add_message "scout_apm.logging.loggers.opentelemetry.proto.logs.v1.ScopeLogs" do
22
+ optional :scope, :message, 1, "scout_apm.logging.loggers.opentelemetry.proto.common.v1.InstrumentationScope"
23
+ repeated :log_records, :message, 2, "scout_apm.logging.loggers.opentelemetry.proto.logs.v1.LogRecord"
24
+ optional :schema_url, :string, 3
25
+ end
26
+ add_message "scout_apm.logging.loggers.opentelemetry.proto.logs.v1.LogRecord" do
27
+ optional :time_unix_nano, :fixed64, 1
28
+ optional :observed_time_unix_nano, :fixed64, 11
29
+ optional :severity_number, :enum, 2, "scout_apm.logging.loggers.opentelemetry.proto.logs.v1.SeverityNumber"
30
+ optional :severity_text, :string, 3
31
+ optional :body, :message, 5, "scout_apm.logging.loggers.opentelemetry.proto.common.v1.AnyValue"
32
+ repeated :attributes, :message, 6, "scout_apm.logging.loggers.opentelemetry.proto.common.v1.KeyValue"
33
+ optional :dropped_attributes_count, :uint32, 7
34
+ optional :flags, :fixed32, 8
35
+ optional :trace_id, :bytes, 9
36
+ optional :span_id, :bytes, 10
37
+ end
38
+ add_enum "scout_apm.logging.loggers.opentelemetry.proto.logs.v1.SeverityNumber" do
39
+ value :SEVERITY_NUMBER_UNSPECIFIED, 0
40
+ value :SEVERITY_NUMBER_TRACE, 1
41
+ value :SEVERITY_NUMBER_TRACE2, 2
42
+ value :SEVERITY_NUMBER_TRACE3, 3
43
+ value :SEVERITY_NUMBER_TRACE4, 4
44
+ value :SEVERITY_NUMBER_DEBUG, 5
45
+ value :SEVERITY_NUMBER_DEBUG2, 6
46
+ value :SEVERITY_NUMBER_DEBUG3, 7
47
+ value :SEVERITY_NUMBER_DEBUG4, 8
48
+ value :SEVERITY_NUMBER_INFO, 9
49
+ value :SEVERITY_NUMBER_INFO2, 10
50
+ value :SEVERITY_NUMBER_INFO3, 11
51
+ value :SEVERITY_NUMBER_INFO4, 12
52
+ value :SEVERITY_NUMBER_WARN, 13
53
+ value :SEVERITY_NUMBER_WARN2, 14
54
+ value :SEVERITY_NUMBER_WARN3, 15
55
+ value :SEVERITY_NUMBER_WARN4, 16
56
+ value :SEVERITY_NUMBER_ERROR, 17
57
+ value :SEVERITY_NUMBER_ERROR2, 18
58
+ value :SEVERITY_NUMBER_ERROR3, 19
59
+ value :SEVERITY_NUMBER_ERROR4, 20
60
+ value :SEVERITY_NUMBER_FATAL, 21
61
+ value :SEVERITY_NUMBER_FATAL2, 22
62
+ value :SEVERITY_NUMBER_FATAL3, 23
63
+ value :SEVERITY_NUMBER_FATAL4, 24
64
+ end
65
+ add_enum "scout_apm.logging.loggers.opentelemetry.proto.logs.v1.LogRecordFlags" do
66
+ value :LOG_RECORD_FLAGS_DO_NOT_USE, 0
67
+ value :LOG_RECORD_FLAGS_TRACE_FLAGS_MASK, 255
68
+ end
69
+ end
70
+ end
71
+
72
+ module ScoutApm
73
+ module Logging
74
+ module Loggers
75
+ module Opentelemetry
76
+ module Proto
77
+ module Logs
78
+ module V1
79
+ LogsData = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("scout_apm.logging.loggers.opentelemetry.proto.logs.v1.LogsData").msgclass
80
+ ResourceLogs = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("scout_apm.logging.loggers.opentelemetry.proto.logs.v1.ResourceLogs").msgclass
81
+ ScopeLogs = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("scout_apm.logging.loggers.opentelemetry.proto.logs.v1.ScopeLogs").msgclass
82
+ LogRecord = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("scout_apm.logging.loggers.opentelemetry.proto.logs.v1.LogRecord").msgclass
83
+ SeverityNumber = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("scout_apm.logging.loggers.opentelemetry.proto.logs.v1.SeverityNumber").enummodule
84
+ LogRecordFlags = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("scout_apm.logging.loggers.opentelemetry.proto.logs.v1.LogRecordFlags").enummodule
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end