From 935ef1e605314b1388cfdaca6cb1ce46d8a30653 Mon Sep 17 00:00:00 2001 From: Robert Laurin Date: Thu, 27 Jan 2022 11:04:28 -0500 Subject: [PATCH 01/25] WIP: Metrics SDK implementation --- metrics_api/lib/opentelemetry-metrics-api.rb | 4 + metrics_sdk/.rubocop.yml | 22 ++ metrics_sdk/.yardopts | 9 + metrics_sdk/CHANGELOG.md | 1 + metrics_sdk/Gemfile | 17 ++ metrics_sdk/LICENSE | 201 ++++++++++++++++++ metrics_sdk/Rakefile | 28 +++ metrics_sdk/lib/opentelemetry-metrics-sdk.rb | 7 + metrics_sdk/lib/opentelemetry/metrics_sdk.rb | 32 +++ .../opentelemetry/metrics_sdk/configurator.rb | 22 ++ .../metrics_sdk/instrumentation_library.rb | 13 ++ .../lib/opentelemetry/metrics_sdk/metrics.rb | 18 ++ .../metrics_sdk/metrics/instrument.rb | 16 ++ .../metrics_sdk/metrics/instrument/counter.rb | 26 +++ .../metrics_sdk/metrics/meter.rb | 35 +++ .../metrics_sdk/metrics/meter_provider.rb | 37 ++++ .../lib/opentelemetry/metrics_sdk/version.rb | 12 ++ metrics_sdk/opentelemetry-metrics-sdk.gemspec | 47 ++++ metrics_sdk/test/.rubocop.yml | 6 + .../test/opentelemetry/metrics_sdk_test.rb | 46 ++++ metrics_sdk/test/test_helper.rb | 15 ++ 21 files changed, 614 insertions(+) create mode 100644 metrics_sdk/.rubocop.yml create mode 100644 metrics_sdk/.yardopts create mode 100644 metrics_sdk/CHANGELOG.md create mode 100644 metrics_sdk/Gemfile create mode 100644 metrics_sdk/LICENSE create mode 100644 metrics_sdk/Rakefile create mode 100644 metrics_sdk/lib/opentelemetry-metrics-sdk.rb create mode 100644 metrics_sdk/lib/opentelemetry/metrics_sdk.rb create mode 100644 metrics_sdk/lib/opentelemetry/metrics_sdk/configurator.rb create mode 100644 metrics_sdk/lib/opentelemetry/metrics_sdk/instrumentation_library.rb create mode 100644 metrics_sdk/lib/opentelemetry/metrics_sdk/metrics.rb create mode 100644 metrics_sdk/lib/opentelemetry/metrics_sdk/metrics/instrument.rb create mode 100644 metrics_sdk/lib/opentelemetry/metrics_sdk/metrics/instrument/counter.rb create mode 100644 metrics_sdk/lib/opentelemetry/metrics_sdk/metrics/meter.rb create mode 100644 metrics_sdk/lib/opentelemetry/metrics_sdk/metrics/meter_provider.rb create mode 100644 metrics_sdk/lib/opentelemetry/metrics_sdk/version.rb create mode 100644 metrics_sdk/opentelemetry-metrics-sdk.gemspec create mode 100644 metrics_sdk/test/.rubocop.yml create mode 100644 metrics_sdk/test/opentelemetry/metrics_sdk_test.rb create mode 100644 metrics_sdk/test/test_helper.rb diff --git a/metrics_api/lib/opentelemetry-metrics-api.rb b/metrics_api/lib/opentelemetry-metrics-api.rb index 4fc3d5b65..2f7992787 100644 --- a/metrics_api/lib/opentelemetry-metrics-api.rb +++ b/metrics_api/lib/opentelemetry-metrics-api.rb @@ -17,6 +17,10 @@ # # The OpenTelemetry module provides global accessors for telemetry objects. module OpenTelemetry + + @mutex = Mutex.new + @meter_provider = Internal::ProxyMeterProvider.new + # Register the global meter provider. # # @param [MeterProvider] provider A meter provider to register as the diff --git a/metrics_sdk/.rubocop.yml b/metrics_sdk/.rubocop.yml new file mode 100644 index 000000000..e2c49e785 --- /dev/null +++ b/metrics_sdk/.rubocop.yml @@ -0,0 +1,22 @@ +AllCops: + TargetRubyVersion: "2.5.0" + +Lint/UnusedMethodArgument: + Enabled: false +Metrics/AbcSize: + Max: 30 +Metrics/LineLength: + Enabled: false +Metrics/MethodLength: + Max: 50 +Metrics/PerceivedComplexity: + Max: 30 +Metrics/CyclomaticComplexity: + Max: 20 +Metrics/ParameterLists: + Enabled: false +Naming/FileName: + Exclude: + - "lib/opentelemetry-metrics-sdk.rb" +Style/ModuleFunction: + Enabled: false diff --git a/metrics_sdk/.yardopts b/metrics_sdk/.yardopts new file mode 100644 index 000000000..8c871bc8d --- /dev/null +++ b/metrics_sdk/.yardopts @@ -0,0 +1,9 @@ +--no-private +--title=OpenTelemetry Metrics SDK +--markup=markdown +--main=README.md +./lib/opentelemetry/**/*.rb +./lib/opentelemetry.rb +- +README.md +CHANGELOG.md diff --git a/metrics_sdk/CHANGELOG.md b/metrics_sdk/CHANGELOG.md new file mode 100644 index 000000000..789f15a23 --- /dev/null +++ b/metrics_sdk/CHANGELOG.md @@ -0,0 +1 @@ +# Release History: opentelemetry-metrics-sdk diff --git a/metrics_sdk/Gemfile b/metrics_sdk/Gemfile new file mode 100644 index 000000000..15d63fa36 --- /dev/null +++ b/metrics_sdk/Gemfile @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +source 'https://rubygems.org' + +gemspec + +gem 'opentelemetry-api', path: '../api' +gem 'opentelemetry-metrics-api', path: '../metrics_api' + +group :test, :development do + gem 'pry' + gem 'pry-byebug' unless RUBY_ENGINE == 'jruby' +end diff --git a/metrics_sdk/LICENSE b/metrics_sdk/LICENSE new file mode 100644 index 000000000..1ef7dad2c --- /dev/null +++ b/metrics_sdk/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright The OpenTelemetry Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/metrics_sdk/Rakefile b/metrics_sdk/Rakefile new file mode 100644 index 000000000..99808b5ac --- /dev/null +++ b/metrics_sdk/Rakefile @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'bundler/gem_tasks' +require 'rake/testtask' +require 'yard' + +require 'rubocop/rake_task' +RuboCop::RakeTask.new + +Rake::TestTask.new :test do |t| + t.libs << 'test' + t.libs << 'lib' + t.test_files = FileList['test/**/*_test.rb'] +end + +YARD::Rake::YardocTask.new do |t| + t.stats_options = ['--list-undoc'] +end + +if RUBY_ENGINE == 'truffleruby' + task default: %i[test] +else + task default: %i[test rubocop yard] +end diff --git a/metrics_sdk/lib/opentelemetry-metrics-sdk.rb b/metrics_sdk/lib/opentelemetry-metrics-sdk.rb new file mode 100644 index 000000000..593bf9bac --- /dev/null +++ b/metrics_sdk/lib/opentelemetry-metrics-sdk.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'opentelemetry/metrics_sdk' diff --git a/metrics_sdk/lib/opentelemetry/metrics_sdk.rb b/metrics_sdk/lib/opentelemetry/metrics_sdk.rb new file mode 100644 index 000000000..dfee84d35 --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/metrics_sdk.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'opentelemetry-metrics-api' + +module OpenTelemetry + # MetricsSDK provides the reference implementation of the OpenTelemetry Metrics API. + module MetricsSDK + extend self + + ConfigurationError = Class.new(OpenTelemetry::Error) + + def configure + configurator = Configurator.new + yield configurator if block_given? + configurator.configure + rescue StandardError + begin + raise ConfigurationError + rescue ConfigurationError => e + OpenTelemetry.handle_error(exception: e, message: "unexpected configuration error due to #{e.cause}") + end + end + end +end + +require 'opentelemetry/metrics_sdk/configurator' +require 'opentelemetry/metrics_sdk/instrumentation_library' +require 'opentelemetry/metrics_sdk/metrics' diff --git a/metrics_sdk/lib/opentelemetry/metrics_sdk/configurator.rb b/metrics_sdk/lib/opentelemetry/metrics_sdk/configurator.rb new file mode 100644 index 000000000..5cdbe01dd --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/metrics_sdk/configurator.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module MetricsSDK + # The configurator provides defaults and facilitates configuring the + # SDK for use. + class Configurator + + # @api private + # The configure method is where we define the setup process. This allows + # us to make certain guarantees about which systems and globals are setup + # at each stage. + def configure + OpenTelemetry.meter_provider = Metrics::MeterProvider.new + end + end + end +end diff --git a/metrics_sdk/lib/opentelemetry/metrics_sdk/instrumentation_library.rb b/metrics_sdk/lib/opentelemetry/metrics_sdk/instrumentation_library.rb new file mode 100644 index 000000000..b4549e956 --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/metrics_sdk/instrumentation_library.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module MetricsSDK + # InstrumentationLibrary is a struct containing library information for export. + InstrumentationLibrary = Struct.new(:name, + :version) + end +end diff --git a/metrics_sdk/lib/opentelemetry/metrics_sdk/metrics.rb b/metrics_sdk/lib/opentelemetry/metrics_sdk/metrics.rb new file mode 100644 index 000000000..49c108a46 --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/metrics_sdk/metrics.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module MetricsSDK + # The Metrics module contains the OpenTelemetry metrics reference + # implementation. + module Metrics + end + end +end + +require 'opentelemetry/metrics_sdk/metrics/instrument' +require 'opentelemetry/metrics_sdk/metrics/meter' +require 'opentelemetry/metrics_sdk/metrics/meter_provider' diff --git a/metrics_sdk/lib/opentelemetry/metrics_sdk/metrics/instrument.rb b/metrics_sdk/lib/opentelemetry/metrics_sdk/metrics/instrument.rb new file mode 100644 index 000000000..73116c42b --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/metrics_sdk/metrics/instrument.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module MetricsSDK + # The Instrument module contains the OpenTelemetry instruments reference + # implementation. + module Instrument + end + end +end + +require 'opentelemetry/metrics_sdk/metrics/instrument/counter' diff --git a/metrics_sdk/lib/opentelemetry/metrics_sdk/metrics/instrument/counter.rb b/metrics_sdk/lib/opentelemetry/metrics_sdk/metrics/instrument/counter.rb new file mode 100644 index 000000000..3d8175448 --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/metrics_sdk/metrics/instrument/counter.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module MetricsSDK + module Metrics + module Instrument + # {Counter} is the SDK implementation of {OpenTelemetry::Metrics::Counter}. + class Counter < OpenTelemetry::Metrics::Instrument::Counter + + # Increment the Counter by a fixed amount. + # + # @param [numeric] increment The increment amount, which MUST be a non-negative numeric value. + # @param [Hash{String => String, Numeric, Boolean, Array}] attributes + # Values must be non-nil and (array of) string, boolean or numeric type. + # Array values must not contain nil elements and all elements must be of + # the same basic type (string, numeric, boolean). + def add(increment, attributes: nil); end + end + end + end + end +end diff --git a/metrics_sdk/lib/opentelemetry/metrics_sdk/metrics/meter.rb b/metrics_sdk/lib/opentelemetry/metrics_sdk/metrics/meter.rb new file mode 100644 index 000000000..609664f7f --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/metrics_sdk/metrics/meter.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module MetricsSDK + # The Metrics module contains the OpenTelemetry metrics reference + # implementation. + module Metrics + # {Meter} is the SDK implementation of {OpenTelemetry::Metrics::Meter}. + class Meter < OpenTelemetry::Metrics::Meter + # @api private + # + # Returns a new {Meter} instance. + # + # @param [String] name Instrumentation package name + # @param [String] version Instrumentation package version + # @param [TracerProvider] tracer_provider TracerProvider that initialized the tracer + # + # @return [Tracer] + def initialize(name, version) + @mutex = Mutex.new + @registry = {} + @instrumentation_library = InstrumentationLibrary.new(name, version) + end + + def create_counter(name, unit: nil, description: nil) + create_instrument(:counter, name, unit, description, nil) { OpenTelemetry::MetricsSDK::Metrics::Instrument::Counter.new } + end + end + end + end +end diff --git a/metrics_sdk/lib/opentelemetry/metrics_sdk/metrics/meter_provider.rb b/metrics_sdk/lib/opentelemetry/metrics_sdk/metrics/meter_provider.rb new file mode 100644 index 000000000..9dae383b8 --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/metrics_sdk/metrics/meter_provider.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module MetricsSDK + # The Metrics module contains the OpenTelemetry metrics reference + # implementation. + module Metrics + # {MeterProvider} is the SDK implementation of {OpenTelemetry::Metrics::MeterProvider}. + class MeterProvider < OpenTelemetry::Metrics::MeterProvider + Key = Struct.new(:name, :version) + private_constant(:Key) + + def initialize + @registry = {} + @registry_mutex = Mutex.new + end + + # Returns a {Meter} instance. + # + # @param [optional String] name Instrumentation package name + # @param [optional String] version Instrumentation package version + # + # @return [Meter] + def meter(name = nil, version = nil) + name ||= '' + version ||= '' + OpenTelemetry.logger.warn 'calling MeterProvider#meter without providing a meter name.' if name.empty? + @registry_mutex.synchronize { @registry[Key.new(name, version)] ||= Meter.new(name, version) } + end + end + end + end +end diff --git a/metrics_sdk/lib/opentelemetry/metrics_sdk/version.rb b/metrics_sdk/lib/opentelemetry/metrics_sdk/version.rb new file mode 100644 index 000000000..26c125942 --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/metrics_sdk/version.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module MetricsSDK + # Current OpenTelemetry metrics sdk version + VERSION = '0.0.1' + end +end diff --git a/metrics_sdk/opentelemetry-metrics-sdk.gemspec b/metrics_sdk/opentelemetry-metrics-sdk.gemspec new file mode 100644 index 000000000..cdd56344d --- /dev/null +++ b/metrics_sdk/opentelemetry-metrics-sdk.gemspec @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +lib = File.expand_path('lib', __dir__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) +require 'opentelemetry/metrics_sdk/version' + +Gem::Specification.new do |spec| + spec.name = 'opentelemetry-metrics-sdk' + spec.version = OpenTelemetry::MetricsSDK::VERSION + spec.authors = ['OpenTelemetry Authors'] + spec.email = ['cncf-opentelemetry-contributors@lists.cncf.io'] + + spec.summary = 'A stats collection and distributed tracing framework' + spec.description = 'A stats collection and distributed tracing framework' + spec.homepage = 'https://github.com/open-telemetry/opentelemetry-ruby' + spec.license = 'Apache-2.0' + + spec.files = ::Dir.glob('lib/**/*.rb') + + ::Dir.glob('*.md') + + ['LICENSE', '.yardopts'] + spec.require_paths = ['lib'] + spec.required_ruby_version = '>= 2.5.0' + + spec.add_dependency 'opentelemetry-api' + spec.add_dependency 'opentelemetry-metrics-api' + + spec.add_development_dependency 'benchmark-ipsa', '~> 0.2.0' + spec.add_development_dependency 'bundler', '>= 1.17' + spec.add_development_dependency 'faraday', '~> 0.13' + spec.add_development_dependency 'minitest', '~> 5.0' + spec.add_development_dependency 'rake', '~> 12.0' + spec.add_development_dependency 'rubocop', '~> 0.73.0' + spec.add_development_dependency 'simplecov', '~> 0.17' + spec.add_development_dependency 'yard', '~> 0.9' + 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-metrics-sdk/v#{OpenTelemetry::MetricsSDK::VERSION}/file.CHANGELOG.html" + spec.metadata['source_code_uri'] = 'https://github.com/open-telemetry/opentelemetry-ruby/tree/main/api' + 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-metrics-sdk/v#{OpenTelemetry::MetricsSDK::VERSION}" + end +end diff --git a/metrics_sdk/test/.rubocop.yml b/metrics_sdk/test/.rubocop.yml new file mode 100644 index 000000000..4c8c0d91e --- /dev/null +++ b/metrics_sdk/test/.rubocop.yml @@ -0,0 +1,6 @@ +inherit_from: ../.rubocop.yml + +Metrics/BlockLength: + Enabled: false +Metrics/LineLength: + Enabled: false diff --git a/metrics_sdk/test/opentelemetry/metrics_sdk_test.rb b/metrics_sdk/test/opentelemetry/metrics_sdk_test.rb new file mode 100644 index 000000000..e220912d1 --- /dev/null +++ b/metrics_sdk/test/opentelemetry/metrics_sdk_test.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'test_helper' + +describe OpenTelemetry::MetricsSDK do + describe '#configure' do + after do + # Ensure we don't leak custom loggers and error handlers to other tests + OpenTelemetry.logger = Logger.new(File::NULL) + OpenTelemetry.error_handler = nil + end + + it 'upgrades the API MeterProvider, Meters, and Instruments' do + meter_provider = OpenTelemetry.meter_provider + meter = meter_provider.meter("test") + instrument = meter.create_counter("a_counter") + + OpenTelemetry::MetricsSDK.configure + + _(meter_provider.instance_variable_get(:@delegate)).must_be_instance_of OpenTelemetry::MetricsSDK::Metrics::MeterProvider + _(meter.instance_variable_get(:@delegate)).must_be_instance_of OpenTelemetry::MetricsSDK::Metrics::Meter + _(instrument.instance_variable_get(:@delegate)).must_be_instance_of OpenTelemetry::MetricsSDK::Metrics::Instrument::Counter + end + + it 'sends the original configuration error to the error handler' do + received_exception = nil + received_message = nil + + OpenTelemetry.error_handler = lambda do |exception: nil, message: nil| + received_exception = exception + received_message = message + end + + OpenTelemetry::MetricsSDK.configure do |config| + config.do_something + end + + _(received_exception).must_be_instance_of OpenTelemetry::MetricsSDK::ConfigurationError + _(received_message).must_match(/unexpected configuration error due to undefined method `do_something/) + end + end +end diff --git a/metrics_sdk/test/test_helper.rb b/metrics_sdk/test/test_helper.rb new file mode 100644 index 000000000..3106e91ce --- /dev/null +++ b/metrics_sdk/test/test_helper.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +# require 'simplecov' +# # SimpleCov.start +# # SimpleCov.minimum_coverage 85 + +require 'opentelemetry-metrics-sdk' +require 'minitest/autorun' +require 'pry' + +OpenTelemetry.logger = Logger.new(File::NULL) From ac267b29e4f2994ab2690524303c1de0c90f13a2 Mon Sep 17 00:00:00 2001 From: Robert Laurin Date: Thu, 27 Jan 2022 12:54:31 -0500 Subject: [PATCH 02/25] Extend the existing OpenTelemetry SDK --- metrics_sdk/Gemfile | 1 + metrics_sdk/lib/opentelemetry-metrics-sdk.rb | 5 ++- metrics_sdk/lib/opentelemetry/metrics_sdk.rb | 32 ------------------- .../opentelemetry/metrics_sdk/configurator.rb | 22 ------------- .../metrics_sdk/instrumentation_library.rb | 13 -------- .../{metrics_sdk => sdk}/metrics.rb | 9 +++--- .../sdk/metrics/configuration_patch.rb | 26 +++++++++++++++ .../metrics/instrument.rb | 4 +-- .../metrics/instrument/counter.rb | 2 +- .../{metrics_sdk => sdk}/metrics/meter.rb | 4 +-- .../metrics/meter_provider.rb | 2 +- .../{metrics_sdk => sdk/metrics}/version.rb | 8 +++-- metrics_sdk/opentelemetry-metrics-sdk.gemspec | 11 ++++--- .../test/opentelemetry/metrics_sdk_test.rb | 14 ++++---- sdk/lib/opentelemetry/sdk/configurator.rb | 3 ++ 15 files changed, 63 insertions(+), 93 deletions(-) delete mode 100644 metrics_sdk/lib/opentelemetry/metrics_sdk.rb delete mode 100644 metrics_sdk/lib/opentelemetry/metrics_sdk/configurator.rb delete mode 100644 metrics_sdk/lib/opentelemetry/metrics_sdk/instrumentation_library.rb rename metrics_sdk/lib/opentelemetry/{metrics_sdk => sdk}/metrics.rb (55%) create mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/configuration_patch.rb rename metrics_sdk/lib/opentelemetry/{metrics_sdk => sdk}/metrics/instrument.rb (76%) rename metrics_sdk/lib/opentelemetry/{metrics_sdk => sdk}/metrics/instrument/counter.rb (97%) rename metrics_sdk/lib/opentelemetry/{metrics_sdk => sdk}/metrics/meter.rb (92%) rename metrics_sdk/lib/opentelemetry/{metrics_sdk => sdk}/metrics/meter_provider.rb (98%) rename metrics_sdk/lib/opentelemetry/{metrics_sdk => sdk/metrics}/version.rb (55%) diff --git a/metrics_sdk/Gemfile b/metrics_sdk/Gemfile index 15d63fa36..01b69a3d4 100644 --- a/metrics_sdk/Gemfile +++ b/metrics_sdk/Gemfile @@ -9,6 +9,7 @@ source 'https://rubygems.org' gemspec gem 'opentelemetry-api', path: '../api' +gem 'opentelemetry-sdk', path: '../sdk' gem 'opentelemetry-metrics-api', path: '../metrics_api' group :test, :development do diff --git a/metrics_sdk/lib/opentelemetry-metrics-sdk.rb b/metrics_sdk/lib/opentelemetry-metrics-sdk.rb index 593bf9bac..a64dc3a6d 100644 --- a/metrics_sdk/lib/opentelemetry-metrics-sdk.rb +++ b/metrics_sdk/lib/opentelemetry-metrics-sdk.rb @@ -4,4 +4,7 @@ # # SPDX-License-Identifier: Apache-2.0 -require 'opentelemetry/metrics_sdk' +require 'opentelemetry/sdk' +require 'opentelemetry-metrics-api' +require 'opentelemetry/sdk/metrics' + diff --git a/metrics_sdk/lib/opentelemetry/metrics_sdk.rb b/metrics_sdk/lib/opentelemetry/metrics_sdk.rb deleted file mode 100644 index dfee84d35..000000000 --- a/metrics_sdk/lib/opentelemetry/metrics_sdk.rb +++ /dev/null @@ -1,32 +0,0 @@ -# frozen_string_literal: true - -# Copyright The OpenTelemetry Authors -# -# SPDX-License-Identifier: Apache-2.0 - -require 'opentelemetry-metrics-api' - -module OpenTelemetry - # MetricsSDK provides the reference implementation of the OpenTelemetry Metrics API. - module MetricsSDK - extend self - - ConfigurationError = Class.new(OpenTelemetry::Error) - - def configure - configurator = Configurator.new - yield configurator if block_given? - configurator.configure - rescue StandardError - begin - raise ConfigurationError - rescue ConfigurationError => e - OpenTelemetry.handle_error(exception: e, message: "unexpected configuration error due to #{e.cause}") - end - end - end -end - -require 'opentelemetry/metrics_sdk/configurator' -require 'opentelemetry/metrics_sdk/instrumentation_library' -require 'opentelemetry/metrics_sdk/metrics' diff --git a/metrics_sdk/lib/opentelemetry/metrics_sdk/configurator.rb b/metrics_sdk/lib/opentelemetry/metrics_sdk/configurator.rb deleted file mode 100644 index 5cdbe01dd..000000000 --- a/metrics_sdk/lib/opentelemetry/metrics_sdk/configurator.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true - -# Copyright The OpenTelemetry Authors -# -# SPDX-License-Identifier: Apache-2.0 - -module OpenTelemetry - module MetricsSDK - # The configurator provides defaults and facilitates configuring the - # SDK for use. - class Configurator - - # @api private - # The configure method is where we define the setup process. This allows - # us to make certain guarantees about which systems and globals are setup - # at each stage. - def configure - OpenTelemetry.meter_provider = Metrics::MeterProvider.new - end - end - end -end diff --git a/metrics_sdk/lib/opentelemetry/metrics_sdk/instrumentation_library.rb b/metrics_sdk/lib/opentelemetry/metrics_sdk/instrumentation_library.rb deleted file mode 100644 index b4549e956..000000000 --- a/metrics_sdk/lib/opentelemetry/metrics_sdk/instrumentation_library.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true - -# Copyright The OpenTelemetry Authors -# -# SPDX-License-Identifier: Apache-2.0 - -module OpenTelemetry - module MetricsSDK - # InstrumentationLibrary is a struct containing library information for export. - InstrumentationLibrary = Struct.new(:name, - :version) - end -end diff --git a/metrics_sdk/lib/opentelemetry/metrics_sdk/metrics.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics.rb similarity index 55% rename from metrics_sdk/lib/opentelemetry/metrics_sdk/metrics.rb rename to metrics_sdk/lib/opentelemetry/sdk/metrics.rb index 49c108a46..1dff64d96 100644 --- a/metrics_sdk/lib/opentelemetry/metrics_sdk/metrics.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics.rb @@ -5,7 +5,7 @@ # SPDX-License-Identifier: Apache-2.0 module OpenTelemetry - module MetricsSDK + module SDK # The Metrics module contains the OpenTelemetry metrics reference # implementation. module Metrics @@ -13,6 +13,7 @@ module Metrics end end -require 'opentelemetry/metrics_sdk/metrics/instrument' -require 'opentelemetry/metrics_sdk/metrics/meter' -require 'opentelemetry/metrics_sdk/metrics/meter_provider' +require 'opentelemetry/sdk/metrics/instrument' +require 'opentelemetry/sdk/metrics/meter' +require 'opentelemetry/sdk/metrics/meter_provider' +require 'opentelemetry/sdk/metrics/configuration_patch' diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/configuration_patch.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/configuration_patch.rb new file mode 100644 index 000000000..708958e84 --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/configuration_patch.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Metrics + # The ConfiguratorPatch implements a hook to configure the metrics + # portion of the SDK. + module ConfiguratorPatch + + private + + # The metrics_configuration_hook method is where we define the setup process + # for metrics SDK. + def metrics_configuration_hook + OpenTelemetry.meter_provider = Metrics::MeterProvider.new + end + end + end + end +end + +OpenTelemetry::SDK::Configurator.prepend(OpenTelemetry::SDK::Metrics::ConfiguratorPatch) diff --git a/metrics_sdk/lib/opentelemetry/metrics_sdk/metrics/instrument.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument.rb similarity index 76% rename from metrics_sdk/lib/opentelemetry/metrics_sdk/metrics/instrument.rb rename to metrics_sdk/lib/opentelemetry/sdk/metrics/instrument.rb index 73116c42b..883c4959b 100644 --- a/metrics_sdk/lib/opentelemetry/metrics_sdk/metrics/instrument.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument.rb @@ -5,7 +5,7 @@ # SPDX-License-Identifier: Apache-2.0 module OpenTelemetry - module MetricsSDK + module SDK # The Instrument module contains the OpenTelemetry instruments reference # implementation. module Instrument @@ -13,4 +13,4 @@ module Instrument end end -require 'opentelemetry/metrics_sdk/metrics/instrument/counter' +require 'opentelemetry/sdk/metrics/instrument/counter' diff --git a/metrics_sdk/lib/opentelemetry/metrics_sdk/metrics/instrument/counter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/counter.rb similarity index 97% rename from metrics_sdk/lib/opentelemetry/metrics_sdk/metrics/instrument/counter.rb rename to metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/counter.rb index 3d8175448..171d9f072 100644 --- a/metrics_sdk/lib/opentelemetry/metrics_sdk/metrics/instrument/counter.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/counter.rb @@ -5,7 +5,7 @@ # SPDX-License-Identifier: Apache-2.0 module OpenTelemetry - module MetricsSDK + module SDK module Metrics module Instrument # {Counter} is the SDK implementation of {OpenTelemetry::Metrics::Counter}. diff --git a/metrics_sdk/lib/opentelemetry/metrics_sdk/metrics/meter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb similarity index 92% rename from metrics_sdk/lib/opentelemetry/metrics_sdk/metrics/meter.rb rename to metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb index 609664f7f..ac54c8b9d 100644 --- a/metrics_sdk/lib/opentelemetry/metrics_sdk/metrics/meter.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb @@ -5,7 +5,7 @@ # SPDX-License-Identifier: Apache-2.0 module OpenTelemetry - module MetricsSDK + module SDK # The Metrics module contains the OpenTelemetry metrics reference # implementation. module Metrics @@ -27,7 +27,7 @@ def initialize(name, version) end def create_counter(name, unit: nil, description: nil) - create_instrument(:counter, name, unit, description, nil) { OpenTelemetry::MetricsSDK::Metrics::Instrument::Counter.new } + create_instrument(:counter, name, unit, description, nil) { OpenTelemetry::SDK::Metrics::Instrument::Counter.new } end end end diff --git a/metrics_sdk/lib/opentelemetry/metrics_sdk/metrics/meter_provider.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb similarity index 98% rename from metrics_sdk/lib/opentelemetry/metrics_sdk/metrics/meter_provider.rb rename to metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb index 9dae383b8..c81a247c4 100644 --- a/metrics_sdk/lib/opentelemetry/metrics_sdk/metrics/meter_provider.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb @@ -5,7 +5,7 @@ # SPDX-License-Identifier: Apache-2.0 module OpenTelemetry - module MetricsSDK + module SDK # The Metrics module contains the OpenTelemetry metrics reference # implementation. module Metrics diff --git a/metrics_sdk/lib/opentelemetry/metrics_sdk/version.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/version.rb similarity index 55% rename from metrics_sdk/lib/opentelemetry/metrics_sdk/version.rb rename to metrics_sdk/lib/opentelemetry/sdk/metrics/version.rb index 26c125942..a26a06fec 100644 --- a/metrics_sdk/lib/opentelemetry/metrics_sdk/version.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/version.rb @@ -5,8 +5,10 @@ # SPDX-License-Identifier: Apache-2.0 module OpenTelemetry - module MetricsSDK - # Current OpenTelemetry metrics sdk version - VERSION = '0.0.1' + module SDK + module Metrics + # Current OpenTelemetry metrics sdk version + VERSION = '0.0.1' + end end end diff --git a/metrics_sdk/opentelemetry-metrics-sdk.gemspec b/metrics_sdk/opentelemetry-metrics-sdk.gemspec index cdd56344d..29a1eae8b 100644 --- a/metrics_sdk/opentelemetry-metrics-sdk.gemspec +++ b/metrics_sdk/opentelemetry-metrics-sdk.gemspec @@ -6,11 +6,11 @@ lib = File.expand_path('lib', __dir__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) -require 'opentelemetry/metrics_sdk/version' +require 'opentelemetry/sdk/metrics/version' Gem::Specification.new do |spec| spec.name = 'opentelemetry-metrics-sdk' - spec.version = OpenTelemetry::MetricsSDK::VERSION + spec.version = OpenTelemetry::SDK::Metrics::VERSION spec.authors = ['OpenTelemetry Authors'] spec.email = ['cncf-opentelemetry-contributors@lists.cncf.io'] @@ -25,8 +25,9 @@ Gem::Specification.new do |spec| spec.require_paths = ['lib'] spec.required_ruby_version = '>= 2.5.0' - spec.add_dependency 'opentelemetry-api' + spec.add_dependency 'opentelemetry-api', '~> 1.0' spec.add_dependency 'opentelemetry-metrics-api' + spec.add_dependency 'opentelemetry-sdk', '~> 1.0' spec.add_development_dependency 'benchmark-ipsa', '~> 0.2.0' spec.add_development_dependency 'bundler', '>= 1.17' @@ -39,9 +40,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-metrics-sdk/v#{OpenTelemetry::MetricsSDK::VERSION}/file.CHANGELOG.html" + spec.metadata['changelog_uri'] = "https://open-telemetry.github.io/opentelemetry-ruby/opentelemetry-metrics-sdk/v#{OpenTelemetry::SDK::Metrics::VERSION}/file.CHANGELOG.html" spec.metadata['source_code_uri'] = 'https://github.com/open-telemetry/opentelemetry-ruby/tree/main/api' 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-metrics-sdk/v#{OpenTelemetry::MetricsSDK::VERSION}" + spec.metadata['documentation_uri'] = "https://open-telemetry.github.io/opentelemetry-ruby/opentelemetry-metrics-sdk/v#{OpenTelemetry::SDK::Metrics::VERSION}" end end diff --git a/metrics_sdk/test/opentelemetry/metrics_sdk_test.rb b/metrics_sdk/test/opentelemetry/metrics_sdk_test.rb index e220912d1..167e4ca20 100644 --- a/metrics_sdk/test/opentelemetry/metrics_sdk_test.rb +++ b/metrics_sdk/test/opentelemetry/metrics_sdk_test.rb @@ -6,7 +6,7 @@ require 'test_helper' -describe OpenTelemetry::MetricsSDK do +describe OpenTelemetry::SDK do describe '#configure' do after do # Ensure we don't leak custom loggers and error handlers to other tests @@ -19,11 +19,11 @@ meter = meter_provider.meter("test") instrument = meter.create_counter("a_counter") - OpenTelemetry::MetricsSDK.configure + OpenTelemetry::SDK.configure - _(meter_provider.instance_variable_get(:@delegate)).must_be_instance_of OpenTelemetry::MetricsSDK::Metrics::MeterProvider - _(meter.instance_variable_get(:@delegate)).must_be_instance_of OpenTelemetry::MetricsSDK::Metrics::Meter - _(instrument.instance_variable_get(:@delegate)).must_be_instance_of OpenTelemetry::MetricsSDK::Metrics::Instrument::Counter + _(meter_provider.instance_variable_get(:@delegate)).must_be_instance_of OpenTelemetry::SDK::Metrics::MeterProvider + _(meter.instance_variable_get(:@delegate)).must_be_instance_of OpenTelemetry::SDK::Metrics::Meter + _(instrument.instance_variable_get(:@delegate)).must_be_instance_of OpenTelemetry::SDK::Metrics::Instrument::Counter end it 'sends the original configuration error to the error handler' do @@ -35,11 +35,11 @@ received_message = message end - OpenTelemetry::MetricsSDK.configure do |config| + OpenTelemetry::SDK.configure do |config| config.do_something end - _(received_exception).must_be_instance_of OpenTelemetry::MetricsSDK::ConfigurationError + _(received_exception).must_be_instance_of OpenTelemetry::SDK::ConfigurationError _(received_message).must_match(/unexpected configuration error due to undefined method `do_something/) end end diff --git a/sdk/lib/opentelemetry/sdk/configurator.rb b/sdk/lib/opentelemetry/sdk/configurator.rb index dcdb3097e..5b84b8959 100644 --- a/sdk/lib/opentelemetry/sdk/configurator.rb +++ b/sdk/lib/opentelemetry/sdk/configurator.rb @@ -141,11 +141,14 @@ def configure configure_span_processors tracer_provider.id_generator = @id_generator OpenTelemetry.tracer_provider = tracer_provider + metrics_configuration_hook install_instrumentation end private + def metrics_configuration_hook; end + def tracer_provider @tracer_provider ||= Trace::TracerProvider.new(resource: @resource) end From 2b2a17604a5f2ae5c851a1b6be35352231ab197d Mon Sep 17 00:00:00 2001 From: Robert Laurin Date: Thu, 27 Jan 2022 12:59:36 -0500 Subject: [PATCH 03/25] fix: remove duplicate mutex instantiation --- metrics_api/lib/opentelemetry-metrics-api.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/metrics_api/lib/opentelemetry-metrics-api.rb b/metrics_api/lib/opentelemetry-metrics-api.rb index 2f7992787..771d7c34d 100644 --- a/metrics_api/lib/opentelemetry-metrics-api.rb +++ b/metrics_api/lib/opentelemetry-metrics-api.rb @@ -18,7 +18,6 @@ # The OpenTelemetry module provides global accessors for telemetry objects. module OpenTelemetry - @mutex = Mutex.new @meter_provider = Internal::ProxyMeterProvider.new # Register the global meter provider. From 62959dfc2fc370f0c1f793c7b4c427e32c5fb854 Mon Sep 17 00:00:00 2001 From: Robert Laurin Date: Mon, 31 Jan 2022 11:23:31 -0500 Subject: [PATCH 04/25] feat: add api metrics measurement --- metrics_api/lib/opentelemetry/metrics/measurement.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 metrics_api/lib/opentelemetry/metrics/measurement.rb diff --git a/metrics_api/lib/opentelemetry/metrics/measurement.rb b/metrics_api/lib/opentelemetry/metrics/measurement.rb new file mode 100644 index 000000000..e16e35c3f --- /dev/null +++ b/metrics_api/lib/opentelemetry/metrics/measurement.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module Metrics + Measurement = Struct.new(:value, :attributes) + end +end From 8e5470516d5b52f28ff48e9b242101c22444b7a5 Mon Sep 17 00:00:00 2001 From: Robert Laurin Date: Thu, 3 Feb 2022 11:45:03 -0500 Subject: [PATCH 05/25] fix: add metric install sdk tests --- .../test/opentelemetry/metrics_sdk_test.rb | 17 ++++++++++++----- metrics_sdk/test/test_helper.rb | 12 +++++++++++- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/metrics_sdk/test/opentelemetry/metrics_sdk_test.rb b/metrics_sdk/test/opentelemetry/metrics_sdk_test.rb index 167e4ca20..ac99e7831 100644 --- a/metrics_sdk/test/opentelemetry/metrics_sdk_test.rb +++ b/metrics_sdk/test/opentelemetry/metrics_sdk_test.rb @@ -8,22 +8,29 @@ describe OpenTelemetry::SDK do describe '#configure' do - after do - # Ensure we don't leak custom loggers and error handlers to other tests - OpenTelemetry.logger = Logger.new(File::NULL) - OpenTelemetry.error_handler = nil - end + before { reset_metrics_sdk } it 'upgrades the API MeterProvider, Meters, and Instruments' do meter_provider = OpenTelemetry.meter_provider meter = meter_provider.meter("test") instrument = meter.create_counter("a_counter") + # Calls before the SDK is configured return ProxY implementations + _(meter_provider).must_be_instance_of OpenTelemetry::Internal::ProxyMeterProvider + _(meter).must_be_instance_of OpenTelemetry::Internal::ProxyMeter + _(instrument).must_be_instance_of OpenTelemetry::Internal::ProxyInstrument + OpenTelemetry::SDK.configure + # Proxy implementations now have their delegates set _(meter_provider.instance_variable_get(:@delegate)).must_be_instance_of OpenTelemetry::SDK::Metrics::MeterProvider _(meter.instance_variable_get(:@delegate)).must_be_instance_of OpenTelemetry::SDK::Metrics::Meter _(instrument.instance_variable_get(:@delegate)).must_be_instance_of OpenTelemetry::SDK::Metrics::Instrument::Counter + + # Calls after the SDK is configured now return the SDK implementations directly + _(OpenTelemetry.meter_provider).must_be_instance_of OpenTelemetry::SDK::Metrics::MeterProvider + _(OpenTelemetry.meter_provider.meter('test')).must_be_instance_of OpenTelemetry::SDK::Metrics::Meter + _(OpenTelemetry.meter_provider.meter('test').create_counter("b_counter")).must_be_instance_of OpenTelemetry::SDK::Metrics::Instrument::Counter end it 'sends the original configuration error to the error handler' do diff --git a/metrics_sdk/test/test_helper.rb b/metrics_sdk/test/test_helper.rb index 3106e91ce..b16c66cd6 100644 --- a/metrics_sdk/test/test_helper.rb +++ b/metrics_sdk/test/test_helper.rb @@ -12,4 +12,14 @@ require 'minitest/autorun' require 'pry' -OpenTelemetry.logger = Logger.new(File::NULL) +# reset_metrics_sdk is a test helper used to clear +# SDK configuration state between calls +def reset_metrics_sdk + OpenTelemetry.instance_variable_set( + :@meter_provider, + OpenTelemetry::Internal::ProxyMeterProvider.new + ) + + OpenTelemetry.logger = Logger.new(File::NULL) + OpenTelemetry.error_handler = nil +end From 32bda0cecf60fc494048cddec7cef8b464bc3496 Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 10 Feb 2022 11:54:32 -0500 Subject: [PATCH 06/25] fix: comments Co-authored-by: Francis Bogsanyi --- metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb | 3 +-- metrics_sdk/test/opentelemetry/metrics_sdk_test.rb | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb index ac54c8b9d..cf0fef873 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb @@ -17,9 +17,8 @@ class Meter < OpenTelemetry::Metrics::Meter # # @param [String] name Instrumentation package name # @param [String] version Instrumentation package version - # @param [TracerProvider] tracer_provider TracerProvider that initialized the tracer # - # @return [Tracer] + # @return [Meter] def initialize(name, version) @mutex = Mutex.new @registry = {} diff --git a/metrics_sdk/test/opentelemetry/metrics_sdk_test.rb b/metrics_sdk/test/opentelemetry/metrics_sdk_test.rb index ac99e7831..ee1d7e2a6 100644 --- a/metrics_sdk/test/opentelemetry/metrics_sdk_test.rb +++ b/metrics_sdk/test/opentelemetry/metrics_sdk_test.rb @@ -15,7 +15,7 @@ meter = meter_provider.meter("test") instrument = meter.create_counter("a_counter") - # Calls before the SDK is configured return ProxY implementations + # Calls before the SDK is configured return Proxy implementations _(meter_provider).must_be_instance_of OpenTelemetry::Internal::ProxyMeterProvider _(meter).must_be_instance_of OpenTelemetry::Internal::ProxyMeter _(instrument).must_be_instance_of OpenTelemetry::Internal::ProxyInstrument From e664c20a669a01c2293215f1928484508bbb528b Mon Sep 17 00:00:00 2001 From: Robert Laurin Date: Tue, 22 Feb 2022 15:52:49 -0500 Subject: [PATCH 07/25] fix: add meter provider implementation and tests --- .../lib/opentelemetry/sdk/metrics/export.rb | 27 +++++ .../sdk/metrics/export/console_exporter.rb | 30 +++++ .../sdk/metrics/export/metric_reader.rb | 26 ++++ .../sdk/metrics/meter_provider.rb | 98 +++++++++++++-- .../sdk/metrics/meter_provider_test.rb | 114 ++++++++++++++++++ metrics_sdk/test/test_helper.rb | 9 ++ 6 files changed, 297 insertions(+), 7 deletions(-) create mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/export.rb create mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/export/console_exporter.rb create mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/export/metric_reader.rb create mode 100644 metrics_sdk/test/opentelemetry/sdk/metrics/meter_provider_test.rb diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/export.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/export.rb new file mode 100644 index 000000000..aa98919a6 --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/export.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Metrics + module Export + ExportError = Class.new(OpenTelemetry::Error) + + # The operation finished successfully. + SUCCESS = 0 + + # The operation finished with an error. + FAILURE = 1 + + # The operation timed out. + TIMEOUT = 2 + end + end + end +end + +require 'opentelemetry/sdk/metrics/export/metric_reader' +require 'opentelemetry/sdk/metrics/export/console_exporter' diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/export/console_exporter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/export/console_exporter.rb new file mode 100644 index 000000000..94278fb3d --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/export/console_exporter.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Metrics + module Export + class ConsoleExporter + PREFERRED_TEMPORALITY = 'delta' + + def export(metrics) + puts metrics + end + + def shutdown + SUCCESS + end + + def preferred_temporality + PREFERRED_TEMPORALITY + end + end + end + end + end +end + diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/export/metric_reader.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/export/metric_reader.rb new file mode 100644 index 000000000..d4913af9c --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/export/metric_reader.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Metrics + module Export + class MetricReader + def initialize(exporter) + @exporter = exporter + end + + def collect; end + + def shutdown(timeout: nil) + SUCCESS + end + end + end + end + end +end + diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb index c81a247c4..374559ea6 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb @@ -14,22 +14,106 @@ class MeterProvider < OpenTelemetry::Metrics::MeterProvider Key = Struct.new(:name, :version) private_constant(:Key) - def initialize + def initialize(resource: OpenTelemetry::SDK::Resources::Resource.create) + @mutex = Mutex.new @registry = {} - @registry_mutex = Mutex.new + @stopped = false + + @metric_readers = [] + @views = [] end # Returns a {Meter} instance. # - # @param [optional String] name Instrumentation package name + # @param [String] name Instrumentation package name # @param [optional String] version Instrumentation package version # # @return [Meter] - def meter(name = nil, version = nil) - name ||= '' + def meter(name, version = nil) version ||= '' - OpenTelemetry.logger.warn 'calling MeterProvider#meter without providing a meter name.' if name.empty? - @registry_mutex.synchronize { @registry[Key.new(name, version)] ||= Meter.new(name, version) } + if @stopped + OpenTelemetry.logger.warn 'calling MeterProvider#meter after shutdown, a noop meter will be returned.' + OpenTelemetry::Metrics::Meter.new + else + @mutex.synchronize { @registry[Key.new(name, version)] ||= Meter.new(name, version, self) } + end + end + + # Attempts to stop all the activity for this {MeterProvider}. + # + # Calls MetricReader#shutdown for all registered MetricReaders. + # Calls MetricExporter#shutdown for all registered MetricExporters. + # + # After this is called all the newly created {Meter}s will be no-op. + # + # @param [optional Numeric] timeout An optional timeout in seconds. + # @return [Integer] Export::SUCCESS if no error occurred, Export::FAILURE if + # a non-specific failure occurred, Export::TIMEOUT if a timeout occurred. + def shutdown(timeout: nil) + @mutex.synchronize do + if @stopped + OpenTelemetry.logger.warn('calling MetricProvider#shutdown multiple times.') + Export::FAILURE + else + start_time = OpenTelemetry::Common::Utilities.timeout_timestamp + + results = @metric_readers.map do |metric_reader| + remaining_timeout = OpenTelemetry::Common::Utilities.maybe_timeout(timeout, start_time) + break [Export::TIMEOUT] if remaining_timeout&.zero? + metric_reader.shutdown(timeout: remaining_timeout) + end + + @stopped = true + results.max || Export::SUCCESS + end + end + end + + # This method provides a way for provider to notify the registered + # {MetricReader} instances, so they can do as much as they could to consume + # or send the metrics. Note: unlike Push Metric Exporter which can send data on + # its own schedule, Pull Metric Exporter can only send the data when it is + # being asked by the scraper, so ForceFlush would not make much sense. + # + # @param [optional Numeric] timeout An optional timeout in seconds. + # @return [Integer] Export::SUCCESS if no error occurred, Export::FAILURE if + # a non-specific failure occurred, Export::TIMEOUT if a timeout occurred. + def force_flush(timeout: nil) + @mutex.synchronize do + return Export::SUCCESS if @stopped + + start_time = OpenTelemetry::Common::Utilities.timeout_timestamp + results = @metric_readers.map do |metric_reader| + remaining_timeout = OpenTelemetry::Common::Utilities.maybe_timeout(timeout, start_time) + return Export::TIMEOUT if remaining_timeout&.zero? + + metric_reader.force_flush(timeout: remaining_timeout) + end + results.max || Export::SUCCESS + end + end + + # Adds a new MetricReader to this {MeterProvider}. + # + # @param metric_reader the new MetricReader to be added. + def add_metric_reader(metric_reader) + @mutex.synchronize do + if @stopped + OpenTelemetry.logger.warn('calling MetricProvider#add_metric_reader after shutdown.') + return + end + + @metric_readers = @metric_readers.push(metric_reader) + end + end + + # The type of the Instrument(s) (optional). + # The name of the Instrument(s). OpenTelemetry SDK authors MAY choose to support wildcard characters, with the question mark (?) matching exactly one character and the asterisk character (*) matching zero or more characters. + # The name of the Meter (optional). + # The version of the Meter (optional). + # The schema_url of the Meter (optional). + def add_view + # TODO: For each meter add this view to all applicable instruments end end end diff --git a/metrics_sdk/test/opentelemetry/sdk/metrics/meter_provider_test.rb b/metrics_sdk/test/opentelemetry/sdk/metrics/meter_provider_test.rb new file mode 100644 index 000000000..e71d8af3a --- /dev/null +++ b/metrics_sdk/test/opentelemetry/sdk/metrics/meter_provider_test.rb @@ -0,0 +1,114 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'test_helper' + +describe OpenTelemetry::SDK::Metrics::MeterProvider do + before do + reset_metrics_sdk + OpenTelemetry::SDK.configure + end + + describe '#meter' do + it 'requires a meter name' do + _(-> { OpenTelemetry.meter_provider.meter }).must_raise(ArgumentError) + end + + it 'creates a new meter' do + meter = OpenTelemetry.meter_provider.meter('test') + + _(meter).must_be_instance_of(OpenTelemetry::SDK::Metrics::Meter) + end + + it 'repeated calls does not recreate a meter of the same name' do + meter_a = OpenTelemetry.meter_provider.meter('test') + meter_b = OpenTelemetry.meter_provider.meter('test') + + _(meter_a).must_equal(meter_b) + end + end + + describe '#shutdown' do + it 'repeated calls to shutdown result in a failure' do + with_test_logger do |log_stream| + _(OpenTelemetry.meter_provider.shutdown).must_equal(OpenTelemetry::SDK::Metrics::Export::SUCCESS) + _(OpenTelemetry.meter_provider.shutdown).must_equal(OpenTelemetry::SDK::Metrics::Export::FAILURE) + _(log_stream.string).must_match(/calling MetricProvider#shutdown multiple times/) + end + end + + it 'returns a no-op meter after being shutdown' do + with_test_logger do |log_stream| + OpenTelemetry.meter_provider.shutdown + + _(OpenTelemetry.meter_provider.meter('test')).must_be_instance_of(OpenTelemetry::Metrics::Meter) + _(log_stream.string).must_match(/calling MeterProvider#meter after shutdown, a noop meter will be returned/) + end + end + + it 'returns a timeout response when it times out' do + mock_metric_reader = Minitest::Mock.new + mock_metric_reader.expect(:nothing_gets_called_because_it_times_out_first, nil) + OpenTelemetry.meter_provider.add_metric_reader(mock_metric_reader) + + _(OpenTelemetry.meter_provider.shutdown(timeout: 0)).must_equal(OpenTelemetry::SDK::Metrics::Export::TIMEOUT) + end + + it 'invokes shutdown on all registered Metric Readers' do + mock_metric_reader_1 = Minitest::Mock.new + mock_metric_reader_2 = Minitest::Mock.new + mock_metric_reader_1.expect(:shutdown, nil, [{ timeout: nil }]) + mock_metric_reader_2.expect(:shutdown, nil, [{ timeout: nil }]) + + OpenTelemetry.meter_provider.add_metric_reader(mock_metric_reader_1) + OpenTelemetry.meter_provider.add_metric_reader(mock_metric_reader_2) + OpenTelemetry.meter_provider.shutdown + + mock_metric_reader_1.verify + mock_metric_reader_2.verify + end + end + + describe '#force_flush' do + it 'returns a timeout response when it times out' do + mock_metric_reader = Minitest::Mock.new + mock_metric_reader.expect(:nothing_gets_called_because_it_times_out_first, nil) + OpenTelemetry.meter_provider.add_metric_reader(mock_metric_reader) + + _(OpenTelemetry.meter_provider.force_flush(timeout: 0)).must_equal(OpenTelemetry::SDK::Metrics::Export::TIMEOUT) + end + + it 'invokes force_flush on all registered Metric Readers' do + mock_metric_reader_1 = Minitest::Mock.new + mock_metric_reader_2 = Minitest::Mock.new + mock_metric_reader_1.expect(:force_flush, nil, [{ timeout: nil }]) + mock_metric_reader_2.expect(:force_flush, nil, [{ timeout: nil }]) + OpenTelemetry.meter_provider.add_metric_reader(mock_metric_reader_1) + OpenTelemetry.meter_provider.add_metric_reader(mock_metric_reader_2) + + OpenTelemetry.meter_provider.force_flush + + mock_metric_reader_1.verify + mock_metric_reader_2.verify + end + end + + describe '#add_metric_reader' do + it 'adds a metric reader' do + metric_reader = OpenTelemetry::SDK::Metrics::Export::MetricReader.new( + OpenTelemetry::SDK::Metrics::Export::ConsoleExporter.new + ) + + OpenTelemetry.meter_provider.add_metric_reader(metric_reader) + + _(OpenTelemetry.meter_provider.instance_variable_get(:@metric_readers)).must_equal([metric_reader]) + end + end + + describe '#add_view' do + # TODO + end +end diff --git a/metrics_sdk/test/test_helper.rb b/metrics_sdk/test/test_helper.rb index b16c66cd6..0b5f2d125 100644 --- a/metrics_sdk/test/test_helper.rb +++ b/metrics_sdk/test/test_helper.rb @@ -23,3 +23,12 @@ def reset_metrics_sdk OpenTelemetry.logger = Logger.new(File::NULL) OpenTelemetry.error_handler = nil end + +def with_test_logger + log_stream = StringIO.new + original_logger = OpenTelemetry.logger + OpenTelemetry.logger = ::Logger.new(log_stream) + yield log_stream +ensure + OpenTelemetry.logger = original_logger +end From 8b82fc39997e1bdd435b6c2ca161af2227baa365 Mon Sep 17 00:00:00 2001 From: Robert Laurin Date: Fri, 25 Feb 2022 14:56:51 -0500 Subject: [PATCH 08/25] fix: ruby style --- .../sdk/metrics/meter_provider.rb | 35 +++++++++++------- .../sdk/metrics/meter_provider_test.rb | 37 ++++++++++--------- 2 files changed, 41 insertions(+), 31 deletions(-) diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb index 374559ea6..ec46352c3 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb @@ -42,7 +42,6 @@ def meter(name, version = nil) # Attempts to stop all the activity for this {MeterProvider}. # # Calls MetricReader#shutdown for all registered MetricReaders. - # Calls MetricExporter#shutdown for all registered MetricExporters. # # After this is called all the newly created {Meter}s will be no-op. # @@ -56,11 +55,13 @@ def shutdown(timeout: nil) Export::FAILURE else start_time = OpenTelemetry::Common::Utilities.timeout_timestamp - results = @metric_readers.map do |metric_reader| remaining_timeout = OpenTelemetry::Common::Utilities.maybe_timeout(timeout, start_time) - break [Export::TIMEOUT] if remaining_timeout&.zero? - metric_reader.shutdown(timeout: remaining_timeout) + if remaining_timeout&.zero? + Export::TIMEOUT + else + metric_reader.shutdown(timeout: remaining_timeout) + end end @stopped = true @@ -80,16 +81,21 @@ def shutdown(timeout: nil) # a non-specific failure occurred, Export::TIMEOUT if a timeout occurred. def force_flush(timeout: nil) @mutex.synchronize do - return Export::SUCCESS if @stopped - - start_time = OpenTelemetry::Common::Utilities.timeout_timestamp - results = @metric_readers.map do |metric_reader| - remaining_timeout = OpenTelemetry::Common::Utilities.maybe_timeout(timeout, start_time) - return Export::TIMEOUT if remaining_timeout&.zero? + if @stopped + Export::SUCCESS + else + start_time = OpenTelemetry::Common::Utilities.timeout_timestamp + results = @metric_readers.map do |metric_reader| + remaining_timeout = OpenTelemetry::Common::Utilities.maybe_timeout(timeout, start_time) + if remaining_timeout&.zero? + Export::TIMEOUT + else + metric_reader.force_flush(timeout: remaining_timeout) + end + end - metric_reader.force_flush(timeout: remaining_timeout) + results.max || Export::SUCCESS end - results.max || Export::SUCCESS end end @@ -100,10 +106,11 @@ def add_metric_reader(metric_reader) @mutex.synchronize do if @stopped OpenTelemetry.logger.warn('calling MetricProvider#add_metric_reader after shutdown.') - return + else + @metric_readers.push(metric_reader) end - @metric_readers = @metric_readers.push(metric_reader) + nil end end diff --git a/metrics_sdk/test/opentelemetry/sdk/metrics/meter_provider_test.rb b/metrics_sdk/test/opentelemetry/sdk/metrics/meter_provider_test.rb index e71d8af3a..692eda7ed 100644 --- a/metrics_sdk/test/opentelemetry/sdk/metrics/meter_provider_test.rb +++ b/metrics_sdk/test/opentelemetry/sdk/metrics/meter_provider_test.rb @@ -58,17 +58,17 @@ end it 'invokes shutdown on all registered Metric Readers' do - mock_metric_reader_1 = Minitest::Mock.new - mock_metric_reader_2 = Minitest::Mock.new - mock_metric_reader_1.expect(:shutdown, nil, [{ timeout: nil }]) - mock_metric_reader_2.expect(:shutdown, nil, [{ timeout: nil }]) + mock_metric_reader1 = Minitest::Mock.new + mock_metric_reader2 = Minitest::Mock.new + mock_metric_reader1.expect(:shutdown, nil, [{ timeout: nil }]) + mock_metric_reader2.expect(:shutdown, nil, [{ timeout: nil }]) - OpenTelemetry.meter_provider.add_metric_reader(mock_metric_reader_1) - OpenTelemetry.meter_provider.add_metric_reader(mock_metric_reader_2) + OpenTelemetry.meter_provider.add_metric_reader(mock_metric_reader1) + OpenTelemetry.meter_provider.add_metric_reader(mock_metric_reader2) OpenTelemetry.meter_provider.shutdown - mock_metric_reader_1.verify - mock_metric_reader_2.verify + mock_metric_reader1.verify + mock_metric_reader2.verify end end @@ -82,17 +82,17 @@ end it 'invokes force_flush on all registered Metric Readers' do - mock_metric_reader_1 = Minitest::Mock.new - mock_metric_reader_2 = Minitest::Mock.new - mock_metric_reader_1.expect(:force_flush, nil, [{ timeout: nil }]) - mock_metric_reader_2.expect(:force_flush, nil, [{ timeout: nil }]) - OpenTelemetry.meter_provider.add_metric_reader(mock_metric_reader_1) - OpenTelemetry.meter_provider.add_metric_reader(mock_metric_reader_2) + mock_metric_reader1 = Minitest::Mock.new + mock_metric_reader2 = Minitest::Mock.new + mock_metric_reader1.expect(:force_flush, nil, [{ timeout: nil }]) + mock_metric_reader2.expect(:force_flush, nil, [{ timeout: nil }]) + OpenTelemetry.meter_provider.add_metric_reader(mock_metric_reader1) + OpenTelemetry.meter_provider.add_metric_reader(mock_metric_reader2) OpenTelemetry.meter_provider.force_flush - mock_metric_reader_1.verify - mock_metric_reader_2.verify + mock_metric_reader1.verify + mock_metric_reader2.verify end end @@ -109,6 +109,9 @@ end describe '#add_view' do - # TODO + it 'adds a view' do + # TODO + # OpenTelemetry.meter_provider.add_view + end end end From 50be5a5cddf338a95bd5fcb95912ac5bab30f47b Mon Sep 17 00:00:00 2001 From: Robert Laurin Date: Fri, 4 Mar 2022 12:39:10 -0500 Subject: [PATCH 09/25] Implement SDK instrument creation --- .../opentelemetry/sdk/metrics/instrument.rb | 5 ++ .../sdk/metrics/instrument/counter.rb | 11 +++- .../sdk/metrics/instrument/histogram.rb | 34 +++++++++++++ .../metrics/instrument/observable_counter.rb | 26 ++++++++++ .../metrics/instrument/observable_gauge.rb | 26 ++++++++++ .../instrument/observable_up_down_counter.rb | 26 ++++++++++ .../sdk/metrics/instrument/up_down_counter.rb | 34 +++++++++++++ .../lib/opentelemetry/sdk/metrics/meter.rb | 16 ++++-- .../opentelemetry/sdk/metrics/meter_test.rb | 51 +++++++++++++++++++ 9 files changed, 225 insertions(+), 4 deletions(-) create mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/histogram.rb create mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/observable_counter.rb create mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/observable_gauge.rb create mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/observable_up_down_counter.rb create mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/up_down_counter.rb create mode 100644 metrics_sdk/test/opentelemetry/sdk/metrics/meter_test.rb diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument.rb index 883c4959b..00004706c 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument.rb @@ -14,3 +14,8 @@ module Instrument end require 'opentelemetry/sdk/metrics/instrument/counter' +require 'opentelemetry/sdk/metrics/instrument/histogram' +require 'opentelemetry/sdk/metrics/instrument/observable_counter' +require 'opentelemetry/sdk/metrics/instrument/observable_gauge' +require 'opentelemetry/sdk/metrics/instrument/observable_up_down_counter' +require 'opentelemetry/sdk/metrics/instrument/up_down_counter' diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/counter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/counter.rb index 171d9f072..de29dcc99 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/counter.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/counter.rb @@ -10,6 +10,14 @@ module Metrics module Instrument # {Counter} is the SDK implementation of {OpenTelemetry::Metrics::Counter}. class Counter < OpenTelemetry::Metrics::Instrument::Counter + attr_reader :name, :unit, :description + + def initialize(name, unit, description, meter) + @name = name + @unit = unit + @description = description + @meter = meter + end # Increment the Counter by a fixed amount. # @@ -18,7 +26,8 @@ class Counter < OpenTelemetry::Metrics::Instrument::Counter # Values must be non-nil and (array of) string, boolean or numeric type. # Array values must not contain nil elements and all elements must be of # the same basic type (string, numeric, boolean). - def add(increment, attributes: nil); end + def add(increment, attributes: nil) + end end end end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/histogram.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/histogram.rb new file mode 100644 index 000000000..65e343381 --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/histogram.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Metrics + module Instrument + # {Histogram} is the SDK implementation of {OpenTelemetry::Metrics::Histogram}. + class Histogram < OpenTelemetry::Metrics::Instrument::Histogram + attr_reader :name, :unit, :description + + def initialize(name, unit, description, meter) + @name = name + @unit = unit + @description = description + @meter = meter + end + + # Updates the statistics with the specified amount. + # + # @param [numeric] amount The amount of the Measurement, which MUST be a non-negative numeric value. + # @param [Hash{String => String, Numeric, Boolean, Array}] attributes + # Values must be non-nil and (array of) string, boolean or numeric type. + # Array values must not contain nil elements and all elements must be of + # the same basic type (string, numeric, boolean). + def record(amount, attributes: nil); end + end + end + end + end +end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/observable_counter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/observable_counter.rb new file mode 100644 index 000000000..4e5d49102 --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/observable_counter.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Metrics + module Instrument + # {ObservableCounter} is the SDK implementation of {OpenTelemetry::Metrics::ObservableCounter}. + class ObservableCounter < OpenTelemetry::Metrics::Instrument::ObservableCounter + attr_reader :name, :unit, :description + + def initialize(name, unit, description, callback, meter) + @name = name + @unit = unit + @description = description + @callback = callback + @meter = meter + end + end + end + end + end +end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/observable_gauge.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/observable_gauge.rb new file mode 100644 index 000000000..7a122184f --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/observable_gauge.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Metrics + module Instrument + # {ObservableGauge} is the SDK implementation of {OpenTelemetry::Metrics::ObservableGauge}. + class ObservableGauge < OpenTelemetry::Metrics::Instrument::ObservableGauge + attr_reader :name, :unit, :description + + def initialize(name, unit, description, callback, meter) + @name = name + @unit = unit + @description = description + @callback = callback + @meter = meter + end + end + end + end + end +end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/observable_up_down_counter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/observable_up_down_counter.rb new file mode 100644 index 000000000..8eb812c5f --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/observable_up_down_counter.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Metrics + module Instrument + # {ObservableUpDownCounter} is the SDK implementation of {OpenTelemetry::Metrics::ObservableUpDownCounter}. + class ObservableUpDownCounter < OpenTelemetry::Metrics::Instrument::ObservableUpDownCounter + attr_reader :name, :unit, :description + + def initialize(name, unit, description, callback, meter) + @name = name + @unit = unit + @description = description + @callback = callback + @meter = meter + end + end + end + end + end +end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/up_down_counter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/up_down_counter.rb new file mode 100644 index 000000000..d3b82fb0e --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/up_down_counter.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Metrics + module Instrument + # {UpDownCounter} is the SDK implementation of {OpenTelemetry::Metrics::UpDownCounter}. + class UpDownCounter < OpenTelemetry::Metrics::Instrument::UpDownCounter + attr_reader :name, :unit, :description + + def initialize(name, unit, description, meter) + @name = name + @unit = unit + @description = description + @meter = meter + end + + # Increment or decrement the UpDownCounter by a fixed amount. + # + # @param [Numeric] amount The amount to be added, can be positive, negative or zero. + # @param [Hash{String => String, Numeric, Boolean, Array}] attributes + # Values must be non-nil and (array of) string, boolean or numeric type. + # Array values must not contain nil elements and all elements must be of + # the same basic type (string, numeric, boolean). + def add(amount, attributes: nil); end + end + end + end + end +end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb index cf0fef873..9ec54dc65 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb @@ -19,14 +19,24 @@ class Meter < OpenTelemetry::Metrics::Meter # @param [String] version Instrumentation package version # # @return [Meter] - def initialize(name, version) + def initialize(name, version, meter_provider) @mutex = Mutex.new @registry = {} @instrumentation_library = InstrumentationLibrary.new(name, version) + @meter_provider = meter_provider end - def create_counter(name, unit: nil, description: nil) - create_instrument(:counter, name, unit, description, nil) { OpenTelemetry::SDK::Metrics::Instrument::Counter.new } + def create_instrument(kind, name, unit, description, callback) + super do + case kind + when :counter then OpenTelemetry::SDK::Metrics::Instrument::Counter.new(name, unit, description, self) + when :observable_counter then OpenTelemetry::SDK::Metrics::Instrument::ObservableCounter.new(name, unit, description, callback, self) + when :histogram then OpenTelemetry::SDK::Metrics::Instrument::Histogram.new(name, unit, description, self) + when :observable_gauge then OpenTelemetry::SDK::Metrics::Instrument::ObservableGauge.new(name, unit, description, callback, self) + when :up_down_counter then OpenTelemetry::SDK::Metrics::Instrument::UpDownCounter.new(name, unit, description, self) + when :observable_up_down_counter then OpenTelemetry::SDK::Metrics::Instrument::ObservableUpDownCounter.new(name, unit, description, callback, self) + end + end end end end diff --git a/metrics_sdk/test/opentelemetry/sdk/metrics/meter_test.rb b/metrics_sdk/test/opentelemetry/sdk/metrics/meter_test.rb new file mode 100644 index 000000000..8c1b3bff3 --- /dev/null +++ b/metrics_sdk/test/opentelemetry/sdk/metrics/meter_test.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: false + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'test_helper' + +describe OpenTelemetry::SDK::Metrics::Meter do + before { OpenTelemetry::SDK.configure } + + let(:meter) { OpenTelemetry.meter_provider.meter('new_meter') } + + describe '#create_counter' do + it 'creates a counter instrument' do + instrument = meter.create_counter('a_counter', unit: 'minutes', description: 'useful description') + _(instrument).must_be_instance_of OpenTelemetry::SDK::Metrics::Instrument::Counter + end + end + describe '#create_observable_counter' do + it 'creates a observable_counter instrument' do + instrument = meter.create_observable_counter('a_observable_counter', unit: 'minutes', description: 'useful description', callback: nil) + _(instrument).must_be_instance_of OpenTelemetry::SDK::Metrics::Instrument::ObservableCounter + end + end + describe '#create_histogram' do + it 'creates a histogram instrument' do + instrument = meter.create_histogram('a_histogram', unit: 'minutes', description: 'useful description') + _(instrument).must_be_instance_of OpenTelemetry::SDK::Metrics::Instrument::Histogram + end + end + describe '#create_observable_gauge' do + it 'creates a observable_gauge instrument' do + instrument = meter.create_observable_gauge('a_observable_gauge', unit: 'minutes', description: 'useful description', callback: nil) + _(instrument).must_be_instance_of OpenTelemetry::SDK::Metrics::Instrument::ObservableGauge + end + end + describe '#create_up_down_counter' do + it 'creates a up_down_counter instrument' do + instrument = meter.create_up_down_counter('a_up_down_counter', unit: 'minutes', description: 'useful description') + _(instrument).must_be_instance_of OpenTelemetry::SDK::Metrics::Instrument::UpDownCounter + end + end + describe '#create_observable_up_down_counter' do + it 'creates a observable_up_down_counter instrument' do + instrument = meter.create_observable_up_down_counter('a_observable_up_down_counter', unit: 'minutes', description: 'useful description', callback: nil) + _(instrument).must_be_instance_of OpenTelemetry::SDK::Metrics::Instrument::ObservableUpDownCounter + end + end + +end From dba982864fc788f830ce92bafcdc28551a2a54d2 Mon Sep 17 00:00:00 2001 From: Robert Laurin Date: Fri, 4 Mar 2022 12:43:50 -0500 Subject: [PATCH 10/25] fix: handling of immutable descriptions --- metrics_api/lib/opentelemetry/metrics/meter.rb | 2 +- metrics_sdk/test/opentelemetry/sdk/metrics/meter_test.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/metrics_api/lib/opentelemetry/metrics/meter.rb b/metrics_api/lib/opentelemetry/metrics/meter.rb index bce93fec6..de5821d9a 100644 --- a/metrics_api/lib/opentelemetry/metrics/meter.rb +++ b/metrics_api/lib/opentelemetry/metrics/meter.rb @@ -60,7 +60,7 @@ def create_instrument(kind, name, unit, description, callback) raise InstrumentNameError if name.empty? raise InstrumentNameError unless NAME_REGEX.match?(name) raise InstrumentUnitError if unit && (!unit.ascii_only? || unit.size > 63) - raise InstrumentDescriptionError if description && (description.size > 1023 || !utf8mb3_encoding?(description)) + raise InstrumentDescriptionError if description && (description.size > 1023 || !utf8mb3_encoding?(description.dup)) @mutex.synchronize do raise DuplicateInstrumentError if @registry.include? name diff --git a/metrics_sdk/test/opentelemetry/sdk/metrics/meter_test.rb b/metrics_sdk/test/opentelemetry/sdk/metrics/meter_test.rb index 8c1b3bff3..be7014f87 100644 --- a/metrics_sdk/test/opentelemetry/sdk/metrics/meter_test.rb +++ b/metrics_sdk/test/opentelemetry/sdk/metrics/meter_test.rb @@ -1,4 +1,4 @@ -# frozen_string_literal: false +# frozen_string_literal: true # Copyright The OpenTelemetry Authors # From 552b71940ba22fc8e87e7b775cc2253f076316de Mon Sep 17 00:00:00 2001 From: Robert Laurin Date: Wed, 11 May 2022 19:40:53 -0500 Subject: [PATCH 11/25] wip: safety commit --- metrics_sdk/lib/opentelemetry/sdk/metrics.rb | 4 +- .../opentelemetry/sdk/metrics/aggregation.rb | 17 ++++++ .../metrics/aggregation/drop_aggregation.rb | 16 ++++++ .../metrics/aggregation/sum_aggregation.rb | 17 ++++++ .../lib/opentelemetry/sdk/metrics/export.rb | 4 +- ...exporter.rb => console_metric_exporter.rb} | 2 +- .../export/in_memory_metric_pull_exporter.rb | 52 +++++++++++++++++++ .../sdk/metrics/export/metric_reader.rb | 17 +++++- .../periodic_exporting_metric_reader.rb | 35 +++++++++++++ .../opentelemetry/sdk/metrics/histogram.rb | 0 .../sdk/metrics/instrument/counter.rb | 11 ++-- .../sdk/metrics/instrument/histogram.rb | 5 +- .../sdk/metrics/instrument/up_down_counter.rb | 5 +- .../lib/opentelemetry/sdk/metrics/meter.rb | 12 ++--- .../sdk/metrics/meter_provider.rb | 6 ++- .../lib/opentelemetry/sdk/metrics/state.rb | 19 +++++++ .../sdk/metrics/state/metric_store.rb | 51 ++++++++++++++++++ .../metrics/state/metric_store_registry.rb | 47 +++++++++++++++++ .../sdk/metrics/state/metric_stream.rb | 48 +++++++++++++++++ .../in_memory_metric_pull_exporter_test.rb | 33 ++++++++++++ .../sdk/metrics/instrument/counter_test.rb | 11 ++++ .../sdk/metrics/meter_provider_test.rb | 20 ++++--- .../opentelemetry/sdk/metrics/meter_test.rb | 40 +++++++------- 23 files changed, 428 insertions(+), 44 deletions(-) create mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation.rb create mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/drop_aggregation.rb create mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/sum_aggregation.rb rename metrics_sdk/lib/opentelemetry/sdk/metrics/export/{console_exporter.rb => console_metric_exporter.rb} (93%) create mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/export/in_memory_metric_pull_exporter.rb create mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader.rb create mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/histogram.rb create mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/state.rb create mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_store.rb create mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_store_registry.rb create mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_stream.rb create mode 100644 metrics_sdk/test/integration/in_memory_metric_pull_exporter_test.rb create mode 100644 metrics_sdk/test/opentelemetry/sdk/metrics/instrument/counter_test.rb diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics.rb index 1dff64d96..c6b9a73e7 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics.rb @@ -13,7 +13,9 @@ module Metrics end end +require 'opentelemetry/sdk/metrics/configuration_patch' +require 'opentelemetry/sdk/metrics/export' require 'opentelemetry/sdk/metrics/instrument' require 'opentelemetry/sdk/metrics/meter' require 'opentelemetry/sdk/metrics/meter_provider' -require 'opentelemetry/sdk/metrics/configuration_patch' +require 'opentelemetry/sdk/metrics/state' diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation.rb new file mode 100644 index 000000000..e83fc94e4 --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Metrics + module Aggregation + + end + end + end +end + +require 'opentelemetry/sdk/metrics/aggregation/sum_aggregation' diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/drop_aggregation.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/drop_aggregation.rb new file mode 100644 index 000000000..75142bec8 --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/drop_aggregation.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Metrics + module Aggregation + class DropAggregation + end + end + end + end +end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/sum_aggregation.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/sum_aggregation.rb new file mode 100644 index 000000000..ebf8309af --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/sum_aggregation.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Metrics + module Aggregation + class SumAggregation + end + end + end + end +end + diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/export.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/export.rb index aa98919a6..b1d916b01 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/export.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/export.rb @@ -24,4 +24,6 @@ module Export end require 'opentelemetry/sdk/metrics/export/metric_reader' -require 'opentelemetry/sdk/metrics/export/console_exporter' +require 'opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader' +require 'opentelemetry/sdk/metrics/export/console_metric_exporter' +require 'opentelemetry/sdk/metrics/export/in_memory_metric_pull_exporter' diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/export/console_exporter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/export/console_metric_exporter.rb similarity index 93% rename from metrics_sdk/lib/opentelemetry/sdk/metrics/export/console_exporter.rb rename to metrics_sdk/lib/opentelemetry/sdk/metrics/export/console_metric_exporter.rb index 94278fb3d..70f01cceb 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/export/console_exporter.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/export/console_metric_exporter.rb @@ -8,7 +8,7 @@ module OpenTelemetry module SDK module Metrics module Export - class ConsoleExporter + class ConsoleMetricExporter PREFERRED_TEMPORALITY = 'delta' def export(metrics) 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 new file mode 100644 index 000000000..150ed7ad3 --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/export/in_memory_metric_pull_exporter.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Metrics + module Export + class InMemoryMetricPullExporter < MetricReader + PREFERRED_TEMPORALITY = 'delta' + + attr_reader :metric_snapshots + + def initialize + @metric_snapshots = [] + @mutex = Mutex.new + end + + def pull + export(collect) + end + + def export(metrics) + @mutex.synchronize do + return FAILURE if @stopped + + @metric_snapshots << metrics + end + SUCCESS + end + + def reset + @mutex.synchronize do + @metric_snapshots.clear + end + end + + def shutdown + SUCCESS + end + + def preferred_temporality + PREFERRED_TEMPORALITY + end + end + end + end + end +end + diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/export/metric_reader.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/export/metric_reader.rb index d4913af9c..d5e2b31ff 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/export/metric_reader.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/export/metric_reader.rb @@ -13,11 +13,26 @@ def initialize(exporter) @exporter = exporter end - def collect; end + def collect + @metric_store.collect + end + + # @api private + def metric_store=(metric_store) + if defined?(:@metric_store) && !@metric_store.nil? + OpenTelemetry.handle_error(message: 'repeated attempts to set metric_store on metric reader') + else + @metric_store = metric_store + end + end def shutdown(timeout: nil) SUCCESS end + + def force_flush(timeout: nil) + SUCCESS + end end end end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader.rb new file mode 100644 index 000000000..fcd5e06bb --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Metrics + module Export + class PeriodicExportingMetricReader < MetricReader + + # exporter - the push exporter where the metrics are sent to. + # exportIntervalMillis - the time interval in milliseconds between two consecutive exports. The default value is 60000 (milliseconds). + # exportTimeoutMillis - how long the export can run before it is cancelled. The default value is 30000 (milliseconds). + def initialize(exporter, aggregation_temporality: nil, export_interval_millis: 60_000, export_timeout_millis: 30_000) + @exporter = exporter + @export_interval_millis = export_interval_millis + @export_timeout_millis = export_timeout_millis + @aggregation_temporality = aggregation_temporality || exporter.preferred_temporality # || Cumulative + + @thread = Thread.new { work } + end + + private + + def work + sleep(3) + @exporter.export(collect) + end + end + end + end + end +end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/histogram.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/histogram.rb new file mode 100644 index 000000000..e69de29bb diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/counter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/counter.rb index de29dcc99..e3dc59169 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/counter.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/counter.rb @@ -10,13 +10,14 @@ module Metrics module Instrument # {Counter} is the SDK implementation of {OpenTelemetry::Metrics::Counter}. class Counter < OpenTelemetry::Metrics::Instrument::Counter - attr_reader :name, :unit, :description + attr_reader :name, :unit, :description, :instrumentation_library - def initialize(name, unit, description, meter) + def initialize(name, unit, description, metric_store_registry, instrumentation_library) @name = name @unit = unit @description = description - @meter = meter + @metric_store_registry = metric_store_registry + @instrumentation_library = instrumentation_library end # Increment the Counter by a fixed amount. @@ -27,6 +28,10 @@ def initialize(name, unit, description, meter) # Array values must not contain nil elements and all elements must be of # the same basic type (string, numeric, boolean). def add(increment, attributes: nil) + @metric_store_registry.produce( + OpenTelemetry::Metrics::Measurement.new(increment, attributes), + self + ) end end end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/histogram.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/histogram.rb index 65e343381..ea3b6b9a1 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/histogram.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/histogram.rb @@ -12,11 +12,12 @@ module Instrument class Histogram < OpenTelemetry::Metrics::Instrument::Histogram attr_reader :name, :unit, :description - def initialize(name, unit, description, meter) + def initialize(name, unit, description, metric_store_registry, instrumentation_library) @name = name @unit = unit @description = description - @meter = meter + @metric_store_registry = metric_store_registry + @instrumentation_library = instrumentation_library end # Updates the statistics with the specified amount. diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/up_down_counter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/up_down_counter.rb index d3b82fb0e..644dd5f04 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/up_down_counter.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/up_down_counter.rb @@ -12,11 +12,12 @@ module Instrument class UpDownCounter < OpenTelemetry::Metrics::Instrument::UpDownCounter attr_reader :name, :unit, :description - def initialize(name, unit, description, meter) + def initialize(name, unit, description, metric_store_registry, instrumentation_library) @name = name @unit = unit @description = description - @meter = meter + @metric_store_registry = metric_store_registry + @instrumentation_library = instrumentation_library end # Increment or decrement the UpDownCounter by a fixed amount. diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb index 9ec54dc65..5c9727319 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb @@ -29,12 +29,12 @@ def initialize(name, version, meter_provider) def create_instrument(kind, name, unit, description, callback) super do case kind - when :counter then OpenTelemetry::SDK::Metrics::Instrument::Counter.new(name, unit, description, self) - when :observable_counter then OpenTelemetry::SDK::Metrics::Instrument::ObservableCounter.new(name, unit, description, callback, self) - when :histogram then OpenTelemetry::SDK::Metrics::Instrument::Histogram.new(name, unit, description, self) - when :observable_gauge then OpenTelemetry::SDK::Metrics::Instrument::ObservableGauge.new(name, unit, description, callback, self) - when :up_down_counter then OpenTelemetry::SDK::Metrics::Instrument::UpDownCounter.new(name, unit, description, self) - when :observable_up_down_counter then OpenTelemetry::SDK::Metrics::Instrument::ObservableUpDownCounter.new(name, unit, description, callback, self) + when :counter then OpenTelemetry::SDK::Metrics::Instrument::Counter.new(name, unit, description, @meter_provider.metric_store_registry, @instrumentation_library) + when :observable_counter then OpenTelemetry::SDK::Metrics::Instrument::ObservableCounter.new(name, unit, description, callback, @meter_provider.metric_store_registry, @instrumentation_library) + when :histogram then OpenTelemetry::SDK::Metrics::Instrument::Histogram.new(name, unit, description, @meter_provider.metric_store_registry, @instrumentation_library) + when :observable_gauge then OpenTelemetry::SDK::Metrics::Instrument::ObservableGauge.new(name, unit, description, callback, @meter_provider.metric_store_registry, @instrumentation_library) + when :up_down_counter then OpenTelemetry::SDK::Metrics::Instrument::UpDownCounter.new(name, unit, description, @meter_provider.metric_store_registry, @instrumentation_library) + when :observable_up_down_counter then OpenTelemetry::SDK::Metrics::Instrument::ObservableUpDownCounter.new(name, unit, description, callback, @meter_provider.metric_store_registry, @instrumentation_library) end end end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb index ec46352c3..27e3e048f 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb @@ -14,13 +14,14 @@ class MeterProvider < OpenTelemetry::Metrics::MeterProvider Key = Struct.new(:name, :version) private_constant(:Key) + attr_reader :metric_store_registry + def initialize(resource: OpenTelemetry::SDK::Resources::Resource.create) @mutex = Mutex.new @registry = {} @stopped = false - @metric_readers = [] - @views = [] + @metric_store_registry = State::MetricStoreRegistry.new(resource) end # Returns a {Meter} instance. @@ -107,6 +108,7 @@ def add_metric_reader(metric_reader) if @stopped OpenTelemetry.logger.warn('calling MetricProvider#add_metric_reader after shutdown.') else + metric_reader.metric_store = @metric_store_registry.add_metric_store @metric_readers.push(metric_reader) end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/state.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/state.rb new file mode 100644 index 000000000..d9a2bb395 --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/state.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Metrics + module State + end + end + end +end + +require 'opentelemetry/sdk/metrics/state/metric_store' +require 'opentelemetry/sdk/metrics/state/metric_store_registry' +require 'opentelemetry/sdk/metrics/state/metric_stream' + diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_store.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_store.rb new file mode 100644 index 000000000..d6d05439b --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_store.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Metrics + module State + class MetricStore + def initialize + @mutex = Mutex.new + # @epoch_start_time = now + @metric_streams = {} + end + + def collect + # this probably needs to take the mutex, take a snapshot of state, (reset state?), release the mutex + # end_time = now + @metric_streams.map do |_k, metric_stream| + metric_stream + end + # @epoch_start_time = end_time + end + + def record(measurement, instrument, resource) + # compute metric stream name + # find or create the metric stream + # run aggregation on the metric stream + + # need to block on lookup or creation + # if aggregation is quick hold the lock? + # otherwise we want to release the lock for the lookup/creation + # and takeup a lock on the aggregator + + metric_stream = @mutex.synchronize do + if @metric_streams[instrument.to_s] + @metric_streams[instrument.to_s] + else + @metric_streams[instrument.to_s] = MetricStream.new(instrument, resource) + end + end + + metric_stream.update(measurement) + end + end + end + end + end +end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_store_registry.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_store_registry.rb new file mode 100644 index 000000000..07de45e46 --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_store_registry.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Metrics + module State + class MetricStoreRegistry + def initialize(resource) + @resource = resource + @metric_stores = [] + end + + def add_metric_store + new_metric_store = MetricStore.new + @metric_stores = @metric_stores.dup.push(new_metric_store) + new_metric_store + end + + def produce(measurement, instrument) + # Info we need to build a metric: + # measurement + # @meter_provider.resource + # instrument.class? or a symbol representing the instrument 'kind' from Meter.create_instrument + # instrument.name + # instrument.unit + # instrument.description + # instrument.instrumentation_library + + # TODO: once we add views + # instrument.views do |view| + # view.metrics(measurement, instrument, @meter_provider.resource) do |metric| + # @metric_stores.each { |ms| ms.record(metric) } + # end + # end + + # record metric stream + @metric_stores.each { |ms| ms.record(measurement, instrument, @resource) } + end + end + end + end + end +end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_stream.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_stream.rb new file mode 100644 index 000000000..41b5caca9 --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_stream.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Metrics + module State + class MetricStream + attr_reader :instrument, :resource + + def initialize(instrument, resource) + @instrument = instrument + @resource = resource + @data_points = {} + @mutex = Mutex.new + end + + def update(measurement) + @mutex.synchronize do + if @data_points[measurement.attributes] + @data_points[measurement.attributes] = data_point + measurement.value + else + @data_points[measurement.attributes] = measurement.value + end + end + end + + def to_s + metric_stream_string = String.new + metric_stream_string << "name=#{instrument.name}" + metric_stream_string << " description=#{instrument.description}" if instrument.description + metric_stream_string << " unit=#{instrument.unit}" if instrument.unit + map = @data_points.map do |attributes, value| + str = String.new + str << metric_stream_string + str << " attributes=#{attributes}" if attributes + str << " #{value}" + end + map.join("\n") + end + end + end + end + end +end diff --git a/metrics_sdk/test/integration/in_memory_metric_pull_exporter_test.rb b/metrics_sdk/test/integration/in_memory_metric_pull_exporter_test.rb new file mode 100644 index 000000000..dc639ced1 --- /dev/null +++ b/metrics_sdk/test/integration/in_memory_metric_pull_exporter_test.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'test_helper' + +describe OpenTelemetry::SDK do + describe '#configure' do + before { reset_metrics_sdk } + + it 'emits metrics' do + meter = OpenTelemetry.meter_provider.meter("test") + instrument = meter.create_counter("b_counter") + + OpenTelemetry::SDK.configure + + metric_exporter = OpenTelemetry::SDK::Metrics::Export::InMemoryMetricPullExporter.new + OpenTelemetry.meter_provider.add_metric_reader(metric_exporter) + + instrument.add(1) + instrument.add(2, attributes: { 'a' => 'b' }) + instrument.add(3, attributes: { 'b' => 'c' }) + instrument.add(4, attributes: { 'd' => 'e' }) + + metric_exporter.pull + last_snapshot = metric_exporter.metric_snapshots.last[0] + _(last_snapshot.instrument.name).must_equal('b_counter') + _(last_snapshot.instrument.instrumentation_library.name).must_equal('test') + end + end +end diff --git a/metrics_sdk/test/opentelemetry/sdk/metrics/instrument/counter_test.rb b/metrics_sdk/test/opentelemetry/sdk/metrics/instrument/counter_test.rb new file mode 100644 index 000000000..71ee62b6b --- /dev/null +++ b/metrics_sdk/test/opentelemetry/sdk/metrics/instrument/counter_test.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'test_helper' + +describe OpenTelemetry::SDK::Metrics::Instrument::Counter do + +end diff --git a/metrics_sdk/test/opentelemetry/sdk/metrics/meter_provider_test.rb b/metrics_sdk/test/opentelemetry/sdk/metrics/meter_provider_test.rb index 692eda7ed..3829e11a4 100644 --- a/metrics_sdk/test/opentelemetry/sdk/metrics/meter_provider_test.rb +++ b/metrics_sdk/test/opentelemetry/sdk/metrics/meter_provider_test.rb @@ -50,7 +50,7 @@ end it 'returns a timeout response when it times out' do - mock_metric_reader = Minitest::Mock.new + mock_metric_reader = new_mock_reader mock_metric_reader.expect(:nothing_gets_called_because_it_times_out_first, nil) OpenTelemetry.meter_provider.add_metric_reader(mock_metric_reader) @@ -58,8 +58,8 @@ end it 'invokes shutdown on all registered Metric Readers' do - mock_metric_reader1 = Minitest::Mock.new - mock_metric_reader2 = Minitest::Mock.new + mock_metric_reader1 = new_mock_reader + mock_metric_reader2 = new_mock_reader mock_metric_reader1.expect(:shutdown, nil, [{ timeout: nil }]) mock_metric_reader2.expect(:shutdown, nil, [{ timeout: nil }]) @@ -74,7 +74,7 @@ describe '#force_flush' do it 'returns a timeout response when it times out' do - mock_metric_reader = Minitest::Mock.new + mock_metric_reader = new_mock_reader mock_metric_reader.expect(:nothing_gets_called_because_it_times_out_first, nil) OpenTelemetry.meter_provider.add_metric_reader(mock_metric_reader) @@ -82,8 +82,8 @@ end it 'invokes force_flush on all registered Metric Readers' do - mock_metric_reader1 = Minitest::Mock.new - mock_metric_reader2 = Minitest::Mock.new + mock_metric_reader1 = new_mock_reader + mock_metric_reader2 = new_mock_reader mock_metric_reader1.expect(:force_flush, nil, [{ timeout: nil }]) mock_metric_reader2.expect(:force_flush, nil, [{ timeout: nil }]) OpenTelemetry.meter_provider.add_metric_reader(mock_metric_reader1) @@ -99,7 +99,7 @@ describe '#add_metric_reader' do it 'adds a metric reader' do metric_reader = OpenTelemetry::SDK::Metrics::Export::MetricReader.new( - OpenTelemetry::SDK::Metrics::Export::ConsoleExporter.new + OpenTelemetry::SDK::Metrics::Export::ConsoleMetricExporter.new ) OpenTelemetry.meter_provider.add_metric_reader(metric_reader) @@ -114,4 +114,10 @@ # OpenTelemetry.meter_provider.add_view end end + + private + + def new_mock_reader + Minitest::Mock.new(OpenTelemetry::SDK::Metrics::Export::MetricReader.new(nil)) + end end diff --git a/metrics_sdk/test/opentelemetry/sdk/metrics/meter_test.rb b/metrics_sdk/test/opentelemetry/sdk/metrics/meter_test.rb index be7014f87..7d4678de0 100644 --- a/metrics_sdk/test/opentelemetry/sdk/metrics/meter_test.rb +++ b/metrics_sdk/test/opentelemetry/sdk/metrics/meter_test.rb @@ -17,35 +17,39 @@ _(instrument).must_be_instance_of OpenTelemetry::SDK::Metrics::Instrument::Counter end end - describe '#create_observable_counter' do - it 'creates a observable_counter instrument' do - instrument = meter.create_observable_counter('a_observable_counter', unit: 'minutes', description: 'useful description', callback: nil) - _(instrument).must_be_instance_of OpenTelemetry::SDK::Metrics::Instrument::ObservableCounter - end - end + + # describe '#create_observable_counter' do + # it 'creates a observable_counter instrument' do + # instrument = meter.create_observable_counter('a_observable_counter', unit: 'minutes', description: 'useful description', callback: nil) + # _(instrument).must_be_instance_of OpenTelemetry::SDK::Metrics::Instrument::ObservableCounter + # end + # end + describe '#create_histogram' do it 'creates a histogram instrument' do instrument = meter.create_histogram('a_histogram', unit: 'minutes', description: 'useful description') _(instrument).must_be_instance_of OpenTelemetry::SDK::Metrics::Instrument::Histogram end end - describe '#create_observable_gauge' do - it 'creates a observable_gauge instrument' do - instrument = meter.create_observable_gauge('a_observable_gauge', unit: 'minutes', description: 'useful description', callback: nil) - _(instrument).must_be_instance_of OpenTelemetry::SDK::Metrics::Instrument::ObservableGauge - end - end + + # describe '#create_observable_gauge' do + # it 'creates a observable_gauge instrument' do + # instrument = meter.create_observable_gauge('a_observable_gauge', unit: 'minutes', description: 'useful description', callback: nil) + # _(instrument).must_be_instance_of OpenTelemetry::SDK::Metrics::Instrument::ObservableGauge + # end + # end + describe '#create_up_down_counter' do it 'creates a up_down_counter instrument' do instrument = meter.create_up_down_counter('a_up_down_counter', unit: 'minutes', description: 'useful description') _(instrument).must_be_instance_of OpenTelemetry::SDK::Metrics::Instrument::UpDownCounter end end - describe '#create_observable_up_down_counter' do - it 'creates a observable_up_down_counter instrument' do - instrument = meter.create_observable_up_down_counter('a_observable_up_down_counter', unit: 'minutes', description: 'useful description', callback: nil) - _(instrument).must_be_instance_of OpenTelemetry::SDK::Metrics::Instrument::ObservableUpDownCounter - end - end + # describe '#create_observable_up_down_counter' do + # it 'creates a observable_up_down_counter instrument' do + # instrument = meter.create_observable_up_down_counter('a_observable_up_down_counter', unit: 'minutes', description: 'useful description', callback: nil) + # _(instrument).must_be_instance_of OpenTelemetry::SDK::Metrics::Instrument::ObservableUpDownCounter + # end + # end end From c1955be97a931602de26e57bfc3b9741cc3a3390 Mon Sep 17 00:00:00 2001 From: Robert Laurin Date: Fri, 13 May 2022 11:08:52 -0500 Subject: [PATCH 12/25] chore: rubocops --- metrics_sdk/Gemfile | 2 +- metrics_sdk/lib/opentelemetry-metrics-sdk.rb | 1 - .../lib/opentelemetry/sdk/metrics/aggregation.rb | 1 - .../sdk/metrics/aggregation/sum_aggregation.rb | 1 - .../opentelemetry/sdk/metrics/configuration_patch.rb | 1 - .../sdk/metrics/export/console_metric_exporter.rb | 1 - .../metrics/export/in_memory_metric_pull_exporter.rb | 1 - .../opentelemetry/sdk/metrics/export/metric_reader.rb | 1 - .../metrics/export/periodic_exporting_metric_reader.rb | 1 - metrics_sdk/lib/opentelemetry/sdk/metrics/state.rb | 1 - .../opentelemetry/sdk/metrics/state/metric_store.rb | 6 +----- .../opentelemetry/sdk/metrics/state/metric_stream.rb | 10 +++++----- .../integration/in_memory_metric_pull_exporter_test.rb | 4 ++-- metrics_sdk/test/opentelemetry/metrics_sdk_test.rb | 10 ++++------ .../sdk/metrics/instrument/counter_test.rb | 1 - 15 files changed, 13 insertions(+), 29 deletions(-) diff --git a/metrics_sdk/Gemfile b/metrics_sdk/Gemfile index 01b69a3d4..327e4410b 100644 --- a/metrics_sdk/Gemfile +++ b/metrics_sdk/Gemfile @@ -9,8 +9,8 @@ source 'https://rubygems.org' gemspec gem 'opentelemetry-api', path: '../api' -gem 'opentelemetry-sdk', path: '../sdk' gem 'opentelemetry-metrics-api', path: '../metrics_api' +gem 'opentelemetry-sdk', path: '../sdk' group :test, :development do gem 'pry' diff --git a/metrics_sdk/lib/opentelemetry-metrics-sdk.rb b/metrics_sdk/lib/opentelemetry-metrics-sdk.rb index a64dc3a6d..bda7992d3 100644 --- a/metrics_sdk/lib/opentelemetry-metrics-sdk.rb +++ b/metrics_sdk/lib/opentelemetry-metrics-sdk.rb @@ -7,4 +7,3 @@ require 'opentelemetry/sdk' require 'opentelemetry-metrics-api' require 'opentelemetry/sdk/metrics' - diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation.rb index e83fc94e4..db1ffac52 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation.rb @@ -8,7 +8,6 @@ module OpenTelemetry module SDK module Metrics module Aggregation - end end end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/sum_aggregation.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/sum_aggregation.rb index ebf8309af..c4dc38940 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/sum_aggregation.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/sum_aggregation.rb @@ -14,4 +14,3 @@ class SumAggregation end end end - diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/configuration_patch.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/configuration_patch.rb index 708958e84..53b0b1f7f 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/configuration_patch.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/configuration_patch.rb @@ -10,7 +10,6 @@ module Metrics # The ConfiguratorPatch implements a hook to configure the metrics # portion of the SDK. module ConfiguratorPatch - private # The metrics_configuration_hook method is where we define the setup process diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/export/console_metric_exporter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/export/console_metric_exporter.rb index 70f01cceb..ae4ca9808 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/export/console_metric_exporter.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/export/console_metric_exporter.rb @@ -27,4 +27,3 @@ def preferred_temporality end end end - 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 150ed7ad3..3f075b159 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 @@ -49,4 +49,3 @@ def preferred_temporality end end end - diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/export/metric_reader.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/export/metric_reader.rb index d5e2b31ff..17569681c 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/export/metric_reader.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/export/metric_reader.rb @@ -38,4 +38,3 @@ def force_flush(timeout: nil) end end end - diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader.rb index fcd5e06bb..54e5970e5 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader.rb @@ -9,7 +9,6 @@ module SDK module Metrics module Export class PeriodicExportingMetricReader < MetricReader - # exporter - the push exporter where the metrics are sent to. # exportIntervalMillis - the time interval in milliseconds between two consecutive exports. The default value is 60000 (milliseconds). # exportTimeoutMillis - how long the export can run before it is cancelled. The default value is 30000 (milliseconds). diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/state.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/state.rb index d9a2bb395..172488b1c 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/state.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/state.rb @@ -16,4 +16,3 @@ module State require 'opentelemetry/sdk/metrics/state/metric_store' require 'opentelemetry/sdk/metrics/state/metric_store_registry' require 'opentelemetry/sdk/metrics/state/metric_stream' - diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_store.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_store.rb index d6d05439b..3814b4e7e 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_store.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_store.rb @@ -35,11 +35,7 @@ def record(measurement, instrument, resource) # and takeup a lock on the aggregator metric_stream = @mutex.synchronize do - if @metric_streams[instrument.to_s] - @metric_streams[instrument.to_s] - else - @metric_streams[instrument.to_s] = MetricStream.new(instrument, resource) - end + @metric_streams[instrument.to_s] || @metric_streams[instrument.to_s] = MetricStream.new(instrument, resource) end metric_stream.update(measurement) diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_stream.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_stream.rb index 41b5caca9..f352a2179 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_stream.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_stream.rb @@ -20,11 +20,11 @@ def initialize(instrument, resource) def update(measurement) @mutex.synchronize do - if @data_points[measurement.attributes] - @data_points[measurement.attributes] = data_point + measurement.value - else - @data_points[measurement.attributes] = measurement.value - end + @data_points[measurement.attributes] = if @data_points[measurement.attributes] + data_point + measurement.value + else + measurement.value + end end end diff --git a/metrics_sdk/test/integration/in_memory_metric_pull_exporter_test.rb b/metrics_sdk/test/integration/in_memory_metric_pull_exporter_test.rb index dc639ced1..2b5e57c15 100644 --- a/metrics_sdk/test/integration/in_memory_metric_pull_exporter_test.rb +++ b/metrics_sdk/test/integration/in_memory_metric_pull_exporter_test.rb @@ -11,8 +11,8 @@ before { reset_metrics_sdk } it 'emits metrics' do - meter = OpenTelemetry.meter_provider.meter("test") - instrument = meter.create_counter("b_counter") + meter = OpenTelemetry.meter_provider.meter('test') + instrument = meter.create_counter('b_counter') OpenTelemetry::SDK.configure diff --git a/metrics_sdk/test/opentelemetry/metrics_sdk_test.rb b/metrics_sdk/test/opentelemetry/metrics_sdk_test.rb index ee1d7e2a6..62a9ecf5c 100644 --- a/metrics_sdk/test/opentelemetry/metrics_sdk_test.rb +++ b/metrics_sdk/test/opentelemetry/metrics_sdk_test.rb @@ -12,8 +12,8 @@ it 'upgrades the API MeterProvider, Meters, and Instruments' do meter_provider = OpenTelemetry.meter_provider - meter = meter_provider.meter("test") - instrument = meter.create_counter("a_counter") + meter = meter_provider.meter('test') + instrument = meter.create_counter('a_counter') # Calls before the SDK is configured return Proxy implementations _(meter_provider).must_be_instance_of OpenTelemetry::Internal::ProxyMeterProvider @@ -30,7 +30,7 @@ # Calls after the SDK is configured now return the SDK implementations directly _(OpenTelemetry.meter_provider).must_be_instance_of OpenTelemetry::SDK::Metrics::MeterProvider _(OpenTelemetry.meter_provider.meter('test')).must_be_instance_of OpenTelemetry::SDK::Metrics::Meter - _(OpenTelemetry.meter_provider.meter('test').create_counter("b_counter")).must_be_instance_of OpenTelemetry::SDK::Metrics::Instrument::Counter + _(OpenTelemetry.meter_provider.meter('test').create_counter('b_counter')).must_be_instance_of OpenTelemetry::SDK::Metrics::Instrument::Counter end it 'sends the original configuration error to the error handler' do @@ -42,9 +42,7 @@ received_message = message end - OpenTelemetry::SDK.configure do |config| - config.do_something - end + OpenTelemetry::SDK.configure(&:do_something) _(received_exception).must_be_instance_of OpenTelemetry::SDK::ConfigurationError _(received_message).must_match(/unexpected configuration error due to undefined method `do_something/) diff --git a/metrics_sdk/test/opentelemetry/sdk/metrics/instrument/counter_test.rb b/metrics_sdk/test/opentelemetry/sdk/metrics/instrument/counter_test.rb index 71ee62b6b..8e9fb77c9 100644 --- a/metrics_sdk/test/opentelemetry/sdk/metrics/instrument/counter_test.rb +++ b/metrics_sdk/test/opentelemetry/sdk/metrics/instrument/counter_test.rb @@ -7,5 +7,4 @@ require 'test_helper' describe OpenTelemetry::SDK::Metrics::Instrument::Counter do - end From 1791cb7ef1f69465a3de87e7c6aa21cd2fd14ff1 Mon Sep 17 00:00:00 2001 From: Robert Laurin Date: Fri, 13 May 2022 11:09:27 -0500 Subject: [PATCH 13/25] fix: add missing require --- metrics_api/lib/opentelemetry/metrics.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/metrics_api/lib/opentelemetry/metrics.rb b/metrics_api/lib/opentelemetry/metrics.rb index ec586d831..ac10d6062 100644 --- a/metrics_api/lib/opentelemetry/metrics.rb +++ b/metrics_api/lib/opentelemetry/metrics.rb @@ -11,5 +11,6 @@ module Metrics end require 'opentelemetry/metrics/instrument' +require 'opentelemetry/metrics/measurement' require 'opentelemetry/metrics/meter' require 'opentelemetry/metrics/meter_provider' From 9c34b4e826d457b240a5ce1f3a36a195837c407f Mon Sep 17 00:00:00 2001 From: Robert Laurin Date: Mon, 20 Jun 2022 16:08:47 -0500 Subject: [PATCH 14/25] Synchronous instruments --- exporter/otlp-common/Gemfile | 2 + .../lib/opentelemetry/exporter/otlp/common.rb | 62 ++++ .../exporter/otlp/common/common_test.rb | 35 +++ exporter/otlp-common/test/test_helper.rb | 1 + exporter/otlp-http/Gemfile | 2 + .../lib/opentelemetry/exporter/otlp/http.rb | 1 + .../exporter/otlp/http/metric_exporter.rb | 287 ++++++++++++++++++ .../otlp/http/metric_exporter_test.rb | 43 +++ exporter/otlp-http/test/test_helper.rb | 1 + .../lib/opentelemetry/internal/proxy_meter.rb | 2 +- .../lib/opentelemetry/metrics/meter.rb | 26 +- metrics_api/opentelemetry-metrics-api.gemspec | 1 + .../test/opentelemetry/metrics/meter_test.rb | 10 +- metrics_api/test/test_helper.rb | 1 + metrics_sdk/Gemfile | 1 + .../metrics/aggregation/sum_aggregation.rb | 1 + .../export/in_memory_metric_pull_exporter.rb | 3 +- .../sdk/metrics/export/metric_reader.rb | 12 +- .../opentelemetry/sdk/metrics/histogram.rb | 0 .../opentelemetry/sdk/metrics/instrument.rb | 1 + .../sdk/metrics/instrument/counter.rb | 22 +- .../sdk/metrics/instrument/histogram.rb | 19 +- .../instrument/synchronous_instrument.rb | 48 +++ .../sdk/metrics/instrument/up_down_counter.rb | 19 +- .../lib/opentelemetry/sdk/metrics/meter.rb | 14 +- .../sdk/metrics/meter_provider.rb | 14 +- .../lib/opentelemetry/sdk/metrics/state.rb | 1 - .../sdk/metrics/state/metric_store.rb | 34 +-- .../metrics/state/metric_store_registry.rb | 47 --- .../sdk/metrics/state/metric_stream.rb | 50 +-- metrics_sdk/opentelemetry-metrics-sdk.gemspec | 1 + .../in_memory_metric_pull_exporter_test.rb | 15 +- .../sdk/metrics/meter_provider_test.rb | 28 ++ .../opentelemetry/sdk/metrics/meter_test.rb | 46 +-- .../sdk/metrics/state/metric_store_test.rb | 15 + .../sdk/metrics/state/metric_stream_test.rb | 12 + metrics_sdk/test/test_helper.rb | 1 + .../lib/opentelemetry/test_helpers.rb | 22 ++ 38 files changed, 715 insertions(+), 185 deletions(-) create mode 100644 exporter/otlp-http/lib/opentelemetry/exporter/otlp/http/metric_exporter.rb create mode 100644 exporter/otlp-http/test/opentelemetry/exporter/otlp/http/metric_exporter_test.rb delete mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/histogram.rb create mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/synchronous_instrument.rb delete mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_store_registry.rb create mode 100644 metrics_sdk/test/opentelemetry/sdk/metrics/state/metric_store_test.rb create mode 100644 metrics_sdk/test/opentelemetry/sdk/metrics/state/metric_stream_test.rb diff --git a/exporter/otlp-common/Gemfile b/exporter/otlp-common/Gemfile index 08f000453..57b3af201 100644 --- a/exporter/otlp-common/Gemfile +++ b/exporter/otlp-common/Gemfile @@ -11,5 +11,7 @@ gemspec group :test, :development do gem 'opentelemetry-api', path: '../../api' gem 'opentelemetry-sdk', path: '../../sdk' + gem 'opentelemetry-metrics-sdk', path: '../../metrics_sdk' + gem 'opentelemetry-metrics-api', path: '../../metrics_api' gem 'opentelemetry-test-helpers', path: '../../test_helpers' end diff --git a/exporter/otlp-common/lib/opentelemetry/exporter/otlp/common.rb b/exporter/otlp-common/lib/opentelemetry/exporter/otlp/common.rb index 745d60ee9..48f6ab6d2 100644 --- a/exporter/otlp-common/lib/opentelemetry/exporter/otlp/common.rb +++ b/exporter/otlp-common/lib/opentelemetry/exporter/otlp/common.rb @@ -14,6 +14,9 @@ require 'opentelemetry/proto/trace/v1/trace_pb' require 'opentelemetry/proto/collector/trace/v1/trace_service_pb' +require 'opentelemetry/proto/metrics/v1/metrics_pb' +require 'opentelemetry/proto/collector/metrics/v1/metrics_service_pb' + module OpenTelemetry module Exporter module OTLP @@ -68,8 +71,67 @@ def as_etsr(span_data) # rubocop:disable Metrics/MethodLength ) end + def as_encoded_emsr(metric_data) + Opentelemetry::Proto::Collector::Metrics::V1::ExportMetricsServiceRequest.encode(as_emsr(metric_data)) + end + + def as_emsr(metric_data) + Opentelemetry::Proto::Collector::Metrics::V1::ExportMetricsServiceRequest.new( + resource_metrics: metric_data + .group_by(&:resource) + .map do |resource, metric_datas| + 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) } + ), + instrumentation_library_metrics: metric_datas + .group_by(&:instrumentation_library) + .map do |il, mds| + Opentelemetry::Proto::Metrics::V1::InstrumentationLibraryMetrics.new( + instrumentation_library: Opentelemetry::Proto::Common::V1::InstrumentationLibrary.new( + name: il.name, + version: il.version + ), + metrics: mds.group_by.map do |metric_stream| + as_otlp_metric(metric_stream) + end + ) + end + ) + end + ) + end + private + def as_otlp_metric(metric_stream) + case metric_stream.instrument_kind + when :counter then as_sum_metric(metric_stream) + end + end + + def as_sum_metric(metric_stream) + Opentelemetry::Proto::Metrics::V1::Metric.new( + name: metric_stream.name, + description: metric_stream.description, + unit: metric_stream.unit, + sum: Opentelemetry::Proto::Metrics::V1::Sum.new( + data_points: metric_stream.data_points.map { |attr, val| as_sum_data_point(attr, val) } + ), + ) + end + + def as_sum_data_point(attributes, value) + Opentelemetry::Proto::Metrics::V1::NumberDataPoint.new( + attributes: attributes&.map { |k, v| as_otlp_key_value(k, v) }, + as_int: value + ) + end + + def as_otlp_string_key_value(key, value) + Opentelemetry::Proto::Common::V1::StringKeyValue.new(key: key, value: value) + end + def as_otlp_span(span_data) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength Opentelemetry::Proto::Trace::V1::Span.new( trace_id: span_data.trace_id, diff --git a/exporter/otlp-common/test/opentelemetry/exporter/otlp/common/common_test.rb b/exporter/otlp-common/test/opentelemetry/exporter/otlp/common/common_test.rb index f3d7ab56f..c1232337d 100644 --- a/exporter/otlp-common/test/opentelemetry/exporter/otlp/common/common_test.rb +++ b/exporter/otlp-common/test/opentelemetry/exporter/otlp/common/common_test.rb @@ -7,6 +7,41 @@ require 'test_helper' describe OpenTelemetry::Exporter::OTLP::Common do + describe '#as_emsr' do + it 'batches per resource' do + il = OpenTelemetry::SDK::InstrumentationLibrary.new('test_scope', 'v0.0.1') + r = OpenTelemetry::SDK::Resources::Resource.telemetry_sdk + + il2 = OpenTelemetry::SDK::InstrumentationLibrary.new('secondary_scope', 'v0.0.2') + r2 = OpenTelemetry::SDK::Resources::Resource.telemetry_sdk + metrics = [ + create_metric_stream(name: 'test_instrumenta', value: 10, attributes: {'foo'=>'bar'}, resource: r, instrumentation_library: il), + create_metric_stream(name: 'test_instrumenta', value: 10, attributes: {'foo'=>'bar'}, resource: r2, instrumentation_library: il2), + create_metric_stream(name: 'test_instrumentb', value: 10, attributes: {'foo'=>'bar'}, resource: r2, instrumentation_library: il2), + ] + + emsr = OpenTelemetry::Exporter::OTLP::Common.as_emsr(metrics) + + _(emsr.resource_metrics.length).must_equal(2) + _(emsr.resource_metrics[0].instrumentation_library_metrics[0].metrics.length).must_equal(1) + _(emsr.resource_metrics[1].instrumentation_library_metrics[0].metrics.length).must_equal(2) + end + + def create_metric_stream(name: 'test_instrument', description: 'a wonderful instrument', unit: 'cm', instrument_kind: :counter, value: 1, attributes: {}, resource: OpenTelemetry::SDK::Resources::Resource.telemetry_sdk, instrumentation_library: OpenTelemetry::SDK::InstrumentationLibrary.new('test_scope', 'v0.0.1')) + OpenTelemetry::SDK::Metrics::State::MetricStream.new( + name, + description, + unit, + instrument_kind, + value, + attributes, + resource, + instrumentation_library, + ) + end + end + + describe '#as_encoded_etsr' do it 'handles encoding errors with poise and grace' do OpenTelemetry::TestHelpers.with_test_logger do |log_stream| diff --git a/exporter/otlp-common/test/test_helper.rb b/exporter/otlp-common/test/test_helper.rb index 7ae8fe543..1d69ecd98 100644 --- a/exporter/otlp-common/test/test_helper.rb +++ b/exporter/otlp-common/test/test_helper.rb @@ -8,6 +8,7 @@ SimpleCov.start require 'opentelemetry/sdk' +require 'opentelemetry-metrics-sdk' require 'opentelemetry-test-helpers' require 'opentelemetry-exporter-otlp-common' diff --git a/exporter/otlp-http/Gemfile b/exporter/otlp-http/Gemfile index f00a94870..4f69e4166 100644 --- a/exporter/otlp-http/Gemfile +++ b/exporter/otlp-http/Gemfile @@ -14,6 +14,8 @@ group :test, :development do gem 'opentelemetry-api', path: '../../api' gem 'opentelemetry-exporter-otlp-common', path: '../otlp-common' gem 'opentelemetry-sdk', path: '../../sdk' + gem 'opentelemetry-metrics-sdk', path: '../../metrics_sdk' + gem 'opentelemetry-metrics-api', path: '../../metrics_api' gem 'opentelemetry-semantic_conventions', path: '../../semantic_conventions' gem 'opentelemetry-test-helpers', path: '../../test_helpers' end diff --git a/exporter/otlp-http/lib/opentelemetry/exporter/otlp/http.rb b/exporter/otlp-http/lib/opentelemetry/exporter/otlp/http.rb index 5a89d2fff..f8f8fbc4c 100644 --- a/exporter/otlp-http/lib/opentelemetry/exporter/otlp/http.rb +++ b/exporter/otlp-http/lib/opentelemetry/exporter/otlp/http.rb @@ -21,4 +21,5 @@ module HTTP end require 'opentelemetry/exporter/otlp/http/trace_exporter' +require 'opentelemetry/exporter/otlp/http/metric_exporter' require 'opentelemetry/exporter/otlp/http/version' diff --git a/exporter/otlp-http/lib/opentelemetry/exporter/otlp/http/metric_exporter.rb b/exporter/otlp-http/lib/opentelemetry/exporter/otlp/http/metric_exporter.rb new file mode 100644 index 000000000..98c72772b --- /dev/null +++ b/exporter/otlp-http/lib/opentelemetry/exporter/otlp/http/metric_exporter.rb @@ -0,0 +1,287 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'opentelemetry/common' +require 'opentelemetry/exporter/otlp/common' +require 'opentelemetry/sdk' +require 'opentelemetry-metrics-sdk' +require 'net/http' +require 'csv' +require 'zlib' + +require 'google/rpc/status_pb' + +module OpenTelemetry + module Exporter + module OTLP + module HTTP + # An OpenTelemetry metric exporter that sends metrics over HTTP as Protobuf encoded OTLP ExportMetricServiceRequests. + class MetricExporter # rubocop:disable Metrics/ClassLength + SUCCESS = OpenTelemetry::SDK::Metrics::Export::SUCCESS + FAILURE = OpenTelemetry::SDK::Metrics::Export::FAILURE + private_constant(:SUCCESS, :FAILURE) + + # Default timeouts in seconds. + KEEP_ALIVE_TIMEOUT = 30 + RETRY_COUNT = 5 + WRITE_TIMEOUT_SUPPORTED = Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.6') + private_constant(:KEEP_ALIVE_TIMEOUT, :RETRY_COUNT, :WRITE_TIMEOUT_SUPPORTED) + + ERROR_MESSAGE_INVALID_HEADERS = 'headers must be a String with comma-separated URL Encoded UTF-8 k=v pairs or a Hash' + private_constant(:ERROR_MESSAGE_INVALID_HEADERS) + + def initialize(endpoint: OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_METRICS_ENDPOINT', 'OTEL_EXPORTER_OTLP_ENDPOINT', default: 'http://localhost:4318/v1/metrics'), # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity + certificate_file: OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE', 'OTEL_EXPORTER_OTLP_CERTIFICATE'), + ssl_verify_mode: fetch_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), + metrics_reporter: nil) + raise ArgumentError, "invalid url for OTLP::Exporter #{endpoint}" unless OpenTelemetry::Common::Utilities.valid_url?(endpoint) + raise ArgumentError, "unsupported compression key #{compression}" unless compression.nil? || %w[gzip none].include?(compression) + + @uri = if endpoint == ENV['OTEL_EXPORTER_OTLP_ENDPOINT'] + URI("#{endpoint}/v1/metrics") + else + URI(endpoint) + end + + @http = http_connection(@uri, ssl_verify_mode, certificate_file) + + @path = @uri.path + @headers = case headers + when String then parse_headers(headers) + when Hash then headers + else + raise ArgumentError, ERROR_MESSAGE_INVALID_HEADERS + end + @timeout = timeout.to_f + @compression = compression + @metrics_reporter = metrics_reporter || OpenTelemetry::SDK::Trace::Export::MetricsReporter + @shutdown = false + end + + # Called to export {OpenTelemetry::SDK::Metrics::State::MetricStream}. + # + # @param [Enumerable] metric_data the + # list of recorded {OpenTelemetry::SDK::Metrics::State::MetricStream} to be + # exported. + # @param [optional Numeric] timeout An optional timeout in seconds. + # @return [Integer] the result of the export. + def export(metric_data, timeout: nil) + return FAILURE if @shutdown + + send_bytes(OpenTelemetry::Exporter::OTLP::Common.as_encoded_emsr(metric_data), timeout: timeout) + end + + # Called when {OpenTelemetry::SDK::Metric::MeterProvider#force_flush} is called, if + # this exporter is registered to a {OpenTelemetry::SDK::Metric::MeterProvider} + # object. + # + # @param [optional Numeric] timeout An optional timeout in seconds. + def force_flush(timeout: nil) + SUCCESS + end + + # Called when {OpenTelemetry::SDK::Metric::MeterProvider#shutdown} is called, if + # this exporter is registered to a {OpenTelemetry::SDK::Trace::MeterProvider} + # object. + # + # @param [optional Numeric] timeout An optional timeout in seconds. + def shutdown(timeout: nil) + @shutdown = true + @http.finish if @http.started? + SUCCESS + end + + private + + def fetch_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 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 + + # The around_request is a private method that provides an extension + # point for the exporters network calls. The default behaviour + # is to not trace these operations. + # + # An example use case would be to prepend a patch, or extend this class + # and override this method's behaviour to explicitly trace the HTTP request. + # This would allow you to trace your export pipeline. + def around_request + OpenTelemetry::Common::Utilities.untraced { yield } + end + + def send_bytes(bytes, timeout:) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity + return FAILURE if bytes.nil? + + retry_count = 0 + timeout ||= @timeout + start_time = OpenTelemetry::Common::Utilities.timeout_timestamp + around_request do # rubocop:disable Metrics/BlockLength + request = Net::HTTP::Post.new(@path) + request.body = if @compression == 'gzip' + request.add_field('Content-Encoding', 'gzip') + Zlib.gzip(bytes) + else + bytes + end + request.add_field('Content-Type', 'application/x-protobuf') + @headers.each { |key, value| request.add_field(key, value) } + + 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) + 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) + FAILURE + when Net::HTTPBadRequest, Net::HTTPClientError, Net::HTTPServerError + log_status(response.body) + @metrics_reporter.add_to_counter('otel.otlp_exporter.failure', labels: { 'reason' => response.code }) + 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 + FAILURE + end + rescue Net::OpenTimeout, Net::ReadTimeout + retry if backoff?(retry_count: retry_count += 1, reason: 'timeout') + return FAILURE + rescue OpenSSL::SSL::SSLError + retry if backoff?(retry_count: retry_count += 1, reason: 'openssl_error') + return FAILURE + rescue SocketError + retry if backoff?(retry_count: retry_count += 1, reason: 'socket_error') + return FAILURE + rescue SystemCallError => e + retry if backoff?(retry_count: retry_count += 1, reason: e.class.name) + return FAILURE + rescue EOFError + retry if backoff?(retry_count: retry_count += 1, reason: 'eof_error') + return FAILURE + rescue Zlib::DataError + retry if backoff?(retry_count: retry_count += 1, reason: 'zlib_error') + return FAILURE + rescue StandardError => e + OpenTelemetry.handle_error(exception: e, message: 'unexpected error in OTLP::Exporter#send_bytes') + @metrics_reporter.add_to_counter('otel.otlp_exporter.failure', labels: { 'reason' => e.class.to_s }) + 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 handle_redirect(location) + # TODO: figure out destination and reinitialize @http and @path + 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 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::Exporter#log_status') + end + + def measure_request_duration + start = Process.clock_gettime(Process::CLOCK_MONOTONIC) + begin + response = yield + ensure + stop = Process.clock_gettime(Process::CLOCK_MONOTONIC) + duration_ms = 1000.0 * (stop - start) + @metrics_reporter.record_value('otel.otlp_exporter.request_duration', + value: duration_ms, + labels: { 'status' => response&.code || 'unknown' }) + end + end + + def backoff?(retry_after: nil, retry_count:, reason:) + @metrics_reporter.add_to_counter('otel.otlp_exporter.failure', labels: { 'reason' => reason }) + 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 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 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 + end + end + end + end +end diff --git a/exporter/otlp-http/test/opentelemetry/exporter/otlp/http/metric_exporter_test.rb b/exporter/otlp-http/test/opentelemetry/exporter/otlp/http/metric_exporter_test.rb new file mode 100644 index 000000000..10002dc4e --- /dev/null +++ b/exporter/otlp-http/test/opentelemetry/exporter/otlp/http/metric_exporter_test.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 +require 'test_helper' + +describe OpenTelemetry::Exporter::OTLP::HTTP::MetricExporter do + SUCCESS = OpenTelemetry::SDK::Metrics::Export::SUCCESS + FAILURE = OpenTelemetry::SDK::Metrics::Export::FAILURE + + it 'integrates with collector' do + WebMock.disable_net_connect!(allow: 'localhost') + OpenTelemetry::SDK.configure + meter = OpenTelemetry.meter_provider.meter('test') + instrument = meter.create_counter('b_counter') + + il = OpenTelemetry::SDK::InstrumentationLibrary.new('test_scope', 'v0.0.1') + r = OpenTelemetry::SDK::Resources::Resource.telemetry_sdk + il2 = OpenTelemetry::SDK::InstrumentationLibrary.new('secondary_scope', 'v0.0.2') + r = OpenTelemetry::SDK::Resources::Resource.telemetry_sdk + + ms1 = create_metric_stream(name: 'test_instrument_a', resource: r, instrumentation_library: il) + ms1.update(OpenTelemetry::Metrics::Measurement.new(11, { 'foo' => 'bar' })) + ms2 = create_metric_stream(name: 'test_instrument_b', resource: r, instrumentation_library: il2) + ms2.update(OpenTelemetry::Metrics::Measurement.new(11, { 'foo' => 'bar' })) + + exporter = OpenTelemetry::Exporter::OTLP::HTTP::MetricExporter.new(endpoint: 'http://localhost:4318/v1/metrics', compression: 'gzip') + result = exporter.export([ms1,ms2]) + _(result).must_equal(SUCCESS) + end + + def create_metric_stream(name: 'test_instrument', description: 'a wonderful instrument', unit: 'cm', instrument_kind: :counter, resource: OpenTelemetry::SDK::Resources::Resource.telemetry_sdk, instrumentation_library: OpenTelemetry::SDK::InstrumentationLibrary.new('test_scope', 'v0.0.1')) + OpenTelemetry::SDK::Metrics::State::MetricStream.new( + name, + description, + unit, + instrument_kind, + resource, + instrumentation_library, + ) + end +end diff --git a/exporter/otlp-http/test/test_helper.rb b/exporter/otlp-http/test/test_helper.rb index bca44c00a..46dafee45 100644 --- a/exporter/otlp-http/test/test_helper.rb +++ b/exporter/otlp-http/test/test_helper.rb @@ -11,5 +11,6 @@ require 'opentelemetry-exporter-otlp-http' require 'minitest/autorun' require 'webmock/minitest' +require 'pry' OpenTelemetry.logger = Logger.new(File::NULL) diff --git a/metrics_api/lib/opentelemetry/internal/proxy_meter.rb b/metrics_api/lib/opentelemetry/internal/proxy_meter.rb index 8d1a94ced..74d497c68 100644 --- a/metrics_api/lib/opentelemetry/internal/proxy_meter.rb +++ b/metrics_api/lib/opentelemetry/internal/proxy_meter.rb @@ -29,7 +29,7 @@ def delegate=(meter) @mutex.synchronize do if @delegate.nil? @delegate = meter - @registry.each_value { |instrument| instrument.upgrade_with(meter) } + @instrument_registry.each_value { |instrument| instrument.upgrade_with(meter) } else OpenTelemetry.logger.warn 'Attempt to reset delegate in ProxyMeter ignored.' end diff --git a/metrics_api/lib/opentelemetry/metrics/meter.rb b/metrics_api/lib/opentelemetry/metrics/meter.rb index de5821d9a..5ce9c0ed2 100644 --- a/metrics_api/lib/opentelemetry/metrics/meter.rb +++ b/metrics_api/lib/opentelemetry/metrics/meter.rb @@ -24,31 +24,33 @@ class Meter InstrumentUnitError = Class.new(OpenTelemetry::Error) InstrumentDescriptionError = Class.new(OpenTelemetry::Error) + attr_reader :instrument_registry + def initialize @mutex = Mutex.new - @registry = {} + @instrument_registry = {} end def create_counter(name, unit: nil, description: nil) create_instrument(:counter, name, unit, description, nil) { COUNTER } end - def create_observable_counter(name, unit: nil, description: nil, callback:) - create_instrument(:observable_counter, name, unit, description, callback) { OBSERVABLE_COUNTER } - end - def create_histogram(name, unit: nil, description: nil) create_instrument(:histogram, name, unit, description, nil) { HISTOGRAM } end - def create_observable_gauge(name, unit: nil, description: nil, callback:) - create_instrument(:observable_gauge, name, unit, description, callback) { OBSERVABLE_GAUGE } - end - def create_up_down_counter(name, unit: nil, description: nil) create_instrument(:up_down_counter, name, unit, description, nil) { UP_DOWN_COUNTER } end + def create_observable_counter(name, unit: nil, description: nil, callback:) + create_instrument(:observable_counter, name, unit, description, callback) { OBSERVABLE_COUNTER } + end + + def create_observable_gauge(name, unit: nil, description: nil, callback:) + create_instrument(:observable_gauge, name, unit, description, callback) { OBSERVABLE_GAUGE } + end + def create_observable_up_down_counter(name, unit: nil, description: nil, callback:) create_instrument(:observable_up_down_counter, name, unit, description, callback) { OBSERVABLE_UP_DOWN_COUNTER } end @@ -63,9 +65,11 @@ def create_instrument(kind, name, unit, description, callback) raise InstrumentDescriptionError if description && (description.size > 1023 || !utf8mb3_encoding?(description.dup)) @mutex.synchronize do - raise DuplicateInstrumentError if @registry.include? name + if @instrument_registry.include? name + OpenTelemetry.logger.warn("duplicate instrument registration occured for instrument #{name}") + end - @registry[name] = yield + @instrument_registry[name] = yield end end diff --git a/metrics_api/opentelemetry-metrics-api.gemspec b/metrics_api/opentelemetry-metrics-api.gemspec index de8064485..de32e11bc 100644 --- a/metrics_api/opentelemetry-metrics-api.gemspec +++ b/metrics_api/opentelemetry-metrics-api.gemspec @@ -27,6 +27,7 @@ Gem::Specification.new do |spec| spec.add_dependency 'opentelemetry-api', '~> 1.0' + spec.add_development_dependency 'opentelemetry-test-helpers' spec.add_development_dependency 'benchmark-ipsa', '~> 0.2.0' spec.add_development_dependency 'bundler', '>= 1.17' spec.add_development_dependency 'faraday', '~> 0.13' diff --git a/metrics_api/test/opentelemetry/metrics/meter_test.rb b/metrics_api/test/opentelemetry/metrics/meter_test.rb index 1bfecea58..904857fa5 100644 --- a/metrics_api/test/opentelemetry/metrics/meter_test.rb +++ b/metrics_api/test/opentelemetry/metrics/meter_test.rb @@ -16,10 +16,12 @@ let(:meter) { meter_provider.meter('test-meter') } describe 'creating an instrument' do - it 'instrument name must be unique' do - meter.create_counter('a_counter') - _(-> { meter.create_counter('a_counter') }).must_raise(DUPLICATE_INSTRUMENT_ERROR) - _(-> { meter.create_histogram('a_counter') }).must_raise(DUPLICATE_INSTRUMENT_ERROR) + it 'duplicate instrument registration logs a warning' do + OpenTelemetry::TestHelpers.with_test_logger do |log_stream| + meter.create_counter('a_counter') + meter.create_counter('a_counter') + _(log_stream.string).must_match(/duplicate instrument registration occured for instrument a_counter/) + end end it 'instrument name must not be nil' do diff --git a/metrics_api/test/test_helper.rb b/metrics_api/test/test_helper.rb index 753cb8610..9e9698666 100644 --- a/metrics_api/test/test_helper.rb +++ b/metrics_api/test/test_helper.rb @@ -8,6 +8,7 @@ # # SimpleCov.start # # SimpleCov.minimum_coverage 85 +require 'opentelemetry-test-helpers' require 'opentelemetry-metrics-api' require 'minitest/autorun' require 'pry' diff --git a/metrics_sdk/Gemfile b/metrics_sdk/Gemfile index 327e4410b..39ca86ec6 100644 --- a/metrics_sdk/Gemfile +++ b/metrics_sdk/Gemfile @@ -11,6 +11,7 @@ gemspec gem 'opentelemetry-api', path: '../api' gem 'opentelemetry-metrics-api', path: '../metrics_api' gem 'opentelemetry-sdk', path: '../sdk' +gem 'opentelemetry-test-helpers', path: '../test_helpers' group :test, :development do gem 'pry' diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/sum_aggregation.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/sum_aggregation.rb index c4dc38940..3c1c75bab 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/sum_aggregation.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/sum_aggregation.rb @@ -9,6 +9,7 @@ module SDK module Metrics module Aggregation class SumAggregation + end end end 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 3f075b159..b9f8ffac8 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 @@ -11,11 +11,12 @@ module Export class InMemoryMetricPullExporter < MetricReader PREFERRED_TEMPORALITY = 'delta' - attr_reader :metric_snapshots + attr_reader :metric_snapshots, :metric_store def initialize @metric_snapshots = [] @mutex = Mutex.new + @metric_store = OpenTelemetry::SDK::Metrics::State::MetricStore.new end def pull diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/export/metric_reader.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/export/metric_reader.rb index 17569681c..cebebbe22 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/export/metric_reader.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/export/metric_reader.rb @@ -9,23 +9,17 @@ module SDK module Metrics module Export class MetricReader + attr_reader :metric_store + def initialize(exporter) @exporter = exporter + @metric_store = OpenTelemetry::SDK::Metrics::State::MetricStore.new end def collect @metric_store.collect end - # @api private - def metric_store=(metric_store) - if defined?(:@metric_store) && !@metric_store.nil? - OpenTelemetry.handle_error(message: 'repeated attempts to set metric_store on metric reader') - else - @metric_store = metric_store - end - end - def shutdown(timeout: nil) SUCCESS end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/histogram.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/histogram.rb deleted file mode 100644 index e69de29bb..000000000 diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument.rb index 00004706c..c440634c8 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument.rb @@ -13,6 +13,7 @@ module Instrument end end +require 'opentelemetry/sdk/metrics/instrument/synchronous_instrument' require 'opentelemetry/sdk/metrics/instrument/counter' require 'opentelemetry/sdk/metrics/instrument/histogram' require 'opentelemetry/sdk/metrics/instrument/observable_counter' diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/counter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/counter.rb index e3dc59169..b2699097b 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/counter.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/counter.rb @@ -9,15 +9,12 @@ module SDK module Metrics module Instrument # {Counter} is the SDK implementation of {OpenTelemetry::Metrics::Counter}. - class Counter < OpenTelemetry::Metrics::Instrument::Counter - attr_reader :name, :unit, :description, :instrumentation_library - - def initialize(name, unit, description, metric_store_registry, instrumentation_library) - @name = name - @unit = unit - @description = description - @metric_store_registry = metric_store_registry - @instrumentation_library = instrumentation_library + class Counter < OpenTelemetry::SDK::Metrics::Instrument::SynchronousInstrument + # Returns the instrument kind as a Symbol + # + # @return [Symbol] + def instrument_kind + :counter end # Increment the Counter by a fixed amount. @@ -27,10 +24,9 @@ def initialize(name, unit, description, metric_store_registry, instrumentation_l # Values must be non-nil and (array of) string, boolean or numeric type. # Array values must not contain nil elements and all elements must be of # the same basic type (string, numeric, boolean). - def add(increment, attributes: nil) - @metric_store_registry.produce( - OpenTelemetry::Metrics::Measurement.new(increment, attributes), - self + def add(increment, attributes: {}) + update( + OpenTelemetry::Metrics::Measurement.new(increment, attributes) ) end end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/histogram.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/histogram.rb index ea3b6b9a1..c632d1da5 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/histogram.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/histogram.rb @@ -9,15 +9,12 @@ module SDK module Metrics module Instrument # {Histogram} is the SDK implementation of {OpenTelemetry::Metrics::Histogram}. - class Histogram < OpenTelemetry::Metrics::Instrument::Histogram - attr_reader :name, :unit, :description - - def initialize(name, unit, description, metric_store_registry, instrumentation_library) - @name = name - @unit = unit - @description = description - @metric_store_registry = metric_store_registry - @instrumentation_library = instrumentation_library + class Histogram < OpenTelemetry::SDK::Metrics::Instrument::SynchronousInstrument + # Returns the instrument kind as a Symbol + # + # @return [Symbol] + def instrument_kind + :histogram end # Updates the statistics with the specified amount. @@ -27,7 +24,9 @@ def initialize(name, unit, description, metric_store_registry, instrumentation_l # Values must be non-nil and (array of) string, boolean or numeric type. # Array values must not contain nil elements and all elements must be of # the same basic type (string, numeric, boolean). - def record(amount, attributes: nil); end + def record(amount, attributes: nil); + update(OpenTelemetry::Metrics::Measurement.new(increment, attributes)) + end end end end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/synchronous_instrument.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/synchronous_instrument.rb new file mode 100644 index 000000000..dfb62a685 --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/synchronous_instrument.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Metrics + module Instrument + class SynchronousInstrument + def initialize(name, unit, description, instrumentation_library, meter_provider) + @name = name + @unit = unit + @description = description + @instrumentation_library = instrumentation_library + @metric_streams = [] + + meter_provider.metric_readers.each do |metric_reader| + register_with_new_metric_store(metric_reader.metric_store) + end + end + + def register_with_new_metric_store(metric_store) + ms = OpenTelemetry::SDK::Metrics::State::MetricStream.new( + @name, + @description, + @unit, + instrument_kind, + nil, #meter_provider.resource, + @instrumentation_library + ) + @metric_streams << ms + metric_store.add_metric_stream(ms) + end + + private + + def update(measurement) + @metric_streams.each do |ms| + ms.update(measurement) + end + end + end + end + end + end +end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/up_down_counter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/up_down_counter.rb index 644dd5f04..81ddc6c79 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/up_down_counter.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/up_down_counter.rb @@ -9,15 +9,12 @@ module SDK module Metrics module Instrument # {UpDownCounter} is the SDK implementation of {OpenTelemetry::Metrics::UpDownCounter}. - class UpDownCounter < OpenTelemetry::Metrics::Instrument::UpDownCounter - attr_reader :name, :unit, :description - - def initialize(name, unit, description, metric_store_registry, instrumentation_library) - @name = name - @unit = unit - @description = description - @metric_store_registry = metric_store_registry - @instrumentation_library = instrumentation_library + class UpDownCounter < OpenTelemetry::SDK::Metrics::Instrument::SynchronousInstrument + # Returns the instrument kind as a Symbol + # + # @return [Symbol] + def instrument_kind + :up_down_counter end # Increment or decrement the UpDownCounter by a fixed amount. @@ -27,7 +24,9 @@ def initialize(name, unit, description, metric_store_registry, instrumentation_l # Values must be non-nil and (array of) string, boolean or numeric type. # Array values must not contain nil elements and all elements must be of # the same basic type (string, numeric, boolean). - def add(amount, attributes: nil); end + def add(amount, attributes: nil) + update(OpenTelemetry::Metrics::Measurement.new(increment, attributes)) + end end end end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb index 5c9727319..dd77e143c 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb @@ -21,7 +21,7 @@ class Meter < OpenTelemetry::Metrics::Meter # @return [Meter] def initialize(name, version, meter_provider) @mutex = Mutex.new - @registry = {} + @instrument_registry = {} @instrumentation_library = InstrumentationLibrary.new(name, version) @meter_provider = meter_provider end @@ -29,12 +29,12 @@ def initialize(name, version, meter_provider) def create_instrument(kind, name, unit, description, callback) super do case kind - when :counter then OpenTelemetry::SDK::Metrics::Instrument::Counter.new(name, unit, description, @meter_provider.metric_store_registry, @instrumentation_library) - when :observable_counter then OpenTelemetry::SDK::Metrics::Instrument::ObservableCounter.new(name, unit, description, callback, @meter_provider.metric_store_registry, @instrumentation_library) - when :histogram then OpenTelemetry::SDK::Metrics::Instrument::Histogram.new(name, unit, description, @meter_provider.metric_store_registry, @instrumentation_library) - when :observable_gauge then OpenTelemetry::SDK::Metrics::Instrument::ObservableGauge.new(name, unit, description, callback, @meter_provider.metric_store_registry, @instrumentation_library) - when :up_down_counter then OpenTelemetry::SDK::Metrics::Instrument::UpDownCounter.new(name, unit, description, @meter_provider.metric_store_registry, @instrumentation_library) - when :observable_up_down_counter then OpenTelemetry::SDK::Metrics::Instrument::ObservableUpDownCounter.new(name, unit, description, callback, @meter_provider.metric_store_registry, @instrumentation_library) + when :counter then OpenTelemetry::SDK::Metrics::Instrument::Counter.new(name, unit, description, @instrumentation_library, @meter_provider) + when :observable_counter then OpenTelemetry::SDK::Metrics::Instrument::ObservableCounter.new(name, unit, description, callback, @instrumentation_library, @meter_provider) + when :histogram then OpenTelemetry::SDK::Metrics::Instrument::Histogram.new(name, unit, description, @instrumentation_library, @meter_provider) + when :observable_gauge then OpenTelemetry::SDK::Metrics::Instrument::ObservableGauge.new(name, unit, description, callback, @instrumentation_library, @meter_provider) + when :up_down_counter then OpenTelemetry::SDK::Metrics::Instrument::UpDownCounter.new(name, unit, description, @instrumentation_library, @meter_provider) + when :observable_up_down_counter then OpenTelemetry::SDK::Metrics::Instrument::ObservableUpDownCounter.new(name, unit, description, callback, @instrumentation_library, @meter_provider) end end end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb index 27e3e048f..aec44838e 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb @@ -14,14 +14,14 @@ class MeterProvider < OpenTelemetry::Metrics::MeterProvider Key = Struct.new(:name, :version) private_constant(:Key) - attr_reader :metric_store_registry + attr_reader :resource, :metric_readers def initialize(resource: OpenTelemetry::SDK::Resources::Resource.create) @mutex = Mutex.new - @registry = {} + @meter_registry = {} @stopped = false @metric_readers = [] - @metric_store_registry = State::MetricStoreRegistry.new(resource) + @resource = resource end # Returns a {Meter} instance. @@ -36,7 +36,7 @@ def meter(name, version = nil) OpenTelemetry.logger.warn 'calling MeterProvider#meter after shutdown, a noop meter will be returned.' OpenTelemetry::Metrics::Meter.new else - @mutex.synchronize { @registry[Key.new(name, version)] ||= Meter.new(name, version, self) } + @mutex.synchronize { @meter_registry[Key.new(name, version)] ||= Meter.new(name, version, self) } end end @@ -108,8 +108,12 @@ def add_metric_reader(metric_reader) if @stopped OpenTelemetry.logger.warn('calling MetricProvider#add_metric_reader after shutdown.') else - metric_reader.metric_store = @metric_store_registry.add_metric_store @metric_readers.push(metric_reader) + @meter_registry.each do |_k, meter| + meter.instrument_registry.each do |_n, instrument| + instrument.register_with_new_metric_store(metric_reader.metric_store) + end + end end nil diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/state.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/state.rb index 172488b1c..3c7f4c395 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/state.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/state.rb @@ -14,5 +14,4 @@ module State end require 'opentelemetry/sdk/metrics/state/metric_store' -require 'opentelemetry/sdk/metrics/state/metric_store_registry' require 'opentelemetry/sdk/metrics/state/metric_stream' diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_store.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_store.rb index 3814b4e7e..24de6af0e 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_store.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_store.rb @@ -11,34 +11,26 @@ module State class MetricStore def initialize @mutex = Mutex.new - # @epoch_start_time = now - @metric_streams = {} + @epoch_start_time = Time.now.to_i + @epoch_end_time = nil; + @metric_streams = [] end def collect # this probably needs to take the mutex, take a snapshot of state, (reset state?), release the mutex - # end_time = now - @metric_streams.map do |_k, metric_stream| - metric_stream + @mutex.synchronize do + [ + @end_time = Time.now.to_i, + @metric_streams, + @epoch_start_time = @end_time, + ] + @metric_streams end - # @epoch_start_time = end_time end - def record(measurement, instrument, resource) - # compute metric stream name - # find or create the metric stream - # run aggregation on the metric stream - - # need to block on lookup or creation - # if aggregation is quick hold the lock? - # otherwise we want to release the lock for the lookup/creation - # and takeup a lock on the aggregator - - metric_stream = @mutex.synchronize do - @metric_streams[instrument.to_s] || @metric_streams[instrument.to_s] = MetricStream.new(instrument, resource) - end - - metric_stream.update(measurement) + def add_metric_stream(metric_stream) + @metric_streams = @metric_streams.dup.push(metric_stream) + nil end end end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_store_registry.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_store_registry.rb deleted file mode 100644 index 07de45e46..000000000 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_store_registry.rb +++ /dev/null @@ -1,47 +0,0 @@ -# frozen_string_literal: true - -# Copyright The OpenTelemetry Authors -# -# SPDX-License-Identifier: Apache-2.0 - -module OpenTelemetry - module SDK - module Metrics - module State - class MetricStoreRegistry - def initialize(resource) - @resource = resource - @metric_stores = [] - end - - def add_metric_store - new_metric_store = MetricStore.new - @metric_stores = @metric_stores.dup.push(new_metric_store) - new_metric_store - end - - def produce(measurement, instrument) - # Info we need to build a metric: - # measurement - # @meter_provider.resource - # instrument.class? or a symbol representing the instrument 'kind' from Meter.create_instrument - # instrument.name - # instrument.unit - # instrument.description - # instrument.instrumentation_library - - # TODO: once we add views - # instrument.views do |view| - # view.metrics(measurement, instrument, @meter_provider.resource) do |metric| - # @metric_stores.each { |ms| ms.record(metric) } - # end - # end - - # record metric stream - @metric_stores.each { |ms| ms.record(measurement, instrument, @resource) } - end - end - end - end - end -end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_stream.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_stream.rb index f352a2179..64693563b 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_stream.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_stream.rb @@ -9,37 +9,49 @@ module SDK module Metrics module State class MetricStream - attr_reader :instrument, :resource + attr_reader :name, :description, :unit, :instrument_kind, :resource, :instrumentation_library, :data_points - def initialize(instrument, resource) - @instrument = instrument + def initialize( + name, + description, + unit, + instrument_kind, + resource, + instrumentation_library + ) + @name = name + @description = description + @unit = unit + @instrument_kind = instrument_kind @resource = resource + @instrumentation_library = instrumentation_library + @data_points = {} @mutex = Mutex.new end def update(measurement) @mutex.synchronize do - @data_points[measurement.attributes] = if @data_points[measurement.attributes] - data_point + measurement.value - else - measurement.value - end + if @data_points[measurement.attributes] + @data_points[measurement.attributes] = @data_points[measurement.attributes] + measurement.value + else + @data_points[measurement.attributes] = measurement.value + end end end def to_s - metric_stream_string = String.new - metric_stream_string << "name=#{instrument.name}" - metric_stream_string << " description=#{instrument.description}" if instrument.description - metric_stream_string << " unit=#{instrument.unit}" if instrument.unit - map = @data_points.map do |attributes, value| - str = String.new - str << metric_stream_string - str << " attributes=#{attributes}" if attributes - str << " #{value}" - end - map.join("\n") + instrument_info = String.new + instrument_info << "name=#{@name}" + instrument_info << " description=#{@description}" if @description + instrument_info << " unit=#{@unit}" if @unit + @data_points.map do |attributes, value| + metric_stream_string = String.new + metric_stream_string << instrument_info + metric_stream_string << " attributes=#{attributes}" if attributes + metric_stream_string << " #{value}" + metric_stream_string + end.join("\n") end end end diff --git a/metrics_sdk/opentelemetry-metrics-sdk.gemspec b/metrics_sdk/opentelemetry-metrics-sdk.gemspec index 29a1eae8b..f17e64227 100644 --- a/metrics_sdk/opentelemetry-metrics-sdk.gemspec +++ b/metrics_sdk/opentelemetry-metrics-sdk.gemspec @@ -29,6 +29,7 @@ Gem::Specification.new do |spec| spec.add_dependency 'opentelemetry-metrics-api' spec.add_dependency 'opentelemetry-sdk', '~> 1.0' + spec.add_development_dependency 'opentelemetry-test-helpers' spec.add_development_dependency 'benchmark-ipsa', '~> 0.2.0' spec.add_development_dependency 'bundler', '>= 1.17' spec.add_development_dependency 'faraday', '~> 0.13' diff --git a/metrics_sdk/test/integration/in_memory_metric_pull_exporter_test.rb b/metrics_sdk/test/integration/in_memory_metric_pull_exporter_test.rb index 2b5e57c15..716aa0812 100644 --- a/metrics_sdk/test/integration/in_memory_metric_pull_exporter_test.rb +++ b/metrics_sdk/test/integration/in_memory_metric_pull_exporter_test.rb @@ -11,23 +11,26 @@ before { reset_metrics_sdk } it 'emits metrics' do - meter = OpenTelemetry.meter_provider.meter('test') - instrument = meter.create_counter('b_counter') - OpenTelemetry::SDK.configure metric_exporter = OpenTelemetry::SDK::Metrics::Export::InMemoryMetricPullExporter.new OpenTelemetry.meter_provider.add_metric_reader(metric_exporter) + meter = OpenTelemetry.meter_provider.meter('test') + instrument = meter.create_counter('b_counter') + instrument.add(1) instrument.add(2, attributes: { 'a' => 'b' }) + instrument.add(2, attributes: { 'a' => 'b' }) instrument.add(3, attributes: { 'b' => 'c' }) instrument.add(4, attributes: { 'd' => 'e' }) metric_exporter.pull - last_snapshot = metric_exporter.metric_snapshots.last[0] - _(last_snapshot.instrument.name).must_equal('b_counter') - _(last_snapshot.instrument.instrumentation_library.name).must_equal('test') + last_snapshot = metric_exporter.metric_snapshots.last + + _(last_snapshot).wont_be_empty + _(last_snapshot[0].name).must_equal('b_counter') + _(last_snapshot[0].instrumentation_library.name).must_equal('test') end end end diff --git a/metrics_sdk/test/opentelemetry/sdk/metrics/meter_provider_test.rb b/metrics_sdk/test/opentelemetry/sdk/metrics/meter_provider_test.rb index 3829e11a4..6a532dc3e 100644 --- a/metrics_sdk/test/opentelemetry/sdk/metrics/meter_provider_test.rb +++ b/metrics_sdk/test/opentelemetry/sdk/metrics/meter_provider_test.rb @@ -106,6 +106,34 @@ _(OpenTelemetry.meter_provider.instance_variable_get(:@metric_readers)).must_equal([metric_reader]) end + + it 'associates the metric store with instruments created before the metric reader' do + meter_a = OpenTelemetry.meter_provider.meter('a').create_counter('meter_a') + + metric_reader_a = OpenTelemetry::SDK::Metrics::Export::MetricReader.new(OpenTelemetry::SDK::Metrics::Export::ConsoleMetricExporter.new) + OpenTelemetry.meter_provider.add_metric_reader(metric_reader_a) + + metric_reader_b = OpenTelemetry::SDK::Metrics::Export::MetricReader.new(OpenTelemetry::SDK::Metrics::Export::ConsoleMetricExporter.new) + OpenTelemetry.meter_provider.add_metric_reader(metric_reader_b) + + _(meter_a.instance_variable_get(:@metric_streams).size).must_equal(2) + _(metric_reader_a.metric_store.instance_variable_get(:@metric_streams).size).must_equal(1) + _(metric_reader_b.metric_store.instance_variable_get(:@metric_streams).size).must_equal(1) + end + + it 'associates the metric store with instruments created after the metric reader' do + metric_reader_a = OpenTelemetry::SDK::Metrics::Export::MetricReader.new(OpenTelemetry::SDK::Metrics::Export::ConsoleMetricExporter.new) + OpenTelemetry.meter_provider.add_metric_reader(metric_reader_a) + + metric_reader_b = OpenTelemetry::SDK::Metrics::Export::MetricReader.new(OpenTelemetry::SDK::Metrics::Export::ConsoleMetricExporter.new) + OpenTelemetry.meter_provider.add_metric_reader(metric_reader_b) + + meter_a = OpenTelemetry.meter_provider.meter('a').create_counter('meter_a') + + _(meter_a.instance_variable_get(:@metric_streams).size).must_equal(2) + _(metric_reader_a.metric_store.instance_variable_get(:@metric_streams).size).must_equal(1) + _(metric_reader_b.metric_store.instance_variable_get(:@metric_streams).size).must_equal(1) + end end describe '#add_view' do diff --git a/metrics_sdk/test/opentelemetry/sdk/metrics/meter_test.rb b/metrics_sdk/test/opentelemetry/sdk/metrics/meter_test.rb index 7d4678de0..6a6cba2fd 100644 --- a/metrics_sdk/test/opentelemetry/sdk/metrics/meter_test.rb +++ b/metrics_sdk/test/opentelemetry/sdk/metrics/meter_test.rb @@ -18,13 +18,6 @@ end end - # describe '#create_observable_counter' do - # it 'creates a observable_counter instrument' do - # instrument = meter.create_observable_counter('a_observable_counter', unit: 'minutes', description: 'useful description', callback: nil) - # _(instrument).must_be_instance_of OpenTelemetry::SDK::Metrics::Instrument::ObservableCounter - # end - # end - describe '#create_histogram' do it 'creates a histogram instrument' do instrument = meter.create_histogram('a_histogram', unit: 'minutes', description: 'useful description') @@ -32,13 +25,6 @@ end end - # describe '#create_observable_gauge' do - # it 'creates a observable_gauge instrument' do - # instrument = meter.create_observable_gauge('a_observable_gauge', unit: 'minutes', description: 'useful description', callback: nil) - # _(instrument).must_be_instance_of OpenTelemetry::SDK::Metrics::Instrument::ObservableGauge - # end - # end - describe '#create_up_down_counter' do it 'creates a up_down_counter instrument' do instrument = meter.create_up_down_counter('a_up_down_counter', unit: 'minutes', description: 'useful description') @@ -46,10 +32,30 @@ end end - # describe '#create_observable_up_down_counter' do - # it 'creates a observable_up_down_counter instrument' do - # instrument = meter.create_observable_up_down_counter('a_observable_up_down_counter', unit: 'minutes', description: 'useful description', callback: nil) - # _(instrument).must_be_instance_of OpenTelemetry::SDK::Metrics::Instrument::ObservableUpDownCounter - # end - # end + describe '#create_observable_counter' do + it 'creates a observable_counter instrument' do + # TODO: Implement observable instruments + skip + instrument = meter.create_observable_counter('a_observable_counter', unit: 'minutes', description: 'useful description', callback: nil) + _(instrument).must_be_instance_of OpenTelemetry::SDK::Metrics::Instrument::ObservableCounter + end + end + + describe '#create_observable_gauge' do + it 'creates a observable_gauge instrument' do + # TODO: Implement observable instruments + skip + instrument = meter.create_observable_gauge('a_observable_gauge', unit: 'minutes', description: 'useful description', callback: nil) + _(instrument).must_be_instance_of OpenTelemetry::SDK::Metrics::Instrument::ObservableGauge + end + end + + describe '#create_observable_up_down_counter' do + it 'creates a observable_up_down_counter instrument' do + # TODO: Implement observable instruments + skip + instrument = meter.create_observable_up_down_counter('a_observable_up_down_counter', unit: 'minutes', description: 'useful description', callback: nil) + _(instrument).must_be_instance_of OpenTelemetry::SDK::Metrics::Instrument::ObservableUpDownCounter + end + end end diff --git a/metrics_sdk/test/opentelemetry/sdk/metrics/state/metric_store_test.rb b/metrics_sdk/test/opentelemetry/sdk/metrics/state/metric_store_test.rb new file mode 100644 index 000000000..01cd0fcf9 --- /dev/null +++ b/metrics_sdk/test/opentelemetry/sdk/metrics/state/metric_store_test.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'test_helper' + +describe OpenTelemetry::SDK::Metrics::State::MetricStore do + describe '#collect' do + end + + describe '#add_metric_stream' do + end +end diff --git a/metrics_sdk/test/opentelemetry/sdk/metrics/state/metric_stream_test.rb b/metrics_sdk/test/opentelemetry/sdk/metrics/state/metric_stream_test.rb new file mode 100644 index 000000000..8a0084101 --- /dev/null +++ b/metrics_sdk/test/opentelemetry/sdk/metrics/state/metric_stream_test.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'test_helper' + +describe OpenTelemetry::SDK::Metrics::State::MetricStream do + describe '#update' do + end +end diff --git a/metrics_sdk/test/test_helper.rb b/metrics_sdk/test/test_helper.rb index 0b5f2d125..99aa4deee 100644 --- a/metrics_sdk/test/test_helper.rb +++ b/metrics_sdk/test/test_helper.rb @@ -9,6 +9,7 @@ # # SimpleCov.minimum_coverage 85 require 'opentelemetry-metrics-sdk' +require 'opentelemetry-test-helpers' require 'minitest/autorun' require 'pry' diff --git a/test_helpers/lib/opentelemetry/test_helpers.rb b/test_helpers/lib/opentelemetry/test_helpers.rb index b43095c99..e5ff6a39d 100644 --- a/test_helpers/lib/opentelemetry/test_helpers.rb +++ b/test_helpers/lib/opentelemetry/test_helpers.rb @@ -63,6 +63,28 @@ def with_ids(trace_id, span_id) end end + def create_metric_stream( + name: 'test_instrument', + description: 'a wonderful instrument', + unit: 'ms', + instrument_kind: :counter, + value: 1, + attributes: {}, + resource: OpenTelemetry::SDK::Resources::Resource.telemetry_sdk, + instrumentation_library: OpenTelemetry::SDK::InstrumentationLibrary.new('test_scope', 'v0.0.1') + ) + ms = OpenTelemetry::SDK::Metrics::State::MetricStream.new( + name, + description, + unit, + instrument_kind, + resource, + instrumentation_library, + ) + ms.update(OpenTelemetry::Metrics::Measurement.new(value, attributes)) + ms + end + def create_span_data(name: '', kind: nil, status: nil, parent_span_id: OpenTelemetry::Trace::INVALID_SPAN_ID, total_recorded_attributes: 0, total_recorded_events: 0, total_recorded_links: 0, start_timestamp: OpenTelemetry::TestHelpers.exportable_timestamp, end_timestamp: OpenTelemetry::TestHelpers.exportable_timestamp, attributes: nil, links: nil, events: nil, resource: nil, From 14178d048426324f31a582bcdba32cba8989d0fb Mon Sep 17 00:00:00 2001 From: Robert Laurin Date: Fri, 24 Jun 2022 13:37:34 -0500 Subject: [PATCH 15/25] chore: remove exporter work from PR --- exporter/otlp-common/Gemfile | 2 - .../lib/opentelemetry/exporter/otlp/common.rb | 62 ---- .../exporter/otlp/common/common_test.rb | 35 --- exporter/otlp-common/test/test_helper.rb | 1 - exporter/otlp-http/Gemfile | 2 - .../lib/opentelemetry/exporter/otlp/http.rb | 1 - .../exporter/otlp/http/metric_exporter.rb | 287 ------------------ .../otlp/http/metric_exporter_test.rb | 43 --- exporter/otlp-http/test/test_helper.rb | 1 - 9 files changed, 434 deletions(-) delete mode 100644 exporter/otlp-http/lib/opentelemetry/exporter/otlp/http/metric_exporter.rb delete mode 100644 exporter/otlp-http/test/opentelemetry/exporter/otlp/http/metric_exporter_test.rb diff --git a/exporter/otlp-common/Gemfile b/exporter/otlp-common/Gemfile index 57b3af201..08f000453 100644 --- a/exporter/otlp-common/Gemfile +++ b/exporter/otlp-common/Gemfile @@ -11,7 +11,5 @@ gemspec group :test, :development do gem 'opentelemetry-api', path: '../../api' gem 'opentelemetry-sdk', path: '../../sdk' - gem 'opentelemetry-metrics-sdk', path: '../../metrics_sdk' - gem 'opentelemetry-metrics-api', path: '../../metrics_api' gem 'opentelemetry-test-helpers', path: '../../test_helpers' end diff --git a/exporter/otlp-common/lib/opentelemetry/exporter/otlp/common.rb b/exporter/otlp-common/lib/opentelemetry/exporter/otlp/common.rb index 48f6ab6d2..745d60ee9 100644 --- a/exporter/otlp-common/lib/opentelemetry/exporter/otlp/common.rb +++ b/exporter/otlp-common/lib/opentelemetry/exporter/otlp/common.rb @@ -14,9 +14,6 @@ require 'opentelemetry/proto/trace/v1/trace_pb' require 'opentelemetry/proto/collector/trace/v1/trace_service_pb' -require 'opentelemetry/proto/metrics/v1/metrics_pb' -require 'opentelemetry/proto/collector/metrics/v1/metrics_service_pb' - module OpenTelemetry module Exporter module OTLP @@ -71,67 +68,8 @@ def as_etsr(span_data) # rubocop:disable Metrics/MethodLength ) end - def as_encoded_emsr(metric_data) - Opentelemetry::Proto::Collector::Metrics::V1::ExportMetricsServiceRequest.encode(as_emsr(metric_data)) - end - - def as_emsr(metric_data) - Opentelemetry::Proto::Collector::Metrics::V1::ExportMetricsServiceRequest.new( - resource_metrics: metric_data - .group_by(&:resource) - .map do |resource, metric_datas| - 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) } - ), - instrumentation_library_metrics: metric_datas - .group_by(&:instrumentation_library) - .map do |il, mds| - Opentelemetry::Proto::Metrics::V1::InstrumentationLibraryMetrics.new( - instrumentation_library: Opentelemetry::Proto::Common::V1::InstrumentationLibrary.new( - name: il.name, - version: il.version - ), - metrics: mds.group_by.map do |metric_stream| - as_otlp_metric(metric_stream) - end - ) - end - ) - end - ) - end - private - def as_otlp_metric(metric_stream) - case metric_stream.instrument_kind - when :counter then as_sum_metric(metric_stream) - end - end - - def as_sum_metric(metric_stream) - Opentelemetry::Proto::Metrics::V1::Metric.new( - name: metric_stream.name, - description: metric_stream.description, - unit: metric_stream.unit, - sum: Opentelemetry::Proto::Metrics::V1::Sum.new( - data_points: metric_stream.data_points.map { |attr, val| as_sum_data_point(attr, val) } - ), - ) - end - - def as_sum_data_point(attributes, value) - Opentelemetry::Proto::Metrics::V1::NumberDataPoint.new( - attributes: attributes&.map { |k, v| as_otlp_key_value(k, v) }, - as_int: value - ) - end - - def as_otlp_string_key_value(key, value) - Opentelemetry::Proto::Common::V1::StringKeyValue.new(key: key, value: value) - end - def as_otlp_span(span_data) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength Opentelemetry::Proto::Trace::V1::Span.new( trace_id: span_data.trace_id, diff --git a/exporter/otlp-common/test/opentelemetry/exporter/otlp/common/common_test.rb b/exporter/otlp-common/test/opentelemetry/exporter/otlp/common/common_test.rb index c1232337d..f3d7ab56f 100644 --- a/exporter/otlp-common/test/opentelemetry/exporter/otlp/common/common_test.rb +++ b/exporter/otlp-common/test/opentelemetry/exporter/otlp/common/common_test.rb @@ -7,41 +7,6 @@ require 'test_helper' describe OpenTelemetry::Exporter::OTLP::Common do - describe '#as_emsr' do - it 'batches per resource' do - il = OpenTelemetry::SDK::InstrumentationLibrary.new('test_scope', 'v0.0.1') - r = OpenTelemetry::SDK::Resources::Resource.telemetry_sdk - - il2 = OpenTelemetry::SDK::InstrumentationLibrary.new('secondary_scope', 'v0.0.2') - r2 = OpenTelemetry::SDK::Resources::Resource.telemetry_sdk - metrics = [ - create_metric_stream(name: 'test_instrumenta', value: 10, attributes: {'foo'=>'bar'}, resource: r, instrumentation_library: il), - create_metric_stream(name: 'test_instrumenta', value: 10, attributes: {'foo'=>'bar'}, resource: r2, instrumentation_library: il2), - create_metric_stream(name: 'test_instrumentb', value: 10, attributes: {'foo'=>'bar'}, resource: r2, instrumentation_library: il2), - ] - - emsr = OpenTelemetry::Exporter::OTLP::Common.as_emsr(metrics) - - _(emsr.resource_metrics.length).must_equal(2) - _(emsr.resource_metrics[0].instrumentation_library_metrics[0].metrics.length).must_equal(1) - _(emsr.resource_metrics[1].instrumentation_library_metrics[0].metrics.length).must_equal(2) - end - - def create_metric_stream(name: 'test_instrument', description: 'a wonderful instrument', unit: 'cm', instrument_kind: :counter, value: 1, attributes: {}, resource: OpenTelemetry::SDK::Resources::Resource.telemetry_sdk, instrumentation_library: OpenTelemetry::SDK::InstrumentationLibrary.new('test_scope', 'v0.0.1')) - OpenTelemetry::SDK::Metrics::State::MetricStream.new( - name, - description, - unit, - instrument_kind, - value, - attributes, - resource, - instrumentation_library, - ) - end - end - - describe '#as_encoded_etsr' do it 'handles encoding errors with poise and grace' do OpenTelemetry::TestHelpers.with_test_logger do |log_stream| diff --git a/exporter/otlp-common/test/test_helper.rb b/exporter/otlp-common/test/test_helper.rb index 1d69ecd98..7ae8fe543 100644 --- a/exporter/otlp-common/test/test_helper.rb +++ b/exporter/otlp-common/test/test_helper.rb @@ -8,7 +8,6 @@ SimpleCov.start require 'opentelemetry/sdk' -require 'opentelemetry-metrics-sdk' require 'opentelemetry-test-helpers' require 'opentelemetry-exporter-otlp-common' diff --git a/exporter/otlp-http/Gemfile b/exporter/otlp-http/Gemfile index 4f69e4166..f00a94870 100644 --- a/exporter/otlp-http/Gemfile +++ b/exporter/otlp-http/Gemfile @@ -14,8 +14,6 @@ group :test, :development do gem 'opentelemetry-api', path: '../../api' gem 'opentelemetry-exporter-otlp-common', path: '../otlp-common' gem 'opentelemetry-sdk', path: '../../sdk' - gem 'opentelemetry-metrics-sdk', path: '../../metrics_sdk' - gem 'opentelemetry-metrics-api', path: '../../metrics_api' gem 'opentelemetry-semantic_conventions', path: '../../semantic_conventions' gem 'opentelemetry-test-helpers', path: '../../test_helpers' end diff --git a/exporter/otlp-http/lib/opentelemetry/exporter/otlp/http.rb b/exporter/otlp-http/lib/opentelemetry/exporter/otlp/http.rb index f8f8fbc4c..5a89d2fff 100644 --- a/exporter/otlp-http/lib/opentelemetry/exporter/otlp/http.rb +++ b/exporter/otlp-http/lib/opentelemetry/exporter/otlp/http.rb @@ -21,5 +21,4 @@ module HTTP end require 'opentelemetry/exporter/otlp/http/trace_exporter' -require 'opentelemetry/exporter/otlp/http/metric_exporter' require 'opentelemetry/exporter/otlp/http/version' diff --git a/exporter/otlp-http/lib/opentelemetry/exporter/otlp/http/metric_exporter.rb b/exporter/otlp-http/lib/opentelemetry/exporter/otlp/http/metric_exporter.rb deleted file mode 100644 index 98c72772b..000000000 --- a/exporter/otlp-http/lib/opentelemetry/exporter/otlp/http/metric_exporter.rb +++ /dev/null @@ -1,287 +0,0 @@ -# frozen_string_literal: true - -# Copyright The OpenTelemetry Authors -# -# SPDX-License-Identifier: Apache-2.0 - -require 'opentelemetry/common' -require 'opentelemetry/exporter/otlp/common' -require 'opentelemetry/sdk' -require 'opentelemetry-metrics-sdk' -require 'net/http' -require 'csv' -require 'zlib' - -require 'google/rpc/status_pb' - -module OpenTelemetry - module Exporter - module OTLP - module HTTP - # An OpenTelemetry metric exporter that sends metrics over HTTP as Protobuf encoded OTLP ExportMetricServiceRequests. - class MetricExporter # rubocop:disable Metrics/ClassLength - SUCCESS = OpenTelemetry::SDK::Metrics::Export::SUCCESS - FAILURE = OpenTelemetry::SDK::Metrics::Export::FAILURE - private_constant(:SUCCESS, :FAILURE) - - # Default timeouts in seconds. - KEEP_ALIVE_TIMEOUT = 30 - RETRY_COUNT = 5 - WRITE_TIMEOUT_SUPPORTED = Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.6') - private_constant(:KEEP_ALIVE_TIMEOUT, :RETRY_COUNT, :WRITE_TIMEOUT_SUPPORTED) - - ERROR_MESSAGE_INVALID_HEADERS = 'headers must be a String with comma-separated URL Encoded UTF-8 k=v pairs or a Hash' - private_constant(:ERROR_MESSAGE_INVALID_HEADERS) - - def initialize(endpoint: OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_METRICS_ENDPOINT', 'OTEL_EXPORTER_OTLP_ENDPOINT', default: 'http://localhost:4318/v1/metrics'), # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity - certificate_file: OpenTelemetry::Common::Utilities.config_opt('OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE', 'OTEL_EXPORTER_OTLP_CERTIFICATE'), - ssl_verify_mode: fetch_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), - metrics_reporter: nil) - raise ArgumentError, "invalid url for OTLP::Exporter #{endpoint}" unless OpenTelemetry::Common::Utilities.valid_url?(endpoint) - raise ArgumentError, "unsupported compression key #{compression}" unless compression.nil? || %w[gzip none].include?(compression) - - @uri = if endpoint == ENV['OTEL_EXPORTER_OTLP_ENDPOINT'] - URI("#{endpoint}/v1/metrics") - else - URI(endpoint) - end - - @http = http_connection(@uri, ssl_verify_mode, certificate_file) - - @path = @uri.path - @headers = case headers - when String then parse_headers(headers) - when Hash then headers - else - raise ArgumentError, ERROR_MESSAGE_INVALID_HEADERS - end - @timeout = timeout.to_f - @compression = compression - @metrics_reporter = metrics_reporter || OpenTelemetry::SDK::Trace::Export::MetricsReporter - @shutdown = false - end - - # Called to export {OpenTelemetry::SDK::Metrics::State::MetricStream}. - # - # @param [Enumerable] metric_data the - # list of recorded {OpenTelemetry::SDK::Metrics::State::MetricStream} to be - # exported. - # @param [optional Numeric] timeout An optional timeout in seconds. - # @return [Integer] the result of the export. - def export(metric_data, timeout: nil) - return FAILURE if @shutdown - - send_bytes(OpenTelemetry::Exporter::OTLP::Common.as_encoded_emsr(metric_data), timeout: timeout) - end - - # Called when {OpenTelemetry::SDK::Metric::MeterProvider#force_flush} is called, if - # this exporter is registered to a {OpenTelemetry::SDK::Metric::MeterProvider} - # object. - # - # @param [optional Numeric] timeout An optional timeout in seconds. - def force_flush(timeout: nil) - SUCCESS - end - - # Called when {OpenTelemetry::SDK::Metric::MeterProvider#shutdown} is called, if - # this exporter is registered to a {OpenTelemetry::SDK::Trace::MeterProvider} - # object. - # - # @param [optional Numeric] timeout An optional timeout in seconds. - def shutdown(timeout: nil) - @shutdown = true - @http.finish if @http.started? - SUCCESS - end - - private - - def fetch_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 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 - - # The around_request is a private method that provides an extension - # point for the exporters network calls. The default behaviour - # is to not trace these operations. - # - # An example use case would be to prepend a patch, or extend this class - # and override this method's behaviour to explicitly trace the HTTP request. - # This would allow you to trace your export pipeline. - def around_request - OpenTelemetry::Common::Utilities.untraced { yield } - end - - def send_bytes(bytes, timeout:) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity - return FAILURE if bytes.nil? - - retry_count = 0 - timeout ||= @timeout - start_time = OpenTelemetry::Common::Utilities.timeout_timestamp - around_request do # rubocop:disable Metrics/BlockLength - request = Net::HTTP::Post.new(@path) - request.body = if @compression == 'gzip' - request.add_field('Content-Encoding', 'gzip') - Zlib.gzip(bytes) - else - bytes - end - request.add_field('Content-Type', 'application/x-protobuf') - @headers.each { |key, value| request.add_field(key, value) } - - 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) - 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) - FAILURE - when Net::HTTPBadRequest, Net::HTTPClientError, Net::HTTPServerError - log_status(response.body) - @metrics_reporter.add_to_counter('otel.otlp_exporter.failure', labels: { 'reason' => response.code }) - 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 - FAILURE - end - rescue Net::OpenTimeout, Net::ReadTimeout - retry if backoff?(retry_count: retry_count += 1, reason: 'timeout') - return FAILURE - rescue OpenSSL::SSL::SSLError - retry if backoff?(retry_count: retry_count += 1, reason: 'openssl_error') - return FAILURE - rescue SocketError - retry if backoff?(retry_count: retry_count += 1, reason: 'socket_error') - return FAILURE - rescue SystemCallError => e - retry if backoff?(retry_count: retry_count += 1, reason: e.class.name) - return FAILURE - rescue EOFError - retry if backoff?(retry_count: retry_count += 1, reason: 'eof_error') - return FAILURE - rescue Zlib::DataError - retry if backoff?(retry_count: retry_count += 1, reason: 'zlib_error') - return FAILURE - rescue StandardError => e - OpenTelemetry.handle_error(exception: e, message: 'unexpected error in OTLP::Exporter#send_bytes') - @metrics_reporter.add_to_counter('otel.otlp_exporter.failure', labels: { 'reason' => e.class.to_s }) - 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 handle_redirect(location) - # TODO: figure out destination and reinitialize @http and @path - 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 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::Exporter#log_status') - end - - def measure_request_duration - start = Process.clock_gettime(Process::CLOCK_MONOTONIC) - begin - response = yield - ensure - stop = Process.clock_gettime(Process::CLOCK_MONOTONIC) - duration_ms = 1000.0 * (stop - start) - @metrics_reporter.record_value('otel.otlp_exporter.request_duration', - value: duration_ms, - labels: { 'status' => response&.code || 'unknown' }) - end - end - - def backoff?(retry_after: nil, retry_count:, reason:) - @metrics_reporter.add_to_counter('otel.otlp_exporter.failure', labels: { 'reason' => reason }) - 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 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 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 - end - end - end - end -end diff --git a/exporter/otlp-http/test/opentelemetry/exporter/otlp/http/metric_exporter_test.rb b/exporter/otlp-http/test/opentelemetry/exporter/otlp/http/metric_exporter_test.rb deleted file mode 100644 index 10002dc4e..000000000 --- a/exporter/otlp-http/test/opentelemetry/exporter/otlp/http/metric_exporter_test.rb +++ /dev/null @@ -1,43 +0,0 @@ -# frozen_string_literal: true - -# Copyright The OpenTelemetry Authors -# -# SPDX-License-Identifier: Apache-2.0 -require 'test_helper' - -describe OpenTelemetry::Exporter::OTLP::HTTP::MetricExporter do - SUCCESS = OpenTelemetry::SDK::Metrics::Export::SUCCESS - FAILURE = OpenTelemetry::SDK::Metrics::Export::FAILURE - - it 'integrates with collector' do - WebMock.disable_net_connect!(allow: 'localhost') - OpenTelemetry::SDK.configure - meter = OpenTelemetry.meter_provider.meter('test') - instrument = meter.create_counter('b_counter') - - il = OpenTelemetry::SDK::InstrumentationLibrary.new('test_scope', 'v0.0.1') - r = OpenTelemetry::SDK::Resources::Resource.telemetry_sdk - il2 = OpenTelemetry::SDK::InstrumentationLibrary.new('secondary_scope', 'v0.0.2') - r = OpenTelemetry::SDK::Resources::Resource.telemetry_sdk - - ms1 = create_metric_stream(name: 'test_instrument_a', resource: r, instrumentation_library: il) - ms1.update(OpenTelemetry::Metrics::Measurement.new(11, { 'foo' => 'bar' })) - ms2 = create_metric_stream(name: 'test_instrument_b', resource: r, instrumentation_library: il2) - ms2.update(OpenTelemetry::Metrics::Measurement.new(11, { 'foo' => 'bar' })) - - exporter = OpenTelemetry::Exporter::OTLP::HTTP::MetricExporter.new(endpoint: 'http://localhost:4318/v1/metrics', compression: 'gzip') - result = exporter.export([ms1,ms2]) - _(result).must_equal(SUCCESS) - end - - def create_metric_stream(name: 'test_instrument', description: 'a wonderful instrument', unit: 'cm', instrument_kind: :counter, resource: OpenTelemetry::SDK::Resources::Resource.telemetry_sdk, instrumentation_library: OpenTelemetry::SDK::InstrumentationLibrary.new('test_scope', 'v0.0.1')) - OpenTelemetry::SDK::Metrics::State::MetricStream.new( - name, - description, - unit, - instrument_kind, - resource, - instrumentation_library, - ) - end -end diff --git a/exporter/otlp-http/test/test_helper.rb b/exporter/otlp-http/test/test_helper.rb index 46dafee45..bca44c00a 100644 --- a/exporter/otlp-http/test/test_helper.rb +++ b/exporter/otlp-http/test/test_helper.rb @@ -11,6 +11,5 @@ require 'opentelemetry-exporter-otlp-http' require 'minitest/autorun' require 'webmock/minitest' -require 'pry' OpenTelemetry.logger = Logger.new(File::NULL) From 5975289ab8efcb258f45f749743ebad5146b96d8 Mon Sep 17 00:00:00 2001 From: Robert Laurin Date: Tue, 28 Jun 2022 12:24:05 -0500 Subject: [PATCH 16/25] Metric Stream refactor --- metrics_sdk/.rubocop.yml | 2 +- metrics_sdk/lib/opentelemetry/sdk/metrics.rb | 1 + .../opentelemetry/sdk/metrics/aggregation.rb | 8 +++-- .../metrics/aggregation/drop_aggregation.rb | 16 --------- .../sdk/metrics/aggregation/histogram.rb | 30 ++++++++++++++++ .../metrics/aggregation/sum_aggregation.rb | 17 ---------- .../sdk/metrics/configuration_patch.rb | 2 +- .../lib/opentelemetry/sdk/metrics/export.rb | 1 - .../metrics/export/console_metric_exporter.rb | 6 ---- .../export/in_memory_metric_pull_exporter.rb | 14 ++++---- .../sdk/metrics/export/metric_reader.rb | 3 +- .../periodic_exporting_metric_reader.rb | 34 ------------------- .../sdk/metrics/instrument/counter.rb | 17 ++++++++-- .../sdk/metrics/instrument/histogram.rb | 8 ++++- .../instrument/synchronous_instrument.rb | 7 ++-- .../sdk/metrics/instrument/up_down_counter.rb | 11 +++++- .../lib/opentelemetry/sdk/metrics/state.rb | 1 + .../sdk/metrics/state/metric_data.rb | 24 +++++++++++++ .../sdk/metrics/state/metric_store.rb | 18 ++++++---- .../sdk/metrics/state/metric_stream.rb | 26 +++++++++++--- .../sdk/metrics/meter_provider_test.rb | 22 +++++------- 21 files changed, 147 insertions(+), 121 deletions(-) delete mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/drop_aggregation.rb create mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/histogram.rb delete mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/sum_aggregation.rb delete mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader.rb create mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_data.rb diff --git a/metrics_sdk/.rubocop.yml b/metrics_sdk/.rubocop.yml index e2c49e785..26f13b7c4 100644 --- a/metrics_sdk/.rubocop.yml +++ b/metrics_sdk/.rubocop.yml @@ -1,5 +1,5 @@ AllCops: - TargetRubyVersion: "2.5.0" + TargetRubyVersion: "2.6.0" Lint/UnusedMethodArgument: Enabled: false diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics.rb index c6b9a73e7..42807c708 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics.rb @@ -13,6 +13,7 @@ module Metrics end end +require 'opentelemetry/sdk/metrics/aggregation' require 'opentelemetry/sdk/metrics/configuration_patch' require 'opentelemetry/sdk/metrics/export' require 'opentelemetry/sdk/metrics/instrument' diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation.rb index db1ffac52..c709f36d0 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation.rb @@ -4,13 +4,17 @@ # # SPDX-License-Identifier: Apache-2.0 +require 'opentelemetry/sdk/metrics/aggregation/histogram' + module OpenTelemetry module SDK module Metrics module Aggregation + extend self + + SUM = ->(v1, v2) { v1 + v2 } + EXPLICIT_BUCKET_HISTOGRAM = ExplicitBucketHistogram.new end end end end - -require 'opentelemetry/sdk/metrics/aggregation/sum_aggregation' diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/drop_aggregation.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/drop_aggregation.rb deleted file mode 100644 index 75142bec8..000000000 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/drop_aggregation.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true - -# Copyright The OpenTelemetry Authors -# -# SPDX-License-Identifier: Apache-2.0 - -module OpenTelemetry - module SDK - module Metrics - module Aggregation - class DropAggregation - end - end - end - end -end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/histogram.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/histogram.rb new file mode 100644 index 000000000..70811fca9 --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/histogram.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Metrics + module Aggregation + class ExplicitBucketHistogram + # The Default Value represents the following buckets: + # (-∞, 0], (0, 5.0], (5.0, 10.0], (10.0, 25.0], (25.0, 50.0], + # (50.0, 75.0], (75.0, 100.0], (100.0, 250.0], (250.0, 500.0], + # (500.0, 1000.0], (1000.0, +∞) + DEFAULT_BOUNDARIES = [0, 5, 10, 25, 50, 75, 100, 250, 500, 1000].freeze + def initialize(boundaries: DEFAULT_BOUNDARIES, record_min_max: true) + @boundaries = boundaries + @record_min_max = record_min_max + + end + + def call(v1, v2) + # TODO: + end + end + end + end + end +end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/sum_aggregation.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/sum_aggregation.rb deleted file mode 100644 index 3c1c75bab..000000000 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/sum_aggregation.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -# Copyright The OpenTelemetry Authors -# -# SPDX-License-Identifier: Apache-2.0 - -module OpenTelemetry - module SDK - module Metrics - module Aggregation - class SumAggregation - - end - end - end - end -end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/configuration_patch.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/configuration_patch.rb index 53b0b1f7f..ac529e67f 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/configuration_patch.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/configuration_patch.rb @@ -15,7 +15,7 @@ module ConfiguratorPatch # The metrics_configuration_hook method is where we define the setup process # for metrics SDK. def metrics_configuration_hook - OpenTelemetry.meter_provider = Metrics::MeterProvider.new + OpenTelemetry.meter_provider = Metrics::MeterProvider.new(resource: @resource) end end end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/export.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/export.rb index b1d916b01..033f86929 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/export.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/export.rb @@ -24,6 +24,5 @@ module Export end require 'opentelemetry/sdk/metrics/export/metric_reader' -require 'opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader' require 'opentelemetry/sdk/metrics/export/console_metric_exporter' require 'opentelemetry/sdk/metrics/export/in_memory_metric_pull_exporter' diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/export/console_metric_exporter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/export/console_metric_exporter.rb index ae4ca9808..9ff0a615c 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/export/console_metric_exporter.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/export/console_metric_exporter.rb @@ -9,8 +9,6 @@ module SDK module Metrics module Export class ConsoleMetricExporter - PREFERRED_TEMPORALITY = 'delta' - def export(metrics) puts metrics end @@ -18,10 +16,6 @@ def export(metrics) def shutdown SUCCESS end - - def preferred_temporality - PREFERRED_TEMPORALITY - end end end end 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 b9f8ffac8..a0a7e6176 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 @@ -8,9 +8,9 @@ module OpenTelemetry module SDK module Metrics module Export - class InMemoryMetricPullExporter < MetricReader - PREFERRED_TEMPORALITY = 'delta' - + # The InMemoryMetricPullExporter behaves as a Metric Reader and Exporter. + # To be used for testing purposes, not production. + class InMemoryMetricPullExporter attr_reader :metric_snapshots, :metric_store def initialize @@ -19,6 +19,10 @@ def initialize @metric_store = OpenTelemetry::SDK::Metrics::State::MetricStore.new end + def collect + @metric_store.collect + end + def pull export(collect) end @@ -41,10 +45,6 @@ def reset def shutdown SUCCESS end - - def preferred_temporality - PREFERRED_TEMPORALITY - end end end end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/export/metric_reader.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/export/metric_reader.rb index cebebbe22..ae67e2cf4 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/export/metric_reader.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/export/metric_reader.rb @@ -11,8 +11,7 @@ module Export class MetricReader attr_reader :metric_store - def initialize(exporter) - @exporter = exporter + def initialize @metric_store = OpenTelemetry::SDK::Metrics::State::MetricStore.new end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader.rb deleted file mode 100644 index 54e5970e5..000000000 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader.rb +++ /dev/null @@ -1,34 +0,0 @@ -# frozen_string_literal: true - -# Copyright The OpenTelemetry Authors -# -# SPDX-License-Identifier: Apache-2.0 - -module OpenTelemetry - module SDK - module Metrics - module Export - class PeriodicExportingMetricReader < MetricReader - # exporter - the push exporter where the metrics are sent to. - # exportIntervalMillis - the time interval in milliseconds between two consecutive exports. The default value is 60000 (milliseconds). - # exportTimeoutMillis - how long the export can run before it is cancelled. The default value is 30000 (milliseconds). - def initialize(exporter, aggregation_temporality: nil, export_interval_millis: 60_000, export_timeout_millis: 30_000) - @exporter = exporter - @export_interval_millis = export_interval_millis - @export_timeout_millis = export_timeout_millis - @aggregation_temporality = aggregation_temporality || exporter.preferred_temporality # || Cumulative - - @thread = Thread.new { work } - end - - private - - def work - sleep(3) - @exporter.export(collect) - end - end - end - end - end -end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/counter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/counter.rb index b2699097b..fa8150303 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/counter.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/counter.rb @@ -10,6 +10,8 @@ module Metrics module Instrument # {Counter} is the SDK implementation of {OpenTelemetry::Metrics::Counter}. class Counter < OpenTelemetry::SDK::Metrics::Instrument::SynchronousInstrument + DEFAULT_AGGREGATION = OpenTelemetry::SDK::Metrics::Aggregation::SUM + # Returns the instrument kind as a Symbol # # @return [Symbol] @@ -25,9 +27,18 @@ def instrument_kind # Array values must not contain nil elements and all elements must be of # the same basic type (string, numeric, boolean). def add(increment, attributes: {}) - update( - OpenTelemetry::Metrics::Measurement.new(increment, attributes) - ) + if increment.negative? + OpenTelemetry.logger.warn("#{@name} received a negative value") + else + update( + OpenTelemetry::Metrics::Measurement.new(increment, attributes), + DEFAULT_AGGREGATION + ) + end + nil + rescue => e + OpenTelemetry.handle_error(exception: e) + nil end end end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/histogram.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/histogram.rb index c632d1da5..7ce21d1e5 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/histogram.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/histogram.rb @@ -10,6 +10,8 @@ module Metrics module Instrument # {Histogram} is the SDK implementation of {OpenTelemetry::Metrics::Histogram}. class Histogram < OpenTelemetry::SDK::Metrics::Instrument::SynchronousInstrument + DEFAULT_AGGREGATION = OpenTelemetry::SDK::Metrics::Aggregation::EXPLICIT_BUCKET_HISTOGRAM + # Returns the instrument kind as a Symbol # # @return [Symbol] @@ -25,7 +27,11 @@ def instrument_kind # Array values must not contain nil elements and all elements must be of # the same basic type (string, numeric, boolean). def record(amount, attributes: nil); - update(OpenTelemetry::Metrics::Measurement.new(increment, attributes)) + update(OpenTelemetry::Metrics::Measurement.new(amount, attributes)) + nil + rescue => e + OpenTelemetry.handle_error(exception: e) + nil end end end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/synchronous_instrument.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/synchronous_instrument.rb index dfb62a685..8d078e1d7 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/synchronous_instrument.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/synchronous_instrument.rb @@ -14,6 +14,7 @@ def initialize(name, unit, description, instrumentation_library, meter_provider) @unit = unit @description = description @instrumentation_library = instrumentation_library + @meter_provider = meter_provider @metric_streams = [] meter_provider.metric_readers.each do |metric_reader| @@ -27,7 +28,7 @@ def register_with_new_metric_store(metric_store) @description, @unit, instrument_kind, - nil, #meter_provider.resource, + @meter_provider, @instrumentation_library ) @metric_streams << ms @@ -36,9 +37,9 @@ def register_with_new_metric_store(metric_store) private - def update(measurement) + def update(measurement, aggregation) @metric_streams.each do |ms| - ms.update(measurement) + ms.update(measurement, aggregation) end end end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/up_down_counter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/up_down_counter.rb index 81ddc6c79..38012cffd 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/up_down_counter.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/up_down_counter.rb @@ -10,6 +10,8 @@ module Metrics module Instrument # {UpDownCounter} is the SDK implementation of {OpenTelemetry::Metrics::UpDownCounter}. class UpDownCounter < OpenTelemetry::SDK::Metrics::Instrument::SynchronousInstrument + DEFAULT_AGGREGATION = OpenTelemetry::SDK::Metrics::Aggregation::SUM + # Returns the instrument kind as a Symbol # # @return [Symbol] @@ -25,7 +27,14 @@ def instrument_kind # Array values must not contain nil elements and all elements must be of # the same basic type (string, numeric, boolean). def add(amount, attributes: nil) - update(OpenTelemetry::Metrics::Measurement.new(increment, attributes)) + update( + OpenTelemetry::Metrics::Measurement.new(amount, attributes), + DEFAULT_AGGREGATION + ) + nil + rescue => e + OpenTelemetry.handle_error(exception: e) + nil end end end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/state.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/state.rb index 3c7f4c395..313700f3d 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/state.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/state.rb @@ -13,5 +13,6 @@ module State end end +require 'opentelemetry/sdk/metrics/state/metric_data' require 'opentelemetry/sdk/metrics/state/metric_store' require 'opentelemetry/sdk/metrics/state/metric_stream' diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_data.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_data.rb new file mode 100644 index 000000000..bf982c5cd --- /dev/null +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_data.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Metrics + module State + # MetricData is a Struct containing {MetricStream} data for export. + MetricData = Struct.new(:name, # String + :description, # String + :unit, # String + :instrument_kind, # Symbol + :resource, # OpenTelemetry::SDK::Resources::Resource + :instrumentation_library, # OpenTelemetry::SDK::InstrumentationLibrary + :data_points, # Hash{Hash{String => String, Numeric, Boolean, Array} => Numeric} + :start_time_unix_nano, # Integer nanoseconds since Epoch + :time_unix_nano) # Integer nanoseconds since Epoch + end + end + end +end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_store.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_store.rb index 24de6af0e..dfd77f41d 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_store.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_store.rb @@ -11,7 +11,7 @@ module State class MetricStore def initialize @mutex = Mutex.new - @epoch_start_time = Time.now.to_i + @epoch_start_time = now_in_nano @epoch_end_time = nil; @metric_streams = [] end @@ -19,12 +19,10 @@ def initialize def collect # this probably needs to take the mutex, take a snapshot of state, (reset state?), release the mutex @mutex.synchronize do - [ - @end_time = Time.now.to_i, - @metric_streams, - @epoch_start_time = @end_time, - ] - @metric_streams + @epoch_end_time = now_in_nano + snapshot = @metric_streams.map { |ms| ms.collect(@epoch_start_time, @epoch_end_time)} + @epoch_start_time = @epoch_end_time + snapshot end end @@ -32,6 +30,12 @@ def add_metric_stream(metric_stream) @metric_streams = @metric_streams.dup.push(metric_stream) nil end + + private + + def now_in_nano + (Time.now.to_r * 1_000_000_000).to_i + end end end end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_stream.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_stream.rb index 64693563b..d939e0d1b 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_stream.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_stream.rb @@ -9,31 +9,47 @@ module SDK module Metrics module State class MetricStream - attr_reader :name, :description, :unit, :instrument_kind, :resource, :instrumentation_library, :data_points + attr_reader :name, :description, :unit, :instrument_kind, :instrumentation_library, :data_points def initialize( name, description, unit, instrument_kind, - resource, + meter_provider, instrumentation_library ) @name = name @description = description @unit = unit @instrument_kind = instrument_kind - @resource = resource + @meter_provider = meter_provider @instrumentation_library = instrumentation_library @data_points = {} @mutex = Mutex.new end - def update(measurement) + def collect(start_time, end_time) + @mutex.synchronize do + MetricData.new( + @name, + @description, + @unit, + @instrument_kind, + @meter_provider.resource, + @instrumentation_library, + @data_points.dup, + start_time, + end_time + ) + end + end + + def update(measurement, aggregation) @mutex.synchronize do if @data_points[measurement.attributes] - @data_points[measurement.attributes] = @data_points[measurement.attributes] + measurement.value + @data_points[measurement.attributes] = aggregation.call(@data_points[measurement.attributes], measurement.value) else @data_points[measurement.attributes] = measurement.value end diff --git a/metrics_sdk/test/opentelemetry/sdk/metrics/meter_provider_test.rb b/metrics_sdk/test/opentelemetry/sdk/metrics/meter_provider_test.rb index 6a532dc3e..e7b3f2061 100644 --- a/metrics_sdk/test/opentelemetry/sdk/metrics/meter_provider_test.rb +++ b/metrics_sdk/test/opentelemetry/sdk/metrics/meter_provider_test.rb @@ -98,9 +98,7 @@ describe '#add_metric_reader' do it 'adds a metric reader' do - metric_reader = OpenTelemetry::SDK::Metrics::Export::MetricReader.new( - OpenTelemetry::SDK::Metrics::Export::ConsoleMetricExporter.new - ) + metric_reader = OpenTelemetry::SDK::Metrics::Export::MetricReader.new OpenTelemetry.meter_provider.add_metric_reader(metric_reader) @@ -110,10 +108,10 @@ it 'associates the metric store with instruments created before the metric reader' do meter_a = OpenTelemetry.meter_provider.meter('a').create_counter('meter_a') - metric_reader_a = OpenTelemetry::SDK::Metrics::Export::MetricReader.new(OpenTelemetry::SDK::Metrics::Export::ConsoleMetricExporter.new) + metric_reader_a = OpenTelemetry::SDK::Metrics::Export::MetricReader.new OpenTelemetry.meter_provider.add_metric_reader(metric_reader_a) - metric_reader_b = OpenTelemetry::SDK::Metrics::Export::MetricReader.new(OpenTelemetry::SDK::Metrics::Export::ConsoleMetricExporter.new) + metric_reader_b = OpenTelemetry::SDK::Metrics::Export::MetricReader.new OpenTelemetry.meter_provider.add_metric_reader(metric_reader_b) _(meter_a.instance_variable_get(:@metric_streams).size).must_equal(2) @@ -122,10 +120,10 @@ end it 'associates the metric store with instruments created after the metric reader' do - metric_reader_a = OpenTelemetry::SDK::Metrics::Export::MetricReader.new(OpenTelemetry::SDK::Metrics::Export::ConsoleMetricExporter.new) + metric_reader_a = OpenTelemetry::SDK::Metrics::Export::MetricReader.new OpenTelemetry.meter_provider.add_metric_reader(metric_reader_a) - metric_reader_b = OpenTelemetry::SDK::Metrics::Export::MetricReader.new(OpenTelemetry::SDK::Metrics::Export::ConsoleMetricExporter.new) + metric_reader_b = OpenTelemetry::SDK::Metrics::Export::MetricReader.new OpenTelemetry.meter_provider.add_metric_reader(metric_reader_b) meter_a = OpenTelemetry.meter_provider.meter('a').create_counter('meter_a') @@ -136,16 +134,12 @@ end end - describe '#add_view' do - it 'adds a view' do - # TODO - # OpenTelemetry.meter_provider.add_view - end - end + # TODO:OpenTelemetry.meter_provider.add_view + describe '#add_view' do; end private def new_mock_reader - Minitest::Mock.new(OpenTelemetry::SDK::Metrics::Export::MetricReader.new(nil)) + Minitest::Mock.new(OpenTelemetry::SDK::Metrics::Export::MetricReader.new) end end From 9fe4f639c0f9ab0255d45a5a4efc5b9272a5d4f3 Mon Sep 17 00:00:00 2001 From: Robert Laurin Date: Tue, 28 Jun 2022 12:28:04 -0500 Subject: [PATCH 17/25] Remove metrics test helper --- .../lib/opentelemetry/test_helpers.rb | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/test_helpers/lib/opentelemetry/test_helpers.rb b/test_helpers/lib/opentelemetry/test_helpers.rb index e5ff6a39d..b43095c99 100644 --- a/test_helpers/lib/opentelemetry/test_helpers.rb +++ b/test_helpers/lib/opentelemetry/test_helpers.rb @@ -63,28 +63,6 @@ def with_ids(trace_id, span_id) end end - def create_metric_stream( - name: 'test_instrument', - description: 'a wonderful instrument', - unit: 'ms', - instrument_kind: :counter, - value: 1, - attributes: {}, - resource: OpenTelemetry::SDK::Resources::Resource.telemetry_sdk, - instrumentation_library: OpenTelemetry::SDK::InstrumentationLibrary.new('test_scope', 'v0.0.1') - ) - ms = OpenTelemetry::SDK::Metrics::State::MetricStream.new( - name, - description, - unit, - instrument_kind, - resource, - instrumentation_library, - ) - ms.update(OpenTelemetry::Metrics::Measurement.new(value, attributes)) - ms - end - def create_span_data(name: '', kind: nil, status: nil, parent_span_id: OpenTelemetry::Trace::INVALID_SPAN_ID, total_recorded_attributes: 0, total_recorded_events: 0, total_recorded_links: 0, start_timestamp: OpenTelemetry::TestHelpers.exportable_timestamp, end_timestamp: OpenTelemetry::TestHelpers.exportable_timestamp, attributes: nil, links: nil, events: nil, resource: nil, From fdfda89375c2b732e4e953a8fceac65728475bb5 Mon Sep 17 00:00:00 2001 From: Robert Laurin Date: Tue, 28 Jun 2022 13:57:46 -0500 Subject: [PATCH 18/25] feedback fixups --- metrics_api/lib/opentelemetry/metrics/meter.rb | 4 +--- metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb | 6 ++++++ .../lib/opentelemetry/sdk/metrics/meter_provider.rb | 6 +----- .../lib/opentelemetry/sdk/metrics/state/metric_store.rb | 9 +++++---- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/metrics_api/lib/opentelemetry/metrics/meter.rb b/metrics_api/lib/opentelemetry/metrics/meter.rb index 5ce9c0ed2..cc859329e 100644 --- a/metrics_api/lib/opentelemetry/metrics/meter.rb +++ b/metrics_api/lib/opentelemetry/metrics/meter.rb @@ -24,8 +24,6 @@ class Meter InstrumentUnitError = Class.new(OpenTelemetry::Error) InstrumentDescriptionError = Class.new(OpenTelemetry::Error) - attr_reader :instrument_registry - def initialize @mutex = Mutex.new @instrument_registry = {} @@ -66,7 +64,7 @@ def create_instrument(kind, name, unit, description, callback) @mutex.synchronize do if @instrument_registry.include? name - OpenTelemetry.logger.warn("duplicate instrument registration occured for instrument #{name}") + OpenTelemetry.logger.warn("duplicate instrument registration occurred for instrument #{name}") end @instrument_registry[name] = yield diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb index dd77e143c..f53627c63 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb @@ -26,6 +26,12 @@ def initialize(name, version, meter_provider) @meter_provider = meter_provider end + def add_metric_reader(metric_reader) + @instrument_registry.each do |_n, instrument| + instrument.register_with_new_metric_store(metric_reader.metric_store) + end + end + def create_instrument(kind, name, unit, description, callback) super do case kind diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb index aec44838e..c3b3c8000 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter_provider.rb @@ -109,11 +109,7 @@ def add_metric_reader(metric_reader) OpenTelemetry.logger.warn('calling MetricProvider#add_metric_reader after shutdown.') else @metric_readers.push(metric_reader) - @meter_registry.each do |_k, meter| - meter.instrument_registry.each do |_n, instrument| - instrument.register_with_new_metric_store(metric_reader.metric_store) - end - end + @meter_registry.each_value { |meter| meter.add_metric_reader(metric_reader) } end nil diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_store.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_store.rb index dfd77f41d..02452cd66 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_store.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_store.rb @@ -12,12 +12,11 @@ class MetricStore def initialize @mutex = Mutex.new @epoch_start_time = now_in_nano - @epoch_end_time = nil; + @epoch_end_time = nil @metric_streams = [] end def collect - # this probably needs to take the mutex, take a snapshot of state, (reset state?), release the mutex @mutex.synchronize do @epoch_end_time = now_in_nano snapshot = @metric_streams.map { |ms| ms.collect(@epoch_start_time, @epoch_end_time)} @@ -27,8 +26,10 @@ def collect end def add_metric_stream(metric_stream) - @metric_streams = @metric_streams.dup.push(metric_stream) - nil + @mutex.synchronize do + @metric_streams = @metric_streams.dup.push(metric_stream) + nil + end end private From 79605fddebf9b12e2be05d389b2a8023ef569c6b Mon Sep 17 00:00:00 2001 From: Robert Laurin Date: Tue, 28 Jun 2022 14:05:06 -0500 Subject: [PATCH 19/25] Remove console exporter --- .../lib/opentelemetry/sdk/metrics/export.rb | 1 - .../metrics/export/console_metric_exporter.rb | 23 ------------------- 2 files changed, 24 deletions(-) delete mode 100644 metrics_sdk/lib/opentelemetry/sdk/metrics/export/console_metric_exporter.rb diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/export.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/export.rb index 033f86929..37716c801 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/export.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/export.rb @@ -24,5 +24,4 @@ module Export end require 'opentelemetry/sdk/metrics/export/metric_reader' -require 'opentelemetry/sdk/metrics/export/console_metric_exporter' require 'opentelemetry/sdk/metrics/export/in_memory_metric_pull_exporter' diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/export/console_metric_exporter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/export/console_metric_exporter.rb deleted file mode 100644 index 9ff0a615c..000000000 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/export/console_metric_exporter.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -# Copyright The OpenTelemetry Authors -# -# SPDX-License-Identifier: Apache-2.0 - -module OpenTelemetry - module SDK - module Metrics - module Export - class ConsoleMetricExporter - def export(metrics) - puts metrics - end - - def shutdown - SUCCESS - end - end - end - end - end -end From dfcd8f5eb1c64c4b7bdf98d8b8fd0284e30121db Mon Sep 17 00:00:00 2001 From: Robert Laurin Date: Tue, 28 Jun 2022 14:13:16 -0500 Subject: [PATCH 20/25] Add integration test assertions --- .../in_memory_metric_pull_exporter_test.rb | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/metrics_sdk/test/integration/in_memory_metric_pull_exporter_test.rb b/metrics_sdk/test/integration/in_memory_metric_pull_exporter_test.rb index 716aa0812..99a44e44f 100644 --- a/metrics_sdk/test/integration/in_memory_metric_pull_exporter_test.rb +++ b/metrics_sdk/test/integration/in_memory_metric_pull_exporter_test.rb @@ -17,7 +17,7 @@ OpenTelemetry.meter_provider.add_metric_reader(metric_exporter) meter = OpenTelemetry.meter_provider.meter('test') - instrument = meter.create_counter('b_counter') + instrument = meter.create_counter('b_counter', unit: 'smidgen', description: 'a small amount of something') instrument.add(1) instrument.add(2, attributes: { 'a' => 'b' }) @@ -30,7 +30,17 @@ _(last_snapshot).wont_be_empty _(last_snapshot[0].name).must_equal('b_counter') + _(last_snapshot[0].unit).must_equal('smidgen') + _(last_snapshot[0].description).must_equal('a small amount of something') _(last_snapshot[0].instrumentation_library.name).must_equal('test') + _(last_snapshot[0].data_points).must_equal( + { + {} => 1, + { 'a' => 'b' } => 4, + { 'b' => 'c' } => 3, + { 'd' => 'e' } => 4 + } + ) end end end From e8684d632e558b50a7808993ab66a750853fa82c Mon Sep 17 00:00:00 2001 From: Robert Laurin Date: Tue, 28 Jun 2022 14:32:01 -0500 Subject: [PATCH 21/25] fix: broken test assert --- metrics_api/test/opentelemetry/metrics/meter_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metrics_api/test/opentelemetry/metrics/meter_test.rb b/metrics_api/test/opentelemetry/metrics/meter_test.rb index 904857fa5..493c373b5 100644 --- a/metrics_api/test/opentelemetry/metrics/meter_test.rb +++ b/metrics_api/test/opentelemetry/metrics/meter_test.rb @@ -20,7 +20,7 @@ OpenTelemetry::TestHelpers.with_test_logger do |log_stream| meter.create_counter('a_counter') meter.create_counter('a_counter') - _(log_stream.string).must_match(/duplicate instrument registration occured for instrument a_counter/) + _(log_stream.string).must_match(/duplicate instrument registration occurred for instrument a_counter/) end end From cd4fbb79bb7fafc0eaf02a31960a156506a78d53 Mon Sep 17 00:00:00 2001 From: Robert Laurin Date: Tue, 28 Jun 2022 14:36:50 -0500 Subject: [PATCH 22/25] chore: appease the cop --- metrics_api/lib/opentelemetry-metrics-api.rb | 1 - metrics_api/lib/opentelemetry/metrics/meter.rb | 4 +--- metrics_api/opentelemetry-metrics-api.gemspec | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/metrics_api/lib/opentelemetry-metrics-api.rb b/metrics_api/lib/opentelemetry-metrics-api.rb index 771d7c34d..7363a7561 100644 --- a/metrics_api/lib/opentelemetry-metrics-api.rb +++ b/metrics_api/lib/opentelemetry-metrics-api.rb @@ -17,7 +17,6 @@ # # The OpenTelemetry module provides global accessors for telemetry objects. module OpenTelemetry - @meter_provider = Internal::ProxyMeterProvider.new # Register the global meter provider. diff --git a/metrics_api/lib/opentelemetry/metrics/meter.rb b/metrics_api/lib/opentelemetry/metrics/meter.rb index cc859329e..78b4c2416 100644 --- a/metrics_api/lib/opentelemetry/metrics/meter.rb +++ b/metrics_api/lib/opentelemetry/metrics/meter.rb @@ -63,9 +63,7 @@ def create_instrument(kind, name, unit, description, callback) raise InstrumentDescriptionError if description && (description.size > 1023 || !utf8mb3_encoding?(description.dup)) @mutex.synchronize do - if @instrument_registry.include? name - OpenTelemetry.logger.warn("duplicate instrument registration occurred for instrument #{name}") - end + OpenTelemetry.logger.warn("duplicate instrument registration occurred for instrument #{name}") if @instrument_registry.include? name @instrument_registry[name] = yield end diff --git a/metrics_api/opentelemetry-metrics-api.gemspec b/metrics_api/opentelemetry-metrics-api.gemspec index de32e11bc..708d665b1 100644 --- a/metrics_api/opentelemetry-metrics-api.gemspec +++ b/metrics_api/opentelemetry-metrics-api.gemspec @@ -27,11 +27,11 @@ Gem::Specification.new do |spec| spec.add_dependency 'opentelemetry-api', '~> 1.0' - spec.add_development_dependency 'opentelemetry-test-helpers' spec.add_development_dependency 'benchmark-ipsa', '~> 0.2.0' spec.add_development_dependency 'bundler', '>= 1.17' spec.add_development_dependency 'faraday', '~> 0.13' 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', '~> 0.73.0' spec.add_development_dependency 'simplecov', '~> 0.17' From a46ba22e8c8cea4fe849c5d3cd58dc554148631e Mon Sep 17 00:00:00 2001 From: Robert Laurin Date: Tue, 28 Jun 2022 15:08:53 -0500 Subject: [PATCH 23/25] chore: appease the cop --- .../lib/opentelemetry/sdk/metrics/aggregation.rb | 2 ++ .../sdk/metrics/aggregation/histogram.rb | 12 ++++++------ .../sdk/metrics/export/metric_reader.rb | 7 +++++-- .../sdk/metrics/instrument/counter.rb | 2 +- .../sdk/metrics/instrument/histogram.rb | 4 ++-- .../metrics/instrument/synchronous_instrument.rb | 3 +++ .../sdk/metrics/instrument/up_down_counter.rb | 2 +- metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb | 1 + metrics_sdk/lib/opentelemetry/sdk/metrics/state.rb | 4 ++++ .../sdk/metrics/state/metric_store.rb | 6 +++++- .../sdk/metrics/state/metric_stream.rb | 14 +++++++++----- metrics_sdk/opentelemetry-metrics-sdk.gemspec | 4 ++-- .../in_memory_metric_pull_exporter_test.rb | 10 ++++------ .../sdk/metrics/meter_provider_test.rb | 5 +++-- 14 files changed, 48 insertions(+), 28 deletions(-) diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation.rb index c709f36d0..8d4f9ba23 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation.rb @@ -9,6 +9,8 @@ module OpenTelemetry module SDK module Metrics + # The Aggregation module contains the OpenTelemetry metrics reference + # aggregation implementations. module Aggregation extend self diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/histogram.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/histogram.rb index 70811fca9..0ae5f5e32 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/histogram.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/histogram.rb @@ -8,21 +8,21 @@ module OpenTelemetry module SDK module Metrics module Aggregation + # Contains the implementation of the ExplicitBucketHistogram aggregation + # https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#explicit-bucket-histogram-aggregation class ExplicitBucketHistogram # The Default Value represents the following buckets: - # (-∞, 0], (0, 5.0], (5.0, 10.0], (10.0, 25.0], (25.0, 50.0], + # (-inf, 0], (0, 5.0], (5.0, 10.0], (10.0, 25.0], (25.0, 50.0], # (50.0, 75.0], (75.0, 100.0], (100.0, 250.0], (250.0, 500.0], - # (500.0, 1000.0], (1000.0, +∞) + # (500.0, 1000.0], (1000.0, +inf) DEFAULT_BOUNDARIES = [0, 5, 10, 25, 50, 75, 100, 250, 500, 1000].freeze def initialize(boundaries: DEFAULT_BOUNDARIES, record_min_max: true) @boundaries = boundaries @record_min_max = record_min_max - end - def call(v1, v2) - # TODO: - end + # TODO: Implement ExplicitBucketHistogram + def call(_old, _new); end end end end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/export/metric_reader.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/export/metric_reader.rb index ae67e2cf4..c261f4f10 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/export/metric_reader.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/export/metric_reader.rb @@ -8,6 +8,9 @@ module OpenTelemetry module SDK module Metrics module Export + # MetricReader provides a minimal example implementation. + # It is not required to subclass this class to provide an implementation + # of MetricReader, provided the interface is satisfied. class MetricReader attr_reader :metric_store @@ -20,11 +23,11 @@ def collect end def shutdown(timeout: nil) - SUCCESS + Export::SUCCESS end def force_flush(timeout: nil) - SUCCESS + Export::SUCCESS end end end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/counter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/counter.rb index fa8150303..526c57362 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/counter.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/counter.rb @@ -36,7 +36,7 @@ def add(increment, attributes: {}) ) end nil - rescue => e + rescue StandardError => e OpenTelemetry.handle_error(exception: e) nil end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/histogram.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/histogram.rb index 7ce21d1e5..cb78f99ea 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/histogram.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/histogram.rb @@ -26,10 +26,10 @@ def instrument_kind # Values must be non-nil and (array of) string, boolean or numeric type. # Array values must not contain nil elements and all elements must be of # the same basic type (string, numeric, boolean). - def record(amount, attributes: nil); + def record(amount, attributes: nil) update(OpenTelemetry::Metrics::Measurement.new(amount, attributes)) nil - rescue => e + rescue StandardError => e OpenTelemetry.handle_error(exception: e) nil end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/synchronous_instrument.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/synchronous_instrument.rb index 8d078e1d7..0080186fd 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/synchronous_instrument.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/synchronous_instrument.rb @@ -8,6 +8,8 @@ module OpenTelemetry module SDK module Metrics module Instrument + # {SynchronousInstrument} contains the common functionality shared across + # the synchronous instruments SDK instruments. class SynchronousInstrument def initialize(name, unit, description, instrumentation_library, meter_provider) @name = name @@ -22,6 +24,7 @@ def initialize(name, unit, description, instrumentation_library, meter_provider) end end + # @api private def register_with_new_metric_store(metric_store) ms = OpenTelemetry::SDK::Metrics::State::MetricStream.new( @name, diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/up_down_counter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/up_down_counter.rb index 38012cffd..5e512f415 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/up_down_counter.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/up_down_counter.rb @@ -32,7 +32,7 @@ def add(amount, attributes: nil) DEFAULT_AGGREGATION ) nil - rescue => e + rescue StandardError => e OpenTelemetry.handle_error(exception: e) nil end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb index f53627c63..f4355f116 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb @@ -26,6 +26,7 @@ def initialize(name, version, meter_provider) @meter_provider = meter_provider end + # @api private def add_metric_reader(metric_reader) @instrument_registry.each do |_n, instrument| instrument.register_with_new_metric_store(metric_reader.metric_store) diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/state.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/state.rb index 313700f3d..0512f5136 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/state.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/state.rb @@ -7,6 +7,10 @@ module OpenTelemetry module SDK module Metrics + # @api private + # + # The State module provides SDK internal functionality that is not a part of the + # public API. module State end end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_store.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_store.rb index 02452cd66..de2ecb83b 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_store.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_store.rb @@ -8,6 +8,10 @@ module OpenTelemetry module SDK module Metrics module State + # @api private + # + # The MetricStore module provides SDK internal functionality that is not a part of the + # public API. class MetricStore def initialize @mutex = Mutex.new @@ -19,7 +23,7 @@ def initialize def collect @mutex.synchronize do @epoch_end_time = now_in_nano - snapshot = @metric_streams.map { |ms| ms.collect(@epoch_start_time, @epoch_end_time)} + snapshot = @metric_streams.map { |ms| ms.collect(@epoch_start_time, @epoch_end_time) } @epoch_start_time = @epoch_end_time snapshot end diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_stream.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_stream.rb index d939e0d1b..60ed86e75 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_stream.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_stream.rb @@ -8,6 +8,10 @@ module OpenTelemetry module SDK module Metrics module State + # @api private + # + # The MetricStream class provides SDK internal functionality that is not a part of the + # public API. class MetricStream attr_reader :name, :description, :unit, :instrument_kind, :instrumentation_library, :data_points @@ -48,11 +52,11 @@ def collect(start_time, end_time) def update(measurement, aggregation) @mutex.synchronize do - if @data_points[measurement.attributes] - @data_points[measurement.attributes] = aggregation.call(@data_points[measurement.attributes], measurement.value) - else - @data_points[measurement.attributes] = measurement.value - end + @data_points[measurement.attributes] = if @data_points[measurement.attributes] + aggregation.call(@data_points[measurement.attributes], measurement.value) + else + measurement.value + end end end diff --git a/metrics_sdk/opentelemetry-metrics-sdk.gemspec b/metrics_sdk/opentelemetry-metrics-sdk.gemspec index f17e64227..4674ef152 100644 --- a/metrics_sdk/opentelemetry-metrics-sdk.gemspec +++ b/metrics_sdk/opentelemetry-metrics-sdk.gemspec @@ -23,17 +23,17 @@ Gem::Specification.new do |spec| ::Dir.glob('*.md') + ['LICENSE', '.yardopts'] spec.require_paths = ['lib'] - spec.required_ruby_version = '>= 2.5.0' + spec.required_ruby_version = '>= 2.6.0' spec.add_dependency 'opentelemetry-api', '~> 1.0' spec.add_dependency 'opentelemetry-metrics-api' spec.add_dependency 'opentelemetry-sdk', '~> 1.0' - spec.add_development_dependency 'opentelemetry-test-helpers' spec.add_development_dependency 'benchmark-ipsa', '~> 0.2.0' spec.add_development_dependency 'bundler', '>= 1.17' spec.add_development_dependency 'faraday', '~> 0.13' 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', '~> 0.73.0' spec.add_development_dependency 'simplecov', '~> 0.17' diff --git a/metrics_sdk/test/integration/in_memory_metric_pull_exporter_test.rb b/metrics_sdk/test/integration/in_memory_metric_pull_exporter_test.rb index 99a44e44f..a62443da1 100644 --- a/metrics_sdk/test/integration/in_memory_metric_pull_exporter_test.rb +++ b/metrics_sdk/test/integration/in_memory_metric_pull_exporter_test.rb @@ -34,12 +34,10 @@ _(last_snapshot[0].description).must_equal('a small amount of something') _(last_snapshot[0].instrumentation_library.name).must_equal('test') _(last_snapshot[0].data_points).must_equal( - { - {} => 1, - { 'a' => 'b' } => 4, - { 'b' => 'c' } => 3, - { 'd' => 'e' } => 4 - } + {} => 1, + { 'a' => 'b' } => 4, + { 'b' => 'c' } => 3, + { 'd' => 'e' } => 4 ) end end diff --git a/metrics_sdk/test/opentelemetry/sdk/metrics/meter_provider_test.rb b/metrics_sdk/test/opentelemetry/sdk/metrics/meter_provider_test.rb index e7b3f2061..a234cdd69 100644 --- a/metrics_sdk/test/opentelemetry/sdk/metrics/meter_provider_test.rb +++ b/metrics_sdk/test/opentelemetry/sdk/metrics/meter_provider_test.rb @@ -134,8 +134,9 @@ end end - # TODO:OpenTelemetry.meter_provider.add_view - describe '#add_view' do; end + # TODO: OpenTelemetry.meter_provider.add_view + describe '#add_view' do + end private From 241db2c6bf54bd673fc918a7c65b521b31ed0bce Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 30 Jun 2022 10:46:36 -0500 Subject: [PATCH 24/25] fix: francis feedback Co-authored-by: Francis Bogsanyi --- .../metrics/export/in_memory_metric_pull_exporter.rb | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) 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 a0a7e6176..9a8612c22 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 @@ -10,17 +10,13 @@ module Metrics module Export # The InMemoryMetricPullExporter behaves as a Metric Reader and Exporter. # To be used for testing purposes, not production. - class InMemoryMetricPullExporter - attr_reader :metric_snapshots, :metric_store + class InMemoryMetricPullExporter < MetricReader + attr_reader :metric_snapshots def initialize + super @metric_snapshots = [] @mutex = Mutex.new - @metric_store = OpenTelemetry::SDK::Metrics::State::MetricStore.new - end - - def collect - @metric_store.collect end def pull From c261b6d603c42aa6c7977fc6bb03b4a349b348b6 Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 30 Jun 2022 10:46:54 -0500 Subject: [PATCH 25/25] fix: add hayworth comment Co-authored-by: Andrew Hayworth --- .../lib/opentelemetry/sdk/metrics/instrument/counter.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/counter.rb b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/counter.rb index 526c57362..0ad83d385 100644 --- a/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/counter.rb +++ b/metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/counter.rb @@ -27,6 +27,9 @@ def instrument_kind # Array values must not contain nil elements and all elements must be of # the same basic type (string, numeric, boolean). def add(increment, attributes: {}) + # TODO: When the metrics SDK stabilizes and is merged into the main SDK, + # we can leverage the SDK Internal validation classes to enforce this: + # https://github.com/open-telemetry/opentelemetry-ruby/blob/6bec625ef49004f364457c26263df421526b60d6/sdk/lib/opentelemetry/sdk/internal.rb#L47 if increment.negative? OpenTelemetry.logger.warn("#{@name} received a negative value") else