

# Integrating with Python
<a name="integrating-with-python"></a>

You can use Amazon CodeGuru Profiler to profile your Python application. Before you begin profiling your Python application, make sure your application is running on Python 3.6 or later.

**Topics**
+ [Profiling your applications that run on AWS Lambda](python-lambda.md)
+ [Enabling the agent with code](python-code-change.md)
+ [Enabling the agent from the command line](python-command-line.md)
+ [Profiling Distributed systems](python-distributed-systems.md)
+ [Enabling logs](python-enabling-logs.md)

# Profiling your applications that run on AWS Lambda
<a name="python-lambda"></a>

CodeGuru Profiler integration for AWS Lambda is currently available for applications that run on Python 3.7 up to Python 3.9. To start CodeGuru Profiler in your application running on Lambda, you can either apply the CodeGuru Profiler function decorator to your handler function, update your Lambda function configuration by adding layers, or enable profiling in the Lambda console.

If you enabled profiling in the Lambda console, you don't have to complete the procedure outlined in the following sections. To learn more about enabling profiling from the Lambda console, see [Set up in the Lambda console](setting-up-short.md).

**Note**  
You can profile your Lambda functions running in Python if they are called often enough for CodeGuru Profiler to gather enough samples. CodeGuru Profiler collects data once per second, aggregated into 5-minute sampling buckets. For Lambda functions running for fewer than 5 minutes, your application must run multiple times so CodeGuru Profiler can collect enough data. If it runs too infrequently, CodeGuru Profiler can't generate enough data to provide recommendations and flame graphs. For long-running Lambda applications, processing can take up to 15 minutes to display graphs and information. If you are running your application in shorter durations, processing takes longer to display information.

**Topics**
+ [Apply the CodeGuru Profiler function decorator to your handler function](python-lambda-command-line.md)
+ [Use AWS Lambda layers](python-lambda-layers.md)



# Apply the CodeGuru Profiler function decorator to your handler function
<a name="python-lambda-command-line"></a>

Pull your `codeguru_profiler_agent` dependency to your local environment through `pip` and include it in the .zip file for Lambda.

The only required configuration option to start the agent is the profiling group name. You can find this in the **Settings** section of your profiling group on the CodeGuru Profiler console. You can also provide the Region if you want to use a profiling group that was created in a different region than the one where Lambda is running. You can also provide the profiling group ARN directly, which contains both the name and Region. Either the profiling group name or ARN must be provided. 


| Option | Environment variable key | Environment variable value | Decorator argument example | 
| --- | --- | --- | --- | 
|  Profiling group name  |  `AWS_CODEGURU_PROFILER_GROUP_NAME`  |  `MyGroupName`  |  `@with_lambda_profiler(profiling_group_name="MyGroupName")`  | 
|  Profiling group ARN  |  `AWS_CODEGURU_PROFILER_GROUP_ARN`  |  `arn:aws:codeguru-profiler:us-east-1:123456789123:profilingGroup/MyGroupName`  |  An ARN cannot be passed as a decorator argument  | 
|  Region  |  `AWS_CODEGURU_PROFILER_TARGET_REGION`  |  `us-east-1`  |  `@with_lambda_profiler(region_name="us-east-1")`  | 

Decorate your handler function with `@with_lambda_profiler()`. The following example shows what your handler function code looks like with CodeGuru Profiler turned on.

```
from codeguru_profiler_agent import with_lambda_profiler

@with_lambda_profiler(profiling_group_name="MyGroupName")
def handler_name(event, context):
    return "Hello World"
```

Only decorate your handler function. You do not have to decorate other internal functions. You can pass the profiling group name directly in the decorator, or with environment variables.

# Use AWS Lambda layers
<a name="python-lambda-layers"></a>

There are two ways you can use layers to enable CodeGuru in Lambda functions using Python. The preferred method uses a wrapper script and only works for applications that run on Python 3.8 or 3.9. The second method works for Python 3.7, and can also be used for Python 3.8 and 3.9 if you already have a lambda wrapper or if the preferred method otherwise does not work.

**For applications that run on Python 3.8 or 3.9 (preferred)**

1. Add the CodeGuru Profiler layer to Lambda. Choose **Specify an ARN** and add `arn:aws:lambda:region:157417159150:layer:AWSCodeGuruProfilerPythonAgentLambdaLayer:11`. For more information on adding a Lambda layer, see [AWS Lambda layers](https://docs.aws.amazon.com/lambda/latest/dg/chapter-layers.html).

1. Add the following environment variable: `AWS_LAMBDA_EXEC_WRAPPER=/opt/codeguru_profiler_lambda_exec`

1. Add an environment variable with your profiling group name or ARN. For information on using your ARN, see the table listed in [Apply the CodeGuru Profiler function decorator to your handler function](https://docs.aws.amazon.com/codeguru/latest/profiler-ug/python-lambda-command-line.html).

**Note**  
You can only have one Lambda wrapper script. If you are currently using one, try the solution for applications that run on Python 3.7, 3.8 or 3.9.

**For applications that run on Python 3.7, 3.8 or 3.9**

1. Add the CodeGuru Profiler layer to Lambda. Choose **Specify an ARN** and `arn:aws:lambda:region:157417159150:layer:AWSCodeGuruProfilerPythonAgentLambdaLayer:11`. For more information on adding a Lambda layer, see [AWS Lambda layers](https://docs.aws.amazon.com/lambda/latest/dg/chapter-layers.html).

1. Set the environment variable, `HANDLER_ENV_NAME_FOR_CODEGURU` to your handler function.

1. Change the Lambda handler function to `codeguru_profiler_agent.aws_lambda.lambda_handler.call_handler`.

1. Add an environment variable with your profiling group name or ARN. For information on using your ARN, see the table listed in [Apply the CodeGuru Profiler function decorator to your handler function](https://docs.aws.amazon.com/codeguru/latest/profiler-ug/python-lambda-command-line).

**Note**  
You can only have up to five layers for a Lambda function. If you are already using five layers, see [Apply the CodeGuru Profiler function decorator to your handler function](https://docs.aws.amazon.com/codeguru/latest/profiler-ug/python-lambda-command-line).

# Enabling the agent with code
<a name="python-code-change"></a>

If your application runs on a platform other than Lambda, install `codeguru_profiler_agent` through `pip`.

```
pip install codeguru_profiler_agent
```

You can configure the agent by passing different parameters to the `Profiler` object.


| Option | Constructor argument | Details | 
| --- | --- | --- | 
|  Profiling group name (required)  |  `profiling_group_name="MyProfilingGroup"`  |  The name of the profiling group to send the data into. The Profiling group must exist.  | 
|  Region  |  `region_name="eu-west-2"`  |  Use this if your application is not running in the same region as the one where the profiling group was created.  | 
|  AWS session  |  `aws_session=boto3.session.Session()`  |  The session object that should be used to target the CodeGuru Profiler backends. Use this if you want to use different credentials or region than the default one. See [boto3 session](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/session.html) for more information.  | 

Start the agent from one place in your application. We recommend you start the agent in your startup code. Only one Profiler object can be started at the time. The following is a runtime example.

```
from codeguru_profiler_agent import Profiler
from boto3.session import Session
...
custom_session = Session(profile_name='dev', region_name='us-east-1')
Profiler(profiling_group_name='MyProfilingGroup', aws_session=custom_session).start()
start_application()
...
```

You can find the sample code for the following examples in [ Amazon CodeGuru Profiler Python Demo Applications](https://github.com/aws-samples/aws-codeguru-profiler-python-demo-application).

The following is an example of a simple application that sets your profiling group name to `MyProfilingGroup`.

```
from codeguru_profiler_agent import Profiler

if __name__ == '__main__':
    Profiler(profiling_group_name='MyProfilingGroup').start()
    start_application()
```

## Supported web components
<a name="supported-languages"></a>

The following topics provide code that you can add to your application to enable the Amazon CodeGuru Profiler agent.

**Topics**
+ [Django](python-django.md)
+ [Flask](python-flask.md)
+ [WSGI servers](python-wsgi.md)

# Django
<a name="python-django"></a>

Start the profiler in your settings file. This is usually the file that you’re setting for `DJANGO_SETTINGS_MODULE`. Then, start your application as usual.

The following is an example that you can add to `settings.py`

```
from codeguru_profiler_agent import Profiler
Profiler(profiling_group_name='MyProfilingGroup').start()
```

Set the following in your `wsgi.py` file. This example is for a module named `mysite`. Your files may be in a different location.

```
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')
```

Set the following in your `settings.py` file.

```
Profiler(profiling_group_name='MyProfilingGroup').start()
```

# Flask
<a name="python-flask"></a>

Start the profiler based on the configuration for your web server. For an example with gunicorn, see [WSGI servers](python-wsgi.md).

# WSGI servers
<a name="python-wsgi"></a>

Start the profiler based on the configuration for your web server.

## uWSGI
<a name="w2aac16c13c27b5"></a>

Configure CodeGuru Profiler in your `wsgi.py` file. Then, start your application by adding the `--enable-threads` and `--lazy-apps` parameters to your uWSGI startup configuration. These are required for CodeGuru Profiler to run in your uWSGI applications.

```
uwsgi --http :8000 --chdir . --wsgi-file wsgi.py --enable-threads --lazy-apps --workers=4
```

## gunicorn
<a name="w2aac16c13c27b7"></a>

Configure CodeGuru Profiler in your `post-fork` method. Then start the application as usual.

```
def post_fork(server, worker):
    server.log.info('Starting profiler for {} in {}'.format(os.getpid(), threading.get_ident()))
    worker.profiler = Profiler(profiling_group_name='MyProfilingGroup')
    worker.profiler.start()
    server.log.info('Profiler started running for worker pid {}: master pid {}.'.format(os.getpid(), worker.ppid))
```

## Apache
<a name="w2aac16c13c27b9"></a>

For Apache (httpd) with `mod_wsgi` module, use the same wsgi configuration. Make sure the `wsgi.py` file is configured to be visible in the `httpd.conf` file.

```
<Directory [to_be_replaced]>
<Files wsgi.py>
Require all granted
</Files>
</Directory>
```

Start your application with `apachectl start`.

# Enabling the agent from the command line
<a name="python-command-line"></a>

If your application runs on a platform other than Lambda, install `codeguru_profiler_agent` through `pip`.

```
pip install codeguru_profiler_agent
```

The only required configuration option to start the CodeGuru Profiler agent is the profiling group name. You can find this in the **Settings** section of your profiling group. You can use the credential profile name parameter to have the agent use credentials that are different from the default credentials. For more information about credential profiles, see [Shared credential files](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html). You can specify these options as an environment variable or as a command line option.


| Option | Environment variable | Command line option | 
| --- | --- | --- | 
|  Profiling group name (required)  |  `AWS_CODEGURU_PROFILER_GROUP_NAME`  |  `-p, --profiling-group-name`  | 
|  Region  |  `AWS_CODEGURU_PROFILER_TARGET_REGION`  |  `-r, --region`  | 
|  (Alternative) credential profile name  |  Not available  |  `-c, --credential-profile-name`  | 

The following is an example that uses environment variables. In this example, `my_script.py` is your main script that you would otherwise call directly with `python my_script.py`.

```
#!/bin/bash
export AWS_CODEGURU_PROFILER_GROUP_NAME=MyProfilingGroup
export AWS_CODEGURU_PROFILER_TARGET_REGION=us-west-2

python -m codeguru_profiler_agent my_script.py
```

The following is an example that uses command line arguments to specify the configuration options.

```
python -m codeguru_profiler_agent -p MyProfilingGroup -r us-west-2 \
-c prod-credential-profile my_script.py
```

You can find more details about each command line option by running it with `-h` to display the list of available options.

# Profiling Distributed systems
<a name="python-distributed-systems"></a>

Amazon CodeGuru Profiler offers limited support when implemented on distributed systems like Spark on EMR or Glue jobs running across clusters. In such cases, Profiler is able to profile the application running on the manager node; however, it may not be able to profile the part of the application running on the worker nodes. Please consult with your local technical representative for further clarifications. 

# Enabling logs
<a name="python-enabling-logs"></a>

The Amazon CodeGuru Profiler agent uses the `logging` library. It only uses logs at or below the `INFO` level. To see the logs, include `logging.basicConfig(level=logging.INFO)` or `logging.getLogger('codeguru_profiler_agent').setLevel(logging.INFO)` in your handler code.