From 9491fe833444f6df1cfbc0bdadd920683066c93f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moura?= Date: Thu, 18 Sep 2025 21:47:09 -0300 Subject: [PATCH] Adding Ability for user to get deeper observability (#3541) * feat(tracing): enhance first-time trace display and auto-open browser * avoinding line breaking * set tracing if user enables it * linted --------- Co-authored-by: lorenzejay --- .../tracing/first_time_trace_handler.py | 61 ++++++++++++++++++- .../listeners/tracing/trace_batch_manager.py | 22 ++++--- 2 files changed, 71 insertions(+), 12 deletions(-) diff --git a/src/crewai/events/listeners/tracing/first_time_trace_handler.py b/src/crewai/events/listeners/tracing/first_time_trace_handler.py index 1e66ec555..4de56dc26 100644 --- a/src/crewai/events/listeners/tracing/first_time_trace_handler.py +++ b/src/crewai/events/listeners/tracing/first_time_trace_handler.py @@ -1,5 +1,7 @@ import logging import uuid +import webbrowser +from pathlib import Path from rich.console import Console from rich.panel import Panel @@ -14,6 +16,47 @@ from crewai.events.listeners.tracing.utils import ( logger = logging.getLogger(__name__) +def _update_or_create_env_file(): + """Update or create .env file with CREWAI_TRACING_ENABLED=true.""" + env_path = Path(".env") + env_content = "" + variable_name = "CREWAI_TRACING_ENABLED" + variable_value = "true" + + # Read existing content if file exists + if env_path.exists(): + with open(env_path, "r") as f: + env_content = f.read() + + # Check if CREWAI_TRACING_ENABLED is already set + lines = env_content.splitlines() + variable_exists = False + updated_lines = [] + + for line in lines: + if line.strip().startswith(f"{variable_name}="): + # Update existing variable + updated_lines.append(f"{variable_name}={variable_value}") + variable_exists = True + else: + updated_lines.append(line) + + # Add variable if it doesn't exist + if not variable_exists: + if updated_lines and not updated_lines[-1].strip(): + # If last line is empty, replace it + updated_lines[-1] = f"{variable_name}={variable_value}" + else: + # Add new line and then the variable + updated_lines.append(f"{variable_name}={variable_value}") + + # Write updated content + with open(env_path, "w") as f: + f.write("\n".join(updated_lines)) + if updated_lines: # Add final newline if there's content + f.write("\n") + + class FirstTimeTraceHandler: """Handles the first-time user trace collection and display flow.""" @@ -48,6 +91,12 @@ class FirstTimeTraceHandler: if user_wants_traces: self._initialize_backend_and_send_events() + # Enable tracing for future runs by updating .env file + try: + _update_or_create_env_file() + except Exception: # noqa: S110 + pass + if self.ephemeral_url: self._display_ephemeral_trace_link() @@ -108,9 +157,14 @@ class FirstTimeTraceHandler: self._gracefully_fail(f"Backend initialization failed: {e}") def _display_ephemeral_trace_link(self): - """Display the ephemeral trace link to the user.""" + """Display the ephemeral trace link to the user and automatically open browser.""" console = Console() + try: + webbrowser.open(self.ephemeral_url) + except Exception: # noqa: S110 + pass + panel_content = f""" 🎉 Your First CrewAI Execution Trace is Ready! @@ -123,7 +177,8 @@ This trace shows: • Tool usage and results • LLM calls and responses -To use traces add tracing=True to your Crew(tracing=True) / Flow(tracing=True) +✅ Tracing has been enabled for future runs! (CREWAI_TRACING_ENABLED=true added to .env) +You can also add tracing=True to your Crew(tracing=True) / Flow(tracing=True) for more control. 📝 Note: This link will expire in 24 hours. """.strip() @@ -158,8 +213,8 @@ Unfortunately, we couldn't upload them to the server right now, but here's what • Execution duration: {self.batch_manager.calculate_duration("execution")}ms • Batch ID: {self.batch_manager.trace_batch_id} +Tracing has been enabled for future runs! (CREWAI_TRACING_ENABLED=true added to .env) The traces include agent decisions, task execution, and tool usage. -Try running with CREWAI_TRACING_ENABLED=true next time for persistent traces. """.strip() panel = Panel( diff --git a/src/crewai/events/listeners/tracing/trace_batch_manager.py b/src/crewai/events/listeners/tracing/trace_batch_manager.py index 5bc898923..826524d25 100644 --- a/src/crewai/events/listeners/tracing/trace_batch_manager.py +++ b/src/crewai/events/listeners/tracing/trace_batch_manager.py @@ -138,13 +138,6 @@ class TraceBatchManager: if not use_ephemeral else response_data["ephemeral_trace_id"] ) - console = Console() - panel = Panel( - f"✅ Trace batch initialized with session ID: {self.trace_batch_id}", - title="Trace Batch Initialization", - border_style="green", - ) - console.print(panel) else: logger.warning( f"Trace batch initialization returned status {response.status_code}. Continuing without tracing." @@ -258,12 +251,23 @@ class TraceBatchManager: if self.is_current_batch_ephemeral: self.ephemeral_trace_url = return_link + # Create a properly formatted message with URL on its own line + message_parts = [ + f"✅ Trace batch finalized with session ID: {self.trace_batch_id}", + "", + f"🔗 View here: {return_link}", + ] + + if access_code: + message_parts.append(f"🔑 Access Code: {access_code}") + panel = Panel( - f"✅ Trace batch finalized with session ID: {self.trace_batch_id}. View here: {return_link} {f', Access Code: {access_code}' if access_code else ''}", + "\n".join(message_parts), title="Trace Batch Finalization", border_style="green", ) - console.print(panel) + if not should_auto_collect_first_time_traces(): + console.print(panel) else: logger.error(