Skip to content
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

Refactor EnvironmentLogger V2 #3004

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 25 additions & 110 deletions lib/datadog/core/diagnostics/environment_logger.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,22 @@ module EnvironmentLogger
class << self
# Outputs environment information to {Datadog.logger}.
# Executes only once for the lifetime of the program.
def log!(transport_responses)
def log!(**data)
return if (defined?(@executed) && @executed) || !log?

@executed = true

data = EnvironmentCollector.new.collect!(transport_responses)
data.reject! { |_, v| v.nil? } # Remove empty values from hash output
collector = EnvironmentCollector.new

log_environment!(data.to_json)
log_error!('Agent Error'.freeze, data[:agent_error]) if data[:agent_error]
collected_data = collector.collect!(**data)
collected_data.reject! { |_, v| v.nil? } # Remove empty values from hash
log_environment!(collected_data.to_json)

errors = collector.collect_errors!(**data)
errors.reject! { |_, message| message.nil? } # Remove empty values from list
errors.each do |type, message|
log_error!(type, message)
end
rescue => e
Datadog.logger.warn("Failed to collect environment information: #{e} Location: #{Array(e.backtrace).first}")
end
Expand All @@ -32,8 +38,8 @@ def log_environment!(line)
Datadog.logger.info("DATADOG CONFIGURATION - #{line}")
end

def log_error!(type, error)
Datadog.logger.warn("DATADOG DIAGNOSTIC - #{type}: #{error}")
def log_error!(type, message)
Datadog.logger.warn("DATADOG DIAGNOSTIC - #{type}: #{message}")
end

# Are we logging the environment data?
Expand Down Expand Up @@ -89,11 +95,6 @@ def env
Datadog.configuration.env
end

# @return [Boolean, nil]
def enabled
Datadog.configuration.tracing.enabled
end

# @return [String] configured application service name
def service
Datadog.configuration.service
Expand All @@ -104,68 +105,11 @@ def dd_version
Datadog.configuration.version
end

# @return [String, nil] target agent URL for trace flushing
def agent_url
# Retrieve the effect agent URL, regardless of how it was configured
transport = Tracing.send(:tracer).writer.transport

# return `nil` with IO transport
return unless transport.respond_to?(:client)

adapter = transport.client.api.adapter
adapter.url
end

# Error returned by Datadog agent during a tracer flush attempt
# @return [String] concatenated list of transport errors
def agent_error(transport_responses)
error_responses = transport_responses.reject(&:ok?)

return nil if error_responses.empty?

error_responses.map(&:inspect).join(','.freeze)
end

# @return [Boolean, nil] debug mode enabled in configuration
def debug
!!Datadog.configuration.diagnostics.debug
end

# @return [Boolean, nil] analytics enabled in configuration
def analytics_enabled
!!Datadog.configuration.tracing.analytics.enabled
end

# @return [Numeric, nil] tracer sample rate configured
def sample_rate
sampler = Datadog.configuration.tracing.sampler
return nil unless sampler

sampler.sample_rate(nil) rescue nil
end

# DEV: We currently only support SimpleRule instances.
# DEV: These are the most commonly used rules.
# DEV: We should expand support for other rules in the future,
# DEV: although it is tricky to serialize arbitrary rules.
#
# @return [Hash, nil] sample rules configured
def sampling_rules
sampler = Datadog.configuration.tracing.sampler
return nil unless sampler.is_a?(Tracing::Sampling::PrioritySampler) &&
sampler.priority_sampler.is_a?(Tracing::Sampling::RuleSampler)

sampler.priority_sampler.rules.map do |rule|
next unless rule.is_a?(Tracing::Sampling::SimpleRule)

{
name: rule.matcher.name,
service: rule.matcher.service,
sample_rate: rule.sampler.sample_rate(nil)
}
end.compact
end

# @return [Hash, nil] concatenated list of global tracer tags configured
def tags
tags = Datadog.configuration.tags
Expand Down Expand Up @@ -204,77 +148,42 @@ def vm
end
end

# @return [Boolean, nil] partial flushing enabled in configuration
def partial_flushing_enabled
!!Datadog.configuration.tracing.partial_flush.enabled
end

# @return [Boolean, nil] priority sampling enabled in configuration
def priority_sampling_enabled
!!Datadog.configuration.tracing.priority_sampling
end

# @return [Boolean, nil] health metrics enabled in configuration
def health_metrics_enabled
!!Datadog.configuration.diagnostics.health_metrics.enabled
end

def profiling_enabled
!!Datadog.configuration.profiling.enabled
end

# TODO: Populate when automatic log correlation is implemented
# def logs_correlation_enabled
# end

# @return [Hash] environment information available at call time
def collect!(transport_responses)
def collect!(**data)
{
date: date,
os_name: os_name,
version: version,
lang: lang,
lang_version: lang_version,
env: env,
enabled: enabled,
service: service,
dd_version: dd_version,
agent_url: agent_url,
agent_error: agent_error(transport_responses),
debug: debug,
analytics_enabled: analytics_enabled,
sample_rate: sample_rate,
sampling_rules: sampling_rules,
tags: tags,
runtime_metrics_enabled: runtime_metrics_enabled,
integrations_loaded: integrations_loaded,
vm: vm,
partial_flushing_enabled: partial_flushing_enabled,
priority_sampling_enabled: priority_sampling_enabled,
health_metrics_enabled: health_metrics_enabled,
profiling_enabled: profiling_enabled,
**instrumented_integrations_settings
}
end

private

def instrumented_integrations
Datadog.configuration.tracing.instrumented_integrations
def collect_errors!(**data)
[
# List of [error type, error message]
]
end

# Capture all active integration settings into "integrationName_settingName: value" entries.
def instrumented_integrations_settings
instrumented_integrations.flat_map do |name, integration|
integration.configuration.to_h.flat_map do |setting, value|
next [] if setting == :tracer # Skip internal Ruby objects

# Convert value to a string to avoid custom #to_json
# handlers possibly causing errors.
[[:"integration_#{name}_#{setting}", value.to_s]]
end
end.to_h
end
private

# Outputs "k1:v1,k2:v2,..."
def hash_serializer(h)
Expand All @@ -284,3 +193,9 @@ def hash_serializer(h)
end
end
end




# have base environment logger
# exposes log that takes arbitrary data
25 changes: 25 additions & 0 deletions lib/datadog/profiling/diagnostics/environment_logger.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
require 'date'
require 'json'
require 'rbconfig'
require_relative '../../core/diagnostics/environment_logger'

module Datadog
module Profiling
module Diagnostics
# Collects profiling environment information for diagnostic logging
module ProfilingEnvironmentCollector
def profiling_enabled
!!Datadog.configuration.profiling.enabled
end

def collect!(**data)
super.merge(
profiling_enabled: profiling_enabled
)
end
end

Core::Diagnostics::EnvironmentCollector.prepend(ProfilingEnvironmentCollector)
end
end
end
2 changes: 1 addition & 1 deletion lib/datadog/tracing/component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ def subscribe_to_writer_events!(writer, sampler, test_mode)
end

WRITER_RECORD_ENVIRONMENT_INFORMATION_CALLBACK = lambda do |_, responses|
Core::Diagnostics::EnvironmentLogger.log!(responses)
Core::Diagnostics::EnvironmentLogger.log!(tracing_responses: responses)
end

# Create new lambda for writer callback,
Expand Down
123 changes: 123 additions & 0 deletions lib/datadog/tracing/diagnostics/environment_logger.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
require 'date'
require 'json'
require 'rbconfig'
require_relative '../../core/diagnostics/environment_logger'

module Datadog
module Tracing
module Diagnostics
# Collects tracing environment information for diagnostic logging
module TracingEnvironmentCollector
# @return [Boolean, nil]
def enabled
Datadog.configuration.tracing.enabled
end

# @return [String, nil] target agent URL for trace flushing
def agent_url
# Retrieve the effect agent URL, regardless of how it was configured
transport = Tracing.send(:tracer).writer.transport

# return `nil` with IO transport
return unless transport.respond_to?(:client)

adapter = transport.client.api.adapter
adapter.url
end

# Error returned by Datadog agent during a tracer flush attempt
# @return [String] concatenated list of transport errors
def agent_error(tracing_responses)
return nil if tracing_responses.nil?

error_responses = tracing_responses.reject(&:ok?)

return nil if error_responses.empty?

error_responses.map(&:inspect).join(','.freeze)
end

def analytics_enabled
!!Datadog.configuration.tracing.analytics.enabled
end

# @return [Numeric, nil] tracer sample rate configured
def sample_rate
sampler = Datadog.configuration.tracing.sampler
return nil unless sampler

sampler.sample_rate(nil) rescue nil
end

# DEV: We currently only support SimpleRule instances.
# DEV: These are the most commonly used rules.
# DEV: We should expand support for other rules in the future,
# DEV: although it is tricky to serialize arbitrary rules.
#
# @return [Hash, nil] sample rules configured
def sampling_rules
sampler = Datadog.configuration.tracing.sampler
return nil unless sampler.is_a?(Tracing::Sampling::PrioritySampler) &&
sampler.priority_sampler.is_a?(Tracing::Sampling::RuleSampler)

sampler.priority_sampler.rules.map do |rule|
next unless rule.is_a?(Tracing::Sampling::SimpleRule)

{
name: rule.matcher.name,
service: rule.matcher.service,
sample_rate: rule.sampler.sample_rate(nil)
}
end.compact
end

def partial_flushing_enabled
!!Datadog.configuration.tracing.partial_flush.enabled
end

# @return [Boolean, nil] priority sampling enabled in configuration
def priority_sampling_enabled
!!Datadog.configuration.tracing.priority_sampling
end

def collect!(**data)
super.merge(
enabled: enabled,
agent_url: agent_url,
analytics_enabled: analytics_enabled,
sample_rate: sample_rate,
sampling_rules: sampling_rules,
partial_flushing_enabled: partial_flushing_enabled,
priority_sampling_enabled: priority_sampling_enabled,
**instrumented_integrations_settings
)
end

def collect_errors!(**data)
super << ['Agent Error', agent_error(data[:tracing_responses])]
end

private

def instrumented_integrations
Datadog.configuration.tracing.instrumented_integrations
end

# Capture all active integration settings into "integrationName_settingName: value" entries.
def instrumented_integrations_settings
instrumented_integrations.flat_map do |name, integration|
integration.configuration.to_h.flat_map do |setting, value|
next [] if setting == :tracer # Skip internal Ruby objects

# Convert value to a string to avoid custom #to_json
# handlers possibly causing errors.
[[:"integration_#{name}_#{setting}", value.to_s]]
end
end.to_h
end
end

Core::Diagnostics::EnvironmentCollector.prepend(TracingEnvironmentCollector)
end
end
end
Loading
Loading