diff --git a/reflex/app.py b/reflex/app.py index 5c85a9ef8..4d99d6949 100644 --- a/reflex/app.py +++ b/reflex/app.py @@ -1067,51 +1067,61 @@ async def process( headers: The client headers. client_ip: The client_ip. + Raises: + ReflexError: If a reflex specific error occurs during processing the event. + Yields: The state updates after processing the event. """ - # Add request data to the state. - router_data = event.router_data - router_data.update( - { - constants.RouteVar.QUERY: format.format_query_params(event.router_data), - constants.RouteVar.CLIENT_TOKEN: event.token, - constants.RouteVar.SESSION_ID: sid, - constants.RouteVar.HEADERS: headers, - constants.RouteVar.CLIENT_IP: client_ip, - } - ) - # Get the state for the session exclusively. - async with app.state_manager.modify_state(event.substate_token) as state: - # re-assign only when the value is different - if state.router_data != router_data: - # assignment will recurse into substates and force recalculation of - # dependent ComputedVar (dynamic route variables) - state.router_data = router_data - state.router = RouterData(router_data) + from reflex.utils import telemetry + from reflex.utils.exceptions import ReflexError - # Preprocess the event. - update = await app._preprocess(state, event) + try: + # Add request data to the state. + router_data = event.router_data + router_data.update( + { + constants.RouteVar.QUERY: format.format_query_params(event.router_data), + constants.RouteVar.CLIENT_TOKEN: event.token, + constants.RouteVar.SESSION_ID: sid, + constants.RouteVar.HEADERS: headers, + constants.RouteVar.CLIENT_IP: client_ip, + } + ) + # Get the state for the session exclusively. + async with app.state_manager.modify_state(event.substate_token) as state: + # re-assign only when the value is different + if state.router_data != router_data: + # assignment will recurse into substates and force recalculation of + # dependent ComputedVar (dynamic route variables) + state.router_data = router_data + state.router = RouterData(router_data) - # If there was an update, yield it. - if update is not None: - yield update + # Preprocess the event. + update = await app._preprocess(state, event) - # Only process the event if there is no update. - else: - if app._process_background(state, event) is not None: - # `final=True` allows the frontend send more events immediately. - yield StateUpdate(final=True) - return - - # Process the event synchronously. - async for update in state._process(event): - # Postprocess the event. - update = await app._postprocess(state, event, update) - - # Yield the update. + # If there was an update, yield it. + if update is not None: yield update + # Only process the event if there is no update. + else: + if app._process_background(state, event) is not None: + # `final=True` allows the frontend send more events immediately. + yield StateUpdate(final=True) + return + + # Process the event synchronously. + async for update in state._process(event): + # Postprocess the event. + update = await app._postprocess(state, event, update) + + # Yield the update. + yield update + except ReflexError as ex: + telemetry.send("error", context="backend", detail=str(ex)) + raise + async def ping() -> str: """Test API endpoint. diff --git a/reflex/state.py b/reflex/state.py index bc77f8140..ebe33aa26 100644 --- a/reflex/state.py +++ b/reflex/state.py @@ -1462,6 +1462,9 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow): Yields: StateUpdate object """ + from reflex.utils import telemetry + from reflex.utils.exceptions import ReflexError + # Get the function to process the event. fn = functools.partial(handler.fn, state) @@ -1497,9 +1500,11 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow): yield state._as_state_update(handler, events, final=True) # If an error occurs, throw a window alert. - except Exception: + except Exception as ex: error = traceback.format_exc() print(error) + if isinstance(ex, ReflexError): + telemetry.send("error", context="backend", detail=str(ex)) yield state._as_state_update( handler, window_alert("An error occurred. See logs for details."), diff --git a/reflex/utils/prerequisites.py b/reflex/utils/prerequisites.py index 870b0a7be..de4741c1e 100644 --- a/reflex/utils/prerequisites.py +++ b/reflex/utils/prerequisites.py @@ -856,7 +856,6 @@ def install_frontend_packages(packages: set[str], config: Config): [get_install_package_manager(), "install"], # type: ignore fallback=fallback_command, analytics_enabled=True, - error_filter_fn=lambda output: "404" in output, show_status_message="Installing base frontend packages", cwd=constants.Dirs.WEB, shell=constants.IS_WINDOWS, @@ -873,7 +872,6 @@ def install_frontend_packages(packages: set[str], config: Config): ], fallback=fallback_command, analytics_enabled=True, - error_filter_fn=lambda output: "404" in output, show_status_message="Installing tailwind", cwd=constants.Dirs.WEB, shell=constants.IS_WINDOWS, @@ -885,7 +883,6 @@ def install_frontend_packages(packages: set[str], config: Config): [get_install_package_manager(), "add", *packages], fallback=fallback_command, analytics_enabled=True, - error_filter_fn=lambda output: "404" in output, show_status_message="Installing frontend packages from config and components", cwd=constants.Dirs.WEB, shell=constants.IS_WINDOWS, diff --git a/reflex/utils/processes.py b/reflex/utils/processes.py index c42b8b277..cadd87547 100644 --- a/reflex/utils/processes.py +++ b/reflex/utils/processes.py @@ -212,7 +212,6 @@ def stream_logs( progress=None, suppress_errors: bool = False, analytics_enabled: bool = False, - error_filter_fn: Callable[[str], bool] | None = None, ): """Stream the logs for a process. @@ -222,7 +221,6 @@ def stream_logs( progress: The ongoing progress bar if one is being used. suppress_errors: If True, do not exit if errors are encountered (for fallback). analytics_enabled: Whether analytics are enabled for this command. - error_filter_fn: A function that takes a line of output and returns True if the line should be considered an error, False otherwise. If None, all lines are considered errors. Yields: The lines of the process output. @@ -253,15 +251,7 @@ def stream_logs( for line in logs: console.error(line, end="") if analytics_enabled: - error_lines = [ - line.strip() - for line in logs - if error_filter_fn is None or error_filter_fn(line) - ] - max_error_lines = 20 - telemetry.send( - "error", context=message, detail=error_lines[:max_error_lines] - ) + telemetry.send("error", context=message) console.error("Run with [bold]--loglevel debug [/bold] for the full log.") raise typer.Exit(1) @@ -282,7 +272,6 @@ def show_status( process: subprocess.Popen, suppress_errors: bool = False, analytics_enabled: bool = False, - error_filter_fn: Callable[[str], bool] | None = None, ): """Show the status of a process. @@ -291,7 +280,6 @@ def show_status( process: The process. suppress_errors: If True, do not exit if errors are encountered (for fallback). analytics_enabled: Whether analytics are enabled for this command. - error_filter_fn: A function that takes a line of output and returns True if the line should be considered an error, False otherwise. If None, all lines are considered errors. """ with console.status(message) as status: for line in stream_logs( @@ -299,7 +287,6 @@ def show_status( process, suppress_errors=suppress_errors, analytics_enabled=analytics_enabled, - error_filter_fn=error_filter_fn, ): status.update(f"{message} {line}") @@ -355,7 +342,6 @@ def run_process_with_fallback( show_status_message, fallback=None, analytics_enabled: bool = False, - error_filter_fn: Callable[[str], bool] | None = None, **kwargs, ): """Run subprocess and retry using fallback command if initial command fails. @@ -365,7 +351,6 @@ def run_process_with_fallback( show_status_message: The status message to be displayed in the console. fallback: The fallback command to run. analytics_enabled: Whether analytics are enabled for this command. - error_filter_fn: A function that takes a line of output and returns True if the line should be considered an error, False otherwise. If None, all lines are considered errors. kwargs: Kwargs to pass to new_process function. """ process = new_process(get_command_with_loglevel(args), **kwargs) @@ -375,7 +360,6 @@ def run_process_with_fallback( show_status_message, process, analytics_enabled=analytics_enabled, - error_filter_fn=error_filter_fn, ) else: # Suppress errors for initial command, because we will try to fallback @@ -391,7 +375,6 @@ def run_process_with_fallback( show_status_message=show_status_message, fallback=None, analytics_enabled=analytics_enabled, - error_filter_fn=error_filter_fn, **kwargs, )