Module: Splunk::Otel

Defined in:
lib/splunk/otel.rb,
lib/splunk/otel/common.rb,
lib/splunk/otel/logging.rb,
lib/splunk/otel/version.rb,
lib/splunk/otel/proprietary_exporters.rb,
lib/splunk/otel/instrumentation/action_pack.rb,
lib/splunk/otel/instrumentation/rack/middleware.rb,
lib/splunk/otel/instrumentation/action_pack/railtie.rb,
lib/splunk/otel/instrumentation/action_pack/version.rb,
lib/splunk/otel/instrumentation/action_pack/instrumentation.rb

Overview

main module for application startup configuration

Defined Under Namespace

Modules: Common, ExporterExtensions, Instrumentation, Logging, Rack Classes: Error

Constant Summary collapse

VERSION =
"0.2.0"

Class Method Summary collapse

Class Method Details

.configure(service_name: ENV.fetch("OTEL_SERVICE_NAME", "unnamed-ruby-service"), auto_instrument: false, trace_response_header_enabled: ENV.fetch("SPLUNK_TRACE_RESPONSE_HEADER_ENABLED", "true")) {|configurator| ... } ⇒ Object

Configures the OpenTelemetry SDK and instrumentation with Splunk defaults.

Example usage:

Without a block defaults are installed without any instrumentation

  Splunk::Otel.configure

Install instrumentation individually with optional config

  Splunk::Otel.configure do |c|
    c.use 'OpenTelemetry::Instrumentation::Faraday', tracer_middleware: SomeMiddleware
  end

Install all instrumentation with optional config

  Splunk::Otel.configure do |c|
    c.use_all 'OpenTelemetry::Instrumentation::Faraday' => { tracer_middleware: SomeMiddleware }
  end

Add a span processor

  Splunk::Otel.configure do |c|
    c.add_span_processor SpanProcessor.new(SomeExporter.new)
  end

Configure everything

  Splunk::Otel.configure do |c|
    c.logger = Logger.new(File::NULL)
    c.add_span_processor SpanProcessor.new(SomeExporter.new)
    c.use_all
  end

Yield Parameters:

  • configurator (Configurator)

    Yields a configurator to the provided block



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/splunk/otel.rb', line 55

def configure(service_name: ENV.fetch("OTEL_SERVICE_NAME", "unnamed-ruby-service"),
              auto_instrument: false,
              trace_response_header_enabled: ENV.fetch("SPLUNK_TRACE_RESPONSE_HEADER_ENABLED", "true"))
  @trace_response_header_enabled = to_boolean(trace_response_header_enabled)

  set_defaults

  # run SDK's setup function
  OpenTelemetry::SDK.configure do |configurator|
    class << configurator
      include Splunk::Otel::ExporterExtensions
    end

    configurator.service_name = service_name
    configurator.resource = OpenTelemetry::SDK::Resources::Resource.create(
      "splunk.distro.version" => Splunk::Otel::VERSION
    )

    configurator.use_all if auto_instrument
    yield configurator if block_given?
  end

  # set span limits to GDI defaults if not set by the user
  OpenTelemetry.tracer_provider.span_limits = gdi_span_limits

  verify_service_name
end

.default_env_vars(env) ⇒ Object

set environment varaible default’s if the user hasn’t set them



163
164
165
166
167
# File 'lib/splunk/otel.rb', line 163

def default_env_vars(env)
  env.each do |k, v|
    ENV[k] = v if ENV[k].nil?
  end
end

.gdi_span_limitsObject



147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/splunk/otel.rb', line 147

def gdi_span_limits
  infinite_defaults = { "OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT" => :attribute_count_limit,
                        "OTEL_SPAN_EVENT_COUNT_LIMIT" => :event_count_limit,
                        "OTEL_SPAN_LINK_COUNT_LIMIT" => :link_count_limit,
                        "OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT" => :event_attribute_count_limit,
                        "OTEL_LINK_ATTRIBUTE_COUNT_LIMIT" => :link_attribute_count_limit }

  defaults = {}
  infinite_defaults.each do |k, v|
    defaults[v] = Float::INFINITY if ENV[k].nil?
  end

  OpenTelemetry::SDK::Trace::SpanLimits.new(**defaults)
end

.service_name_warningObject



183
184
185
186
187
188
189
# File 'lib/splunk/otel.rb', line 183

def service_name_warning
  "    service.name attribute is not set, your service is unnamed and will be difficult to identify.\n    set your service name using the OTEL_SERVICE_NAME environment variable.\n    E.g. `OTEL_SERVICE_NAME=\"<YOUR_SERVICE_NAME_HERE>\"`\n  WARNING\nend\n"

.set_access_token_headerObject

add the access token header if the env variable is set



125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/splunk/otel.rb', line 125

def set_access_token_header
  splunk_access_token = ENV.fetch("SPLUNK_ACCESS_TOKEN", nil)
  return if splunk_access_token.nil?

  access_header = "x-sf-token=#{splunk_access_token}"
  headers = ENV.fetch("OTEL_EXPORTER_OTLP_HEADERS", nil)
  ENV["OTEL_EXPORTER_OTLP_HEADERS"] = if headers.to_s.empty?
                                        access_header
                                      else
                                        "#{headers},#{access_header}"
                                      end
end

.set_default_exporterObject



98
99
100
101
102
103
# File 'lib/splunk/otel.rb', line 98

def set_default_exporter
  set_endpoint
  default_env_vars({ "OTEL_EXPORTER_OTLP_TRACES_COMPRESSION" => "gzip",
                     "OTEL_EXPORTER_OTLP_COMPRESSION" => "gzip",
                     "OTEL_TRACES_EXPORTER" => "otlp" })
end

.set_default_propagatorsObject



138
139
140
# File 'lib/splunk/otel.rb', line 138

def set_default_propagators
  default_env_vars({ "OTEL_PROPAGATORS" => "tracecontext,baggage" })
end

.set_default_span_limitsObject



142
143
144
145
# File 'lib/splunk/otel.rb', line 142

def set_default_span_limits
  default_env_vars({ "OTEL_SPAN_LINK_COUNT_LIMIT" => "1000",
                     "OTEL_RUBY_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT" => "12000" })
end

.set_defaultsObject



83
84
85
86
87
88
# File 'lib/splunk/otel.rb', line 83

def set_defaults
  set_default_propagators
  set_access_token_header
  set_default_exporter
  set_default_span_limits
end

.set_endpointObject



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/splunk/otel.rb', line 105

def set_endpoint
  traces_endpoint = ENV.fetch("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", nil)
  endpoint = ENV.fetch("OTEL_EXPORTER_OTLP_ENDPOINT", nil)
  splunk_realm = ENV.fetch("SPLUNK_REALM", nil)

  # if user hasn't set traces endpoint or endpoint but has set the realm then
  # set the endpoints to be https://api.<SPLUNK_REALM>.signalfx.com
  # if either endpoint variable was set by the user then use those even if
  # they've also set SPLUNK_REALM
  return unless traces_endpoint.to_s.empty?
  return unless endpoint.to_s.empty?

  ENV["OTEL_EXPORTER_OTLP_TRACES_ENDPOINT"] = if splunk_realm.to_s.empty? || splunk_realm.to_s.eql?("none")
                                                "http://localhost:4318/v1/traces"
                                              else
                                                "https://ingest.#{splunk_realm}.signalfx.com/v2/trace/otlp"
                                              end
end

.to_boolean(val) ⇒ Object



169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/splunk/otel.rb', line 169

def to_boolean(val)
  # val could already be a boolean so just return the value if it is already
  # true or false
  case val
  when true then true
  when false then false
  else
    !%w[false
        no
        f
        0].include?(val.strip.downcase)
  end
end

.trace_response_header_enabledObject

Returns the value of attribute trace_response_header_enabled.



16
17
18
# File 'lib/splunk/otel.rb', line 16

def trace_response_header_enabled
  @trace_response_header_enabled
end

.verify_service_nameObject

verify ‘service.name` is set and print a warning if it is still the default



91
92
93
94
95
96
# File 'lib/splunk/otel.rb', line 91

def verify_service_name
  provider_resource = OpenTelemetry.tracer_provider.instance_variable_get(:@resource)
  resource_attributes = provider_resource.instance_variable_get(:@attributes)
  service_name = resource_attributes[OpenTelemetry::SemanticConventions::Resource::SERVICE_NAME]
  OpenTelemetry.logger.warn service_name_warning if service_name == "unknown_service"
end