nui-lambda-shared-utils 1.1.1__tar.gz → 1.1.4__tar.gz
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.
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/PKG-INFO +1 -1
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/docs/README.md +8 -3
- nui_lambda_shared_utils-1.1.4/docs/guides/elasticsearch-integration.md +375 -0
- nui_lambda_shared_utils-1.1.4/docs/guides/lambda-utilities.md +274 -0
- nui_lambda_shared_utils-1.1.4/docs/guides/shared-types.md +527 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/nui_lambda_shared_utils/__init__.py +5 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/nui_lambda_shared_utils/cli.py +1 -1
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/nui_lambda_shared_utils/cloudwatch_metrics.py +2 -2
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/nui_lambda_shared_utils/db_client.py +1 -1
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/nui_lambda_shared_utils/error_handler.py +1 -1
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/nui_lambda_shared_utils/es_client.py +82 -4
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/nui_lambda_shared_utils/es_query_builder.py +2 -2
- nui_lambda_shared_utils-1.1.4/nui_lambda_shared_utils/lambda_helpers.py +84 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/nui_lambda_shared_utils/powertools_helpers.py +15 -7
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/nui_lambda_shared_utils/slack_client.py +12 -7
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/nui_lambda_shared_utils/slack_setup/channel_creator.py +1 -1
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/nui_lambda_shared_utils.egg-info/SOURCES.txt +5 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/tests/test_aws_utils.py +0 -2
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/tests/test_base_client.py +1 -2
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/tests/test_cloudwatch_metrics.py +3 -2
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/tests/test_config.py +0 -1
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/tests/test_db_client.py +3 -2
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/tests/test_error_handler.py +1 -5
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/tests/test_es_client.py +147 -1
- nui_lambda_shared_utils-1.1.4/tests/test_lambda_helpers.py +247 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/tests/test_powertools_helpers.py +2 -4
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/tests/test_slack_client.py +4 -2
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/tests/test_slack_formatter.py +1 -2
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/tests/test_timezone.py +0 -2
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/tests/test_utils.py +1 -1
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/.editorconfig +0 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/.github/workflows/ci.yml +0 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/.github/workflows/publish.yml +0 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/.github/workflows/test.yml +0 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/.markdownlint-cli2.yaml +0 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/CLAUDE.md +0 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/CONTRIBUTING.md +0 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/LICENSE +0 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/MANIFEST.in +0 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/README.md +0 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/docs/development/testing.md +0 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/docs/getting-started/configuration.md +0 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/docs/getting-started/installation.md +0 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/docs/getting-started/quickstart.md +0 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/docs/guides/cli-tools.md +0 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/docs/guides/powertools-integration.md +0 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/docs/guides/slack-integration.md +0 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/mypy.ini +0 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/nui_lambda_shared_utils/base_client.py +0 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/nui_lambda_shared_utils/config.py +0 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/nui_lambda_shared_utils/secrets_helper.py +0 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/nui_lambda_shared_utils/slack_formatter.py +0 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/nui_lambda_shared_utils/slack_setup/__init__.py +0 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/nui_lambda_shared_utils/slack_setup/channel_definitions.py +0 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/nui_lambda_shared_utils/slack_setup/setup_helpers.py +0 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/nui_lambda_shared_utils/timezone.py +0 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/nui_lambda_shared_utils/utils.py +0 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/pyproject.toml +0 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/pytest.ini +0 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/requirements-test.txt +0 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/setup.cfg +0 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/setup.py +0 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/tests/__init__.py +0 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/tests/test_es_query_builder.py +0 -0
- {nui_lambda_shared_utils-1.1.1 → nui_lambda_shared_utils-1.1.4}/tests/test_secrets_helper.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nui-lambda-shared-utils
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.4
|
|
4
4
|
Summary: Enterprise-grade utilities for AWS Lambda functions with Slack, Elasticsearch, and monitoring integrations
|
|
5
5
|
Home-page: https://github.com/nuimarkets/nui-lambda-shared-utils
|
|
6
6
|
Author: NUI Markets
|
|
@@ -17,8 +17,9 @@ Welcome to the comprehensive documentation for `nui-lambda-shared-utils`.
|
|
|
17
17
|
Component-specific guides for major features:
|
|
18
18
|
|
|
19
19
|
- **[AWS Powertools Integration](guides/powertools-integration.md)** - Standardized logging, metrics, and error handling
|
|
20
|
+
- **[Lambda Context Helpers](guides/lambda-utilities.md)** - Environment info extraction for logging and metrics
|
|
20
21
|
- **[Slack Integration](guides/slack-integration.md)** - Messaging, formatting, and file uploads
|
|
21
|
-
- Elasticsearch
|
|
22
|
+
- **[Elasticsearch Integration](guides/elasticsearch-integration.md)** - Search, bulk indexing, health checks
|
|
22
23
|
- Database Connections (planned)
|
|
23
24
|
- Error Handling Patterns (planned)
|
|
24
25
|
- CloudWatch Metrics (planned)
|
|
@@ -32,8 +33,9 @@ Command-line utilities included with the package:
|
|
|
32
33
|
|
|
33
34
|
### 📋 Reference
|
|
34
35
|
|
|
35
|
-
API reference and detailed component documentation
|
|
36
|
+
API reference and detailed component documentation:
|
|
36
37
|
|
|
38
|
+
- **[Shared Types & Data Structures](guides/shared-types.md)** - Core types, interfaces, and response structures
|
|
37
39
|
- Client APIs (planned)
|
|
38
40
|
- Utility Functions (planned)
|
|
39
41
|
- Configuration Options (planned)
|
|
@@ -117,14 +119,17 @@ When contributing to documentation:
|
|
|
117
119
|
- Main documentation (this README)
|
|
118
120
|
- Getting started guides (installation, configuration, quickstart)
|
|
119
121
|
- AWS Powertools integration guide (guides/powertools-integration.md)
|
|
122
|
+
- Lambda context helpers guide (guides/lambda-utilities.md)
|
|
120
123
|
- Slack integration guide (guides/slack-integration.md)
|
|
124
|
+
- Elasticsearch integration guide (guides/elasticsearch-integration.md)
|
|
125
|
+
- Shared types reference (guides/shared-types.md)
|
|
121
126
|
- CLI tools guide (guides/cli-tools.md)
|
|
122
127
|
- Testing guide (development/testing.md)
|
|
123
128
|
- Configuration templates (Slack account names, channel setup)
|
|
124
129
|
|
|
125
130
|
### 🚧 Planned
|
|
126
131
|
|
|
127
|
-
- Component-specific guides (
|
|
132
|
+
- Component-specific guides (Database, Metrics, Error Handling)
|
|
128
133
|
- API reference documentation
|
|
129
134
|
- Advanced topics (AWS infrastructure, Lambda integration)
|
|
130
135
|
- Troubleshooting guide
|
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
# Elasticsearch Integration Guide
|
|
2
|
+
|
|
3
|
+
Comprehensive guide for using Elasticsearch utilities in `nui-lambda-shared-utils`.
|
|
4
|
+
|
|
5
|
+
**Last Updated**: 2026-01-30
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
The package provides Elasticsearch integration through:
|
|
10
|
+
|
|
11
|
+
- **ElasticsearchClient** - Search, aggregations, bulk indexing, health checks
|
|
12
|
+
- **ElasticsearchQueryBuilder** - Fluent query construction
|
|
13
|
+
|
|
14
|
+
## Quick Start
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
import nui_lambda_shared_utils as nui
|
|
18
|
+
|
|
19
|
+
# Configure Elasticsearch credentials
|
|
20
|
+
nui.configure(es_credentials_secret="prod/elasticsearch-credentials")
|
|
21
|
+
|
|
22
|
+
# Create client and search
|
|
23
|
+
es = nui.ElasticsearchClient()
|
|
24
|
+
results = es.search(
|
|
25
|
+
index="logs-*",
|
|
26
|
+
body={"query": {"match": {"level": "error"}}}
|
|
27
|
+
)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
Install with the elasticsearch extra:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install nui-lambda-shared-utils[elasticsearch]
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Configuration
|
|
39
|
+
|
|
40
|
+
### AWS Secrets Manager Setup
|
|
41
|
+
|
|
42
|
+
Create a secret with your Elasticsearch credentials:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
aws secretsmanager create-secret \
|
|
46
|
+
--name "elasticsearch-credentials" \
|
|
47
|
+
--description "Elasticsearch credentials" \
|
|
48
|
+
--secret-string '{"username":"elastic","password":"YOUR_PASSWORD"}'
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Secret Format:**
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
{
|
|
55
|
+
"username": "elastic",
|
|
56
|
+
"password": "YOUR_PASSWORD"
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Environment Variables
|
|
61
|
+
|
|
62
|
+
| Variable | Description | Default |
|
|
63
|
+
|----------|-------------|---------|
|
|
64
|
+
| `ES_HOST` | Elasticsearch host | `localhost:9200` |
|
|
65
|
+
| `ES_CREDENTIALS_SECRET` | Secret name for credentials | `elasticsearch-credentials` |
|
|
66
|
+
|
|
67
|
+
### Programmatic Configuration
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
import nui_lambda_shared_utils as nui
|
|
71
|
+
|
|
72
|
+
# Configure at startup
|
|
73
|
+
nui.configure(
|
|
74
|
+
es_host="elasticsearch.example.com:9200",
|
|
75
|
+
es_credentials_secret="prod/es-creds"
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# Or pass directly to client
|
|
79
|
+
client = nui.ElasticsearchClient(
|
|
80
|
+
host="elasticsearch.example.com:9200",
|
|
81
|
+
secret_name="prod/es-creds"
|
|
82
|
+
)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Basic Operations
|
|
86
|
+
|
|
87
|
+
### Search
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from nui_lambda_shared_utils import ElasticsearchClient
|
|
91
|
+
|
|
92
|
+
es = ElasticsearchClient()
|
|
93
|
+
|
|
94
|
+
# Simple search
|
|
95
|
+
results = es.search(
|
|
96
|
+
index="logs-*",
|
|
97
|
+
body={"query": {"match_all": {}}},
|
|
98
|
+
size=100
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
for doc in results:
|
|
102
|
+
print(doc["message"])
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Aggregations
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
# Get aggregation results
|
|
109
|
+
aggs = es.aggregate(
|
|
110
|
+
index="logs-*",
|
|
111
|
+
body={
|
|
112
|
+
"query": {"range": {"@timestamp": {"gte": "now-1h"}}},
|
|
113
|
+
"aggs": {
|
|
114
|
+
"by_level": {"terms": {"field": "level.keyword"}},
|
|
115
|
+
"avg_duration": {"avg": {"field": "duration"}}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
print(aggs["by_level"]["buckets"])
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Count Documents
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
# Count all documents
|
|
127
|
+
total = es.count(index="logs-*")
|
|
128
|
+
|
|
129
|
+
# Count with query
|
|
130
|
+
errors = es.count(
|
|
131
|
+
index="logs-*",
|
|
132
|
+
body={"query": {"term": {"level": "error"}}}
|
|
133
|
+
)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Bulk Indexing
|
|
137
|
+
|
|
138
|
+
### streaming_bulk Method
|
|
139
|
+
|
|
140
|
+
For efficient bulk indexing of documents, use the `streaming_bulk` method:
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
from nui_lambda_shared_utils import ElasticsearchClient
|
|
144
|
+
|
|
145
|
+
es = ElasticsearchClient()
|
|
146
|
+
|
|
147
|
+
def generate_documents():
|
|
148
|
+
"""Yield documents to index."""
|
|
149
|
+
for item in items:
|
|
150
|
+
yield {
|
|
151
|
+
"_index": "my-index",
|
|
152
|
+
"_source": {
|
|
153
|
+
"field1": item.field1,
|
|
154
|
+
"field2": item.field2,
|
|
155
|
+
"@timestamp": item.timestamp.isoformat()
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
# Index documents with automatic error handling
|
|
160
|
+
success, failed = es.streaming_bulk(
|
|
161
|
+
actions=generate_documents(),
|
|
162
|
+
chunk_size=100,
|
|
163
|
+
max_retries=2
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
print(f"Indexed {success} documents, {failed} failures")
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Parameters
|
|
170
|
+
|
|
171
|
+
| Parameter | Type | Default | Description |
|
|
172
|
+
|-----------|------|---------|-------------|
|
|
173
|
+
| `actions` | Iterator[Dict] | required | Iterator yielding action dictionaries |
|
|
174
|
+
| `chunk_size` | int | 100 | Documents per batch |
|
|
175
|
+
| `max_retries` | int | 2 | Retries for failed documents |
|
|
176
|
+
| `raise_on_error` | bool | False | Raise exception on first error |
|
|
177
|
+
|
|
178
|
+
### Action Dictionary Format
|
|
179
|
+
|
|
180
|
+
Each yielded action should contain:
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
{
|
|
184
|
+
"_index": "target-index-name",
|
|
185
|
+
"_source": {
|
|
186
|
+
# Your document fields
|
|
187
|
+
},
|
|
188
|
+
# Optional fields:
|
|
189
|
+
"_id": "custom-id", # Auto-generated if not provided
|
|
190
|
+
"_op_type": "index" # "index", "create", "update", "delete"
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Error Handling
|
|
195
|
+
|
|
196
|
+
By default, `streaming_bulk` logs errors but continues processing:
|
|
197
|
+
|
|
198
|
+
```python
|
|
199
|
+
# Default: log errors, continue processing
|
|
200
|
+
success, failed = es.streaming_bulk(generate_docs())
|
|
201
|
+
if failed > 0:
|
|
202
|
+
logger.warning(f"{failed} documents failed to index")
|
|
203
|
+
|
|
204
|
+
# Strict mode: raise on first error
|
|
205
|
+
try:
|
|
206
|
+
success, failed = es.streaming_bulk(
|
|
207
|
+
generate_docs(),
|
|
208
|
+
raise_on_error=True
|
|
209
|
+
)
|
|
210
|
+
except Exception as e:
|
|
211
|
+
logger.error(f"Bulk indexing failed: {e}")
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Integration with Log Processors
|
|
215
|
+
|
|
216
|
+
The `streaming_bulk` method pairs well with log extraction utilities:
|
|
217
|
+
|
|
218
|
+
```python
|
|
219
|
+
from nui_lambda_shared_utils import ElasticsearchClient
|
|
220
|
+
|
|
221
|
+
es = ElasticsearchClient()
|
|
222
|
+
|
|
223
|
+
def extract_logs_from_kinesis(records):
|
|
224
|
+
"""Extract logs from Kinesis records."""
|
|
225
|
+
for record in records:
|
|
226
|
+
# Your log extraction logic
|
|
227
|
+
yield {
|
|
228
|
+
"_index": f"logs-{service_name}-{date}",
|
|
229
|
+
"_source": log_data
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
def handler(event, context):
|
|
233
|
+
success, failed = es.streaming_bulk(
|
|
234
|
+
actions=extract_logs_from_kinesis(event["Records"]),
|
|
235
|
+
chunk_size=200
|
|
236
|
+
)
|
|
237
|
+
return {"indexed": success, "failed": failed}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## Service Statistics
|
|
241
|
+
|
|
242
|
+
Get comprehensive service statistics:
|
|
243
|
+
|
|
244
|
+
```python
|
|
245
|
+
stats = es.get_service_stats(
|
|
246
|
+
service="order",
|
|
247
|
+
hours=24,
|
|
248
|
+
index_prefix="logs"
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
print(f"Total requests: {stats['total_count']}")
|
|
252
|
+
print(f"Error rate: {stats['error_rate']:.2f}%")
|
|
253
|
+
print(f"P95 latency: {stats['p95_response_time']}ms")
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Recent Errors
|
|
257
|
+
|
|
258
|
+
Retrieve recent error logs:
|
|
259
|
+
|
|
260
|
+
```python
|
|
261
|
+
errors = es.get_recent_errors(
|
|
262
|
+
service="auth",
|
|
263
|
+
hours=1,
|
|
264
|
+
limit=10
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
for error in errors:
|
|
268
|
+
print(f"{error['@timestamp']}: {error['message']}")
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## Health Checks
|
|
272
|
+
|
|
273
|
+
### Cluster Health
|
|
274
|
+
|
|
275
|
+
```python
|
|
276
|
+
# Get cluster info
|
|
277
|
+
info = es.get_cluster_info()
|
|
278
|
+
print(f"Cluster: {info['cluster_name']}, Status: {info['cluster_status']}")
|
|
279
|
+
|
|
280
|
+
# Check health (raises on failure)
|
|
281
|
+
health = es.check_health()
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### Index Information
|
|
285
|
+
|
|
286
|
+
```python
|
|
287
|
+
indices = es.get_indices_info(pattern="logs-*")
|
|
288
|
+
for idx in indices:
|
|
289
|
+
print(f"{idx['index']}: {idx['docs.count']} docs, {idx['store.size']}")
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
## Query Builder
|
|
293
|
+
|
|
294
|
+
For complex queries, use the query builder:
|
|
295
|
+
|
|
296
|
+
```python
|
|
297
|
+
from nui_lambda_shared_utils import ElasticsearchQueryBuilder
|
|
298
|
+
|
|
299
|
+
query = (
|
|
300
|
+
ElasticsearchQueryBuilder()
|
|
301
|
+
.must_match("service", "order")
|
|
302
|
+
.must_range("@timestamp", gte="now-1h")
|
|
303
|
+
.filter_term("level", "error")
|
|
304
|
+
.sort("@timestamp", "desc")
|
|
305
|
+
.build()
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
results = es.search(index="logs-*", body=query)
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## Lambda Handler Pattern
|
|
312
|
+
|
|
313
|
+
Recommended pattern for Lambda functions:
|
|
314
|
+
|
|
315
|
+
```python
|
|
316
|
+
import os
|
|
317
|
+
import nui_lambda_shared_utils as nui
|
|
318
|
+
from nui_lambda_shared_utils import (
|
|
319
|
+
ElasticsearchClient,
|
|
320
|
+
get_powertools_logger,
|
|
321
|
+
powertools_handler
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
# Configure once at module level
|
|
325
|
+
nui.configure(
|
|
326
|
+
es_host=os.environ.get("ES_HOST"),
|
|
327
|
+
es_credentials_secret=os.environ.get("ES_CREDENTIALS_SECRET")
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
logger = get_powertools_logger("my-service")
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
@powertools_handler(service_name="my-service")
|
|
334
|
+
def handler(event, context):
|
|
335
|
+
es = ElasticsearchClient()
|
|
336
|
+
|
|
337
|
+
# Your indexing logic
|
|
338
|
+
success, failed = es.streaming_bulk(
|
|
339
|
+
actions=process_records(event["Records"])
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
logger.info(f"Indexed {success} documents, {failed} failures")
|
|
343
|
+
return {"statusCode": 200, "body": f"Processed {success} documents"}
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
## Troubleshooting
|
|
347
|
+
|
|
348
|
+
### Connection Issues
|
|
349
|
+
|
|
350
|
+
```python
|
|
351
|
+
# Test connectivity
|
|
352
|
+
try:
|
|
353
|
+
es = ElasticsearchClient()
|
|
354
|
+
info = es.get_cluster_info()
|
|
355
|
+
print(f"Connected to: {info['cluster_name']}")
|
|
356
|
+
except Exception as e:
|
|
357
|
+
print(f"Connection failed: {e}")
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### Common Errors
|
|
361
|
+
|
|
362
|
+
| Error | Cause | Solution |
|
|
363
|
+
|-------|-------|----------|
|
|
364
|
+
| `AuthenticationException` | Invalid credentials | Check secret values |
|
|
365
|
+
| `ConnectionError` | Host unreachable | Verify `ES_HOST` and network |
|
|
366
|
+
| `TransportError(403)` | Missing permissions | Check index permissions |
|
|
367
|
+
|
|
368
|
+
### Debug Logging
|
|
369
|
+
|
|
370
|
+
Enable debug logging for troubleshooting:
|
|
371
|
+
|
|
372
|
+
```python
|
|
373
|
+
import logging
|
|
374
|
+
logging.getLogger("elasticsearch").setLevel(logging.DEBUG)
|
|
375
|
+
```
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
# Lambda Context Helpers Guide
|
|
2
|
+
|
|
3
|
+
This guide covers the Lambda context helpers provided by `nui-lambda-shared-utils`. These utilities provide standardized environment info extraction for logging context, metric dimensions, and conditional behavior based on execution environment.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Installation](#installation)
|
|
8
|
+
- [Quick Start](#quick-start)
|
|
9
|
+
- [Function Reference](#function-reference)
|
|
10
|
+
- [Usage Patterns](#usage-patterns)
|
|
11
|
+
- [Environment Detection](#environment-detection)
|
|
12
|
+
- [Related Documentation](#related-documentation)
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
No additional dependencies required. Lambda helpers use only Python standard library:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# Base package includes lambda_helpers
|
|
20
|
+
pip install nui-lambda-shared-utils
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
from nui_lambda_shared_utils import get_lambda_environment_info
|
|
27
|
+
|
|
28
|
+
def handler(event, context):
|
|
29
|
+
env_info = get_lambda_environment_info()
|
|
30
|
+
|
|
31
|
+
# Use for conditional behavior
|
|
32
|
+
if env_info["is_local"]:
|
|
33
|
+
print("Running locally")
|
|
34
|
+
else:
|
|
35
|
+
print(f"Running in Lambda: {env_info['function_name']}")
|
|
36
|
+
|
|
37
|
+
return {"statusCode": 200}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Function Reference
|
|
41
|
+
|
|
42
|
+
### `get_lambda_environment_info()`
|
|
43
|
+
|
|
44
|
+
Extracts standard Lambda environment info from environment variables.
|
|
45
|
+
|
|
46
|
+
**Signature:**
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
def get_lambda_environment_info() -> Dict[str, str | bool]:
|
|
50
|
+
"""
|
|
51
|
+
Extract standard Lambda environment info.
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
Dict with keys:
|
|
55
|
+
- environment: "prod" | "dev" | "staging" | "unknown"
|
|
56
|
+
- aws_region: AWS region (e.g., "ap-southeast-2")
|
|
57
|
+
- function_name: Lambda function name
|
|
58
|
+
- function_version: Lambda function version (e.g., "$LATEST")
|
|
59
|
+
- memory_limit: Memory limit in MB (e.g., "512")
|
|
60
|
+
- is_local: True if running outside Lambda environment
|
|
61
|
+
"""
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Return Value Example:**
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
{
|
|
68
|
+
"environment": "prod",
|
|
69
|
+
"aws_region": "ap-southeast-2",
|
|
70
|
+
"function_name": "order-processor",
|
|
71
|
+
"function_version": "$LATEST",
|
|
72
|
+
"memory_limit": "512",
|
|
73
|
+
"is_local": False
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Environment Variable Sources:**
|
|
78
|
+
|
|
79
|
+
| Return Key | Primary Env Var | Fallback | Default |
|
|
80
|
+
|------------|-----------------|----------|---------|
|
|
81
|
+
| `environment` | `ENVIRONMENT` | `ENV`, `STAGE` | `"unknown"` |
|
|
82
|
+
| `aws_region` | `AWS_REGION` | `AWS_DEFAULT_REGION` | `""` |
|
|
83
|
+
| `function_name` | `AWS_LAMBDA_FUNCTION_NAME` | - | `""` |
|
|
84
|
+
| `function_version` | `AWS_LAMBDA_FUNCTION_VERSION` | - | `""` |
|
|
85
|
+
| `memory_limit` | `AWS_LAMBDA_FUNCTION_MEMORY_SIZE` | - | `""` |
|
|
86
|
+
| `is_local` | (absence of `AWS_LAMBDA_RUNTIME_API`) | - | `True` |
|
|
87
|
+
|
|
88
|
+
**Environment Normalization:**
|
|
89
|
+
|
|
90
|
+
The `environment` value is normalized for consistency:
|
|
91
|
+
|
|
92
|
+
- `"production"`, `"prd"` → `"prod"`
|
|
93
|
+
- `"development"` → `"dev"`
|
|
94
|
+
- All values are lowercased
|
|
95
|
+
|
|
96
|
+
## Usage Patterns
|
|
97
|
+
|
|
98
|
+
### With Powertools Logger
|
|
99
|
+
|
|
100
|
+
Add environment context to structured logs:
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
from nui_lambda_shared_utils import get_powertools_logger, get_lambda_environment_info
|
|
104
|
+
|
|
105
|
+
logger = get_powertools_logger("my-service")
|
|
106
|
+
|
|
107
|
+
def handler(event, context):
|
|
108
|
+
env_info = get_lambda_environment_info()
|
|
109
|
+
|
|
110
|
+
logger.info(
|
|
111
|
+
"Processing event",
|
|
112
|
+
extra={
|
|
113
|
+
**env_info,
|
|
114
|
+
"event_type": event.get("type")
|
|
115
|
+
}
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
return {"statusCode": 200}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### With CloudWatch Metrics
|
|
122
|
+
|
|
123
|
+
Add standard dimensions to metrics:
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
from nui_lambda_shared_utils import MetricsPublisher, get_lambda_environment_info
|
|
127
|
+
|
|
128
|
+
def handler(event, context):
|
|
129
|
+
env_info = get_lambda_environment_info()
|
|
130
|
+
|
|
131
|
+
metrics = MetricsPublisher(namespace="MyService")
|
|
132
|
+
metrics.add_dimension("Environment", env_info["environment"])
|
|
133
|
+
metrics.add_dimension("FunctionName", env_info["function_name"])
|
|
134
|
+
metrics.add_dimension("Region", env_info["aws_region"])
|
|
135
|
+
|
|
136
|
+
# Publish metrics with consistent dimensions
|
|
137
|
+
metrics.put_metric("ProcessedEvents", 1, "Count")
|
|
138
|
+
|
|
139
|
+
return {"statusCode": 200}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Conditional Behavior
|
|
143
|
+
|
|
144
|
+
Execute different code paths based on environment:
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
from nui_lambda_shared_utils import get_lambda_environment_info
|
|
148
|
+
|
|
149
|
+
def handler(event, context):
|
|
150
|
+
env_info = get_lambda_environment_info()
|
|
151
|
+
|
|
152
|
+
# Use different configuration for local vs Lambda
|
|
153
|
+
if env_info["is_local"]:
|
|
154
|
+
config = load_local_config()
|
|
155
|
+
else:
|
|
156
|
+
config = load_lambda_config()
|
|
157
|
+
|
|
158
|
+
# Environment-specific behavior
|
|
159
|
+
if env_info["environment"] == "prod":
|
|
160
|
+
send_to_production_queue(event)
|
|
161
|
+
else:
|
|
162
|
+
send_to_dev_queue(event)
|
|
163
|
+
|
|
164
|
+
return {"statusCode": 200}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Error Context Enhancement
|
|
168
|
+
|
|
169
|
+
Include environment info in error reports:
|
|
170
|
+
|
|
171
|
+
```python
|
|
172
|
+
from nui_lambda_shared_utils import (
|
|
173
|
+
get_powertools_logger,
|
|
174
|
+
get_lambda_environment_info,
|
|
175
|
+
SlackClient
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
logger = get_powertools_logger("my-service")
|
|
179
|
+
|
|
180
|
+
def handler(event, context):
|
|
181
|
+
env_info = get_lambda_environment_info()
|
|
182
|
+
|
|
183
|
+
try:
|
|
184
|
+
process_event(event)
|
|
185
|
+
except Exception as e:
|
|
186
|
+
logger.exception(
|
|
187
|
+
"Handler failed",
|
|
188
|
+
extra={
|
|
189
|
+
**env_info,
|
|
190
|
+
"error_type": type(e).__name__,
|
|
191
|
+
"error_message": str(e)
|
|
192
|
+
}
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
# Include environment context in Slack alerts
|
|
196
|
+
slack = SlackClient()
|
|
197
|
+
slack.send_message(
|
|
198
|
+
channel="#errors",
|
|
199
|
+
text=(
|
|
200
|
+
f"*Error in {env_info['function_name']}*\n"
|
|
201
|
+
f"Environment: {env_info['environment']}\n"
|
|
202
|
+
f"Region: {env_info['aws_region']}\n"
|
|
203
|
+
f"Error: {str(e)}"
|
|
204
|
+
)
|
|
205
|
+
)
|
|
206
|
+
raise
|
|
207
|
+
|
|
208
|
+
return {"statusCode": 200}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Environment Detection
|
|
212
|
+
|
|
213
|
+
### How `is_local` Works
|
|
214
|
+
|
|
215
|
+
The `is_local` flag detects whether code is running in a Lambda environment:
|
|
216
|
+
|
|
217
|
+
```python
|
|
218
|
+
# Detection logic
|
|
219
|
+
is_local = os.getenv("AWS_LAMBDA_RUNTIME_API") is None
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
**Detection Rules:**
|
|
223
|
+
|
|
224
|
+
| `AWS_LAMBDA_RUNTIME_API` | Result | Scenario |
|
|
225
|
+
|--------------------------|--------|----------|
|
|
226
|
+
| Not set | `is_local = True` | Local dev, unit tests |
|
|
227
|
+
| Set to any value | `is_local = False` | Lambda runtime |
|
|
228
|
+
|
|
229
|
+
**Note:** SAM Local sets `AWS_LAMBDA_RUNTIME_API` during `sam local invoke`. Use `AWS_SAM_LOCAL` to detect SAM specifically if needed.
|
|
230
|
+
|
|
231
|
+
### Testing Considerations
|
|
232
|
+
|
|
233
|
+
For unit tests, mock environment variables:
|
|
234
|
+
|
|
235
|
+
```python
|
|
236
|
+
import pytest
|
|
237
|
+
|
|
238
|
+
def test_handler_local_behavior(monkeypatch):
|
|
239
|
+
"""Test handler behavior when running locally"""
|
|
240
|
+
monkeypatch.delenv("AWS_LAMBDA_RUNTIME_API", raising=False)
|
|
241
|
+
|
|
242
|
+
from nui_lambda_shared_utils import get_lambda_environment_info
|
|
243
|
+
|
|
244
|
+
env_info = get_lambda_environment_info()
|
|
245
|
+
assert env_info["is_local"] is True
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def test_handler_lambda_behavior(monkeypatch):
|
|
249
|
+
"""Test handler behavior when running in Lambda"""
|
|
250
|
+
monkeypatch.setenv("AWS_LAMBDA_RUNTIME_API", "127.0.0.1:9001")
|
|
251
|
+
monkeypatch.setenv("ENVIRONMENT", "prod")
|
|
252
|
+
monkeypatch.setenv("AWS_REGION", "ap-southeast-2")
|
|
253
|
+
monkeypatch.setenv("AWS_LAMBDA_FUNCTION_NAME", "my-function")
|
|
254
|
+
|
|
255
|
+
from nui_lambda_shared_utils import get_lambda_environment_info
|
|
256
|
+
|
|
257
|
+
env_info = get_lambda_environment_info()
|
|
258
|
+
assert env_info["is_local"] is False
|
|
259
|
+
assert env_info["environment"] == "prod"
|
|
260
|
+
assert env_info["function_name"] == "my-function"
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## Related Documentation
|
|
264
|
+
|
|
265
|
+
- [Powertools Integration Guide](powertools-integration.md) - Logger and handler decorators
|
|
266
|
+
- [Quick Start Guide](../getting-started/quickstart.md) - Package usage patterns
|
|
267
|
+
- [CloudWatch Metrics](../getting-started/quickstart.md#cloudwatch-metrics) - Metrics publishing
|
|
268
|
+
|
|
269
|
+
## Support
|
|
270
|
+
|
|
271
|
+
For issues or questions:
|
|
272
|
+
|
|
273
|
+
- [GitHub Issues](https://github.com/nuimarkets/nui-lambda-shared-utils/issues)
|
|
274
|
+
- [Package Documentation](https://github.com/nuimarkets/nui-lambda-shared-utils)
|