Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add synchronous gauge #1718

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def initialize(kind, name, unit, desc, callable)

def upgrade_with(meter)
@delegate = case @kind
when :counter, :histogram, :up_down_counter
when :counter, :histogram, :up_down_counter, :gauge
meter.send("create_#{@kind}", @name, unit: @unit, description: @desc)
when :observable_counter, :observable_gauge, :observable_up_down_counter
meter.send("create_#{@kind}", @name, unit: @unit, description: @desc, callback: @callback)
Expand Down
1 change: 1 addition & 0 deletions metrics_api/lib/opentelemetry/internal/proxy_meter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ def create_instrument(kind, name, unit, description, callback)
case kind
when :counter then @delegate.create_counter(name, unit: unit, description: description)
when :histogram then @delegate.create_histogram(name, unit: unit, description: description)
when :gauge then @delegate.create_gauge(name, unit: unit, description: description)
when :up_down_counter then @delegate.create_up_down_counter(name, unit: unit, description: description)
when :observable_counter then @delegate.create_observable_counter(name, unit: unit, description: description, callback: callback)
when :observable_gauge then @delegate.create_observable_gauge(name, unit: unit, description: description, callback: callback)
Expand Down
1 change: 1 addition & 0 deletions metrics_api/lib/opentelemetry/metrics/instrument.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

require 'opentelemetry/metrics/instrument/counter'
require 'opentelemetry/metrics/instrument/histogram'
require 'opentelemetry/metrics/instrument/gauge'
require 'opentelemetry/metrics/instrument/observable_counter'
require 'opentelemetry/metrics/instrument/observable_gauge'
require 'opentelemetry/metrics/instrument/observable_up_down_counter'
Expand Down
23 changes: 23 additions & 0 deletions metrics_api/lib/opentelemetry/metrics/instrument/gauge.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# frozen_string_literal: true

# Copyright The OpenTelemetry Authors
#
# SPDX-License-Identifier: Apache-2.0

module OpenTelemetry
module Metrics
module Instrument
# No-op implementation of Gauge.
class Gauge
# Record the current value for the Gauge
#
# @param [Numeric] amount The current absolute value.
# @param [Hash{String => String, Numeric, Boolean, Array<String, Numeric, Boolean>}] 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: {}); end
end
end
end
end
23 changes: 22 additions & 1 deletion metrics_api/lib/opentelemetry/metrics/meter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@ class Meter
COUNTER = Instrument::Counter.new
OBSERVABLE_COUNTER = Instrument::ObservableCounter.new
HISTOGRAM = Instrument::Histogram.new
GAUGE = Instrument::Gauge.new
OBSERVABLE_GAUGE = Instrument::ObservableGauge.new
UP_DOWN_COUNTER = Instrument::UpDownCounter.new
OBSERVABLE_UP_DOWN_COUNTER = Instrument::ObservableUpDownCounter.new

private_constant(:COUNTER, :OBSERVABLE_COUNTER, :HISTOGRAM, :OBSERVABLE_GAUGE, :UP_DOWN_COUNTER, :OBSERVABLE_UP_DOWN_COUNTER)
NAME_REGEX = /\A[a-zA-Z][-.\w]{0,62}\z/

private_constant(:COUNTER, :OBSERVABLE_COUNTER, :HISTOGRAM, :GAUGE, :OBSERVABLE_GAUGE, :UP_DOWN_COUNTER, :OBSERVABLE_UP_DOWN_COUNTER)

DuplicateInstrumentError = Class.new(OpenTelemetry::Error)
InstrumentNameError = Class.new(OpenTelemetry::Error)
Expand All @@ -35,6 +38,24 @@ def create_histogram(name, unit: nil, description: nil)
create_instrument(:histogram, name, unit, description, nil) { HISTOGRAM }
end

# Gauge is an synchronous Instrument which reports non-additive value(s)
#
# With this api call:
#
# meter.create_gauge("cpu.frequency",
# description: "the real-time CPU clock speed",
# unit: "ms")
#
#
# @param name [String] the name of the gauge.
# @param unit [optional String] an optional string provided by user.
# @param description [optional String] an optional free-form text provided by user.
#
# @return [nil] after creation of gauge, it will be stored in instrument_registry
def create_gauge(name, unit: nil, description: nil)
create_instrument(:gauge, name, unit, description, nil) { GAUGE }
end
kaylareopelle marked this conversation as resolved.
Show resolved Hide resolved

def create_up_down_counter(name, unit: nil, description: nil)
create_instrument(:up_down_counter, name, unit, description, nil) { UP_DOWN_COUNTER }
end
Expand Down
1 change: 1 addition & 0 deletions metrics_sdk/lib/opentelemetry/sdk/metrics/instrument.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ module Instrument
require 'opentelemetry/sdk/metrics/instrument/observable_gauge'
require 'opentelemetry/sdk/metrics/instrument/observable_up_down_counter'
require 'opentelemetry/sdk/metrics/instrument/up_down_counter'
require 'opentelemetry/sdk/metrics/instrument/gauge'
48 changes: 48 additions & 0 deletions metrics_sdk/lib/opentelemetry/sdk/metrics/instrument/gauge.rb
Original file line number Diff line number Diff line change
@@ -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
# {Gauge} is the SDK implementation of {OpenTelemetry::Metrics::Gauge}.
class Gauge < OpenTelemetry::SDK::Metrics::Instrument::SynchronousInstrument
# Returns the instrument kind as a Symbol
#
# @return [Symbol]
def instrument_kind
:gauge
end

# Record the absolute value of the Gauge.
#
# @param [numeric] value The current absolute value.
# @param [Hash{String => String, Numeric, Boolean, Array<String, Numeric, Boolean>}] 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(value, 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
Comment on lines +28 to +30
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we turn this TODO into an issue, or reference it on an existing issue?

update(value, attributes)
nil
rescue StandardError => e
OpenTelemetry.handle_error(exception: e)
nil
end

private

# TODO: replace the default aggregation to LastValue
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# TODO: replace the default aggregation to LastValue

def default_aggregation
OpenTelemetry::SDK::Metrics::Aggregation::LastValue.new
end
end
end
end
end
end
1 change: 1 addition & 0 deletions metrics_sdk/lib/opentelemetry/sdk/metrics/meter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ def create_instrument(kind, name, unit, description, callback)
case kind
when :counter then OpenTelemetry::SDK::Metrics::Instrument::Counter.new(name, unit, description, @instrumentation_scope, @meter_provider)
when :observable_counter then OpenTelemetry::SDK::Metrics::Instrument::ObservableCounter.new(name, unit, description, callback, @instrumentation_scope, @meter_provider)
when :gauge then OpenTelemetry::SDK::Metrics::Instrument::Gauge.new(name, unit, description, @instrumentation_scope, @meter_provider)
when :histogram then OpenTelemetry::SDK::Metrics::Instrument::Histogram.new(name, unit, description, @instrumentation_scope, @meter_provider)
when :observable_gauge then OpenTelemetry::SDK::Metrics::Instrument::ObservableGauge.new(name, unit, description, callback, @instrumentation_scope, @meter_provider)
when :up_down_counter then OpenTelemetry::SDK::Metrics::Instrument::UpDownCounter.new(name, unit, description, @instrumentation_scope, @meter_provider)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# frozen_string_literal: true

# Copyright The OpenTelemetry Authors
#
# SPDX-License-Identifier: Apache-2.0

require 'test_helper'

describe OpenTelemetry::SDK::Metrics::Instrument::Gauge do
let(:metric_exporter) { OpenTelemetry::SDK::Metrics::Export::InMemoryMetricPullExporter.new }
let(:meter) { OpenTelemetry.meter_provider.meter('test') }
let(:gauge) { meter.create_gauge('gauge', unit: 'smidgen', description: 'a small amount of something') }

before do
reset_metrics_sdk
OpenTelemetry::SDK.configure
OpenTelemetry.meter_provider.add_metric_reader(metric_exporter)
end

it 'gauge should count -2' do
gauge.record(-2, attributes: { 'foo' => 'bar' })
metric_exporter.pull
last_snapshot = metric_exporter.metric_snapshots

_(last_snapshot[0].name).must_equal('gauge')
_(last_snapshot[0].unit).must_equal('smidgen')
_(last_snapshot[0].description).must_equal('a small amount of something')
_(last_snapshot[0].instrumentation_scope.name).must_equal('test')
_(last_snapshot[0].data_points[0].attributes).must_equal('foo' => 'bar')
_(last_snapshot[0].data_points[0].value).must_equal(-2)
_(last_snapshot[0].aggregation_temporality).must_equal(:delta)
end

it 'gauge should count 1 for last recording' do
gauge.record(-2, attributes: { 'foo' => 'bar' })
gauge.record(1, attributes: { 'foo' => 'bar' })
metric_exporter.pull
last_snapshot = metric_exporter.metric_snapshots

_(last_snapshot.size).must_equal(1)
_(last_snapshot[0].data_points.size).must_equal(1)
_(last_snapshot[0].data_points[0].value).must_equal(1)
end

it 'gauge should count 1 for last recording' do
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this needs a different title for the assertion? It seems like the goal here is to make sure two different gauges can be evaluated individually?

gauge.record(-2, attributes: { 'foo' => 'bar' })
gauge.record(1, attributes: { 'foo' => 'bar' })
gauge2 = meter.create_gauge('gauge2', unit: 'smidgen', description: 'a small amount of something')
gauge2.record(10, attributes: {})

metric_exporter.pull
last_snapshot = metric_exporter.metric_snapshots

_(last_snapshot.size).must_equal(2)
_(last_snapshot[0].data_points[0].value).must_equal(1)
_(last_snapshot[1].data_points[0].value).must_equal(10)
end
end
Loading