From 3f106c6462ecdf79c515d6ab9e0135c32bc50520 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 24 Jul 2024 10:42:56 -0700 Subject: [PATCH 01/20] release: Release 4 gems (#1654) * opentelemetry-api 1.3.0 (was 1.2.5) * opentelemetry-sdk 1.5.0 (was 1.4.1) * opentelemetry-exporter-otlp 0.28.1 (was 0.28.0) * opentelemetry-semantic_conventions 1.10.1 (was 1.10.0) Co-authored-by: Daniel Azuma --- api/CHANGELOG.md | 4 ++++ api/lib/opentelemetry/version.rb | 2 +- exporter/otlp/CHANGELOG.md | 4 ++++ exporter/otlp/lib/opentelemetry/exporter/otlp/version.rb | 2 +- sdk/CHANGELOG.md | 5 +++++ sdk/lib/opentelemetry/sdk/version.rb | 2 +- semantic_conventions/CHANGELOG.md | 6 ++++++ .../lib/opentelemetry/semantic_conventions/version.rb | 2 +- 8 files changed, 23 insertions(+), 4 deletions(-) diff --git a/api/CHANGELOG.md b/api/CHANGELOG.md index 8eab444104..43b6e5fb6d 100644 --- a/api/CHANGELOG.md +++ b/api/CHANGELOG.md @@ -1,5 +1,9 @@ # Release History: opentelemetry-api +### v1.3.0 / 2024-07-24 + +* ADDED: Add add_link to span api/sdk + ### v1.2.5 / 2024-02-20 * FIXED: Replace Context stack on clear diff --git a/api/lib/opentelemetry/version.rb b/api/lib/opentelemetry/version.rb index db6ee4be29..009daf16ba 100644 --- a/api/lib/opentelemetry/version.rb +++ b/api/lib/opentelemetry/version.rb @@ -6,5 +6,5 @@ module OpenTelemetry ## Current OpenTelemetry version - VERSION = '1.2.5' + VERSION = '1.3.0' end diff --git a/exporter/otlp/CHANGELOG.md b/exporter/otlp/CHANGELOG.md index a60e72e8b8..88ee03a7dc 100644 --- a/exporter/otlp/CHANGELOG.md +++ b/exporter/otlp/CHANGELOG.md @@ -1,5 +1,9 @@ # Release History: opentelemetry-exporter-otlp +### v0.28.1 / 2024-07-24 + +* ADDED: Improve SSL error logging. + ### v0.28.0 / 2024-06-19 * ADDED: Bump google_protobuf >=3.18, < 5.a diff --git a/exporter/otlp/lib/opentelemetry/exporter/otlp/version.rb b/exporter/otlp/lib/opentelemetry/exporter/otlp/version.rb index 7a2f368bc5..b12e7e7de2 100644 --- a/exporter/otlp/lib/opentelemetry/exporter/otlp/version.rb +++ b/exporter/otlp/lib/opentelemetry/exporter/otlp/version.rb @@ -8,7 +8,7 @@ module OpenTelemetry module Exporter module OTLP ## Current OpenTelemetry OTLP exporter version - VERSION = '0.28.0' + VERSION = '0.28.1' end end end diff --git a/sdk/CHANGELOG.md b/sdk/CHANGELOG.md index 7404541b84..a832c5ed8a 100644 --- a/sdk/CHANGELOG.md +++ b/sdk/CHANGELOG.md @@ -1,5 +1,10 @@ # Release History: opentelemetry-sdk +### v1.5.0 / 2024-07-24 + +* ADDED: Add add_link to span api/sdk +* FIXED: Update `untraced` to suppress logging "Calling finish on an ended Span" warnings + ### v1.4.1 / 2024-03-21 * FIXED: ForwardingLogger should forward block param. diff --git a/sdk/lib/opentelemetry/sdk/version.rb b/sdk/lib/opentelemetry/sdk/version.rb index c875cf6efe..bdc6d2eacd 100644 --- a/sdk/lib/opentelemetry/sdk/version.rb +++ b/sdk/lib/opentelemetry/sdk/version.rb @@ -7,6 +7,6 @@ module OpenTelemetry module SDK ## Current OpenTelemetry version - VERSION = '1.4.1' + VERSION = '1.5.0' end end diff --git a/semantic_conventions/CHANGELOG.md b/semantic_conventions/CHANGELOG.md index 4d8825a434..71c8e5c007 100644 --- a/semantic_conventions/CHANGELOG.md +++ b/semantic_conventions/CHANGELOG.md @@ -1,5 +1,11 @@ # Release History: opentelemetry-semantic_conventions +### v1.10.1 / 2024-07-24 + +* BREAKING CHANGE: Remove support for EoL Ruby 2.7 + +* ADDED: Remove support for EoL Ruby 2.7 + ### v1.10.0 / 2023-05-30 * FIXED: Warning messages from gem build diff --git a/semantic_conventions/lib/opentelemetry/semantic_conventions/version.rb b/semantic_conventions/lib/opentelemetry/semantic_conventions/version.rb index b323abd7e3..c40014a4ed 100644 --- a/semantic_conventions/lib/opentelemetry/semantic_conventions/version.rb +++ b/semantic_conventions/lib/opentelemetry/semantic_conventions/version.rb @@ -6,6 +6,6 @@ module OpenTelemetry module SemanticConventions - VERSION = '1.10.0' + VERSION = '1.10.1' end end From 309de202db9bc440730b611a858a26655f618a60 Mon Sep 17 00:00:00 2001 From: Kayla Reopelle <87386821+kaylareopelle@users.noreply.github.com> Date: Thu, 25 Jul 2024 15:55:29 -0700 Subject: [PATCH 02/20] feat: Update Logger#on_emit to create LogRecords (#1611) * feat: Update Logger#on_emit to create LogRecords * Update logs_sdk/lib/opentelemetry/sdk/logs/log_record_data.rb Co-authored-by: James Bunch * feat: Use Context instead of SpanContext Previously, trace_id, span_id, and trace_flags were assigned based on the OpenTelemetry::Trace.current_span's SpanContext. The specification requires a context be accepted as an argument on emit. If no context is provided, OpenTelemetry::Context.current will be used. If there isn't a current span on OpenTelemetry::Context.current trace_id, span_id, and trace_flags will be nil unless they are passed as arguments. Also, remaining #emit methods are renamed to #on_emit. * chore: Add logger provider on emit test * chore: Remove span_context from LogRecord This code was leftover and should have been removed. Now, the specfic `trace_id`, `span_id`, and `trace_flags` attributes are used instead. * feat: Use Time for timestamps Convert Time to the appropriate nanosecond format in LogRecordData. Remove Float option from argument declaration. * feat: Move nanosecond conversion to LogRecord The LogRecordData definition says the time is already Integer nanoseconds since epoch, so move the logic to convert the timestamp to LogRecord#to_log_record_data * feat: Create LogRecord in LoggerProvider#on_emit * Follow the pattern in TracerProvider#internal_start_span * Update spacing in LogRecordData * Remove resource references outside LoggerProvider * Remove Logger attr_readers for logger_provider and instrumentation_scope --------- Co-authored-by: James Bunch Co-authored-by: Matthew Wear --- logs_api/lib/opentelemetry/logs/logger.rb | 38 +++--- .../test/opentelemetry/logs/logger_test.rb | 2 +- logs_sdk/lib/opentelemetry/sdk/logs.rb | 2 + .../lib/opentelemetry/sdk/logs/log_record.rb | 109 ++++++++++++++++++ .../opentelemetry/sdk/logs/log_record_data.rb | 28 +++++ logs_sdk/lib/opentelemetry/sdk/logs/logger.rb | 60 ++++++++++ .../opentelemetry/sdk/logs/logger_provider.rb | 30 ++++- .../opentelemetry/sdk/logs/log_record_test.rb | 100 ++++++++++++++++ .../sdk/logs/logger_provider_test.rb | 47 +++++++- .../opentelemetry/sdk/logs/logger_test.rb | 42 +++++++ 10 files changed, 439 insertions(+), 19 deletions(-) create mode 100644 logs_sdk/lib/opentelemetry/sdk/logs/log_record.rb create mode 100644 logs_sdk/lib/opentelemetry/sdk/logs/log_record_data.rb create mode 100644 logs_sdk/test/opentelemetry/sdk/logs/log_record_test.rb create mode 100644 logs_sdk/test/opentelemetry/sdk/logs/logger_test.rb diff --git a/logs_api/lib/opentelemetry/logs/logger.rb b/logs_api/lib/opentelemetry/logs/logger.rb index 91649e27d6..c6caa34a77 100644 --- a/logs_api/lib/opentelemetry/logs/logger.rb +++ b/logs_api/lib/opentelemetry/logs/logger.rb @@ -12,36 +12,44 @@ class Logger # Emit a {LogRecord} to the processing pipeline. # - # @param timestamp [optional Float, Time] Time in nanoseconds since Unix - # epoch when the event occurred measured by the origin clock, i.e. the - # time at the source. - # @param observed_timestamp [optional Float, Time] Time in nanoseconds - # since Unix epoch when the event was observed by the collection system. - # Intended default: Process.clock_gettime(Process::CLOCK_REALTIME) + # @param timestamp [optional Time] Time when the event occurred. + # @param observed_timestamp [optional Time] Time when the event was + # observed by the collection system. # @param context [optional Context] The Context to associate with the # LogRecord. Intended default: OpenTelemetry::Context.current # @param severity_number [optional Integer] Numerical value of the # severity. Smaller numerical values correspond to less severe events # (such as debug events), larger numerical values correspond to more # severe events (such as errors and critical events). - # @param severity_text [optional String] Original string representation of + # @param [optional String] severity_text Original string representation of # the severity as it is known at the source. Also known as log level. - # @param body [optional String, Numeric, Boolean, Array, Hash{String => String, Numeric, Boolean, Array}] A value containing the body of the log record. - # @param attributes [optional Hash{String => String, Numeric, Boolean, - # Array}] Additional information about the - # event. + # Numeric, Boolean>}] body A value containing the body of the log record. + # @param [optional String] trace_id The trace ID associated with the + # current context. + # @param [optional String] span_id The span ID associated with the + # current context. + # @param [optional TraceFlags] trace_flags The trace flags associated + # with the current context. + # @param [optional Hash{String => String, Numeric, Boolean, + # Array}] attributes Additional information + # about the event. + # @param [optional Context] context The Context to associate with the + # LogRecord. Intended default: OpenTelemetry::Context.current # # @api public - def emit( + def on_emit( timestamp: nil, observed_timestamp: nil, - context: nil, severity_number: nil, severity_text: nil, body: nil, - attributes: nil + trace_id: nil, + span_id: nil, + trace_flags: nil, + attributes: nil, + context: nil ) end # rubocop:enable Style/EmptyMethod diff --git a/logs_api/test/opentelemetry/logs/logger_test.rb b/logs_api/test/opentelemetry/logs/logger_test.rb index 11ae4ceb45..9fd35a651c 100644 --- a/logs_api/test/opentelemetry/logs/logger_test.rb +++ b/logs_api/test/opentelemetry/logs/logger_test.rb @@ -11,7 +11,7 @@ describe '#emit' do it 'returns nil, as it is a no-op method' do - assert_nil(logger.emit) + assert_nil(logger.on_emit) end end end diff --git a/logs_sdk/lib/opentelemetry/sdk/logs.rb b/logs_sdk/lib/opentelemetry/sdk/logs.rb index 4331257d39..fbd978c68c 100644 --- a/logs_sdk/lib/opentelemetry/sdk/logs.rb +++ b/logs_sdk/lib/opentelemetry/sdk/logs.rb @@ -7,6 +7,8 @@ require_relative 'logs/version' require_relative 'logs/logger' require_relative 'logs/logger_provider' +require_relative 'logs/log_record' +require_relative 'logs/log_record_data' require_relative 'logs/log_record_processor' require_relative 'logs/export' diff --git a/logs_sdk/lib/opentelemetry/sdk/logs/log_record.rb b/logs_sdk/lib/opentelemetry/sdk/logs/log_record.rb new file mode 100644 index 0000000000..255158df8d --- /dev/null +++ b/logs_sdk/lib/opentelemetry/sdk/logs/log_record.rb @@ -0,0 +1,109 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Logs + # Implementation of OpenTelemetry::Logs::LogRecord that records log events. + class LogRecord < OpenTelemetry::Logs::LogRecord + attr_accessor :timestamp, + :observed_timestamp, + :severity_text, + :severity_number, + :body, + :attributes, + :trace_id, + :span_id, + :trace_flags, + :resource, + :instrumentation_scope + + # Creates a new {LogRecord}. + # + # @param [optional Time] timestamp Time when the event occurred. + # @param [optional Time] observed_timestamp Time when the event + # was observed by the collection system. If nil, will first attempt + # to set to `timestamp`. If `timestamp` is nil, will set to Time.now. + # @param [optional OpenTelemetry::Trace::SpanContext] span_context The + # OpenTelemetry::Trace::SpanContext to associate with the LogRecord. + # @param [optional String] severity_text The log severity, also known as + # log level. + # @param [optional Integer] severity_number The numerical value of the + # log severity. + # @param [optional String, Numeric, Boolean, Array, Hash{String => String, Numeric, Boolean, Array}] body The body of the {LogRecord}. + # @param [optional Hash{String => String, Numeric, Boolean, + # Array}] attributes Attributes to associate + # with the {LogRecord}. + # @param [optional String] trace_id The trace ID associated with the + # current context. + # @param [optional String] span_id The span ID associated with the + # current context. + # @param [optional OpenTelemetry::Trace::TraceFlags] trace_flags The + # trace flags associated with the current context. + # @param [optional OpenTelemetry::SDK::Resources::Resource] recource The + # source of the log, desrived from the LoggerProvider. + # @param [optional OpenTelemetry::SDK::InstrumentationScope] instrumentation_scope + # The instrumentation scope, derived from the emitting Logger + # + # + # @return [LogRecord] + def initialize( + timestamp: nil, + observed_timestamp: nil, + severity_text: nil, + severity_number: nil, + body: nil, + attributes: nil, + trace_id: nil, + span_id: nil, + trace_flags: nil, + resource: nil, + instrumentation_scope: nil + ) + @timestamp = timestamp + @observed_timestamp = observed_timestamp || timestamp || Time.now + @severity_text = severity_text + @severity_number = severity_number + @body = body + @attributes = attributes.nil? ? nil : Hash[attributes] # We need a mutable copy of attributes + @trace_id = trace_id + @span_id = span_id + @trace_flags = trace_flags + @resource = resource + @instrumentation_scope = instrumentation_scope + @total_recorded_attributes = @attributes&.size || 0 + end + + def to_log_record_data + LogRecordData.new( + to_integer_nanoseconds(@timestamp), + to_integer_nanoseconds(@observed_timestamp), + @severity_text, + @severity_number, + @body, + @attributes, + @trace_id, + @span_id, + @trace_flags, + @resource, + @instrumentation_scope, + @total_recorded_attributes + ) + end + + private + + def to_integer_nanoseconds(timestamp) + return unless timestamp.is_a?(Time) + + t = (timestamp.to_r * 10**9).to_i + end + end + end + end +end diff --git a/logs_sdk/lib/opentelemetry/sdk/logs/log_record_data.rb b/logs_sdk/lib/opentelemetry/sdk/logs/log_record_data.rb new file mode 100644 index 0000000000..5bf304f85c --- /dev/null +++ b/logs_sdk/lib/opentelemetry/sdk/logs/log_record_data.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Logs + # LogRecordData is a Struct containing {LogRecord} data for export. + LogRecordData = Struct.new(:timestamp, # optional Integer nanoseconds since Epoch + :observed_timestamp, # Integer nanoseconds since Epoch + :severity_text, # optional String + :severity_number, # optional Integer + :body, # optional String, Numeric, Boolean, Array, Hash{String => String, Numeric, Boolean, + # Array} + :attributes, # optional Hash{String => String, Numeric, Boolean, + # Array} + :trace_id, # optional String (16-byte binary) + :span_id, # optional String (8-byte binary) + :trace_flags, # optional Integer (8-bit byte of bit flags) + :resource, # optional OpenTelemetry::SDK::Resources::Resource + :instrumentation_scope, # optional OpenTelemetry::SDK::InstrumentationScope + :total_recorded_attributes) # Integer + end + end +end diff --git a/logs_sdk/lib/opentelemetry/sdk/logs/logger.rb b/logs_sdk/lib/opentelemetry/sdk/logs/logger.rb index e1b855079b..15fa3bcaf3 100644 --- a/logs_sdk/lib/opentelemetry/sdk/logs/logger.rb +++ b/logs_sdk/lib/opentelemetry/sdk/logs/logger.rb @@ -25,6 +25,66 @@ def initialize(name, version, logger_provider) @instrumentation_scope = InstrumentationScope.new(name, version) @logger_provider = logger_provider end + + # Emit a {LogRecord} to the processing pipeline. + # + # @param [optional Time] timestamp Time when the event occurred. + # @param [optional Time] observed_timestamp Time when the event was + # observed by the collection system. + # @param [optional OpenTelemetry::Trace::SpanContext] span_context The + # OpenTelemetry::Trace::SpanContext to associate with the + # {LogRecord}. + # @param severity_number [optional Integer] Numerical value of the + # severity. Smaller numerical values correspond to less severe events + # (such as debug events), larger numerical values correspond to more + # severe events (such as errors and critical events). + # @param [optional String, Numeric, Boolean, Array, Hash{String => String, Numeric, Boolean, Array}] body A value containing the body of the log record. + # @param [optional Hash{String => String, Numeric, Boolean, + # Array}] attributes Additional information + # about the event. + # @param [optional String (16-byte binary)] trace_id Request trace id as + # defined in {https://www.w3.org/TR/trace-context/#trace-id W3C Trace Context}. + # Can be set for logs that are part of request processing and have an + # assigned trace id. + # @param [optional String (8-byte binary)] span_id Span id. Can be set + # for logs that are part of a particular processing span. If span_id + # is present trace_id should also be present. + # @param [optional Integer (8-bit byte of bit flags)] trace_flags Trace + # flag as defined in {https://www.w3.org/TR/trace-context/#trace-flags W3C Trace Context} + # specification. At the time of writing the specification defines one + # flag - the SAMPLED flag. + # @param [optional OpenTelemetry::Context] context The OpenTelemetry::Context + # to associate with the {LogRecord}. + # + # @api public + def on_emit(timestamp: nil, + observed_timestamp: Time.now, + severity_text: nil, + severity_number: nil, + body: nil, + attributes: nil, + trace_id: nil, + span_id: nil, + trace_flags: nil, + context: OpenTelemetry::Context.current) + + current_span = OpenTelemetry::Trace.current_span(context) + span_context = current_span.context unless current_span == OpenTelemetry::Trace::Span::INVALID + + @logger_provider.on_emit(timestamp: timestamp, + observed_timestamp: observed_timestamp, + severity_text: severity_text, + severity_number: severity_number, + body: body, + attributes: attributes, + trace_id: trace_id || span_context&.trace_id, + span_id: span_id || span_context&.span_id, + trace_flags: trace_flags || span_context&.trace_flags, + instrumentation_scope: @instrumentation_scope, + context: context) + end end end end diff --git a/logs_sdk/lib/opentelemetry/sdk/logs/logger_provider.rb b/logs_sdk/lib/opentelemetry/sdk/logs/logger_provider.rb index 756811c206..99e7df10e5 100644 --- a/logs_sdk/lib/opentelemetry/sdk/logs/logger_provider.rb +++ b/logs_sdk/lib/opentelemetry/sdk/logs/logger_provider.rb @@ -9,8 +9,6 @@ module SDK module Logs # The SDK implementation of OpenTelemetry::Logs::LoggerProvider. class LoggerProvider < OpenTelemetry::Logs::LoggerProvider - attr_reader :resource - UNEXPECTED_ERROR_MESSAGE = 'unexpected error in ' \ 'OpenTelemetry::SDK::Logs::LoggerProvider#%s' @@ -120,6 +118,34 @@ def force_flush(timeout: nil) results.max || Export::SUCCESS end end + + # @api private + def on_emit(timestamp: nil, + observed_timestamp: nil, + severity_text: nil, + severity_number: nil, + body: nil, + attributes: nil, + trace_id: nil, + span_id: nil, + trace_flags: nil, + instrumentation_scope: nil, + context: nil) + + log_record = LogRecord.new(timestamp: timestamp, + observed_timestamp: observed_timestamp, + severity_text: severity_text, + severity_number: severity_number, + body: body, + attributes: attributes, + trace_id: trace_id, + span_id: span_id, + trace_flags: trace_flags, + resource: @resource, + instrumentation_scope: instrumentation_scope) + + @log_record_processors.each { |processor| processor.on_emit(log_record, context) } + end end end end diff --git a/logs_sdk/test/opentelemetry/sdk/logs/log_record_test.rb b/logs_sdk/test/opentelemetry/sdk/logs/log_record_test.rb new file mode 100644 index 0000000000..1d9711a946 --- /dev/null +++ b/logs_sdk/test/opentelemetry/sdk/logs/log_record_test.rb @@ -0,0 +1,100 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'test_helper' + +describe OpenTelemetry::SDK::Logs::LogRecord do + Logs = OpenTelemetry::SDK::Logs + let(:log_record) { Logs::LogRecord.new(**args) } + let(:args) { {} } + let(:logger) { Logs::Logger.new('', '', Logs::LoggerProvider.new) } + + describe '#initialize' do + describe 'observed_timestamp' do + describe 'when observed_timestamp is present' do + let(:observed_timestamp) { '1692661486.2841358' } + let(:args) { { observed_timestamp: observed_timestamp } } + + it 'is equal to observed_timestamp' do + assert_equal(observed_timestamp, log_record.observed_timestamp) + end + + it 'is not equal to timestamp' do + refute_equal(log_record.timestamp, log_record.observed_timestamp) + end + + # Process.clock_gettime is used to set the current time + # That method returns a Float. Since the stubbed value of + # observed_timestamp is a String, we can know the the + # observed_timestamp was not set to the value of Process.clock_gettime + # by making sure its value is not a Float. + it 'is not equal to the current time' do + refute_instance_of(Float, log_record.observed_timestamp) + end + end + + describe 'when timestamp is present' do + let(:timestamp) { Time.now } + let(:args) { { timestamp: timestamp } } + + it 'is equal to timestamp' do + assert_equal(timestamp, log_record.observed_timestamp) + end + end + + describe 'when observed_timestamp and timestamp are nil' do + let(:args) { { timestamp: nil, observed_timestamp: nil } } + + it 'is not nil' do + refute_nil(log_record.observed_timestamp) + end + + it 'is equal to the current time' do + # Since I can't get the current time when the test was run + # I'm going to assert it's an Integer, which is the + # Process.clock_gettime return value class when passed the + # :nanosecond option + assert_instance_of(Time, log_record.observed_timestamp) + end + end + end + + describe '#to_log_record_data' do + let(:args) do + span_context = OpenTelemetry::Trace::SpanContext.new + { + timestamp: Time.now, + observed_timestamp: Time.now + 1, + severity_text: 'DEBUG', + severity_number: 0, + body: 'body', + attributes: { 'a' => 'b' }, + trace_id: span_context.trace_id, + span_id: span_context.span_id, + trace_flags: span_context.trace_flags, + resource: logger.instance_variable_get(:@logger_provider).instance_variable_get(:@resource), + instrumentation_scope: logger.instance_variable_get(:@instrumentation_scope) + } + end + + it 'transforms the LogRecord into a LogRecordData' do + log_record_data = log_record.to_log_record_data + + assert_equal(args[:timestamp].strftime("%s%N").to_i, log_record_data.timestamp) + assert_equal(args[:observed_timestamp].strftime("%s%N").to_i, log_record_data.observed_timestamp) + assert_equal(args[:severity_text], log_record_data.severity_text) + assert_equal(args[:severity_number], log_record_data.severity_number) + assert_equal(args[:body], log_record_data.body) + assert_equal(args[:attributes], log_record_data.attributes) + assert_equal(args[:trace_id], log_record_data.trace_id) + assert_equal(args[:span_id], log_record_data.span_id) + assert_equal(args[:trace_flags], log_record_data.trace_flags) + assert_equal(args[:resource], log_record_data.resource) + assert_equal(args[:instrumentation_scope], log_record_data.instrumentation_scope) + end + end + end +end diff --git a/logs_sdk/test/opentelemetry/sdk/logs/logger_provider_test.rb b/logs_sdk/test/opentelemetry/sdk/logs/logger_provider_test.rb index e37cdfac5f..fda91bb9bb 100644 --- a/logs_sdk/test/opentelemetry/sdk/logs/logger_provider_test.rb +++ b/logs_sdk/test/opentelemetry/sdk/logs/logger_provider_test.rb @@ -19,7 +19,7 @@ it 'allows a resource to be associated with the logger provider' do assert_instance_of( - OpenTelemetry::SDK::Resources::Resource, logger_provider.resource + OpenTelemetry::SDK::Resources::Resource, logger_provider.instance_variable_get(:@resource) ) end end @@ -170,4 +170,49 @@ end end end + + describe '#on_emit' do + let(:mock_context) do + mock_context = Minitest::Mock.new + def mock_context.value(key); OpenTelemetry::Trace::Span::INVALID; end # rubocop:disable Style/SingleLineMethods + + mock_context + end + + let(:args) do + span_context = OpenTelemetry::Trace::SpanContext.new + { + timestamp: Time.now, + observed_timestamp: Time.now + 1, + severity_text: 'DEBUG', + severity_number: 0, + body: 'body', + attributes: { 'a' => 'b' }, + trace_id: span_context.trace_id, + span_id: span_context.span_id, + trace_flags: span_context.trace_flags, + instrumentation_scope: OpenTelemetry::SDK::InstrumentationScope, + context: mock_context, + } + end + + it 'creates a new log record' do + output = 'union station' + OpenTelemetry::SDK::Logs::LogRecord.stub(:new, ->(_) { puts output }) do + assert_output(/#{output}/) { logger_provider.on_emit(**args) } + end + end + + it 'sends the log record to the processors' do + mock_log_record = Minitest::Mock.new + + OpenTelemetry::SDK::Logs::LogRecord.stub :new, mock_log_record do + logger_provider.add_log_record_processor(mock_log_record_processor) + mock_log_record_processor.expect(:on_emit, nil, [mock_log_record, mock_context]) + + logger_provider.on_emit(**args) + mock_log_record_processor.verify + end + end + end end diff --git a/logs_sdk/test/opentelemetry/sdk/logs/logger_test.rb b/logs_sdk/test/opentelemetry/sdk/logs/logger_test.rb new file mode 100644 index 0000000000..6fae3a85f8 --- /dev/null +++ b/logs_sdk/test/opentelemetry/sdk/logs/logger_test.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'test_helper' + +describe OpenTelemetry::SDK::Logs::Logger do + let(:logger_provider) { OpenTelemetry::SDK::Logs::LoggerProvider.new } + let(:logger) { logger_provider.logger(name: 'default_logger') } + + describe '#on_emit' do + it 'creates a new LogRecord' do + output = 'chocolate cherry' + OpenTelemetry::SDK::Logs::LogRecord.stub(:new, ->(_) { puts output }) do + assert_output(/#{output}/) { logger.on_emit } + end + end + + it 'sends the newly-created log record to the processors' do + mock_log_record = Minitest::Mock.new + mock_context = Minitest::Mock.new + def mock_context.value(key); OpenTelemetry::Trace::Span::INVALID; end # rubocop:disable Style/SingleLineMethods + + OpenTelemetry::SDK::Logs::LogRecord.stub(:new, ->(_) { mock_log_record }) do + mock_log_record_processor = Minitest::Mock.new + logger_provider.add_log_record_processor(mock_log_record_processor) + mock_log_record_processor.expect(:on_emit, nil, [mock_log_record, mock_context]) + logger.on_emit(context: mock_context) + mock_log_record_processor.verify + end + end + + describe 'when the provider has no processors' do + it 'does not error' do + logger_provider.instance_variable_set(:@log_record_processors, []) + assert(logger.on_emit) + end + end + end +end From 8de0bd785eb15fe46626407b09a1ccb38646f181 Mon Sep 17 00:00:00 2001 From: Xuan <112967240+xuan-cao-swi@users.noreply.github.com> Date: Mon, 29 Jul 2024 22:31:45 -0400 Subject: [PATCH 03/20] release: metrics-sdk metrics-api (#1641) * feat: metrics alpha release * remove release exporter * update one more script to run otlp metrics exporter * docs: Add READMEs for metrics-api and metrics-sdk * chore: Rename variable to match value * chore: Edit text of metrics example README * Update metrics_api/lib/opentelemetry/metrics/version.rb Co-authored-by: Matthew Wear * Update metrics_sdk/lib/opentelemetry/sdk/metrics/version.rb Co-authored-by: Matthew Wear --------- Co-authored-by: Kayla Reopelle Co-authored-by: Matthew Wear --- .toys/.data/releases.yml | 8 ++ examples/metrics_sdk/README.md | 75 +++++++++++++ examples/metrics_sdk/metrics_collect.rb | 31 ++++++ examples/metrics_sdk/metrics_collect_otlp.rb | 34 ++++++ metrics_api/README.md | 69 ++++++++++++ .../lib/opentelemetry/metrics/version.rb | 2 +- metrics_sdk/README.md | 105 ++++++++++++++++++ .../lib/opentelemetry/sdk/metrics/version.rb | 2 +- 8 files changed, 324 insertions(+), 2 deletions(-) create mode 100644 examples/metrics_sdk/README.md create mode 100644 examples/metrics_sdk/metrics_collect.rb create mode 100644 examples/metrics_sdk/metrics_collect_otlp.rb create mode 100644 metrics_api/README.md create mode 100644 metrics_sdk/README.md diff --git a/.toys/.data/releases.yml b/.toys/.data/releases.yml index fbbdb585b3..2b1712a703 100644 --- a/.toys/.data/releases.yml +++ b/.toys/.data/releases.yml @@ -84,3 +84,11 @@ gems: directory: test_helpers version_rb_path: lib/opentelemetry/test_helpers/version.rb version_constant: [OpenTelemetry, TestHelpers, VERSION] + + - name: opentelemetry-metrics-api + directory: metrics_api + version_constant: [OpenTelemetry, Metrics, VERSION] + + - name: opentelemetry-metrics-sdk + directory: metrics_sdk + version_constant: [OpenTelemetry, SDK, Metrics, VERSION] diff --git a/examples/metrics_sdk/README.md b/examples/metrics_sdk/README.md new file mode 100644 index 0000000000..46f7b94efa --- /dev/null +++ b/examples/metrics_sdk/README.md @@ -0,0 +1,75 @@ +# OpenTelemetry Ruby Metrics SDK Example + +### metrics_collect.rb + +Run the script to see the metric data from console + +```sh +ruby metrics_collect.rb +``` + +### metrics_collect_otlp.rb + +**WARN: this example doesn't work on alpine aarch64 container due to grpc installation issues.** + +This example tests both the metrics sdk and the metrics otlp http exporter. + +You can view the metrics in your favored backend (e.g. jaeger). + +#### 1. Set up the local opentelemetry-collector. + +Given you have a `config.yml` file in your current directory and Docker is installed on your machine, run the following commands to pull the collector image and run the collector. + +```sh +docker pull otel/opentelemetry-collector +docker run --rm -v $(pwd)/config.yaml:/etc/otel/config.yaml -p 4317:4317 -p 4318:4318 otel/opentelemetry-collector --config /etc/otel/config.yaml +``` + +Sample config.yaml + +```yaml +receivers: + otlp: + protocols: + grpc: + http: + # Default endpoints: 0.0.0.0:4317 for gRPC and 0.0.0.0:4318 for HTTP + +exporters: + logging: + loglevel: debug + +processors: + batch: + +service: + pipelines: + traces: + receivers: [otlp] + processors: [batch] + exporters: [logging] + metrics: + receivers: [otlp] + processors: [batch] + exporters: [logging] +``` + +More information on how to setup the OTel collector can be found in the in [quick start docs](https://opentelemetry.io/docs/collector/quick-start/). + +#### 2. Assign the endpoint value to your destination address + +``` +# Using environment variable +ENV['OTEL_EXPORTER_OTLP_METRICS_ENDPOINT'] = 'http://host.docker.internal:4318/v1/metrics' + +# Or using export command +export OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=http://host.docker.internal:4318/v1/metrics +``` + +#### 3. Run the script to send metric data to OTLP collector + +```sh +ruby metrics_collect_otlp.rb +``` + +You should see metric data appear in the collector. diff --git a/examples/metrics_sdk/metrics_collect.rb b/examples/metrics_sdk/metrics_collect.rb new file mode 100644 index 0000000000..407932ca11 --- /dev/null +++ b/examples/metrics_sdk/metrics_collect.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require 'bundler/inline' + +gemfile(true) do + source 'https://rubygems.org' + gem "opentelemetry-api" + gem "opentelemetry-common" + gem "opentelemetry-sdk" + + gem 'opentelemetry-metrics-api', path: '../../metrics_api' + gem 'opentelemetry-metrics-sdk', path: '../../metrics_sdk' +end + +require 'opentelemetry/sdk' +require 'opentelemetry-metrics-sdk' + +OpenTelemetry::SDK.configure + +console_metric_exporter = OpenTelemetry::SDK::Metrics::Export::ConsoleMetricPullExporter.new + +OpenTelemetry.meter_provider.add_metric_reader(console_metric_exporter) + +meter = OpenTelemetry.meter_provider.meter("SAMPLE_METER_NAME") + +histogram = meter.create_histogram('histogram', unit: 'smidgen', description: 'desscription') + +histogram.record(123, attributes: {'foo' => 'bar'}) + +OpenTelemetry.meter_provider.metric_readers.each(&:pull) +OpenTelemetry.meter_provider.shutdown diff --git a/examples/metrics_sdk/metrics_collect_otlp.rb b/examples/metrics_sdk/metrics_collect_otlp.rb new file mode 100644 index 0000000000..d8b727a863 --- /dev/null +++ b/examples/metrics_sdk/metrics_collect_otlp.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require 'bundler/inline' + +gemfile(true) do + source 'https://rubygems.org' + gem "opentelemetry-api" + gem "opentelemetry-common" + gem "opentelemetry-sdk" + + gem 'opentelemetry-metrics-api', path: '../../metrics_api' + gem 'opentelemetry-metrics-sdk', path: '../../metrics_sdk' + gem 'opentelemetry-exporter-otlp-metrics', path: '../../exporter/otlp-metrics' + +end + +require 'opentelemetry/sdk' +require 'opentelemetry-metrics-sdk' +require 'opentelemetry-exporter-otlp-metrics' + +OpenTelemetry::SDK.configure + +otlp_metric_exporter = OpenTelemetry::Exporter::OTLP::MetricsExporter.new + +OpenTelemetry.meter_provider.add_metric_reader(otlp_metric_exporter) + +meter = OpenTelemetry.meter_provider.meter("SAMPLE_METER_NAME") + +histogram = meter.create_histogram('histogram', unit: 'smidgen', description: 'desscription') + +histogram.record(123, attributes: {'foo' => 'bar'}) + +OpenTelemetry.meter_provider.metric_readers.each(&:pull) +OpenTelemetry.meter_provider.shutdown diff --git a/metrics_api/README.md b/metrics_api/README.md new file mode 100644 index 0000000000..dcefba929d --- /dev/null +++ b/metrics_api/README.md @@ -0,0 +1,69 @@ +# opentelemetry-metrics-api + +The `opentelemetry-metrics-api` gem is an alpha implementation of the [OpenTelemetry Metrics API][metrics-api] for Ruby applications. Using `opentelemetry-metrics-api`, a library or application can code against the OpenTelemetry interfaces to produce metric data. + +## What is OpenTelemetry? + +[OpenTelemetry][opentelemetry-home] is an open source observability framework, providing a general-purpose API, SDK, and related tools required for the instrumentation of cloud-native software, frameworks, and libraries. + +OpenTelemetry provides a single set of APIs, libraries, agents, and collector services to capture distributed traces and metrics from your application. You can analyze them using Prometheus, Jaeger, and other observability tools. + +## How does this gem fit in? + +The `opentelemetry-metrics-api` gem defines the core OpenTelemetry interfaces in the form of abstract classes and no-op implementations. That is, it defines interfaces and data types sufficient for a library or application to code against to produce telemetry data, but does not actually collect, analyze, or export the data. + +To collect and analyze telemetry data, _applications_ should also install a concrete implementation of the API, such as the `opentelemetry-metrics-sdk` gem. However, _libraries_ that produce telemetry data should depend only on `opentelemetry-metrics-api`, deferring the choise of concrete implementation to the application developer. + +This code is still under development and is not a complete implementation of the Metrics API. Until the code becomes stable, Metrics API functionality will live outside the `opentelemetry-api` library. + +## How do I get started? + +Install the gem using: + +``` +gem install opentelemetry-metrics-api +``` + +Or, if you use [bundler][bundler-home], include `opentelemetry-metrics-api` in your `Gemfile`. + +Then, use the OpenTelemetry interfaces to produces traces and other telemetry data. Following is a basic example. + +```ruby +require 'opentelemetry-metrics-api' + +# Obtain the current default meter provider +provider = OpenTelemetry.meter_provider + +# Create a meter +meter = provider.meter('my_app', '1.0') + +# Record a metric +histogram = meter.create_histogram('histogram', unit: 's', description: 'duration in seconds') + +# Record a metric. +histogram.record(123, attributes: {'foo' => 'bar'}) +``` + +For additional examples, see the [examples on github][examples-github]. + +## How can I get involved? + +The `opentelemetry-metrics-api` gem source is [on github][repo-github], along with related gems including `opentelemetry-metrics-sdk`. + +The OpenTelemetry Ruby gems are maintained by the OpenTelemetry-Ruby special interest group (SIG). You can get involved by joining us in [GitHub Discussions][discussions-url] or attending our weekly meeting. See the [meeting calendar][community-meetings] for dates and times. For more information on this and other language SIGs, see the OpenTelemetry [community page][ruby-sig]. + +There's still work to be done, to get to a spec-compliant metrics implementation and we'd love to have more folks contributing to the project. Check the [repo][repo-github] for issues and PRs labeled with `metrics` to see what's available. + +## License + +The `opentelemetry-api` gem is distributed under the Apache 2.0 license. See [LICENSE][license-github] for more information. + +[metrics-api]: https://opentelemetry.io/docs/specs/otel/metrics/api/ +[opentelemetry-home]: https://opentelemetry.io +[bundler-home]: https://bundler.io +[repo-github]: https://github.com/open-telemetry/opentelemetry-ruby +[license-github]: https://github.com/open-telemetry/opentelemetry-ruby/blob/main/LICENSE +[examples-github]: https://github.com/open-telemetry/opentelemetry-ruby/tree/main/examples +[ruby-sig]: https://github.com/open-telemetry/community#ruby-sig +[community-meetings]: https://github.com/open-telemetry/community#community-meetings +[discussions-url]: https://github.com/open-telemetry/opentelemetry-ruby/discussions diff --git a/metrics_api/lib/opentelemetry/metrics/version.rb b/metrics_api/lib/opentelemetry/metrics/version.rb index 1698cc40c0..a1238c7212 100644 --- a/metrics_api/lib/opentelemetry/metrics/version.rb +++ b/metrics_api/lib/opentelemetry/metrics/version.rb @@ -7,6 +7,6 @@ module OpenTelemetry module Metrics ## Current OpenTelemetry metrics version - VERSION = '0.0.1' + VERSION = '0.1.0' end end diff --git a/metrics_sdk/README.md b/metrics_sdk/README.md new file mode 100644 index 0000000000..e67c9685fb --- /dev/null +++ b/metrics_sdk/README.md @@ -0,0 +1,105 @@ +# opentelemetry-metrics-sdk + +The `opentelemetry-metrics-sdk` is an alpha implementation of the [OpenTelemetry Metrics SDK][metrics-sdk] for Ruby. It should be used in conjunction with the `opentelemetry-sdk` to collect, analyze and export metrics. + +## What is OpenTelemetry? + +[OpenTelemetry][opentelemetry-home] is an open source observability framework, providing a general-purpose API, SDK, and related tools required for the instrumentation of cloud-native software, frameworks, and libraries. + +OpenTelemetry provides a single set of APIs, libraries, agents, and collector services to capture distributed traces, metrics, and logs from your application. You can analyze them using Prometheus, Jaeger, and other observability tools. + +## How does this gem fit in? + +Metrics is one of the core signals in OpenTelemetry. This package allows you to emit OpenTelemetry metrics using Ruby. It leverages an alpha implementation of the OpenTelemetry Metrics API. At the current stage, things may break and APIs may change. Use this tool with caution. + +This gem does not have a full implementation of the Metrics SDK specification. The work is in progress. + +At this time, you should be able to: +* Create synchronous: + * counters + * up down counters + * histograms + * observable counters + * observable gauges + * observable up down counters +* Export using a pull exporter +* Use delta aggregation temporality + +We do not yet have support for: +* Asynchronous instruments +* Cumulative aggregation temporality +* Metrics Views +* Metrics Exemplars +* Periodic Exporting Metric Reader +* Push metric exporting + +These lists are incomplete and are intended to give a broad description of what's available. + +Until the Ruby implementation of OpenTelemetry Metrics becomes stable, the functionality to create and export metrics will remain in a gem separate from the stable features available from the `opentelemetry-sdk`. + +## How do I get started? + +Install the gems using: + +``` +gem install opentelemetry-metrics-sdk +gem install opentelemetry-sdk +``` + +Or, if you use [bundler][bundler-home], include `opentelemetry-metrics-sdk` and `opentelemetry-sdk` in your `Gemfile`. + +Then, configure the SDK according to your desired handling of telemetry data, and use the OpenTelemetry interfaces to produces traces and other information. Following is a basic example. + +```ruby +require 'opentelemetry/sdk' +require 'opentelemetry-metrics-sdk' + +# Configure the sdk with default export and context propagation formats. +OpenTelemetry::SDK.configure + +# Create an exporter. This example exports metrics to the console. +console_metric_exporter = OpenTelemetry::SDK::Metrics::Export::ConsoleMetricPullExporter.new + +# Add the exporter to the meter provider as a new metric reader. +OpenTelemetry.meter_provider.add_metric_reader(console_metric_exporter) + +# Create a meter to generate instruments. +meter = OpenTelemetry.meter_provider.meter("SAMPLE_METER_NAME") + +# Create an instrument. +histogram = meter.create_histogram('histogram', unit: 'smidgen', description: 'desscription') + +# Record a metric. +histogram.record(123, attributes: {'foo' => 'bar'}) + +# Send the recorded metrics to the metric readers. +OpenTelemetry.meter_provider.metric_readers.each(&:pull) + +# Shut down the meter provider. +OpenTelemetry.meter_provider.shutdown +``` + +For additional examples, see the [examples on github][examples-github]. + +## How can I get involved? + +The `opentelemetry-metrics-sdk` gem source is [on github][repo-github], along with related gems including `opentelemetry-sdk`. + +The OpenTelemetry Ruby gems are maintained by the OpenTelemetry Ruby special interest group (SIG). You can get involved by joining us in [GitHub Discussions][discussions-url] or attending our weekly meeting. See the [meeting calendar][community-meetings] for dates and times. For more information on this and other language SIGs, see the OpenTelemetry [community page][ruby-sig]. + +There's still work to be done, to get to a spec-compliant metrics implementation and we'd love to have more folks contributing to the project. Check the [repo][repo-github] for issues and PRs labeled with `metrics` to see what's available. + +## License + +The `opentelemetry-metrics-sdk` gem is distributed under the Apache 2.0 license. See [LICENSE][license-github] for more information. + + +[metrics-sdk]: https://opentelemetry.io/docs/specs/otel/metrics/sdk/ +[opentelemetry-home]: https://opentelemetry.io +[bundler-home]: https://bundler.io +[repo-github]: https://github.com/open-telemetry/opentelemetry-ruby +[license-github]: https://github.com/open-telemetry/opentelemetry-ruby/blob/main/LICENSE +[examples-github]: https://github.com/open-telemetry/opentelemetry-ruby/tree/main/examples/ +[ruby-sig]: https://github.com/open-telemetry/community#ruby-sig +[community-meetings]: https://github.com/open-telemetry/community#community-meetings +[discussions-url]: https://github.com/open-telemetry/opentelemetry-ruby/discussions diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/version.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/version.rb index a26a06fecc..41bd724c7a 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/version.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/version.rb @@ -8,7 +8,7 @@ module OpenTelemetry module SDK module Metrics # Current OpenTelemetry metrics sdk version - VERSION = '0.0.1' + VERSION = '0.1.0' end end end From cc4d87c945ded9780b3dca06f04717f2c9227d21 Mon Sep 17 00:00:00 2001 From: Kayla Reopelle <87386821+kaylareopelle@users.noreply.github.com> Date: Mon, 29 Jul 2024 19:42:23 -0700 Subject: [PATCH 04/20] ci: Prevent stale action runs on forks (#1650) Co-authored-by: Matthew Wear --- .github/workflows/stale.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stale.yaml b/.github/workflows/stale.yaml index c85932c9ca..ab487f9c6d 100644 --- a/.github/workflows/stale.yaml +++ b/.github/workflows/stale.yaml @@ -6,7 +6,7 @@ on: jobs: stale: - + if: ${{ github.repository == 'open-telemetry/opentelemetry-ruby' }} runs-on: ubuntu-latest steps: From b8eb582ca9eeb02c9c27c1d5c081145fd7bc8c58 Mon Sep 17 00:00:00 2001 From: Xuan <112967240+xuan-cao-swi@users.noreply.github.com> Date: Tue, 30 Jul 2024 13:12:15 -0400 Subject: [PATCH 05/20] fix!: update the version_rb_path for metrics gem (#1659) --- .toys/.data/releases.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.toys/.data/releases.yml b/.toys/.data/releases.yml index 2b1712a703..95c53559b2 100644 --- a/.toys/.data/releases.yml +++ b/.toys/.data/releases.yml @@ -87,8 +87,10 @@ gems: - name: opentelemetry-metrics-api directory: metrics_api + version_rb_path: lib/opentelemetry/metrics/version.rb version_constant: [OpenTelemetry, Metrics, VERSION] - name: opentelemetry-metrics-sdk directory: metrics_sdk + version_rb_path: lib/opentelemetry/sdk/metrics/version.rb version_constant: [OpenTelemetry, SDK, Metrics, VERSION] From fb2527ccc83012142f7ffd68e22425bc58d582aa Mon Sep 17 00:00:00 2001 From: Kayla Reopelle <87386821+kaylareopelle@users.noreply.github.com> Date: Wed, 31 Jul 2024 11:23:53 -0700 Subject: [PATCH 06/20] chore: Add link to issue for feedback (#1663) During the SIG, we talked about including an issue to hold feedback related to the Metrics gems to keep the conversations in a centralized location. --- metrics_api/README.md | 5 +++++ metrics_sdk/README.md | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/metrics_api/README.md b/metrics_api/README.md index dcefba929d..48721a5358 100644 --- a/metrics_api/README.md +++ b/metrics_api/README.md @@ -54,6 +54,10 @@ The OpenTelemetry Ruby gems are maintained by the OpenTelemetry-Ruby special int There's still work to be done, to get to a spec-compliant metrics implementation and we'd love to have more folks contributing to the project. Check the [repo][repo-github] for issues and PRs labeled with `metrics` to see what's available. +## Feedback + +During this experimental stage, we're looking for lots of community feedback about this gem. Please add your comments to Issue [#1662][1662]. + ## License The `opentelemetry-api` gem is distributed under the Apache 2.0 license. See [LICENSE][license-github] for more information. @@ -67,3 +71,4 @@ The `opentelemetry-api` gem is distributed under the Apache 2.0 license. See [LI [ruby-sig]: https://github.com/open-telemetry/community#ruby-sig [community-meetings]: https://github.com/open-telemetry/community#community-meetings [discussions-url]: https://github.com/open-telemetry/opentelemetry-ruby/discussions +[1662]: https://github.com/open-telemetry/opentelemetry-ruby/issues/1662 diff --git a/metrics_sdk/README.md b/metrics_sdk/README.md index e67c9685fb..feb8e89aa7 100644 --- a/metrics_sdk/README.md +++ b/metrics_sdk/README.md @@ -89,6 +89,10 @@ The OpenTelemetry Ruby gems are maintained by the OpenTelemetry Ruby special int There's still work to be done, to get to a spec-compliant metrics implementation and we'd love to have more folks contributing to the project. Check the [repo][repo-github] for issues and PRs labeled with `metrics` to see what's available. +## Feedback + +During this experimental stage, we're looking for lots of community feedback about this gem. Please add your comments to Issue [#1662][1662]. + ## License The `opentelemetry-metrics-sdk` gem is distributed under the Apache 2.0 license. See [LICENSE][license-github] for more information. @@ -103,3 +107,4 @@ The `opentelemetry-metrics-sdk` gem is distributed under the Apache 2.0 license. [ruby-sig]: https://github.com/open-telemetry/community#ruby-sig [community-meetings]: https://github.com/open-telemetry/community#community-meetings [discussions-url]: https://github.com/open-telemetry/opentelemetry-ruby/discussions +[1662]: https://github.com/open-telemetry/opentelemetry-ruby/issues/1662 From 92748f2b74c510d55c50b226380f9f1c51db4a4a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 31 Jul 2024 15:22:07 -0700 Subject: [PATCH 07/20] release: Release 2 gems (#1664) * opentelemetry-metrics-api 0.1.0 (initial release) * opentelemetry-metrics-sdk 0.1.0 (initial release) Co-authored-by: Daniel Azuma --- metrics_api/CHANGELOG.md | 4 ++++ metrics_sdk/CHANGELOG.md | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/metrics_api/CHANGELOG.md b/metrics_api/CHANGELOG.md index 4e6dba42c5..bdadf3885e 100644 --- a/metrics_api/CHANGELOG.md +++ b/metrics_api/CHANGELOG.md @@ -1 +1,5 @@ # Release History: opentelemetry-metrics-api + +### v0.1.0 / 2024-07-31 + +Initial release. diff --git a/metrics_sdk/CHANGELOG.md b/metrics_sdk/CHANGELOG.md index 789f15a238..a1385c5c6b 100644 --- a/metrics_sdk/CHANGELOG.md +++ b/metrics_sdk/CHANGELOG.md @@ -1 +1,5 @@ # Release History: opentelemetry-metrics-sdk + +### v0.1.0 / 2024-07-31 + +Initial release. From 9415f740dd9dfce31b5585f1ed3dde8acd80be1b Mon Sep 17 00:00:00 2001 From: Kayla Reopelle <87386821+kaylareopelle@users.noreply.github.com> Date: Wed, 7 Aug 2024 09:10:06 -0700 Subject: [PATCH 08/20] chore: Add CI for logs sdk, Rubocop (#1665) Co-authored-by: Matthew Wear --- .github/workflows/ci.yml | 1 + .../lib/opentelemetry/sdk/logs/log_record.rb | 2 +- .../opentelemetry/sdk/logs/log_record_data.rb | 7 ++----- logs_sdk/lib/opentelemetry/sdk/logs/logger.rb | 20 +++++++++---------- .../opentelemetry/sdk/logs/log_record_test.rb | 4 ++-- .../sdk/logs/logger_provider_test.rb | 2 +- 6 files changed, 17 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6e89f44a98..cde048d562 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,6 +18,7 @@ jobs: - opentelemetry-api - opentelemetry-common - opentelemetry-logs-api + - opentelemetry-logs-sdk - opentelemetry-metrics-api - opentelemetry-metrics-sdk - opentelemetry-registry diff --git a/logs_sdk/lib/opentelemetry/sdk/logs/log_record.rb b/logs_sdk/lib/opentelemetry/sdk/logs/log_record.rb index 255158df8d..8ee21b54b6 100644 --- a/logs_sdk/lib/opentelemetry/sdk/logs/log_record.rb +++ b/logs_sdk/lib/opentelemetry/sdk/logs/log_record.rb @@ -101,7 +101,7 @@ def to_log_record_data def to_integer_nanoseconds(timestamp) return unless timestamp.is_a?(Time) - t = (timestamp.to_r * 10**9).to_i + (timestamp.to_r * 10**9).to_i end end end diff --git a/logs_sdk/lib/opentelemetry/sdk/logs/log_record_data.rb b/logs_sdk/lib/opentelemetry/sdk/logs/log_record_data.rb index 5bf304f85c..17d8b263fc 100644 --- a/logs_sdk/lib/opentelemetry/sdk/logs/log_record_data.rb +++ b/logs_sdk/lib/opentelemetry/sdk/logs/log_record_data.rb @@ -12,11 +12,8 @@ module Logs :observed_timestamp, # Integer nanoseconds since Epoch :severity_text, # optional String :severity_number, # optional Integer - :body, # optional String, Numeric, Boolean, Array, Hash{String => String, Numeric, Boolean, - # Array} - :attributes, # optional Hash{String => String, Numeric, Boolean, - # Array} + :body, # optional String, Numeric, Boolean, Array, Hash{String => String, Numeric, Boolean, Array} + :attributes, # optional Hash{String => String, Numeric, Boolean, Array} :trace_id, # optional String (16-byte binary) :span_id, # optional String (8-byte binary) :trace_flags, # optional Integer (8-bit byte of bit flags) diff --git a/logs_sdk/lib/opentelemetry/sdk/logs/logger.rb b/logs_sdk/lib/opentelemetry/sdk/logs/logger.rb index 15fa3bcaf3..51f096c143 100644 --- a/logs_sdk/lib/opentelemetry/sdk/logs/logger.rb +++ b/logs_sdk/lib/opentelemetry/sdk/logs/logger.rb @@ -74,16 +74,16 @@ def on_emit(timestamp: nil, span_context = current_span.context unless current_span == OpenTelemetry::Trace::Span::INVALID @logger_provider.on_emit(timestamp: timestamp, - observed_timestamp: observed_timestamp, - severity_text: severity_text, - severity_number: severity_number, - body: body, - attributes: attributes, - trace_id: trace_id || span_context&.trace_id, - span_id: span_id || span_context&.span_id, - trace_flags: trace_flags || span_context&.trace_flags, - instrumentation_scope: @instrumentation_scope, - context: context) + observed_timestamp: observed_timestamp, + severity_text: severity_text, + severity_number: severity_number, + body: body, + attributes: attributes, + trace_id: trace_id || span_context&.trace_id, + span_id: span_id || span_context&.span_id, + trace_flags: trace_flags || span_context&.trace_flags, + instrumentation_scope: @instrumentation_scope, + context: context) end end end diff --git a/logs_sdk/test/opentelemetry/sdk/logs/log_record_test.rb b/logs_sdk/test/opentelemetry/sdk/logs/log_record_test.rb index 1d9711a946..7ba19048fe 100644 --- a/logs_sdk/test/opentelemetry/sdk/logs/log_record_test.rb +++ b/logs_sdk/test/opentelemetry/sdk/logs/log_record_test.rb @@ -83,8 +83,8 @@ it 'transforms the LogRecord into a LogRecordData' do log_record_data = log_record.to_log_record_data - assert_equal(args[:timestamp].strftime("%s%N").to_i, log_record_data.timestamp) - assert_equal(args[:observed_timestamp].strftime("%s%N").to_i, log_record_data.observed_timestamp) + assert_equal(args[:timestamp].strftime('%s%N').to_i, log_record_data.timestamp) + assert_equal(args[:observed_timestamp].strftime('%s%N').to_i, log_record_data.observed_timestamp) assert_equal(args[:severity_text], log_record_data.severity_text) assert_equal(args[:severity_number], log_record_data.severity_number) assert_equal(args[:body], log_record_data.body) diff --git a/logs_sdk/test/opentelemetry/sdk/logs/logger_provider_test.rb b/logs_sdk/test/opentelemetry/sdk/logs/logger_provider_test.rb index fda91bb9bb..023a9a67c9 100644 --- a/logs_sdk/test/opentelemetry/sdk/logs/logger_provider_test.rb +++ b/logs_sdk/test/opentelemetry/sdk/logs/logger_provider_test.rb @@ -192,7 +192,7 @@ def mock_context.value(key); OpenTelemetry::Trace::Span::INVALID; end # rubocop: span_id: span_context.span_id, trace_flags: span_context.trace_flags, instrumentation_scope: OpenTelemetry::SDK::InstrumentationScope, - context: mock_context, + context: mock_context } end From d9324e03f8520c35baf1fcaeb0b96d50ba4e923c Mon Sep 17 00:00:00 2001 From: Tobin Richard <154486084+megabus-tobin@users.noreply.github.com> Date: Tue, 13 Aug 2024 08:01:08 +1000 Subject: [PATCH 09/20] feat: Add support for mutual TLS. (#1556) Add support for specifying client certificate and key for mutual TLS when exporting. Co-authored-by: Ariel Valentin Co-authored-by: Matthew Wear --- exporter/otlp-grpc/README.md | 9 ++-- .../exporter/otlp/grpc/trace_exporter.rb | 14 +++++- exporter/otlp-http/README.md | 20 ++++---- .../exporter/otlp/http/trace_exporter.rb | 8 ++- exporter/otlp/README.md | 20 ++++---- .../opentelemetry/exporter/otlp/exporter.rb | 8 ++- .../exporter/otlp/exporter_test.rb | 22 +++++++- .../exporter/otlp/mtls-client-a.pem | 50 +++++++++++++++++++ .../exporter/otlp/mtls-client-b.pem | 50 +++++++++++++++++++ 9 files changed, 173 insertions(+), 28 deletions(-) create mode 100644 exporter/otlp/test/opentelemetry/exporter/otlp/mtls-client-a.pem create mode 100644 exporter/otlp/test/opentelemetry/exporter/otlp/mtls-client-b.pem diff --git a/exporter/otlp-grpc/README.md b/exporter/otlp-grpc/README.md index 5b812ebdc6..ddb20bd8dd 100644 --- a/exporter/otlp-grpc/README.md +++ b/exporter/otlp-grpc/README.md @@ -65,9 +65,12 @@ For additional examples, see the [examples on github][examples-github]. The collector exporter can be configured explicitly in code, or via environment variables as shown above. The configuration parameters, environment variables, and defaults are shown below. -| Parameter | Environment variable | Default | -| ------------------- | -------------------------------------------- | ----------------------------------- | -| `endpoint:` | `OTEL_EXPORTER_OTLP_ENDPOINT` | `"http://localhost:4318/v1/traces"` | +| Parameter | Environment variable | Default | +| ------------------------- | -------------------------------------------- | ----------------------------------- | +| `endpoint:` | `OTEL_EXPORTER_OTLP_ENDPOINT` | `"http://localhost:4318/v1/traces"` | +| `certificate_file:` | `OTEL_EXPORTER_OTLP_CERTIFICATE` | | +| `client_certificate_file` | `OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE` | | +| `client_key_file` | `OTEL_EXPORTER_OTLP_CLIENT_KEY` | | ## How can I get involved? diff --git a/exporter/otlp-grpc/lib/opentelemetry/exporter/otlp/grpc/trace_exporter.rb b/exporter/otlp-grpc/lib/opentelemetry/exporter/otlp/grpc/trace_exporter.rb index 4236842358..2772873e52 100644 --- a/exporter/otlp-grpc/lib/opentelemetry/exporter/otlp/grpc/trace_exporter.rb +++ b/exporter/otlp-grpc/lib/opentelemetry/exporter/otlp/grpc/trace_exporter.rb @@ -24,14 +24,26 @@ class TraceExporter def initialize(endpoint: OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_TRACES_ENDPOINT', 'OTEL_EXPORTER_OTLP_ENDPOINT', default: 'http://localhost:4317/v1/traces'), timeout: OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_TRACES_TIMEOUT', 'OTEL_EXPORTER_OTLP_TIMEOUT', default: 10), + certificate_file: OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE', 'OTEL_EXPORTER_OTLP_CERTIFICATE'), + client_certificate_file: OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_TRACES_CLIENT_CERTIFICATE', 'OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE'), + client_key_file: OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_TRACES_CLIENT_KEY', 'OTEL_EXPORTER_OTLP_CLIENT_KEY'), metrics_reporter: nil) raise ArgumentError, "invalid url for OTLP::Exporter #{endpoint}" unless OpenTelemetry::Common::Utilities.valid_url?(endpoint) uri = URI(endpoint) + creds = if !client_key_file.nil? && !client_certificate_file.nil? + # Permits constructing with nil root cert. + ::GRPC::Core::ChannelCredentials.new(certificate_file, client_key_file, client_certificate_file) + elsif !certificate_file.nil? + ::GRPC::Core::ChannelCredentials.new(certificate_file) + else + :this_channel_is_insecure + end + @client = Opentelemetry::Proto::Collector::Trace::V1::TraceService::Stub.new( "#{uri.host}:#{uri.port}", - :this_channel_is_insecure + creds ) @timeout = timeout.to_f diff --git a/exporter/otlp-http/README.md b/exporter/otlp-http/README.md index 38286a248e..79514b946a 100644 --- a/exporter/otlp-http/README.md +++ b/exporter/otlp-http/README.md @@ -68,15 +68,17 @@ For additional examples, see the [examples on github][examples-github]. The collector exporter can be configured explicitly in code, or via environment variables as shown above. The configuration parameters, environment variables, and defaults are shown below. -| Parameter | Environment variable | Default | -| ------------------- | -------------------------------------------- | ----------------------------------- | -| `endpoint:` | `OTEL_EXPORTER_OTLP_ENDPOINT` | `"http://localhost:4318/v1/traces"` | -| `certificate_file: `| `OTEL_EXPORTER_OTLP_CERTIFICATE` | | -| `headers:` | `OTEL_EXPORTER_OTLP_HEADERS` | | -| `compression:` | `OTEL_EXPORTER_OTLP_COMPRESSION` | `"gzip"` | -| `timeout:` | `OTEL_EXPORTER_OTLP_TIMEOUT` | `10` | -| `ssl_verify_mode:` | `OTEL_RUBY_EXPORTER_OTLP_SSL_VERIFY_PEER` or | `OpenSSL::SSL:VERIFY_PEER` | -| | `OTEL_RUBY_EXPORTER_OTLP_SSL_VERIFY_NONE` | | +| Parameter | Environment variable | Default | +|---------------------------|----------------------------------------------|-------------------------------------| +| `endpoint:` | `OTEL_EXPORTER_OTLP_ENDPOINT` | `"http://localhost:4318/v1/traces"` | +| `certificate_file:` | `OTEL_EXPORTER_OTLP_CERTIFICATE` | | +| `client_certificate_file` | `OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE` | | +| `client_key_file` | `OTEL_EXPORTER_OTLP_CLIENT_KEY` | | +| `headers:` | `OTEL_EXPORTER_OTLP_HEADERS` | | +| `compression:` | `OTEL_EXPORTER_OTLP_COMPRESSION` | `"gzip"` | +| `timeout:` | `OTEL_EXPORTER_OTLP_TIMEOUT` | `10` | +| `ssl_verify_mode:` | `OTEL_RUBY_EXPORTER_OTLP_SSL_VERIFY_PEER` or | `OpenSSL::SSL:VERIFY_PEER` | +| | `OTEL_RUBY_EXPORTER_OTLP_SSL_VERIFY_NONE` | | `ssl_verify_mode:` parameter values should be flags for server certificate verification: `OpenSSL::SSL:VERIFY_PEER` and `OpenSSL::SSL:VERIFY_NONE` are acceptable. These values can also be set using the appropriately named environment variables as shown where `VERIFY_PEER` will take precedence over `VERIFY_NONE`. Please see [the Net::HTTP docs](https://ruby-doc.org/stdlib-2.5.1/libdoc/net/http/rdoc/Net/HTTP.html#verify_mode) for more information about these flags. diff --git a/exporter/otlp-http/lib/opentelemetry/exporter/otlp/http/trace_exporter.rb b/exporter/otlp-http/lib/opentelemetry/exporter/otlp/http/trace_exporter.rb index de2b857788..e71c1a9457 100644 --- a/exporter/otlp-http/lib/opentelemetry/exporter/otlp/http/trace_exporter.rb +++ b/exporter/otlp-http/lib/opentelemetry/exporter/otlp/http/trace_exporter.rb @@ -33,6 +33,8 @@ class TraceExporter # rubocop:disable Metrics/ClassLength def initialize(endpoint: OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_TRACES_ENDPOINT', 'OTEL_EXPORTER_OTLP_ENDPOINT', default: 'http://localhost:4318/v1/traces'), certificate_file: OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE', 'OTEL_EXPORTER_OTLP_CERTIFICATE'), + client_certificate_file: OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_TRACES_CLIENT_CERTIFICATE', 'OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE'), + client_key_file: OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_TRACES_CLIENT_KEY', 'OTEL_EXPORTER_OTLP_CLIENT_KEY'), ssl_verify_mode: fetch_ssl_verify_mode, headers: OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_TRACES_HEADERS', 'OTEL_EXPORTER_OTLP_HEADERS', default: {}), compression: OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_TRACES_COMPRESSION', 'OTEL_EXPORTER_OTLP_COMPRESSION', default: 'gzip'), @@ -47,7 +49,7 @@ def initialize(endpoint: OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPOR URI(endpoint) end - @http = http_connection(@uri, ssl_verify_mode, certificate_file) + @http = http_connection(@uri, ssl_verify_mode, certificate_file, client_certificate_file, client_key_file) @path = @uri.path @headers = case headers @@ -107,11 +109,13 @@ def fetch_ssl_verify_mode end end - def http_connection(uri, ssl_verify_mode, certificate_file) + def http_connection(uri, ssl_verify_mode, certificate_file, client_certificate_file, client_key_file) http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = uri.scheme == 'https' http.verify_mode = ssl_verify_mode http.ca_file = certificate_file unless certificate_file.nil? + http.cert = OpenSSL::X509::Certificate.new(File.read(client_certificate_file)) unless client_certificate_file.nil? + http.key = OpenSSL::PKey::RSA.new(File.read(client_key_file)) unless client_key_file.nil? http.keep_alive_timeout = KEEP_ALIVE_TIMEOUT http end diff --git a/exporter/otlp/README.md b/exporter/otlp/README.md index 55d09b8470..1161313270 100644 --- a/exporter/otlp/README.md +++ b/exporter/otlp/README.md @@ -73,15 +73,17 @@ For additional examples, see the [examples on github][examples-github]. The collector exporter can be configured explicitly in code, or via environment variables as shown above. The configuration parameters, environment variables, and defaults are shown below. -| Parameter | Environment variable | Default | -| ------------------- | -------------------------------------------- | ----------------------------------- | -| `endpoint:` | `OTEL_EXPORTER_OTLP_ENDPOINT` | `"http://localhost:4318/v1/traces"` | -| `certificate_file: `| `OTEL_EXPORTER_OTLP_CERTIFICATE` | | -| `headers:` | `OTEL_EXPORTER_OTLP_HEADERS` | | -| `compression:` | `OTEL_EXPORTER_OTLP_COMPRESSION` | `"gzip"` | -| `timeout:` | `OTEL_EXPORTER_OTLP_TIMEOUT` | `10` | -| `ssl_verify_mode:` | `OTEL_RUBY_EXPORTER_OTLP_SSL_VERIFY_PEER` or | `OpenSSL::SSL:VERIFY_PEER` | -| | `OTEL_RUBY_EXPORTER_OTLP_SSL_VERIFY_NONE` | | +| Parameter | Environment variable | Default | +|---------------------------| -------------------------------------------- | ----------------------------------- | +| `endpoint:` | `OTEL_EXPORTER_OTLP_ENDPOINT` | `"http://localhost:4318/v1/traces"` | +| `certificate_file:` | `OTEL_EXPORTER_OTLP_CERTIFICATE` | | +| `client_certificate_file` | `OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE` | | +| `client_key_file` | `OTEL_EXPORTER_OTLP_CLIENT_KEY` | | +| `headers:` | `OTEL_EXPORTER_OTLP_HEADERS` | | +| `compression:` | `OTEL_EXPORTER_OTLP_COMPRESSION` | `"gzip"` | +| `timeout:` | `OTEL_EXPORTER_OTLP_TIMEOUT` | `10` | +| `ssl_verify_mode:` | `OTEL_RUBY_EXPORTER_OTLP_SSL_VERIFY_PEER` or | `OpenSSL::SSL:VERIFY_PEER` | +| | `OTEL_RUBY_EXPORTER_OTLP_SSL_VERIFY_NONE` | | `ssl_verify_mode:` parameter values should be flags for server certificate verification: `OpenSSL::SSL:VERIFY_PEER` and `OpenSSL::SSL:VERIFY_NONE` are acceptable. These values can also be set using the appropriately named environment variables as shown where `VERIFY_PEER` will take precedence over `VERIFY_NONE`. Please see [the Net::HTTP docs](https://ruby-doc.org/stdlib-2.7.6/libdoc/net/http/rdoc/Net/HTTP.html#verify_mode) for more information about these flags. diff --git a/exporter/otlp/lib/opentelemetry/exporter/otlp/exporter.rb b/exporter/otlp/lib/opentelemetry/exporter/otlp/exporter.rb index 1f32c4bd19..6083692ffc 100644 --- a/exporter/otlp/lib/opentelemetry/exporter/otlp/exporter.rb +++ b/exporter/otlp/lib/opentelemetry/exporter/otlp/exporter.rb @@ -48,6 +48,8 @@ def self.ssl_verify_mode def initialize(endpoint: nil, certificate_file: OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE', 'OTEL_EXPORTER_OTLP_CERTIFICATE'), + client_certificate_file: OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_TRACES_CLIENT_CERTIFICATE', 'OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE'), + client_key_file: OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_TRACES_CLIENT_KEY', 'OTEL_EXPORTER_OTLP_CLIENT_KEY'), ssl_verify_mode: Exporter.ssl_verify_mode, headers: OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_TRACES_HEADERS', 'OTEL_EXPORTER_OTLP_HEADERS', default: {}), compression: OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_TRACES_COMPRESSION', 'OTEL_EXPORTER_OTLP_COMPRESSION', default: 'gzip'), @@ -57,7 +59,7 @@ def initialize(endpoint: nil, raise ArgumentError, "unsupported compression key #{compression}" unless compression.nil? || %w[gzip none].include?(compression) - @http = http_connection(@uri, ssl_verify_mode, certificate_file) + @http = http_connection(@uri, ssl_verify_mode, certificate_file, client_certificate_file, client_key_file) @path = @uri.path @headers = prepare_headers(headers) @@ -102,11 +104,13 @@ def shutdown(timeout: nil) private - def http_connection(uri, ssl_verify_mode, certificate_file) + def http_connection(uri, ssl_verify_mode, certificate_file, client_certificate_file, client_key_file) http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = uri.scheme == 'https' http.verify_mode = ssl_verify_mode http.ca_file = certificate_file unless certificate_file.nil? + http.cert = OpenSSL::X509::Certificate.new(File.read(client_certificate_file)) unless client_certificate_file.nil? + http.key = OpenSSL::PKey::RSA.new(File.read(client_key_file)) unless client_key_file.nil? http.keep_alive_timeout = KEEP_ALIVE_TIMEOUT http end diff --git a/exporter/otlp/test/opentelemetry/exporter/otlp/exporter_test.rb b/exporter/otlp/test/opentelemetry/exporter/otlp/exporter_test.rb index 0f19d9142d..f37fc3a8d3 100644 --- a/exporter/otlp/test/opentelemetry/exporter/otlp/exporter_test.rb +++ b/exporter/otlp/test/opentelemetry/exporter/otlp/exporter_test.rb @@ -12,6 +12,12 @@ FAILURE = OpenTelemetry::SDK::Trace::Export::FAILURE VERSION = OpenTelemetry::Exporter::OTLP::VERSION DEFAULT_USER_AGENT = OpenTelemetry::Exporter::OTLP::Exporter::DEFAULT_USER_AGENT + CLIENT_CERT_A_PATH = File.dirname(__FILE__) + '/mtls-client-a.pem' + CLIENT_CERT_A = OpenSSL::X509::Certificate.new(File.read(CLIENT_CERT_A_PATH)) + CLIENT_KEY_A = OpenSSL::PKey::RSA.new(File.read(CLIENT_CERT_A_PATH)) + CLIENT_CERT_B_PATH = File.dirname(__FILE__) + '/mtls-client-b.pem' + CLIENT_CERT_B = OpenSSL::X509::Certificate.new(File.read(CLIENT_CERT_B_PATH)) + CLIENT_KEY_B = OpenSSL::PKey::RSA.new(File.read(CLIENT_CERT_B_PATH)) describe '#initialize' do it 'initializes with defaults' do @@ -23,6 +29,8 @@ _(exp.instance_variable_get(:@compression)).must_equal 'gzip' http = exp.instance_variable_get(:@http) _(http.ca_file).must_be_nil + _(http.cert).must_be_nil + _(http.key).must_be_nil _(http.use_ssl?).must_equal false _(http.address).must_equal 'localhost' _(http.verify_mode).must_equal OpenSSL::SSL::VERIFY_PEER @@ -76,7 +84,9 @@ it 'sets parameters from the environment' do exp = OpenTelemetry::TestHelpers.with_env('OTEL_EXPORTER_OTLP_ENDPOINT' => 'https://localhost:1234', - 'OTEL_EXPORTER_OTLP_CERTIFICATE' => '/foo/bar', + 'OTEL_EXPORTER_OTLP_CERTIFICATE' => '/foo/bar/cacert', + 'OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE' => CLIENT_CERT_A_PATH, + 'OTEL_EXPORTER_OTLP_CLIENT_KEY' => CLIENT_CERT_A_PATH, 'OTEL_EXPORTER_OTLP_HEADERS' => 'a=b,c=d', 'OTEL_EXPORTER_OTLP_COMPRESSION' => 'gzip', 'OTEL_RUBY_EXPORTER_OTLP_SSL_VERIFY_NONE' => 'true', @@ -88,7 +98,9 @@ _(exp.instance_variable_get(:@path)).must_equal '/v1/traces' _(exp.instance_variable_get(:@compression)).must_equal 'gzip' http = exp.instance_variable_get(:@http) - _(http.ca_file).must_equal '/foo/bar' + _(http.ca_file).must_equal '/foo/bar/cacert' + _(http.cert).must_equal CLIENT_CERT_A + _(http.key.params).must_equal CLIENT_KEY_A.params _(http.use_ssl?).must_equal true _(http.address).must_equal 'localhost' _(http.verify_mode).must_equal OpenSSL::SSL::VERIFY_NONE @@ -98,12 +110,16 @@ it 'prefers explicit parameters rather than the environment' do exp = OpenTelemetry::TestHelpers.with_env('OTEL_EXPORTER_OTLP_ENDPOINT' => 'https://localhost:1234', 'OTEL_EXPORTER_OTLP_CERTIFICATE' => '/foo/bar', + 'OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE' => CLIENT_CERT_A_PATH, + 'OTEL_EXPORTER_OTLP_CLIENT_KEY' => CLIENT_CERT_A_PATH, 'OTEL_EXPORTER_OTLP_HEADERS' => 'a:b,c:d', 'OTEL_EXPORTER_OTLP_COMPRESSION' => 'flate', 'OTEL_RUBY_EXPORTER_OTLP_SSL_VERIFY_PEER' => 'true', 'OTEL_EXPORTER_OTLP_TIMEOUT' => '11') do OpenTelemetry::Exporter::OTLP::Exporter.new(endpoint: 'http://localhost:4321', certificate_file: '/baz', + client_certificate_file: CLIENT_CERT_B_PATH, + client_key_file: CLIENT_CERT_B_PATH, headers: { 'x' => 'y' }, compression: 'gzip', ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE, @@ -115,6 +131,8 @@ _(exp.instance_variable_get(:@compression)).must_equal 'gzip' http = exp.instance_variable_get(:@http) _(http.ca_file).must_equal '/baz' + _(http.cert).must_equal CLIENT_CERT_B + _(http.key.params).must_equal CLIENT_KEY_B.params _(http.use_ssl?).must_equal false _(http.verify_mode).must_equal OpenSSL::SSL::VERIFY_NONE _(http.address).must_equal 'localhost' diff --git a/exporter/otlp/test/opentelemetry/exporter/otlp/mtls-client-a.pem b/exporter/otlp/test/opentelemetry/exporter/otlp/mtls-client-a.pem new file mode 100644 index 0000000000..ac4e9c42f0 --- /dev/null +++ b/exporter/otlp/test/opentelemetry/exporter/otlp/mtls-client-a.pem @@ -0,0 +1,50 @@ +-----BEGIN CERTIFICATE----- +MIIDmzCCAoOgAwIBAgIUGfmv/4kRRFbg319TIHkcwDMC3pUwDQYJKoZIhvcNAQEL +BQAwXTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM +GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEWMBQGA1UEAwwNYS5leGFtcGxlLnRs +ZDAeFw0yNDAzMDYyMjM4MzNaFw0yNDAzMDcyMjM4MzNaMF0xCzAJBgNVBAYTAkFV +MRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRz +IFB0eSBMdGQxFjAUBgNVBAMMDWEuZXhhbXBsZS50bGQwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQCaJG6fvdjFs9cnbF8i3wzO3VPUFH4hbAg5pV6rs81s +LuJnnlG3WX1sxYQGASqLKIzPiz3g4nKFaBfXvBYXo7M/AuJ2tEspedIcvdTqwx2k +owaaX7Y9lSx+h1OhovrviCqrqX5t9cIZWSeTU1bETcoTEd9/5usVe3XeaqmY8mAP +OA0dBKeotGIOxtTEP23CxW1AJwWPLZC1go8ycvsTQfmeif+g+6BOcKeZxkayhCvo +Ous+dt2dXa3x3yROe8ffZ5lAkPBLHfEUOSk/zpQnlkGzVrbXP4LCxLDQ8PlD3qEm +HbCK+c29mNeTjeoye5EVeQDO0ATiNh1/vSlMPpOY93IdAgMBAAGjUzBRMB0GA1Ud +DgQWBBQ5kq4dpjcQ8JSiyYrPswAoeltJJTAfBgNVHSMEGDAWgBQ5kq4dpjcQ8JSi +yYrPswAoeltJJTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBt +E0JyqwqL63ZrlxiBnnYrk0LoCuXjss7B9p3+6F5v0qKxxC+fpAVoh3CTRkDJRerz +ORXECXJ4drBetvje/dTX+5pNSLOOyQfOCaSohO4S82xNLSpFd6LcjYsfN8he482w +E1wuLoi9abktDVmX+sNVeiUUeuDMyqm51NRAlmzDhxTOPvqljdFZXRQO3X00qiQV +YJs7e1xql0R7DbrwOG5J2lenCwfj51ngmIpGxAaU3eLMAqLT6AZHhZxiATzCtCL9 +2cAKOrW/O8dRVhuWbCjFCIJIRIPrThbMaaw6p4mMkED7dnbOZofXLDgM+pxT5/eU +8DnQZlgXAkIlZY+9r3oG +-----END CERTIFICATE----- +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCaJG6fvdjFs9cn +bF8i3wzO3VPUFH4hbAg5pV6rs81sLuJnnlG3WX1sxYQGASqLKIzPiz3g4nKFaBfX +vBYXo7M/AuJ2tEspedIcvdTqwx2kowaaX7Y9lSx+h1OhovrviCqrqX5t9cIZWSeT +U1bETcoTEd9/5usVe3XeaqmY8mAPOA0dBKeotGIOxtTEP23CxW1AJwWPLZC1go8y +cvsTQfmeif+g+6BOcKeZxkayhCvoOus+dt2dXa3x3yROe8ffZ5lAkPBLHfEUOSk/ +zpQnlkGzVrbXP4LCxLDQ8PlD3qEmHbCK+c29mNeTjeoye5EVeQDO0ATiNh1/vSlM +PpOY93IdAgMBAAECggEAGE1LJaM8VHs0njIPU8Of1NN/Pm4WrrVGHLSOVvrLldVU +e6qxznrs8+O2G24+o176yFP3Jwf8rzzImYo9X2+/OF1/j+CAAyOCNWbWdUba2xSa +22bgqBfnQnGahV7ZOj+ZHqRj2vlGp1FvlGIsyVlMVTJZruQcxy/GVxEw+PypmWxp +u1MOjYEZWjvqJuxTjjnYDcQezfy9Qu0JNOF4+cVVKydewGApmcdDGThcDltxWizM +n144vgcfR97g2jDKs0GxSAuCbfYo2xoetei5iEVKmlXI/OuUIS78LSud9oBOoYPF ++5MVYOISTNcE+/GEp/BNTq6E8Kk4A1dhaYMvC2qXUQKBgQDKAtFfVP4DusMQd4PD +oixZ50GENohG4uGCDVBT/RJXUDueil5G4h9iSGxSt0nVOe3nBv5sHMgAARX3S+Pl +717tbzDgLXqfEqhCj9Kow7BtOussfSQ8hwxBazh9GEva48/Bx+OXFLcpPAcmc9WU +oQzCCb1yeZ7gsK6yNiLJTQGYCQKBgQDDVokcPHseqFJMOdd9Hr4Ci9gshbZj4htb +EXiUf50PP0Qco1VJ0MnPW9hateEdRYGVjLeCKHi1XHi8ARQQRJXVI4BI9yiIt8VO +snnFiEYJ/wgq4lyO8eWeNUaimFvhKHDBcz5tKwtmS4CGf7eAHdTB6R0izLtxkXcs +6+ZiO/bGdQKBgHdXVNPKBUq0wdpvkMM5gpQWP6lZAgdGr8zCCsujfXthpecSfYHI +wpuwh3YSXCcA0yAiDJpYInuGKLDw/5DuahlBEBHQLFnfjtHL37rd6NOO9DJTN94e +NkpLipK0kNOetDUZ3sV5cn+EvACme+4TetMDKA2B9i9tkbcsrj5YJPHpAoGAc5Gh +MTl/RlYjysF2AqrLlEoUrdK2ZEYEFU8y3fjafYjazW69KR0EKVCXoqN0+pKC5m4I +rFMxh2ucau7gZfeOBjoozgKc0raXX8YsUXgcqBFhTa37QP9Q8NdoYQ5vXblFbM64 +InKTHgSRmAG8GWqM0+UNvecPB1QfBE7VUU1U5XkCgYA34SlJVa1es7hifeAtb2XC +jVsHeEwcVnVq4S8aNo51taBJqPR9QIg4bssj4QmaMJntyQl94CE7NM11OssQ4rez +lY+BZGSmkuEFybqJ5CwHsKk+Cjdm4agqqU/uupOxFPxEzcD2YDgFto7RMPDP/Daf +iH9tE2qrnzQvE43+caLAuQ== +-----END PRIVATE KEY----- diff --git a/exporter/otlp/test/opentelemetry/exporter/otlp/mtls-client-b.pem b/exporter/otlp/test/opentelemetry/exporter/otlp/mtls-client-b.pem new file mode 100644 index 0000000000..0371b0be72 --- /dev/null +++ b/exporter/otlp/test/opentelemetry/exporter/otlp/mtls-client-b.pem @@ -0,0 +1,50 @@ +-----BEGIN CERTIFICATE----- +MIIDmzCCAoOgAwIBAgIUALrFiUtkMZj2wNSxHvtR+KBoBfowDQYJKoZIhvcNAQEL +BQAwXTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM +GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEWMBQGA1UEAwwNYi5leGFtcGxlLnRs +ZDAeFw0yNDAzMDYyMjM5NDdaFw0yNDAzMDcyMjM5NDdaMF0xCzAJBgNVBAYTAkFV +MRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRz +IFB0eSBMdGQxFjAUBgNVBAMMDWIuZXhhbXBsZS50bGQwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQC78NVeBRf/WK39UM1BpiQROA8mV8YXB8ugGB7AlOC2 +uLmVdWknMoS155afr4u0DICyNGDkxhWxyqG9UEsBOBmlNZSg85ewjdSMSBRh6Lxk +uWP9mVS0f1nRVMMBYNbkEPIJ5T2IRCwHP6/gpNKO9prH06atLCs6HP8y0cLKCWNJ +WixJJpT5goRldEBKTIUtOfM8Sa7ktoYeEvmGmjXjgP9pcdlmC3pjfzTk4+HH3cKL +RGG4dlhSTrOjVNBL30GWQjiNCM2fAHugUcrcGsmXhbBzmkBa8Rs6mI0ZJQIa/bWv +6KfWJ1eDF+VIVyhaQPeEkLgatP5NyuaqafvBrTMT1/GHAgMBAAGjUzBRMB0GA1Ud +DgQWBBQPlAhA673kZnZ9rvRwDejlc2kcjDAfBgNVHSMEGDAWgBQPlAhA673kZnZ9 +rvRwDejlc2kcjDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBx +OOFo6UxqdtFrN7BZPV00e2tlz3on21JlTxlk92fL0lHL86XJPMr1znBg0OkD1riw +OYJQ47wLZAgYIhs8UTrcZIZd1xwbJ4fjSDRFHOnI2BHJ/5pR9/NFsrOgeOorLbZf +x6aa+mQt1qYltTsH8gA1PP5syUcHmlSVLk5NWreMaHEEU8THVsyhxlv8yE+Zuh5y +JX1KpH4Eo3ekM4RwGuqjbtMgumD8gQf4lHysMEyemQOnebKxViz0bfLfEOMYIXBf +DaavjgfinXAQnOhItlHXisuAIDxSajnyR0kvTDdRbZBruRpUeKBcENQO95D4b5uA +BNnkf1CmWZoTwwqvDdNZ +-----END CERTIFICATE----- +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC78NVeBRf/WK39 +UM1BpiQROA8mV8YXB8ugGB7AlOC2uLmVdWknMoS155afr4u0DICyNGDkxhWxyqG9 +UEsBOBmlNZSg85ewjdSMSBRh6LxkuWP9mVS0f1nRVMMBYNbkEPIJ5T2IRCwHP6/g +pNKO9prH06atLCs6HP8y0cLKCWNJWixJJpT5goRldEBKTIUtOfM8Sa7ktoYeEvmG +mjXjgP9pcdlmC3pjfzTk4+HH3cKLRGG4dlhSTrOjVNBL30GWQjiNCM2fAHugUcrc +GsmXhbBzmkBa8Rs6mI0ZJQIa/bWv6KfWJ1eDF+VIVyhaQPeEkLgatP5NyuaqafvB +rTMT1/GHAgMBAAECggEACftFJGnaLReaoK+KlzKptmMR0D7X0x/43UHVWvO36Oca +rtNo3Z9ok6NUc/AGLW5/Ofe02wb3iVUdDfRBEjQr4y2wp8kIL2T4+ueryB9s518+ +mO4XJOxL6uWOOHkvheFYhCoGq0FjopGWrQmpR9UqbJ59uwjI1Zygo878LMV0M9Bc +5ABaF06yRVyZRH5lAC8zihBUsleE5jUM4FULHRUZFUVJx8ivFtJnLqLuywMOlkq4 +yY4Vf0M77dwVVY5enbnTbKEI25Csf47Gp9i5JS2jXY9ORPNjpYb8LHvp8NZTNczd +mGxQsWd6pWohnGkZdbJqQoQxMfCNYev5ejf7bJ1AoQKBgQDkpFtsIA0ExmcefIsE +ChuEpFUXko8wjNeCWFOjJx+Sed9RaRaQiSYOKmWwDjaP7dlJwUC0RIRy65gOGGnf +PUHOd7nEBtyDIjpSsjWWBjfsUEAqrhze0qpXSPmBzhNlOdsN01VzTgTNiTpea7hi +izgYjd1kLvJ5p+CV5NIxNhp+qQKBgQDSbbyKf6vPDNIZb9V/ef10Co31ahDVwlFa +P3FrZv+9eDMJQfXtkRTlu5Auo+TDaFFSMjrb5rTJcEuzBwHyyodqPvPsYC33/DVQ +jGYVqjQuG5q473DNebtinn5JR40ZfHiJlpx2Ms5xdbPIhN1efTRXmYIov0AIaKuR +on9LE8X8rwKBgFsyIzTxY7/v0tmaG2i1D1zMnxQT5QEcbCkVSebdh/5IlgZGwDVO +PtuPlZevU5v85ppAdqpwWdPsnG2i1zevmzvbDUFe6z1yvYiWhEEeodej+rQLVoCZ +zk+aT8qyg5HwjarqDD89czT380wN8zF7DhjdHN0EzLoxd6bR6fSu+8phAoGAV4v0 +PyLy1gedeZu/lXOpcRfbC9l++5AGzKdMhsSpbaiOgzGAIcCUkye/ysfBK1NBUhM3 +zblkSdKAjBFETEDaqedbEGLLfTre644eArF3WB9/9aUYp0QYI+WQ4Of12j6g341b +twlYPngbvjcY6nDoz/E757v55gW2K7cRgqjNXF0CgYEAxNfclcdKbUtGAsttZdY3 ++dcdBtqcLvpYlMsZPQaxNppyKBI5svtK715FsVbmLhINqiNo1aKIA5M3E2P88Fa2 +nqVrKsBOn3gCe+GFlFeWwNAfRlfmP1ZUHDp07zvNtRm4ZR/3hdAze4DbyWv58LfL +WSjqCjjBeurblkRv2QTXu2k= +-----END PRIVATE KEY----- From f9c86da5daf3025e93c18a1e02cd43a15adc0cec Mon Sep 17 00:00:00 2001 From: Damien Mathieu <42@dmathieu.com> Date: Wed, 14 Aug 2024 19:57:40 +0200 Subject: [PATCH 10/20] Add code spell task, fix issues and run the task in CI (#1661) * setup codespell * fix all codespell issues * add codespell github action --------- Co-authored-by: Matthew Wear --- .codespellignore | 2 ++ .codespellrc | 10 ++++++ .github/workflows/ci.yml | 6 ++++ .gitignore | 3 ++ CHANGELOG.md | 2 +- CONTRIBUTING.md | 2 +- Makefile | 35 +++++++++++++++++++ api/CHANGELOG.md | 2 +- api/README.md | 5 ++- api/lib/opentelemetry/baggage.rb | 2 +- api/lib/opentelemetry/baggage/builder.rb | 2 +- .../propagation/text_map_propagator.rb | 2 +- .../trace/propagation/trace_context.rb | 2 +- api/lib/opentelemetry/trace/span_kind.rb | 2 +- .../propagation/text_map_getter_test.rb | 2 +- .../trace_context/trace_parent_test.rb | 2 +- .../jaeger/thrift/gen-rb/zipkincore_types.rb | 4 +-- exporter/jaeger/thrift/zipkincore.thrift | 6 ++-- exporter/otlp-metrics/README.md | 2 +- exporter/otlp/README.md | 4 +-- .../opentelemetry/exporter/zipkin/exporter.rb | 2 +- .../exporter/zipkin/transformer.rb | 2 +- .../lib/opentelemetry/sdk/logs/log_record.rb | 2 +- metrics_api/README.md | 6 +++- .../test/opentelemetry/opentelemetry_test.rb | 2 +- propagator/b3/CHANGELOG.md | 2 +- propagator/jaeger/CHANGELOG.md | 2 +- registry/CHANGELOG.md | 2 +- .../opentelemetry/instrumentation/registry.rb | 2 +- requirements.txt | 1 + sdk/README.md | 8 ++++- sdk/lib/opentelemetry/sdk/configurator.rb | 2 +- sdk/opentelemetry-sdk.gemspec | 2 +- .../opentelemetry/sdk/configurator_test.rb | 2 +- .../export/in_memory_span_exporter_test.rb | 2 +- .../sdk/trace/span_limits_test.rb | 2 +- .../semantic_conventions/resource.rb | 2 +- 37 files changed, 105 insertions(+), 35 deletions(-) create mode 100644 .codespellignore create mode 100644 .codespellrc create mode 100644 Makefile create mode 100644 requirements.txt diff --git a/.codespellignore b/.codespellignore new file mode 100644 index 0000000000..c43186667f --- /dev/null +++ b/.codespellignore @@ -0,0 +1,2 @@ +ot +te diff --git a/.codespellrc b/.codespellrc new file mode 100644 index 0000000000..0dbfda339f --- /dev/null +++ b/.codespellrc @@ -0,0 +1,10 @@ +# https://github.com/codespell-project/codespell +[codespell] +builtin = clear,rare,informal +check-filenames = +check-hidden = +ignore-words = .codespellignore +interactive = 1 +skip = .git,venv,coverage,doc,docs +uri-ignore-words-list = * +write = diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cde048d562..8acd5270ca 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -208,3 +208,9 @@ jobs: with: gem: "${{ matrix.gem }}" ruby: "truffleruby" + + codespell: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - run: make codespell diff --git a/.gitignore b/.gitignore index c8016688c9..9d20c23219 100644 --- a/.gitignore +++ b/.gitignore @@ -22,4 +22,7 @@ # rbenv configuration .ruby-version +# Python virtual env for codespell +venv + tags diff --git a/CHANGELOG.md b/CHANGELOG.md index 597328c71f..e9c8721cd0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,6 @@ All notable changes to this project were documented in this file. This is of his * [#286](https://github.com/open-telemetry/opentelemetry-ruby/pull/286) Update Schedule. ([@fbogsany](https://github.com/fbogsany)) -* [#288](https://github.com/open-telemetry/opentelemetry-ruby/pull/288) Fix api/sdk gem install instuctions. ([@mwlang](https://github.com/mwlang)) +* [#288](https://github.com/open-telemetry/opentelemetry-ruby/pull/288) Fix api/sdk gem install instructions. ([@mwlang](https://github.com/mwlang)) * [#294](https://github.com/open-telemetry/opentelemetry-ruby/pull/294) Add CHANGELOG. ([@ericmustin](https://github.com/ericmustin)) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8df79412eb..0e0bf8446a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -154,7 +154,7 @@ to ensure that your code complies before opening a pull request. We also use Yard to generate class documentation automatically. Among other things, this means: - * Methods and arguments should include the appropraite type annotations + * Methods and arguments should include the appropriate type annotations * You can use markdown formatting in your documentation comments You can generate the docs locally to see the results, by running: diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000..7b9a6baa4d --- /dev/null +++ b/Makefile @@ -0,0 +1,35 @@ +# Virtualized python tools via docker + +# The directory where the virtual environment is created. +VENVDIR := venv + +PYTOOLS := $(VENVDIR)/bin + +# The pip executable in the virtual environment. +PIP := $(PYTOOLS)/pip + +# The directory in the docker image where the current directory is mounted. +WORKDIR := /workdir + +# The python image to use for the virtual environment. +PYTHONIMAGE := python:3.11.3-slim-bullseye + +# Run the python image with the current directory mounted. +DOCKERPY := docker run --rm -v "$(CURDIR):$(WORKDIR)" -w $(WORKDIR) $(PYTHONIMAGE) + +# Create a virtual environment for Python tools. +$(PYTOOLS): +# The `--upgrade` flag is needed to ensure that the virtual environment is +# created with the latest pip version. + @$(DOCKERPY) bash -c "python3 -m venv $(VENVDIR) && $(PIP) install --upgrade pip" + +# Install python packages into the virtual environment. +$(PYTOOLS)/%: $(PYTOOLS) + @$(DOCKERPY) $(PIP) install -r requirements.txt + +CODESPELL = $(PYTOOLS)/codespell +$(CODESPELL): PACKAGE=codespell + +.PHONY: codespell +codespell: $(CODESPELL) + @$(DOCKERPY) $(CODESPELL) diff --git a/api/CHANGELOG.md b/api/CHANGELOG.md index 43b6e5fb6d..35c3e87ad9 100644 --- a/api/CHANGELOG.md +++ b/api/CHANGELOG.md @@ -71,7 +71,7 @@ * BREAKING CHANGE: Remove optional parent_context from in_span [729](https://github.com/open-telemetry/opentelemetry-ruby/pull/729) * BREAKING CHANGE: Refactor Baggage to remove Noop* [800](https://github.com/open-telemetry/opentelemetry-ruby/pull/800) - - The noop baggage manger has been removed. + - The noop baggage manager has been removed. - The baggage management methods are now available through OpenTelemetry::Baggage#method, previously OpenTelemetry.baggage#method * BREAKING CHANGE: Total order constraint on span.status= [805](https://github.com/open-telemetry/opentelemetry-ruby/pull/805) - The OpenTelemetry::Trace::Util::HttpToStatus module has been removed as it was incorrectly setting the span status to OK for codes codes in the range 100..399 diff --git a/api/README.md b/api/README.md index 3e464b3927..b44d1ff8a0 100644 --- a/api/README.md +++ b/api/README.md @@ -12,7 +12,10 @@ OpenTelemetry provides a single set of APIs, libraries, agents, and collector se The `opentelemetry-api` gem defines the core OpenTelemetry interfaces in the form of abstract classes and no-op implementations. That is, it defines interfaces and data types sufficient for a library or application to code against to produce telemetry data, but does not actually collect, analyze, or export the data. -To collect and analyze telemetry data, *applications* should also install a concrete implementation of the API, such as the `opentelemetry-sdk` gem. However, *libraries* that produce telemetry data should depend only on `opentelemetry-api`, deferring the choise of concrete implementation to the application developer. +To collect and analyze telemetry data, *applications* should also +install a concrete implementation of the API, such as the +`opentelemetry-sdk` gem. However, *libraries* that produce telemetry +data should depend only on `opentelemetry-api`, deferring the choice of concrete implementation to the application developer. ## How do I get started? diff --git a/api/lib/opentelemetry/baggage.rb b/api/lib/opentelemetry/baggage.rb index 10cfc483be..2391e0c7f5 100644 --- a/api/lib/opentelemetry/baggage.rb +++ b/api/lib/opentelemetry/baggage.rb @@ -73,7 +73,7 @@ def raw_entries(context: Context.current) # @param [String] key The key to store this value under # @param [String] value String value to be stored under key # @param [optional String] metadata This is here to store properties - # received from other W3C Baggage impelmentations but is not exposed in + # received from other W3C Baggage implementations but is not exposed in # OpenTelemetry. This is condsidered private API and not for use by # end-users. # @param [optional Context] context The context to update with new diff --git a/api/lib/opentelemetry/baggage/builder.rb b/api/lib/opentelemetry/baggage/builder.rb index ff213a28e0..b3323dbe7f 100644 --- a/api/lib/opentelemetry/baggage/builder.rb +++ b/api/lib/opentelemetry/baggage/builder.rb @@ -21,7 +21,7 @@ def initialize(entries) # @param [String] key The key to store this value under # @param [String] value String value to be stored under key # @param [optional String] metadata This is here to store properties - # received from other W3C Baggage impelmentations but is not exposed in + # received from other W3C Baggage implementations but is not exposed in # OpenTelemetry. This is condsidered private API and not for use by # end-users. def set_value(key, value, metadata: nil) diff --git a/api/lib/opentelemetry/baggage/propagation/text_map_propagator.rb b/api/lib/opentelemetry/baggage/propagation/text_map_propagator.rb index 3dadc0c166..53bb51b995 100644 --- a/api/lib/opentelemetry/baggage/propagation/text_map_propagator.rb +++ b/api/lib/opentelemetry/baggage/propagation/text_map_propagator.rb @@ -100,7 +100,7 @@ def encode(baggage) def encode_value(key, entry) result = +"#{CGI.escape(key.to_s)}=#{CGI.escape(entry.value.to_s)}" - # We preserve metadata recieved on extract and assume it's already formatted + # We preserve metadata received on extract and assume it's already formatted # for transport. It's sent as-is without further processing. result << ";#{entry.metadata}" if entry.metadata result diff --git a/api/lib/opentelemetry/trace/propagation/trace_context.rb b/api/lib/opentelemetry/trace/propagation/trace_context.rb index f811787809..1030809896 100644 --- a/api/lib/opentelemetry/trace/propagation/trace_context.rb +++ b/api/lib/opentelemetry/trace/propagation/trace_context.rb @@ -10,7 +10,7 @@ module OpenTelemetry module Trace module Propagation - # The TraceContext module contains injectors, extractors, and utilties + # The TraceContext module contains injectors, extractors, and utilities # for context propagation in the W3C Trace Context format. module TraceContext extend self diff --git a/api/lib/opentelemetry/trace/span_kind.rb b/api/lib/opentelemetry/trace/span_kind.rb index 2312a9523b..fe89e80bca 100644 --- a/api/lib/opentelemetry/trace/span_kind.rb +++ b/api/lib/opentelemetry/trace/span_kind.rb @@ -26,7 +26,7 @@ module SpanKind # spans. PRODUCER = :producer - # Indicates that the span describes consumer recieving a message from a broker. Unlike client + # Indicates that the span describes consumer receiving a message from a broker. Unlike client # and server, there is no direct critical path latency relationship between producer and # consumer spans. CONSUMER = :consumer diff --git a/api/test/opentelemetry/context/propagation/text_map_getter_test.rb b/api/test/opentelemetry/context/propagation/text_map_getter_test.rb index 88f3689197..d2d4589704 100644 --- a/api/test/opentelemetry/context/propagation/text_map_getter_test.rb +++ b/api/test/opentelemetry/context/propagation/text_map_getter_test.rb @@ -20,7 +20,7 @@ _(getter.get(carrier, 'x-source-id')).must_equal('123') end - it 'returns nil for non-existant key' do + it 'returns nil for non-existent key' do _(getter.get(carrier, 'not-here')).must_be_nil end end diff --git a/api/test/opentelemetry/trace/propagation/trace_context/trace_parent_test.rb b/api/test/opentelemetry/trace/propagation/trace_context/trace_parent_test.rb index 4869f8b171..65f47d8633 100644 --- a/api/test/opentelemetry/trace/propagation/trace_context/trace_parent_test.rb +++ b/api/test/opentelemetry/trace/propagation/trace_context/trace_parent_test.rb @@ -76,7 +76,7 @@ _(tp.to_s).must_equal(expected) end - it 'must ignore flags it doesnt know (use the mask)' do + it 'must ignore flags it doesn\'t know (use the mask)' do value = '00-0000000000000000000000000000000a-000000000000000a-ff' assert TraceParent.from_string(value).sampled? value = '00-0000000000000000000000000000000a-000000000000000a-04' diff --git a/exporter/jaeger/thrift/gen-rb/zipkincore_types.rb b/exporter/jaeger/thrift/gen-rb/zipkincore_types.rb index 11ac3a4e35..95aea0edec 100644 --- a/exporter/jaeger/thrift/gen-rb/zipkincore_types.rb +++ b/exporter/jaeger/thrift/gen-rb/zipkincore_types.rb @@ -181,7 +181,7 @@ class Span # precise value possible. For example, gettimeofday or syncing nanoTime # against a tick of currentTimeMillis. # -# For compatibilty with instrumentation that precede this field, collectors +# For compatibility with instrumentation that precede this field, collectors # or span stores can derive this via Annotation.timestamp. # For example, SERVER_RECV.timestamp or CLIENT_SEND.timestamp. # @@ -194,7 +194,7 @@ class Span # precise measurement decoupled from problems of clocks, such as skew or NTP # updates causing time to move backwards. # -# For compatibilty with instrumentation that precede this field, collectors +# For compatibility with instrumentation that precede this field, collectors # or span stores can derive this by subtracting Annotation.timestamp. # For example, SERVER_SEND.timestamp - SERVER_RECV.timestamp. # diff --git a/exporter/jaeger/thrift/zipkincore.thrift b/exporter/jaeger/thrift/zipkincore.thrift index 2bb563c56a..854294df58 100644 --- a/exporter/jaeger/thrift/zipkincore.thrift +++ b/exporter/jaeger/thrift/zipkincore.thrift @@ -292,7 +292,7 @@ struct Span { 3: string name, 4: i64 id, # unique span id, only used for this span 5: optional i64 parent_id, # parent span id - 6: list annotations, # all annotations/events that occured, sorted by timestamp + 6: list annotations, # all annotations/events that occurred, sorted by timestamp 8: list binary_annotations # any binary annotations 9: optional bool debug = 0 # if true, we DEMAND that this span passes all samplers /** @@ -302,7 +302,7 @@ struct Span { * precise value possible. For example, gettimeofday or syncing nanoTime * against a tick of currentTimeMillis. * - * For compatibilty with instrumentation that precede this field, collectors + * For compatibility with instrumentation that precede this field, collectors * or span stores can derive this via Annotation.timestamp. * For example, SERVER_RECV.timestamp or CLIENT_SEND.timestamp. * @@ -317,7 +317,7 @@ struct Span { * precise measurement decoupled from problems of clocks, such as skew or NTP * updates causing time to move backwards. * - * For compatibilty with instrumentation that precede this field, collectors + * For compatibility with instrumentation that precede this field, collectors * or span stores can derive this by subtracting Annotation.timestamp. * For example, SERVER_SEND.timestamp - SERVER_RECV.timestamp. * diff --git a/exporter/otlp-metrics/README.md b/exporter/otlp-metrics/README.md index fa596d224d..559cfe5285 100644 --- a/exporter/otlp-metrics/README.md +++ b/exporter/otlp-metrics/README.md @@ -122,7 +122,7 @@ The `opentelemetry-exporter-otlp-metrics` gem is distributed under the Apache 2. ## Working with Proto Definitions -The OTel community maintains a [repository with protobuf definitions][otel-proto-github] that language and collector implementors use to generate code. +The OTel community maintains a [repository with protobuf definitions][otel-proto-github] that language and collector implementers use to generate code. Maintainers are expected to keep up to date with the latest version of protos. This guide will provide you with step-by-step instructions on updating the OTLP Exporter gem with the latest definitions. diff --git a/exporter/otlp/README.md b/exporter/otlp/README.md index 1161313270..3a29f1ab1e 100644 --- a/exporter/otlp/README.md +++ b/exporter/otlp/README.md @@ -99,7 +99,7 @@ The `opentelemetry-exporter-otlp` gem is distributed under the Apache 2.0 licens ## Working with Proto Definitions -The OTel community maintains a [repository with protobuf definitions][otel-proto-github] that language and collector implementors use to generate code. +The OTel community maintains a [repository with protobuf definitions][otel-proto-github] that language and collector implementers use to generate code. Maintainers are expected to keep up to date with the latest version of protos. This guide will provide you with step-by-step instructions on updating the OTLP Exporter gem with the latest definitions. @@ -138,7 +138,7 @@ $> bundle exec rake test ``` -**Commit the chnages and open a PR!** +**Commit the changes and open a PR!** [opentelemetry-collector-home]: https://opentelemetry.io/docs/collector/about/ [opentelemetry-home]: https://opentelemetry.io diff --git a/exporter/zipkin/lib/opentelemetry/exporter/zipkin/exporter.rb b/exporter/zipkin/lib/opentelemetry/exporter/zipkin/exporter.rb index 1052ff871f..43efbee2cb 100644 --- a/exporter/zipkin/lib/opentelemetry/exporter/zipkin/exporter.rb +++ b/exporter/zipkin/lib/opentelemetry/exporter/zipkin/exporter.rb @@ -135,7 +135,7 @@ def send_spans(zipkin_spans, timeout: nil) # rubocop:disable Metrics/MethodLengt response = measure_request_duration { @http.request(request) } response.body # Read and discard body - # in opentelemetry-js 200-399 is succcess, in opentelemetry-collector zipkin exporter,200-299 is a success + # in opentelemetry-js 200-399 is success, in opentelemetry-collector zipkin exporter,200-299 is a success # zipkin api docs list 202 as default success code # https://zipkin.io/zipkin-api/#/default/post_spans # TODO: redirect diff --git a/exporter/zipkin/lib/opentelemetry/exporter/zipkin/transformer.rb b/exporter/zipkin/lib/opentelemetry/exporter/zipkin/transformer.rb index 410978289c..bed33c0cf6 100644 --- a/exporter/zipkin/lib/opentelemetry/exporter/zipkin/transformer.rb +++ b/exporter/zipkin/lib/opentelemetry/exporter/zipkin/transformer.rb @@ -50,7 +50,7 @@ def to_zipkin_span(span_d, resource) add_status_tags(span_d, tags) tags = aggregate_span_tags(span_d, tags) - # TOOO: set debug flag? (is that represented in tracestate?) + # TODO: set debug flag? (is that represented in tracestate?) # https://github.com/openzipkin/b3-propagation#why-is-debug-encoded-as-x-b3-flags-1 # https://github.com/openzipkin/zipkin-api/blob/7692ca7be4dc3be9225db550d60c4d30e6e9ec59/zipkin2-api.yaml#L475 # TODO: shared key mapping diff --git a/logs_sdk/lib/opentelemetry/sdk/logs/log_record.rb b/logs_sdk/lib/opentelemetry/sdk/logs/log_record.rb index 8ee21b54b6..10d1dcb7ab 100644 --- a/logs_sdk/lib/opentelemetry/sdk/logs/log_record.rb +++ b/logs_sdk/lib/opentelemetry/sdk/logs/log_record.rb @@ -45,7 +45,7 @@ class LogRecord < OpenTelemetry::Logs::LogRecord # current context. # @param [optional OpenTelemetry::Trace::TraceFlags] trace_flags The # trace flags associated with the current context. - # @param [optional OpenTelemetry::SDK::Resources::Resource] recource The + # @param [optional OpenTelemetry::SDK::Resources::Resource] resource The # source of the log, desrived from the LoggerProvider. # @param [optional OpenTelemetry::SDK::InstrumentationScope] instrumentation_scope # The instrumentation scope, derived from the emitting Logger diff --git a/metrics_api/README.md b/metrics_api/README.md index 48721a5358..8656711979 100644 --- a/metrics_api/README.md +++ b/metrics_api/README.md @@ -12,7 +12,11 @@ OpenTelemetry provides a single set of APIs, libraries, agents, and collector se The `opentelemetry-metrics-api` gem defines the core OpenTelemetry interfaces in the form of abstract classes and no-op implementations. That is, it defines interfaces and data types sufficient for a library or application to code against to produce telemetry data, but does not actually collect, analyze, or export the data. -To collect and analyze telemetry data, _applications_ should also install a concrete implementation of the API, such as the `opentelemetry-metrics-sdk` gem. However, _libraries_ that produce telemetry data should depend only on `opentelemetry-metrics-api`, deferring the choise of concrete implementation to the application developer. +To collect and analyze telemetry data, _applications_ should also +install a concrete implementation of the API, such as the +`opentelemetry-metrics-sdk` gem. However, _libraries_ that produce +telemetry data should depend only on `opentelemetry-metrics-api`, +deferring the choice of concrete implementation to the application developer. This code is still under development and is not a complete implementation of the Metrics API. Until the code becomes stable, Metrics API functionality will live outside the `opentelemetry-api` library. diff --git a/metrics_api/test/opentelemetry/opentelemetry_test.rb b/metrics_api/test/opentelemetry/opentelemetry_test.rb index 81324228c1..d7d768e476 100644 --- a/metrics_api/test/opentelemetry/opentelemetry_test.rb +++ b/metrics_api/test/opentelemetry/opentelemetry_test.rb @@ -8,7 +8,7 @@ describe OpenTelemetry do after do - # TODO: After Metrics SDK is incoporated into OpenTelemetry SDK, move this + # TODO: After Metrics SDK is incorporated into OpenTelemetry SDK, move this # to OpenTelemetry::TestHelpers.reset_opentelemetry OpenTelemetry.instance_variable_set( :@meter_provider, diff --git a/propagator/b3/CHANGELOG.md b/propagator/b3/CHANGELOG.md index 96fb82debf..c58f37f922 100644 --- a/propagator/b3/CHANGELOG.md +++ b/propagator/b3/CHANGELOG.md @@ -24,7 +24,7 @@ ### v0.18.0 / 2021-05-21 -* ADDED: Updated API depedency for 1.0.0.rc1 +* ADDED: Updated API dependency for 1.0.0.rc1 ### v0.17.0 / 2021-04-22 diff --git a/propagator/jaeger/CHANGELOG.md b/propagator/jaeger/CHANGELOG.md index c70ff7dc62..b57549485e 100644 --- a/propagator/jaeger/CHANGELOG.md +++ b/propagator/jaeger/CHANGELOG.md @@ -39,7 +39,7 @@ ### v0.18.0 / 2021-05-21 -* ADDED: Updated API depedency for 1.0.0.rc1 +* ADDED: Updated API dependency for 1.0.0.rc1 ### v0.17.0 / 2021-04-22 diff --git a/registry/CHANGELOG.md b/registry/CHANGELOG.md index e3f64d18ab..0f9578da22 100644 --- a/registry/CHANGELOG.md +++ b/registry/CHANGELOG.md @@ -12,7 +12,7 @@ ### v0.2.0 / 2022-09-14 -* Bump minimum API version depedency to 1.1 +* Bump minimum API version dependency to 1.1 ### v0.1.0 / 2022-04-11 diff --git a/registry/lib/opentelemetry/instrumentation/registry.rb b/registry/lib/opentelemetry/instrumentation/registry.rb index e3414e71c1..4fa21526bd 100644 --- a/registry/lib/opentelemetry/instrumentation/registry.rb +++ b/registry/lib/opentelemetry/instrumentation/registry.rb @@ -9,7 +9,7 @@ module Instrumentation # The instrumentation Registry contains information about instrumentation # available and facilitates discovery, installation and # configuration. This functionality is primarily useful for SDK - # implementors. + # implementers. class Registry def initialize @lock = Mutex.new diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000..ab09daf9d5 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +codespell==2.3.0 diff --git a/sdk/README.md b/sdk/README.md index 1d10475c21..97da0a9884 100644 --- a/sdk/README.md +++ b/sdk/README.md @@ -12,7 +12,13 @@ OpenTelemetry provides a single set of APIs, libraries, agents, and collector se The `opentelemetry-sdk` gem provides the reference implementation of the OpenTelemetry Ruby interfaces defined in the `opentelemetry-api` gem. That is, it includes the *functionality* needed to collect, analyze, and export telemetry data produced using the API. -Generally, Ruby *applications* should install `opentelemetry-sdk` (or other concrete implementation of the OpenTelemetry API). Using the SDK, an application can configure how it wants telemetry data to be handled, including which data should be persisted, how it should be formatted, and where it should be recorded or exported. However, *libraries* that produce telemetry data should generally depend only on `opentelemetry-api`, deferring the choise of concrete implementation to the application developer. +Generally, Ruby *applications* should install `opentelemetry-sdk` (or +other concrete implementation of the OpenTelemetry API). Using the SDK, +an application can configure how it wants telemetry data to be handled, +including which data should be persisted, how it should be formatted, +and where it should be recorded or exported. However, *libraries* that +produce telemetry data should generally depend only on +`opentelemetry-api`, deferring the choice of concrete implementation to the application developer. ## How do I get started? diff --git a/sdk/lib/opentelemetry/sdk/configurator.rb b/sdk/lib/opentelemetry/sdk/configurator.rb index 1aa918a4c2..ae1bbaf07b 100644 --- a/sdk/lib/opentelemetry/sdk/configurator.rb +++ b/sdk/lib/opentelemetry/sdk/configurator.rb @@ -90,7 +90,7 @@ def service_version=(service_version) ) end - # Install an instrumentation with specificied optional +config+. + # Install an instrumentation with specified optional +config+. # Use can be called multiple times to install multiple instrumentation. # Only +use+ or +use_all+, but not both when installing # instrumentation. A call to +use_all+ after +use+ will result in an diff --git a/sdk/opentelemetry-sdk.gemspec b/sdk/opentelemetry-sdk.gemspec index 0adc96fdfd..de7edca8a4 100644 --- a/sdk/opentelemetry-sdk.gemspec +++ b/sdk/opentelemetry-sdk.gemspec @@ -31,7 +31,7 @@ Gem::Specification.new do |spec| # This is an intentionally loose dependency, since we want to be able to # release new versions of opentelemetry-semantic_conventions without requiring - # a new SDK release. The requirements of the SDK have been satisifed since the + # a new SDK release. The requirements of the SDK have been satisfied since the # initial release of opentelemetry-semantic_conventions, so we feel it is safe. spec.add_dependency 'opentelemetry-semantic_conventions' diff --git a/sdk/test/opentelemetry/sdk/configurator_test.rb b/sdk/test/opentelemetry/sdk/configurator_test.rb index 50e6c18ef7..b363695b1e 100644 --- a/sdk/test/opentelemetry/sdk/configurator_test.rb +++ b/sdk/test/opentelemetry/sdk/configurator_test.rb @@ -292,7 +292,7 @@ ) end - it 'accepts comma separated list with preceeding or trailing spaces as an environment variable' do + it 'accepts comma separated list with preceding or trailing spaces as an environment variable' do OpenTelemetry::TestHelpers.with_env('OTEL_TRACES_EXPORTER' => 'zipkin , console') do configurator.configure end diff --git a/sdk/test/opentelemetry/sdk/trace/export/in_memory_span_exporter_test.rb b/sdk/test/opentelemetry/sdk/trace/export/in_memory_span_exporter_test.rb index 0d00255b55..0a3da217ac 100644 --- a/sdk/test/opentelemetry/sdk/trace/export/in_memory_span_exporter_test.rb +++ b/sdk/test/opentelemetry/sdk/trace/export/in_memory_span_exporter_test.rb @@ -23,7 +23,7 @@ end it 'accepts an Enumerable of SpanDatas as argument to #export' do - # An anonymous Struct serves as a handy implementor of Enumerable + # An anonymous Struct serves as a handy implementer of Enumerable enumerable = Struct.new(:span_data1, :span_data2).new enumerable.span_data1 = span_data1 enumerable.span_data2 = span_data2 diff --git a/sdk/test/opentelemetry/sdk/trace/span_limits_test.rb b/sdk/test/opentelemetry/sdk/trace/span_limits_test.rb index b8d421ea4a..57aeb65e09 100644 --- a/sdk/test/opentelemetry/sdk/trace/span_limits_test.rb +++ b/sdk/test/opentelemetry/sdk/trace/span_limits_test.rb @@ -20,7 +20,7 @@ _(span_limits.link_attribute_count_limit).must_equal 128 end - it 'prioritizes specific environment varibles for attribute value length limits' do + it 'prioritizes specific environment variables for attribute value length limits' do OpenTelemetry::TestHelpers.with_env('OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT' => '35', 'OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT' => '33', 'OTEL_EVENT_ATTRIBUTE_VALUE_LENGTH_LIMIT' => '32') do diff --git a/semantic_conventions/lib/opentelemetry/semantic_conventions/resource.rb b/semantic_conventions/lib/opentelemetry/semantic_conventions/resource.rb index 912946f71f..39e8efeef3 100644 --- a/semantic_conventions/lib/opentelemetry/semantic_conventions/resource.rb +++ b/semantic_conventions/lib/opentelemetry/semantic_conventions/resource.rb @@ -104,7 +104,7 @@ module Resource # # * **AWS Lambda:** The function [ARN](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html). # Take care not to use the "invoked ARN" directly but replace any - # [alias suffix](https://docs.aws.amazon.com/lambda/latest/dg/configuration-aliases.html) with the resolved function version, as the same runtime instance may be invokable with multiple + # [alias suffix](https://docs.aws.amazon.com/lambda/latest/dg/configuration-aliases.html) with the resolved function version, as the same runtime instance may be invocable with multiple # different aliases. # * **GCP:** The [URI of the resource](https://cloud.google.com/iam/docs/full-resource-names) # * **Azure:** The [Fully Qualified Resource ID](https://docs.microsoft.com/en-us/rest/api/resources/resources/get-by-id). From 79aa07b5fe3ad54f98f7b95db8a8cebf8d9f1890 Mon Sep 17 00:00:00 2001 From: Kayla Reopelle <87386821+kaylareopelle@users.noreply.github.com> Date: Wed, 14 Aug 2024 11:06:18 -0700 Subject: [PATCH 11/20] feat: Add log record exporter interface (#1666) * feat: Add log record exporter interface * chore: Move ExportError within Export module --------- Co-authored-by: Matthew Wear --- logs_sdk/lib/opentelemetry/sdk/logs/export.rb | 6 +- .../sdk/logs/export/log_record_exporter.rb | 58 +++++++++++++++++++ .../logs/export/log_record_exporter_test.rb | 39 +++++++++++++ 3 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 logs_sdk/lib/opentelemetry/sdk/logs/export/log_record_exporter.rb create mode 100644 logs_sdk/test/opentelemetry/sdk/logs/export/log_record_exporter_test.rb diff --git a/logs_sdk/lib/opentelemetry/sdk/logs/export.rb b/logs_sdk/lib/opentelemetry/sdk/logs/export.rb index 2a9f150d3b..5cb94455a9 100644 --- a/logs_sdk/lib/opentelemetry/sdk/logs/export.rb +++ b/logs_sdk/lib/opentelemetry/sdk/logs/export.rb @@ -7,8 +7,10 @@ module OpenTelemetry module SDK module Logs - # The Export module contains result codes for exporters + # The Export module contains the built-in exporters and log record + # processors for the OpenTelemetry reference implementation. module Export + ExportError = Class.new(OpenTelemetry::Error) # The operation finished successfully. SUCCESS = 0 @@ -21,3 +23,5 @@ module Export end end end + +require_relative 'export/log_record_exporter' diff --git a/logs_sdk/lib/opentelemetry/sdk/logs/export/log_record_exporter.rb b/logs_sdk/lib/opentelemetry/sdk/logs/export/log_record_exporter.rb new file mode 100644 index 0000000000..7e4012aa34 --- /dev/null +++ b/logs_sdk/lib/opentelemetry/sdk/logs/export/log_record_exporter.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Logs + module Export + # LogRecordExporter describes a duck type. It is not required to + # subclass this class to provide an implementation of LogRecordExporter, + # provided the interface is satisfied. LogRecordExporter allows + # different tracing services to export log record data in their own format. + # + # To export data an exporter MUST be registered to the {LoggerProvider} + # using a {LogRecordProcessor} implementation. + class LogRecordExporter + def initialize + @stopped = false + end + + # Called to export {LogRecordData}s. + # + # @param [Enumerable] log_record_data the list of + # {LogRecordData} to be exported. + # @param [optional Numeric] timeout An optional timeout in seconds. + # + # @return [Integer] the result of the export. + def export(log_record_data, timeout: nil) + return SUCCESS unless @stopped + + FAILURE + end + + # Called when {LoggerProvider#force_flush} is called, if this exporter is + # registered to a {LoggerProvider} object. + # + # @param [optional Numeric] timeout An optional timeout in seconds. + # @return [Integer] SUCCESS if no error occurred, FAILURE if a + # non-specific failure occurred, TIMEOUT if a timeout occurred. + def force_flush(timeout: nil) + SUCCESS + end + + # Called when {LoggerProvider#shutdown} is called, if this exporter is + # registered to a {LoggerProvider} object. + # + # @param [optional Numeric] timeout An optional timeout in seconds. + def shutdown(timeout: nil) + @stopped = true + SUCCESS + end + end + end + end + end +end diff --git a/logs_sdk/test/opentelemetry/sdk/logs/export/log_record_exporter_test.rb b/logs_sdk/test/opentelemetry/sdk/logs/export/log_record_exporter_test.rb new file mode 100644 index 0000000000..9d1bb79b1b --- /dev/null +++ b/logs_sdk/test/opentelemetry/sdk/logs/export/log_record_exporter_test.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'test_helper' + +describe OpenTelemetry::SDK::Logs::Export::LogRecordExporter do + export = OpenTelemetry::SDK::Logs::Export + + let(:log_record_data1) { OpenTelemetry::SDK::Logs::LogRecordData.new({ name: 'name1' }) } + let(:log_record_data2) { OpenTelemetry::SDK::Logs::LogRecordData.new({ name: 'name2' }) } + let(:log_records) { [log_record_data1, log_record_data2] } + let(:exporter) { export::LogRecordExporter.new } + + it 'accepts an Array of LogRecordData as arg to #export and succeeds' do + _(exporter.export(log_records)).must_equal export::SUCCESS + end + + it 'accepts an Enumerable of LogRecordData as arg to #export and succeeds' do + enumerable = Struct.new(:log_record0, :log_record1).new(log_records[0], log_records[1]) + + _(exporter.export(enumerable)).must_equal export::SUCCESS + end + + it 'accepts calls to #shutdown' do + exporter.shutdown + end + + it 'fails to export after shutdown' do + exporter.shutdown + _(exporter.export(log_records)).must_equal export::FAILURE + end + + it 'returns SUCCESS when #force_flush is called' do + assert(export::SUCCESS, exporter.force_flush) + end +end From 0109aa7744bc9482878565d53b52aca683840755 Mon Sep 17 00:00:00 2001 From: Xuan <112967240+xuan-cao-swi@users.noreply.github.com> Date: Thu, 15 Aug 2024 18:55:42 -0400 Subject: [PATCH 12/20] feat: include backtrace first line for better debug info (#1674) * issue-1669 * update the regex * update the regex --------- Co-authored-by: Matthew Wear --- api/lib/opentelemetry.rb | 2 +- api/test/opentelemetry_test.rb | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/api/lib/opentelemetry.rb b/api/lib/opentelemetry.rb index a85edd1243..3234fd782a 100644 --- a/api/lib/opentelemetry.rb +++ b/api/lib/opentelemetry.rb @@ -34,7 +34,7 @@ def logger # @return [Callable] configured error handler or a default that logs the # exception and message at ERROR level. def error_handler - @error_handler ||= ->(exception: nil, message: nil) { logger.error("OpenTelemetry error: #{[message, exception&.message].compact.join(' - ')}") } + @error_handler ||= ->(exception: nil, message: nil) { logger.error("OpenTelemetry error: #{[message, exception&.message, exception&.backtrace&.first].compact.join(' - ')}") } end # Handles an error by calling the configured error_handler. diff --git a/api/test/opentelemetry_test.rb b/api/test/opentelemetry_test.rb index 891740ace6..b059365aea 100644 --- a/api/test/opentelemetry_test.rb +++ b/api/test/opentelemetry_test.rb @@ -96,7 +96,9 @@ def error(message) rescue StandardError => e OpenTelemetry.handle_error(exception: e, message: 'hi') end - _(OpenTelemetry.logger.messages).must_equal ['OpenTelemetry error: foo', 'OpenTelemetry error: hell', 'OpenTelemetry error: hi - bar'] + _(OpenTelemetry.logger.messages[0]).must_equal('OpenTelemetry error: foo') + _(OpenTelemetry.logger.messages[1]).must_match(%r{OpenTelemetry error: hell - .+/opentelemetry_test\.rb:\d+:in .+'}) + _(OpenTelemetry.logger.messages[2]).must_match(%r{OpenTelemetry error: hi - bar - .+/opentelemetry_test\.rb:\d+:in .+'}) end it 'calls user specified error handler' do From 62bb1507dd07ca20097e2b718debd9397b570c80 Mon Sep 17 00:00:00 2001 From: Xuan <112967240+xuan-cao-swi@users.noreply.github.com> Date: Wed, 21 Aug 2024 13:45:12 -0400 Subject: [PATCH 13/20] feat: add basic periodic exporting metric_reader (#1603) * feat: add basic periodic-reader * feat: lint * feat: make thread properly close * Update metrics_sdk/lib/opentelemetry/sdk/metrics/export/periodic_metric_reader.rb Co-authored-by: Kayla Reopelle (she/her) <87386821+kaylareopelle@users.noreply.github.com> * Update metrics_sdk/lib/opentelemetry/sdk/metrics/export/periodic_metric_reader.rb Co-authored-by: Kayla Reopelle (she/her) <87386821+kaylareopelle@users.noreply.github.com> * Update metrics_sdk/lib/opentelemetry/sdk/metrics/export/periodic_metric_reader.rb Co-authored-by: Kayla Reopelle (she/her) <87386821+kaylareopelle@users.noreply.github.com> * Update metrics_sdk/test/integration/periodic_metric_reader_test.rb Co-authored-by: Kayla Reopelle (she/her) <87386821+kaylareopelle@users.noreply.github.com> * feat: periodic reader - revision * feat: change interval and timeout name * feat: change back to millis * revision * lint --------- Co-authored-by: Kayla Reopelle (she/her) <87386821+kaylareopelle@users.noreply.github.com> Co-authored-by: Matthew Wear --- .../lib/opentelemetry/sdk/metrics/export.rb | 1 + .../export/in_memory_metric_pull_exporter.rb | 2 +- .../metrics/export/periodic_metric_reader.rb | 100 ++++++++++++++++++ .../periodic_metric_reader_test.rb | 85 +++++++++++++++ 4 files changed, 187 insertions(+), 1 deletion(-) create mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/export/periodic_metric_reader.rb create mode 100644 metrics_sdk/test/integration/periodic_metric_reader_test.rb diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/export.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/export.rb index 64a038f819..782a75aae4 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/export.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/export.rb @@ -26,3 +26,4 @@ module Export require 'opentelemetry/sdk/metrics/export/metric_reader' require 'opentelemetry/sdk/metrics/export/in_memory_metric_pull_exporter' require 'opentelemetry/sdk/metrics/export/console_metric_pull_exporter' +require 'opentelemetry/sdk/metrics/export/periodic_metric_reader' diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/export/in_memory_metric_pull_exporter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/export/in_memory_metric_pull_exporter.rb index 35c2dfe575..8291ed9fe7 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/export/in_memory_metric_pull_exporter.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/export/in_memory_metric_pull_exporter.rb @@ -23,7 +23,7 @@ def pull export(collect) end - def export(metrics) + def export(metrics, timeout: nil) @mutex.synchronize do @metric_snapshots << metrics end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/export/periodic_metric_reader.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/export/periodic_metric_reader.rb new file mode 100644 index 0000000000..948f98976c --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/export/periodic_metric_reader.rb @@ -0,0 +1,100 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Metrics + module Export + # PeriodicMetricReader provides a minimal example implementation. + class PeriodicMetricReader < MetricReader + # Returns a new instance of the {PeriodicMetricReader}. + # + # @param [Integer] export_interval_millis the maximum interval time. + # Defaults to the value of the OTEL_METRIC_EXPORT_INTERVAL environment + # variable, if set, or 60_000. + # @param [Integer] export_timeout_millis the maximum export timeout. + # Defaults to the value of the OTEL_METRIC_EXPORT_TIMEOUT environment + # variable, if set, or 30_000. + # @param [MetricReader] exporter the (duck type) MetricReader to where the + # recorded metrics are pushed after certain interval. + # + # @return a new instance of the {PeriodicMetricReader}. + def initialize(export_interval_millis: Float(ENV.fetch('OTEL_METRIC_EXPORT_INTERVAL', 60_000)), + export_timeout_millis: Float(ENV.fetch('OTEL_METRIC_EXPORT_TIMEOUT', 30_000)), + exporter: nil) + super() + + @export_interval = export_interval_millis / 1000.0 + @export_timeout = export_timeout_millis / 1000.0 + @exporter = exporter + @thread = nil + @continue = false + @mutex = Mutex.new + @export_mutex = Mutex.new + + start + end + + def shutdown(timeout: nil) + thread = lock do + @continue = false # force termination in next iteration + @thread + end + thread&.join(@export_interval) + @exporter.force_flush if @exporter.respond_to?(:force_flush) + @exporter.shutdown + Export::SUCCESS + rescue StandardError => e + OpenTelemetry.handle_error(exception: e, message: 'Fail to shutdown PeriodicMetricReader.') + Export::FAILURE + end + + def force_flush(timeout: nil) + export(timeout: timeout) + Export::SUCCESS + rescue StandardError + Export::FAILURE + end + + private + + def start + @continue = true + if @exporter.nil? + OpenTelemetry.logger.warn 'Missing exporter in PeriodicMetricReader.' + elsif @thread&.alive? + OpenTelemetry.logger.warn 'PeriodicMetricReader is still running. Please shutdown it if it needs to restart.' + else + @thread = Thread.new do + while @continue + sleep(@export_interval) + begin + Timeout.timeout(@export_timeout) do + export(timeout: @export_timeout) + end + rescue Timeout::Error => e + OpenTelemetry.handle_error(exception: e, message: 'PeriodicMetricReader timeout.') + end + end + end + end + end + + def export(timeout: nil) + @export_mutex.synchronize do + collected_metrics = collect + @exporter.export(collected_metrics, timeout: timeout || @export_timeout) unless collected_metrics.empty? + end + end + + def lock(&block) + @mutex.synchronize(&block) + end + end + end + end + end +end diff --git a/metrics_sdk/test/integration/periodic_metric_reader_test.rb b/metrics_sdk/test/integration/periodic_metric_reader_test.rb new file mode 100644 index 0000000000..7bf00a08fc --- /dev/null +++ b/metrics_sdk/test/integration/periodic_metric_reader_test.rb @@ -0,0 +1,85 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'test_helper' + +describe OpenTelemetry::SDK do + describe '#periodic_metric_reader' do + before { reset_metrics_sdk } + + it 'emits 2 metrics after 10 seconds' do + OpenTelemetry::SDK.configure + + metric_exporter = OpenTelemetry::SDK::Metrics::Export::InMemoryMetricPullExporter.new + periodic_metric_reader = OpenTelemetry::SDK::Metrics::Export::PeriodicMetricReader.new(export_interval_millis: 5000, export_timeout_millis: 5000, exporter: metric_exporter) + + OpenTelemetry.meter_provider.add_metric_reader(periodic_metric_reader) + + meter = OpenTelemetry.meter_provider.meter('test') + counter = meter.create_counter('counter', unit: 'smidgen', description: 'a small amount of something') + + counter.add(1) + counter.add(2, attributes: { 'a' => 'b' }) + counter.add(2, attributes: { 'a' => 'b' }) + counter.add(3, attributes: { 'b' => 'c' }) + counter.add(4, attributes: { 'd' => 'e' }) + + sleep(8) + + periodic_metric_reader.shutdown + snapshot = metric_exporter.metric_snapshots + + _(snapshot.size).must_equal(2) + + first_snapshot = snapshot[0] + _(first_snapshot[0].name).must_equal('counter') + _(first_snapshot[0].unit).must_equal('smidgen') + _(first_snapshot[0].description).must_equal('a small amount of something') + + _(first_snapshot[0].instrumentation_scope.name).must_equal('test') + + _(first_snapshot[0].data_points[0].value).must_equal(1) + _(first_snapshot[0].data_points[0].attributes).must_equal({}) + + _(first_snapshot[0].data_points[1].value).must_equal(4) + _(first_snapshot[0].data_points[1].attributes).must_equal('a' => 'b') + + _(first_snapshot[0].data_points[2].value).must_equal(3) + _(first_snapshot[0].data_points[2].attributes).must_equal('b' => 'c') + + _(first_snapshot[0].data_points[3].value).must_equal(4) + _(first_snapshot[0].data_points[3].attributes).must_equal('d' => 'e') + + _(periodic_metric_reader.instance_variable_get(:@thread).alive?).must_equal false + end + + it 'emits 1 metric after 1 second when interval is > 1 second' do + OpenTelemetry::SDK.configure + + metric_exporter = OpenTelemetry::SDK::Metrics::Export::InMemoryMetricPullExporter.new + periodic_metric_reader = OpenTelemetry::SDK::Metrics::Export::PeriodicMetricReader.new(export_interval_millis: 5000, export_timeout_millis: 5000, exporter: metric_exporter) + + OpenTelemetry.meter_provider.add_metric_reader(periodic_metric_reader) + + meter = OpenTelemetry.meter_provider.meter('test') + counter = meter.create_counter('counter', unit: 'smidgen', description: 'a small amount of something') + + counter.add(1) + counter.add(2, attributes: { 'a' => 'b' }) + counter.add(2, attributes: { 'a' => 'b' }) + counter.add(3, attributes: { 'b' => 'c' }) + counter.add(4, attributes: { 'd' => 'e' }) + + sleep(1) + + periodic_metric_reader.shutdown + snapshot = metric_exporter.metric_snapshots + + _(snapshot.size).must_equal(1) + _(periodic_metric_reader.instance_variable_get(:@thread).alive?).must_equal false + end + end +end From 2f87a1d1c90e51679838b36609e07cd3acdc9bcf Mon Sep 17 00:00:00 2001 From: Xuan <112967240+xuan-cao-swi@users.noreply.github.com> Date: Fri, 23 Aug 2024 16:52:31 -0400 Subject: [PATCH 14/20] feat: initialize metrics exporter http gem (#1670) * feat: initialize metrics exporter http gem * remove version_rb_path for metrics exporter in release * lint * chore: Update metrics exporter README This removes content related to installing the gem before the metrics API and SDK gems were released. * chore: Update MetricsExporter class in example Now, there's an extra Metrics:: namespace * chore: Update class for MetricsExporter * revision * add force_flush --------- Co-authored-by: Kayla Reopelle --- .toys/.data/releases.yml | 4 + examples/metrics_sdk/metrics_collect_otlp.rb | 2 +- exporter/otlp-metrics/README.md | 46 +-- .../exporter/otlp/metrics/metrics_exporter.rb | 311 ++++++++++++++++++ .../exporter/otlp/metrics/util.rb | 141 ++++++++ .../exporter/otlp/{ => metrics}/version.rb | 6 +- .../exporter/otlp/metrics_exporter.rb | 309 ----------------- .../lib/opentelemetry/exporter/otlp/util.rb | 139 -------- .../opentelemetry/exporter/otlp_metrics.rb | 4 +- ...pentelemetry-exporter-otlp-metrics.gemspec | 10 +- .../exporter/otlp/metrics_exporter_test.rb | 96 +++--- 11 files changed, 523 insertions(+), 545 deletions(-) create mode 100644 exporter/otlp-metrics/lib/opentelemetry/exporter/otlp/metrics/metrics_exporter.rb create mode 100644 exporter/otlp-metrics/lib/opentelemetry/exporter/otlp/metrics/util.rb rename exporter/otlp-metrics/lib/opentelemetry/exporter/otlp/{ => metrics}/version.rb (62%) delete mode 100644 exporter/otlp-metrics/lib/opentelemetry/exporter/otlp/metrics_exporter.rb delete mode 100644 exporter/otlp-metrics/lib/opentelemetry/exporter/otlp/util.rb diff --git a/.toys/.data/releases.yml b/.toys/.data/releases.yml index 95c53559b2..a63f0be44f 100644 --- a/.toys/.data/releases.yml +++ b/.toys/.data/releases.yml @@ -63,6 +63,10 @@ gems: directory: exporter/otlp version_constant: [OpenTelemetry, Exporter, OTLP, VERSION] + - name: opentelemetry-exporter-otlp-metrics + directory: exporter/otlp-metrics + version_constant: [OpenTelemetry, Exporter, OTLP, Metrics, VERSION] + - name: opentelemetry-exporter-zipkin directory: exporter/zipkin version_constant: [OpenTelemetry, Exporter, Zipkin, VERSION] diff --git a/examples/metrics_sdk/metrics_collect_otlp.rb b/examples/metrics_sdk/metrics_collect_otlp.rb index d8b727a863..355c42d655 100644 --- a/examples/metrics_sdk/metrics_collect_otlp.rb +++ b/examples/metrics_sdk/metrics_collect_otlp.rb @@ -20,7 +20,7 @@ OpenTelemetry::SDK.configure -otlp_metric_exporter = OpenTelemetry::Exporter::OTLP::MetricsExporter.new +otlp_metric_exporter = OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new OpenTelemetry.meter_provider.add_metric_reader(otlp_metric_exporter) diff --git a/exporter/otlp-metrics/README.md b/exporter/otlp-metrics/README.md index 559cfe5285..3bbca793dd 100644 --- a/exporter/otlp-metrics/README.md +++ b/exporter/otlp-metrics/README.md @@ -18,41 +18,6 @@ Generally, *libraries* that produce telemetry data should avoid depending direct This gem supports the [v0.20.0 release][otel-proto-release] of OTLP. -## Prerequisite - -The exporter-oltp-metrics depends on two gems that have not been officially released: opentelemetry-metrics-sdk and opentelemetry-metrics-api. - -Within the .gemspec file, these gems are not listed as dependencies. However, for users who need utilize this metrics exporter, they must first install and load these two gems before they can use the exporter. - -To facilitate this, there are couple recommended approaches: - -#### 1. Download the source code - -1. Download the [opentelemetry-ruby](https://github.com/open-telemetry/opentelemetry-ruby). -2. Navigate to subfolder, then build the [metrics_sdk](https://github.com/open-telemetry/opentelemetry-ruby/tree/main/metrics_sdk) and [metrics_api](https://github.com/open-telemetry/opentelemetry-ruby/tree/main/metrics_api). -3. Execute `gem build *.gemspec`. -4. Lastly, install the built gem into the system. - -#### 2. Using `path:` option in Gemfile with downloaded source code - -git clone [opentelemetry-ruby](https://github.com/open-telemetry/opentelemetry-ruby) first, then use Gemfile - -```ruby -# Gemfile -source 'https://rubygems.org' -gem 'opentelemetry-metrics-api', path: "opentelemetry-ruby/metrics_api" -gem 'opentelemetry-metrics-sdk', path: "opentelemetry-ruby/metrics_sdk" -``` - -#### 3. Using `git:` option in Gemfile - -```ruby -# Gemfile -source 'https://rubygems.org' -gem 'opentelemetry-metrics-api', git: "https://github.com/open-telemetry/opentelemetry-ruby", glob: 'metrics_api/*.gemspec' -gem 'opentelemetry-metrics-sdk', git: "https://github.com/open-telemetry/opentelemetry-ruby", glob: 'metrics_sdk/*.gemspec' -``` - ## How do I get started? Install the gem using: @@ -60,13 +25,14 @@ Install the gem using: ```console gem install opentelemetry-sdk +gem install opentelemetry-metrics-sdk gem install opentelemetry-exporter-otlp-metrics ``` -Or, if you use [bundler][bundler-home], include `opentelemetry-sdk` in your `Gemfile`. +Or, if you use [bundler][bundler-home], include `opentelemetry-sdk`, `opentelemetry-metrics-sdk`, and `opentelemetry-exporter-otlp-metrics` in your `Gemfile`. -Then, configure the SDK to use the OTLP metrics exporter +Then, configure the SDK to use the OTLP metrics exporter ```ruby require 'opentelemetry/sdk' @@ -77,7 +43,7 @@ OpenTelemetry::SDK.configure # To start a trace you need to get a Tracer from the TracerProvider -otlp_metric_exporter = OpenTelemetry::Exporter::OTLP::MetricsExporter.new +otlp_metric_exporter = OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new OpenTelemetry.meter_provider.add_metric_reader(otlp_metric_exporter) @@ -112,7 +78,7 @@ The collector exporter can be configured explicitly in code, or via environment ## How can I get involved? -The `opentelemetry-exporter-otlp-metrics` gem source is [on github][repo-github], along with related gems including `opentelemetry-sdk`. +The `opentelemetry-exporter-otlp-metrics` gem source is [on github][repo-github], along with related gems including `opentelemetry-metrics-sdk`. The OpenTelemetry Ruby gems are maintained by the OpenTelemetry-Ruby special interest group (SIG). You can get involved by joining us in [GitHub Discussions][discussions-url] or attending our weekly meeting. See the [meeting calendar][community-meetings] for dates and times. For more information on this and other language SIGs, see the OpenTelemetry [community page][ruby-sig]. @@ -176,4 +142,4 @@ $> bundle exec rake test [protoc-install]: https://github.com/protocolbuffers/protobuf/releases/tag/v22.5 [ruby-downloads]: https://www.ruby-lang.org/en/downloads/ [otel-proto-github]: https://github.com/open-telemetry/opentelemetry-proto -[otel-proto-release]: https://github.com/open-telemetry/opentelemetry-proto/releases/tag/v0.20.0 \ No newline at end of file +[otel-proto-release]: https://github.com/open-telemetry/opentelemetry-proto/releases/tag/v0.20.0 diff --git a/exporter/otlp-metrics/lib/opentelemetry/exporter/otlp/metrics/metrics_exporter.rb b/exporter/otlp-metrics/lib/opentelemetry/exporter/otlp/metrics/metrics_exporter.rb new file mode 100644 index 0000000000..be21d5dde2 --- /dev/null +++ b/exporter/otlp-metrics/lib/opentelemetry/exporter/otlp/metrics/metrics_exporter.rb @@ -0,0 +1,311 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'opentelemetry/common' +require 'opentelemetry/sdk' +require 'net/http' +require 'zlib' + +require 'google/rpc/status_pb' + +require 'opentelemetry/proto/common/v1/common_pb' +require 'opentelemetry/proto/resource/v1/resource_pb' +require 'opentelemetry/proto/metrics/v1/metrics_pb' +require 'opentelemetry/proto/collector/metrics/v1/metrics_service_pb' + +require 'opentelemetry/metrics' +require 'opentelemetry/sdk/metrics' + +require_relative './util' + +module OpenTelemetry + module Exporter + module OTLP + module Metrics + # An OpenTelemetry metrics exporter that sends metrics over HTTP as Protobuf encoded OTLP ExportMetricsServiceRequest. + class MetricsExporter < ::OpenTelemetry::SDK::Metrics::Export::MetricReader # rubocop:disable Metrics/ClassLength + include Util + + attr_reader :metric_snapshots + + SUCCESS = OpenTelemetry::SDK::Metrics::Export::SUCCESS + FAILURE = OpenTelemetry::SDK::Metrics::Export::FAILURE + private_constant(:SUCCESS, :FAILURE) + + def self.ssl_verify_mode + if ENV.key?('OTEL_RUBY_EXPORTER_OTLP_SSL_VERIFY_PEER') + OpenSSL::SSL::VERIFY_PEER + elsif ENV.key?('OTEL_RUBY_EXPORTER_OTLP_SSL_VERIFY_NONE') + OpenSSL::SSL::VERIFY_NONE + else + OpenSSL::SSL::VERIFY_PEER + end + end + + def initialize(endpoint: OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_METRICS_ENDPOINT', 'OTEL_EXPORTER_OTLP_ENDPOINT', default: 'http://localhost:4318/v1/metrics'), + certificate_file: OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE', 'OTEL_EXPORTER_OTLP_CERTIFICATE'), + ssl_verify_mode: MetricsExporter.ssl_verify_mode, + headers: OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_METRICS_HEADERS', 'OTEL_EXPORTER_OTLP_HEADERS', default: {}), + compression: OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_METRICS_COMPRESSION', 'OTEL_EXPORTER_OTLP_COMPRESSION', default: 'gzip'), + timeout: OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_METRICS_TIMEOUT', 'OTEL_EXPORTER_OTLP_TIMEOUT', default: 10)) + raise ArgumentError, "invalid url for OTLP::MetricsExporter #{endpoint}" unless OpenTelemetry::Common::Utilities.valid_url?(endpoint) + raise ArgumentError, "unsupported compression key #{compression}" unless compression.nil? || %w[gzip none].include?(compression) + + # create the MetricStore object + super() + + @uri = if endpoint == ENV['OTEL_EXPORTER_OTLP_ENDPOINT'] + URI.join(endpoint, 'v1/metrics') + else + URI(endpoint) + end + + @http = http_connection(@uri, ssl_verify_mode, certificate_file) + + @path = @uri.path + @headers = prepare_headers(headers) + @timeout = timeout.to_f + @compression = compression + @mutex = Mutex.new + @shutdown = false + end + + # consolidate the metrics data into the form of MetricData + # + # return MetricData + def pull + export(collect) + end + + # metrics Array[MetricData] + def export(metrics, timeout: nil) + @mutex.synchronize do + send_bytes(encode(metrics), timeout: timeout) + end + end + + def send_bytes(bytes, timeout:) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity + return FAILURE if bytes.nil? + + request = Net::HTTP::Post.new(@path) + + if @compression == 'gzip' + request.add_field('Content-Encoding', 'gzip') + body = Zlib.gzip(bytes) + else + body = bytes + end + + request.body = body + request.add_field('Content-Type', 'application/x-protobuf') + @headers.each { |key, value| request.add_field(key, value) } + + retry_count = 0 + timeout ||= @timeout + start_time = OpenTelemetry::Common::Utilities.timeout_timestamp + + around_request do + remaining_timeout = OpenTelemetry::Common::Utilities.maybe_timeout(timeout, start_time) + return FAILURE if remaining_timeout.zero? + + @http.open_timeout = remaining_timeout + @http.read_timeout = remaining_timeout + @http.write_timeout = remaining_timeout + @http.start unless @http.started? + response = measure_request_duration { @http.request(request) } + case response + when Net::HTTPOK + response.body # Read and discard body + SUCCESS + when Net::HTTPServiceUnavailable, Net::HTTPTooManyRequests + response.body # Read and discard body + redo if backoff?(retry_after: response['Retry-After'], retry_count: retry_count += 1, reason: response.code) + OpenTelemetry.logger.warn('Net::HTTPServiceUnavailable/Net::HTTPTooManyRequests in MetricsExporter#send_bytes') + FAILURE + when Net::HTTPRequestTimeOut, Net::HTTPGatewayTimeOut, Net::HTTPBadGateway + response.body # Read and discard body + redo if backoff?(retry_count: retry_count += 1, reason: response.code) + OpenTelemetry.logger.warn('Net::HTTPRequestTimeOut/Net::HTTPGatewayTimeOut/Net::HTTPBadGateway in MetricsExporter#send_bytes') + FAILURE + when Net::HTTPNotFound + OpenTelemetry.handle_error(message: "OTLP metrics_exporter received http.code=404 for uri: '#{@path}'") + FAILURE + when Net::HTTPBadRequest, Net::HTTPClientError, Net::HTTPServerError + log_status(response.body) + OpenTelemetry.logger.warn('Net::HTTPBadRequest/Net::HTTPClientError/Net::HTTPServerError in MetricsExporter#send_bytes') + FAILURE + when Net::HTTPRedirection + @http.finish + handle_redirect(response['location']) + redo if backoff?(retry_after: 0, retry_count: retry_count += 1, reason: response.code) + else + @http.finish + OpenTelemetry.logger.warn("Unexpected error in OTLP::MetricsExporter#send_bytes - #{response.message}") + FAILURE + end + rescue Net::OpenTimeout, Net::ReadTimeout + retry if backoff?(retry_count: retry_count += 1, reason: 'timeout') + OpenTelemetry.logger.warn('Net::OpenTimeout/Net::ReadTimeout in MetricsExporter#send_bytes') + return FAILURE + rescue OpenSSL::SSL::SSLError + retry if backoff?(retry_count: retry_count += 1, reason: 'openssl_error') + OpenTelemetry.logger.warn('OpenSSL::SSL::SSLError in MetricsExporter#send_bytes') + return FAILURE + rescue SocketError + retry if backoff?(retry_count: retry_count += 1, reason: 'socket_error') + OpenTelemetry.logger.warn('SocketError in MetricsExporter#send_bytes') + return FAILURE + rescue SystemCallError => e + retry if backoff?(retry_count: retry_count += 1, reason: e.class.name) + OpenTelemetry.logger.warn('SystemCallError in MetricsExporter#send_bytes') + return FAILURE + rescue EOFError + retry if backoff?(retry_count: retry_count += 1, reason: 'eof_error') + OpenTelemetry.logger.warn('EOFError in MetricsExporter#send_bytes') + return FAILURE + rescue Zlib::DataError + retry if backoff?(retry_count: retry_count += 1, reason: 'zlib_error') + OpenTelemetry.logger.warn('Zlib::DataError in MetricsExporter#send_bytes') + return FAILURE + rescue StandardError => e + OpenTelemetry.handle_error(exception: e, message: 'unexpected error in OTLP::MetricsExporter#send_bytes') + return FAILURE + end + ensure + # Reset timeouts to defaults for the next call. + @http.open_timeout = @timeout + @http.read_timeout = @timeout + @http.write_timeout = @timeout + end + + def encode(metrics_data) # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity + Opentelemetry::Proto::Collector::Metrics::V1::ExportMetricsServiceRequest.encode( + Opentelemetry::Proto::Collector::Metrics::V1::ExportMetricsServiceRequest.new( + resource_metrics: metrics_data + .group_by(&:resource) + .map do |resource, scope_metrics| + Opentelemetry::Proto::Metrics::V1::ResourceMetrics.new( + resource: Opentelemetry::Proto::Resource::V1::Resource.new( + attributes: resource.attribute_enumerator.map { |key, value| as_otlp_key_value(key, value) } + ), + scope_metrics: scope_metrics + .group_by(&:instrumentation_scope) + .map do |instrumentation_scope, metrics| + Opentelemetry::Proto::Metrics::V1::ScopeMetrics.new( + scope: Opentelemetry::Proto::Common::V1::InstrumentationScope.new( + name: instrumentation_scope.name, + version: instrumentation_scope.version + ), + metrics: metrics.map { |sd| as_otlp_metrics(sd) } + ) + end + ) + end + ) + ) + rescue StandardError => e + OpenTelemetry.handle_error(exception: e, message: 'unexpected error in OTLP::MetricsExporter#encode') + nil + end + + # metrics_pb has following type of data: :gauge, :sum, :histogram, :exponential_histogram, :summary + # current metric sdk only implements instrument: :counter -> :sum, :histogram -> :histogram + # + # metrics [MetricData] + def as_otlp_metrics(metrics) # rubocop:disable Metrics/MethodLength + case metrics.instrument_kind + when :observable_gauge + Opentelemetry::Proto::Metrics::V1::Metric.new( + name: metrics.name, + description: metrics.description, + unit: metrics.unit, + gauge: Opentelemetry::Proto::Metrics::V1::Gauge.new( + aggregation_temporality: as_otlp_aggregation_temporality(metrics.aggregation_temporality), + data_points: metrics.data_points.map do |ndp| + number_data_point(ndp) + end + ) + ) + + when :counter, :up_down_counter + Opentelemetry::Proto::Metrics::V1::Metric.new( + name: metrics.name, + description: metrics.description, + unit: metrics.unit, + sum: Opentelemetry::Proto::Metrics::V1::Sum.new( + aggregation_temporality: as_otlp_aggregation_temporality(metrics.aggregation_temporality), + data_points: metrics.data_points.map do |ndp| + number_data_point(ndp) + end + ) + ) + + when :histogram + Opentelemetry::Proto::Metrics::V1::Metric.new( + name: metrics.name, + description: metrics.description, + unit: metrics.unit, + histogram: Opentelemetry::Proto::Metrics::V1::Histogram.new( + aggregation_temporality: as_otlp_aggregation_temporality(metrics.aggregation_temporality), + data_points: metrics.data_points.map do |hdp| + histogram_data_point(hdp) + end + ) + ) + end + end + + def as_otlp_aggregation_temporality(type) + case type + when :delta then Opentelemetry::Proto::Metrics::V1::AggregationTemporality::AGGREGATION_TEMPORALITY_DELTA + when :cumulative then Opentelemetry::Proto::Metrics::V1::AggregationTemporality::AGGREGATION_TEMPORALITY_CUMULATIVE + else Opentelemetry::Proto::Metrics::V1::AggregationTemporality::AGGREGATION_TEMPORALITY_UNSPECIFIED + end + end + + def histogram_data_point(hdp) + Opentelemetry::Proto::Metrics::V1::HistogramDataPoint.new( + attributes: hdp.attributes.map { |k, v| as_otlp_key_value(k, v) }, + start_time_unix_nano: hdp.start_time_unix_nano, + time_unix_nano: hdp.time_unix_nano, + count: hdp.count, + sum: hdp.sum, + bucket_counts: hdp.bucket_counts, + explicit_bounds: hdp.explicit_bounds, + exemplars: hdp.exemplars, + min: hdp.min, + max: hdp.max + ) + end + + def number_data_point(ndp) + Opentelemetry::Proto::Metrics::V1::NumberDataPoint.new( + attributes: ndp.attributes.map { |k, v| as_otlp_key_value(k, v) }, + as_int: ndp.value, + start_time_unix_nano: ndp.start_time_unix_nano, + time_unix_nano: ndp.time_unix_nano, + exemplars: ndp.exemplars # exemplars not implemented yet from metrics sdk + ) + end + + # may not need this + def reset + SUCCESS + end + + def force_flush(timeout: nil) + SUCCESS + end + + def shutdown(timeout: nil) + @shutdown = true + SUCCESS + end + end + end + end + end +end diff --git a/exporter/otlp-metrics/lib/opentelemetry/exporter/otlp/metrics/util.rb b/exporter/otlp-metrics/lib/opentelemetry/exporter/otlp/metrics/util.rb new file mode 100644 index 0000000000..568c9a0e48 --- /dev/null +++ b/exporter/otlp-metrics/lib/opentelemetry/exporter/otlp/metrics/util.rb @@ -0,0 +1,141 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module Exporter + module OTLP + module Metrics + # Util module provide essential functionality for exporter + module Util # rubocop:disable Metrics/ModuleLength + KEEP_ALIVE_TIMEOUT = 30 + RETRY_COUNT = 5 + ERROR_MESSAGE_INVALID_HEADERS = 'headers must be a String with comma-separated URL Encoded UTF-8 k=v pairs or a Hash' + DEFAULT_USER_AGENT = "OTel-OTLP-MetricsExporter-Ruby/#{OpenTelemetry::Exporter::OTLP::Metrics::VERSION} Ruby/#{RUBY_VERSION} (#{RUBY_PLATFORM}; #{RUBY_ENGINE}/#{RUBY_ENGINE_VERSION})".freeze + + def http_connection(uri, ssl_verify_mode, certificate_file) + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = uri.scheme == 'https' + http.verify_mode = ssl_verify_mode + http.ca_file = certificate_file unless certificate_file.nil? + http.keep_alive_timeout = KEEP_ALIVE_TIMEOUT + http + end + + def around_request + OpenTelemetry::Common::Utilities.untraced { yield } # rubocop:disable Style/ExplicitBlockArgument + end + + def as_otlp_key_value(key, value) + Opentelemetry::Proto::Common::V1::KeyValue.new(key: key, value: as_otlp_any_value(value)) + rescue Encoding::UndefinedConversionError => e + encoded_value = value.encode('UTF-8', invalid: :replace, undef: :replace, replace: '�') + OpenTelemetry.handle_error(exception: e, message: "encoding error for key #{key} and value #{encoded_value}") + Opentelemetry::Proto::Common::V1::KeyValue.new(key: key, value: as_otlp_any_value('Encoding Error')) + end + + def as_otlp_any_value(value) + result = Opentelemetry::Proto::Common::V1::AnyValue.new + case value + when String + result.string_value = value + when Integer + result.int_value = value + when Float + result.double_value = value + when true, false + result.bool_value = value + when Array + values = value.map { |element| as_otlp_any_value(element) } + result.array_value = Opentelemetry::Proto::Common::V1::ArrayValue.new(values: values) + end + result + end + + def prepare_headers(config_headers) + headers = case config_headers + when String then parse_headers(config_headers) + when Hash then config_headers.dup + else + raise ArgumentError, ERROR_MESSAGE_INVALID_HEADERS + end + + headers['User-Agent'] = "#{headers.fetch('User-Agent', '')} #{DEFAULT_USER_AGENT}".strip + + headers + end + + def measure_request_duration + start = Process.clock_gettime(Process::CLOCK_MONOTONIC) + begin + yield + ensure + stop = Process.clock_gettime(Process::CLOCK_MONOTONIC) + 1000.0 * (stop - start) + end + end + + def parse_headers(raw) + entries = raw.split(',') + raise ArgumentError, ERROR_MESSAGE_INVALID_HEADERS if entries.empty? + + entries.each_with_object({}) do |entry, headers| + k, v = entry.split('=', 2).map(&CGI.method(:unescape)) + begin + k = k.to_s.strip + v = v.to_s.strip + rescue Encoding::CompatibilityError + raise ArgumentError, ERROR_MESSAGE_INVALID_HEADERS + rescue ArgumentError => e + raise e, ERROR_MESSAGE_INVALID_HEADERS + end + raise ArgumentError, ERROR_MESSAGE_INVALID_HEADERS if k.empty? || v.empty? + + headers[k] = v + end + end + + def backoff?(retry_count:, reason:, retry_after: nil) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity + return false if retry_count > RETRY_COUNT + + sleep_interval = nil + unless retry_after.nil? + sleep_interval = + begin + Integer(retry_after) + rescue ArgumentError + nil + end + sleep_interval ||= + begin + Time.httpdate(retry_after) - Time.now + rescue # rubocop:disable Style/RescueStandardError + nil + end + sleep_interval = nil unless sleep_interval&.positive? + end + sleep_interval ||= rand(2**retry_count) + + sleep(sleep_interval) + true + end + + def log_status(body) + status = Google::Rpc::Status.decode(body) + details = status.details.map do |detail| + klass_or_nil = ::Google::Protobuf::DescriptorPool.generated_pool.lookup(detail.type_name).msgclass + detail.unpack(klass_or_nil) if klass_or_nil + end.compact + OpenTelemetry.handle_error(message: "OTLP metrics_exporter received rpc.Status{message=#{status.message}, details=#{details}}") + rescue StandardError => e + OpenTelemetry.handle_error(exception: e, message: 'unexpected error decoding rpc.Status in OTLP::MetricsExporter#log_status') + end + + def handle_redirect(location); end + end + end + end + end +end diff --git a/exporter/otlp-metrics/lib/opentelemetry/exporter/otlp/version.rb b/exporter/otlp-metrics/lib/opentelemetry/exporter/otlp/metrics/version.rb similarity index 62% rename from exporter/otlp-metrics/lib/opentelemetry/exporter/otlp/version.rb rename to exporter/otlp-metrics/lib/opentelemetry/exporter/otlp/metrics/version.rb index a851733507..2d326a3580 100644 --- a/exporter/otlp-metrics/lib/opentelemetry/exporter/otlp/version.rb +++ b/exporter/otlp-metrics/lib/opentelemetry/exporter/otlp/metrics/version.rb @@ -7,8 +7,10 @@ module OpenTelemetry module Exporter module OTLP - ## Current OpenTelemetry OTLP exporter version - VERSION = '0.0.1' + module Metrics + ## Current OpenTelemetry OTLP exporter version + VERSION = '0.1.0' + end end end end diff --git a/exporter/otlp-metrics/lib/opentelemetry/exporter/otlp/metrics_exporter.rb b/exporter/otlp-metrics/lib/opentelemetry/exporter/otlp/metrics_exporter.rb deleted file mode 100644 index 36e70274ab..0000000000 --- a/exporter/otlp-metrics/lib/opentelemetry/exporter/otlp/metrics_exporter.rb +++ /dev/null @@ -1,309 +0,0 @@ -# frozen_string_literal: true - -# Copyright The OpenTelemetry Authors -# -# SPDX-License-Identifier: Apache-2.0 - -require 'opentelemetry/common' -require 'opentelemetry/sdk' -require 'net/http' -require 'csv' -require 'zlib' - -require 'google/rpc/status_pb' - -require 'opentelemetry/proto/common/v1/common_pb' -require 'opentelemetry/proto/resource/v1/resource_pb' -require 'opentelemetry/proto/metrics/v1/metrics_pb' -require 'opentelemetry/proto/collector/metrics/v1/metrics_service_pb' - -require 'opentelemetry/metrics' -require 'opentelemetry/sdk/metrics' - -require_relative './util' - -module OpenTelemetry - module Exporter - module OTLP - # An OpenTelemetry metrics exporter that sends metrics over HTTP as Protobuf encoded OTLP ExportMetricsServiceRequest. - class MetricsExporter < ::OpenTelemetry::SDK::Metrics::Export::MetricReader # rubocop:disable Metrics/ClassLength - include Util - - attr_reader :metric_snapshots - - SUCCESS = OpenTelemetry::SDK::Metrics::Export::SUCCESS - FAILURE = OpenTelemetry::SDK::Metrics::Export::FAILURE - private_constant(:SUCCESS, :FAILURE) - - WRITE_TIMEOUT_SUPPORTED = Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.6') - private_constant(:WRITE_TIMEOUT_SUPPORTED) - - def self.ssl_verify_mode - if ENV.key?('OTEL_RUBY_EXPORTER_OTLP_SSL_VERIFY_PEER') - OpenSSL::SSL::VERIFY_PEER - elsif ENV.key?('OTEL_RUBY_EXPORTER_OTLP_SSL_VERIFY_NONE') - OpenSSL::SSL::VERIFY_NONE - else - OpenSSL::SSL::VERIFY_PEER - end - end - - def initialize(endpoint: OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_METRICS_ENDPOINT', 'OTEL_EXPORTER_OTLP_ENDPOINT', default: 'http://localhost:4318/v1/metrics'), - certificate_file: OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE', 'OTEL_EXPORTER_OTLP_CERTIFICATE'), - ssl_verify_mode: MetricsExporter.ssl_verify_mode, - headers: OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_METRICS_HEADERS', 'OTEL_EXPORTER_OTLP_HEADERS', default: {}), - compression: OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_METRICS_COMPRESSION', 'OTEL_EXPORTER_OTLP_COMPRESSION', default: 'gzip'), - timeout: OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_METRICS_TIMEOUT', 'OTEL_EXPORTER_OTLP_TIMEOUT', default: 10)) - raise ArgumentError, "invalid url for OTLP::MetricsExporter #{endpoint}" unless OpenTelemetry::Common::Utilities.valid_url?(endpoint) - raise ArgumentError, "unsupported compression key #{compression}" unless compression.nil? || %w[gzip none].include?(compression) - - # create the MetricStore object - super() - - @uri = if endpoint == ENV['OTEL_EXPORTER_OTLP_ENDPOINT'] - URI.join(endpoint, 'v1/metrics') - else - URI(endpoint) - end - - @http = http_connection(@uri, ssl_verify_mode, certificate_file) - - @path = @uri.path - @headers = prepare_headers(headers) - @timeout = timeout.to_f - @compression = compression - @mutex = Mutex.new - @shutdown = false - end - - # consolidate the metrics data into the form of MetricData - # - # return MetricData - def pull - export(collect) - end - - # metrics Array[MetricData] - def export(metrics, timeout: nil) - @mutex.synchronize do - send_bytes(encode(metrics), timeout: timeout) - end - end - - def send_bytes(bytes, timeout:) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity - return FAILURE if bytes.nil? - - request = Net::HTTP::Post.new(@path) - - if @compression == 'gzip' - request.add_field('Content-Encoding', 'gzip') - body = Zlib.gzip(bytes) - else - body = bytes - end - - request.body = body - request.add_field('Content-Type', 'application/x-protobuf') - @headers.each { |key, value| request.add_field(key, value) } - - retry_count = 0 - timeout ||= @timeout - start_time = OpenTelemetry::Common::Utilities.timeout_timestamp - - around_request do - remaining_timeout = OpenTelemetry::Common::Utilities.maybe_timeout(timeout, start_time) - return FAILURE if remaining_timeout.zero? - - @http.open_timeout = remaining_timeout - @http.read_timeout = remaining_timeout - @http.write_timeout = remaining_timeout if WRITE_TIMEOUT_SUPPORTED - @http.start unless @http.started? - response = measure_request_duration { @http.request(request) } - case response - when Net::HTTPOK - response.body # Read and discard body - SUCCESS - when Net::HTTPServiceUnavailable, Net::HTTPTooManyRequests - response.body # Read and discard body - redo if backoff?(retry_after: response['Retry-After'], retry_count: retry_count += 1, reason: response.code) - OpenTelemetry.logger.warn('Net::HTTPServiceUnavailable/Net::HTTPTooManyRequests in MetricsExporter#send_bytes') - FAILURE - when Net::HTTPRequestTimeOut, Net::HTTPGatewayTimeOut, Net::HTTPBadGateway - response.body # Read and discard body - redo if backoff?(retry_count: retry_count += 1, reason: response.code) - OpenTelemetry.logger.warn('Net::HTTPRequestTimeOut/Net::HTTPGatewayTimeOut/Net::HTTPBadGateway in MetricsExporter#send_bytes') - FAILURE - when Net::HTTPNotFound - OpenTelemetry.handle_error(message: "OTLP metrics_exporter received http.code=404 for uri: '#{@path}'") - FAILURE - when Net::HTTPBadRequest, Net::HTTPClientError, Net::HTTPServerError - log_status(response.body) - OpenTelemetry.logger.warn('Net::HTTPBadRequest/Net::HTTPClientError/Net::HTTPServerError in MetricsExporter#send_bytes') - FAILURE - when Net::HTTPRedirection - @http.finish - handle_redirect(response['location']) - redo if backoff?(retry_after: 0, retry_count: retry_count += 1, reason: response.code) - else - @http.finish - OpenTelemetry.logger.warn("Unexpected error in OTLP::MetricsExporter#send_bytes - #{response.message}") - FAILURE - end - rescue Net::OpenTimeout, Net::ReadTimeout - retry if backoff?(retry_count: retry_count += 1, reason: 'timeout') - OpenTelemetry.logger.warn('Net::OpenTimeout/Net::ReadTimeout in MetricsExporter#send_bytes') - return FAILURE - rescue OpenSSL::SSL::SSLError - retry if backoff?(retry_count: retry_count += 1, reason: 'openssl_error') - OpenTelemetry.logger.warn('OpenSSL::SSL::SSLError in MetricsExporter#send_bytes') - return FAILURE - rescue SocketError - retry if backoff?(retry_count: retry_count += 1, reason: 'socket_error') - OpenTelemetry.logger.warn('SocketError in MetricsExporter#send_bytes') - return FAILURE - rescue SystemCallError => e - retry if backoff?(retry_count: retry_count += 1, reason: e.class.name) - OpenTelemetry.logger.warn('SystemCallError in MetricsExporter#send_bytes') - return FAILURE - rescue EOFError - retry if backoff?(retry_count: retry_count += 1, reason: 'eof_error') - OpenTelemetry.logger.warn('EOFError in MetricsExporter#send_bytes') - return FAILURE - rescue Zlib::DataError - retry if backoff?(retry_count: retry_count += 1, reason: 'zlib_error') - OpenTelemetry.logger.warn('Zlib::DataError in MetricsExporter#send_bytes') - return FAILURE - rescue StandardError => e - OpenTelemetry.handle_error(exception: e, message: 'unexpected error in OTLP::MetricsExporter#send_bytes') - return FAILURE - end - ensure - # Reset timeouts to defaults for the next call. - @http.open_timeout = @timeout - @http.read_timeout = @timeout - @http.write_timeout = @timeout if WRITE_TIMEOUT_SUPPORTED - end - - def encode(metrics_data) # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity - Opentelemetry::Proto::Collector::Metrics::V1::ExportMetricsServiceRequest.encode( - Opentelemetry::Proto::Collector::Metrics::V1::ExportMetricsServiceRequest.new( - resource_metrics: metrics_data - .group_by(&:resource) - .map do |resource, scope_metrics| - Opentelemetry::Proto::Metrics::V1::ResourceMetrics.new( - resource: Opentelemetry::Proto::Resource::V1::Resource.new( - attributes: resource.attribute_enumerator.map { |key, value| as_otlp_key_value(key, value) } - ), - scope_metrics: scope_metrics - .group_by(&:instrumentation_scope) - .map do |instrumentation_scope, metrics| - Opentelemetry::Proto::Metrics::V1::ScopeMetrics.new( - scope: Opentelemetry::Proto::Common::V1::InstrumentationScope.new( - name: instrumentation_scope.name, - version: instrumentation_scope.version - ), - metrics: metrics.map { |sd| as_otlp_metrics(sd) } - ) - end - ) - end - ) - ) - rescue StandardError => e - OpenTelemetry.handle_error(exception: e, message: 'unexpected error in OTLP::MetricsExporter#encode') - nil - end - - # metrics_pb has following type of data: :gauge, :sum, :histogram, :exponential_histogram, :summary - # current metric sdk only implements instrument: :counter -> :sum, :histogram -> :histogram - # - # metrics [MetricData] - def as_otlp_metrics(metrics) # rubocop:disable Metrics/MethodLength - case metrics.instrument_kind - when :observable_gauge - Opentelemetry::Proto::Metrics::V1::Metric.new( - name: metrics.name, - description: metrics.description, - unit: metrics.unit, - gauge: Opentelemetry::Proto::Metrics::V1::Gauge.new( - aggregation_temporality: as_otlp_aggregation_temporality(metrics.aggregation_temporality), - data_points: metrics.data_points.map do |ndp| - number_data_point(ndp) - end - ) - ) - - when :counter, :up_down_counter - Opentelemetry::Proto::Metrics::V1::Metric.new( - name: metrics.name, - description: metrics.description, - unit: metrics.unit, - sum: Opentelemetry::Proto::Metrics::V1::Sum.new( - aggregation_temporality: as_otlp_aggregation_temporality(metrics.aggregation_temporality), - data_points: metrics.data_points.map do |ndp| - number_data_point(ndp) - end - ) - ) - - when :histogram - Opentelemetry::Proto::Metrics::V1::Metric.new( - name: metrics.name, - description: metrics.description, - unit: metrics.unit, - histogram: Opentelemetry::Proto::Metrics::V1::Histogram.new( - aggregation_temporality: as_otlp_aggregation_temporality(metrics.aggregation_temporality), - data_points: metrics.data_points.map do |hdp| - histogram_data_point(hdp) - end - ) - ) - end - end - - def as_otlp_aggregation_temporality(type) - case type - when :delta then Opentelemetry::Proto::Metrics::V1::AggregationTemporality::AGGREGATION_TEMPORALITY_DELTA - when :cumulative then Opentelemetry::Proto::Metrics::V1::AggregationTemporality::AGGREGATION_TEMPORALITY_CUMULATIVE - else Opentelemetry::Proto::Metrics::V1::AggregationTemporality::AGGREGATION_TEMPORALITY_UNSPECIFIED - end - end - - def histogram_data_point(hdp) - Opentelemetry::Proto::Metrics::V1::HistogramDataPoint.new( - attributes: hdp.attributes.map { |k, v| as_otlp_key_value(k, v) }, - start_time_unix_nano: hdp.start_time_unix_nano, - time_unix_nano: hdp.time_unix_nano, - count: hdp.count, - sum: hdp.sum, - bucket_counts: hdp.bucket_counts, - explicit_bounds: hdp.explicit_bounds, - exemplars: hdp.exemplars, - min: hdp.min, - max: hdp.max - ) - end - - def number_data_point(ndp) - Opentelemetry::Proto::Metrics::V1::NumberDataPoint.new( - attributes: ndp.attributes.map { |k, v| as_otlp_key_value(k, v) }, - as_int: ndp.value, - start_time_unix_nano: ndp.start_time_unix_nano, - time_unix_nano: ndp.time_unix_nano, - exemplars: ndp.exemplars # exemplars not implemented yet from metrics sdk - ) - end - - # may not need this - def reset - SUCCESS - end - - def shutdown(timeout: nil) - @shutdown = true - SUCCESS - end - end - end - end -end diff --git a/exporter/otlp-metrics/lib/opentelemetry/exporter/otlp/util.rb b/exporter/otlp-metrics/lib/opentelemetry/exporter/otlp/util.rb deleted file mode 100644 index 9075893fea..0000000000 --- a/exporter/otlp-metrics/lib/opentelemetry/exporter/otlp/util.rb +++ /dev/null @@ -1,139 +0,0 @@ -# frozen_string_literal: true - -# Copyright The OpenTelemetry Authors -# -# SPDX-License-Identifier: Apache-2.0 - -module OpenTelemetry - module Exporter - module OTLP - # Util module provide essential functionality for exporter - module Util # rubocop:disable Metrics/ModuleLength - KEEP_ALIVE_TIMEOUT = 30 - RETRY_COUNT = 5 - ERROR_MESSAGE_INVALID_HEADERS = 'headers must be a String with comma-separated URL Encoded UTF-8 k=v pairs or a Hash' - DEFAULT_USER_AGENT = "OTel-OTLP-MetricsExporter-Ruby/#{OpenTelemetry::Exporter::OTLP::VERSION} Ruby/#{RUBY_VERSION} (#{RUBY_PLATFORM}; #{RUBY_ENGINE}/#{RUBY_ENGINE_VERSION})".freeze - - def http_connection(uri, ssl_verify_mode, certificate_file) - http = Net::HTTP.new(uri.host, uri.port) - http.use_ssl = uri.scheme == 'https' - http.verify_mode = ssl_verify_mode - http.ca_file = certificate_file unless certificate_file.nil? - http.keep_alive_timeout = KEEP_ALIVE_TIMEOUT - http - end - - def around_request - OpenTelemetry::Common::Utilities.untraced { yield } # rubocop:disable Style/ExplicitBlockArgument - end - - def as_otlp_key_value(key, value) - Opentelemetry::Proto::Common::V1::KeyValue.new(key: key, value: as_otlp_any_value(value)) - rescue Encoding::UndefinedConversionError => e - encoded_value = value.encode('UTF-8', invalid: :replace, undef: :replace, replace: '�') - OpenTelemetry.handle_error(exception: e, message: "encoding error for key #{key} and value #{encoded_value}") - Opentelemetry::Proto::Common::V1::KeyValue.new(key: key, value: as_otlp_any_value('Encoding Error')) - end - - def as_otlp_any_value(value) - result = Opentelemetry::Proto::Common::V1::AnyValue.new - case value - when String - result.string_value = value - when Integer - result.int_value = value - when Float - result.double_value = value - when true, false - result.bool_value = value - when Array - values = value.map { |element| as_otlp_any_value(element) } - result.array_value = Opentelemetry::Proto::Common::V1::ArrayValue.new(values: values) - end - result - end - - def prepare_headers(config_headers) - headers = case config_headers - when String then parse_headers(config_headers) - when Hash then config_headers.dup - else - raise ArgumentError, ERROR_MESSAGE_INVALID_HEADERS - end - - headers['User-Agent'] = "#{headers.fetch('User-Agent', '')} #{DEFAULT_USER_AGENT}".strip - - headers - end - - def measure_request_duration - start = Process.clock_gettime(Process::CLOCK_MONOTONIC) - begin - yield - ensure - stop = Process.clock_gettime(Process::CLOCK_MONOTONIC) - 1000.0 * (stop - start) - end - end - - def parse_headers(raw) - entries = raw.split(',') - raise ArgumentError, ERROR_MESSAGE_INVALID_HEADERS if entries.empty? - - entries.each_with_object({}) do |entry, headers| - k, v = entry.split('=', 2).map(&CGI.method(:unescape)) - begin - k = k.to_s.strip - v = v.to_s.strip - rescue Encoding::CompatibilityError - raise ArgumentError, ERROR_MESSAGE_INVALID_HEADERS - rescue ArgumentError => e - raise e, ERROR_MESSAGE_INVALID_HEADERS - end - raise ArgumentError, ERROR_MESSAGE_INVALID_HEADERS if k.empty? || v.empty? - - headers[k] = v - end - end - - def backoff?(retry_count:, reason:, retry_after: nil) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity - return false if retry_count > RETRY_COUNT - - sleep_interval = nil - unless retry_after.nil? - sleep_interval = - begin - Integer(retry_after) - rescue ArgumentError - nil - end - sleep_interval ||= - begin - Time.httpdate(retry_after) - Time.now - rescue # rubocop:disable Style/RescueStandardError - nil - end - sleep_interval = nil unless sleep_interval&.positive? - end - sleep_interval ||= rand(2**retry_count) - - sleep(sleep_interval) - true - end - - def log_status(body) - status = Google::Rpc::Status.decode(body) - details = status.details.map do |detail| - klass_or_nil = ::Google::Protobuf::DescriptorPool.generated_pool.lookup(detail.type_name).msgclass - detail.unpack(klass_or_nil) if klass_or_nil - end.compact - OpenTelemetry.handle_error(message: "OTLP metrics_exporter received rpc.Status{message=#{status.message}, details=#{details}}") - rescue StandardError => e - OpenTelemetry.handle_error(exception: e, message: 'unexpected error decoding rpc.Status in OTLP::MetricsExporter#log_status') - end - - def handle_redirect(location); end - end - end - end -end diff --git a/exporter/otlp-metrics/lib/opentelemetry/exporter/otlp_metrics.rb b/exporter/otlp-metrics/lib/opentelemetry/exporter/otlp_metrics.rb index fe2d7d4c13..05f6b35972 100644 --- a/exporter/otlp-metrics/lib/opentelemetry/exporter/otlp_metrics.rb +++ b/exporter/otlp-metrics/lib/opentelemetry/exporter/otlp_metrics.rb @@ -4,8 +4,8 @@ # # SPDX-License-Identifier: Apache-2.0 -require 'opentelemetry/exporter/otlp/version' -require 'opentelemetry/exporter/otlp/metrics_exporter' +require 'opentelemetry/exporter/otlp/metrics/version' +require 'opentelemetry/exporter/otlp/metrics/metrics_exporter' # OpenTelemetry is an open source observability framework, providing a # general-purpose API, SDK, and related tools required for the instrumentation diff --git a/exporter/otlp-metrics/opentelemetry-exporter-otlp-metrics.gemspec b/exporter/otlp-metrics/opentelemetry-exporter-otlp-metrics.gemspec index 655be1d78c..cd4ee258e0 100644 --- a/exporter/otlp-metrics/opentelemetry-exporter-otlp-metrics.gemspec +++ b/exporter/otlp-metrics/opentelemetry-exporter-otlp-metrics.gemspec @@ -6,11 +6,11 @@ lib = File.expand_path('lib', __dir__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) -require 'opentelemetry/exporter/otlp/version' +require 'opentelemetry/exporter/otlp/metrics/version' Gem::Specification.new do |spec| spec.name = 'opentelemetry-exporter-otlp-metrics' - spec.version = OpenTelemetry::Exporter::OTLP::VERSION + spec.version = OpenTelemetry::Exporter::OTLP::Metrics::VERSION spec.authors = ['OpenTelemetry Authors'] spec.email = ['cncf-opentelemetry-contributors@lists.cncf.io'] @@ -29,6 +29,8 @@ Gem::Specification.new do |spec| spec.add_dependency 'google-protobuf', '>= 3.18', '< 5.0' spec.add_dependency 'opentelemetry-api', '~> 1.1' spec.add_dependency 'opentelemetry-common', '~> 0.20' + spec.add_dependency 'opentelemetry-metrics-api', '~> 0.1.0' + spec.add_dependency 'opentelemetry-metrics-sdk', '~> 0.1.0' spec.add_dependency 'opentelemetry-sdk', '~> 1.2' spec.add_dependency 'opentelemetry-semantic_conventions' @@ -46,9 +48,9 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'yard-doctest', '~> 0.1.6' if spec.respond_to?(:metadata) - spec.metadata['changelog_uri'] = "https://open-telemetry.github.io/opentelemetry-ruby/opentelemetry-exporter-otlp-metrics/v#{OpenTelemetry::Exporter::OTLP::VERSION}/file.CHANGELOG.html" + spec.metadata['changelog_uri'] = "https://open-telemetry.github.io/opentelemetry-ruby/opentelemetry-exporter-otlp-metrics/v#{OpenTelemetry::Exporter::OTLP::Metrics::VERSION}/file.CHANGELOG.html" spec.metadata['source_code_uri'] = 'https://github.com/open-telemetry/opentelemetry-ruby/tree/main/exporter/otlp-metrics' spec.metadata['bug_tracker_uri'] = 'https://github.com/open-telemetry/opentelemetry-ruby/issues' - spec.metadata['documentation_uri'] = "https://open-telemetry.github.io/opentelemetry-ruby/opentelemetry-exporter-otlp-metrics/v#{OpenTelemetry::Exporter::OTLP::VERSION}" + spec.metadata['documentation_uri'] = "https://open-telemetry.github.io/opentelemetry-ruby/opentelemetry-exporter-otlp-metrics/v#{OpenTelemetry::Exporter::OTLP::Metrics::VERSION}" end end diff --git a/exporter/otlp-metrics/test/opentelemetry/exporter/otlp/metrics_exporter_test.rb b/exporter/otlp-metrics/test/opentelemetry/exporter/otlp/metrics_exporter_test.rb index f4d11883b6..5add4cbc55 100644 --- a/exporter/otlp-metrics/test/opentelemetry/exporter/otlp/metrics_exporter_test.rb +++ b/exporter/otlp-metrics/test/opentelemetry/exporter/otlp/metrics_exporter_test.rb @@ -8,15 +8,15 @@ require 'google/protobuf/wrappers_pb' require 'google/protobuf/well_known_types' -describe OpenTelemetry::Exporter::OTLP::MetricsExporter do +describe OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter do METRICS_SUCCESS = OpenTelemetry::SDK::Metrics::Export::SUCCESS METRICS_FAILURE = OpenTelemetry::SDK::Metrics::Export::FAILURE - METRICS_VERSION = OpenTelemetry::Exporter::OTLP::VERSION - METRICS_DEFAULT_USER_AGENT = OpenTelemetry::Exporter::OTLP::Util::DEFAULT_USER_AGENT + METRICS_VERSION = OpenTelemetry::Exporter::OTLP::Metrics::VERSION + METRICS_DEFAULT_USER_AGENT = OpenTelemetry::Exporter::OTLP::Metrics::Util::DEFAULT_USER_AGENT describe '#initialize' do it 'initializes with defaults' do - exp = OpenTelemetry::Exporter::OTLP::MetricsExporter.new + exp = OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new _(exp).wont_be_nil _(exp.instance_variable_get(:@headers)).must_equal('User-Agent' => METRICS_DEFAULT_USER_AGENT) _(exp.instance_variable_get(:@timeout)).must_equal 10.0 @@ -39,24 +39,24 @@ it 'refuses invalid endpoint' do assert_raises ArgumentError do - OpenTelemetry::Exporter::OTLP::MetricsExporter.new(endpoint: 'not a url') + OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new(endpoint: 'not a url') end end it 'uses endpoints path if provided' do - exp = OpenTelemetry::Exporter::OTLP::MetricsExporter.new(endpoint: 'https://localhost/custom/path') + exp = OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new(endpoint: 'https://localhost/custom/path') _(exp.instance_variable_get(:@path)).must_equal '/custom/path' end it 'only allows gzip compression or none' do assert_raises ArgumentError do - OpenTelemetry::Exporter::OTLP::MetricsExporter.new(compression: 'flate') + OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new(compression: 'flate') end - exp = OpenTelemetry::Exporter::OTLP::MetricsExporter.new(compression: nil) + exp = OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new(compression: nil) _(exp.instance_variable_get(:@compression)).must_be_nil %w[gzip none].each do |compression| - exp = OpenTelemetry::Exporter::OTLP::MetricsExporter.new(compression: compression) + exp = OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new(compression: compression) _(exp.instance_variable_get(:@compression)).must_equal(compression) end @@ -67,7 +67,7 @@ { envar: 'OTEL_EXPORTER_OTLP_METRICS_COMPRESSION', value: 'none' } ].each do |example| OpenTelemetry::TestHelpers.with_env(example[:envar] => example[:value]) do - exp = OpenTelemetry::Exporter::OTLP::MetricsExporter.new + exp = OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new _(exp.instance_variable_get(:@compression)).must_equal(example[:value]) end end @@ -80,7 +80,7 @@ 'OTEL_EXPORTER_OTLP_COMPRESSION' => 'gzip', 'OTEL_RUBY_EXPORTER_OTLP_SSL_VERIFY_NONE' => 'true', 'OTEL_EXPORTER_OTLP_TIMEOUT' => '11') do - OpenTelemetry::Exporter::OTLP::MetricsExporter.new + OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new end _(exp.instance_variable_get(:@headers)).must_equal('a' => 'b', 'c' => 'd', 'User-Agent' => METRICS_DEFAULT_USER_AGENT) _(exp.instance_variable_get(:@timeout)).must_equal 11.0 @@ -101,12 +101,12 @@ 'OTEL_EXPORTER_OTLP_COMPRESSION' => 'flate', 'OTEL_RUBY_EXPORTER_OTLP_SSL_VERIFY_PEER' => 'true', 'OTEL_EXPORTER_OTLP_TIMEOUT' => '11') do - OpenTelemetry::Exporter::OTLP::MetricsExporter.new(endpoint: 'http://localhost:4321', - certificate_file: '/baz', - headers: { 'x' => 'y' }, - compression: 'gzip', - ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE, - timeout: 12) + OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new(endpoint: 'http://localhost:4321', + certificate_file: '/baz', + headers: { 'x' => 'y' }, + compression: 'gzip', + ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE, + timeout: 12) end _(exp.instance_variable_get(:@headers)).must_equal('x' => 'y', 'User-Agent' => METRICS_DEFAULT_USER_AGENT) _(exp.instance_variable_get(:@timeout)).must_equal 12.0 @@ -124,7 +124,7 @@ exp = OpenTelemetry::TestHelpers.with_env( 'OTEL_EXPORTER_OTLP_ENDPOINT' => 'https://localhost:1234/' ) do - OpenTelemetry::Exporter::OTLP::MetricsExporter.new + OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new end _(exp.instance_variable_get(:@path)).must_equal '/v1/metrics' end @@ -133,20 +133,20 @@ exp = OpenTelemetry::TestHelpers.with_env( 'OTEL_EXPORTER_OTLP_ENDPOINT' => 'https://localhost:1234' ) do - OpenTelemetry::Exporter::OTLP::MetricsExporter.new + OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new end _(exp.instance_variable_get(:@path)).must_equal '/v1/metrics' end it 'restricts explicit headers to a String or Hash' do - exp = OpenTelemetry::Exporter::OTLP::MetricsExporter.new(headers: { 'token' => 'über' }) + exp = OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new(headers: { 'token' => 'über' }) _(exp.instance_variable_get(:@headers)).must_equal('token' => 'über', 'User-Agent' => METRICS_DEFAULT_USER_AGENT) - exp = OpenTelemetry::Exporter::OTLP::MetricsExporter.new(headers: 'token=%C3%BCber') + exp = OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new(headers: 'token=%C3%BCber') _(exp.instance_variable_get(:@headers)).must_equal('token' => 'über', 'User-Agent' => METRICS_DEFAULT_USER_AGENT) error = _ do - exp = OpenTelemetry::Exporter::OTLP::MetricsExporter.new(headers: Object.new) + exp = OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new(headers: Object.new) _(exp.instance_variable_get(:@headers)).must_equal('token' => 'über') end.must_raise(ArgumentError) _(error.message).must_match(/headers/i) @@ -154,7 +154,7 @@ it 'ignores later mutations of a headers Hash parameter' do a_hash_to_mutate_later = { 'token' => 'über' } - exp = OpenTelemetry::Exporter::OTLP::MetricsExporter.new(headers: a_hash_to_mutate_later) + exp = OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new(headers: a_hash_to_mutate_later) _(exp.instance_variable_get(:@headers)).must_equal('token' => 'über', 'User-Agent' => METRICS_DEFAULT_USER_AGENT) a_hash_to_mutate_later['token'] = 'unter' @@ -165,60 +165,60 @@ describe 'Headers Environment Variable' do it 'allows any number of the equal sign (=) characters in the value' do exp = OpenTelemetry::TestHelpers.with_env('OTEL_EXPORTER_OTLP_HEADERS' => 'a=b,c=d==,e=f') do - OpenTelemetry::Exporter::OTLP::MetricsExporter.new + OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new end _(exp.instance_variable_get(:@headers)).must_equal('a' => 'b', 'c' => 'd==', 'e' => 'f', 'User-Agent' => METRICS_DEFAULT_USER_AGENT) exp = OpenTelemetry::TestHelpers.with_env('OTEL_EXPORTER_OTLP_METRICS_HEADERS' => 'a=b,c=d==,e=f') do - OpenTelemetry::Exporter::OTLP::MetricsExporter.new + OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new end _(exp.instance_variable_get(:@headers)).must_equal('a' => 'b', 'c' => 'd==', 'e' => 'f', 'User-Agent' => METRICS_DEFAULT_USER_AGENT) end it 'trims any leading or trailing whitespaces in keys and values' do exp = OpenTelemetry::TestHelpers.with_env('OTEL_EXPORTER_OTLP_HEADERS' => 'a = b ,c=d , e=f') do - OpenTelemetry::Exporter::OTLP::MetricsExporter.new + OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new end _(exp.instance_variable_get(:@headers)).must_equal('a' => 'b', 'c' => 'd', 'e' => 'f', 'User-Agent' => METRICS_DEFAULT_USER_AGENT) exp = OpenTelemetry::TestHelpers.with_env('OTEL_EXPORTER_OTLP_METRICS_HEADERS' => 'a = b ,c=d , e=f') do - OpenTelemetry::Exporter::OTLP::MetricsExporter.new + OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new end _(exp.instance_variable_get(:@headers)).must_equal('a' => 'b', 'c' => 'd', 'e' => 'f', 'User-Agent' => METRICS_DEFAULT_USER_AGENT) end it 'decodes values as URL encoded UTF-8 strings' do exp = OpenTelemetry::TestHelpers.with_env('OTEL_EXPORTER_OTLP_HEADERS' => 'token=%C3%BCber') do - OpenTelemetry::Exporter::OTLP::MetricsExporter.new + OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new end _(exp.instance_variable_get(:@headers)).must_equal('token' => 'über', 'User-Agent' => METRICS_DEFAULT_USER_AGENT) exp = OpenTelemetry::TestHelpers.with_env('OTEL_EXPORTER_OTLP_HEADERS' => '%C3%BCber=token') do - OpenTelemetry::Exporter::OTLP::MetricsExporter.new + OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new end _(exp.instance_variable_get(:@headers)).must_equal('über' => 'token', 'User-Agent' => METRICS_DEFAULT_USER_AGENT) exp = OpenTelemetry::TestHelpers.with_env('OTEL_EXPORTER_OTLP_METRICS_HEADERS' => 'token=%C3%BCber') do - OpenTelemetry::Exporter::OTLP::MetricsExporter.new + OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new end _(exp.instance_variable_get(:@headers)).must_equal('token' => 'über', 'User-Agent' => METRICS_DEFAULT_USER_AGENT) exp = OpenTelemetry::TestHelpers.with_env('OTEL_EXPORTER_OTLP_METRICS_HEADERS' => '%C3%BCber=token') do - OpenTelemetry::Exporter::OTLP::MetricsExporter.new + OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new end _(exp.instance_variable_get(:@headers)).must_equal('über' => 'token', 'User-Agent' => METRICS_DEFAULT_USER_AGENT) end it 'appends the default user agent to one provided in config' do exp = OpenTelemetry::TestHelpers.with_env('OTEL_EXPORTER_OTLP_HEADERS' => 'User-Agent=%C3%BCber/3.2.1') do - OpenTelemetry::Exporter::OTLP::MetricsExporter.new + OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new end _(exp.instance_variable_get(:@headers)).must_equal('User-Agent' => "über/3.2.1 #{METRICS_DEFAULT_USER_AGENT}") end it 'prefers METRICS specific variable' do exp = OpenTelemetry::TestHelpers.with_env('OTEL_EXPORTER_OTLP_HEADERS' => 'a=b,c=d==,e=f', 'OTEL_EXPORTER_OTLP_METRICS_HEADERS' => 'token=%C3%BCber') do - OpenTelemetry::Exporter::OTLP::MetricsExporter.new + OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new end _(exp.instance_variable_get(:@headers)).must_equal('token' => 'über', 'User-Agent' => METRICS_DEFAULT_USER_AGENT) end @@ -226,14 +226,14 @@ it 'fails fast when header values are missing' do error = _ do OpenTelemetry::TestHelpers.with_env('OTEL_EXPORTER_OTLP_HEADERS' => 'a = ') do - OpenTelemetry::Exporter::OTLP::MetricsExporter.new + OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new end end.must_raise(ArgumentError) _(error.message).must_match(/headers/i) error = _ do OpenTelemetry::TestHelpers.with_env('OTEL_EXPORTER_OTLP_METRICS_HEADERS' => 'a = ') do - OpenTelemetry::Exporter::OTLP::MetricsExporter.new + OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new end end.must_raise(ArgumentError) _(error.message).must_match(/headers/i) @@ -242,14 +242,14 @@ it 'fails fast when header or values are not found' do error = _ do OpenTelemetry::TestHelpers.with_env('OTEL_EXPORTER_OTLP_HEADERS' => ',') do - OpenTelemetry::Exporter::OTLP::MetricsExporter.new + OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new end end.must_raise(ArgumentError) _(error.message).must_match(/headers/i) error = _ do OpenTelemetry::TestHelpers.with_env('OTEL_EXPORTER_OTLP_METRICS_HEADERS' => ',') do - OpenTelemetry::Exporter::OTLP::MetricsExporter.new + OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new end end.must_raise(ArgumentError) _(error.message).must_match(/headers/i) @@ -258,14 +258,14 @@ it 'fails fast when header values contain invalid escape characters' do error = _ do OpenTelemetry::TestHelpers.with_env('OTEL_EXPORTER_OTLP_HEADERS' => 'c=hi%F3') do - OpenTelemetry::Exporter::OTLP::MetricsExporter.new + OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new end end.must_raise(ArgumentError) _(error.message).must_match(/headers/i) error = _ do OpenTelemetry::TestHelpers.with_env('OTEL_EXPORTER_OTLP_METRICS_HEADERS' => 'c=hi%F3') do - OpenTelemetry::Exporter::OTLP::MetricsExporter.new + OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new end end.must_raise(ArgumentError) _(error.message).must_match(/headers/i) @@ -274,14 +274,14 @@ it 'fails fast when headers are invalid' do error = _ do OpenTelemetry::TestHelpers.with_env('OTEL_EXPORTER_OTLP_HEADERS' => 'this is not a header') do - OpenTelemetry::Exporter::OTLP::MetricsExporter.new + OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new end end.must_raise(ArgumentError) _(error.message).must_match(/headers/i) error = _ do OpenTelemetry::TestHelpers.with_env('OTEL_EXPORTER_OTLP_METRICS_HEADERS' => 'this is not a header') do - OpenTelemetry::Exporter::OTLP::MetricsExporter.new + OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new end end.must_raise(ArgumentError) _(error.message).must_match(/headers/i) @@ -292,7 +292,7 @@ describe 'ssl_verify_mode:' do it 'can be set to VERIFY_NONE by an envvar' do exp = OpenTelemetry::TestHelpers.with_env('OTEL_RUBY_EXPORTER_OTLP_SSL_VERIFY_NONE' => 'true') do - OpenTelemetry::Exporter::OTLP::MetricsExporter.new + OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new end http = exp.instance_variable_get(:@http) _(http.verify_mode).must_equal OpenSSL::SSL::VERIFY_NONE @@ -300,7 +300,7 @@ it 'can be set to VERIFY_PEER by an envvar' do exp = OpenTelemetry::TestHelpers.with_env('OTEL_RUBY_EXPORTER_OTLP_SSL_VERIFY_PEER' => 'true') do - OpenTelemetry::Exporter::OTLP::MetricsExporter.new + OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new end http = exp.instance_variable_get(:@http) _(http.verify_mode).must_equal OpenSSL::SSL::VERIFY_PEER @@ -309,7 +309,7 @@ it 'VERIFY_PEER will override VERIFY_NONE' do exp = OpenTelemetry::TestHelpers.with_env('OTEL_RUBY_EXPORTER_OTLP_SSL_VERIFY_NONE' => 'true', 'OTEL_RUBY_EXPORTER_OTLP_SSL_VERIFY_PEER' => 'true') do - OpenTelemetry::Exporter::OTLP::MetricsExporter.new + OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new end http = exp.instance_variable_get(:@http) _(http.verify_mode).must_equal OpenSSL::SSL::VERIFY_PEER @@ -317,14 +317,14 @@ end describe '#export' do - let(:exporter) { OpenTelemetry::Exporter::OTLP::MetricsExporter.new } + let(:exporter) { OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new } let(:meter_provider) { OpenTelemetry::SDK::Metrics::MeterProvider.new(resource: OpenTelemetry::SDK::Resources::Resource.telemetry_sdk) } it 'integrates with collector' do skip unless ENV['TRACING_INTEGRATION_TEST'] WebMock.disable_net_connect!(allow: 'localhost') metrics_data = create_metrics_data - exporter = OpenTelemetry::Exporter::OTLP::MetricsExporter.new(endpoint: 'http://localhost:4318', compression: 'gzip') + exporter = OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new(endpoint: 'http://localhost:4318', compression: 'gzip') result = exporter.export([metrics_data]) _(result).must_equal(METRICS_SUCCESS) end @@ -404,7 +404,7 @@ end it 'returns METRICS_FAILURE when encryption to receiver endpoint fails' do - exporter = OpenTelemetry::Exporter::OTLP::MetricsExporter.new(endpoint: 'https://localhost:4318/v1/metrics') + exporter = OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new(endpoint: 'https://localhost:4318/v1/metrics') stub_request(:post, 'https://localhost:4318/v1/metrics').to_raise(OpenSSL::SSL::SSLError.new('enigma wedged')) metrics_data = create_metrics_data exporter.stub(:backoff?, ->(**_) { false }) do @@ -506,7 +506,7 @@ end it 'compresses with gzip if enabled' do - exporter = OpenTelemetry::Exporter::OTLP::MetricsExporter.new(compression: 'gzip') + exporter = OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new(compression: 'gzip') stub_post = stub_request(:post, 'http://localhost:4318/v1/metrics').to_return do |request| Opentelemetry::Proto::Collector::Metrics::V1::ExportMetricsServiceRequest.decode(Zlib.gunzip(request.body)) { status: 200 } From de318dae6ebc2215fdb4efb3826ebdc711ccbaba Mon Sep 17 00:00:00 2001 From: Kayla Reopelle <87386821+kaylareopelle@users.noreply.github.com> Date: Fri, 23 Aug 2024 15:35:57 -0700 Subject: [PATCH 15/20] feat: Add log record processors (#1682) * feat: Add log record exporter interface * chore: Move ExportError within Export module * feat: Add simple and batch log record processors * test: Adjust for JRuby * Replace let variables with specific declarations in each test * Add more records for buffer-full tests --------- Co-authored-by: Matthew Wear --- logs_sdk/lib/opentelemetry/sdk/logs/export.rb | 2 + .../logs/export/batch_log_record_processor.rb | 219 +++++++ .../export/simple_log_record_processor.rb | 88 +++ .../export/batch_log_record_processor_test.rb | 535 ++++++++++++++++++ .../simple_log_record_processor_test.rb | 134 +++++ 5 files changed, 978 insertions(+) create mode 100644 logs_sdk/lib/opentelemetry/sdk/logs/export/batch_log_record_processor.rb create mode 100644 logs_sdk/lib/opentelemetry/sdk/logs/export/simple_log_record_processor.rb create mode 100644 logs_sdk/test/opentelemetry/sdk/logs/export/batch_log_record_processor_test.rb create mode 100644 logs_sdk/test/opentelemetry/sdk/logs/export/simple_log_record_processor_test.rb diff --git a/logs_sdk/lib/opentelemetry/sdk/logs/export.rb b/logs_sdk/lib/opentelemetry/sdk/logs/export.rb index 5cb94455a9..2565dbf85f 100644 --- a/logs_sdk/lib/opentelemetry/sdk/logs/export.rb +++ b/logs_sdk/lib/opentelemetry/sdk/logs/export.rb @@ -25,3 +25,5 @@ module Export end require_relative 'export/log_record_exporter' +require_relative 'export/simple_log_record_processor' +require_relative 'export/batch_log_record_processor' diff --git a/logs_sdk/lib/opentelemetry/sdk/logs/export/batch_log_record_processor.rb b/logs_sdk/lib/opentelemetry/sdk/logs/export/batch_log_record_processor.rb new file mode 100644 index 0000000000..65f99a1512 --- /dev/null +++ b/logs_sdk/lib/opentelemetry/sdk/logs/export/batch_log_record_processor.rb @@ -0,0 +1,219 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Logs + module Export + # WARNING - The spec has some differences from the LogRecord version of this processor + # Implementation of the duck type LogRecordProcessor that batches + # log records exported by the SDK then pushes them to the exporter + # pipeline. + # + # Typically, the BatchLogRecordProcessor will be more suitable for + # production environments than the SimpleLogRecordProcessor. + class BatchLogRecordProcessor < LogRecordProcessor # rubocop:disable Metrics/ClassLength + # Returns a new instance of the {BatchLogRecordProcessor}. + # + # @param [LogRecordExporter] exporter The (duck type) LogRecordExporter to where the + # recorded LogRecords are pushed after batching. + # @param [Numeric] exporter_timeout The maximum allowed time to export data. + # Defaults to the value of the OTEL_BLRP_EXPORT_TIMEOUT + # environment variable, if set, or 30,000 (30 seconds). + # @param [Numeric] schedule_delay the delay interval between two consecutive exports. + # Defaults to the value of the OTEL_BLRP_SCHEDULE_DELAY environment + # variable, if set, or 1,000 (1 second). + # @param [Integer] max_queue_size the maximum queue size in log records. + # Defaults to the value of the OTEL_BLRP_MAX_QUEUE_SIZE environment + # variable, if set, or 2048. + # @param [Integer] max_export_batch_size the maximum batch size in log records. + # Defaults to the value of the OTEL_BLRP_MAX_EXPORT_BATCH_SIZE environment + # variable, if set, or 512. + # + # @return a new instance of the {BatchLogRecordProcessor}. + def initialize(exporter, + exporter_timeout: Float(ENV.fetch('OTEL_BLRP_EXPORT_TIMEOUT', 30_000)), + schedule_delay: Float(ENV.fetch('OTEL_BLRP_SCHEDULE_DELAY', 1000)), + max_queue_size: Integer(ENV.fetch('OTEL_BLRP_MAX_QUEUE_SIZE', 2048)), + max_export_batch_size: Integer(ENV.fetch('OTEL_BLRP_MAX_EXPORT_BATCH_SIZE', 512)), + start_thread_on_boot: String(ENV['OTEL_RUBY_BLRP_START_THREAD_ON_BOOT']) !~ /false/i) + + unless max_export_batch_size <= max_queue_size + raise ArgumentError, + 'max_export_batch_size much be less than or equal to max_queue_size' + end + + unless Common::Utilities.valid_exporter?(exporter) + raise ArgumentError, + "exporter #{exporter.inspect} does not appear to be a valid exporter" + end + + @exporter = exporter + @exporter_timeout_seconds = exporter_timeout / 1000.0 + @mutex = Mutex.new + @export_mutex = Mutex.new + @condition = ConditionVariable.new + @keep_running = true + @stopped = false + @delay_seconds = schedule_delay / 1000.0 + @max_queue_size = max_queue_size + @batch_size = max_export_batch_size + @log_records = [] + @pid = nil + @thread = nil + reset_on_fork(restart_thread: start_thread_on_boot) + end + + # Adds a log record to the batch. Thread-safe; may block on lock. + def on_emit(log_record, _context) + return if @stopped + + lock do + reset_on_fork + n = log_records.size + 1 - max_queue_size + if n.positive? + log_records.shift(n) + report_dropped_log_records(n, reason: 'buffer-full') + end + log_records << log_record + @condition.signal if log_records.size > batch_size + end + end + + # Export all emitted log records that have not yet been exported to + # the configured `Exporter`. + # + # This method should only be called in cases where it is absolutely + # necessary, such as when using some FaaS providers that may suspend + # the process after an invocation, but before the `Processor` exports + # the completed log records. + # + # @param [optional Numeric] timeout An optional timeout in seconds. + # @return [Integer] SUCCESS if no error occurred, FAILURE if a + # non-specific failure occurred, TIMEOUT if a timeout occurred. + def force_flush(timeout: nil) + start_time = OpenTelemetry::Common::Utilities.timeout_timestamp + + snapshot = lock do + reset_on_fork if @keep_running + log_records.shift(log_records.size) + end + + until snapshot.empty? + remaining_timeout = OpenTelemetry::Common::Utilities.maybe_timeout(timeout, start_time) + return TIMEOUT if remaining_timeout&.zero? + + batch = snapshot.shift(batch_size).map!(&:to_log_record_data) + result_code = export_batch(batch, timeout: remaining_timeout) + return result_code unless result_code == SUCCESS + end + + @exporter.force_flush(timeout: OpenTelemetry::Common::Utilities.maybe_timeout(timeout, start_time)) + ensure + # Unshift the remaining log records if we timed out. We drop excess + # log records from the snapshot because they're older than any + # records in the buffer. + lock do + n = log_records.size + snapshot.size - max_queue_size + + if n.positive? + snapshot.shift(n) + report_dropped_log_records(n, reason: 'buffer-full') + end + + log_records.unshift(*snapshot) unless snapshot.empty? + @condition.signal if log_records.size > max_queue_size / 2 + end + end + + # Shuts the consumer thread down and flushes the current accumulated + # buffer will block until the thread is finished. + # + # @param [optional Numeric] timeout An optional timeout in seconds. + # @return [Integer] SUCCESS if no error occurred, FAILURE if a + # non-specific failure occurred, TIMEOUT if a timeout occurred. + def shutdown(timeout: nil) + return if @stopped + + start_time = OpenTelemetry::Common::Utilities.timeout_timestamp + thread = lock do + @keep_running = false + @stopped = true + @condition.signal + @thread + end + + thread&.join(timeout) + force_flush(timeout: OpenTelemetry::Common::Utilities.maybe_timeout(timeout, start_time)) + dropped_log_records = lock { log_records.size } + report_dropped_log_records(dropped_log_records, reason: 'terminating') if dropped_log_records.positive? + + @exporter.shutdown(timeout: OpenTelemetry::Common::Utilities.maybe_timeout(timeout, start_time)) + end + + private + + attr_reader :log_records, :max_queue_size, :batch_size + + def work + loop do + batch = lock do + @condition.wait(@mutex, @delay_seconds) if log_records.size < batch_size && @keep_running + @condition.wait(@mutex, @delay_seconds) while log_records.empty? && @keep_running + return unless @keep_running + + fetch_batch + end + + export_batch(batch) + end + end + + def reset_on_fork(restart_thread: true) + pid = Process.pid + return if @pid == pid + + @pid = pid + log_records.clear + @thread = restart_thread ? Thread.new { work } : nil + rescue ThreadError => e + OpenTelemetry.handle_error(exception: e, message: 'unexpected error in BatchLogRecordProcessor#reset_on_fork') + end + + def export_batch(batch, timeout: @exporter_timeout_seconds) + result_code = @export_mutex.synchronize { @exporter.export(batch, timeout: timeout) } + report_result(result_code, batch) + result_code + rescue StandardError => e + report_result(FAILURE, batch) + OpenTelemetry.handle_error(exception: e, message: 'unexpected error in BatchLogRecordProcessor#export_batch') + end + + def report_result(result_code, batch) + if result_code == SUCCESS + OpenTelemetry.logger.debug("Successfully exported #{batch.size} log records") + else + OpenTelemetry.handle_error(exception: ExportError.new("Unable to export #{batch.size} log records")) + OpenTelemetry.logger.error("Result code: #{result_code}") + end + end + + def report_dropped_log_records(count, reason:) + OpenTelemetry.logger.warn("#{count} log record(s) dropped. Reason: #{reason}") + end + + def fetch_batch + log_records.shift(@batch_size).map!(&:to_log_record_data) + end + + def lock(&block) + @mutex.synchronize(&block) + end + end + end + end + end +end diff --git a/logs_sdk/lib/opentelemetry/sdk/logs/export/simple_log_record_processor.rb b/logs_sdk/lib/opentelemetry/sdk/logs/export/simple_log_record_processor.rb new file mode 100644 index 0000000000..9cf4fcbbd8 --- /dev/null +++ b/logs_sdk/lib/opentelemetry/sdk/logs/export/simple_log_record_processor.rb @@ -0,0 +1,88 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Logs + module Export + # An implementation of {LogRecordProcessor} that converts the LogRecord + # into a ReadableLogRecord and passes it to the configured exporter + # on emit. + # + # Typically, the SimpleLogRecordProcessor will be most suitable for use + # in testing; it should be used with caution in production. It may be + # appropriate for production use in scenarios where creating multiple + # threads is not desirable as well as scenarios where different custom + # attributes should be added to individual log records based on code + # scopes. + class SimpleLogRecordProcessor < OpenTelemetry::SDK::Logs::LogRecordProcessor + # Returns a new {SimpleLogRecordProcessor} that converts log records + # to {ReadableLogRecords} and forwards them to the given + # log_record_exporter. + # + # @param log_record_exporter the LogRecordExporter to push the + # recorded log records. + # @return [SimpleLogRecordProcessor] + # @raise ArgumentError if the log_record_exporter is invalid or nil. + def initialize(log_record_exporter) + raise ArgumentError, "exporter #{log_record_exporter.inspect} does not appear to be a valid exporter" unless Common::Utilities.valid_exporter?(log_record_exporter) + + @log_record_exporter = log_record_exporter + @stopped = false + end + + # Called when a LogRecord is emitted. + # + # This method is called synchronously on the execution thread. It + # should not throw or block the execution thread. It may not be called + # after shutdown. + # + # @param [LogRecord] log_record The emitted {LogRecord} + # @param [Context] _context The current {Context} + def on_emit(log_record, _context) + return if @stopped + + @log_record_exporter&.export([log_record.to_log_record_data]) + rescue => e # rubocop:disable Style/RescueStandardError + OpenTelemetry.handle_error(exception: e, message: 'Unexpected error in Logger#on_emit') + end + + # Export all log records to the configured `Exporter` that have not + # yet been exported, then call {Exporter#force_flush}. + # + # This method should only be called in cases where it is absolutely + # necessary, such as when using some FaaS providers that may suspend + # the process after an invocation, but before the `Processor` exports + # the completed log records. + # + # @param [optional Numeric] timeout An optional timeout in seconds. + # @return [Integer] SUCCESS if no error occurred, FAILURE if a + # non-specific failure occurred, TIMEOUT if a timeout occurred. + # TODO: Should a rescue/handle error be added here for non-specific failures? + def force_flush(timeout: nil) + return if @stopped + + @log_record_exporter&.force_flush(timeout: timeout) || SUCCESS + end + + # Called when {LoggerProvider#shutdown} is called. + # + # @param [optional Numeric] timeout An optional timeout in seconds. + # @return [Integer] SUCCESS if no error occurred, FAILURE if a + # non-specific failure occurred, TIMEOUT if a timeout occurred. + # TODO: Should a rescue/handle error be added here for non-specific failures? + def shutdown(timeout: nil) + return if @stopped + + @log_record_exporter&.shutdown(timeout: timeout) || SUCCESS + ensure + @stopped = true + end + end + end + end + end +end diff --git a/logs_sdk/test/opentelemetry/sdk/logs/export/batch_log_record_processor_test.rb b/logs_sdk/test/opentelemetry/sdk/logs/export/batch_log_record_processor_test.rb new file mode 100644 index 0000000000..08aee76e85 --- /dev/null +++ b/logs_sdk/test/opentelemetry/sdk/logs/export/batch_log_record_processor_test.rb @@ -0,0 +1,535 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'test_helper' + +# rubocop:disable Lint/ConstantDefinitionInBlock, Style/Documentation +describe OpenTelemetry::SDK::Logs::Export::BatchLogRecordProcessor do + BatchLogRecordProcessor = OpenTelemetry::SDK::Logs::Export::BatchLogRecordProcessor + SUCCESS = OpenTelemetry::SDK::Logs::Export::SUCCESS + FAILURE = OpenTelemetry::SDK::Logs::Export::FAILURE + TIMEOUT = OpenTelemetry::SDK::Logs::Export::TIMEOUT + + class TestExporter + def initialize(status_codes: nil) + @status_codes = status_codes || [] + @batches = [] + @failed_batches = [] + end + + attr_reader :batches, :failed_batches + + def export(batch, timeout: nil) + # If status codes are empty, return success for less verbose testing + s = @status_codes.shift + if s.nil? || s == SUCCESS + @batches << batch + SUCCESS + else + @failed_batches << batch + s + end + end + + def shutdown(timeout: nil); end + + def force_flush(timeout: nil); end + end + + class NotAnExporter + end + + class RaisingExporter + def export(batch, timeout: nil) + raise 'boom!' + end + + def shutdown(timeout: nil); end + + def force_flush(timeout: nil); end + end + + class TestLogRecord + def initialize(body = nil) + @body = body + end + + attr_reader :body + + def to_log_record_data + self + end + end + + let(:mock_context) { Minitest::Mock.new } + + describe 'initialization' do + it 'raises if max batch size is greater than max queue size' do + assert_raises ArgumentError do + BatchLogRecordProcessor.new(TestExporter.new, max_queue_size: 6, max_export_batch_size: 999) + end + end + + it 'raises if OTEL_BLRP_EXPORT_TIMEOUT env var is not numeric' do + assert_raises ArgumentError do + OpenTelemetry::TestHelpers.with_env('OTEL_BLRP_EXPORT_TIMEOUT' => 'foo') do + BatchLogRecordProcessor.new(TestExporter.new) + end + end + end + + it 'raises if exporter is nil' do + _(-> { BatchLogRecordProcessor.new(nil) }).must_raise(ArgumentError) + end + + it 'raises if exporter is not an exporter' do + _(-> { BatchLogRecordProcessor.new(NotAnExporter.new) }).must_raise(ArgumentError) + end + + it 'sets parameters from the environment' do + processor = OpenTelemetry::TestHelpers.with_env('OTEL_BLRP_EXPORT_TIMEOUT' => '4', + 'OTEL_BLRP_SCHEDULE_DELAY' => '3', + 'OTEL_BLRP_MAX_QUEUE_SIZE' => '2', + 'OTEL_BLRP_MAX_EXPORT_BATCH_SIZE' => '1') do + BatchLogRecordProcessor.new(TestExporter.new) + end + _(processor.instance_variable_get(:@exporter_timeout_seconds)).must_equal 0.004 + _(processor.instance_variable_get(:@delay_seconds)).must_equal 0.003 + _(processor.instance_variable_get(:@max_queue_size)).must_equal 2 + _(processor.instance_variable_get(:@batch_size)).must_equal 1 + end + + it 'prefers explicit parameters rather than the environment' do + processor = OpenTelemetry::TestHelpers.with_env('OTEL_BLRP_EXPORT_TIMEOUT' => '4', + 'OTEL_BLRP_SCHEDULE_DELAY' => '3', + 'OTEL_BLRP_MAX_QUEUE_SIZE' => '2', + 'OTEL_BLRP_MAX_EXPORT_BATCH_SIZE' => '1') do + BatchLogRecordProcessor.new(TestExporter.new, + exporter_timeout: 10, + schedule_delay: 9, + max_queue_size: 8, + max_export_batch_size: 7) + end + _(processor.instance_variable_get(:@exporter_timeout_seconds)).must_equal 0.01 + _(processor.instance_variable_get(:@delay_seconds)).must_equal 0.009 + _(processor.instance_variable_get(:@max_queue_size)).must_equal 8 + _(processor.instance_variable_get(:@batch_size)).must_equal 7 + end + + it 'sets defaults for parameters not in the environment' do + processor = BatchLogRecordProcessor.new(TestExporter.new) + _(processor.instance_variable_get(:@exporter_timeout_seconds)).must_equal 30.0 + _(processor.instance_variable_get(:@delay_seconds)).must_equal 1.0 + _(processor.instance_variable_get(:@max_queue_size)).must_equal 2048 + _(processor.instance_variable_get(:@batch_size)).must_equal 512 + end + + it 'spawns a thread on boot by default' do + mock = Minitest::Mock.new + mock.expect(:call, nil) + + Thread.stub(:new, mock) do + BatchLogRecordProcessor.new(TestExporter.new) + end + + mock.verify + end + + it 'spawns a thread on boot if OTEL_RUBY_BLRP_START_THREAD_ON_BOOT is true' do + mock = Minitest::Mock.new + mock.expect(:call, nil) + + Thread.stub(:new, mock) do + OpenTelemetry::TestHelpers.with_env('OTEL_RUBY_BLRP_START_THREAD_ON_BOOT' => 'true') do + BatchLogRecordProcessor.new(TestExporter.new) + end + end + + mock.verify + end + + it 'does not spawn a thread on boot if OTEL_RUBY_BLRP_START_THREAD_ON_BOOT is false' do + mock = Minitest::Mock.new + mock.expect(:call, nil) { assert false } + + Thread.stub(:new, mock) do + OpenTelemetry::TestHelpers.with_env('OTEL_RUBY_BLRP_START_THREAD_ON_BOOT' => 'false') do + BatchLogRecordProcessor.new(TestExporter.new) + end + end + end + + it 'prefers explicit start_thread_on_boot parameter rather than the environment' do + mock = Minitest::Mock.new + mock.expect(:call, nil) { assert false } + + Thread.stub(:new, mock) do + OpenTelemetry::TestHelpers.with_env('OTEL_RUBY_BLRP_START_THREAD_ON_BOOT' => 'true') do + BatchLogRecordProcessor.new(TestExporter.new, + start_thread_on_boot: false) + end + end + end + end + + describe '#on_emit' do + it 'adds the log record to the batch' do + processor = BatchLogRecordProcessor.new(TestExporter.new) + log_record = TestLogRecord.new + + processor.on_emit(log_record, mock_context) + + assert_includes(processor.instance_variable_get(:@log_records), log_record) + end + + it 'removes the older log records from the batch if full' do + processor = BatchLogRecordProcessor.new(TestExporter.new, max_queue_size: 1, max_export_batch_size: 1) + + older_log_record = TestLogRecord.new + newer_log_record = TestLogRecord.new + newest_log_record = TestLogRecord.new + + processor.on_emit(older_log_record, mock_context) + processor.on_emit(newer_log_record, mock_context) + processor.on_emit(newest_log_record, mock_context) + + records = processor.instance_variable_get(:@log_records) + + assert_includes(records, newest_log_record) + refute_includes(records, newer_log_record) + refute_includes(records, older_log_record) + end + + it 'logs a warning if a log record was emitted after the buffer is full' do + mock_otel_logger = Minitest::Mock.new + mock_otel_logger.expect(:warn, nil, ['1 log record(s) dropped. Reason: buffer-full']) + + OpenTelemetry.stub(:logger, mock_otel_logger) do + processor = BatchLogRecordProcessor.new(TestExporter.new, max_queue_size: 1, max_export_batch_size: 1) + + log_record = TestLogRecord.new + log_record2 = TestLogRecord.new + + processor.on_emit(log_record, mock_context) + processor.on_emit(log_record2, mock_context) + end + + mock_otel_logger.verify + end + + it 'does not emit a log record if stopped' do + processor = BatchLogRecordProcessor.new(TestExporter.new) + + processor.instance_variable_set(:@stopped, true) + processor.on_emit(TestLogRecord.new, mock_context) + + assert_empty(processor.instance_variable_get(:@log_records)) + end + end + + describe '#force_flush' do + it 'reenqueues excess log_records on timeout' do + exporter = TestExporter.new + processor = BatchLogRecordProcessor.new(exporter) + + processor.on_emit(TestLogRecord.new, mock_context) + result = processor.force_flush(timeout: 0) + + _(result).must_equal(TIMEOUT) + + _(exporter.failed_batches.size).must_equal(0) + _(exporter.batches.size).must_equal(0) + + _(processor.instance_variable_get(:@log_records).size).must_equal(1) + end + + it 'exports the log record data and calls #force_flush on the exporter' do + mock_exporter = Minitest::Mock.new + processor = BatchLogRecordProcessor.new(TestExporter.new) + processor.instance_variable_set(:@exporter, mock_exporter) + log_record = TestLogRecord.new + log_record_data_mock = Minitest::Mock.new + + log_record.stub(:to_log_record_data, log_record_data_mock) do + processor.on_emit(log_record, mock_context) + mock_exporter.expect(:export, 0, [[log_record_data_mock]], timeout: nil) + mock_exporter.expect(:force_flush, nil, timeout: nil) + processor.force_flush + mock_exporter.verify + end + end + + it 'returns failure code if export_batch fails' do + processor = BatchLogRecordProcessor.new(TestExporter.new) + + processor.stub(:export_batch, OpenTelemetry::SDK::Logs::Export::FAILURE) do + processor.on_emit(TestLogRecord.new, mock_context) + assert_equal(OpenTelemetry::SDK::Logs::Export::FAILURE, processor.force_flush) + end + end + + it 'reports dropped logs if timeout occurs with full buffer' do + mock_otel_logger = Minitest::Mock.new + mock_otel_logger.expect(:warn, nil, [/buffer-full/]) + + OpenTelemetry.stub(:logger, mock_otel_logger) do + OpenTelemetry::Common::Utilities.stub(:maybe_timeout, 0) do + processor = BatchLogRecordProcessor.new(TestExporter.new, max_queue_size: 1, max_export_batch_size: 1) + processor.instance_variable_set(:@log_records, [TestLogRecord.new, TestLogRecord.new, TestLogRecord.new]) + processor.force_flush + end + end + + mock_otel_logger.verify + end + end + + describe '#shutdown' do + it 'does not allow subsequent calls to emit after shutdown' do + processor = BatchLogRecordProcessor.new(TestExporter.new) + + processor.shutdown + processor.on_emit(TestLogRecord.new, mock_context) + + assert_empty(processor.instance_variable_get(:@log_records)) + end + + it 'does not send shutdown to exporter if already shutdown' do + exporter = TestExporter.new + processor = BatchLogRecordProcessor.new(exporter) + + processor.instance_variable_set(:@stopped, true) + + exporter.stub(:shutdown, ->(_) { raise 'whoops!' }) do + processor.shutdown + end + end + + it 'sets @stopped to true' do + processor = BatchLogRecordProcessor.new(TestExporter.new) + + refute(processor.instance_variable_get(:@stopped)) + + processor.shutdown + + assert(processor.instance_variable_get(:@stopped)) + end + + it 'respects the timeout' do + exporter = TestExporter.new + processor = BatchLogRecordProcessor.new(exporter) + + processor.on_emit(TestLogRecord.new, mock_context) + processor.shutdown(timeout: 0) + + _(exporter.failed_batches.size).must_equal(0) + _(exporter.batches.size).must_equal(0) + + _(processor.instance_variable_get(:@log_records).size).must_equal(1) + end + + it 'works if the thread is not running' do + processor = BatchLogRecordProcessor.new(TestExporter.new, start_thread_on_boot: false) + processor.shutdown(timeout: 0) + end + + it 'returns a SUCCESS status if no error' do + test_exporter = TestExporter.new + test_exporter.instance_eval do + def shutdown(timeout: nil) + SUCCESS + end + end + + processor = BatchLogRecordProcessor.new(test_exporter) + processor.on_emit(TestLogRecord.new, mock_context) + result = processor.shutdown(timeout: 0) + + _(result).must_equal(SUCCESS) + end + + it 'returns a FAILURE status if a non-specific export error occurs' do + test_exporter = TestExporter.new + test_exporter.instance_eval do + def shutdown(timeout: nil) + FAILURE + end + end + + processor = BatchLogRecordProcessor.new(test_exporter) + processor.on_emit(TestLogRecord.new, mock_context) + result = processor.shutdown(timeout: 0) + + _(result).must_equal(FAILURE) + end + + it 'returns a TIMEOUT status if a timeout export error occurs' do + test_exporter = TestExporter.new + test_exporter.instance_eval do + def shutdown(timeout: nil) + TIMEOUT + end + end + + processor = BatchLogRecordProcessor.new(test_exporter) + processor.on_emit(TestLogRecord.new, mock_context) + result = processor.shutdown(timeout: 0) + + _(result).must_equal(TIMEOUT) + end + end + + describe 'lifecycle' do + it 'should stop and start correctly' do + processor = BatchLogRecordProcessor.new(TestExporter.new) + processor.shutdown + end + + it 'should flush everything on shutdown' do + exporter = TestExporter.new + processor = BatchLogRecordProcessor.new(exporter) + log_record = TestLogRecord.new + + processor.on_emit(log_record, mock_context) + processor.shutdown + + _(exporter.batches).must_equal [[log_record]] + end + end + + describe 'batching' do + it 'should batch up to but not over the max_batch' do + exporter = TestExporter.new + processor = BatchLogRecordProcessor.new(exporter, max_queue_size: 6, max_export_batch_size: 3) + + log_records = [TestLogRecord.new, TestLogRecord.new, TestLogRecord.new, TestLogRecord.new] + log_records.each { |log_record| processor.on_emit(log_record, mock_context) } + processor.shutdown + + _(exporter.batches[0].size).must_equal(3) + end + end + + describe 'export retry' do + it 'should not retry on FAILURE exports' do + exporter = TestExporter.new(status_codes: [FAILURE, SUCCESS]) + processor = BatchLogRecordProcessor.new(exporter, + schedule_delay: 999, + max_queue_size: 6, + max_export_batch_size: 3) + log_records = [TestLogRecord.new, TestLogRecord.new, TestLogRecord.new, TestLogRecord.new] + log_records.each { |log_record| processor.on_emit(log_record, mock_context) } + + # Ensure that our work thread has time to loop + sleep(1) + processor.shutdown + + _(exporter.batches.size).must_equal(1) + _(exporter.batches[0].size).must_equal(1) + + _(exporter.failed_batches.size).must_equal(1) + _(exporter.failed_batches[0].size).must_equal(3) + end + end + + describe 'stress test' do + it 'does not blow up with a lot of things' do + exporter = TestExporter.new + processor = BatchLogRecordProcessor.new(exporter) + + producers = 10.times.map do |i| + Thread.new do + x = i * 10 + 10.times do |j| + processor.on_emit(TestLogRecord.new(x + j), mock_context) + end + sleep(rand(0.01)) + end + end + producers.each(&:join) + processor.shutdown + + out = exporter.batches.flatten.map(&:body).sort + + expected = 100.times.map { |i| i } + + _(out).must_equal(expected) + end + end + + describe 'faulty exporter' do + let(:exporter) { RaisingExporter.new } + let(:processor) { BatchLogRecordProcessor.new(exporter) } + + it 'reports export failures' do + mock_logger = Minitest::Mock.new + mock_logger.expect(:error, nil, [/Unable to export/]) + mock_logger.expect(:error, nil, [/Result code: 1/]) + mock_logger.expect(:error, nil, [/unexpected error in .*\#export_batch/]) + + OpenTelemetry.stub(:logger, mock_logger) do + log_records = [TestLogRecord.new, TestLogRecord.new, TestLogRecord.new, TestLogRecord.new] + log_records.each { |log_record| processor.on_emit(log_record, mock_context) } + processor.shutdown + end + + mock_logger.verify + end + end + + describe 'fork safety test' do + let(:exporter) { TestExporter.new } + let(:processor) do + BatchLogRecordProcessor.new(exporter, + max_queue_size: 10, + max_export_batch_size: 3) + end + + it 'when ThreadError is raised it handles it gracefully' do + parent_pid = processor.instance_variable_get(:@pid) + parent_work_thread_id = processor.instance_variable_get(:@thread).object_id + Process.stub(:pid, parent_pid + rand(1..10)) do + Thread.stub(:new, -> { raise ThreadError }) do + processor.on_emit(TestLogRecord.new, mock_context) + end + + current_pid = processor.instance_variable_get(:@pid) + current_work_thread_id = processor.instance_variable_get(:@thread).object_id + _(parent_pid).wont_equal current_pid + _(parent_work_thread_id).must_equal current_work_thread_id + end + end + + describe 'when a process fork occurs' do + it 'creates new work thread when emit is called' do + parent_pid = processor.instance_variable_get(:@pid) + parent_work_thread_id = processor.instance_variable_get(:@thread).object_id + Process.stub(:pid, parent_pid + rand(1..10)) do + # Emit a new log record on the forked process and export it. + processor.on_emit(TestLogRecord.new, mock_context) + current_pid = processor.instance_variable_get(:@pid) + current_work_thread_id = processor.instance_variable_get(:@thread).object_id + _(parent_pid).wont_equal current_pid + _(parent_work_thread_id).wont_equal current_work_thread_id + end + end + + it 'creates new work thread when force_flush' do + parent_pid = processor.instance_variable_get(:@pid) + parent_work_thread_id = processor.instance_variable_get(:@thread).object_id + Process.stub(:pid, parent_pid + rand(1..10)) do + # Force flush on the forked process. + processor.force_flush + current_pid = processor.instance_variable_get(:@pid) + current_work_thread_id = processor.instance_variable_get(:@thread).object_id + _(parent_pid).wont_equal current_pid + _(parent_work_thread_id).wont_equal current_work_thread_id + end + end + end + end +end +# rubocop:enable Lint/ConstantDefinitionInBlock, Style/Documentation diff --git a/logs_sdk/test/opentelemetry/sdk/logs/export/simple_log_record_processor_test.rb b/logs_sdk/test/opentelemetry/sdk/logs/export/simple_log_record_processor_test.rb new file mode 100644 index 0000000000..b7a1fa0927 --- /dev/null +++ b/logs_sdk/test/opentelemetry/sdk/logs/export/simple_log_record_processor_test.rb @@ -0,0 +1,134 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'test_helper' + +describe OpenTelemetry::SDK::Logs::Export::SimpleLogRecordProcessor do + let(:exporter) { OpenTelemetry::SDK::Logs::Export::LogRecordExporter.new } + let(:processor) { OpenTelemetry::SDK::Logs::Export::SimpleLogRecordProcessor.new(exporter) } + let(:log_record) { OpenTelemetry::SDK::Logs::LogRecord.new } + let(:mock_context) { Minitest::Mock.new } + + describe '#initialize' do + it 'raises an error when exporter is invalid' do + OpenTelemetry::Common::Utilities.stub(:valid_exporter?, false) do + assert_raises(ArgumentError) { OpenTelemetry::SDK::Logs::Export::SimpleLogRecordProcessor.new(exporter) } + end + end + end + + describe '#on_emit' do + it 'exports the log records' do + mock_exporter = Minitest::Mock.new + processor.instance_variable_set(:@log_record_exporter, mock_exporter) + mock_log_record_data = Minitest::Mock.new + + log_record.stub(:to_log_record_data, mock_log_record_data) do + OpenTelemetry::Common::Utilities.stub(:valid_exporter?, true) do + mock_exporter.expect(:export, OpenTelemetry::SDK::Logs::Export::SUCCESS, [[mock_log_record_data]]) + processor.on_emit(log_record, mock_context) + mock_exporter.verify + end + end + end + + it 'does not export if stopped' do + processor.instance_variable_set(:@stopped, true) + # raise if export is invoked + exporter.stub(:export, ->(_) { raise 'whoops!' }) do + processor.on_emit(log_record, mock_context) + end + end + + it 'does not export if log_record is nil' do + # raise if export is invoked + exporter.stub(:export, ->(_) { raise 'whoops!' }) do + processor.on_emit(nil, mock_context) + end + end + + it 'does not raise if exporter is nil' do + processor.instance_variable_set(:@log_record_exporter, nil) + processor.on_emit(log_record, mock_context) + end + + it 'catches and logs exporter errors' do + error_message = 'uh oh' + logger_mock = Minitest::Mock.new + logger_mock.expect(:error, nil, [/#{error_message}/]) + # raise if exporter's emit call is invoked + OpenTelemetry.stub(:logger, logger_mock) do + exporter.stub(:export, ->(_) { raise error_message }) do + processor.on_emit(log_record, mock_context) + end + end + + logger_mock.verify + end + end + + describe '#force_flush' do + it 'does not attempt to flush if stopped' do + processor.instance_variable_set(:@stopped, true) + # raise if export is invoked + exporter.stub(:force_flush, ->(_) { raise 'whoops!' }) do + processor.force_flush + end + end + + it 'returns success when the exporter cannot be found' do + processor.instance_variable_set(:@log_record_exporter, nil) + assert_equal(OpenTelemetry::SDK::Logs::Export::SUCCESS, processor.force_flush) + end + + it 'calls #force_flush on the exporter' do + exporter = Minitest::Mock.new + processor.instance_variable_set(:@log_record_exporter, exporter) + exporter.expect(:force_flush, nil, timeout: nil) + processor.force_flush + exporter.verify + end + end + + describe '#shutdown' do + it 'does not attempt to shutdown if stopped' do + processor.instance_variable_set(:@stopped, true) + # raise if export is invoked + exporter.stub(:shutdown, ->(_) { raise 'whoops!' }) do + processor.shutdown + end + end + + describe 'when exporter is nil' do + it 'returns success' do + processor.instance_variable_set(:@log_record_exporter, nil) + assert_equal(OpenTelemetry::SDK::Logs::Export::SUCCESS, processor.shutdown) + end + + it 'sets stopped to true' do + processor.instance_variable_set(:@log_record_exporter, nil) + processor.shutdown + assert(processor.instance_variable_get(:@stopped)) + end + end + + it 'calls shutdown on the exporter' do + exporter = Minitest::Mock.new + processor.instance_variable_set(:@log_record_exporter, exporter) + exporter.expect(:shutdown, nil, timeout: nil) + processor.shutdown + exporter.verify + end + + it 'sets stopped to true after calling shutdown on the exporter' do + exporter = Minitest::Mock.new + processor.instance_variable_set(:@log_record_exporter, exporter) + exporter.expect(:shutdown, nil, timeout: nil) + processor.shutdown + assert(processor.instance_variable_get(:@stopped)) + end + end +end From 2f0f5917194e5362be6126317bf75284a87c0ccc Mon Sep 17 00:00:00 2001 From: Kayla Reopelle <87386821+kaylareopelle@users.noreply.github.com> Date: Fri, 23 Aug 2024 15:53:40 -0700 Subject: [PATCH 16/20] chore: Update ruby/setup-ruby GHA (#1675) * chore: Update ruby/setup-ruby GHA * chore: Bump bundler version to 2.5.17 2.5.11 has a known truffleruby problem --------- Co-authored-by: Matthew Wear --- .github/actions/test_gem/action.yml | 8 ++++---- .github/workflows/release-hook-on-closed.yml | 2 +- .github/workflows/release-hook-on-push.yml | 2 +- .github/workflows/release-perform.yml | 2 +- .github/workflows/release-request.yml | 2 +- .github/workflows/release-retry.yml | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/actions/test_gem/action.yml b/.github/actions/test_gem/action.yml index aa6486e360..009270fd81 100644 --- a/.github/actions/test_gem/action.yml +++ b/.github/actions/test_gem/action.yml @@ -58,21 +58,21 @@ runs: # ...but not for appraisals, sadly. - name: Install Ruby ${{ inputs.ruby }} with dependencies if: "${{ steps.setup.outputs.appraisals == 'false' }}" - uses: ruby/setup-ruby@v1.179.0 + uses: ruby/setup-ruby@v1.190.0 with: ruby-version: "${{ inputs.ruby }}" working-directory: "${{ steps.setup.outputs.gem_dir }}" - bundler: "2.5.10" + bundler: "2.5.17" bundler-cache: true cache-version: "v1-${{ steps.setup.outputs.cache_key }}" # If we're using appraisals, do it all manually. - name: Install Ruby ${{ inputs.ruby }} without dependencies if: "${{ steps.setup.outputs.appraisals == 'true' }}" - uses: ruby/setup-ruby@v1.179.0 + uses: ruby/setup-ruby@v1.190.0 with: ruby-version: "${{ inputs.ruby }}" - bundler: "2.5.10" + bundler: "2.5.17" working-directory: "${{ steps.setup.outputs.gem_dir }}" - name: Install dependencies and generate appraisals if: "${{ steps.setup.outputs.appraisals == 'true' }}" diff --git a/.github/workflows/release-hook-on-closed.yml b/.github/workflows/release-hook-on-closed.yml index 8e15e2124b..9b125d3f3f 100644 --- a/.github/workflows/release-hook-on-closed.yml +++ b/.github/workflows/release-hook-on-closed.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Install Ruby ${{ env.ruby_version }} - uses: ruby/setup-ruby@v1.179.0 + uses: ruby/setup-ruby@v1.190.0 with: ruby-version: ${{ env.ruby_version }} - name: Checkout repo diff --git a/.github/workflows/release-hook-on-push.yml b/.github/workflows/release-hook-on-push.yml index 00cf10864c..5afad6cdc3 100644 --- a/.github/workflows/release-hook-on-push.yml +++ b/.github/workflows/release-hook-on-push.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Install Ruby ${{ env.ruby_version }} - uses: ruby/setup-ruby@v1.179.0 + uses: ruby/setup-ruby@v1.190.0 with: ruby-version: ${{ env.ruby_version }} - name: Checkout repo diff --git a/.github/workflows/release-perform.yml b/.github/workflows/release-perform.yml index 49aa216584..a4ac5d06b5 100644 --- a/.github/workflows/release-perform.yml +++ b/.github/workflows/release-perform.yml @@ -22,7 +22,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Install Ruby ${{ env.ruby_version }} - uses: ruby/setup-ruby@v1.179.0 + uses: ruby/setup-ruby@v1.190.0 with: ruby-version: ${{ env.ruby_version }} - name: Checkout repo diff --git a/.github/workflows/release-request.yml b/.github/workflows/release-request.yml index cd10d07fba..1b7d21f60f 100644 --- a/.github/workflows/release-request.yml +++ b/.github/workflows/release-request.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Install Ruby ${{ env.ruby_version }} - uses: ruby/setup-ruby@v1.179.0 + uses: ruby/setup-ruby@v1.190.0 with: ruby-version: ${{ env.ruby_version }} - name: Checkout repo diff --git a/.github/workflows/release-retry.yml b/.github/workflows/release-retry.yml index dc2f940691..1e67aed366 100644 --- a/.github/workflows/release-retry.yml +++ b/.github/workflows/release-retry.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Install Ruby ${{ env.ruby_version }} - uses: ruby/setup-ruby@v1.179.0 + uses: ruby/setup-ruby@v1.190.0 with: ruby-version: ${{ env.ruby_version }} - name: Checkout repo From bf3ce24cc4fbf133e424b0bc09914a716c0aee4a Mon Sep 17 00:00:00 2001 From: Kayla Reopelle <87386821+kaylareopelle@users.noreply.github.com> Date: Fri, 23 Aug 2024 16:30:13 -0700 Subject: [PATCH 17/20] chore: Update rubocop to ~> 1.65 (#1676) * chore: Update rubocop to ~> 1.65 * chore: Update add_metric_reader to use each_value Rubocop Style/HashEachMethods update --------- Co-authored-by: Matthew Wear --- api/opentelemetry-api.gemspec | 2 +- common/opentelemetry-common.gemspec | 2 +- exporter/jaeger/opentelemetry-exporter-jaeger.gemspec | 2 +- exporter/otlp-common/opentelemetry-exporter-otlp-common.gemspec | 2 +- exporter/otlp-grpc/opentelemetry-exporter-otlp-grpc.gemspec | 2 +- exporter/otlp-http/opentelemetry-exporter-otlp-http.gemspec | 2 +- .../otlp-metrics/opentelemetry-exporter-otlp-metrics.gemspec | 2 +- exporter/otlp/opentelemetry-exporter-otlp.gemspec | 2 +- exporter/zipkin/opentelemetry-exporter-zipkin.gemspec | 2 +- logs_api/opentelemetry-logs-api.gemspec | 2 +- logs_sdk/opentelemetry-logs-sdk.gemspec | 2 +- metrics_api/opentelemetry-metrics-api.gemspec | 2 +- metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb | 2 +- metrics_sdk/opentelemetry-metrics-sdk.gemspec | 2 +- propagator/b3/opentelemetry-propagator-b3.gemspec | 2 +- propagator/jaeger/opentelemetry-propagator-jaeger.gemspec | 2 +- registry/opentelemetry-registry.gemspec | 2 +- sdk/opentelemetry-sdk.gemspec | 2 +- sdk_experimental/opentelemetry-sdk-experimental.gemspec | 2 +- semantic_conventions/opentelemetry-semantic_conventions.gemspec | 2 +- test_helpers/opentelemetry-test-helpers.gemspec | 2 +- 21 files changed, 21 insertions(+), 21 deletions(-) diff --git a/api/opentelemetry-api.gemspec b/api/opentelemetry-api.gemspec index 12e2a2558e..f18bfeec02 100644 --- a/api/opentelemetry-api.gemspec +++ b/api/opentelemetry-api.gemspec @@ -31,7 +31,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'minitest', '~> 5.0' spec.add_development_dependency 'opentelemetry-test-helpers' spec.add_development_dependency 'rake', '~> 12.0' - spec.add_development_dependency 'rubocop', '~> 1.30' + spec.add_development_dependency 'rubocop', '~> 1.65' spec.add_development_dependency 'simplecov', '~> 0.17' spec.add_development_dependency 'yard', '~> 0.9' spec.add_development_dependency 'yard-doctest', '~> 0.1.6' diff --git a/common/opentelemetry-common.gemspec b/common/opentelemetry-common.gemspec index 164e87e01d..fbc2837d16 100644 --- a/common/opentelemetry-common.gemspec +++ b/common/opentelemetry-common.gemspec @@ -31,7 +31,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'minitest', '~> 5.0' spec.add_development_dependency 'opentelemetry-test-helpers' spec.add_development_dependency 'rake', '~> 12.0' - spec.add_development_dependency 'rubocop', '~> 1.3' + spec.add_development_dependency 'rubocop', '~> 1.65' spec.add_development_dependency 'simplecov', '~> 0.17' spec.add_development_dependency 'yard', '~> 0.9' spec.add_development_dependency 'yard-doctest', '~> 0.1.6' diff --git a/exporter/jaeger/opentelemetry-exporter-jaeger.gemspec b/exporter/jaeger/opentelemetry-exporter-jaeger.gemspec index 61656ebb59..6b043a91f4 100644 --- a/exporter/jaeger/opentelemetry-exporter-jaeger.gemspec +++ b/exporter/jaeger/opentelemetry-exporter-jaeger.gemspec @@ -38,7 +38,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'opentelemetry-test-helpers' spec.add_development_dependency 'rake', '~> 12.0' spec.add_development_dependency 'rspec-mocks' - spec.add_development_dependency 'rubocop', '~> 1.3' + spec.add_development_dependency 'rubocop', '~> 1.65' spec.add_development_dependency 'simplecov', '~> 0.17' spec.add_development_dependency 'webmock', '~> 3.7.6' spec.add_development_dependency 'yard', '~> 0.9' diff --git a/exporter/otlp-common/opentelemetry-exporter-otlp-common.gemspec b/exporter/otlp-common/opentelemetry-exporter-otlp-common.gemspec index dda7839b93..23ac0dd50a 100644 --- a/exporter/otlp-common/opentelemetry-exporter-otlp-common.gemspec +++ b/exporter/otlp-common/opentelemetry-exporter-otlp-common.gemspec @@ -37,7 +37,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'pry' spec.add_development_dependency 'pry-byebug' unless RUBY_ENGINE == 'jruby' spec.add_development_dependency 'rake', '~> 12.0' - spec.add_development_dependency 'rubocop', '~> 1.51.0' + spec.add_development_dependency 'rubocop', '~> 1.65' spec.add_development_dependency 'simplecov', '~> 0.17' spec.add_development_dependency 'webmock', '~> 3.7.6' spec.add_development_dependency 'yard', '~> 0.9' diff --git a/exporter/otlp-grpc/opentelemetry-exporter-otlp-grpc.gemspec b/exporter/otlp-grpc/opentelemetry-exporter-otlp-grpc.gemspec index beb9aa7f73..0a1c789cf4 100644 --- a/exporter/otlp-grpc/opentelemetry-exporter-otlp-grpc.gemspec +++ b/exporter/otlp-grpc/opentelemetry-exporter-otlp-grpc.gemspec @@ -37,7 +37,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'opentelemetry-test-helpers' spec.add_development_dependency 'pry-byebug' unless RUBY_ENGINE == 'jruby' spec.add_development_dependency 'rake', '~> 12.0' - spec.add_development_dependency 'rubocop', '~> 1.51.0' + spec.add_development_dependency 'rubocop', '~> 1.65' spec.add_development_dependency 'simplecov', '~> 0.17' spec.add_development_dependency 'webmock', '~> 3.7.6' spec.add_development_dependency 'yard', '~> 0.9' diff --git a/exporter/otlp-http/opentelemetry-exporter-otlp-http.gemspec b/exporter/otlp-http/opentelemetry-exporter-otlp-http.gemspec index 26e7cb013a..7d12b22337 100644 --- a/exporter/otlp-http/opentelemetry-exporter-otlp-http.gemspec +++ b/exporter/otlp-http/opentelemetry-exporter-otlp-http.gemspec @@ -36,7 +36,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'opentelemetry-test-helpers' spec.add_development_dependency 'pry-byebug' unless RUBY_ENGINE == 'jruby' spec.add_development_dependency 'rake', '~> 12.0' - spec.add_development_dependency 'rubocop', '~> 1.51.0' + spec.add_development_dependency 'rubocop', '~> 1.65' spec.add_development_dependency 'simplecov', '~> 0.17' spec.add_development_dependency 'webmock', '~> 3.7.6' spec.add_development_dependency 'yard', '~> 0.9' diff --git a/exporter/otlp-metrics/opentelemetry-exporter-otlp-metrics.gemspec b/exporter/otlp-metrics/opentelemetry-exporter-otlp-metrics.gemspec index cd4ee258e0..571b3b203e 100644 --- a/exporter/otlp-metrics/opentelemetry-exporter-otlp-metrics.gemspec +++ b/exporter/otlp-metrics/opentelemetry-exporter-otlp-metrics.gemspec @@ -41,7 +41,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'opentelemetry-test-helpers' spec.add_development_dependency 'pry-byebug' unless RUBY_ENGINE == 'jruby' spec.add_development_dependency 'rake', '~> 12.0' - spec.add_development_dependency 'rubocop', '~> 1.3' + spec.add_development_dependency 'rubocop', '~> 1.65' spec.add_development_dependency 'simplecov', '~> 0.17' spec.add_development_dependency 'webmock', '~> 3.7.6' spec.add_development_dependency 'yard', '~> 0.9' diff --git a/exporter/otlp/opentelemetry-exporter-otlp.gemspec b/exporter/otlp/opentelemetry-exporter-otlp.gemspec index 3b79b3a0bd..0371621bbd 100644 --- a/exporter/otlp/opentelemetry-exporter-otlp.gemspec +++ b/exporter/otlp/opentelemetry-exporter-otlp.gemspec @@ -39,7 +39,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'opentelemetry-test-helpers' spec.add_development_dependency 'pry-byebug' unless RUBY_ENGINE == 'jruby' spec.add_development_dependency 'rake', '~> 12.0' - spec.add_development_dependency 'rubocop', '~> 1.3' + spec.add_development_dependency 'rubocop', '~> 1.65' spec.add_development_dependency 'simplecov', '~> 0.17' spec.add_development_dependency 'webmock', '~> 3.7.6' spec.add_development_dependency 'yard', '~> 0.9' diff --git a/exporter/zipkin/opentelemetry-exporter-zipkin.gemspec b/exporter/zipkin/opentelemetry-exporter-zipkin.gemspec index bbad42c52c..40d86cde57 100644 --- a/exporter/zipkin/opentelemetry-exporter-zipkin.gemspec +++ b/exporter/zipkin/opentelemetry-exporter-zipkin.gemspec @@ -37,7 +37,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'minitest', '~> 5.0' spec.add_development_dependency 'opentelemetry-test-helpers' spec.add_development_dependency 'rake', '~> 12.0' - spec.add_development_dependency 'rubocop', '~> 1.51.0' + spec.add_development_dependency 'rubocop', '~> 1.65' spec.add_development_dependency 'simplecov', '~> 0.17' spec.add_development_dependency 'webmock', '~> 3.7.6' spec.add_development_dependency 'yard', '~> 0.9' diff --git a/logs_api/opentelemetry-logs-api.gemspec b/logs_api/opentelemetry-logs-api.gemspec index 36b1729119..0426ff8759 100644 --- a/logs_api/opentelemetry-logs-api.gemspec +++ b/logs_api/opentelemetry-logs-api.gemspec @@ -29,7 +29,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'bundler', '>= 1.17' spec.add_development_dependency 'minitest', '~> 5.19' spec.add_development_dependency 'rake', '~> 12.0' - spec.add_development_dependency 'rubocop', '~> 1.55' + spec.add_development_dependency 'rubocop', '~> 1.65' spec.add_development_dependency 'simplecov', '~> 0.22' spec.add_development_dependency 'yard', '~> 0.9' spec.add_development_dependency 'yard-doctest', '~> 0.1' diff --git a/logs_sdk/opentelemetry-logs-sdk.gemspec b/logs_sdk/opentelemetry-logs-sdk.gemspec index 29910cb296..766cf6be5d 100644 --- a/logs_sdk/opentelemetry-logs-sdk.gemspec +++ b/logs_sdk/opentelemetry-logs-sdk.gemspec @@ -32,7 +32,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'minitest', '~> 5.19' spec.add_development_dependency 'opentelemetry-test-helpers', '~> 0.4' spec.add_development_dependency 'rake', '~> 13.0' - spec.add_development_dependency 'rubocop', '~> 1.56' + spec.add_development_dependency 'rubocop', '~> 1.65' spec.add_development_dependency 'simplecov', '~> 0.22' spec.add_development_dependency 'yard', '~> 0.9' spec.add_development_dependency 'yard-doctest', '~> 0.1.17' diff --git a/metrics_api/opentelemetry-metrics-api.gemspec b/metrics_api/opentelemetry-metrics-api.gemspec index e08b9bc3c9..ea541bc429 100644 --- a/metrics_api/opentelemetry-metrics-api.gemspec +++ b/metrics_api/opentelemetry-metrics-api.gemspec @@ -32,7 +32,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'minitest', '~> 5.0' spec.add_development_dependency 'opentelemetry-test-helpers', '~> 0.3.0' spec.add_development_dependency 'rake', '~> 12.0' - spec.add_development_dependency 'rubocop', '~> 1.51.0' + spec.add_development_dependency 'rubocop', '~> 1.65' spec.add_development_dependency 'simplecov', '~> 0.17' spec.add_development_dependency 'yard', '~> 0.9' spec.add_development_dependency 'yard-doctest', '~> 0.1.6' diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb index faf8e9adb1..8db3ea992c 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb @@ -28,7 +28,7 @@ def initialize(name, version, meter_provider) # @api private def add_metric_reader(metric_reader) - @instrument_registry.each do |_n, instrument| + @instrument_registry.each_value do |instrument| instrument.register_with_new_metric_store(metric_reader.metric_store) end end diff --git a/metrics_sdk/opentelemetry-metrics-sdk.gemspec b/metrics_sdk/opentelemetry-metrics-sdk.gemspec index c82bdf5977..f7459963e2 100644 --- a/metrics_sdk/opentelemetry-metrics-sdk.gemspec +++ b/metrics_sdk/opentelemetry-metrics-sdk.gemspec @@ -35,7 +35,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'minitest', '~> 5.0' spec.add_development_dependency 'opentelemetry-test-helpers' spec.add_development_dependency 'rake', '~> 12.0' - spec.add_development_dependency 'rubocop', '~> 1.51.0' + spec.add_development_dependency 'rubocop', '~> 1.65' spec.add_development_dependency 'simplecov', '~> 0.17' spec.add_development_dependency 'yard', '~> 0.9' spec.add_development_dependency 'yard-doctest', '~> 0.1.6' diff --git a/propagator/b3/opentelemetry-propagator-b3.gemspec b/propagator/b3/opentelemetry-propagator-b3.gemspec index 46137babb4..3a75351735 100644 --- a/propagator/b3/opentelemetry-propagator-b3.gemspec +++ b/propagator/b3/opentelemetry-propagator-b3.gemspec @@ -31,7 +31,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'bundler', '>= 1.17' spec.add_development_dependency 'minitest', '~> 5.0' spec.add_development_dependency 'rake', '~> 12.0' - spec.add_development_dependency 'rubocop', '~> 1.51.0' + spec.add_development_dependency 'rubocop', '~> 1.65' spec.add_development_dependency 'simplecov', '~> 0.17.1' spec.add_development_dependency 'yard', '~> 0.9' spec.add_development_dependency 'yard-doctest', '~> 0.1.6' diff --git a/propagator/jaeger/opentelemetry-propagator-jaeger.gemspec b/propagator/jaeger/opentelemetry-propagator-jaeger.gemspec index aada4f9e8d..4016ede97d 100644 --- a/propagator/jaeger/opentelemetry-propagator-jaeger.gemspec +++ b/propagator/jaeger/opentelemetry-propagator-jaeger.gemspec @@ -32,7 +32,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'minitest', '~> 5.0' spec.add_development_dependency 'opentelemetry-sdk', '~> 1.2' spec.add_development_dependency 'rake', '~> 12.0' - spec.add_development_dependency 'rubocop', '~> 1.51.0' + spec.add_development_dependency 'rubocop', '~> 1.65' spec.add_development_dependency 'simplecov', '~> 0.17.1' spec.add_development_dependency 'yard', '~> 0.9' spec.add_development_dependency 'yard-doctest', '~> 0.1.6' diff --git a/registry/opentelemetry-registry.gemspec b/registry/opentelemetry-registry.gemspec index 510d12ceb9..9b9371e736 100644 --- a/registry/opentelemetry-registry.gemspec +++ b/registry/opentelemetry-registry.gemspec @@ -32,7 +32,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'minitest', '~> 5.0' spec.add_development_dependency 'rake', '~> 12.3.3' spec.add_development_dependency 'rspec-mocks' - spec.add_development_dependency 'rubocop', '~> 1.3' + spec.add_development_dependency 'rubocop', '~> 1.65' spec.add_development_dependency 'simplecov', '~> 0.17.1' spec.add_development_dependency 'yard', '~> 0.9' spec.add_development_dependency 'yard-doctest', '~> 0.1.6' diff --git a/sdk/opentelemetry-sdk.gemspec b/sdk/opentelemetry-sdk.gemspec index de7edca8a4..decb49270f 100644 --- a/sdk/opentelemetry-sdk.gemspec +++ b/sdk/opentelemetry-sdk.gemspec @@ -43,7 +43,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'opentelemetry-test-helpers' spec.add_development_dependency 'pry' spec.add_development_dependency 'rake', '~> 12.0' - spec.add_development_dependency 'rubocop', '~> 1.51.0' + spec.add_development_dependency 'rubocop', '~> 1.65' spec.add_development_dependency 'simplecov', '~> 0.17' spec.add_development_dependency 'yard', '~> 0.9' spec.add_development_dependency 'yard-doctest', '~> 0.1.6' diff --git a/sdk_experimental/opentelemetry-sdk-experimental.gemspec b/sdk_experimental/opentelemetry-sdk-experimental.gemspec index a9fa3dd5f4..647d71ee2e 100644 --- a/sdk_experimental/opentelemetry-sdk-experimental.gemspec +++ b/sdk_experimental/opentelemetry-sdk-experimental.gemspec @@ -34,7 +34,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'minitest', '~> 5.0' spec.add_development_dependency 'opentelemetry-test-helpers' spec.add_development_dependency 'rake', '~> 12.0' - spec.add_development_dependency 'rubocop', '~> 1.51.0' + spec.add_development_dependency 'rubocop', '~> 1.65' spec.add_development_dependency 'simplecov', '~> 0.17' spec.add_development_dependency 'yard', '~> 0.9' spec.add_development_dependency 'yard-doctest', '~> 0.1.6' diff --git a/semantic_conventions/opentelemetry-semantic_conventions.gemspec b/semantic_conventions/opentelemetry-semantic_conventions.gemspec index e09b108c86..2eae221991 100644 --- a/semantic_conventions/opentelemetry-semantic_conventions.gemspec +++ b/semantic_conventions/opentelemetry-semantic_conventions.gemspec @@ -30,7 +30,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'bundler', '>= 1.17' spec.add_development_dependency 'minitest', '~> 5.0' spec.add_development_dependency 'rake', '~> 12.0' - spec.add_development_dependency 'rubocop', '~> 1.3' + spec.add_development_dependency 'rubocop', '~> 1.65' spec.add_development_dependency 'simplecov', '~> 0.17' spec.add_development_dependency 'yard', '~> 0.9' spec.add_development_dependency 'yard-doctest', '~> 0.1.6' diff --git a/test_helpers/opentelemetry-test-helpers.gemspec b/test_helpers/opentelemetry-test-helpers.gemspec index 5aff33cada..faab90233b 100644 --- a/test_helpers/opentelemetry-test-helpers.gemspec +++ b/test_helpers/opentelemetry-test-helpers.gemspec @@ -31,7 +31,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'pry' spec.add_development_dependency 'pry-byebug' unless RUBY_ENGINE == 'jruby' spec.add_development_dependency 'rake', '~> 12.0' - spec.add_development_dependency 'rubocop', '~> 1.3' + spec.add_development_dependency 'rubocop', '~> 1.65' spec.add_development_dependency 'simplecov', '~> 0.17' spec.add_development_dependency 'yard', '~> 0.9' spec.add_development_dependency 'yard-doctest', '~> 0.1.6' From 1fef415c071e293c90ed3eb027e9a3be55cfe979 Mon Sep 17 00:00:00 2001 From: Kayla Reopelle <87386821+kaylareopelle@users.noreply.github.com> Date: Fri, 23 Aug 2024 16:45:08 -0700 Subject: [PATCH 18/20] ci: Add weekly release request (#1677) opentelemetry-ruby-contrib uses this workflow to open a PR to release any gems with changes just before the SIG meeting. This may help keep core releases up to date with the repository. Co-authored-by: Matthew Wear --- .github/workflows/release-request-weekly.yml | 28 ++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .github/workflows/release-request-weekly.yml diff --git a/.github/workflows/release-request-weekly.yml b/.github/workflows/release-request-weekly.yml new file mode 100644 index 0000000000..20a80a562d --- /dev/null +++ b/.github/workflows/release-request-weekly.yml @@ -0,0 +1,28 @@ +name: Open release request - Weekly + +on: + schedule: + - cron: "0 15 * * 2" + +jobs: + release-request: + if: ${{ github.repository == 'open-telemetry/opentelemetry-ruby' }} + env: + ruby_version: "3.0" + runs-on: ubuntu-latest + steps: + - name: Install Ruby ${{ env.ruby_version }} + uses: ruby/setup-ruby@v1.190.0 + with: + ruby-version: ${{ env.ruby_version }} + - name: Checkout repo + uses: actions/checkout@v4 + - name: Install Toys + run: "gem install --no-document toys -v 0.15.5" + - name: Open release pull request + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + toys release request --yes --verbose \ + "--release-ref=${{ github.ref }}" \ + < /dev/null From 21717b5d4d676a6ffd23a0f6c333d19f5dfd9656 Mon Sep 17 00:00:00 2001 From: Kayla Reopelle <87386821+kaylareopelle@users.noreply.github.com> Date: Fri, 23 Aug 2024 16:58:00 -0700 Subject: [PATCH 19/20] ci: Cancel workflows in progress on new push to PR (#1679) This ensures only one instance of the workflow is running per PR by canceling any previous runs of the workflow. Co-authored-by: Matthew Wear --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8acd5270ca..1eadcc02c4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,6 +9,10 @@ on: branches: - main +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number }} # Ensure that only one instance of this workflow is running per Pull Request + cancel-in-progress: true # Cancel any previous runs of this workflow + jobs: base: strategy: From a217e9aec8ce9df2baa92ecff224bf67098268cf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 28 Aug 2024 11:52:18 -0700 Subject: [PATCH 20/20] release: Release 4 gems (#1692) * opentelemetry-api 1.4.0 (was 1.3.0) * opentelemetry-exporter-otlp 0.29.0 (was 0.28.1) * opentelemetry-exporter-otlp-metrics 0.1.0 (initial release) * opentelemetry-metrics-sdk 0.2.0 (was 0.1.0) Co-authored-by: Daniel Azuma --- api/CHANGELOG.md | 4 ++++ api/lib/opentelemetry/version.rb | 2 +- exporter/otlp-metrics/CHANGELOG.md | 4 ++++ exporter/otlp/CHANGELOG.md | 4 ++++ exporter/otlp/lib/opentelemetry/exporter/otlp/version.rb | 2 +- metrics_sdk/CHANGELOG.md | 4 ++++ metrics_sdk/lib/opentelemetry/sdk/metrics/version.rb | 2 +- 7 files changed, 19 insertions(+), 3 deletions(-) diff --git a/api/CHANGELOG.md b/api/CHANGELOG.md index 35c3e87ad9..1cb9acd401 100644 --- a/api/CHANGELOG.md +++ b/api/CHANGELOG.md @@ -1,5 +1,9 @@ # Release History: opentelemetry-api +### v1.4.0 / 2024-08-27 + +* ADDED: Include backtrace first line for better debug info + ### v1.3.0 / 2024-07-24 * ADDED: Add add_link to span api/sdk diff --git a/api/lib/opentelemetry/version.rb b/api/lib/opentelemetry/version.rb index 009daf16ba..86295b3a96 100644 --- a/api/lib/opentelemetry/version.rb +++ b/api/lib/opentelemetry/version.rb @@ -6,5 +6,5 @@ module OpenTelemetry ## Current OpenTelemetry version - VERSION = '1.3.0' + VERSION = '1.4.0' end diff --git a/exporter/otlp-metrics/CHANGELOG.md b/exporter/otlp-metrics/CHANGELOG.md index 94a2c19729..b42812d565 100644 --- a/exporter/otlp-metrics/CHANGELOG.md +++ b/exporter/otlp-metrics/CHANGELOG.md @@ -1 +1,5 @@ # Release History: opentelemetry-exporter-otlp-metrics + +### v0.1.0 / 2024-08-27 + +Initial release. diff --git a/exporter/otlp/CHANGELOG.md b/exporter/otlp/CHANGELOG.md index 88ee03a7dc..bd86c03135 100644 --- a/exporter/otlp/CHANGELOG.md +++ b/exporter/otlp/CHANGELOG.md @@ -1,5 +1,9 @@ # Release History: opentelemetry-exporter-otlp +### v0.29.0 / 2024-08-27 + +* ADDED: Add support for mutual TLS. + ### v0.28.1 / 2024-07-24 * ADDED: Improve SSL error logging. diff --git a/exporter/otlp/lib/opentelemetry/exporter/otlp/version.rb b/exporter/otlp/lib/opentelemetry/exporter/otlp/version.rb index b12e7e7de2..fd37180664 100644 --- a/exporter/otlp/lib/opentelemetry/exporter/otlp/version.rb +++ b/exporter/otlp/lib/opentelemetry/exporter/otlp/version.rb @@ -8,7 +8,7 @@ module OpenTelemetry module Exporter module OTLP ## Current OpenTelemetry OTLP exporter version - VERSION = '0.28.1' + VERSION = '0.29.0' end end end diff --git a/metrics_sdk/CHANGELOG.md b/metrics_sdk/CHANGELOG.md index a1385c5c6b..1328411e29 100644 --- a/metrics_sdk/CHANGELOG.md +++ b/metrics_sdk/CHANGELOG.md @@ -1,5 +1,9 @@ # Release History: opentelemetry-metrics-sdk +### v0.2.0 / 2024-08-27 + +* ADDED: Add basic periodic exporting metric_reader + ### v0.1.0 / 2024-07-31 Initial release. diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/version.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/version.rb index 41bd724c7a..4016895fb7 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/version.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/version.rb @@ -8,7 +8,7 @@ module OpenTelemetry module SDK module Metrics # Current OpenTelemetry metrics sdk version - VERSION = '0.1.0' + VERSION = '0.2.0' end end end