lambda_loadout 0.0.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 +7 -0
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +50 -0
- data/LICENSE.txt +21 -0
- data/README.md +499 -0
- data/certs/stowzilla.pem +26 -0
- data/lib/lambda_loadout/error_notifier.rb +248 -0
- data/lib/lambda_loadout/errors.rb +218 -0
- data/lib/lambda_loadout/global.rb +83 -0
- data/lib/lambda_loadout/logger.rb +256 -0
- data/lib/lambda_loadout/metrics.rb +296 -0
- data/lib/lambda_loadout/middleware.rb +137 -0
- data/lib/lambda_loadout/version.rb +5 -0
- data/lib/lambda_loadout.rb +101 -0
- data.tar.gz.sig +3 -0
- metadata +168 -0
- metadata.gz.sig +0 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: de1d78c715119ebec2046c1cb5e16d8bdae6c33058f86c879186b172cb8d0118
|
|
4
|
+
data.tar.gz: 7f18ba031fafe3230594917e841bc22b4c24ca4fd02c50bb4daf1b60b2b6b6b0
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 105685b48b4d49732fc10ebf173792812d3ef25d638c46fde658edec4662114773d0aacee3e3c3896dcb22342f6e8762e25a2db84d0d89ee9bbc95dedb9d0969
|
|
7
|
+
data.tar.gz: 554ef9161c2aea601fbf8f9019d507ab05cbce82e606b516e0590718ced36ee764cf9650c1184de0fc9d771af9739afb93e212ac349612eea235e8729705694f
|
checksums.yaml.gz.sig
ADDED
|
Binary file
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.0.1] - 2026-06-09
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Initial RubyGems release of Lambda Loadout
|
|
14
|
+
- CloudWatch Metrics with EMF (Embedded Metric Format) support
|
|
15
|
+
- Metric creation with units and dimensions
|
|
16
|
+
- High-resolution metrics (1-second granularity)
|
|
17
|
+
- Metadata support for searchable fields
|
|
18
|
+
- Custom timestamp support
|
|
19
|
+
- Automatic metric batching (up to 100 metrics per EMF object)
|
|
20
|
+
- Structured JSON logging
|
|
21
|
+
- Multiple log levels (debug, info, warn, error, fatal)
|
|
22
|
+
- Lambda context injection
|
|
23
|
+
- Persistent fields support
|
|
24
|
+
- Exception logging with stack traces
|
|
25
|
+
- Sampling support for debug logs
|
|
26
|
+
- Error Notification System via SNS
|
|
27
|
+
- Rich email notifications with error details, stack traces, and request context
|
|
28
|
+
- Deep links to CloudWatch Logs Insights and Log streams
|
|
29
|
+
- Support for API Gateway, SQS, SNS, DynamoDB Streams, and EventBridge events
|
|
30
|
+
- Graceful fallback if notification sending fails
|
|
31
|
+
- Error handling and alerting
|
|
32
|
+
- ErrorHandler class for automatic error capture
|
|
33
|
+
- AlarmConfig for generating CloudWatch Alarm definitions
|
|
34
|
+
- CloudFormation and Terraform export support
|
|
35
|
+
- Lambda middleware pattern
|
|
36
|
+
- `with_observability` helper method
|
|
37
|
+
- `LambdaLoadout::Handler` DSL for simplified integration
|
|
38
|
+
- Cold start tracking
|
|
39
|
+
- Automatic cold start metric capture
|
|
40
|
+
- Per-function cold start tracking
|
|
41
|
+
- Rake tasks for Lambda layer management
|
|
42
|
+
- `rake layer:build` — builds lambda-loadout-layer.zip
|
|
43
|
+
- `rake layer:publish` — builds + publishes to AWS Lambda
|
|
44
|
+
- `rake layer:clean` — removes build artifacts
|
|
45
|
+
- Gem signing with Stowzilla cert chain
|
|
46
|
+
- GitHub Actions CI (test matrix Ruby 3.2/3.3/3.4, lint, security, auto-tag release)
|
|
47
|
+
- Comprehensive documentation and examples
|
|
48
|
+
|
|
49
|
+
[Unreleased]: https://github.com/stowzilla/lambda-loadout/compare/v0.0.1...HEAD
|
|
50
|
+
[0.0.1]: https://github.com/stowzilla/lambda-loadout/releases/tag/v0.0.1
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Stowzilla Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,499 @@
|
|
|
1
|
+
# Lambda Loadout
|
|
2
|
+
|
|
3
|
+
[](https://rubygems.org/gems/lambda_loadout)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://www.ruby-lang.org/)
|
|
6
|
+
|
|
7
|
+
**AWS Lambda Powertools for Ruby** — A developer toolkit to implement serverless best practices and increase developer velocity.
|
|
8
|
+
|
|
9
|
+
Lambda Loadout provides structured logging, CloudWatch metrics via Embedded Metric Format (EMF), error handling, error notifications via SNS, and alerting for AWS Lambda functions. Inspired by [AWS Lambda Powertools for Python](https://github.com/aws-powertools/powertools-lambda-python).
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- **CloudWatch Metrics (EMF)** — Custom metrics via CloudWatch Embedded Metric Format (zero API calls, zero latency overhead)
|
|
14
|
+
- **Structured Logging** — JSON structured logging with automatic Lambda context enrichment
|
|
15
|
+
- **Error Notifications** — Detailed error alerts via SNS with CloudWatch deep links and event source detection
|
|
16
|
+
- **Error Handling & Alerting** — Automatic error capture with CloudWatch alarm configuration helpers (CloudFormation & Terraform)
|
|
17
|
+
- **Cold Start Tracking** — Built-in cold start metric capture
|
|
18
|
+
- **Middleware Pattern** — Clean `with_observability` wrapper and `Handler` DSL for Ruby Lambda handlers
|
|
19
|
+
- **Global Configuration** — Module-level API for shared logger/metrics instances
|
|
20
|
+
- **Lambda Layer Support** — Build and publish as a Lambda layer via Rake tasks
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
Add to your Lambda function's Gemfile:
|
|
25
|
+
|
|
26
|
+
```ruby
|
|
27
|
+
gem 'lambda_loadout'
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Or install directly:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
gem install lambda_loadout
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Dependencies
|
|
37
|
+
|
|
38
|
+
- `json` (~> 2.0) — JSON serialization (Ruby stdlib)
|
|
39
|
+
- `aws-sdk-sns` (~> 1.0) — SNS error notifications
|
|
40
|
+
|
|
41
|
+
## Quick Start
|
|
42
|
+
|
|
43
|
+
### Basic Usage
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
require 'lambda_loadout'
|
|
47
|
+
|
|
48
|
+
LOGGER = LambdaLoadout::Logger.new(service: "payment")
|
|
49
|
+
METRICS = LambdaLoadout::Metrics.new(namespace: "MyApp", service: "payment")
|
|
50
|
+
|
|
51
|
+
def lambda_handler(event:, context:)
|
|
52
|
+
LambdaLoadout.with_logging_and_metrics(LOGGER, METRICS, context) do
|
|
53
|
+
LOGGER.info("Processing payment", order_id: event['orderId'])
|
|
54
|
+
METRICS.add_metric(name: "PaymentProcessed", unit: "Count", value: 1)
|
|
55
|
+
METRICS.add_dimension(name: "payment_type", value: event['type'])
|
|
56
|
+
|
|
57
|
+
{ statusCode: 200, body: "Payment processed" }
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### With Error Notifications
|
|
63
|
+
|
|
64
|
+
```ruby
|
|
65
|
+
def lambda_handler(event:, context:)
|
|
66
|
+
LambdaLoadout.with_logging_and_metrics(
|
|
67
|
+
LOGGER, METRICS, context,
|
|
68
|
+
event: event,
|
|
69
|
+
error_notification_config: { sns_topic_arn: ENV['ERROR_NOTIFICATION_TOPIC_ARN'] }
|
|
70
|
+
) do
|
|
71
|
+
process_payment(event)
|
|
72
|
+
{ statusCode: 200, body: "Success" }
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
When an error occurs, the `ErrorNotifier` sends a detailed SNS message including:
|
|
78
|
+
- Error class, message, and stack trace (first 15 lines)
|
|
79
|
+
- Lambda request context (function name, request ID, memory, remaining time)
|
|
80
|
+
- Event source detection (API Gateway, SQS, SNS, DynamoDB Streams, EventBridge)
|
|
81
|
+
- Deep links to CloudWatch Logs Insights (pre-filtered query) and log streams
|
|
82
|
+
- JSON-formatted error data for programmatic consumption
|
|
83
|
+
|
|
84
|
+
Notification failures are caught and logged — they won't break your Lambda execution.
|
|
85
|
+
|
|
86
|
+
### Using Middleware Pattern
|
|
87
|
+
|
|
88
|
+
```ruby
|
|
89
|
+
require 'lambda_loadout'
|
|
90
|
+
|
|
91
|
+
class PaymentHandler
|
|
92
|
+
include LambdaLoadout::Middleware
|
|
93
|
+
|
|
94
|
+
def initialize
|
|
95
|
+
@logger = LambdaLoadout::Logger.new(service: "payment")
|
|
96
|
+
@metrics = LambdaLoadout::Metrics.new(namespace: "MyApp", service: "payment")
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def call(event:, context:)
|
|
100
|
+
with_observability(context) do
|
|
101
|
+
@logger.info("Processing payment", event: event)
|
|
102
|
+
result = process_payment(event)
|
|
103
|
+
@metrics.add_metric(name: "PaymentProcessed", unit: "Count", value: 1)
|
|
104
|
+
{ statusCode: 200, body: result.to_json }
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
HANDLER = PaymentHandler.new
|
|
110
|
+
|
|
111
|
+
def lambda_handler(event:, context:)
|
|
112
|
+
HANDLER.call(event: event, context: context)
|
|
113
|
+
end
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Using Handler DSL
|
|
117
|
+
|
|
118
|
+
```ruby
|
|
119
|
+
require 'lambda_loadout'
|
|
120
|
+
|
|
121
|
+
LambdaLoadout::Handler.configure do |config|
|
|
122
|
+
config.service = "payment"
|
|
123
|
+
config.namespace = "MyApp"
|
|
124
|
+
config.log_level = :info
|
|
125
|
+
config.capture_cold_start = true
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def lambda_handler(event:, context:)
|
|
129
|
+
LambdaLoadout::Handler.call(event, context) do |logger, metrics|
|
|
130
|
+
logger.info("Processing event", event_type: event['type'])
|
|
131
|
+
metrics.add_metric(name: "EventProcessed", unit: "Count", value: 1)
|
|
132
|
+
{ statusCode: 200, body: "OK" }
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Global Configuration
|
|
138
|
+
|
|
139
|
+
```ruby
|
|
140
|
+
require 'lambda_loadout'
|
|
141
|
+
|
|
142
|
+
LambdaLoadout.configure do |config|
|
|
143
|
+
config.service = "payment-api"
|
|
144
|
+
config.namespace = "MyApp"
|
|
145
|
+
config.log_level = :debug
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Access global instances anywhere
|
|
149
|
+
LambdaLoadout.logger.info("Starting up")
|
|
150
|
+
LambdaLoadout.metrics.add_metric(name: "Boot", unit: "Count", value: 1)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## CloudWatch Metrics (EMF)
|
|
154
|
+
|
|
155
|
+
Metrics are published via CloudWatch Embedded Metric Format — structured JSON written to stdout that CloudWatch automatically extracts. No `PutMetricData` API calls, no additional latency.
|
|
156
|
+
|
|
157
|
+
### Creating Metrics
|
|
158
|
+
|
|
159
|
+
```ruby
|
|
160
|
+
metrics = LambdaLoadout::Metrics.new(namespace: "MyApp", service: "payment")
|
|
161
|
+
|
|
162
|
+
metrics.add_metric(name: "BookingConfirmation", unit: "Count", value: 1)
|
|
163
|
+
metrics.add_metric(name: "ResponseTime", unit: "Milliseconds", value: 145.5)
|
|
164
|
+
metrics.add_dimension(name: "environment", value: "production")
|
|
165
|
+
metrics.add_metadata(key: "booking_id", value: "abc-123")
|
|
166
|
+
metrics.flush
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### High-Resolution Metrics
|
|
170
|
+
|
|
171
|
+
```ruby
|
|
172
|
+
metrics.add_metric(name: "HighPrecisionMetric", unit: "Count", value: 1, resolution: 1)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Default Dimensions
|
|
176
|
+
|
|
177
|
+
```ruby
|
|
178
|
+
metrics.set_default_dimensions(environment: "production", region: "us-east-1")
|
|
179
|
+
# These persist across flushes
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Custom Timestamps
|
|
183
|
+
|
|
184
|
+
```ruby
|
|
185
|
+
metrics.set_timestamp(Time.now) # Time object
|
|
186
|
+
metrics.set_timestamp(1699876543000) # Epoch milliseconds
|
|
187
|
+
# Validates within CloudWatch limits (14 days past, 2 hours future)
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Auto-Flush
|
|
191
|
+
|
|
192
|
+
Metrics automatically flush when the 100-metric limit is reached. You can also call `metrics.flush` manually or rely on the `with_logging_and_metrics` / `with_observability` wrappers to flush in their `ensure` block.
|
|
193
|
+
|
|
194
|
+
### Supported Units
|
|
195
|
+
|
|
196
|
+
`Count`, `Seconds`, `Milliseconds`, `Microseconds`, `Bytes`, `Kilobytes`, `Megabytes`, `Gigabytes`, `Terabytes`, `Bits`, `Kilobits`, `Megabits`, `Gigabits`, `Terabits`, `Percent`, `Count/Second`, `Bytes/Second`, `Kilobytes/Second`, `Megabytes/Second`, `Gigabytes/Second`, `Terabytes/Second`, `Bits/Second`, `Kilobits/Second`, `Megabits/Second`, `Gigabits/Second`, `Terabits/Second`, `None`
|
|
197
|
+
|
|
198
|
+
### EMF Output Example
|
|
199
|
+
|
|
200
|
+
```json
|
|
201
|
+
{
|
|
202
|
+
"_aws": {
|
|
203
|
+
"Timestamp": 1699876543000,
|
|
204
|
+
"CloudWatchMetrics": [{
|
|
205
|
+
"Namespace": "MyApp",
|
|
206
|
+
"Dimensions": [["service", "environment"]],
|
|
207
|
+
"Metrics": [
|
|
208
|
+
{"Name": "PaymentProcessed", "Unit": "Count"},
|
|
209
|
+
{"Name": "ResponseTime", "Unit": "Milliseconds"}
|
|
210
|
+
]
|
|
211
|
+
}]
|
|
212
|
+
},
|
|
213
|
+
"service": "payment",
|
|
214
|
+
"environment": "production",
|
|
215
|
+
"PaymentProcessed": 1.0,
|
|
216
|
+
"ResponseTime": 145.5,
|
|
217
|
+
"booking_id": "abc-123"
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## Structured Logging
|
|
222
|
+
|
|
223
|
+
### Basic Logging
|
|
224
|
+
|
|
225
|
+
```ruby
|
|
226
|
+
logger = LambdaLoadout::Logger.new(service: "payment", level: :info)
|
|
227
|
+
|
|
228
|
+
logger.info("Payment processed", order_id: "12345", amount: 99.99)
|
|
229
|
+
# => {"level":"INFO","timestamp":"2025-11-13T12:00:00.000Z","message":"Payment processed","service":"payment","order_id":"12345","amount":99.99}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Lambda Context Injection
|
|
233
|
+
|
|
234
|
+
```ruby
|
|
235
|
+
def lambda_handler(event:, context:)
|
|
236
|
+
logger.inject_lambda_context(context)
|
|
237
|
+
logger.info("Processing request")
|
|
238
|
+
# Output includes: function_name, function_version, function_request_id, function_memory_size, cold_start
|
|
239
|
+
end
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Exception Logging
|
|
243
|
+
|
|
244
|
+
```ruby
|
|
245
|
+
begin
|
|
246
|
+
risky_operation()
|
|
247
|
+
rescue StandardError => e
|
|
248
|
+
logger.error("Operation failed", e, order_id: order_id)
|
|
249
|
+
# Automatically includes error message, error_class, and backtrace (first 10 lines)
|
|
250
|
+
|
|
251
|
+
# Or use the dedicated exception method
|
|
252
|
+
logger.exception(e, message: "Operation failed", order_id: order_id)
|
|
253
|
+
end
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### Persistent Fields
|
|
257
|
+
|
|
258
|
+
```ruby
|
|
259
|
+
logger.append_keys(correlation_id: "abc-123", tenant_id: "tenant-1")
|
|
260
|
+
logger.info("Event 1") # Includes correlation_id and tenant_id
|
|
261
|
+
|
|
262
|
+
logger.remove_keys(:tenant_id)
|
|
263
|
+
logger.info("Event 2") # Only includes correlation_id
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Debug Log Sampling
|
|
267
|
+
|
|
268
|
+
```ruby
|
|
269
|
+
logger = LambdaLoadout::Logger.new(service: "payment", level: :debug, sampling_rate: 0.1)
|
|
270
|
+
# Only 10% of debug logs will be emitted
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Log Levels
|
|
274
|
+
|
|
275
|
+
`debug`, `info`, `warn`, `error`, `fatal`
|
|
276
|
+
|
|
277
|
+
## Error Notifications via SNS
|
|
278
|
+
|
|
279
|
+
Sends rich error alerts when Lambda functions fail.
|
|
280
|
+
|
|
281
|
+
### Standalone Usage
|
|
282
|
+
|
|
283
|
+
```ruby
|
|
284
|
+
notifier = LambdaLoadout::ErrorNotifier.new(
|
|
285
|
+
sns_topic_arn: ENV['ERROR_NOTIFICATION_TOPIC_ARN'],
|
|
286
|
+
logger: logger,
|
|
287
|
+
region: 'us-east-1' # optional, defaults to AWS_REGION env var
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
notifier.notify(error: e, context: context, event: event)
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### Integrated Usage
|
|
294
|
+
|
|
295
|
+
Pass `error_notification_config` to `with_logging_and_metrics` (see Quick Start above). Notifications are sent automatically on unhandled exceptions.
|
|
296
|
+
|
|
297
|
+
### What's Included in Each Notification
|
|
298
|
+
|
|
299
|
+
- Error class, message, and stack trace (first 15 lines)
|
|
300
|
+
- Lambda request context (function name, request ID, memory, remaining time)
|
|
301
|
+
- Event source detection (API Gateway, SQS, SNS, DynamoDB Streams, EventBridge)
|
|
302
|
+
- Deep links to CloudWatch Logs Insights (pre-filtered query) and log streams
|
|
303
|
+
- JSON-formatted error data for programmatic consumption
|
|
304
|
+
|
|
305
|
+
## Error Handling & Alarms
|
|
306
|
+
|
|
307
|
+
### Automatic Error Capture
|
|
308
|
+
|
|
309
|
+
```ruby
|
|
310
|
+
handler = LambdaLoadout::ErrorHandler.new(
|
|
311
|
+
logger: logger,
|
|
312
|
+
metrics: metrics,
|
|
313
|
+
capture_stack_trace: true,
|
|
314
|
+
error_metric_name: "LambdaError" # default
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
def lambda_handler(event:, context:)
|
|
318
|
+
handler.handle(context) do
|
|
319
|
+
process_event(event)
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### CloudWatch Alarm Configuration
|
|
325
|
+
|
|
326
|
+
Generate CloudFormation or Terraform for alarms:
|
|
327
|
+
|
|
328
|
+
```ruby
|
|
329
|
+
alarm = LambdaLoadout::AlarmConfig.new(
|
|
330
|
+
metric_name: "LambdaError",
|
|
331
|
+
namespace: "MyApp",
|
|
332
|
+
threshold: 1,
|
|
333
|
+
evaluation_periods: 1,
|
|
334
|
+
period: 60,
|
|
335
|
+
statistic: "Sum",
|
|
336
|
+
comparison_operator: "GreaterThanOrEqualToThreshold"
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
puts alarm.to_cloudformation(
|
|
340
|
+
alarm_name: "PaymentErrorAlarm",
|
|
341
|
+
sns_topic_arn: "arn:aws:sns:us-east-1:123456789012:alerts",
|
|
342
|
+
dimensions: { service: "payment" }
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
puts alarm.to_terraform(
|
|
346
|
+
resource_name: "payment_error_alarm",
|
|
347
|
+
sns_topic_arn: "arn:aws:sns:us-east-1:123456789012:alerts",
|
|
348
|
+
dimensions: { service: "payment" }
|
|
349
|
+
)
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
## Lambda Layer
|
|
353
|
+
|
|
354
|
+
Lambda Loadout can be published as a Lambda layer so your functions don't need to bundle it in their deployment packages. This is the recommended approach for teams sharing the gem across multiple Lambda functions.
|
|
355
|
+
|
|
356
|
+
### Rake Tasks
|
|
357
|
+
|
|
358
|
+
```bash
|
|
359
|
+
# Build the layer zip
|
|
360
|
+
bundle exec rake layer:build
|
|
361
|
+
|
|
362
|
+
# Build and publish to AWS (requires configured AWS CLI)
|
|
363
|
+
bundle exec rake layer:publish
|
|
364
|
+
|
|
365
|
+
# Clean build artifacts
|
|
366
|
+
bundle exec rake layer:clean
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
The layer targets the `ruby3.4` runtime. After publishing, add the returned `LayerVersionArn` to your Lambda function configuration.
|
|
370
|
+
|
|
371
|
+
### Shell Scripts (alternative)
|
|
372
|
+
|
|
373
|
+
```bash
|
|
374
|
+
./scripts/build_layer.sh # Build only
|
|
375
|
+
./scripts/publish_layer.sh # Publish (requires prior build)
|
|
376
|
+
./scripts/release.sh # Build + publish interactively
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### Using the Layer in Your Lambda
|
|
380
|
+
|
|
381
|
+
**Terraform:**
|
|
382
|
+
|
|
383
|
+
```hcl
|
|
384
|
+
resource "aws_lambda_function" "my_function" {
|
|
385
|
+
# ...
|
|
386
|
+
layers = ["arn:aws:lambda:us-east-1:ACCOUNT:layer:lambda-loadout:VERSION"]
|
|
387
|
+
}
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
**CloudFormation / SAM:**
|
|
391
|
+
|
|
392
|
+
```yaml
|
|
393
|
+
MyFunction:
|
|
394
|
+
Type: AWS::Lambda::Function
|
|
395
|
+
Properties:
|
|
396
|
+
Layers:
|
|
397
|
+
- arn:aws:lambda:us-east-1:ACCOUNT:layer:lambda-loadout:VERSION
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
## Environment Variables
|
|
401
|
+
|
|
402
|
+
| Variable | Description |
|
|
403
|
+
|----------|-------------|
|
|
404
|
+
| `POWERTOOLS_SERVICE_NAME` | Default service name for Logger and Metrics |
|
|
405
|
+
| `POWERTOOLS_METRICS_NAMESPACE` | Default metrics namespace |
|
|
406
|
+
| `ERROR_NOTIFICATION_TOPIC_ARN` | SNS topic ARN for error notifications |
|
|
407
|
+
| `ENVIRONMENT` / `STAGE` | Environment name (included in error notifications) |
|
|
408
|
+
| `AWS_REGION` | AWS region (used by ErrorNotifier, defaults to `us-east-1`) |
|
|
409
|
+
|
|
410
|
+
## Testing
|
|
411
|
+
|
|
412
|
+
### Testing with Metrics
|
|
413
|
+
|
|
414
|
+
```ruby
|
|
415
|
+
output = StringIO.new
|
|
416
|
+
metrics = LambdaLoadout::Metrics.new(namespace: "MyApp", output: output)
|
|
417
|
+
|
|
418
|
+
metrics.add_metric(name: "TestMetric", unit: "Count", value: 1)
|
|
419
|
+
metrics.flush
|
|
420
|
+
|
|
421
|
+
emf_output = JSON.parse(output.string)
|
|
422
|
+
expect(emf_output['TestMetric']).to eq(1)
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
### Testing with Logger
|
|
426
|
+
|
|
427
|
+
```ruby
|
|
428
|
+
output = StringIO.new
|
|
429
|
+
logger = LambdaLoadout::Logger.new(service: "test", output: output)
|
|
430
|
+
|
|
431
|
+
logger.info("Test message", data: "value")
|
|
432
|
+
|
|
433
|
+
log_entry = JSON.parse(output.string)
|
|
434
|
+
expect(log_entry['message']).to eq("Test message")
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
Both `Logger` and `Metrics` accept an `output:` parameter, making it easy to capture and assert on output in tests without mocking stdout.
|
|
438
|
+
|
|
439
|
+
## Development
|
|
440
|
+
|
|
441
|
+
```bash
|
|
442
|
+
# Install dependencies
|
|
443
|
+
bundle install
|
|
444
|
+
|
|
445
|
+
# Run tests
|
|
446
|
+
bundle exec rake spec
|
|
447
|
+
|
|
448
|
+
# Run linter
|
|
449
|
+
bundle exec rake rubocop
|
|
450
|
+
|
|
451
|
+
# Run both (default task)
|
|
452
|
+
bundle exec rake
|
|
453
|
+
|
|
454
|
+
# List all available tasks
|
|
455
|
+
bundle exec rake -T
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
## Project Structure
|
|
459
|
+
|
|
460
|
+
```
|
|
461
|
+
lambda-loadout/
|
|
462
|
+
├── lib/lambda_loadout/
|
|
463
|
+
│ ├── version.rb # Gem version
|
|
464
|
+
│ ├── logger.rb # Structured JSON logging
|
|
465
|
+
│ ├── metrics.rb # CloudWatch EMF metrics
|
|
466
|
+
│ ├── errors.rb # ErrorHandler, AlarmConfig, custom error classes
|
|
467
|
+
│ ├── error_notifier.rb # SNS error notifications with CloudWatch deep links
|
|
468
|
+
│ ├── middleware.rb # Middleware module + Handler DSL
|
|
469
|
+
│ └── global.rb # Module-level API & global config
|
|
470
|
+
├── examples/ # Working Lambda handler examples
|
|
471
|
+
├── spec/ # RSpec test suite
|
|
472
|
+
├── scripts/ # Layer build & publish scripts
|
|
473
|
+
└── docs/ # Integration guide, alarm templates
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
## Roadmap
|
|
477
|
+
|
|
478
|
+
- [x] CloudWatch Metrics (EMF)
|
|
479
|
+
- [x] Structured Logging
|
|
480
|
+
- [x] Error Handling & Alarm Config (CloudFormation + Terraform)
|
|
481
|
+
- [x] Cold Start Tracking
|
|
482
|
+
- [x] Error Notifications via SNS
|
|
483
|
+
- [x] Middleware Pattern & Handler DSL
|
|
484
|
+
- [x] Global Configuration API
|
|
485
|
+
- [ ] X-Ray Tracing Integration
|
|
486
|
+
- [ ] Parameter Store / Secrets Manager Integration
|
|
487
|
+
- [ ] Idempotency Support
|
|
488
|
+
- [ ] Batch Processing Utilities
|
|
489
|
+
- [ ] Event Source Data Classes
|
|
490
|
+
|
|
491
|
+
## Related Projects
|
|
492
|
+
|
|
493
|
+
- [AWS Lambda Powertools Python](https://github.com/aws-powertools/powertools-lambda-python)
|
|
494
|
+
- [AWS Lambda Powertools TypeScript](https://github.com/aws-powertools/powertools-lambda-typescript)
|
|
495
|
+
- [AWS Lambda Powertools Java](https://github.com/aws-powertools/powertools-lambda-java)
|
|
496
|
+
|
|
497
|
+
## License
|
|
498
|
+
|
|
499
|
+
MIT — see [LICENSE.txt](LICENSE.txt) for details.
|
data/certs/stowzilla.pem
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
|
2
|
+
MIIEdDCCAtygAwIBAgIBATANBgkqhkiG9w0BAQsFADBAMQ4wDAYDVQQDDAVhZ2Vu
|
|
3
|
+
dDEZMBcGCgmSJomT8ixkARkWCXN0b3d6aWxsYTETMBEGCgmSJomT8ixkARkWA2Nv
|
|
4
|
+
bTAeFw0yNjA2MDgxOTExNTlaFw0yNzA2MDgxOTExNTlaMEAxDjAMBgNVBAMMBWFn
|
|
5
|
+
ZW50MRkwFwYKCZImiZPyLGQBGRYJc3Rvd3ppbGxhMRMwEQYKCZImiZPyLGQBGRYD
|
|
6
|
+
Y29tMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAupBquKI/4WvXOgND
|
|
7
|
+
pXyqH2GllZs1wG4TWWdn/DoMg45UoCwD+AWEuGrIdInBCpPN8vEJNJWPoM/RrU+b
|
|
8
|
+
xRBZT4uUk00bnZRW2SYh5GJSqBoBR+rWc2DGkXyGfdRU2sQvkB0+is6ChgQ61WMM
|
|
9
|
+
33LE9+loBlVsZ6EVtrc18Uh2OW0mJpe0hN2nmBrxZqqOZigxC4DKRMFHvpRkxSb6
|
|
10
|
+
mD4kit1AcwX9NEWJsXxrPaetL/SB/VbXaEZX93XAvp6USaXvCWt4slkDS2mIvqtn
|
|
11
|
+
9DtGC43LFC7SDGbnsG9PVenQgVCi8UWFPUAab0PqZSlmi3Qlbhw8qTGPp5Cbv4vz
|
|
12
|
+
qjC2UGPOQigA/7lbbGRhCohMrjOVHMAQwkcgiIqtolUoYlnvPMIy+m3pdvgDv/PH
|
|
13
|
+
bsZGvXQ7i0458xsmp1vaKthZocVAR+GboHbuIiYPUnO45ccXUQ00x6365tTe7mZi
|
|
14
|
+
NvmUYdAGbQmVvFqyxF7IYA6sF74L2Lstu0knSfss557bAe1HAgMBAAGjeTB3MAkG
|
|
15
|
+
A1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQWBBSnxTL/lNBCeLqpeVIX6AUY
|
|
16
|
+
kel4zjAeBgNVHREEFzAVgRNhZ2VudEBzdG93emlsbGEuY29tMB4GA1UdEgQXMBWB
|
|
17
|
+
E2FnZW50QHN0b3d6aWxsYS5jb20wDQYJKoZIhvcNAQELBQADggGBACm9Fjit/UCv
|
|
18
|
+
FxlKqeiCTIG94cIx+QrWAOJSx9knKydwUec1u04D/DbfZjTn3C2Bj227QgxeUn+6
|
|
19
|
+
if3e2v7zAk1896hLmGYzML0+nxQPb0vmtdLR7HETUlSKTVabcv1fbwLyjsuGrBvk
|
|
20
|
+
y51vOEzUEZ508a9yepLYqrQu1kOju4d57c9oA5l3H0mMKWz7av9tFj0B+STvuaWk
|
|
21
|
+
HRYDWc5HgOEVTyV+w0uFt2Kw4OCb8C42uSvC5RfYYtw78MSP+5Ru+LXJ7XOtmuN0
|
|
22
|
+
E6GVmofQ17ig9O3rgfFbMendSInrRmvPIGswvM1yivq9NOllFbdck2OJKPx6FCJF
|
|
23
|
+
7SJIkXQfc9P4B5iASIV1d1FsE0YX+g3jHXPJK/4mGL5bAyBKzpMfQB/mg6vQBzkh
|
|
24
|
+
aOKPwcreFj7TznBl89R5tNS9wZQfPVR98zgPyocddWhK18eQNMSBUnv4eeJ8PPbk
|
|
25
|
+
DovL+G8ajHDZ9fjH/+GVYHEMuiVdLarXrKJpHC1VfGTTUAp4NSEpUQ==
|
|
26
|
+
-----END CERTIFICATE-----
|