# IOpipe Analytics & Distributed Tracing Agent for Python
[![Build Status][1]][2] [![Code Coverage][3]][4] [![PyPI Version][5]][6] [![Apache 2.0][7]][8] [![Slack][9]][10]
[1]: https://circleci.com/gh/iopipe/iopipe-python.svg?style=svg
[2]: https://circleci.com/gh/iopipe/iopipe-python
[3]: https://codecov.io/gh/iopipe/iopipe-python/branch/master/graph/badge.svg
[4]: https://codecov.io/gh/iopipe/iopipe-python
[5]: https://badge.fury.io/py/iopipe.svg
[6]: https://badge.fury.io/py/iopipe
[7]: https://img.shields.io/badge/License-Apache%202.0-blue.svg
[8]: https://github.com/iopipe/iopipe-python/blob/master/LICENSE
[9]: https://img.shields.io/badge/chat-slack-ff69b4.svg
[10]: https://iopipe.now.sh
This package provides analytics and distributed tracing for event-driven applications running on AWS Lambda.
- [Installation](https://github.com/iopipe/iopipe-python#installation)
- [Usage](https://github.com/iopipe/iopipe-python#usage)
- [Configuration](https://github.com/iopipe/iopipe-python#configuration)
- [Reporting Exceptions](https://github.com/iopipe/iopipe-python#reporting-exceptions)
- [Custom Metrics](https://github.com/iopipe/iopipe-python#custom-metrics)
- [Labels](https://github.com/iopipe/iopipe-python#labels)
- [Core Agent](https://github.com/iopipe/iopipe-python#core-agent)
- [Plugins](https://github.com/iopipe/iopipe-python#plugins)
- [Event Info Plugin](https://github.com/iopipe/iopipe-python#event-info-plugin)
- [Logger Plugin](https://github.com/iopipe/iopipe-python#logger-plugin)
- [Profiler Plugin](https://github.com/iopipe/iopipe-python#profiler-plugin)
- [Trace Plugin](https://github.com/iopipe/iopipe-python#trace-plugin)
- [Creating Plugins](https://github.com/iopipe/iopipe-python#creating-plugins)
- [Supported Python Versions](https://github.com/iopipe/iopipe-python#supported-python-versions)
- [Lambda Layers](https://github.com/iopipe/iopipe-python#lambda-layers)
- [Framework Integration](https://github.com/iopipe/iopipe-python#framework-integration)
- [Chalice](https://github.com/iopipe/iopipe-python#chalice)
- [Serverless](https://github.com/iopipe/iopipe-python#serverless)
- [Zappa](https://github.com/iopipe/iopipe-python#zappa)
- [Contributing](https://github.com/iopipe/iopipe-python#contributing)
- [Running Tests](https://github.com/iopipe/iopipe-python#running-tests)
- [License](https://github.com/iopipe/iopipe-python#license)
## Installation
We expect you will import this library into an existing (or new) Python project intended to be run on AWS Lambda. On Lambda, functions are expected to include module dependencies within their project paths, thus we use `-t $PWD`. Users building projects with a requirements.txt file may simply add `iopipe` to their dependencies.
From your project directory:
```bash
$ pip install iopipe -t .
# If running locally or in other environments _besides_ AWS Lambda:
$ pip install jmespath>=0.7.1,<1.0.0 requests -t .
```
Your folder structure for the function should look similar to:
```
index.py # contains your lambda handler
/iopipe
- __init__.py
- iopipe.py
/requests
- __init__.py
- api.py
- ...
```
Installation of the requests library is necessary for local dev/test, but not when running on AWS Lambda as this library is part of the default environment via the botocore library.
More details about lambda deployments are available in the [AWS documentation](https://docs.aws.amazon.com/lambda/latest/dg/lambda-python-how-to-create-deployment-package.html).
## Usage
Simply use our decorator to report metrics:
```python
from iopipe import IOpipe
iopipe = IOpipe('your project token here')
@iopipe
def handler(event, context):
pass
```
The agent comes preloaded with the [Event Info](https://github.com/iopipe/iopipe-python#event-info-plugin), [Profiler](https://github.com/iopipe/iopipe-python#profiler-plugin) and [Trace](https://github.com/iopipe/iopipe-python#trace-plugin) plugins. See the relevant plugin sections for usage.
### Configuration
The following may be set as kwargs to the IOpipe class initializer:
#### `token` (string: required)
Your IOpipe project token. If not supplied, the environment variable `$IOPIPE_TOKEN` will be used if present. [Find your project token](https://dashboard.iopipe.com/install)
#### `debug` (bool: optional = False)
Debug mode will log all data sent to IOpipe servers. This is also a good way to evaluate the sort of data that IOpipe is receiving from your application. If not supplied, the environment variable `IOPIPE_DEBUG` will be used if present.
#### `enabled` (bool: optional = True)
Conditionally enable/disable the agent. For example, you will likely want to disabled the agent during development. The environment variable `$IOPIPE_ENABLED` will also be checked.
#### `network_timeout` (int: optional = 5000)
The number of milliseconds IOpipe will wait while sending a report before timing out. If not supplied, the environment variable `$IOPIPE_NETWORK_TIMEOUT` will be used if present.
#### `timeout_window` (int: optional = 500)
By default, IOpipe will capture timeouts by exiting your function 500 milliseconds early from the AWS configured timeout, to allow time for reporting. You can disable this feature by setting `timeout_window` to `0` in your configuration. If not supplied, the environment variable `$IOPIPE_TIMEOUT_WINDOW` will be used if present.
### Reporting Exceptions
The IOpipe decorator will automatically catch, trace and reraise any uncaught exceptions in your function. If you want to trace exceptions raised in your case, you can use the `.error(exception)` method. This will add the exception to the current report.
```python
from iopipe import IOpipe
iopipe = IOpipe()
# Example 1: uncaught exceptions
@iopipe
def handler(event, context):
raise Exception('This exception will be added to the IOpipe report automatically')
# Example 2: caught exceptions
@iopipe
def handler(event, context):
try:
raise Exception('This exception is being caught by your function')
except Exception as e:
# Makes sure the exception is added to the report
context.iopipe.error(e)
```
It is important to note that a report is sent to IOpipe when `error()` is called. So you should only record exceptions this way for failure states. For caught exceptions that are not a failure state, it is recommended to use custom metrics (see below).
### Custom Metrics
You can log custom values in the data sent upstream to IOpipe using the following syntax:
```python
from iopipe import IOpipe
iopipe = IOpipe()
@iopipe
def handler(event, context):
# the name of the metric must be a string
# numerical (int, long, float) and string types supported for values
context.iopipe.metric('my_metric', 42)
```
Metric key names are limited to 128 characters, and string values are limited to 1024 characters.
### Labels
Label invocations sent to IOpipe by calling the `label` method with a string (limit of 128 characters):
```python
from iopipe import IOpipe
iopipe = IOpipe()
@iopipe
def handler(event, context):
# the name of the tag must be a string
context.iopipe.label('this-invocation-is-special')
```
### Core Agent
By default, the IOpipe agent comes pre-loaded with all the bundled plugins in `iopipe.contrib.*`. If you prefer to run the agent without plugins or configure which plugins are used, you can use `IOpipeCore`:
```python
from iopipe import IOpipeCore
from iopipe.contrib.trace import TracePlugin
# Load IOpipe with only the trace plugin
iopipe = IOpipeCore(plugins=[TracePlugin()])
@iopipe
def handler(event, context):
pass
```
## Plugins
IOpipe's functionality can be extended through plugins. Plugins hook into the agent lifecycle to allow you to perform additional analytics.
### Event Info Plugin
The IOpipe agent comes bundled with an event info plugin that automatically extracts useful information from the `event`
object and creates custom metrics for them.
Here's an example of h