-
Notifications
You must be signed in to change notification settings - Fork 5
feat: Open Telemetry target #30
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
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -61,6 +61,23 @@ Given gem provides built in target for Datadog StatsD client, that uses batch op | |
|
|
||
| You can provide all the tags, namespaces, and other configuration options as always to `Datadog::Statsd.new` method. | ||
|
|
||
| ### OpenTelemetry target | ||
|
|
||
| Given gem provides built in target for OpenTelemetry Metrics SDK, that uses batch operations to publish metrics. | ||
|
|
||
| **NOTE** Be sure to have `opentelemetry-metrics-sdk` gem installed. | ||
|
||
|
|
||
| ```ruby | ||
| config.add_target :open_telemetry, meter_provider: OpenTelemetry.meter_provider | ||
| ``` | ||
|
|
||
| This target supports the following options: | ||
| | Option | Description | Default | Required | | | ||
| |------------|-----------------------------------------------------------------|---------|----------|---| | ||
| | prefix | Metric name prefix. <br> ex) prefix: 'puma' => 'puma.workers.booted' | nil | No | | | ||
| | suffix | Metric name suffix. <br> ex) suffix: 'v1' => 'workers.booted.v1' | nil | No | | | ||
| | attributes | Attributes to be included with the metric | {} | No | | | ||
|
|
||
| ### All available options | ||
|
|
||
| For detailed documentation checkout [`Puma::Plugin::Telemetry::Config`](./lib/puma/plugin/telemetry/config.rb) class. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,11 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| begin | ||
| require 'datadog/statsd' | ||
| rescue LoadError | ||
| # Gracefully handle the case when Datadog::Statsd is not installed | ||
| end | ||
|
||
|
|
||
| module Puma | ||
| class Plugin | ||
| module Telemetry | ||
|
|
@@ -22,7 +28,13 @@ module Targets | |
| # DatadogStatsdTarget.new(client: client) | ||
| # | ||
| class DatadogStatsdTarget | ||
| def self.available? | ||
| !defined?(Datadog::Statsd).nil? | ||
| end | ||
|
|
||
| def initialize(client:) | ||
| raise ArgumentError, ':dogstatsd target can only be used when `datadog/statsd` gem is installed' unless self.class.available? | ||
|
|
||
| @client = client | ||
| end | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| begin | ||
| require 'opentelemetry-metrics-sdk' | ||
| rescue LoadError | ||
| # Gracefully handle the case when OpenTelemetry Metrics SDK is not installed | ||
| end | ||
|
|
||
| module Puma | ||
| class Plugin | ||
| module Telemetry | ||
| module Targets | ||
| # Target wrapping OpenTelemetry Metrics client. | ||
| # | ||
| # ## Example | ||
| # | ||
| # require 'opentelemetry-metrics-sdk' | ||
| # | ||
| # OpenTelemetryTarget.new(meter_provider: OpenTelemetry.meter_provider, prefix: 'puma') | ||
| # | ||
| class OpenTelemetryTarget | ||
| def self.available? | ||
| !defined?(OpenTelemetry::SDK::Metrics).nil? | ||
| end | ||
|
|
||
| def initialize(meter_provider:, prefix: nil, suffix: nil, attributes: {}) | ||
| raise ArgumentError, ':open_telemetry target can only be used when the `opentelemetry-metrics-sdk` and `opentelemetry-exporter-otlp-metrics` gems are installed' unless self.class.available? | ||
|
|
||
| @meter_provider = meter_provider | ||
| @meter = meter_provider.meter('puma.telemetry') | ||
| @prefix = prefix | ||
| @suffix = suffix | ||
| @attributes = attributes | ||
| @instruments = {} | ||
| end | ||
|
|
||
| # We are using `gauge` metric type, which means that only the last value will get exported | ||
| # since the OpenTelemetry exporter aggregates metrics before sending them. | ||
| # | ||
| # This means that we could publish metrics from here several times | ||
| # before they get flushed from the aggregation thread, and when they | ||
| # do, only the last values will get sent. | ||
| # | ||
| # That's why we are explicitly calling force_flush here, in order to persist | ||
| # all metrics, and not only the most recent ones. | ||
| # | ||
| def call(telemetry) | ||
| telemetry.each do |metric, value| | ||
| instrument(metric).record(value, attributes: @attributes) | ||
| end | ||
|
|
||
| @meter_provider.try(:force_flush) | ||
| end | ||
|
|
||
| def instrument(metric) | ||
| @instruments[metric] ||= @meter.create_gauge([@prefix, metric, @suffix].compact.join('.')) | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| require 'opentelemetry/sdk' | ||
| require 'opentelemetry-metrics-sdk' | ||
|
|
||
| # Custom console exporter to support flushing metrics. Console _pull_ exporter doesn't have a force_flush method like the typical exporters | ||
| class ConsoleMetricExporter < OpenTelemetry::SDK::Metrics::Export::ConsoleMetricPullExporter | ||
| def force_flush(timeout: nil) | ||
| pull | ||
| end | ||
| end | ||
|
|
||
| OpenTelemetry::SDK.configure | ||
|
|
||
| console_metric_exporter = ConsoleMetricExporter.new | ||
| OpenTelemetry.meter_provider.add_metric_reader(console_metric_exporter) | ||
|
|
||
| app { |_env| [200, {}, ['embedded app']] } | ||
| lowlevel_error_handler { |_err| [500, {}, ['error page']] } | ||
|
|
||
| threads 1, 1 | ||
|
|
||
| bind "unix://#{ENV.fetch('BIND_PATH', nil)}" | ||
|
|
||
| plugin 'telemetry' | ||
|
|
||
| Puma::Plugin::Telemetry.configure do |config| | ||
| config.add_target :open_telemetry, meter_provider: OpenTelemetry.meter_provider | ||
| config.frequency = 0.2 | ||
| config.enabled = true | ||
| config.initial_delay = 2 | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
groups don't really matter in gems Gemfile, as it's used exclusively in development, only gems from
.gemspecmatter for end usersThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point! Got those removed in b7aa0f0