Skip to content
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

Support non TornadoScopeManager for boto3 #119

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ services:
- "3306:3306"

localstack:
image: localstack/localstack
image: localstack/localstack:0.10.5
environment:
- SERVICES=dynamodb,s3
ports:
Expand Down
29 changes: 26 additions & 3 deletions opentracing_instrumentation/client_hooks/boto3.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
import logging

from opentracing.ext import tags
from opentracing.scope_managers.tornado import TornadoScopeManager
import opentracing.tracer
from tornado.stack_context import wrap as keep_stack_context

from opentracing_instrumentation import utils
from ..request_context import get_current_span, span_in_stack_context
from ..request_context import get_current_span, span_in_stack_context, span_in_context
from ._patcher import Patcher


try:
from boto3.resources.action import ServiceAction
from boto3.s3 import inject as s3_functions
Expand All @@ -25,6 +26,28 @@

logger = logging.getLogger(__name__)

class _DummyStackContext(object):
"""
Stack context that restores previous scope after exit.
Will be returned by helper `_span_in_stack_context` when tracer scope
manager is not `TornadoScopeManager`.
"""
def __init__(self, context):
self._context = context

def __enter__(self):
# Needed for compatibility with `span_in_stack_context`.
return lambda: None

def __exit__(self, exc_type, exc_val, exc_tb):
if self._context:
self._context.close()

def _span_in_stack_context(span):
if isinstance(opentracing.tracer.scope_manager, TornadoScopeManager):
return span_in_stack_context(span)
else:
return _DummyStackContext(span_in_context(span))

class Boto3Patcher(Patcher):
applicable = '_service_action_call' in globals()
Expand Down Expand Up @@ -134,7 +157,7 @@ def perform_call(self, original_func, kind, service_name, operation_name,
span.set_tag(tags.COMPONENT, 'boto3')
span.set_tag('boto3.service_name', service_name)

with span, span_in_stack_context(span):
with span, _span_in_stack_context(span):
try:
response = original_func(*args, **kwargs)
except ClientError as error:
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setup(
name='opentracing_instrumentation',
version='3.3.2.dev0',
version='3.3.2rc1',
author='Yuri Shkuro',
author_email='[email protected]',
description='Tracing Instrumentation using OpenTracing API '
Expand Down
10 changes: 10 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import opentracing
import pytest
from opentracing.scope_managers.tornado import TornadoScopeManager
from opentracing.scope_managers.asyncio import AsyncioScopeManager


def _get_tracers(scope_manager=None):
Expand Down Expand Up @@ -52,3 +53,12 @@ def thread_safe_tracer():
yield dummy_tracer
finally:
opentracing.tracer = old_tracer


@pytest.fixture
def thread_safe_tracer_non_tornado():
old_tracer, dummy_tracer = _get_tracers(AsyncioScopeManager())
try:
yield dummy_tracer
finally:
opentracing.tracer = old_tracer
18 changes: 17 additions & 1 deletion tests/opentracing_instrumentation/test_boto3.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def create_users_table(dynamodb):
@pytest.fixture
def dynamodb_mock():
import moto
with moto.mock_dynamodb2():
with moto.mock_dynamodb():
dynamodb = boto3.resource('dynamodb', region_name='us-east-1')
create_users_table(dynamodb)
yield dynamodb
Expand Down Expand Up @@ -175,24 +175,40 @@ def is_moto_presented():
def test_boto3_dynamodb(thread_safe_tracer, dynamodb):
_test_dynamodb(dynamodb, thread_safe_tracer)

@pytest.mark.skipif(not is_dynamodb_running(),
reason='DynamoDB is not running or cannot connect')
def test_boto3_dynamodb_non_tornado(thread_safe_tracer_non_tornado, dynamodb):
_test_dynamodb(dynamodb, thread_safe_tracer_non_tornado)

@pytest.mark.skipif(not is_moto_presented(),
reason='moto module is not presented')
def test_boto3_dynamodb_with_moto(thread_safe_tracer, dynamodb_mock):
_test_dynamodb(dynamodb_mock, thread_safe_tracer)

@pytest.mark.skipif(not is_moto_presented(),
reason='moto module is not presented')
def test_boto3_dynamodb_with_moto_non_tornado(thread_safe_tracer_non_tornado, dynamodb_mock):
_test_dynamodb(dynamodb_mock, thread_safe_tracer_non_tornado)

@pytest.mark.skipif(not is_s3_running(),
reason='S3 is not running or cannot connect')
def test_boto3_s3(s3, thread_safe_tracer):
_test_s3(s3, thread_safe_tracer)

@pytest.mark.skipif(not is_s3_running(),
reason='S3 is not running or cannot connect')
def test_boto3_s3_non_tornado(s3, thread_safe_tracer_non_tornado):
_test_s3(s3, thread_safe_tracer_non_tornado)

@pytest.mark.skipif(not is_moto_presented(),
reason='moto module is not presented')
def test_boto3_s3_with_moto(s3_mock, thread_safe_tracer):
_test_s3(s3_mock, thread_safe_tracer)

@pytest.mark.skipif(not is_moto_presented(),
reason='moto module is not presented')
def test_boto3_s3_with_moto_non_tornado(s3_mock, thread_safe_tracer_non_tornado):
_test_s3(s3_mock, thread_safe_tracer_non_tornado)

@testfixtures.log_capture()
def test_boto3_s3_missing_func_instrumentation(capture):
Expand Down