Skip to content

Commit

Permalink
Merge pull request #378 from rollbar/bxsx/fastapi-starlette-integration
Browse files Browse the repository at this point in the history
ASGI-family integration pre-release improvements
  • Loading branch information
bxsx authored Jun 3, 2021
2 parents 5696a22 + ec518a7 commit eb93f3b
Show file tree
Hide file tree
Showing 12 changed files with 52 additions and 10 deletions.
6 changes: 6 additions & 0 deletions rollbar/contrib/asgi/middleware.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import logging
import sys

import rollbar
from .integration import IntegrationBase, integrate
from .types import ASGIApp, Receive, Scope, Send
from rollbar.lib._async import RollbarAsyncError, try_report

log = logging.getLogger(__name__)


@integrate(framework_name='asgi')
class ReporterMiddleware(IntegrationBase):
Expand All @@ -23,5 +26,8 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
try:
await try_report(exc_info)
except RollbarAsyncError:
log.warning(
'Failed to report asynchronously. Trying to report synchronously.'
)
rollbar.report_exc_info(exc_info)
raise
3 changes: 3 additions & 0 deletions rollbar/contrib/fastapi/routing.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ async def rollbar_route_handler(request: Request) -> Response:
try:
await try_report(exc_info, request)
except RollbarAsyncError:
log.warning(
'Failed to report asynchronously. Trying to report synchronously.'
)
rollbar.report_exc_info(exc_info, request)
raise

Expand Down
6 changes: 6 additions & 0 deletions rollbar/contrib/starlette/middleware.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import logging
import sys

from starlette import __version__
Expand All @@ -10,6 +11,8 @@
from rollbar.contrib.asgi.integration import integrate
from rollbar.lib._async import RollbarAsyncError, try_report

log = logging.getLogger(__name__)


@integrate(framework_name=f'starlette {__version__}')
class ReporterMiddleware(ASGIReporterMiddleware):
Expand All @@ -35,5 +38,8 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
try:
await try_report(exc_info, request)
except RollbarAsyncError:
log.warning(
'Failed to report asynchronously. Trying to report synchronously.'
)
rollbar.report_exc_info(exc_info, request)
raise
2 changes: 1 addition & 1 deletion rollbar/contrib/starlette/requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

if ContextVar:
_current_request: ContextVar[Optional[Request]] = ContextVar(
'request', default=None
'rollbar-request-object', default=None
)


Expand Down
6 changes: 3 additions & 3 deletions rollbar/examples/fastapi/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ async def localfunc(arg1, arg2, arg3):
@app.get('/error')
async def read_error():
await localfunc('func_arg1', 'func_arg2', 1)
return {'result': "You shouldn't be seeing this")
return {'result': "You shouldn't be seeing this"}


# Cause an uncaught exception to be sent to Rollbar
Expand All @@ -60,7 +60,7 @@ async def read_error():
@app.post('/body')
async def read_body():
cause_error_with_body
return {'result': "You shouldn't be seeing this")
return {'result': "You shouldn't be seeing this"}


# Cause an uncaught exception to be sent to Rollbar
Expand All @@ -70,7 +70,7 @@ async def read_body():
@app.post('/form')
async def read_form():
cause_error_with_form
return {'result': "You shouldn't be seeing this")
return {'result': "You shouldn't be seeing this"}


if __name__ == '__main__':
Expand Down
2 changes: 1 addition & 1 deletion rollbar/examples/fastapi/app_middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ async def localfunc(arg1, arg2, arg3):
@app.get('/error')
async def read_error():
await localfunc('func_arg1', 'func_arg2', 1)
return {'result': "You shouldn't be seeing this")
return {'result': "You shouldn't be seeing this"}


if __name__ == '__main__':
Expand Down
2 changes: 1 addition & 1 deletion rollbar/lib/_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
ContextVar = None

if ContextVar:
_ctx_handler = ContextVar('handler', default=None)
_ctx_handler = ContextVar('rollbar-handler', default=None)
else:
_ctx_handler = None

Expand Down
6 changes: 5 additions & 1 deletion rollbar/test/asgi_tests/test_middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,11 @@ def test_should_use_async_report_exc_info_if_any_async_handler(
self.assertFalse(sync_report_exc_info.called)

@unittest2.skipUnless(ASYNC_REPORT_ENABLED, 'Requires Python 3.6+')
@mock.patch('logging.Logger.warning')
@mock.patch('rollbar.lib._async.report_exc_info', new_callable=AsyncMock)
@mock.patch('rollbar.report_exc_info')
def test_should_use_sync_report_exc_info_if_non_async_handlers(
self, sync_report_exc_info, async_report_exc_info
self, sync_report_exc_info, async_report_exc_info, mock_log
):
import rollbar
from rollbar.contrib.asgi.middleware import ReporterMiddleware
Expand All @@ -118,6 +119,9 @@ def test_should_use_sync_report_exc_info_if_non_async_handlers(

self.assertFalse(async_report_exc_info.called)
self.assertTrue(sync_report_exc_info.called)
mock_log.assert_called_once_with(
'Failed to report asynchronously. Trying to report synchronously.'
)

def test_should_support_http_only(self):
from rollbar.contrib.asgi.middleware import ReporterMiddleware
Expand Down
11 changes: 11 additions & 0 deletions rollbar/test/async_tests/test_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,17 @@ def test_should_not_try_report_with_async_handler_if_non_async_handler(

async_report_exc_info.assert_not_called()

@mock.patch('rollbar.lib._async.httpx', None)
def test_try_report_should_raise_exc_if_httpx_package_is_missing(self):
import rollbar
from rollbar.lib._async import RollbarAsyncError, run, try_report

rollbar.SETTINGS['handler'] = 'httpx'
self.assertEqual(rollbar.SETTINGS['handler'], 'httpx')

with self.assertRaises(RollbarAsyncError):
run(try_report())

@mock.patch('asyncio.ensure_future')
def test_should_schedule_task_in_event_loop(self, ensure_future):
from rollbar.lib._async import call_later, coroutine
Expand Down
6 changes: 5 additions & 1 deletion rollbar/test/fastapi_tests/test_middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,10 +224,11 @@ async def root():
async_report_exc_info.assert_called_once()
sync_report_exc_info.assert_not_called()

@mock.patch('logging.Logger.warning')
@mock.patch('rollbar.lib._async.report_exc_info', new_callable=AsyncMock)
@mock.patch('rollbar.report_exc_info')
def test_should_use_sync_report_exc_info_if_non_async_handlers(
self, sync_report_exc_info, async_report_exc_info
self, sync_report_exc_info, async_report_exc_info, mock_log
):
from fastapi import FastAPI
import rollbar
Expand All @@ -253,6 +254,9 @@ async def root():

sync_report_exc_info.assert_called_once()
async_report_exc_info.assert_not_called()
mock_log.assert_called_once_with(
'Failed to report asynchronously. Trying to report synchronously.'
)

@unittest2.skipUnless(
sys.version_info >= (3, 6), 'Global request access requires Python 3.6+'
Expand Down
6 changes: 5 additions & 1 deletion rollbar/test/fastapi_tests/test_routing.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,10 +367,11 @@ async def root():
async_report_exc_info.assert_called_once()
sync_report_exc_info.assert_not_called()

@mock.patch('logging.Logger.warning')
@mock.patch('rollbar.lib._async.report_exc_info', new_callable=AsyncMock)
@mock.patch('rollbar.report_exc_info')
def test_should_use_sync_report_exc_info_if_non_async_handlers(
self, sync_report_exc_info, async_report_exc_info
self, sync_report_exc_info, async_report_exc_info, mock_log
):
from fastapi import FastAPI
import rollbar
Expand All @@ -396,6 +397,9 @@ async def root():

sync_report_exc_info.assert_called_once()
async_report_exc_info.assert_not_called()
mock_log.assert_called_once_with(
'Failed to report asynchronously. Trying to report synchronously.'
)

def test_should_enable_loading_route_handler_if_fastapi_version_is_sufficient(self):
from fastapi import FastAPI
Expand Down
6 changes: 5 additions & 1 deletion rollbar/test/starlette_tests/test_middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,10 +202,11 @@ async def root(request):
async_report_exc_info.assert_called_once()
sync_report_exc_info.assert_not_called()

@mock.patch('logging.Logger.warning')
@mock.patch('rollbar.lib._async.report_exc_info', new_callable=AsyncMock)
@mock.patch('rollbar.report_exc_info')
def test_should_use_sync_report_exc_info_if_non_async_handlers(
self, sync_report_exc_info, async_report_exc_info
self, sync_report_exc_info, async_report_exc_info, mock_log
):
from starlette.applications import Starlette
from starlette.testclient import TestClient
Expand All @@ -227,6 +228,9 @@ async def root(request):

sync_report_exc_info.assert_called_once()
async_report_exc_info.assert_not_called()
mock_log.assert_called_once_with(
'Failed to report asynchronously. Trying to report synchronously.'
)

@unittest2.skipUnless(
sys.version_info >= (3, 6), 'Global request access requires Python 3.6+'
Expand Down

0 comments on commit eb93f3b

Please sign in to comment.