scout_apm_logging 0.0.12 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +0 -4
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +8 -0
- data/NOTICE +4 -0
- data/lib/scout_apm/logging/config.rb +5 -131
- data/lib/scout_apm/logging/loggers/capture.rb +8 -6
- data/lib/scout_apm/logging/loggers/formatter.rb +21 -2
- data/lib/scout_apm/logging/loggers/opentelemetry/LICENSE +201 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/NOTICE +9 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/api/logs/log_record.rb +18 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/api/logs/logger.rb +64 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/api/logs/logger_provider.rb +31 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/api/logs/severity_number.rb +43 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/api/logs/version.rb +18 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/api/logs.rb +28 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/exporter/exporter/otlp/logs_exporter.rb +389 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/exporter/exporter/otlp/version.rb +20 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/exporter/proto/collector/logs/v1/logs_service_pb.rb +43 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/exporter/proto/common/v1/common_pb.rb +58 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/exporter/proto/logs/v1/logs_pb.rb +91 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/exporter/proto/resource/v1/resource_pb.rb +33 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/opentelemetry.rb +62 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs/export/batch_log_record_processor.rb +225 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs/export/log_record_exporter.rb +64 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs/export.rb +34 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs/log_record.rb +115 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs/log_record_data.rb +31 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs/log_record_processor.rb +53 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs/logger.rb +94 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs/logger_provider.rb +158 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs/version.rb +20 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs.rb +28 -0
- data/lib/scout_apm/logging/loggers/patches/rails_logger.rb +17 -0
- data/lib/scout_apm/logging/loggers/proxy.rb +22 -5
- data/lib/scout_apm/logging/loggers/swaps/rails.rb +4 -12
- data/lib/scout_apm/logging/loggers/swaps/scout.rb +2 -10
- data/lib/scout_apm/logging/loggers/swaps/sidekiq.rb +2 -6
- data/lib/scout_apm/logging/utils.rb +0 -69
- data/lib/scout_apm/logging/version.rb +1 -1
- data/lib/scout_apm_logging.rb +3 -12
- data/scout_apm_logging.gemspec +7 -0
- data/spec/data/config_test_1.yml +0 -1
- data/spec/data/mock_config.yml +0 -3
- data/spec/integration/rails/lifecycle_spec.rb +57 -23
- data/spec/spec_helper.rb +0 -12
- data/spec/unit/config_spec.rb +0 -12
- data/spec/unit/loggers/capture_spec.rb +0 -6
- metadata +127 -39
- data/bin/scout_apm_logging_monitor +0 -6
- data/lib/scout_apm/logging/monitor/_rails.rb +0 -22
- data/lib/scout_apm/logging/monitor/collector/checksum.rb +0 -51
- data/lib/scout_apm/logging/monitor/collector/configuration.rb +0 -150
- data/lib/scout_apm/logging/monitor/collector/downloader.rb +0 -78
- data/lib/scout_apm/logging/monitor/collector/extractor.rb +0 -37
- data/lib/scout_apm/logging/monitor/collector/manager.rb +0 -57
- data/lib/scout_apm/logging/monitor/monitor.rb +0 -216
- data/lib/scout_apm/logging/monitor_manager/manager.rb +0 -162
- data/lib/scout_apm/logging/state.rb +0 -69
- data/spec/data/empty_logs_config.yml +0 -0
- data/spec/data/logs_config.yml +0 -3
- data/spec/data/state_file.json +0 -3
- data/spec/integration/loggers/capture_spec.rb +0 -68
- data/spec/integration/monitor/collector/downloader/will_verify_checksum.rb +0 -49
- data/spec/integration/monitor/collector_healthcheck_spec.rb +0 -29
- data/spec/integration/monitor/continuous_state_collector_spec.rb +0 -31
- data/spec/integration/monitor/previous_collector_setup_spec.rb +0 -45
- data/spec/integration/monitor_manager/disable_agent_spec.rb +0 -30
- data/spec/integration/monitor_manager/monitor_pid_file_spec.rb +0 -38
- data/spec/integration/monitor_manager/single_monitor_spec.rb +0 -53
- data/spec/unit/monitor/collector/configuration_spec.rb +0 -64
- data/spec/unit/state_spec.rb +0 -20
- data/tooling/checksums.rb +0 -106
@@ -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
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
4
|
+
# source: opentelemetry/proto/resource/v1/resource.proto
|
5
|
+
|
6
|
+
require 'google/protobuf'
|
7
|
+
|
8
|
+
require_relative '../../common/v1/common_pb'
|
9
|
+
|
10
|
+
Google::Protobuf::DescriptorPool.generated_pool.build do
|
11
|
+
add_file("scout/opentelemetry/proto/resource/v1/resource.proto", :syntax => :proto3) do
|
12
|
+
add_message "scout_apm.logging.loggers.opentelemetry.proto.resource.v1.Resource" do
|
13
|
+
repeated :attributes, :message, 1, "scout_apm.logging.loggers.opentelemetry.proto.common.v1.KeyValue"
|
14
|
+
optional :dropped_attributes_count, :uint32, 2
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
module ScoutApm
|
20
|
+
module Logging
|
21
|
+
module Loggers
|
22
|
+
module Opentelemetry
|
23
|
+
module Proto
|
24
|
+
module Resource
|
25
|
+
module V1
|
26
|
+
Resource = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("scout_apm.logging.loggers.opentelemetry.proto.resource.v1.Resource").msgclass
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'logger'
|
3
|
+
require 'opentelemetry'
|
4
|
+
require 'opentelemetry/sdk'
|
5
|
+
|
6
|
+
require_relative 'api/logs'
|
7
|
+
require_relative 'sdk/logs'
|
8
|
+
require_relative 'exporter/exporter/otlp/version'
|
9
|
+
require_relative 'exporter/exporter/otlp/logs_exporter'
|
10
|
+
|
11
|
+
module ScoutApm
|
12
|
+
module Logging
|
13
|
+
module Loggers
|
14
|
+
module OpenTelemetry
|
15
|
+
class << self
|
16
|
+
# Overwritten on setup to be the internal logger.
|
17
|
+
# @return [Object, Logger] configured Logger or a default STDOUT Logger.
|
18
|
+
def logger
|
19
|
+
@logger ||= ::Logger.new($stdout, level: ENV['OTEL_LOG_LEVEL'] || ::Logger::INFO)
|
20
|
+
end
|
21
|
+
|
22
|
+
# @return [Callable] configured error handler or a default that logs the
|
23
|
+
# exception and message at ERROR level.
|
24
|
+
def error_handler
|
25
|
+
@error_handler ||= ->(exception: nil, message: nil) { logger.error("OpenTelemetry error: #{[message, exception&.message, exception&.backtrace&.first].compact.join(' - ')}") }
|
26
|
+
end
|
27
|
+
|
28
|
+
# Handles an error by calling the configured error_handler.
|
29
|
+
#
|
30
|
+
# @param [optional Exception] exception The exception to be handled
|
31
|
+
# @param [optional String] message An error message.
|
32
|
+
def handle_error(exception: nil, message: nil)
|
33
|
+
error_handler.call(exception: exception, message: message)
|
34
|
+
end
|
35
|
+
|
36
|
+
def logger_provider=(logger_provider)
|
37
|
+
@logger_provider = logger_provider
|
38
|
+
end
|
39
|
+
|
40
|
+
def logger_provider
|
41
|
+
@logger_provider
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.setup(context)
|
46
|
+
@logger = context.logger
|
47
|
+
|
48
|
+
exporter = OpenTelemetry::Exporter::OTLP::LogsExporter.new(endpoint: context.config.value('logs_reporting_endpoint_http'))
|
49
|
+
processor = OpenTelemetry::SDK::Logs::Export::BatchLogRecordProcessor.new(exporter)
|
50
|
+
ScoutApm::Logging::Loggers::OpenTelemetry.logger_provider = OpenTelemetry::SDK::Logs::LoggerProvider.new(resource: scout_resource(context))
|
51
|
+
ScoutApm::Logging::Loggers::OpenTelemetry.logger_provider.add_log_record_processor(processor)
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.scout_resource(context)
|
55
|
+
our_resources = ::OpenTelemetry::SDK::Resources::Resource.create({'telemetryhub.key' => context.config.value('logs_ingest_key')})
|
56
|
+
default_resources = ::OpenTelemetry::SDK::Resources::Resource.default
|
57
|
+
default_resources.merge(our_resources)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs/export/batch_log_record_processor.rb
ADDED
@@ -0,0 +1,225 @@
|
|
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 SDK
|
12
|
+
module Logs
|
13
|
+
module Export
|
14
|
+
# WARNING - The spec has some differences from the LogRecord version of this processor
|
15
|
+
# Implementation of the duck type LogRecordProcessor that batches
|
16
|
+
# log records exported by the SDK then pushes them to the exporter
|
17
|
+
# pipeline.
|
18
|
+
#
|
19
|
+
# Typically, the BatchLogRecordProcessor will be more suitable for
|
20
|
+
# production environments than the SimpleLogRecordProcessor.
|
21
|
+
class BatchLogRecordProcessor < LogRecordProcessor # rubocop:disable Metrics/ClassLength
|
22
|
+
# Returns a new instance of the {BatchLogRecordProcessor}.
|
23
|
+
#
|
24
|
+
# @param [LogRecordExporter] exporter The (duck type) LogRecordExporter to where the
|
25
|
+
# recorded LogRecords are pushed after batching.
|
26
|
+
# @param [Numeric] exporter_timeout The maximum allowed time to export data.
|
27
|
+
# Defaults to the value of the OTEL_BLRP_EXPORT_TIMEOUT
|
28
|
+
# environment variable, if set, or 30,000 (30 seconds).
|
29
|
+
# @param [Numeric] schedule_delay the delay interval between two consecutive exports.
|
30
|
+
# Defaults to the value of the OTEL_BLRP_SCHEDULE_DELAY environment
|
31
|
+
# variable, if set, or 1,000 (1 second).
|
32
|
+
# @param [Integer] max_queue_size the maximum queue size in log records.
|
33
|
+
# Defaults to the value of the OTEL_BLRP_MAX_QUEUE_SIZE environment
|
34
|
+
# variable, if set, or 2048.
|
35
|
+
# @param [Integer] max_export_batch_size the maximum batch size in log records.
|
36
|
+
# Defaults to the value of the OTEL_BLRP_MAX_EXPORT_BATCH_SIZE environment
|
37
|
+
# variable, if set, or 512.
|
38
|
+
#
|
39
|
+
# @return a new instance of the {BatchLogRecordProcessor}.
|
40
|
+
def initialize(exporter,
|
41
|
+
exporter_timeout: Float(ENV.fetch('OTEL_BLRP_EXPORT_TIMEOUT', 30_000)),
|
42
|
+
schedule_delay: Float(ENV.fetch('OTEL_BLRP_SCHEDULE_DELAY', 1000)),
|
43
|
+
max_queue_size: Integer(ENV.fetch('OTEL_BLRP_MAX_QUEUE_SIZE', 2048)),
|
44
|
+
max_export_batch_size: Integer(ENV.fetch('OTEL_BLRP_MAX_EXPORT_BATCH_SIZE', 256)),
|
45
|
+
start_thread_on_boot: String(ENV['OTEL_RUBY_BLRP_START_THREAD_ON_BOOT']) !~ /false/i)
|
46
|
+
|
47
|
+
unless max_export_batch_size <= max_queue_size
|
48
|
+
raise ArgumentError,
|
49
|
+
'max_export_batch_size much be less than or equal to max_queue_size'
|
50
|
+
end
|
51
|
+
|
52
|
+
unless ::OpenTelemetry::Common::Utilities.valid_exporter?(exporter)
|
53
|
+
raise ArgumentError,
|
54
|
+
"exporter #{exporter.inspect} does not appear to be a valid exporter"
|
55
|
+
end
|
56
|
+
|
57
|
+
@exporter = exporter
|
58
|
+
@exporter_timeout_seconds = exporter_timeout / 1000.0
|
59
|
+
@mutex = Mutex.new
|
60
|
+
@export_mutex = Mutex.new
|
61
|
+
@condition = ConditionVariable.new
|
62
|
+
@keep_running = true
|
63
|
+
@stopped = false
|
64
|
+
@delay_seconds = schedule_delay / 1000.0
|
65
|
+
@max_queue_size = max_queue_size
|
66
|
+
@batch_size = max_export_batch_size
|
67
|
+
@log_records = []
|
68
|
+
@pid = nil
|
69
|
+
@thread = nil
|
70
|
+
reset_on_fork(restart_thread: start_thread_on_boot)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Adds a log record to the batch. Thread-safe; may block on lock.
|
74
|
+
def on_emit(log_record, _context)
|
75
|
+
return if @stopped
|
76
|
+
|
77
|
+
lock do
|
78
|
+
reset_on_fork
|
79
|
+
n = log_records.size + 1 - max_queue_size
|
80
|
+
if n.positive?
|
81
|
+
log_records.shift(n)
|
82
|
+
report_dropped_log_records(n, reason: 'buffer-full')
|
83
|
+
end
|
84
|
+
log_records << log_record
|
85
|
+
@condition.signal if log_records.size > batch_size
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Export all emitted log records that have not yet been exported to
|
90
|
+
# the configured `Exporter`.
|
91
|
+
#
|
92
|
+
# This method should only be called in cases where it is absolutely
|
93
|
+
# necessary, such as when using some FaaS providers that may suspend
|
94
|
+
# the process after an invocation, but before the `Processor` exports
|
95
|
+
# the completed log records.
|
96
|
+
#
|
97
|
+
# @param [optional Numeric] timeout An optional timeout in seconds.
|
98
|
+
# @return [Integer] SUCCESS if no error occurred, FAILURE if a
|
99
|
+
# non-specific failure occurred, TIMEOUT if a timeout occurred.
|
100
|
+
def force_flush(timeout: nil)
|
101
|
+
start_time = ::OpenTelemetry::Common::Utilities.timeout_timestamp
|
102
|
+
|
103
|
+
snapshot = lock do
|
104
|
+
reset_on_fork if @keep_running
|
105
|
+
log_records.shift(log_records.size)
|
106
|
+
end
|
107
|
+
|
108
|
+
until snapshot.empty?
|
109
|
+
remaining_timeout = ::OpenTelemetry::Common::Utilities.maybe_timeout(timeout, start_time)
|
110
|
+
return TIMEOUT if remaining_timeout&.zero?
|
111
|
+
|
112
|
+
batch = snapshot.shift(batch_size).map!(&:to_log_record_data)
|
113
|
+
result_code = export_batch(batch, timeout: remaining_timeout)
|
114
|
+
return result_code unless result_code == SUCCESS
|
115
|
+
end
|
116
|
+
|
117
|
+
@exporter.force_flush(timeout: ::OpenTelemetry::Common::Utilities.maybe_timeout(timeout, start_time))
|
118
|
+
ensure
|
119
|
+
# Unshift the remaining log records if we timed out. We drop excess
|
120
|
+
# log records from the snapshot because they're older than any
|
121
|
+
# records in the buffer.
|
122
|
+
lock do
|
123
|
+
n = log_records.size + snapshot.size - max_queue_size
|
124
|
+
|
125
|
+
if n.positive?
|
126
|
+
snapshot.shift(n)
|
127
|
+
report_dropped_log_records(n, reason: 'buffer-full')
|
128
|
+
end
|
129
|
+
|
130
|
+
log_records.unshift(*snapshot) unless snapshot.empty?
|
131
|
+
@condition.signal if log_records.size > max_queue_size / 2
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Shuts the consumer thread down and flushes the current accumulated
|
136
|
+
# buffer will block until the thread is finished.
|
137
|
+
#
|
138
|
+
# @param [optional Numeric] timeout An optional timeout in seconds.
|
139
|
+
# @return [Integer] SUCCESS if no error occurred, FAILURE if a
|
140
|
+
# non-specific failure occurred, TIMEOUT if a timeout occurred.
|
141
|
+
def shutdown(timeout: nil)
|
142
|
+
return if @stopped
|
143
|
+
|
144
|
+
start_time = ::OpenTelemetry::Common::Utilities.timeout_timestamp
|
145
|
+
thread = lock do
|
146
|
+
@keep_running = false
|
147
|
+
@stopped = true
|
148
|
+
@condition.signal
|
149
|
+
@thread
|
150
|
+
end
|
151
|
+
|
152
|
+
thread&.join(timeout)
|
153
|
+
force_flush(timeout: ::OpenTelemetry::Common::Utilities.maybe_timeout(timeout, start_time))
|
154
|
+
dropped_log_records = lock { log_records.size }
|
155
|
+
report_dropped_log_records(dropped_log_records, reason: 'terminating') if dropped_log_records.positive?
|
156
|
+
|
157
|
+
@exporter.shutdown(timeout: ::OpenTelemetry::Common::Utilities.maybe_timeout(timeout, start_time))
|
158
|
+
end
|
159
|
+
|
160
|
+
private
|
161
|
+
|
162
|
+
attr_reader :log_records, :max_queue_size, :batch_size
|
163
|
+
|
164
|
+
def work
|
165
|
+
loop do
|
166
|
+
batch = lock do
|
167
|
+
@condition.wait(@mutex, @delay_seconds) if log_records.size < batch_size && @keep_running
|
168
|
+
@condition.wait(@mutex, @delay_seconds) while log_records.empty? && @keep_running
|
169
|
+
return unless @keep_running
|
170
|
+
|
171
|
+
fetch_batch
|
172
|
+
end
|
173
|
+
|
174
|
+
export_batch(batch)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def reset_on_fork(restart_thread: true)
|
179
|
+
pid = Process.pid
|
180
|
+
return if @pid == pid
|
181
|
+
|
182
|
+
@pid = pid
|
183
|
+
log_records.clear
|
184
|
+
@thread = restart_thread ? Thread.new { work } : nil
|
185
|
+
rescue ThreadError => e
|
186
|
+
OpenTelemetry.handle_error(exception: e, message: 'unexpected error in BatchLogRecordProcessor#reset_on_fork')
|
187
|
+
end
|
188
|
+
|
189
|
+
def export_batch(batch, timeout: @exporter_timeout_seconds)
|
190
|
+
result_code = @export_mutex.synchronize { @exporter.export(batch, timeout: timeout) }
|
191
|
+
report_result(result_code, batch)
|
192
|
+
result_code
|
193
|
+
rescue StandardError => e
|
194
|
+
report_result(FAILURE, batch)
|
195
|
+
OpenTelemetry.handle_error(exception: e, message: 'unexpected error in BatchLogRecordProcessor#export_batch')
|
196
|
+
end
|
197
|
+
|
198
|
+
def report_result(result_code, batch)
|
199
|
+
if result_code == SUCCESS
|
200
|
+
OpenTelemetry.logger.debug("Successfully exported #{batch.size} log records")
|
201
|
+
else
|
202
|
+
OpenTelemetry.handle_error(exception: ExportError.new("Unable to export #{batch.size} log records"))
|
203
|
+
OpenTelemetry.logger.error("Result code: #{result_code}")
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def report_dropped_log_records(count, reason:)
|
208
|
+
OpenTelemetry.logger.warn("#{count} log record(s) dropped. Reason: #{reason}")
|
209
|
+
end
|
210
|
+
|
211
|
+
def fetch_batch
|
212
|
+
log_records.shift(@batch_size).map!(&:to_log_record_data)
|
213
|
+
end
|
214
|
+
|
215
|
+
def lock(&block)
|
216
|
+
@mutex.synchronize(&block)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
@@ -0,0 +1,64 @@
|
|
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 SDK
|
12
|
+
module Logs
|
13
|
+
module Export
|
14
|
+
# LogRecordExporter describes a duck type. It is not required to
|
15
|
+
# subclass this class to provide an implementation of LogRecordExporter,
|
16
|
+
# provided the interface is satisfied. LogRecordExporter allows
|
17
|
+
# different tracing services to export log record data in their own format.
|
18
|
+
#
|
19
|
+
# To export data an exporter MUST be registered to the {LoggerProvider}
|
20
|
+
# using a {LogRecordProcessor} implementation.
|
21
|
+
class LogRecordExporter
|
22
|
+
def initialize
|
23
|
+
@stopped = false
|
24
|
+
end
|
25
|
+
|
26
|
+
# Called to export {LogRecordData}s.
|
27
|
+
#
|
28
|
+
# @param [Enumerable<LogRecordData>] log_record_data the list of
|
29
|
+
# {LogRecordData} to be exported.
|
30
|
+
# @param [optional Numeric] timeout An optional timeout in seconds.
|
31
|
+
#
|
32
|
+
# @return [Integer] the result of the export.
|
33
|
+
def export(log_record_data, timeout: nil)
|
34
|
+
return SUCCESS unless @stopped
|
35
|
+
|
36
|
+
FAILURE
|
37
|
+
end
|
38
|
+
|
39
|
+
# Called when {LoggerProvider#force_flush} is called, if this exporter is
|
40
|
+
# registered to a {LoggerProvider} object.
|
41
|
+
#
|
42
|
+
# @param [optional Numeric] timeout An optional timeout in seconds.
|
43
|
+
# @return [Integer] SUCCESS if no error occurred, FAILURE if a
|
44
|
+
# non-specific failure occurred, TIMEOUT if a timeout occurred.
|
45
|
+
def force_flush(timeout: nil)
|
46
|
+
SUCCESS
|
47
|
+
end
|
48
|
+
|
49
|
+
# Called when {LoggerProvider#shutdown} is called, if this exporter is
|
50
|
+
# registered to a {LoggerProvider} object.
|
51
|
+
#
|
52
|
+
# @param [optional Numeric] timeout An optional timeout in seconds.
|
53
|
+
def shutdown(timeout: nil)
|
54
|
+
@stopped = true
|
55
|
+
SUCCESS
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,34 @@
|
|
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 SDK
|
12
|
+
module Logs
|
13
|
+
# The Export module contains the built-in exporters and log record
|
14
|
+
# processors for the OpenTelemetry reference implementation.
|
15
|
+
module Export
|
16
|
+
ExportError = Class.new(::OpenTelemetry::Error)
|
17
|
+
# The operation finished successfully.
|
18
|
+
SUCCESS = 0
|
19
|
+
|
20
|
+
# The operation finished with an error.
|
21
|
+
FAILURE = 1
|
22
|
+
|
23
|
+
# The operation timed out.
|
24
|
+
TIMEOUT = 2
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
require_relative 'export/log_record_exporter'
|
34
|
+
require_relative 'export/batch_log_record_processor'
|
@@ -0,0 +1,115 @@
|
|
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 SDK
|
12
|
+
module Logs
|
13
|
+
# Implementation of OpenTelemetry::Logs::LogRecord that records log events.
|
14
|
+
class LogRecord < OpenTelemetry::Logs::LogRecord
|
15
|
+
attr_accessor :timestamp,
|
16
|
+
:observed_timestamp,
|
17
|
+
:severity_text,
|
18
|
+
:severity_number,
|
19
|
+
:body,
|
20
|
+
:attributes,
|
21
|
+
:trace_id,
|
22
|
+
:span_id,
|
23
|
+
:trace_flags,
|
24
|
+
:resource,
|
25
|
+
:instrumentation_scope
|
26
|
+
|
27
|
+
# Creates a new {LogRecord}.
|
28
|
+
#
|
29
|
+
# @param [optional Time] timestamp Time when the event occurred.
|
30
|
+
# @param [optional Time] observed_timestamp Time when the event
|
31
|
+
# was observed by the collection system. If nil, will first attempt
|
32
|
+
# to set to `timestamp`. If `timestamp` is nil, will set to Time.now.
|
33
|
+
# @param [optional OpenTelemetry::Trace::SpanContext] span_context The
|
34
|
+
# OpenTelemetry::Trace::SpanContext to associate with the LogRecord.
|
35
|
+
# @param [optional String] severity_text The log severity, also known as
|
36
|
+
# log level.
|
37
|
+
# @param [optional Integer] severity_number The numerical value of the
|
38
|
+
# log severity.
|
39
|
+
# @param [optional String, Numeric, Boolean, Array<String, Numeric,
|
40
|
+
# Boolean>, Hash{String => String, Numeric, Boolean, Array<String,
|
41
|
+
# Numeric, Boolean>}] body The body of the {LogRecord}.
|
42
|
+
# @param [optional Hash{String => String, Numeric, Boolean,
|
43
|
+
# Array<String, Numeric, Boolean>}] attributes Attributes to associate
|
44
|
+
# with the {LogRecord}.
|
45
|
+
# @param [optional String] trace_id The trace ID associated with the
|
46
|
+
# current context.
|
47
|
+
# @param [optional String] span_id The span ID associated with the
|
48
|
+
# current context.
|
49
|
+
# @param [optional OpenTelemetry::Trace::TraceFlags] trace_flags The
|
50
|
+
# trace flags associated with the current context.
|
51
|
+
# @param [optional OpenTelemetry::SDK::Resources::Resource] resource The
|
52
|
+
# source of the log, desrived from the LoggerProvider.
|
53
|
+
# @param [optional OpenTelemetry::SDK::InstrumentationScope] instrumentation_scope
|
54
|
+
# The instrumentation scope, derived from the emitting Logger
|
55
|
+
#
|
56
|
+
#
|
57
|
+
# @return [LogRecord]
|
58
|
+
def initialize(
|
59
|
+
timestamp: nil,
|
60
|
+
observed_timestamp: nil,
|
61
|
+
severity_text: nil,
|
62
|
+
severity_number: nil,
|
63
|
+
body: nil,
|
64
|
+
attributes: nil,
|
65
|
+
trace_id: nil,
|
66
|
+
span_id: nil,
|
67
|
+
trace_flags: nil,
|
68
|
+
resource: nil,
|
69
|
+
instrumentation_scope: nil
|
70
|
+
)
|
71
|
+
@timestamp = timestamp
|
72
|
+
@observed_timestamp = observed_timestamp || timestamp || Time.now
|
73
|
+
@severity_text = severity_text
|
74
|
+
@severity_number = severity_number
|
75
|
+
@body = body
|
76
|
+
@attributes = attributes.nil? ? nil : Hash[attributes] # We need a mutable copy of attributes
|
77
|
+
@trace_id = trace_id
|
78
|
+
@span_id = span_id
|
79
|
+
@trace_flags = trace_flags
|
80
|
+
@resource = resource
|
81
|
+
@instrumentation_scope = instrumentation_scope
|
82
|
+
@total_recorded_attributes = @attributes&.size || 0
|
83
|
+
end
|
84
|
+
|
85
|
+
def to_log_record_data
|
86
|
+
LogRecordData.new(
|
87
|
+
to_integer_nanoseconds(@timestamp),
|
88
|
+
to_integer_nanoseconds(@observed_timestamp),
|
89
|
+
@severity_text,
|
90
|
+
@severity_number,
|
91
|
+
@body,
|
92
|
+
@attributes,
|
93
|
+
@trace_id,
|
94
|
+
@span_id,
|
95
|
+
@trace_flags,
|
96
|
+
@resource,
|
97
|
+
@instrumentation_scope,
|
98
|
+
@total_recorded_attributes
|
99
|
+
)
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def to_integer_nanoseconds(timestamp)
|
105
|
+
return unless timestamp.is_a?(Time)
|
106
|
+
|
107
|
+
(timestamp.to_r * 10**9).to_i
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,31 @@
|
|
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 SDK
|
12
|
+
module Logs
|
13
|
+
# LogRecordData is a Struct containing {LogRecord} data for export.
|
14
|
+
LogRecordData = Struct.new(:timestamp, # optional Integer nanoseconds since Epoch
|
15
|
+
:observed_timestamp, # Integer nanoseconds since Epoch
|
16
|
+
:severity_text, # optional String
|
17
|
+
:severity_number, # optional Integer
|
18
|
+
:body, # optional String, Numeric, Boolean, Array<String, Numeric, Boolean>, Hash{String => String, Numeric, Boolean, Array<String, Numeric, Boolean>}
|
19
|
+
:attributes, # optional Hash{String => String, Numeric, Boolean, Array<String, Numeric, Boolean>}
|
20
|
+
:trace_id, # optional String (16-byte binary)
|
21
|
+
:span_id, # optional String (8-byte binary)
|
22
|
+
:trace_flags, # optional Integer (8-bit byte of bit flags)
|
23
|
+
:resource, # optional OpenTelemetry::SDK::Resources::Resource
|
24
|
+
:instrumentation_scope, # optional OpenTelemetry::SDK::InstrumentationScope
|
25
|
+
:total_recorded_attributes) # Integer
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|