tpt_serverless 0.3.0 → 0.4.1

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: b6887c4fd9a67c788a18671e57b5710e5e9de0e83057af75f3b2ba9989f947ce
4
- data.tar.gz: d8bb6a3f64a4e6b61b56bdf1c634867de17caa493de9ea6d344a8e0e6c598131
3
+ metadata.gz: bf37a0d47bf872ffbb9633925e4ddb37d6bfc38a570f84f75f3f61f492b8f100
4
+ data.tar.gz: 1038ef65a0ed86399a412fac44fcdb4ef037abfc491307dc3d92554f3a474424
5
5
  SHA512:
6
- metadata.gz: f7b21869a41a38fd9a24d32a8c649bfe6914255e0092ebf35f5519b254ef1cd67f113597b38b305696905d67c2206a5221cc6c3a3d7677f1773c3bafdd6d49c5
7
- data.tar.gz: 3c1b176c3cad4e4f17d592a7ee967d310c0aa0d4f077ab90f94d339279589ecc3eb301372c85aed14af5d4521fe12c7bedfc009f63ee5bf234c37ccffe312e16
6
+ metadata.gz: 89ff6fca675780c16c77fbcbe976fcbbc67daeeaf01513a15f674ec97344047f8084fc7ae61ac62ec5c19f5c69f47830f319a00ffd578e433a9ff8950ef3ca20
7
+ data.tar.gz: d4c7ec90b2c42414f43391f1f7af4bab4f76f55521f3c42fd9810247bd1cdc4e04a06bf61861de57cf790bf4c76fb7dc1a20fcc0819c3fd5e19bb534cc99303a
@@ -0,0 +1,152 @@
1
+ =begin
2
+ Receives logs from CloudWatch and forwards them to Datadog.
3
+
4
+ Example of how to add this to your `serverless.yml`:
5
+
6
+ forwardLogs:
7
+ name: ${self:service.name}-forwardLogs-${self:provider.stage}
8
+ # NOTE: Ensure that you update the path below if you update the tpt_serverless gem
9
+ handler: /opt/ruby/3.4.0/gems/tpt_serverless-X.X.X/lib/tpt_serverless/datadog_log_forwarder.DatadogLogForwarder.handler
10
+ timeout: 10 # seconds
11
+ environment:
12
+ DD_API_KEY: ${self:custom.datadogApiKey}
13
+ # Optional:
14
+ DATADOG_SITE: datadoghq.com # default
15
+ DATADOG_SOURCE: cwl-aws-lambda # default
16
+ DATADOG_SERVICE: ${self:service.name} # optional
17
+ DATADOG_TAGS: stage:${self:provider.stage},team:platform # optional
18
+ events:
19
+ - cloudwatchLog: /aws/lambda/${self:service.name}-functionOne-${self:provider.stage}
20
+ - cloudwatchLog: /aws/lambda/${self:service.name}-functionTwo-${self:provider.stage}
21
+ …etc…
22
+ reservedConcurrency: ${self:custom.datadogConcurrency}
23
+ =end
24
+
25
+ require 'zlib'
26
+ require 'base64'
27
+ require 'json'
28
+ require 'net/http'
29
+ require 'uri'
30
+
31
+ TIME_REGEX = /^20\d\d-\d\d-\d\dT\d\d:\d\d:\d\d\.\d\d\dZ/
32
+
33
+ class DatadogLogForwarder
34
+ class << self
35
+ # This handler receives CloudWatch log events, parses the events and forwards the extracted logs to
36
+ # Datadog.
37
+ #
38
+ # It performs a few helpful cleanup/prep functions as well.
39
+ def handler(event:, context:)
40
+ dd_api_key = ENV['DD_API_KEY']
41
+
42
+ if dd_api_key.nil? || dd_api_key.strip.empty?
43
+ puts 'ERROR: DD_API_KEY is not set. Skipping log forwarding.'
44
+ return
45
+ end
46
+
47
+ raw_data = event.fetch('awslogs').fetch('data')
48
+ unzipped_data = Zlib.gunzip(Base64.decode64(raw_data))
49
+ data = JSON.parse(unzipped_data)
50
+
51
+ message_type = data.fetch('messageType')
52
+ log_group = data.fetch('logGroup')
53
+ log_stream = data.fetch('logStream')
54
+ log_events = data.fetch('logEvents')
55
+
56
+ if message_type == 'CONTROL_MESSAGE'
57
+ puts 'skipping control message'
58
+ return
59
+ end
60
+
61
+ puts "message_count=#{log_events.length}"
62
+
63
+ region = 'unknown'
64
+ account_id = 'unknown'
65
+
66
+ if context&.respond_to?(:invoked_function_arn) && !context.invoked_function_arn.empty?
67
+ arn_parts = context.invoked_function_arn.split(':')
68
+ region = arn_parts[3] if arn_parts[3]
69
+ account_id = arn_parts[4] if arn_parts[4]
70
+ end
71
+
72
+ function_name = log_group&.split('/')&.last
73
+ function_arn = "arn:aws:lambda:#{region}:#{account_id}:function:#{function_name}"
74
+
75
+ normalized_data = log_events.map do |log_event|
76
+ normalized_msg = normalize_messages([log_event]).first
77
+
78
+ {
79
+ message: normalized_msg,
80
+ ddsource: ENV['DATADOG_SOURCE'] || 'cwl-aws-lambda',
81
+ service: ENV['DATADOG_SERVICE'] || function_name,
82
+ hostname: function_arn,
83
+ ddtags: [ENV['DATADOG_TAGS'], "region:#{region}"].compact.reject(&:empty?).join(','),
84
+ aws: {
85
+ awslogs: {
86
+ logGroup: log_group,
87
+ logStream: log_stream
88
+ }
89
+ },
90
+ id: log_event['id']
91
+ }
92
+ end
93
+
94
+ begin
95
+ send_json_to_datadog(normalized_data.to_json)
96
+ puts "Datadog upload successful: sent #{normalized_data.length} log(s) for #{function_name}"
97
+ rescue => e
98
+ puts e.message
99
+ puts "Error details: #{e.backtrace.first}"
100
+ end
101
+
102
+ nil
103
+ end
104
+
105
+ private
106
+
107
+ def normalize_messages(log_events)
108
+ log_events.map do |log_event|
109
+ message = log_event.fetch('message')
110
+ timestamp = Integer(log_event.fetch('timestamp'))
111
+
112
+ # Ensure the message starts with a time.
113
+ # E.g. START/END/REPORT log events don't.
114
+ if TIME_REGEX !~ message
115
+ time_string = Time.at(timestamp/1000.0).utc.strftime('%Y-%m-%dT%H:%M:%S.%LZ')
116
+ message = "#{time_string} #{message}"
117
+ end
118
+
119
+ # AWS replaces newlines with carriage returns in Lambda logs
120
+ message.gsub(/\r(?!\n)/, "\n")
121
+ end
122
+ end
123
+
124
+ def send_json_to_datadog(data)
125
+ datadog_site = ENV['DATADOG_SITE'] || 'datadoghq.com'
126
+ datadog_url = URI("https://http-intake.logs.#{datadog_site}/api/v2/logs")
127
+
128
+ response = Net::HTTP.start(datadog_url.host, datadog_url.port, use_ssl: datadog_url.scheme == 'https') do |http|
129
+ http.open_timeout = 3
130
+ http.read_timeout = 3
131
+
132
+ req = Net::HTTP::Post.new(
133
+ datadog_url,
134
+ {
135
+ 'Content-Type' => 'application/json',
136
+ 'DD-API-KEY' => ENV['DD_API_KEY'],
137
+ }
138
+ )
139
+
140
+ req.body = data
141
+
142
+ http.request(req)
143
+ end
144
+
145
+ unless ['200', '202'].include?(response.code)
146
+ raise "Datadog upload failed: #{response.code} - #{response.body}"
147
+ end
148
+
149
+ response
150
+ end
151
+ end
152
+ end
@@ -6,7 +6,7 @@ Example of how to add this to your `serverless.yml`:
6
6
  forwardLogs:
7
7
  name: ${self:service.name}-forwardLogs-${self:provider.stage}
8
8
  # NOTE: Ensure that you update the path below if you update the tpt_serverless gem
9
- handler: /opt/ruby/2.5.0/gems/tpt_serverless-X.X.X/lib/tpt_serverless/sumo_log_forwarder.SumoLogForwarder.handler
9
+ handler: /opt/ruby/3.4.0/gems/tpt_serverless-X.X.X/lib/tpt_serverless/sumo_log_forwarder.SumoLogForwarder.handler
10
10
  timeout: 10 # seconds
11
11
  environment:
12
12
  SUMO_ENDPOINT: ${self:custom.sumoEndpoint}
@@ -19,6 +19,9 @@ Example of how to add this to your `serverless.yml`:
19
19
 
20
20
  require 'zlib'
21
21
  require 'base64'
22
+ require 'json'
23
+ require 'net/http'
24
+ require 'uri'
22
25
 
23
26
  TIME_REGEX = /^20\d\d-\d\d-\d\dT\d\d:\d\d:\d\d\.\d\d\dZ/
24
27
 
@@ -1,3 +1,3 @@
1
1
  module TptServerless
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.1"
3
3
  end
@@ -1,5 +1,6 @@
1
1
  require "tpt_serverless/version"
2
2
  require 'tpt_serverless/sumo_log_forwarder'
3
+ require 'tpt_serverless/datadog_log_forwarder'
3
4
  require 'tpt_serverless/datadog'
4
5
 
5
6
  module TptServerless
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tpt_serverless
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - TpT
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2021-05-27 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: dogapi
@@ -24,20 +23,18 @@ dependencies:
24
23
  - - "~>"
25
24
  - !ruby/object:Gem::Version
26
25
  version: '1.45'
27
- description:
28
- email:
29
26
  executables: []
30
27
  extensions: []
31
28
  extra_rdoc_files: []
32
29
  files:
33
30
  - lib/tpt_serverless.rb
34
31
  - lib/tpt_serverless/datadog.rb
32
+ - lib/tpt_serverless/datadog_log_forwarder.rb
35
33
  - lib/tpt_serverless/sumo_log_forwarder.rb
36
34
  - lib/tpt_serverless/version.rb
37
35
  homepage: https://github.com/TeachersPayTeachers/tpt_serverless-ruby
38
36
  licenses: []
39
37
  metadata: {}
40
- post_install_message:
41
38
  rdoc_options: []
42
39
  require_paths:
43
40
  - lib
@@ -52,8 +49,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
52
49
  - !ruby/object:Gem::Version
53
50
  version: '0'
54
51
  requirements: []
55
- rubygems_version: 3.1.6
56
- signing_key:
52
+ rubygems_version: 3.6.9
57
53
  specification_version: 4
58
54
  summary: TpT serverless utils for Ruby
59
55
  test_files: []