opentelemetry-exporter-otlp-metrics 0.1.0
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.
- checksums.yaml +7 -0
- data/.yardopts +9 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE +201 -0
- data/README.md +145 -0
- data/lib/opentelemetry/exporter/otlp/metrics/metrics_exporter.rb +311 -0
- data/lib/opentelemetry/exporter/otlp/metrics/util.rb +141 -0
- data/lib/opentelemetry/exporter/otlp/metrics/version.rb +16 -0
- data/lib/opentelemetry/exporter/otlp_metrics.rb +17 -0
- data/lib/opentelemetry/proto/collector/logs/v1/logs_service_pb.rb +27 -0
- data/lib/opentelemetry/proto/collector/metrics/v1/metrics_service_pb.rb +27 -0
- data/lib/opentelemetry/proto/collector/trace/v1/trace_service_pb.rb +27 -0
- data/lib/opentelemetry/proto/common/v1/common_pb.rb +25 -0
- data/lib/opentelemetry/proto/logs/v1/logs_pb.rb +29 -0
- data/lib/opentelemetry/proto/metrics/v1/metrics_pb.rb +41 -0
- data/lib/opentelemetry/proto/resource/v1/resource_pb.rb +23 -0
- data/lib/opentelemetry/proto/trace/v1/trace_pb.rb +32 -0
- data/lib/opentelemetry-exporter-otlp-metrics.rb +7 -0
- metadata +350 -0
@@ -0,0 +1,311 @@
|
|
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 'opentelemetry/proto/common/v1/common_pb'
|
15
|
+
require 'opentelemetry/proto/resource/v1/resource_pb'
|
16
|
+
require 'opentelemetry/proto/metrics/v1/metrics_pb'
|
17
|
+
require 'opentelemetry/proto/collector/metrics/v1/metrics_service_pb'
|
18
|
+
|
19
|
+
require 'opentelemetry/metrics'
|
20
|
+
require 'opentelemetry/sdk/metrics'
|
21
|
+
|
22
|
+
require_relative './util'
|
23
|
+
|
24
|
+
module OpenTelemetry
|
25
|
+
module Exporter
|
26
|
+
module OTLP
|
27
|
+
module Metrics
|
28
|
+
# An OpenTelemetry metrics exporter that sends metrics over HTTP as Protobuf encoded OTLP ExportMetricsServiceRequest.
|
29
|
+
class MetricsExporter < ::OpenTelemetry::SDK::Metrics::Export::MetricReader # rubocop:disable Metrics/ClassLength
|
30
|
+
include Util
|
31
|
+
|
32
|
+
attr_reader :metric_snapshots
|
33
|
+
|
34
|
+
SUCCESS = OpenTelemetry::SDK::Metrics::Export::SUCCESS
|
35
|
+
FAILURE = OpenTelemetry::SDK::Metrics::Export::FAILURE
|
36
|
+
private_constant(:SUCCESS, :FAILURE)
|
37
|
+
|
38
|
+
def self.ssl_verify_mode
|
39
|
+
if ENV.key?('OTEL_RUBY_EXPORTER_OTLP_SSL_VERIFY_PEER')
|
40
|
+
OpenSSL::SSL::VERIFY_PEER
|
41
|
+
elsif ENV.key?('OTEL_RUBY_EXPORTER_OTLP_SSL_VERIFY_NONE')
|
42
|
+
OpenSSL::SSL::VERIFY_NONE
|
43
|
+
else
|
44
|
+
OpenSSL::SSL::VERIFY_PEER
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def initialize(endpoint: OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_METRICS_ENDPOINT', 'OTEL_EXPORTER_OTLP_ENDPOINT', default: 'http://localhost:4318/v1/metrics'),
|
49
|
+
certificate_file: OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE', 'OTEL_EXPORTER_OTLP_CERTIFICATE'),
|
50
|
+
ssl_verify_mode: MetricsExporter.ssl_verify_mode,
|
51
|
+
headers: OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_METRICS_HEADERS', 'OTEL_EXPORTER_OTLP_HEADERS', default: {}),
|
52
|
+
compression: OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_METRICS_COMPRESSION', 'OTEL_EXPORTER_OTLP_COMPRESSION', default: 'gzip'),
|
53
|
+
timeout: OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_METRICS_TIMEOUT', 'OTEL_EXPORTER_OTLP_TIMEOUT', default: 10))
|
54
|
+
raise ArgumentError, "invalid url for OTLP::MetricsExporter #{endpoint}" unless OpenTelemetry::Common::Utilities.valid_url?(endpoint)
|
55
|
+
raise ArgumentError, "unsupported compression key #{compression}" unless compression.nil? || %w[gzip none].include?(compression)
|
56
|
+
|
57
|
+
# create the MetricStore object
|
58
|
+
super()
|
59
|
+
|
60
|
+
@uri = if endpoint == ENV['OTEL_EXPORTER_OTLP_ENDPOINT']
|
61
|
+
URI.join(endpoint, 'v1/metrics')
|
62
|
+
else
|
63
|
+
URI(endpoint)
|
64
|
+
end
|
65
|
+
|
66
|
+
@http = http_connection(@uri, ssl_verify_mode, certificate_file)
|
67
|
+
|
68
|
+
@path = @uri.path
|
69
|
+
@headers = prepare_headers(headers)
|
70
|
+
@timeout = timeout.to_f
|
71
|
+
@compression = compression
|
72
|
+
@mutex = Mutex.new
|
73
|
+
@shutdown = false
|
74
|
+
end
|
75
|
+
|
76
|
+
# consolidate the metrics data into the form of MetricData
|
77
|
+
#
|
78
|
+
# return MetricData
|
79
|
+
def pull
|
80
|
+
export(collect)
|
81
|
+
end
|
82
|
+
|
83
|
+
# metrics Array[MetricData]
|
84
|
+
def export(metrics, timeout: nil)
|
85
|
+
@mutex.synchronize do
|
86
|
+
send_bytes(encode(metrics), timeout: timeout)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def send_bytes(bytes, timeout:) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
91
|
+
return FAILURE if bytes.nil?
|
92
|
+
|
93
|
+
request = Net::HTTP::Post.new(@path)
|
94
|
+
|
95
|
+
if @compression == 'gzip'
|
96
|
+
request.add_field('Content-Encoding', 'gzip')
|
97
|
+
body = Zlib.gzip(bytes)
|
98
|
+
else
|
99
|
+
body = bytes
|
100
|
+
end
|
101
|
+
|
102
|
+
request.body = body
|
103
|
+
request.add_field('Content-Type', 'application/x-protobuf')
|
104
|
+
@headers.each { |key, value| request.add_field(key, value) }
|
105
|
+
|
106
|
+
retry_count = 0
|
107
|
+
timeout ||= @timeout
|
108
|
+
start_time = OpenTelemetry::Common::Utilities.timeout_timestamp
|
109
|
+
|
110
|
+
around_request do
|
111
|
+
remaining_timeout = OpenTelemetry::Common::Utilities.maybe_timeout(timeout, start_time)
|
112
|
+
return FAILURE if remaining_timeout.zero?
|
113
|
+
|
114
|
+
@http.open_timeout = remaining_timeout
|
115
|
+
@http.read_timeout = remaining_timeout
|
116
|
+
@http.write_timeout = remaining_timeout
|
117
|
+
@http.start unless @http.started?
|
118
|
+
response = measure_request_duration { @http.request(request) }
|
119
|
+
case response
|
120
|
+
when Net::HTTPOK
|
121
|
+
response.body # Read and discard body
|
122
|
+
SUCCESS
|
123
|
+
when Net::HTTPServiceUnavailable, Net::HTTPTooManyRequests
|
124
|
+
response.body # Read and discard body
|
125
|
+
redo if backoff?(retry_after: response['Retry-After'], retry_count: retry_count += 1, reason: response.code)
|
126
|
+
OpenTelemetry.logger.warn('Net::HTTPServiceUnavailable/Net::HTTPTooManyRequests in MetricsExporter#send_bytes')
|
127
|
+
FAILURE
|
128
|
+
when Net::HTTPRequestTimeOut, Net::HTTPGatewayTimeOut, Net::HTTPBadGateway
|
129
|
+
response.body # Read and discard body
|
130
|
+
redo if backoff?(retry_count: retry_count += 1, reason: response.code)
|
131
|
+
OpenTelemetry.logger.warn('Net::HTTPRequestTimeOut/Net::HTTPGatewayTimeOut/Net::HTTPBadGateway in MetricsExporter#send_bytes')
|
132
|
+
FAILURE
|
133
|
+
when Net::HTTPNotFound
|
134
|
+
OpenTelemetry.handle_error(message: "OTLP metrics_exporter received http.code=404 for uri: '#{@path}'")
|
135
|
+
FAILURE
|
136
|
+
when Net::HTTPBadRequest, Net::HTTPClientError, Net::HTTPServerError
|
137
|
+
log_status(response.body)
|
138
|
+
OpenTelemetry.logger.warn('Net::HTTPBadRequest/Net::HTTPClientError/Net::HTTPServerError in MetricsExporter#send_bytes')
|
139
|
+
FAILURE
|
140
|
+
when Net::HTTPRedirection
|
141
|
+
@http.finish
|
142
|
+
handle_redirect(response['location'])
|
143
|
+
redo if backoff?(retry_after: 0, retry_count: retry_count += 1, reason: response.code)
|
144
|
+
else
|
145
|
+
@http.finish
|
146
|
+
OpenTelemetry.logger.warn("Unexpected error in OTLP::MetricsExporter#send_bytes - #{response.message}")
|
147
|
+
FAILURE
|
148
|
+
end
|
149
|
+
rescue Net::OpenTimeout, Net::ReadTimeout
|
150
|
+
retry if backoff?(retry_count: retry_count += 1, reason: 'timeout')
|
151
|
+
OpenTelemetry.logger.warn('Net::OpenTimeout/Net::ReadTimeout in MetricsExporter#send_bytes')
|
152
|
+
return FAILURE
|
153
|
+
rescue OpenSSL::SSL::SSLError
|
154
|
+
retry if backoff?(retry_count: retry_count += 1, reason: 'openssl_error')
|
155
|
+
OpenTelemetry.logger.warn('OpenSSL::SSL::SSLError in MetricsExporter#send_bytes')
|
156
|
+
return FAILURE
|
157
|
+
rescue SocketError
|
158
|
+
retry if backoff?(retry_count: retry_count += 1, reason: 'socket_error')
|
159
|
+
OpenTelemetry.logger.warn('SocketError in MetricsExporter#send_bytes')
|
160
|
+
return FAILURE
|
161
|
+
rescue SystemCallError => e
|
162
|
+
retry if backoff?(retry_count: retry_count += 1, reason: e.class.name)
|
163
|
+
OpenTelemetry.logger.warn('SystemCallError in MetricsExporter#send_bytes')
|
164
|
+
return FAILURE
|
165
|
+
rescue EOFError
|
166
|
+
retry if backoff?(retry_count: retry_count += 1, reason: 'eof_error')
|
167
|
+
OpenTelemetry.logger.warn('EOFError in MetricsExporter#send_bytes')
|
168
|
+
return FAILURE
|
169
|
+
rescue Zlib::DataError
|
170
|
+
retry if backoff?(retry_count: retry_count += 1, reason: 'zlib_error')
|
171
|
+
OpenTelemetry.logger.warn('Zlib::DataError in MetricsExporter#send_bytes')
|
172
|
+
return FAILURE
|
173
|
+
rescue StandardError => e
|
174
|
+
OpenTelemetry.handle_error(exception: e, message: 'unexpected error in OTLP::MetricsExporter#send_bytes')
|
175
|
+
return FAILURE
|
176
|
+
end
|
177
|
+
ensure
|
178
|
+
# Reset timeouts to defaults for the next call.
|
179
|
+
@http.open_timeout = @timeout
|
180
|
+
@http.read_timeout = @timeout
|
181
|
+
@http.write_timeout = @timeout
|
182
|
+
end
|
183
|
+
|
184
|
+
def encode(metrics_data) # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
|
185
|
+
Opentelemetry::Proto::Collector::Metrics::V1::ExportMetricsServiceRequest.encode(
|
186
|
+
Opentelemetry::Proto::Collector::Metrics::V1::ExportMetricsServiceRequest.new(
|
187
|
+
resource_metrics: metrics_data
|
188
|
+
.group_by(&:resource)
|
189
|
+
.map do |resource, scope_metrics|
|
190
|
+
Opentelemetry::Proto::Metrics::V1::ResourceMetrics.new(
|
191
|
+
resource: Opentelemetry::Proto::Resource::V1::Resource.new(
|
192
|
+
attributes: resource.attribute_enumerator.map { |key, value| as_otlp_key_value(key, value) }
|
193
|
+
),
|
194
|
+
scope_metrics: scope_metrics
|
195
|
+
.group_by(&:instrumentation_scope)
|
196
|
+
.map do |instrumentation_scope, metrics|
|
197
|
+
Opentelemetry::Proto::Metrics::V1::ScopeMetrics.new(
|
198
|
+
scope: Opentelemetry::Proto::Common::V1::InstrumentationScope.new(
|
199
|
+
name: instrumentation_scope.name,
|
200
|
+
version: instrumentation_scope.version
|
201
|
+
),
|
202
|
+
metrics: metrics.map { |sd| as_otlp_metrics(sd) }
|
203
|
+
)
|
204
|
+
end
|
205
|
+
)
|
206
|
+
end
|
207
|
+
)
|
208
|
+
)
|
209
|
+
rescue StandardError => e
|
210
|
+
OpenTelemetry.handle_error(exception: e, message: 'unexpected error in OTLP::MetricsExporter#encode')
|
211
|
+
nil
|
212
|
+
end
|
213
|
+
|
214
|
+
# metrics_pb has following type of data: :gauge, :sum, :histogram, :exponential_histogram, :summary
|
215
|
+
# current metric sdk only implements instrument: :counter -> :sum, :histogram -> :histogram
|
216
|
+
#
|
217
|
+
# metrics [MetricData]
|
218
|
+
def as_otlp_metrics(metrics) # rubocop:disable Metrics/MethodLength
|
219
|
+
case metrics.instrument_kind
|
220
|
+
when :observable_gauge
|
221
|
+
Opentelemetry::Proto::Metrics::V1::Metric.new(
|
222
|
+
name: metrics.name,
|
223
|
+
description: metrics.description,
|
224
|
+
unit: metrics.unit,
|
225
|
+
gauge: Opentelemetry::Proto::Metrics::V1::Gauge.new(
|
226
|
+
aggregation_temporality: as_otlp_aggregation_temporality(metrics.aggregation_temporality),
|
227
|
+
data_points: metrics.data_points.map do |ndp|
|
228
|
+
number_data_point(ndp)
|
229
|
+
end
|
230
|
+
)
|
231
|
+
)
|
232
|
+
|
233
|
+
when :counter, :up_down_counter
|
234
|
+
Opentelemetry::Proto::Metrics::V1::Metric.new(
|
235
|
+
name: metrics.name,
|
236
|
+
description: metrics.description,
|
237
|
+
unit: metrics.unit,
|
238
|
+
sum: Opentelemetry::Proto::Metrics::V1::Sum.new(
|
239
|
+
aggregation_temporality: as_otlp_aggregation_temporality(metrics.aggregation_temporality),
|
240
|
+
data_points: metrics.data_points.map do |ndp|
|
241
|
+
number_data_point(ndp)
|
242
|
+
end
|
243
|
+
)
|
244
|
+
)
|
245
|
+
|
246
|
+
when :histogram
|
247
|
+
Opentelemetry::Proto::Metrics::V1::Metric.new(
|
248
|
+
name: metrics.name,
|
249
|
+
description: metrics.description,
|
250
|
+
unit: metrics.unit,
|
251
|
+
histogram: Opentelemetry::Proto::Metrics::V1::Histogram.new(
|
252
|
+
aggregation_temporality: as_otlp_aggregation_temporality(metrics.aggregation_temporality),
|
253
|
+
data_points: metrics.data_points.map do |hdp|
|
254
|
+
histogram_data_point(hdp)
|
255
|
+
end
|
256
|
+
)
|
257
|
+
)
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
def as_otlp_aggregation_temporality(type)
|
262
|
+
case type
|
263
|
+
when :delta then Opentelemetry::Proto::Metrics::V1::AggregationTemporality::AGGREGATION_TEMPORALITY_DELTA
|
264
|
+
when :cumulative then Opentelemetry::Proto::Metrics::V1::AggregationTemporality::AGGREGATION_TEMPORALITY_CUMULATIVE
|
265
|
+
else Opentelemetry::Proto::Metrics::V1::AggregationTemporality::AGGREGATION_TEMPORALITY_UNSPECIFIED
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
def histogram_data_point(hdp)
|
270
|
+
Opentelemetry::Proto::Metrics::V1::HistogramDataPoint.new(
|
271
|
+
attributes: hdp.attributes.map { |k, v| as_otlp_key_value(k, v) },
|
272
|
+
start_time_unix_nano: hdp.start_time_unix_nano,
|
273
|
+
time_unix_nano: hdp.time_unix_nano,
|
274
|
+
count: hdp.count,
|
275
|
+
sum: hdp.sum,
|
276
|
+
bucket_counts: hdp.bucket_counts,
|
277
|
+
explicit_bounds: hdp.explicit_bounds,
|
278
|
+
exemplars: hdp.exemplars,
|
279
|
+
min: hdp.min,
|
280
|
+
max: hdp.max
|
281
|
+
)
|
282
|
+
end
|
283
|
+
|
284
|
+
def number_data_point(ndp)
|
285
|
+
Opentelemetry::Proto::Metrics::V1::NumberDataPoint.new(
|
286
|
+
attributes: ndp.attributes.map { |k, v| as_otlp_key_value(k, v) },
|
287
|
+
as_int: ndp.value,
|
288
|
+
start_time_unix_nano: ndp.start_time_unix_nano,
|
289
|
+
time_unix_nano: ndp.time_unix_nano,
|
290
|
+
exemplars: ndp.exemplars # exemplars not implemented yet from metrics sdk
|
291
|
+
)
|
292
|
+
end
|
293
|
+
|
294
|
+
# may not need this
|
295
|
+
def reset
|
296
|
+
SUCCESS
|
297
|
+
end
|
298
|
+
|
299
|
+
def force_flush(timeout: nil)
|
300
|
+
SUCCESS
|
301
|
+
end
|
302
|
+
|
303
|
+
def shutdown(timeout: nil)
|
304
|
+
@shutdown = true
|
305
|
+
SUCCESS
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright The OpenTelemetry Authors
|
4
|
+
#
|
5
|
+
# SPDX-License-Identifier: Apache-2.0
|
6
|
+
|
7
|
+
module OpenTelemetry
|
8
|
+
module Exporter
|
9
|
+
module OTLP
|
10
|
+
module Metrics
|
11
|
+
# Util module provide essential functionality for exporter
|
12
|
+
module Util # rubocop:disable Metrics/ModuleLength
|
13
|
+
KEEP_ALIVE_TIMEOUT = 30
|
14
|
+
RETRY_COUNT = 5
|
15
|
+
ERROR_MESSAGE_INVALID_HEADERS = 'headers must be a String with comma-separated URL Encoded UTF-8 k=v pairs or a Hash'
|
16
|
+
DEFAULT_USER_AGENT = "OTel-OTLP-MetricsExporter-Ruby/#{OpenTelemetry::Exporter::OTLP::Metrics::VERSION} Ruby/#{RUBY_VERSION} (#{RUBY_PLATFORM}; #{RUBY_ENGINE}/#{RUBY_ENGINE_VERSION})".freeze
|
17
|
+
|
18
|
+
def http_connection(uri, ssl_verify_mode, certificate_file)
|
19
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
20
|
+
http.use_ssl = uri.scheme == 'https'
|
21
|
+
http.verify_mode = ssl_verify_mode
|
22
|
+
http.ca_file = certificate_file unless certificate_file.nil?
|
23
|
+
http.keep_alive_timeout = KEEP_ALIVE_TIMEOUT
|
24
|
+
http
|
25
|
+
end
|
26
|
+
|
27
|
+
def around_request
|
28
|
+
OpenTelemetry::Common::Utilities.untraced { yield } # rubocop:disable Style/ExplicitBlockArgument
|
29
|
+
end
|
30
|
+
|
31
|
+
def as_otlp_key_value(key, value)
|
32
|
+
Opentelemetry::Proto::Common::V1::KeyValue.new(key: key, value: as_otlp_any_value(value))
|
33
|
+
rescue Encoding::UndefinedConversionError => e
|
34
|
+
encoded_value = value.encode('UTF-8', invalid: :replace, undef: :replace, replace: '�')
|
35
|
+
OpenTelemetry.handle_error(exception: e, message: "encoding error for key #{key} and value #{encoded_value}")
|
36
|
+
Opentelemetry::Proto::Common::V1::KeyValue.new(key: key, value: as_otlp_any_value('Encoding Error'))
|
37
|
+
end
|
38
|
+
|
39
|
+
def as_otlp_any_value(value)
|
40
|
+
result = Opentelemetry::Proto::Common::V1::AnyValue.new
|
41
|
+
case value
|
42
|
+
when String
|
43
|
+
result.string_value = value
|
44
|
+
when Integer
|
45
|
+
result.int_value = value
|
46
|
+
when Float
|
47
|
+
result.double_value = value
|
48
|
+
when true, false
|
49
|
+
result.bool_value = value
|
50
|
+
when Array
|
51
|
+
values = value.map { |element| as_otlp_any_value(element) }
|
52
|
+
result.array_value = Opentelemetry::Proto::Common::V1::ArrayValue.new(values: values)
|
53
|
+
end
|
54
|
+
result
|
55
|
+
end
|
56
|
+
|
57
|
+
def prepare_headers(config_headers)
|
58
|
+
headers = case config_headers
|
59
|
+
when String then parse_headers(config_headers)
|
60
|
+
when Hash then config_headers.dup
|
61
|
+
else
|
62
|
+
raise ArgumentError, ERROR_MESSAGE_INVALID_HEADERS
|
63
|
+
end
|
64
|
+
|
65
|
+
headers['User-Agent'] = "#{headers.fetch('User-Agent', '')} #{DEFAULT_USER_AGENT}".strip
|
66
|
+
|
67
|
+
headers
|
68
|
+
end
|
69
|
+
|
70
|
+
def measure_request_duration
|
71
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
72
|
+
begin
|
73
|
+
yield
|
74
|
+
ensure
|
75
|
+
stop = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
76
|
+
1000.0 * (stop - start)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def parse_headers(raw)
|
81
|
+
entries = raw.split(',')
|
82
|
+
raise ArgumentError, ERROR_MESSAGE_INVALID_HEADERS if entries.empty?
|
83
|
+
|
84
|
+
entries.each_with_object({}) do |entry, headers|
|
85
|
+
k, v = entry.split('=', 2).map(&CGI.method(:unescape))
|
86
|
+
begin
|
87
|
+
k = k.to_s.strip
|
88
|
+
v = v.to_s.strip
|
89
|
+
rescue Encoding::CompatibilityError
|
90
|
+
raise ArgumentError, ERROR_MESSAGE_INVALID_HEADERS
|
91
|
+
rescue ArgumentError => e
|
92
|
+
raise e, ERROR_MESSAGE_INVALID_HEADERS
|
93
|
+
end
|
94
|
+
raise ArgumentError, ERROR_MESSAGE_INVALID_HEADERS if k.empty? || v.empty?
|
95
|
+
|
96
|
+
headers[k] = v
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def backoff?(retry_count:, reason:, retry_after: nil) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
101
|
+
return false if retry_count > RETRY_COUNT
|
102
|
+
|
103
|
+
sleep_interval = nil
|
104
|
+
unless retry_after.nil?
|
105
|
+
sleep_interval =
|
106
|
+
begin
|
107
|
+
Integer(retry_after)
|
108
|
+
rescue ArgumentError
|
109
|
+
nil
|
110
|
+
end
|
111
|
+
sleep_interval ||=
|
112
|
+
begin
|
113
|
+
Time.httpdate(retry_after) - Time.now
|
114
|
+
rescue # rubocop:disable Style/RescueStandardError
|
115
|
+
nil
|
116
|
+
end
|
117
|
+
sleep_interval = nil unless sleep_interval&.positive?
|
118
|
+
end
|
119
|
+
sleep_interval ||= rand(2**retry_count)
|
120
|
+
|
121
|
+
sleep(sleep_interval)
|
122
|
+
true
|
123
|
+
end
|
124
|
+
|
125
|
+
def log_status(body)
|
126
|
+
status = Google::Rpc::Status.decode(body)
|
127
|
+
details = status.details.map do |detail|
|
128
|
+
klass_or_nil = ::Google::Protobuf::DescriptorPool.generated_pool.lookup(detail.type_name).msgclass
|
129
|
+
detail.unpack(klass_or_nil) if klass_or_nil
|
130
|
+
end.compact
|
131
|
+
OpenTelemetry.handle_error(message: "OTLP metrics_exporter received rpc.Status{message=#{status.message}, details=#{details}}")
|
132
|
+
rescue StandardError => e
|
133
|
+
OpenTelemetry.handle_error(exception: e, message: 'unexpected error decoding rpc.Status in OTLP::MetricsExporter#log_status')
|
134
|
+
end
|
135
|
+
|
136
|
+
def handle_redirect(location); end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright The OpenTelemetry Authors
|
4
|
+
#
|
5
|
+
# SPDX-License-Identifier: Apache-2.0
|
6
|
+
|
7
|
+
module OpenTelemetry
|
8
|
+
module Exporter
|
9
|
+
module OTLP
|
10
|
+
module Metrics
|
11
|
+
## Current OpenTelemetry OTLP exporter version
|
12
|
+
VERSION = '0.1.0'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright The OpenTelemetry Authors
|
4
|
+
#
|
5
|
+
# SPDX-License-Identifier: Apache-2.0
|
6
|
+
|
7
|
+
require 'opentelemetry/exporter/otlp/metrics/version'
|
8
|
+
require 'opentelemetry/exporter/otlp/metrics/metrics_exporter'
|
9
|
+
|
10
|
+
# OpenTelemetry is an open source observability framework, providing a
|
11
|
+
# general-purpose API, SDK, and related tools required for the instrumentation
|
12
|
+
# of cloud-native software, frameworks, and libraries.
|
13
|
+
#
|
14
|
+
# The OpenTelemetry module provides global accessors for telemetry objects.
|
15
|
+
# See the documentation for the `opentelemetry-api` gem for details.
|
16
|
+
module OpenTelemetry
|
17
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
3
|
+
# source: opentelemetry/proto/collector/logs/v1/logs_service.proto
|
4
|
+
|
5
|
+
require 'google/protobuf'
|
6
|
+
|
7
|
+
require 'opentelemetry/proto/logs/v1/logs_pb'
|
8
|
+
|
9
|
+
|
10
|
+
descriptor_data = "\n8opentelemetry/proto/collector/logs/v1/logs_service.proto\x12%opentelemetry.proto.collector.logs.v1\x1a&opentelemetry/proto/logs/v1/logs.proto\"\\\n\x18\x45xportLogsServiceRequest\x12@\n\rresource_logs\x18\x01 \x03(\x0b\x32).opentelemetry.proto.logs.v1.ResourceLogs\"u\n\x19\x45xportLogsServiceResponse\x12X\n\x0fpartial_success\x18\x01 \x01(\x0b\x32?.opentelemetry.proto.collector.logs.v1.ExportLogsPartialSuccess\"O\n\x18\x45xportLogsPartialSuccess\x12\x1c\n\x14rejected_log_records\x18\x01 \x01(\x03\x12\x15\n\rerror_message\x18\x02 \x01(\t2\x9d\x01\n\x0bLogsService\x12\x8d\x01\n\x06\x45xport\x12?.opentelemetry.proto.collector.logs.v1.ExportLogsServiceRequest\x1a@.opentelemetry.proto.collector.logs.v1.ExportLogsServiceResponse\"\x00\x42\x98\x01\n(io.opentelemetry.proto.collector.logs.v1B\x10LogsServiceProtoP\x01Z0go.opentelemetry.io/proto/otlp/collector/logs/v1\xaa\x02%OpenTelemetry.Proto.Collector.Logs.V1b\x06proto3"
|
11
|
+
|
12
|
+
pool = Google::Protobuf::DescriptorPool.generated_pool
|
13
|
+
pool.add_serialized_file(descriptor_data)
|
14
|
+
|
15
|
+
module Opentelemetry
|
16
|
+
module Proto
|
17
|
+
module Collector
|
18
|
+
module Logs
|
19
|
+
module V1
|
20
|
+
ExportLogsServiceRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("opentelemetry.proto.collector.logs.v1.ExportLogsServiceRequest").msgclass
|
21
|
+
ExportLogsServiceResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("opentelemetry.proto.collector.logs.v1.ExportLogsServiceResponse").msgclass
|
22
|
+
ExportLogsPartialSuccess = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("opentelemetry.proto.collector.logs.v1.ExportLogsPartialSuccess").msgclass
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
3
|
+
# source: opentelemetry/proto/collector/metrics/v1/metrics_service.proto
|
4
|
+
|
5
|
+
require 'google/protobuf'
|
6
|
+
|
7
|
+
require 'opentelemetry/proto/metrics/v1/metrics_pb'
|
8
|
+
|
9
|
+
|
10
|
+
descriptor_data = "\n>opentelemetry/proto/collector/metrics/v1/metrics_service.proto\x12(opentelemetry.proto.collector.metrics.v1\x1a,opentelemetry/proto/metrics/v1/metrics.proto\"h\n\x1b\x45xportMetricsServiceRequest\x12I\n\x10resource_metrics\x18\x01 \x03(\x0b\x32/.opentelemetry.proto.metrics.v1.ResourceMetrics\"~\n\x1c\x45xportMetricsServiceResponse\x12^\n\x0fpartial_success\x18\x01 \x01(\x0b\x32\x45.opentelemetry.proto.collector.metrics.v1.ExportMetricsPartialSuccess\"R\n\x1b\x45xportMetricsPartialSuccess\x12\x1c\n\x14rejected_data_points\x18\x01 \x01(\x03\x12\x15\n\rerror_message\x18\x02 \x01(\t2\xac\x01\n\x0eMetricsService\x12\x99\x01\n\x06\x45xport\x12\x45.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceRequest\x1a\x46.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceResponse\"\x00\x42\xa4\x01\n+io.opentelemetry.proto.collector.metrics.v1B\x13MetricsServiceProtoP\x01Z3go.opentelemetry.io/proto/otlp/collector/metrics/v1\xaa\x02(OpenTelemetry.Proto.Collector.Metrics.V1b\x06proto3"
|
11
|
+
|
12
|
+
pool = Google::Protobuf::DescriptorPool.generated_pool
|
13
|
+
pool.add_serialized_file(descriptor_data)
|
14
|
+
|
15
|
+
module Opentelemetry
|
16
|
+
module Proto
|
17
|
+
module Collector
|
18
|
+
module Metrics
|
19
|
+
module V1
|
20
|
+
ExportMetricsServiceRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceRequest").msgclass
|
21
|
+
ExportMetricsServiceResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceResponse").msgclass
|
22
|
+
ExportMetricsPartialSuccess = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("opentelemetry.proto.collector.metrics.v1.ExportMetricsPartialSuccess").msgclass
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
3
|
+
# source: opentelemetry/proto/collector/trace/v1/trace_service.proto
|
4
|
+
|
5
|
+
require 'google/protobuf'
|
6
|
+
|
7
|
+
require 'opentelemetry/proto/trace/v1/trace_pb'
|
8
|
+
|
9
|
+
|
10
|
+
descriptor_data = "\n:opentelemetry/proto/collector/trace/v1/trace_service.proto\x12&opentelemetry.proto.collector.trace.v1\x1a(opentelemetry/proto/trace/v1/trace.proto\"`\n\x19\x45xportTraceServiceRequest\x12\x43\n\x0eresource_spans\x18\x01 \x03(\x0b\x32+.opentelemetry.proto.trace.v1.ResourceSpans\"x\n\x1a\x45xportTraceServiceResponse\x12Z\n\x0fpartial_success\x18\x01 \x01(\x0b\x32\x41.opentelemetry.proto.collector.trace.v1.ExportTracePartialSuccess\"J\n\x19\x45xportTracePartialSuccess\x12\x16\n\x0erejected_spans\x18\x01 \x01(\x03\x12\x15\n\rerror_message\x18\x02 \x01(\t2\xa2\x01\n\x0cTraceService\x12\x91\x01\n\x06\x45xport\x12\x41.opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest\x1a\x42.opentelemetry.proto.collector.trace.v1.ExportTraceServiceResponse\"\x00\x42\x9c\x01\n)io.opentelemetry.proto.collector.trace.v1B\x11TraceServiceProtoP\x01Z1go.opentelemetry.io/proto/otlp/collector/trace/v1\xaa\x02&OpenTelemetry.Proto.Collector.Trace.V1b\x06proto3"
|
11
|
+
|
12
|
+
pool = Google::Protobuf::DescriptorPool.generated_pool
|
13
|
+
pool.add_serialized_file(descriptor_data)
|
14
|
+
|
15
|
+
module Opentelemetry
|
16
|
+
module Proto
|
17
|
+
module Collector
|
18
|
+
module Trace
|
19
|
+
module V1
|
20
|
+
ExportTraceServiceRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest").msgclass
|
21
|
+
ExportTraceServiceResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("opentelemetry.proto.collector.trace.v1.ExportTraceServiceResponse").msgclass
|
22
|
+
ExportTracePartialSuccess = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("opentelemetry.proto.collector.trace.v1.ExportTracePartialSuccess").msgclass
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
3
|
+
# source: opentelemetry/proto/common/v1/common.proto
|
4
|
+
|
5
|
+
require 'google/protobuf'
|
6
|
+
|
7
|
+
|
8
|
+
descriptor_data = "\n*opentelemetry/proto/common/v1/common.proto\x12\x1dopentelemetry.proto.common.v1\"\x8c\x02\n\x08\x41nyValue\x12\x16\n\x0cstring_value\x18\x01 \x01(\tH\x00\x12\x14\n\nbool_value\x18\x02 \x01(\x08H\x00\x12\x13\n\tint_value\x18\x03 \x01(\x03H\x00\x12\x16\n\x0c\x64ouble_value\x18\x04 \x01(\x01H\x00\x12@\n\x0b\x61rray_value\x18\x05 \x01(\x0b\x32).opentelemetry.proto.common.v1.ArrayValueH\x00\x12\x43\n\x0ckvlist_value\x18\x06 \x01(\x0b\x32+.opentelemetry.proto.common.v1.KeyValueListH\x00\x12\x15\n\x0b\x62ytes_value\x18\x07 \x01(\x0cH\x00\x42\x07\n\x05value\"E\n\nArrayValue\x12\x37\n\x06values\x18\x01 \x03(\x0b\x32\'.opentelemetry.proto.common.v1.AnyValue\"G\n\x0cKeyValueList\x12\x37\n\x06values\x18\x01 \x03(\x0b\x32\'.opentelemetry.proto.common.v1.KeyValue\"O\n\x08KeyValue\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x36\n\x05value\x18\x02 \x01(\x0b\x32\'.opentelemetry.proto.common.v1.AnyValue\"\x94\x01\n\x14InstrumentationScope\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07version\x18\x02 \x01(\t\x12;\n\nattributes\x18\x03 \x03(\x0b\x32\'.opentelemetry.proto.common.v1.KeyValue\x12 \n\x18\x64ropped_attributes_count\x18\x04 \x01(\rB{\n io.opentelemetry.proto.common.v1B\x0b\x43ommonProtoP\x01Z(go.opentelemetry.io/proto/otlp/common/v1\xaa\x02\x1dOpenTelemetry.Proto.Common.V1b\x06proto3"
|
9
|
+
|
10
|
+
pool = Google::Protobuf::DescriptorPool.generated_pool
|
11
|
+
pool.add_serialized_file(descriptor_data)
|
12
|
+
|
13
|
+
module Opentelemetry
|
14
|
+
module Proto
|
15
|
+
module Common
|
16
|
+
module V1
|
17
|
+
AnyValue = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("opentelemetry.proto.common.v1.AnyValue").msgclass
|
18
|
+
ArrayValue = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("opentelemetry.proto.common.v1.ArrayValue").msgclass
|
19
|
+
KeyValueList = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("opentelemetry.proto.common.v1.KeyValueList").msgclass
|
20
|
+
KeyValue = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("opentelemetry.proto.common.v1.KeyValue").msgclass
|
21
|
+
InstrumentationScope = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("opentelemetry.proto.common.v1.InstrumentationScope").msgclass
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
3
|
+
# source: opentelemetry/proto/logs/v1/logs.proto
|
4
|
+
|
5
|
+
require 'google/protobuf'
|
6
|
+
|
7
|
+
require 'opentelemetry/proto/common/v1/common_pb'
|
8
|
+
require 'opentelemetry/proto/resource/v1/resource_pb'
|
9
|
+
|
10
|
+
|
11
|
+
descriptor_data = "\n&opentelemetry/proto/logs/v1/logs.proto\x12\x1bopentelemetry.proto.logs.v1\x1a*opentelemetry/proto/common/v1/common.proto\x1a.opentelemetry/proto/resource/v1/resource.proto\"L\n\x08LogsData\x12@\n\rresource_logs\x18\x01 \x03(\x0b\x32).opentelemetry.proto.logs.v1.ResourceLogs\"\xa3\x01\n\x0cResourceLogs\x12;\n\x08resource\x18\x01 \x01(\x0b\x32).opentelemetry.proto.resource.v1.Resource\x12:\n\nscope_logs\x18\x02 \x03(\x0b\x32&.opentelemetry.proto.logs.v1.ScopeLogs\x12\x12\n\nschema_url\x18\x03 \x01(\tJ\x06\x08\xe8\x07\x10\xe9\x07\"\xa0\x01\n\tScopeLogs\x12\x42\n\x05scope\x18\x01 \x01(\x0b\x32\x33.opentelemetry.proto.common.v1.InstrumentationScope\x12;\n\x0blog_records\x18\x02 \x03(\x0b\x32&.opentelemetry.proto.logs.v1.LogRecord\x12\x12\n\nschema_url\x18\x03 \x01(\t\"\xef\x02\n\tLogRecord\x12\x16\n\x0etime_unix_nano\x18\x01 \x01(\x06\x12\x1f\n\x17observed_time_unix_nano\x18\x0b \x01(\x06\x12\x44\n\x0fseverity_number\x18\x02 \x01(\x0e\x32+.opentelemetry.proto.logs.v1.SeverityNumber\x12\x15\n\rseverity_text\x18\x03 \x01(\t\x12\x35\n\x04\x62ody\x18\x05 \x01(\x0b\x32\'.opentelemetry.proto.common.v1.AnyValue\x12;\n\nattributes\x18\x06 \x03(\x0b\x32\'.opentelemetry.proto.common.v1.KeyValue\x12 \n\x18\x64ropped_attributes_count\x18\x07 \x01(\r\x12\r\n\x05\x66lags\x18\x08 \x01(\x07\x12\x10\n\x08trace_id\x18\t \x01(\x0c\x12\x0f\n\x07span_id\x18\n \x01(\x0cJ\x04\x08\x04\x10\x05*\xc3\x05\n\x0eSeverityNumber\x12\x1f\n\x1bSEVERITY_NUMBER_UNSPECIFIED\x10\x00\x12\x19\n\x15SEVERITY_NUMBER_TRACE\x10\x01\x12\x1a\n\x16SEVERITY_NUMBER_TRACE2\x10\x02\x12\x1a\n\x16SEVERITY_NUMBER_TRACE3\x10\x03\x12\x1a\n\x16SEVERITY_NUMBER_TRACE4\x10\x04\x12\x19\n\x15SEVERITY_NUMBER_DEBUG\x10\x05\x12\x1a\n\x16SEVERITY_NUMBER_DEBUG2\x10\x06\x12\x1a\n\x16SEVERITY_NUMBER_DEBUG3\x10\x07\x12\x1a\n\x16SEVERITY_NUMBER_DEBUG4\x10\x08\x12\x18\n\x14SEVERITY_NUMBER_INFO\x10\t\x12\x19\n\x15SEVERITY_NUMBER_INFO2\x10\n\x12\x19\n\x15SEVERITY_NUMBER_INFO3\x10\x0b\x12\x19\n\x15SEVERITY_NUMBER_INFO4\x10\x0c\x12\x18\n\x14SEVERITY_NUMBER_WARN\x10\r\x12\x19\n\x15SEVERITY_NUMBER_WARN2\x10\x0e\x12\x19\n\x15SEVERITY_NUMBER_WARN3\x10\x0f\x12\x19\n\x15SEVERITY_NUMBER_WARN4\x10\x10\x12\x19\n\x15SEVERITY_NUMBER_ERROR\x10\x11\x12\x1a\n\x16SEVERITY_NUMBER_ERROR2\x10\x12\x12\x1a\n\x16SEVERITY_NUMBER_ERROR3\x10\x13\x12\x1a\n\x16SEVERITY_NUMBER_ERROR4\x10\x14\x12\x19\n\x15SEVERITY_NUMBER_FATAL\x10\x15\x12\x1a\n\x16SEVERITY_NUMBER_FATAL2\x10\x16\x12\x1a\n\x16SEVERITY_NUMBER_FATAL3\x10\x17\x12\x1a\n\x16SEVERITY_NUMBER_FATAL4\x10\x18*Y\n\x0eLogRecordFlags\x12\x1f\n\x1bLOG_RECORD_FLAGS_DO_NOT_USE\x10\x00\x12&\n!LOG_RECORD_FLAGS_TRACE_FLAGS_MASK\x10\xff\x01\x42s\n\x1eio.opentelemetry.proto.logs.v1B\tLogsProtoP\x01Z&go.opentelemetry.io/proto/otlp/logs/v1\xaa\x02\x1bOpenTelemetry.Proto.Logs.V1b\x06proto3"
|
12
|
+
|
13
|
+
pool = Google::Protobuf::DescriptorPool.generated_pool
|
14
|
+
pool.add_serialized_file(descriptor_data)
|
15
|
+
|
16
|
+
module Opentelemetry
|
17
|
+
module Proto
|
18
|
+
module Logs
|
19
|
+
module V1
|
20
|
+
LogsData = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("opentelemetry.proto.logs.v1.LogsData").msgclass
|
21
|
+
ResourceLogs = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("opentelemetry.proto.logs.v1.ResourceLogs").msgclass
|
22
|
+
ScopeLogs = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("opentelemetry.proto.logs.v1.ScopeLogs").msgclass
|
23
|
+
LogRecord = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("opentelemetry.proto.logs.v1.LogRecord").msgclass
|
24
|
+
SeverityNumber = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("opentelemetry.proto.logs.v1.SeverityNumber").enummodule
|
25
|
+
LogRecordFlags = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("opentelemetry.proto.logs.v1.LogRecordFlags").enummodule
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|