Catch more errors in frontend/backend (#3346)
This commit is contained in:
parent
656914edef
commit
ec72448b8b
@ -1068,13 +1068,12 @@ async def process(
|
|||||||
client_ip: The client_ip.
|
client_ip: The client_ip.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
ReflexError: If a reflex specific error occurs during processing the event.
|
Exception: If a reflex specific error occurs during processing the event.
|
||||||
|
|
||||||
Yields:
|
Yields:
|
||||||
The state updates after processing the event.
|
The state updates after processing the event.
|
||||||
"""
|
"""
|
||||||
from reflex.utils import telemetry
|
from reflex.utils import telemetry
|
||||||
from reflex.utils.exceptions import ReflexError
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Add request data to the state.
|
# Add request data to the state.
|
||||||
@ -1118,8 +1117,8 @@ async def process(
|
|||||||
|
|
||||||
# Yield the update.
|
# Yield the update.
|
||||||
yield update
|
yield update
|
||||||
except ReflexError as ex:
|
except Exception as ex:
|
||||||
telemetry.send("error", context="backend", detail=str(ex))
|
telemetry.send_error(ex, context="backend")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
@ -4,12 +4,14 @@ Only the app attribute is explicitly exposed.
|
|||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
|
||||||
from reflex import constants
|
from reflex import constants
|
||||||
|
from reflex.utils import telemetry
|
||||||
from reflex.utils.exec import is_prod_mode
|
from reflex.utils.exec import is_prod_mode
|
||||||
from reflex.utils.prerequisites import get_app
|
from reflex.utils.prerequisites import get_app
|
||||||
|
|
||||||
if "app" != constants.CompileVars.APP:
|
if "app" != constants.CompileVars.APP:
|
||||||
raise AssertionError("unexpected variable name for 'app'")
|
raise AssertionError("unexpected variable name for 'app'")
|
||||||
|
|
||||||
|
telemetry.send("compile")
|
||||||
app_module = get_app(reload=False)
|
app_module = get_app(reload=False)
|
||||||
app = getattr(app_module, constants.CompileVars.APP)
|
app = getattr(app_module, constants.CompileVars.APP)
|
||||||
# For py3.8 and py3.9 compatibility when redis is used, we MUST add any decorator pages
|
# For py3.8 and py3.9 compatibility when redis is used, we MUST add any decorator pages
|
||||||
@ -29,5 +31,6 @@ del app_module
|
|||||||
del compile_future
|
del compile_future
|
||||||
del get_app
|
del get_app
|
||||||
del is_prod_mode
|
del is_prod_mode
|
||||||
|
del telemetry
|
||||||
del constants
|
del constants
|
||||||
del ThreadPoolExecutor
|
del ThreadPoolExecutor
|
||||||
|
@ -1476,7 +1476,6 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|||||||
StateUpdate object
|
StateUpdate object
|
||||||
"""
|
"""
|
||||||
from reflex.utils import telemetry
|
from reflex.utils import telemetry
|
||||||
from reflex.utils.exceptions import ReflexError
|
|
||||||
|
|
||||||
# Get the function to process the event.
|
# Get the function to process the event.
|
||||||
fn = functools.partial(handler.fn, state)
|
fn = functools.partial(handler.fn, state)
|
||||||
@ -1516,8 +1515,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
error = traceback.format_exc()
|
error = traceback.format_exc()
|
||||||
print(error)
|
print(error)
|
||||||
if isinstance(ex, ReflexError):
|
telemetry.send_error(ex, context="backend")
|
||||||
telemetry.send("error", context="backend", detail=str(ex))
|
|
||||||
yield state._as_state_update(
|
yield state._as_state_update(
|
||||||
handler,
|
handler,
|
||||||
window_alert("An error occurred. See logs for details."),
|
window_alert("An error occurred. See logs for details."),
|
||||||
|
@ -233,9 +233,8 @@ def get_app(reload: bool = False) -> ModuleType:
|
|||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
RuntimeError: If the app name is not set in the config.
|
RuntimeError: If the app name is not set in the config.
|
||||||
exceptions.ReflexError: Reflex specific errors.
|
|
||||||
"""
|
"""
|
||||||
from reflex.utils import exceptions, telemetry
|
from reflex.utils import telemetry
|
||||||
|
|
||||||
try:
|
try:
|
||||||
os.environ[constants.RELOAD_CONFIG] = str(reload)
|
os.environ[constants.RELOAD_CONFIG] = str(reload)
|
||||||
@ -259,8 +258,8 @@ def get_app(reload: bool = False) -> ModuleType:
|
|||||||
importlib.reload(app)
|
importlib.reload(app)
|
||||||
|
|
||||||
return app
|
return app
|
||||||
except exceptions.ReflexError as ex:
|
except Exception as ex:
|
||||||
telemetry.send("error", context="frontend", detail=str(ex))
|
telemetry.send_error(ex, context="frontend")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import asyncio
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
import platform
|
import platform
|
||||||
|
|
||||||
@ -157,17 +158,7 @@ def _send_event(event_data: dict) -> bool:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def send(event: str, telemetry_enabled: bool | None = None, **kwargs) -> bool:
|
def _send(event, telemetry_enabled, **kwargs):
|
||||||
"""Send anonymous telemetry for Reflex.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
event: The event name.
|
|
||||||
telemetry_enabled: Whether to send the telemetry (If None, get from config).
|
|
||||||
kwargs: Additional data to send with the event.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Whether the telemetry was sent successfully.
|
|
||||||
"""
|
|
||||||
from reflex.config import get_config
|
from reflex.config import get_config
|
||||||
|
|
||||||
# Get the telemetry_enabled from the config if it is not specified.
|
# Get the telemetry_enabled from the config if it is not specified.
|
||||||
@ -182,3 +173,36 @@ def send(event: str, telemetry_enabled: bool | None = None, **kwargs) -> bool:
|
|||||||
if not event_data:
|
if not event_data:
|
||||||
return False
|
return False
|
||||||
return _send_event(event_data)
|
return _send_event(event_data)
|
||||||
|
|
||||||
|
|
||||||
|
def send(event: str, telemetry_enabled: bool | None = None, **kwargs):
|
||||||
|
"""Send anonymous telemetry for Reflex.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
event: The event name.
|
||||||
|
telemetry_enabled: Whether to send the telemetry (If None, get from config).
|
||||||
|
kwargs: Additional data to send with the event.
|
||||||
|
"""
|
||||||
|
|
||||||
|
async def async_send(event, telemetry_enabled, **kwargs):
|
||||||
|
return _send(event, telemetry_enabled, **kwargs)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Within an event loop context, send the event asynchronously.
|
||||||
|
asyncio.create_task(async_send(event, telemetry_enabled, **kwargs))
|
||||||
|
except RuntimeError:
|
||||||
|
# If there is no event loop, send the event synchronously.
|
||||||
|
_send(event, telemetry_enabled, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def send_error(error: Exception, context: str):
|
||||||
|
"""Send an error event.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
error: The error to send.
|
||||||
|
context: The context of the error (e.g. "frontend" or "backend")
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Whether the telemetry was sent successfully.
|
||||||
|
"""
|
||||||
|
return send("error", detail=type(error).__name__, context=context)
|
||||||
|
@ -29,7 +29,7 @@ def test_telemetry():
|
|||||||
|
|
||||||
def test_disable():
|
def test_disable():
|
||||||
"""Test that disabling telemetry works."""
|
"""Test that disabling telemetry works."""
|
||||||
assert not telemetry.send("test", telemetry_enabled=False)
|
assert not telemetry._send("test", telemetry_enabled=False)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("event", ["init", "reinit", "run-dev", "run-prod", "export"])
|
@pytest.mark.parametrize("event", ["init", "reinit", "run-dev", "run-prod", "export"])
|
||||||
@ -43,7 +43,7 @@ def test_send(mocker, event):
|
|||||||
)
|
)
|
||||||
mocker.patch("platform.platform", return_value="Mocked Platform")
|
mocker.patch("platform.platform", return_value="Mocked Platform")
|
||||||
|
|
||||||
telemetry.send(event, telemetry_enabled=True)
|
telemetry._send(event, telemetry_enabled=True)
|
||||||
httpx.post.assert_called_once()
|
httpx.post.assert_called_once()
|
||||||
if telemetry.get_os() == "Windows":
|
if telemetry.get_os() == "Windows":
|
||||||
open.assert_called_with(".web\\reflex.json", "r")
|
open.assert_called_with(".web\\reflex.json", "r")
|
||||||
|
Loading…
Reference in New Issue
Block a user