Class: DatadogLogForwarder

Inherits:
Object
  • Object
show all
Defined in:
lib/tpt_serverless/datadog_log_forwarder.rb

Class Method Summary collapse

Class Method Details

.handler(event:, context:) ⇒ Object

This handler receives CloudWatch log events, parses the events and forwards the extracted logs to Datadog.

It performs a few helpful cleanup/prep functions as well.



47
48
49
50
51
52
53
54
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/tpt_serverless/datadog_log_forwarder.rb', line 47

def handler(event:, context:)
  dd_api_key = ENV['DD_API_KEY']

  if dd_api_key.nil? || dd_api_key.strip.empty?
    puts 'ERROR: DD_API_KEY is not set. Skipping log forwarding.'
    return
  end

  raw_data = event.fetch('awslogs').fetch('data')
  unzipped_data = Zlib.gunzip(Base64.decode64(raw_data))
  data = JSON.parse(unzipped_data)

  message_type = data.fetch('messageType')
  log_group = data.fetch('logGroup')
  log_stream = data.fetch('logStream')
  log_events = data.fetch('logEvents')

  if message_type == 'CONTROL_MESSAGE'
    puts 'skipping control message'
    return
  end

  normalized_log_events = normalize_messages(log_events)

  filtered_log_events =
    if should_filter_lifecycle_logs?
      normalized_log_events.reject { |e| lambda_lifecycle_line?(e[:message]) }
    else
      normalized_log_events
    end

  puts "message_count=#{log_events.length} forwarded_count=#{filtered_log_events.length}"

  region = 'unknown'
   = 'unknown'

  if context&.respond_to?(:invoked_function_arn) && !context.invoked_function_arn.empty?
    arn_parts = context.invoked_function_arn.split(':')
    region = arn_parts[3] if arn_parts[3]
     = arn_parts[4] if arn_parts[4]
  end

  function_name = log_group&.split('/')&.last
  function_arn = "arn:aws:lambda:#{region}:#{}:function:#{function_name}"

  normalized_data = filtered_log_events.map do |log_event|
    {
      message: log_event[:message],
      ddsource: ENV['DATADOG_SOURCE'] || 'cwl-aws-lambda',
      service: ENV['DATADOG_SERVICE'] || function_name,
      hostname: function_arn,
      ddtags: [ENV['DATADOG_TAGS'], "region:#{region}"].compact.reject(&:empty?).join(','),
      aws: {
        awslogs: {
          logGroup: log_group,
          logStream: log_stream
        }
      },
      id: log_event[:id]
    }
  end

  # If we filtered everything out, do nothing.
  return if normalized_data.empty?

  begin
    send_json_to_datadog(normalized_data.to_json)
    puts "Datadog upload successful: sent #{normalized_data.length} log(s) for #{function_name}"
  rescue => e
    puts e.message
    puts "Error details: #{e.backtrace.first}"
  end

  nil
end