-
Notifications
You must be signed in to change notification settings - Fork 172
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: 🦠 Metrics prototype for Rack instrumentation #1129
base: main
Are you sure you want to change the base?
Changes from 9 commits
2e6b9ab
8338fc4
b031ceb
ed4a354
a38aa87
951e0af
92d9ef6
8b4554f
ff2fbdb
3eef72a
258b587
ba9d06d
7f8ce16
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 |
---|---|---|
@@ -0,0 +1,31 @@ | ||
# frozen_string_literal: true | ||
|
||
# Copyright The OpenTelemetry Authors | ||
# | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
module OpenTelemetry | ||
module Instrumentation | ||
# MetricsPatch is a module that provides functionality to create a meter | ||
# and record metrics if both the opentelemetry-metrics-api is present | ||
# and the instrumentation to emit metrics has enabled metrics by setting | ||
# :send_metrics to true | ||
module MetricsPatch | ||
def create_meter | ||
@meter = OpenTelemetry::Metrics::Meter.new | ||
end | ||
|
||
def install_meter | ||
@meter = OpenTelemetry.meter_provider.meter(name, version: version) if metrics_enabled? | ||
end | ||
|
||
def metrics_enabled? | ||
return @metrics_enabled if defined?(@metrics_enabled) | ||
|
||
@metrics_enabled ||= defined?(OpenTelemetry::Metrics) && @config[:send_metrics] | ||
end | ||
end | ||
end | ||
end | ||
|
||
OpenTelemetry::Instrumentation::Base.prepend(OpenTelemetry::Instrumentation::MetricsPatch) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
# frozen_string_literal: true | ||
|
||
# Copyright The OpenTelemetry Authors | ||
# | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
module OpenTelemetry | ||
module Instrumentation | ||
module Rack | ||
module Middlewares | ||
# MetricsPatch is a module that provides functionality to record metrics | ||
# if both the opentelemetry-metrics-api is present and the rack | ||
# instrumentation is configured to emit metrics by setting | ||
# :send_metrics to true | ||
module MetricsPatch | ||
def meter | ||
OpenTelemetry::Instrumentation::Rack::Instrumentation.instance.meter | ||
end | ||
|
||
def http_server_request_duration_histogram | ||
# TODO: Add Advice to set small explicit histogram bucket boundaries | ||
# TODO: Does this need to be memoized? | ||
@http_server_request_duration_histogram ||= meter.create_histogram('http.server.request.duration', unit: 's', description: 'Duration of HTTP server requests.') | ||
end | ||
|
||
# TODO: Update this to define attributes based on SEMCONV_STABILITY_OPT_IN once available | ||
def record_http_server_request_duration_metric(span) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method is very long because it's leaving all the options open for attribute names. The attribute names we currently use are not the ones marked as stable. I think we could use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the metrics attribute only cares about HTTP-related attributes, can it emit metrics with the attributes that are based on the existing # Assume only one of url.scheme and http.scheme will appear
span.attributes.select { |key, value| key.match(/http\..*/) || key.match(/url\..*/) } There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like where you're headed with this! There's a lot of duplication in the current code. However, there are some I wonder if we could use a key-based allowlist to select things instead? Maybe add a constant with all the HTTP metrics keys, and we search for those? Things might need to shift again when the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the updates. Yes, I like key-based allowlist There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ooh, nice idea! |
||
# find span duration | ||
# end - start / a billion to convert nanoseconds to seconds | ||
duration = (span.end_timestamp - span.start_timestamp) / Float(10**9) | ||
# Create attributes | ||
# | ||
attrs = {} | ||
# pattern below goes: | ||
# # stable convention | ||
# current span convention | ||
|
||
# attrs['http.request.method'] | ||
attrs['http.method'] = span.attributes['http.method'] | ||
|
||
# attrs['url.scheme'] | ||
attrs['http.scheme'] = span.attributes['http.scheme'] | ||
|
||
# same in stable semconv | ||
attrs['http.route'] = span.attributes['http.route'] | ||
|
||
# attrs['http.response.status.code'] | ||
attrs['http.status_code'] = span.attributes['http.status_code'] | ||
|
||
# attrs['server.address'] ??? | ||
# attrs['server.port'] ??? | ||
# span includes host and port | ||
attrs['http.host'] = span.attributes['http.host'] | ||
|
||
# attrs not currently in span payload | ||
# attrs['network.protocol.version'] | ||
# attrs['network.protocol.name'] | ||
attrs['error.type'] = span.status.description if span.status.code == OpenTelemetry::Trace::Status::ERROR | ||
|
||
http_server_request_duration_histogram.record(duration, attributes: attrs) | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end | ||
|
||
OpenTelemetry::Instrumentation::Rack::Middlewares::EventHandler.prepend(OpenTelemetry::Instrumentation::Rack::Middlewares::MetricsPatch) |
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.
We talked about requiring "two keys to be turned", to enable metrics on instrumentation:
Since we do some of our setup in base, base checks to see if the library is installed. If it is installed, we require the patch. The config isn't available at this time, so we don't use it in this context. The instrumentation for a particular library, however, checks for both the installation of the API and that the config option is true.
Is this sufficient to protect non-metrics users from any potential problems? Are there other approaches we'd like to explore?
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.
I think these two safeguards are enough for users who don’t want metrics in instrumentation or wish to create custom metrics themselves (e.g., include the metrics SDK but not use it in instrumentation).
Is the patch (e.g., prepend) only because metrics are still experimental features? I was thinking of including them in the base but not installing if
OpenTelemetry::Metrics
is missing (similar to line 25 inmetrics_patch.rb
).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.
Thank you! I think that could work! Yes, the patch is just because the metrics are experimental features. I thought this approach might feel "safer" (since all our instrumentation runs through
base
), but I don't know if truly provides a benefit.It also tries to follow the configurator patches for metrics in the SDK:
I'm happy to cut out the patch and just put everything in base. I sorta tried that with the other prototype.
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.
Whoops, forgot to follow up here. I've changed the structure to use conditions instead of the patches.