opentelemetry-exporter-otlp-metrics 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|