Code Monkey home page Code Monkey logo

epsagon-python's Introduction


Build Status Pyversions PypiVersions

Epsagon Tracing for Python

Trace

This package provides tracing to Python applications for the collection of distributed tracing and performance metrics in Epsagon.

Contents

Installation

To install Epsagon, simply run:

pip install -U epsagon

Usage

Auto-tracing

The simplest way to get started is to run your python command with the following environment variable:

export EPSAGON_TOKEN=<epsagon-token>
export EPSAGON_APP_NAME=<app-name-stage>
export EPSAGON_METADATA=FALSE
export AUTOWRAPT_BOOTSTRAP=epsagon
<python command>

For example:

export EPSAGON_TOKEN=<your-token>
export EPSAGON_APP_NAME=django-prod
export EPSAGON_METADATA=FALSE
export AUTOWRAPT_BOOTSTRAP=epsagon
python app.py

When using inside a Dockerfile, you can use ENV instead of export.

You can see the list of auto-tracing supported frameworks

Calling the SDK

Another simple alternative is to copy the snippet into your code:

import epsagon
epsagon.init(
    token='epsagon-token',
    app_name='app-name-stage',
    metadata_only=False,
)

To run on your framework please refer to supported frameworks

Tagging Traces

You can add custom tags to your traces, for easier filtering and aggregations.

Add the following call inside your code:

epsagon.label('key', 'value')
epsagon.label('user_id', user_id)

You can also use it to ship custom metrics:

epsagon.label('key', 'metric')
epsagon.label('items_in_cart', items_in_cart)

Valid types are string, bool, int and float. In some frameworks tagging can be done in different ways.

Measuring Function Duration

You can measure internal functions duration by using the @epsagon.measure decorator. For example:

@epsagon.measure
def heavy_calculation():
    # Code...

This will ship another metric label to epsagon where the key=heavy_calculation_duration and the value will be the actual duration, in seconds. You'll be able to see this label in the trace search, visualize it over time, and generate alerts based on this metric.

Custom Errors

You can set a trace as an error (although handled correctly) to get an alert or just follow it on the dashboard.

Add the following call inside your code:

try:
    fail = 1 / 0
except Exception as ex:
    epsagon.error(ex)

# Or manually specify Exception object
epsagon.error(Exception('My custom error'))

In some frameworks custom errors can be declared in different ways.

Filter Sensitive Data

You can pass a list of sensitive properties and hostnames and they will be filtered out from the traces:

epsagon.init(
    token='epsagon-token',
    app_name='app-name-stage',
    metadata_only=False,
    keys_to_ignore=['password', 'user_name'],
    url_patterns_to_ignore=['example.com', 'auth.com']
)

Or specify keys that are allowed:

epsagon.init(
    token='epsagon-token',
    app_name='app-name-stage',
    metadata_only=False,
    keys_to_allow=['Request Data', 'Status_Code'],
)

The keys_to_ignore and keys_to_allow properties can contain strings (will perform a loose match, so that First Name also matches first_name). Also, you can set url_patterns_to_ignore to ignore HTTP calls to specific domains.

Ignore Endpoints

You can ignore certain incoming requests by specifying endpoints:

epsagon.init(
    token='epsagon-token',
    app_name='app-name-stage',
    metadata_only=False,
    ignored_endpoints=['/healthcheck'],
)

Trace URL

You can get the Epsagon dashboard URL for the current trace, using the following:

import epsagon

# Inside some endpoint or function
print('Epsagon trace URL:', epsagon.get_trace_url())

This can be useful to have an easy access the trace from different platforms.

Frameworks

The following frameworks are supported by Epsagon:

Framework Supported Version Auto-tracing Supported
AWS Lambda All
  • (Through the dashboard only)
Step Functions All
Generic All
Gunicorn >=20.0.4
Django >=1.11
Flask >=0.5
Tornado >=4.0
fastapi >=0.62.0
Celery >=4.0.0
Azure Functions >=2.0.0
Chalice >=1.0.0
Zappa >=0.30.0

AWS Lambda

Tracing Lambda functions can be done in three methods:

  1. Auto-tracing through the Epsagon dashboard.
  2. Using the serverless-plugin-epsagon if you're using The Serverless Framework.
  3. Calling the SDK.

Make sure to choose just one of the methods

Calling the SDK is simple:

import epsagon
epsagon.init(
    token='<epsagon-token>',
    app_name='<app-name-stage>',
    metadata_only=False,
)

# Wrap your entry point:
@epsagon.lambda_wrapper
def handle(event, context):
    # Your code is here

Step Functions

Tracing Step Functions is similar to regular Lambda functions, but the wrapper changes from lambda_wrapper to step_lambda_wrapper:

import epsagon
epsagon.init(
    token='<epsagon-token>',
    app_name='<app-name-stage>',
    metadata_only=False,
)

# Wrap your entry point:
@epsagon.step_lambda_wrapper
def handle(event, context):
    # Your code is here

Django

Tracing Django application can be done in two methods:

  1. Auto-tracing using the environment variable.
  2. Calling the SDK.

Calling the SDK is simple, and should be done in your main settings.py file where the application is being initialized:

import epsagon
epsagon.init(
    token='<epsagon-token>',
    app_name='<app-name-stage>',
    metadata_only=False,
)

Flask

Tracing Flask application can be done in two methods:

  1. Auto-tracing using the environment variable.
  2. Calling the SDK.

Calling the SDK is simple, and should be done in your main py file where the application is being initialized:

import epsagon
epsagon.init(
    token='<epsagon-token>',
    app_name='<app-name-stage>',
    metadata_only=False,
)

Tornado

Tracing Tornado application can be done in two methods:

  1. Auto-tracing using the environment variable.
  2. Calling the SDK.

Calling the SDK is simple, and should be done in your main py file where the application is being initialized:

import epsagon
epsagon.init(
    token='<epsagon-token>',
    app_name='<app-name-stage>',
    metadata_only=False,
)

fastapi

Tracing fastapi application can be done in two methods:

  1. Auto-tracing using the environment variable.
  2. Calling the SDK.

Calling the SDK is simple, and should be done in your main py file where the application is being initialized:

import epsagon
epsagon.init(
    token='<epsagon-token>',
    app_name='<app-name-stage>',
    metadata_only=False,
)

Celery

Tracing Celery consumer can be done in two methods:

  1. Auto-tracing using the environment variable.
  2. Calling the SDK.

Calling the SDK is simple, and should be done in your main py file where the consumer is being initialized:

import epsagon
epsagon.init(
    token='epsagon-token',
    app_name='app-name-stage',
    metadata_only=False,
)

Gunicorn

Tracing Gunicorn application can be done in two methods:

  1. Auto-tracing using the environment variable.
  2. Calling the SDK.

Calling the SDK is simple, and should be done in your main py file where the application is being initialized:

import epsagon
epsagon.init(
    token='<epsagon-token>',
    app_name='<app-name-stage>',
    metadata_only=False,
)

Azure Functions

Tracing Azure Functions can be done in the following method:

import azure.functions as func
import epsagon
epsagon.init(
    token='epsagon-token',
    app_name='app-name-stage',
    metadata_only=False,
)

@epsagon.azure_wrapper
def main(req):
    return func.HttpResponse('Success')

Chalice

Tracing Chalice applications running on Lambda functions can be done by:

from chalice import Chalice
import epsagon
epsagon.init(
    token='epsagon-token',
    app_name='app-name-stage',
    metadata_only=False
)
app = Chalice(app_name='hello-world')

# Your code is here

app = epsagon.chalice_wrapper(app)

Zappa

Tracing web applications running on Lambda functions using Zappa can be done by:

from zappa.handler import lambda_handler
import epsagon

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

# Your code is here

epsagon_handler = epsagon.lambda_wrapper(lambda_handler)

And in your zappa_settings.json file include the following:

{
  "lambda_handler": "module.path_to.epsagon_handler"
}

Generic

For any tracing, you can simply use the generic Epsagon wrapper using the following example:

import epsagon
epsagon.init(
    token='epsagon-token',
    app_name='app-name-stage',
    metadata_only=False,
)

# Wrap your entry point:
@epsagon.python_wrapper(name='my-resource')
def main(params):
    # Your code is here

Integrations

Epsagon provides out-of-the-box instrumentation (tracing) for many popular frameworks and libraries.

Library Supported Version
logging Fully supported
urllib Fully supported
urllib3 Fully supported
requests >=2.0.0
httplib2 >=0.9.2
redis >=2.10.0
pymongo >=3.0.0
pynamodb >=2.0.0
PyMySQL >=0.7.0
MySQLdb >=1.0.0
psycopg2 >=2.2.0
pg8000 >=1.9.0
botocore (boto3) >=1.4.0
azure.cosmos >=4.0.0
celery >=4.0.0
greengrasssdk >=1.4.0
SQLAlchemy >=1.2.0,<1.4.0
kafka-python >=1.4.0

Configuration

Advanced options can be configured as a parameter to the init() method or as environment variables.

Parameter Environment Variable Type Default Description
token EPSAGON_TOKEN String - Epsagon account token
app_name EPSAGON_APP_NAME String Application Application name that will be set for traces
metadata_only EPSAGON_METADATA Boolean True Whether to send only the metadata (True) or also the payloads (False)
use_ssl EPSAGON_SSL Boolean True Whether to send the traces over HTTPS SSL or not
collector_url EPSAGON_COLLECTOR_URL String - The address of the trace collector to send trace to
keys_to_ignore EPSAGON_IGNORED_KEYS List - List of keys names to be removed from the trace
keys_to_allow EPSAGON_ALLOWED_KEYS List - List of keys names to be included from the trace
ignored_endpoints EPSAGON_ENDPOINTS_TO_IGNORE List - List of endpoints to ignore from tracing (for example /healthcheck
url_patterns_to_ignore EPSAGON_URLS_TO_IGNORE List [] Array of URL patterns to ignore the calls
debug EPSAGON_DEBUG Boolean False Enable debug prints for troubleshooting
disable_timeout_send EPSAGON_DISABLE_ON_TIMEOUT Boolean False Disable timeout detection in Lambda functions
split_on_send EPSAGON_SPLIT_ON_SEND Boolean False Split the trace into multiple chunks to support large traces
propagate_lambda_id EPSAGON_PROPAGATE_LAMBDA_ID Boolean False Insert Lambda request ID into the response payload
logging_tracing_enabled EPSAGON_LOGGING_TRACING_ENABLED Boolean True Add Epsagon Log Id to all logging messages
step_dict_output_path EPSAGON_STEPS_OUTPUT_PATH List None Path in the result dict to append the Epsagon steps data
- EPSAGON_HTTP_ERR_CODE Integer 500 The minimum number of an HTTP response status code to treat as an error
- EPSAGON_SEND_TIMEOUT_SEC Float 1.0 The timeout duration in seconds to send the traces to the trace collector
- EPSAGON_DISABLE_LOGGING_ERRORS Boolean False Disable the automatic capture of error messages into logging
- EPSAGON_IGNORE_FLASK_RESPONSE Boolean False Disable the automatic capture of Flask response data
- EPSAGON_SKIP_HTTP_RESPONSE Boolean False Disable the automatic capture of http client response data
- DISABLE_EPSAGON Boolean False A flag to completely disable Epsagon (can be used for tests or locally)
- DISABLE_EPSAGON_PATCH Boolean False Disable the library patching (instrumentation)
- EPSAGON_LAMBDA_TIMEOUT_THRESHOLD_MS Integer 200 The threshold in millieseconds to send the trace before a Lambda timeout occurs
- EPSAGON_PAYLOADS_TO_IGNORE List - Array of dictionaries to not instrument. Example: '[{"source": "serverless-plugin-warmup"}]'
- EPSAGON_REMOVE_EXCEPTION_FRAMES Boolean False Disable the automatic capture of exception frames data (Python 3)
- EPSAGON_FASTAPI_ASYNC_MODE Boolean False Enable capturing of Fast API async endpoint handlers calls(Python 3)

Getting Help

If you have any issue around using the library or the product, please don't hesitate to:

  • Use the documentation.
  • Use the help widget inside the product.
  • Open an issue in GitHub.

Opening Issues

If you encounter a bug with the Epsagon library for Python, we want to hear about it.

When opening a new issue, please provide as much information about the environment:

  • Library version, Python runtime version, dependencies, etc.
  • Snippet of the usage.
  • A reproducible example can really help.

The GitHub issues are intended for bug reports and feature requests. For help and questions about Epsagon, use the help widget inside the product.

License

Provided under the MIT license. See LICENSE for details.

Copyright 2020, Epsagon

epsagon-python's People

Contributors

adavidai avatar alon-katz avatar dependabot[bot] avatar enoodle avatar galbash avatar glikson avatar henperez avatar idonava avatar liadgo avatar maorlx avatar ojongerius avatar ophiryael avatar ranrib avatar ronnathaniel avatar sagivr2020 avatar shimonuri avatar tvaintrob avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

epsagon-python's Issues

Dependency update

Hi guys,

While trying to install epsagon I faced issue with couple outdated dependencies:

epsagon 1.0.46 has requirement future==0.16.0, but you'll have future 0.17.1 which is incompatible.
epsagon 1.0.46 has requirement six==1.11.0, but you'll have six 1.12.0 which is incompatible.

It causes dependency conflict with other packages in my project.

Could you please update requirements if it is possible?

Missing stack trace since version 1.49.2

Hello. We're using epsagon-python in Django project with gunicorn server (worker_class = "gevent"). Since version 1.49.2, our Slack notifications aren't working as expected. Instead of actual error, we can see only "Error detected" message. I tried it locally with debug option set to True, and I can see, that in version 1.49.1 there was an exception object (with type, message, traceback and so on) inside an event object in trace being sent. In version 1.49.2 and newer, there is no "exception" key in that object.

Just need some info about communication from your service to our infra

Hi, this is not really a issue, I just needed more info on communication from your service to our serverless infra.
I tried asking in help widget but didn't get any reply.
We really liked this service and want to integrate it to monitor our serverless architecture,but our security team raised few questions over communication between your server and our infra.
We are bit confused about how do you send any updates made in dashboard to reflect in our infra, is it via cloudformation custom resource (SNS in this case)?

Thank you.
Sorry for raising this in an inappropriate place.

Epsagon client breaks `chalice local`

Hi there,
I've already talked to @ranrib on Slack about this issue.

I'm posting it here for keeping track of it.

epsagon wrapper for chalice breaks chalice local server:

unicorn_1     | Exception happened during processing of request from ('172.21.0.1', 43164)
unicorn_1     | Traceback (most recent call last):
unicorn_1     |   File "/usr/local/lib/python3.8/socketserver.py", line 650, in process_request_thread
unicorn_1     |     self.finish_request(request, client_address)
unicorn_1     |   File "/usr/local/lib/python3.8/socketserver.py", line 360, in finish_request
unicorn_1     |     self.RequestHandlerClass(request, client_address, self)
unicorn_1     |   File "/tmp/code-_Py8Si6I/lib/python3.8/site-packages/chalice/local.py", line 565, in __init__
unicorn_1     |     self.local_gateway = LocalGateway(app_object, config)
unicorn_1     |   File "/tmp/code-_Py8Si6I/lib/python3.8/site-packages/chalice/local.py", line 436, in __init__
unicorn_1     |     RouteMatcher(list(app_object.routes)),
unicorn_1     | AttributeError: 'LocalChalice' object has no attribute 'routes'
unicorn_1     | ----------------------------------------```

FastAPI Issue

After installing epsagon on my FastAPI application, I get the following error: (route) got an unexpected keyword argument 'epsagon_request'

My configuration is pretty straight forward:

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

Flaky test detected.

I have detected some flaky tests using pytest plugin flake finder, the details of this plugin can be found at:
https://github.com/dropbox/pytest-flakefinder
Flaky test:
test_transports.py::test_httptransport_timeout
location:

def test_httptransport_timeout():
    start_time = time.time()
    # non-routable IP address, will result in a timeout
    http_transport = HTTPTransport('http://10.255.255.1', 'token')
    trace = trace_factory.get_or_create_trace()

    # This will make sure we get TimeoutError and not MaxRetryError
    with pytest.raises(urllib3.exceptions.TimeoutError):
        http_transport.send(trace)

    duration = time.time() - start_time

    # Making sure that an unreachable url will result in duration almost equal to the
    # timeout duration set
    assert http_transport.timeout < duration < http_transport.timeout + 0.3

Where duration exceeds the timeout.
Snapshots of the error report:
FAILED tests/test_transports.py::test_httptransport_timeout[0] - assert 1.6475169658660889 < (1.0 + 0.3)
FAILED tests/test_transports.py::test_httptransport_timeout[1] - assert 1.4870219230651855 < (1.0 + 0.3)
FAILED tests/test_transports.py::test_httptransport_timeout[2] - assert 1.4910881519317627 < (1.0 + 0.3)
FAILED tests/test_transports.py::test_httptransport_timeout[3] - assert 1.4848833084106445 < (1.0 + 0.3)
FAILED tests/test_transports.py::test_httptransport_timeout[4] - assert 1.482806921005249 < (1.0 + 0.3)
FAILED tests/test_transports.py::test_httptransport_timeout[5] - assert 1.4943721294403076 < (1.0 + 0.3)
FAILED tests/test_transports.py::test_httptransport_timeout[6] - assert 1.500823974609375 < (1.0 + 0.3)
FAILED tests/test_transports.py::test_httptransport_timeout[7] - assert 1.4800028800964355 < (1.0 + 0.3)
FAILED tests/test_transports.py::test_httptransport_timeout[8] - assert 1.4843499660491943 < (1.0 + 0.3)
FAILED tests/test_transports.py::test_httptransport_timeout[9] - assert 1.4824299812316895 < (1.0 + 0.3)
FAILED tests/test_transports.py::test_httptransport_timeout[10] - assert 1.4858860969543457 < (1.0 + 0.3)
FAILED tests/test_transports.py::test_httptransport_timeout[11] - assert 1.488236665725708 < (1.0 + 0.3)
FAILED tests/test_transports.py::test_httptransport_timeout[12] - assert 1.505213975906372 < (1.0 + 0.3)
FAILED tests/test_transports.py::test_httptransport_timeout[13] - assert 1.5001637935638428 < (1.0 + 0.3)
FAILED tests/test_transports.py::test_httptransport_timeout[14] - assert 1.4944648742675781 < (1.0 + 0.3)
FAILED tests/test_transports.py::test_httptransport_timeout[15] - assert 1.4844911098480225 < (1.0 + 0.3)
How to re-appear the problem:
in the root directory, install the plugin:
pip install pytest-flakefinder
then run:
pytest --flake-finder
Conclusion:
I have encountered this flaky test, I have tried to read through the code but do not understand exactly why this happened, I am posting this as an issue to see if there is a solution to improve or this is designed on purpose.

boto3 GetObject and HeadObject Failure with python_wrapper

I am running a simple python server, with no framework, calling @epsagon.python_wrapper over the main function.
I use boto3 to make Put/Get/Head operations on an S3 bucket. Only Put operation registeres in the traces

I ran with debug and got the following error for each Head/Get:

[EPSAGON_DEBUG] Epsagon exception: Invalid format string
Traceback (most recent call last):
  File "C:\Git\Production2\glvideo\.venv\lib\site-packages\epsagon\modules\general_wrapper.py", line 35, in wrapper
    factory.create_event(
  File "C:\Git\Production2\glvideo\.venv\lib\site-packages\epsagon\events\botocore.py", line 2061, in create_event
    event = event_class(
  File "C:\Git\Production2\glvideo\.venv\lib\site-packages\epsagon\events\botocore.py", line 213, in __init__
    super(BotocoreS3Event, self).__init__(
  File "C:\Git\Production2\glvideo\.venv\lib\site-packages\epsagon\events\botocore.py", line 77, in __init__
    self.update_response(response)
  File "C:\Git\Production2\glvideo\.venv\lib\site-packages\epsagon\events\botocore.py", line 254, in update_response
    response['LastModified'].strftime('%s')
ValueError: Invalid format string
-----

The bug seems to be you are using:
response['LastModified'].strftime('%s')
Instead of:
response['LastModified'].strftime('%S')

Datetime/date cannot be serialised as JSON

Hi, we're looking at using Epsagon in our Lambdas but we're running into an odd issue. When a part of the trace built by Epsagon contains a datetime value, the trace fails to be uploaded to your service. Our suggestion is to use either specialist functions to serialise types like datetime (this is in our patch) and/or to fallback on str if the type is not serialisable (assuming that more types support str than JSON serialisation).

We've prepared a sample to reproduce the issue and an initial patch. I hope both help.

Sample:

from epsagon import lambda_wrapper
from datetime import date, datetime
from typing import Dict, List


@lambda_wrapper
def handler(event: Dict, context: Dict) -> List[Dict]:
    from_date = event.get("from_date") if event else None

    from_date = (
        datetime.strptime(from_date, "%Y-%m-%d")
        if from_date
        else date.today().replace(day=1)
    )

    return get(
        from_date=from_date,
    )

def get(from_date: datetime.date = None):
    # Use the datetime.
    print(from_date.isoformat())
    return [from_date.strftime("%Y-%m-%d")]

Patch:

--- build/epsagon/trace.py    2019-04-30 20:26:01.000000000 +0100
+++ trace.py    2019-04-30 20:21:34.000000000 +0100
@@ -12,6 +12,8 @@
 import pprint
 import simplejson as json

+
+from datetime import date, datetime
 import requests
 import requests.exceptions
 from epsagon.event import BaseEvent
@@ -355,7 +357,8 @@
             trace = json.dumps(
                 self.to_dict(),
                 cls=TraceEncoder,
-                encoding='latin1'
+                encoding='latin1',
+                default=json_serial
             )
             SESSION.post(
                 self.collector_url,
@@ -382,6 +385,10 @@
             if self.debug:
                 pprint.pprint(self.to_dict())

+def json_serial(obj):
+    """JSON serializer for objects not serializable by default json code"""

+    if isinstance(obj, (datetime, date)):
+        return obj.isoformat()
 # pylint: disable=C0103
 tracer = Trace()

tracing not working with celery worker

I didn't get the time to dig too far but I can't have epsagon traces with celery.

From what I've seen, the _get_trace function returns None, as the unique_id is None and the singleton_trace is also None.

I'm running celery with the following command:

celery -A <app_name> worker --beat --scheduler django

and I tried both the auto tracing config and calling epsagon.init directly, but the outcome is the same.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.