Skip to content

Instantly share code, notes, and snippets.

@stevenharman
Last active November 23, 2021 21:05
Show Gist options
  • Save stevenharman/c375529f1c3ddbdc7886a66a2ea275e5 to your computer and use it in GitHub Desktop.
Save stevenharman/c375529f1c3ddbdc7886a66a2ea275e5 to your computer and use it in GitHub Desktop.
A very thin wrapper for consistently configuring Ruby components for exporting OpenTelemetry tracing to Honeycomb
Telemetry.configure do |c|
c.component = ENV.fetch("COMPONENT_NAME")
c.environment = ENV.fetch("APP_ENV")
c.deployment_environment = ENV.fetch("DEPLOYMENT_ENVIRONMENT")
c.honeycomb_api_key = ENV.fetch("HONEYCOMB_API_KEY", nil)
c.honeycomb_dataset = ENV.fetch("HONEYCOMB_DATASET", nil)
# This is delegating through to `OpenTelemetry::SDK::Configurator#use_all`
c.use_all("OpenTelemetry::Instrumentation::Sidekiq" => {span_naming: :job_class})
end
# frozen_string_literal: true
module Telemetry
ENV_SERVICE_VERSION = "HEROKU_RELEASE_VERSION" # Depends on Feature: https://devcenter.heroku.com/articles/dyno-metadata
ENV_HONEYCOMB_API_KEY = "HONEYCOMB_API_KEY"
ENV_HONEYCOMB_DATASET = "HONEYCOMB_DATASET"
VERSION = "0.9.0-beta"
def self.tracer(name = "telemetry-ruby", version = Telemetry::VERSION)
OpenTelemetry.tracer_provider.tracer(name, version)
end
def self.configure
c = Configuration.new
yield(c) if block_given?
c.configure
end
class Configuration
extend Forwardable
# Delegate the public API of SDK's Configurator to allow an escape hatch for more advanced configuration needs.
delegate [:logger, :logger=, :error_handler, :resource=, :service_name=, :service_version=, :use, :use_all, :add_span_processor] => :sdk_config
attr_accessor :component, :version
attr_accessor :honeycomb_api_key, :honeycomb_dataset
attr_reader :deployment_environment, :environment
def initialize(sdk_config: OpenTelemetry::SDK::Configurator.new)
@sdk_config = sdk_config
end
def configure(env: ENV)
self.honeycomb_api_key = env.fetch(ENV_HONEYCOMB_API_KEY, nil)
self.honeycomb_dataset = env.fetch(ENV_HONEYCOMB_DATASET, nil)
self.environment = "development"
self.deployment_environment = environment
sdk_config.service_version = env.fetch(ENV_SERVICE_VERSION, "v0-dev")
yield(self) if block_given?
sdk_config.service_name = "#{component} - #{deployment_environment}"
sdk_config.resource = OpenTelemetry::SDK::Resources::Resource.create(
"service.component" => component,
OpenTelemetry::SemanticConventions::Resource::DEPLOYMENT_ENVIRONMENT => deployment_environment
)
enable_honeycomb if honeycomb_api_key.present? && honeycomb_dataset.present?
enable_development_mode if development?
enable_test_mode if test?
sdk_config.configure
rescue
# This error handling is roughly equivalent to what the OpenTelementry::SDK itself does.
# see: https://github.com/open-telemetry/opentelemetry-ruby/blob/9428b4e7df915f6bf062445fa6de7fb921cc7a44/sdk/lib/opentelemetry/sdk.rb#L67-L72
begin
raise OpenTelemetry::SDK::ConfigurationError
rescue OpenTelemetry::SDK::ConfigurationError => e
OpenTelemetry.handle_error(exception: e, message: "unexpected configuration error due to #{e.cause}")
end
end
def deployment_environment=(value)
@deployment_environment = String(value)
end
def environment=(value)
@environment = String(value)
end
private
attr_reader :sdk_config
def enable_development_mode
console_exporter = OpenTelemetry::SDK::Trace::Export::ConsoleSpanExporter.new
add_span_processor(OpenTelemetry::SDK::Trace::Export::SimpleSpanProcessor.new(console_exporter))
end
def enable_honeycomb
oltp_exporter = OpenTelemetry::Exporter::OTLP::Exporter.new(
endpoint: "https://api.honeycomb.io:443/v1/traces",
headers: {
"x-honeycomb-team" => honeycomb_api_key,
"x-honeycomb-dataset" => honeycomb_dataset
},
compression: "gzip"
)
batch_processor = OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor.new(oltp_exporter)
sdk_config.add_span_processor(batch_processor)
end
def enable_test_mode
in_memory_exporter = OpenTelemetry::SDK::Trace::Export::InMemorySpanExporter.new(recording: false)
sdk_config.add_span_processor(OpenTelemetry::SDK::Trace::Export::SimpleSpanProcessor.new(in_memory_exporter))
# Quiet the Info/Warn log lines generated by #use_all
# There must be a better way to do this so we don't have to silence warnings.
sdk_config.logger.level = Logger::ERROR
end
def development?
environment == "development"
end
def test?
environment == "test"
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment