Skip to content

docs(tutorial): add new tutorial covering core features #769

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 45 commits into from
Jan 15, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
7392794
Add quickstart section in Readme.md
Oct 18, 2021
73da181
Move quickstart section to the documentation
Oct 25, 2021
86c9f96
Rewrite Quickstart section to follow narrative approach
Nov 28, 2021
fb24c83
Merge branch 'develop' into docs/quick-start
mploski Dec 2, 2021
abb7919
Rephrase multiple sections of quickstart and fix style errors
Dec 17, 2021
cc96e96
Add PR suggestions
Dec 22, 2021
2e465bb
Merge branch 'docs/quick-start' of github.com:mploski/aws-lambda-powe…
Dec 22, 2021
72c954b
Add PR suggestions
Dec 22, 2021
a3f6831
Add PR suggestions
Jan 5, 2022
4a6d081
Update docs/quickstart.md
mploski Jan 11, 2022
ae5414b
Update docs/quickstart.md
mploski Jan 11, 2022
52c629f
Update docs/quickstart.md
mploski Jan 11, 2022
74ee6da
Update docs/quickstart.md
mploski Jan 11, 2022
9f4ee51
Update docs/quickstart.md
mploski Jan 11, 2022
f665b87
Merge branch 'develop' into docs/quick-start
Jan 11, 2022
d23e582
Add some fixes based on PR comments
Jan 11, 2022
0e7b2eb
Move project structure into different section
Jan 13, 2022
4858b28
Fixing templates indentation
Jan 13, 2022
917f8bb
Fix code formatting and use json output
Jan 14, 2022
639b9e1
Add actioniable suggestion to implement authorization for quickstart …
Jan 14, 2022
e1a8f2c
Add tip about using different API testing tools
Jan 14, 2022
9b60f22
Rephrase intro in API Gateway router section
Jan 14, 2022
7371c68
Additional text tweaks
Jan 14, 2022
f24e265
docs(quickstart): tidy requirements up
heitorlessa Jan 14, 2022
bb71ce5
docs(quickstart): expand on intro line
heitorlessa Jan 14, 2022
0951f8d
docs(quickstart): sentence fragmentation, tidy up
heitorlessa Jan 14, 2022
6584364
docs(quickstart): add sub-sections, fix highlight & code
heitorlessa Jan 14, 2022
9cca95c
docs(quickstart): same process for Logger
heitorlessa Jan 14, 2022
9343fa9
docs(quickstart): make section agnostic to json lib
heitorlessa Jan 15, 2022
f03b0fc
fix: remove f-strings that doesn't evaluate expr
heitorlessa Jan 15, 2022
4b94856
fix: incorrect log keys, indentation, snippet consistency
heitorlessa Jan 15, 2022
3af2cac
revert: order to APP logger/service name due to screenshots
heitorlessa Jan 15, 2022
b096f5f
docs(tracer): warning to note on local traces
heitorlessa Jan 15, 2022
9e9b2cd
fix: use decorators, split cold start to ease reading
heitorlessa Jan 15, 2022
931de51
fix: remove apigw contract when using event-handler, apigw tracing
heitorlessa Jan 15, 2022
1b307d2
docs(tracer): add initial image, requirements
heitorlessa Jan 15, 2022
12b35f5
docs(tracer): add annotation, metadata, and image
heitorlessa Jan 15, 2022
f35321f
docs(tracer): update ServiceLens image w/ API GW, copywriting
heitorlessa Jan 15, 2022
d2cb261
fix: remove unused json import
heitorlessa Jan 15, 2022
411ee85
docs(metrics): keep it consistent with other sections, update metric …
heitorlessa Jan 15, 2022
f6e2b92
docs: add final consideration section
heitorlessa Jan 15, 2022
5618a99
docs: rename to tutorial given the size
heitorlessa Jan 15, 2022
4fd97ea
Merge branch 'develop' into docs/quick-start
heitorlessa Jan 15, 2022
d2925a3
docs: rename quickstart to tutorial in readme
heitorlessa Jan 15, 2022
e5e23d1
Add small style improvements and corrections
Jan 15, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ A suite of Python utilities for AWS Lambda functions to ease adopting best pract



**[📜Documentation](https://awslabs.github.io/aws-lambda-powertools-python/)** | **[🐍PyPi](https://pypi.org/project/aws-lambda-powertools/)** | **[Roadmap](https://github.com/awslabs/aws-lambda-powertools-roadmap/projects/1)** | **[Quick hello world example](https://github.com/aws-samples/cookiecutter-aws-sam-python)** | **[Detailed blog post](https://aws.amazon.com/blogs/opensource/simplifying-serverless-best-practices-with-lambda-powertools/)**
**[📜Documentation](https://awslabs.github.io/aws-lambda-powertools-python/)** | **[🐍PyPi](https://pypi.org/project/aws-lambda-powertools/)** | **[Roadmap](https://github.com/awslabs/aws-lambda-powertools-roadmap/projects/1)** | **[Detailed blog post](https://aws.amazon.com/blogs/opensource/simplifying-serverless-best-practices-with-lambda-powertools/)**

> **An AWS Developer Acceleration (DevAx) initiative by Specialist Solution Architects | aws-devax-open-source@amazon.com**

Expand All @@ -29,12 +29,15 @@ A suite of Python utilities for AWS Lambda functions to ease adopting best pract
* **[Idempotency](https://awslabs.github.io/aws-lambda-powertools-python/latest/utilities/idempotency/)** - Convert your Lambda functions into idempotent operations which are safe to retry
* **[Feature Flags](https://awslabs.github.io/aws-lambda-powertools-python/latest/utilities/feature_flags/)** - A simple rule engine to evaluate when one or multiple features should be enabled depending on the input


### Installation

With [pip](https://pip.pypa.io/en/latest/index.html) installed, run: ``pip install aws-lambda-powertools``

## Examples

## Quickstart and Examples

* [Quickstart](https://awslabs.github.io/aws-lambda-powertools-python/quickstart)
* [Serverless Shopping cart](https://github.com/aws-samples/aws-serverless-shopping-cart)
* [Serverless Airline](https://github.com/aws-samples/aws-serverless-airline-booking)
* [Serverless E-commerce platform](https://github.com/aws-samples/aws-serverless-ecommerce-platform)
Expand All @@ -47,7 +50,7 @@ With [pip](https://pip.pypa.io/en/latest/index.html) installed, run: ``pip insta

## Connect

* **AWS Developers Slack**: `#lambda-powertools`** - **[Invite, if you don't have an account](https://join.slack.com/t/awsdevelopers/shared_invite/zt-gu30gquv-EhwIYq3kHhhysaZ2aIX7ew)**
* **AWS Developers Slack**: `#lambda-powertools`**
* **Email**: aws-lambda-powertools-feedback@amazon.com

## License
Expand Down
Binary file added docs/media/metrics_utility_showcase.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/media/tracer_utility_showcase_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
273 changes: 273 additions & 0 deletions docs/quickstart.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
# Installation
With [SAM](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) installed, let's create powertools example project.
=== "shell"
```bash
sam init --location https://github.com/aws-samples/cookiecutter-aws-sam-python
```
Inside of the project directory you will find README.md file with detailed information how to use and deploy the code. Let's go through the code and discuss implemented functionalities in details.

## Code example
Looking at the code included in an app.py file from our hello world project we see 4 Lambda Powertools features included. It is tracing, logging, custom metrics and event handler routing. Our hello-world application consist of few visible sections. First in line 1-6 imports all necessary lambda Powertools packages. Second in line 8-10 institutes all features. Last section creates 3 methods (hello, hello_you and lambda_handler). Every of them is extended with Powertools functionality instantiated in section two by using decorators.

=== "app.py"
```python
from aws_lambda_powertools import Logger, Metrics, Tracer
from aws_lambda_powertools.logging import correlation_paths
from aws_lambda_powertools.metrics import MetricUnit
from aws_lambda_powertools.utilities.typing import LambdaContext
from aws_lambda_powertools.event_handler.api_gateway import ApiGatewayResolver

tracer = Tracer()
logger = Logger()
metrics = Metrics()
app = ApiGatewayResolver()

@app.get("/hello")
@tracer.capture_method
def hello():
query_string_name = app.current_event.get_query_string_value(name="name", default_value="universe")
logger.info(f"Message returned: {query_string_name}")
return {"message": f"hello {query_string_name}"}


@app.get("/hello/<name>")
@tracer.capture_method
def hello_you(name):
metrics.add_metric(name="SuccessfulMessageGeneration", unit=MetricUnit.Count, value=1)
tracer.put_annotation(key="path", value=f"/hello/{name}")
return {"message": f"hello {name}"}


@metrics.log_metrics(capture_cold_start_metric=True)
@logger.inject_lambda_context(correlation_id_path=correlation_paths.API_GATEWAY_REST, log_event=True)
@tracer.capture_lambda_handler
def lambda_handler(event, context: LambdaContext):
try:
return app.resolve(event, context)
except Exception as e:
logger.exception(e)
raise
```

## Structured Logging
Build the package and invoke lambda code locally.
=== "bash"
```bash
make build && make run
```

=== "app.py"
```python hl_lines="1 2 8 16 28 35"
from aws_lambda_powertools import Logger, Metrics, Tracer
from aws_lambda_powertools.logging import correlation_paths
from aws_lambda_powertools.metrics import MetricUnit
from aws_lambda_powertools.utilities.typing import LambdaContext
from aws_lambda_powertools.event_handler.api_gateway import ApiGatewayResolver

tracer = Tracer()
logger = Logger()
metrics = Metrics()
app = ApiGatewayResolver()

@app.get("/hello")
@tracer.capture_method
def hello():
query_string_name = app.current_event.get_query_string_value(name="name", default_value="universe")
logger.info(f"Message returned: {query_string_name}")
return {"message": f"hello {query_string_name}"}


@app.get("/hello/<name>")
@tracer.capture_method
def hello_you(name):
metrics.add_metric(name="SuccessfulMessageGeneration", unit=MetricUnit.Count, value=1)
tracer.put_annotation(key="path", value=f"/hello/{name}")
return {"message": f"hello {name}"}


@metrics.log_metrics(capture_cold_start_metric=True)
@logger.inject_lambda_context(correlation_id_path=correlation_paths.API_GATEWAY_REST, log_event=True)
@tracer.capture_lambda_handler
def lambda_handler(event, context: LambdaContext):
try:
return app.resolve(event, context)
except Exception as e:
logger.exception(e)
raise
```

As a result, we should see two records in our logs following the same structured pattern. First one includes the whole event and context thanks to `inject_lambda_context` decorator. Second is the result of invoking `logger.info` in `hello` method.
=== "Example CloudWatch Log"
```json
{"level":"INFO","location":"hello:17","message":"Message returned: universe","timestamp":"2021-10-22 16:29:58,367+0000","service":"hello","sampling_rate":"0.1","cold_start":true,"function_name":"HelloWorldFunction","function_memory_size":"256","function_arn":"arn:aws:lambda:us-east-1:012345678912:function:HelloWorldFunction","function_request_id":"d50bb07a-7712-4b2d-9f5d-c837302221a2","correlation_id":"bf9b584c-e5d9-4ad5-af3d-db953f2b10dc"}
```

## X-Ray Tracing

=== "app.py"
```python hl_lines="1 7 13 21 30"
from aws_lambda_powertools import Logger, Metrics, Tracer
from aws_lambda_powertools.logging import correlation_paths
from aws_lambda_powertools.metrics import MetricUnit
from aws_lambda_powertools.utilities.typing import LambdaContext
from aws_lambda_powertools.event_handler.api_gateway import ApiGatewayResolver

tracer = Tracer()
logger = Logger()
metrics = Metrics()
app = ApiGatewayResolver()

@app.get("/hello")
@tracer.capture_method
def hello():
query_string_name = app.current_event.get_query_string_value(name="name", default_value="universe")
logger.info(f"Message returned: {query_string_name}")
return {"message": f"hello {query_string_name}"}


@app.get("/hello/<name>")
@tracer.capture_method
def hello_you(name):
metrics.add_metric(name="SuccessfulMessageGeneration", unit=MetricUnit.Count, value=1)
tracer.put_annotation(key="path", value=f"/hello/{name}")
return {"message": f"hello {name}"}


@metrics.log_metrics(capture_cold_start_metric=True)
@logger.inject_lambda_context(correlation_id_path=correlation_paths.API_GATEWAY_REST, log_event=True)
@tracer.capture_lambda_handler
def lambda_handler(event, context: LambdaContext):
try:
return app.resolve(event, context)
except Exception as e:
logger.exception(e)
raise
```

Configure aws credentials to ensure you deploy it to the specific account then build and deploy your code. Next invoke your remote lambda.
=== "bash"
```bash
make deploy.guided && aws lambda invoke --function-name <function-name> response.json
```

As a result, you should see traces for your lambda in X-RAY console.
![Tracer utility](./media/tracer_utility_showcase_2.png)

## Custom Metrics
Let's extend our code with application metrics.

=== "app.py"
```python hl_lines="1 9 23"
from aws_lambda_powertools import Logger, Metrics, Tracer
from aws_lambda_powertools.logging import correlation_paths
from aws_lambda_powertools.metrics import MetricUnit
from aws_lambda_powertools.utilities.typing import LambdaContext
from aws_lambda_powertools.event_handler.api_gateway import ApiGatewayResolver

tracer = Tracer()
logger = Logger()
metrics = Metrics()
app = ApiGatewayResolver()

@app.get("/hello")
@tracer.capture_method
def hello():
query_string_name = app.current_event.get_query_string_value(name="name", default_value="universe")
logger.info(f"Message returned: {query_string_name}")
return {"message": f"hello {query_string_name}"}


@app.get("/hello/<name>")
@tracer.capture_method
def hello_you(name):
metrics.add_metric(name="SuccessfulMessageGeneration", unit=MetricUnit.Count, value=1)
tracer.put_annotation(key="path", value=f"/hello/{name}")
return {"message": f"hello {name}"}


@metrics.log_metrics(capture_cold_start_metric=True)
@logger.inject_lambda_context(correlation_id_path=correlation_paths.API_GATEWAY_REST, log_event=True)
@tracer.capture_lambda_handler
def lambda_handler(event, context: LambdaContext):
try:
return app.resolve(event, context)
except Exception as e:
logger.exception(e)
raise
```
In order to see CloudWatch Embedded Metric Format logs you can invoke lambda locally.
=== "shell"
```shell
make build && make invoke
```

=== "Example CloudWatch EMF Log"
```json
{"_aws":{"Timestamp":1634299249318,"CloudWatchMetrics":[{"Namespace":"ApplicationMetrics","Dimensions":[["service"]],"Metrics":[{"Name":"SuccessfulMessageGeneration","Unit":"Count"}]}]},"service":"exampleAPP","SuccessfulMessageGeneration":[1.0]}
```
if you deploy this changes into your account those logs will be picked up by cloudwatch and corresponding metrics will be generated.
=== "shell"
```bash
make build && make deploy
```
![Custom Metrics](./media/metrics_utility_showcase.png)

## Event Handler for Amazon API Gateway
You might also add event handler router capabilities into your lambda. This way you might configure different methods in your lambda code to be triggered by different API paths.

=== "app.py"
```python hl_lines="5 10 12 20 33"
from aws_lambda_powertools import Logger, Metrics, Tracer
from aws_lambda_powertools.logging import correlation_paths
from aws_lambda_powertools.metrics import MetricUnit
from aws_lambda_powertools.utilities.typing import LambdaContext
from aws_lambda_powertools.event_handler.api_gateway import ApiGatewayResolver

tracer = Tracer()
logger = Logger()
metrics = Metrics()
app = ApiGatewayResolver()

@app.get("/hello")
@tracer.capture_method
def hello():
query_string_name = app.current_event.get_query_string_value(name="name", default_value="universe")
logger.info(f"Message returned: {query_string_name}")
return {"message": f"hello {query_string_name}"}


@app.get("/hello/<name>")
@tracer.capture_method
def hello_you(name):
metrics.add_metric(name="SuccessfulMessageGeneration", unit=MetricUnit.Count, value=1)
tracer.put_annotation(key="path", value=f"/hello/{name}")
return {"message": f"hello {name}"}


@metrics.log_metrics(capture_cold_start_metric=True)
@logger.inject_lambda_context(correlation_id_path=correlation_paths.API_GATEWAY_REST, log_event=True)
@tracer.capture_lambda_handler
def lambda_handler(event, context: LambdaContext):
try:
return app.resolve(event, context)
except Exception as e:
logger.exception(e)
raise
```

Let's test it locally. Run following command in one shell.
=== "shell"
```sh
make build && make invoke
```
and trigger `/hello` and `/hello/<name>` endpoints in second.

=== "shell"
```sh
curl http://127.0.0.1:3000/hello
{"message":"hello universe"}
curl http://127.0.0.1:3000/hello/john
{"message":"hello john"}
curl http://127.0.0.1:3000/hello/anna
{"message":"hello anna"}
```
7 changes: 4 additions & 3 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@ edit_uri: edit/develop/docs
nav:
- Homepage: index.md
- Changelog: changelog.md
- Quickstart: quickstart.md
- Roadmap: https://github.com/awslabs/aws-lambda-powertools-roadmap/projects/1" target="_blank
- API reference: api/" target="_blank
- Core utilities:
- core/tracer.md
- core/logger.md
- core/metrics.md
- Event Handler:
- core/event_handler/appsync.md
- core/event_handler/api_gateway.md
- core/event_handler/appsync.md
- core/event_handler/api_gateway.md
- Utilities:
- utilities/middleware_factory.md
- utilities/parameters.md
Expand Down Expand Up @@ -60,7 +61,7 @@ markdown_extensions:
- pymdownx.superfences
- pymdownx.details
- pymdownx.snippets:
base_path: '.'
base_path: "."
check_paths: true
- meta
- toc:
Expand Down