Module: LaunchDarklyObservability

Defined in:
lib/launchdarkly_observability.rb,
lib/launchdarkly_observability/hook.rb,
lib/launchdarkly_observability/rails.rb,
lib/launchdarkly_observability/plugin.rb,
lib/launchdarkly_observability/version.rb,
lib/launchdarkly_observability/middleware.rb,
lib/launchdarkly_observability/source_context.rb,
lib/launchdarkly_observability/otel_log_bridge.rb,
lib/launchdarkly_observability/opentelemetry_config.rb

Defined Under Namespace

Modules: ControllerHelpers, SourceContext, ViewHelpers Classes: Hook, Middleware, OpenTelemetryConfig, OtelLogBridge, Plugin, Railtie

Constant Summary collapse

DEFAULT_ENDPOINT =

Default OTLP endpoint for LaunchDarkly Observability

'https://otel.observability.app.launchdarkly.com:4318'
PROJECT_ID_ATTRIBUTE =

Resource attribute keys

'launchdarkly.project_id'
SDK_NAME_ATTRIBUTE =
'telemetry.sdk.name'
SDK_VERSION_ATTRIBUTE =
'telemetry.sdk.version'
SDK_LANGUAGE_ATTRIBUTE =
'telemetry.sdk.language'
DISTRO_NAME_ATTRIBUTE =
'telemetry.distro.name'
DISTRO_VERSION_ATTRIBUTE =
'telemetry.distro.version'
FEATURE_FLAG_KEY =

OpenTelemetry semantic convention attribute keys for feature flags See: opentelemetry.io/docs/specs/semconv/feature-flags/feature-flags-events/

'feature_flag.key'
FEATURE_FLAG_PROVIDER_NAME =
'feature_flag.provider.name'
FEATURE_FLAG_CONTEXT_ID =
'feature_flag.context.id'
FEATURE_FLAG_SET_ID =
'feature_flag.set.id'
FEATURE_FLAG_RESULT_VALUE =
'feature_flag.result.value'
FEATURE_FLAG_RESULT_VARIANT =
'feature_flag.result.variant'
FEATURE_FLAG_RESULT_VARIATION_INDEX =
'feature_flag.result.variationIndex'
FEATURE_FLAG_RESULT_REASON_KIND =
'feature_flag.result.reason.kind'
FEATURE_FLAG_RESULT_REASON_IN_EXPERIMENT =
'feature_flag.result.reason.inExperiment'
FEATURE_FLAG_RESULT_REASON_ERROR_KIND =
'feature_flag.result.reason.errorKind'
FEATURE_FLAG_RESULT_REASON_RULE_ID =
'feature_flag.result.reason.ruleId'
FEATURE_FLAG_RESULT_REASON_RULE_INDEX =
'feature_flag.result.reason.ruleIndex'
ERROR_TYPE =
'error.type'
ERROR_MESSAGE =
'error.message'
VERSION =

x-release-please-version

'0.2.1'

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.instancePlugin? (readonly)

Returns The current plugin instance.

Returns:

  • (Plugin, nil)

    The current plugin instance



49
50
51
# File 'lib/launchdarkly_observability.rb', line 49

def instance
  @instance
end

Class Method Details

.current_trace_idString?

Get the current trace ID

Examples:

Get trace ID for logging

trace_id = LaunchDarklyObservability.current_trace_id
logger.info "Processing request: #{trace_id}"

Returns:

  • (String, nil)

    The current trace ID in hex format



153
154
155
156
157
158
159
160
# File 'lib/launchdarkly_observability.rb', line 153

def current_trace_id
  return nil unless defined?(OpenTelemetry)

  span = OpenTelemetry::Trace.current_span
  return nil unless span&.context&.valid?

  span.context.hex_trace_id
end

.flushObject

Flush all pending telemetry data



163
164
165
# File 'lib/launchdarkly_observability.rb', line 163

def flush
  @instance&.flush
end

.in_span(name, attributes: {}) {|span| ... } ⇒ Object

Create a custom span for manual instrumentation

This method matches the OpenTelemetry API naming convention for consistency.

Examples:

Create a custom span

LaunchDarklyObservability.in_span('database-query') do |span|
  span.set_attribute('db.table', 'users')
  perform_query
end

Parameters:

  • name (String)

    The span name

  • attributes (Hash) (defaults to: {})

    Optional span attributes

Yields:

  • (span)

    Block to execute within the span context

Returns:

  • The result of the block



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/launchdarkly_observability.rb', line 87

def in_span(name, attributes: {})
  unless defined?(OpenTelemetry) && OpenTelemetry.tracer_provider
    return yield if block_given?
    return
  end

  tracer = OpenTelemetry.tracer_provider.tracer(
    'launchdarkly-observability',
    LaunchDarklyObservability::VERSION
  )

  tracer.in_span(name, attributes: attributes) do |span|
    yield(span) if block_given?
  end
end

.init(project_id: nil, sdk_key: nil, **options) ⇒ Plugin

Initialize the observability plugin

Parameters:

  • project_id (String, nil) (defaults to: nil)

    LaunchDarkly project ID (optional - SDK key will be extracted from client if not provided)

  • sdk_key (String, nil) (defaults to: nil)

    LaunchDarkly SDK key (optional - will be extracted from client if not provided)

  • options (Hash)

    Additional configuration options

Options Hash (**options):

  • :otlp_endpoint (String)

    Custom OTLP endpoint URL

  • :environment (String)

    Deployment environment (optional - inferred from SDK key by default)

  • :service_name (String)

    Service name for traces

  • :service_version (String)

    Service version

  • :instrumentations (Hash)

    Configuration for auto-instrumentations

Returns:

  • (Plugin)

    The initialized plugin



62
63
64
# File 'lib/launchdarkly_observability.rb', line 62

def init(project_id: nil, sdk_key: nil, **options)
  @instance = Plugin.new(project_id: project_id, sdk_key: sdk_key, **options)
end

.initialized?Boolean

Check if the plugin has been initialized

Returns:

  • (Boolean)

    true if initialized



69
70
71
# File 'lib/launchdarkly_observability.rb', line 69

def initialized?
  !@instance.nil?
end

.logger(output = $stdout) ⇒ OtelLogBridge, Logger

Create a Logger that writes to both a local IO and the OTel Logs pipeline.

Use this in non-Rails applications (Sinatra, Grape, plain Ruby) to get log export with trace correlation out of the box. Must be called after the Plugin has been registered (i.e. after LDClient.new).

Examples:

Sinatra

$logger = LaunchDarklyObservability.logger
$logger.info 'This goes to stdout AND is exported as an OTLP log record'

Parameters:

  • output (IO) (defaults to: $stdout)

    Local IO destination (default: $stdout)

Returns:

  • (OtelLogBridge, Logger)

    An OTel-bridged logger, or a plain Logger if the OTel logger provider is not yet available.



138
139
140
141
142
143
144
# File 'lib/launchdarkly_observability.rb', line 138

def logger(output = $stdout)
  if otel_logger_provider_available?
    OtelLogBridge.new(OpenTelemetry.logger_provider, io: output)
  else
    ::Logger.new(output)
  end
end

.record_exception(exception, attributes: {}) ⇒ Object

Record an exception in the current span

Examples:

Record an exception

begin
  risky_operation
rescue => e
  LaunchDarklyObservability.record_exception(e, foo: 'bar')
  raise
end

Parameters:

  • exception (Exception)

    The exception to record

  • attributes (Hash) (defaults to: {})

    Additional attributes



115
116
117
118
119
120
121
122
123
# File 'lib/launchdarkly_observability.rb', line 115

def record_exception(exception, attributes: {})
  return unless defined?(OpenTelemetry)

  span = OpenTelemetry::Trace.current_span
  return unless span

  span.record_exception(exception, attributes: SourceContext.exception_attributes(exception).merge(attributes))
  span.status = OpenTelemetry::Trace::Status.error(exception.message)
end

.shutdownObject

Shutdown the plugin and flush remaining data



168
169
170
171
# File 'lib/launchdarkly_observability.rb', line 168

def shutdown
  @instance&.shutdown
  @instance = nil
end