浏览代码

[REF-2774] Send runtime error in telemetry (#3276)

Martin Xu 1 年之前
父节点
当前提交
aa2cf80f70
共有 4 个文件被更改,包括 56 次插入61 次删除
  1. 49 39
      reflex/app.py
  2. 6 1
      reflex/state.py
  3. 0 3
      reflex/utils/prerequisites.py
  4. 1 18
      reflex/utils/processes.py

+ 49 - 39
reflex/app.py

@@ -1067,50 +1067,60 @@ 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)
-
-        # Preprocess the event.
-        update = await app._preprocess(state, event)
-
-        # 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
+    from reflex.utils import telemetry
+    from reflex.utils.exceptions import ReflexError
+
+    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)
+
+            # Preprocess the event.
+            update = await app._preprocess(state, event)
+
+            # If there was an update, yield it.
+            if update is not None:
+                yield update
 
-            # Process the event synchronously.
-            async for update in state._process(event):
-                # Postprocess the event.
-                update = await app._postprocess(state, event, 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
 
-                # Yield the update.
-                yield update
+                # 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:

+ 6 - 1
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."),

+ 0 - 3
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,

+ 1 - 18
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,
             )