tpt_serverless 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bf37a0d47bf872ffbb9633925e4ddb37d6bfc38a570f84f75f3f61f492b8f100
4
- data.tar.gz: 1038ef65a0ed86399a412fac44fcdb4ef037abfc491307dc3d92554f3a474424
3
+ metadata.gz: c1f06b4fad5f9e34e05e818977304236568122d0a604ecd88e8cf6da4b331d5a
4
+ data.tar.gz: 2b54c154a4457028ed90778f699aa159acef24ba4abe6d40f01f80deb0926393
5
5
  SHA512:
6
- metadata.gz: 89ff6fca675780c16c77fbcbe976fcbbc67daeeaf01513a15f674ec97344047f8084fc7ae61ac62ec5c19f5c69f47830f319a00ffd578e433a9ff8950ef3ca20
7
- data.tar.gz: d4c7ec90b2c42414f43391f1f7af4bab4f76f55521f3c42fd9810247bd1cdc4e04a06bf61861de57cf790bf4c76fb7dc1a20fcc0819c3fd5e19bb534cc99303a
6
+ metadata.gz: bec09d6328b91ac709832540773484e01773d2da113166b592aea0cf2070823d69376bf32b5bf442cf5cbb9e367fda385bf7fdb62774d8c716b6de28faf97507
7
+ data.tar.gz: 91f0d1ce2681a15b8a5602cfa3736a700e79ab5016e4c9c542fb81dd250da2e98a0c351cf295515f61549bf4746471a84c4ef107ca6a4a00ce361536cdc03e23
@@ -15,6 +15,11 @@ Example of how to add this to your `serverless.yml`:
15
15
  DATADOG_SOURCE: cwl-aws-lambda # default
16
16
  DATADOG_SERVICE: ${self:service.name} # optional
17
17
  DATADOG_TAGS: stage:${self:provider.stage},team:platform # optional
18
+ # If set to a truthy value (1, true, yes, or on; case/whitespace-insensitive),
19
+ # filters AWS Lambda platform lifecycle boilerplate logs like
20
+ # INIT_START / START / END / REPORT before forwarding to Datadog.
21
+ # Defaults to false.
22
+ DATADOG_FILTER_LIFECYCLE_LOGS: "true" # optional
18
23
  events:
19
24
  - cloudwatchLog: /aws/lambda/${self:service.name}-functionOne-${self:provider.stage}
20
25
  - cloudwatchLog: /aws/lambda/${self:service.name}-functionTwo-${self:provider.stage}
@@ -28,9 +33,12 @@ require 'json'
28
33
  require 'net/http'
29
34
  require 'uri'
30
35
 
31
- TIME_REGEX = /^20\d\d-\d\d-\d\dT\d\d:\d\d:\d\d\.\d\d\dZ/
32
-
33
36
  class DatadogLogForwarder
37
+ TIME_REGEX ||= /^20\d\d-\d\d-\d\dT\d\d:\d\d:\d\d\.\d\d\dZ/
38
+
39
+ # Matches AWS Lambda platform lifecycle lines that are usually noisy in Datadog
40
+ LAMBDA_LIFECYCLE_AFTER_TIMESTAMP_REGEX ||= /\A(?:INIT_START\b|START RequestId:|END RequestId:|REPORT RequestId:)/
41
+
34
42
  class << self
35
43
  # This handler receives CloudWatch log events, parses the events and forwards the extracted logs to
36
44
  # Datadog.
@@ -58,7 +66,16 @@ class DatadogLogForwarder
58
66
  return
59
67
  end
60
68
 
61
- puts "message_count=#{log_events.length}"
69
+ normalized_log_events = normalize_messages(log_events)
70
+
71
+ filtered_log_events =
72
+ if should_filter_lifecycle_logs?
73
+ normalized_log_events.reject { |e| lambda_lifecycle_line?(e[:message]) }
74
+ else
75
+ normalized_log_events
76
+ end
77
+
78
+ puts "message_count=#{log_events.length} forwarded_count=#{filtered_log_events.length}"
62
79
 
63
80
  region = 'unknown'
64
81
  account_id = 'unknown'
@@ -72,11 +89,9 @@ class DatadogLogForwarder
72
89
  function_name = log_group&.split('/')&.last
73
90
  function_arn = "arn:aws:lambda:#{region}:#{account_id}:function:#{function_name}"
74
91
 
75
- normalized_data = log_events.map do |log_event|
76
- normalized_msg = normalize_messages([log_event]).first
77
-
92
+ normalized_data = filtered_log_events.map do |log_event|
78
93
  {
79
- message: normalized_msg,
94
+ message: log_event[:message],
80
95
  ddsource: ENV['DATADOG_SOURCE'] || 'cwl-aws-lambda',
81
96
  service: ENV['DATADOG_SERVICE'] || function_name,
82
97
  hostname: function_arn,
@@ -87,10 +102,13 @@ class DatadogLogForwarder
87
102
  logStream: log_stream
88
103
  }
89
104
  },
90
- id: log_event['id']
105
+ id: log_event[:id]
91
106
  }
92
107
  end
93
108
 
109
+ # If we filtered everything out, do nothing.
110
+ return if normalized_data.empty?
111
+
94
112
  begin
95
113
  send_json_to_datadog(normalized_data.to_json)
96
114
  puts "Datadog upload successful: sent #{normalized_data.length} log(s) for #{function_name}"
@@ -106,21 +124,38 @@ class DatadogLogForwarder
106
124
 
107
125
  def normalize_messages(log_events)
108
126
  log_events.map do |log_event|
127
+ id = log_event.fetch('id')
109
128
  message = log_event.fetch('message')
110
- timestamp = Integer(log_event.fetch('timestamp'))
129
+ timestamp_ms = Integer(log_event.fetch('timestamp'))
111
130
 
112
- # Ensure the message starts with a time.
113
- # E.g. START/END/REPORT log events don't.
131
+ # Ensure the message starts with an ISO timestamp (same intent as Node)
114
132
  if TIME_REGEX !~ message
115
- time_string = Time.at(timestamp/1000.0).utc.strftime('%Y-%m-%dT%H:%M:%S.%LZ')
133
+ time_string = Time.at(timestamp_ms / 1000.0).utc.strftime('%Y-%m-%dT%H:%M:%S.%LZ')
116
134
  message = "#{time_string} #{message}"
117
135
  end
118
136
 
119
137
  # AWS replaces newlines with carriage returns in Lambda logs
120
- message.gsub(/\r(?!\n)/, "\n")
138
+ message = message.gsub(/\r(?!\n)/, "\n")
139
+
140
+ { id: id, message: message }
121
141
  end
122
142
  end
123
143
 
144
+ def should_filter_lifecycle_logs?
145
+ truthy_env?(ENV['DATADOG_FILTER_LIFECYCLE_LOGS'])
146
+ end
147
+
148
+ def truthy_env?(value)
149
+ return false if value.nil?
150
+ %w[1 true yes on].include?(value.strip.downcase)
151
+ end
152
+
153
+ def lambda_lifecycle_line?(message_with_timestamp)
154
+ # Strip the leading ISO timestamp and match on the remainder.
155
+ after_timestamp = message_with_timestamp.sub(TIME_REGEX, '').lstrip
156
+ LAMBDA_LIFECYCLE_AFTER_TIMESTAMP_REGEX.match?(after_timestamp)
157
+ end
158
+
124
159
  def send_json_to_datadog(data)
125
160
  datadog_site = ENV['DATADOG_SITE'] || 'datadoghq.com'
126
161
  datadog_url = URI("https://http-intake.logs.#{datadog_site}/api/v2/logs")
@@ -138,7 +173,6 @@ class DatadogLogForwarder
138
173
  )
139
174
 
140
175
  req.body = data
141
-
142
176
  http.request(req)
143
177
  end
144
178
 
@@ -23,9 +23,9 @@ require 'json'
23
23
  require 'net/http'
24
24
  require 'uri'
25
25
 
26
- TIME_REGEX = /^20\d\d-\d\d-\d\dT\d\d:\d\d:\d\d\.\d\d\dZ/
27
-
28
26
  class SumoLogForwarder
27
+ TIME_REGEX ||= /^20\d\d-\d\d-\d\dT\d\d:\d\d:\d\d\.\d\d\dZ/
28
+
29
29
  class << self
30
30
  # This handler receives CloudWatch log events, parses the events and forwards the extracted logs to
31
31
  # Sumo Logic.
@@ -1,3 +1,3 @@
1
1
  module TptServerless
2
- VERSION = "0.4.1"
2
+ VERSION = "0.5.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tpt_serverless
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - TpT