Class: RailsSemanticLogging::Formatters::Datadog

Inherits:
SemanticLogger::Formatters::Raw
  • Object
show all
Defined in:
lib/rails_semantic_logging/formatters/datadog.rb

Overview

Datadog-optimized JSON formatter that maps log fields to Datadog Standard Attributes (docs.datadoghq.com/standard-attributes/).

Key mappings:

name              -> logger.name
level             -> status
duration          -> duration (nanoseconds) + duration_human (Rails format)
exception         -> error: { kind, message, stack }
payload           -> http: { status_code, method, url, ... } (controller requests)
named_tags.dd     -> dd (top-level, for trace linking)
named_tags.user_* -> usr.{id, email, name, role}

Constant Summary collapse

NANOSECONDS_PER_MILLISECOND =
1_000_000
HTTP_PAYLOAD_MAP =

Mapping of Rails payload keys to Datadog http standard attribute names

{
  status: :status_code,
  method: :method,
  host: :host,
  user_agent: :useragent,
  referer: :referer
}.freeze
USER_NAMED_TAGS_MAP =

Mapping of user-related named_tags to usr.* standard attributes

{
  user_id: :id,
  user_email: :email,
  user_name: :name,
  user_role: :role
}.freeze

Instance Method Summary collapse

Constructor Details

#initialize(time_format: :iso_8601, time_key: :timestamp, **args) ⇒ Datadog

rubocop:disable Naming/VariableNumber



36
37
38
# File 'lib/rails_semantic_logging/formatters/datadog.rb', line 36

def initialize(time_format: :iso_8601, time_key: :timestamp, **args) # rubocop:disable Naming/VariableNumber
  super(time_format:, time_key:, log_application: false, log_host: true, log_environment: false, **args)
end

Instance Method Details

#call(log, logger) ⇒ Object



40
41
42
43
44
45
46
47
48
# File 'lib/rails_semantic_logging/formatters/datadog.rb', line 40

def call(log, logger)
  super
  remap_named_tags
  remap_http_payload
  parse_url_details
  apache_message
  deep_compact_blank!(hash)
  hash.to_json
end

#durationObject



62
63
64
65
66
67
68
69
70
71
# File 'lib/rails_semantic_logging/formatters/datadog.rb', line 62

def duration
  # Propagate duration from payload if not set on log
  log.duration = log.payload[:duration] if log.duration.nil? && log.payload&.dig(:duration)
  return unless log.duration

  # Datadog standard: duration in nanoseconds
  hash[:duration] = (log.duration * NANOSECONDS_PER_MILLISECOND).to_i
  # Human-readable duration for readability (Rails format)
  hash[:duration_human] = "#{log.duration.round(2)}ms"
end

#exceptionObject



73
74
75
76
77
78
79
80
81
# File 'lib/rails_semantic_logging/formatters/datadog.rb', line 73

def exception
  return unless log.exception

  hash[:error] = {
    kind: log.exception.class.name,
    message: log.exception.message,
    stack: log.exception.backtrace&.join("\n")
  }
end

#levelObject



58
59
60
# File 'lib/rails_semantic_logging/formatters/datadog.rb', line 58

def level
  hash[:status] = log.level
end

#nameObject



54
55
56
# File 'lib/rails_semantic_logging/formatters/datadog.rb', line 54

def name
  hash[:'logger.name'] = log.name if log.name
end

#thread_nameObject



50
51
52
# File 'lib/rails_semantic_logging/formatters/datadog.rb', line 50

def thread_name
  # Exclude thread_name from output
end