# Epsagon Instrumentation for Python
[![Build Status](https://travis-ci.com/epsagon/epsagon-python.svg?token=wsveVqcNtBtmq6jpZfSf&branch=master)](https://travis-ci.com/epsagon/epsagon-python)
[![Pyversions](https://img.shields.io/pypi/pyversions/epsagon.svg?style=flat)](https://pypi.org/project/epsagon/)
[![PypiVersions](https://img.shields.io/pypi/v/epsagon.svg)](https://pypi.org/project/epsagon/)

This package provides an instrumentation to Python code running on functions for collection of distributed tracing and performance monitoring.

- [Installation](https://github.com/epsagon/epsagon-python#installation)
- [Usage](https://github.com/epsagon/epsagon-python#usage)
  - [AWS Lambda](https://github.com/epsagon/epsagon-python#aws-lambda)
  - [Django Application](https://github.com/epsagon/epsagon-python#django-application)
  - [Flask Application](https://github.com/epsagon/epsagon-python#flask-application)
  - [Tornado Application](https://github.com/epsagon/epsagon-python#tornado-application)
  - [Generic Python](https://github.com/epsagon/epsagon-python#generic-python)
  - [Auto-tracing](https://github.com/epsagon/epsagon-python#auto-tracing)
- [Custom Data](https://github.com/epsagon/epsagon-python#custom-data)
  - [Custom Labels](https://github.com/epsagon/epsagon-python#custom-labels)
  - [Custom Errors](https://github.com/epsagon/epsagon-python#custom-errors)
  - [Ignore Keys](https://github.com/epsagon/epsagon-python#ignore-keys)
- [Frameworks Integration](https://github.com/epsagon/epsagon-python#frameworks-integration)
  - [Serverless](https://github.com/epsagon/epsagon-python#serverless)
  - [Chalice](https://github.com/epsagon/epsagon-python#chalice)
  - [Zappa](https://github.com/epsagon/epsagon-python#zappa)
- [Copyright](https://github.com/epsagon/epsagon-python#copyright)


## Installation

From your project directory:

```
$ pip install epsagon
```

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

Make sure to add `epsagon` under your `requirements.txt` file.

### AWS Lambda

Simply use our decorator to report metrics:

```python
import epsagon
epsagon.init(
    token='my-secret-token',
    app_name='my-app-name',
    metadata_only=False,  # Optional, send more trace data
)

@epsagon.lambda_wrapper
def handler(event, context):
  pass
```

### Django Application

Add the following code to the `settings.py` file:
```python
import epsagon
epsagon.init(
    token='my-secret-token',
    app_name='my-app-name',
    metadata_only=False,  # Optional, send more trace data
)
```


For web frameworks: Use ignored_endpoints to blacklist specific paths and prevent Epsagon from sending a trace.
```python
import epsagon
epsagon.init(
    ...
    ignored_endpoints=['/path', '/path/to/ignore']
)
```

### Flask Application

Use the example snippet:
```python
from flask import Flask
import epsagon

epsagon.init(
    token='my-secret-token',
    app_name='my-app-name',
    metadata_only=False
)

app = Flask(__name__)

@app.route('/')
def hello():
    return "Hello World!"

app.run()
```

### Tornado Application

Use the example snippet:
```python
import tornado.ioloop
import tornado.web
import epsagon

epsagon.init(
    token='my-secret-token',
    app_name='my-app-name',
    metadata_only=False
)


class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write('Hello, world')


def make_app():
    return tornado.web.Application([
        (r'/', MainHandler),
    ])


if __name__ == '__main__':
    app = make_app()
    app.listen(8888)
    tornado.ioloop.IOLoop.current().start()
```

### Generic Python

Use the example snippet:
```python
import epsagon
epsagon.init(
    token='my-secret-token',
    app_name='my-app-name',
    metadata_only=False
)


@epsagon.python_wrapper
def main():
    return 'It worked!'
  
main()
```

### Auto-tracing

You can apply Epsagon tracing without any code changes using:

```bash
AUTOWRAPT_BOOTSTRAP=epsagon <command>
```

For example:

```bash
AUTOWRAPT_BOOTSTRAP=epsagon python app.py
```
### Configuration
You can customize your library usage using flags. The flags
should be set as enviroment variables in your code runtime enviroment.

EPSAGON_SEND_TIMEOUT_SEC - Set a custom trace send timeout.
EPSAGON_HTTP_ERR_CODE - Minimum HTTP status to be treated as an error.
EPSAGON_SSL - TRUE / FALSE. Disable SSL for trace send. Default is TRUE.
EPSAGON_ENDPOINTS_TO_IGNORE - Endpoints to ignore, comma seperated. aka: "endpoint1, endpoint2"
EPSAGON_TOKEN - Account Epsagon token.
EPSAGON_APP_NAME - Application name that will be set for traces.
EPSAGON_METADATA - TRUE / FALSE. Whether to send all collected data, or just metadata. Default is FALSE.
EPSAGON_SPLIT_ON_SEND - TRUE / FALSE. Split big traces into multiple parts. Default is FALSE.

### Lambda specific flags
EPSAGON_DISABLE_ON_TIMEOUT - TRUE / FALSE. Don't send trace on timeout. Default is FALSE.



## Custom Data

### Custom Labels

You can add custom labels to your traces. Filters can later be used for filtering
traces that contains specific labels:
```python
def handler(event, context):
    epsagon.label('key', 'value')
    epsagon.label('user_id', event['headers']['auth'])
    epsagon.label('number_of_records_parsed_successfully', 42)
```

### Custom Errors

You can manually set a trace as an error, even if handled correctly.
Please refer to the full documentation, about handling of this errors in the issues management.

```python
def handler(event, context):
    try:
        fail = 1 / 0
    except Exception as ex:
        epsagon.error(ex)
        
    # or

    if 'my_param' not in event:
        epsagon.error(ValueError('event missing my_param'))
        # or
        epsagon.error('event missing my_param')
```


### Ignore keys

You can prevent data from being sent to epsagon by filtering specific keys in initialization.
```python
import epsagon
epsagon.init(
    token='my-secret-token',
    app_name='my-app-name',
    metadata_only=False,
    keys_to_ignore=['Request Data', 'Status_Code']
)
```
## Frameworks Integration

When using any of the following integrations, make sure to add `epsagon` under your `requirements.txt` file.

### Serverless

Using Epsagon with [Serverless](https://github.com/serverless/serverless) is simple, by using the [serverless-plugin-epsagon](https://github.com/epsagon/serverless-plugin-epsagon).

### Chalice

Using Epsagon with [Chalice](https://github.com/aws/chalice) is simple, follow this example:

```python
from chalice import Chalice
import epsagon
epsagon.init(
    token='my-secret-token',
    app_name='my-app-name',
    metadata_only=False
)
app = Chalice(app_name="hello-world")


@app.route("/")
def index():
    return {"hello": "world"}

app = epsagon.chalice_wrapper(app)
```

or In S3 trigger example:
```python
from chalice import Chalice

app = Chalice(app_name="helloworld")

import epsagon
epsagon.init(
    token='my-secret-token',
    app_name='my-app-name',
    metadata_only=False
)
# Whenever an object is uploaded to 'mybucket'
# this lambda function will be invoked.

@epsagon.lambda_wrapper
@app.on_s3_event(bucket='mybucket')
def handler(event):
    print("Object uploaded for bucket: %s, key: %s"
          % (event.bucket, event.key))
```

### Zappa

Using Epsagon with [Zappa](https://github.com/Miserlou/Zappa) is simple, follow this example:

```python
from flask import Flask
from zappa.handler import lambda_handler
import epsagon

epsagon.init(
    token='my-secret-token',
    app_name='my-app-name',
    metadata_only=False
)
app = Flask(__name__)


@app.route('/')
def hello_world():
    return 'Hello, World!'


epsagon_handler = epsagon.lambda_wrapper(lambda_handler)
```

And in your `zappa_settings.json` file include the following:
```json
{
  "lambda_handler": "module.path_to.epsagon_handler"
}
```

## Copyright


Provided under the MIT license. See LICENSE for details.

Copyright 2019, Epsagon.