fix(tracing): stop nagging users who declined tracing (#5665)

- When user explicitly declined tracing, skip the 'Tracing is disabled'
  message instead of showing it on every crew/flow execution
- Add CREWAI_SUPPRESS_TRACING_MESSAGES env var to let users fully
  suppress the message
- Remove duplicate identical if/else branches in all four
  _show_tracing_disabled_message implementations
- Add 24 tests covering suppression via env var, context var, and
  user-declined scenarios

Co-Authored-By: João <joao@crewai.com>
This commit is contained in:
Devin AI
2026-04-30 04:52:51 +00:00
parent c7f01048b7
commit ee8b3be8e5
6 changed files with 289 additions and 37 deletions

View File

@@ -2272,17 +2272,13 @@ class Crew(FlowTrackable, BaseModel):
if should_suppress_tracing_messages():
return
# Don't nag users who have explicitly declined tracing
if has_user_declined_tracing():
return
console = Console()
if has_user_declined_tracing():
message = """Info: Tracing is disabled.
To enable tracing, do any one of these:
• Set tracing=True in your Crew code
• Set CREWAI_TRACING_ENABLED=true in your project's .env file
• Run: crewai traces enable"""
else:
message = """Info: Tracing is disabled.
message = """Info: Tracing is disabled.
To enable tracing, do any one of these:
• Set tracing=True in your Crew code

View File

@@ -868,17 +868,13 @@ class TraceCollectionListener(BaseEventListener):
if should_suppress_tracing_messages():
return
# Don't nag users who have explicitly declined tracing
if has_user_declined_tracing():
return
console = Console()
if has_user_declined_tracing():
message = """Info: Tracing is disabled.
To enable tracing, do any one of these:
• Set tracing=True in your Crew/Flow code
• Set CREWAI_TRACING_ENABLED=true in your project's .env file
• Run: crewai traces enable"""
else:
message = """Info: Tracing is disabled.
message = """Info: Tracing is disabled.
To enable tracing, do any one of these:
• Set tracing=True in your Crew/Flow code

View File

@@ -53,10 +53,19 @@ def set_suppress_tracing_messages(suppress: bool) -> object:
def should_suppress_tracing_messages() -> bool:
"""Check if tracing messages should be suppressed.
Checks the context variable first, then falls back to the
CREWAI_SUPPRESS_TRACING_MESSAGES environment variable.
Returns:
True if messages should be suppressed, False otherwise.
"""
return _suppress_tracing_messages.get()
if _suppress_tracing_messages.get():
return True
return os.getenv("CREWAI_SUPPRESS_TRACING_MESSAGES", "false").lower() in (
"true",
"1",
"yes",
)
def should_enable_tracing(*, override: bool | None = None) -> bool:

View File

@@ -145,16 +145,12 @@ To update, run: uv sync --upgrade-package crewai"""
if listener and listener.first_time_handler.is_first_time:
return
if not is_tracing_enabled_in_context():
if has_user_declined_tracing():
message = """Info: Tracing is disabled.
# Don't nag users who have explicitly declined tracing
if has_user_declined_tracing():
return
To enable tracing, do any one of these:
• Set tracing=True in your Crew/Flow code
• Set CREWAI_TRACING_ENABLED=true in your project's .env file
• Run: crewai traces enable"""
else:
message = """Info: Tracing is disabled.
if not is_tracing_enabled_in_context():
message = """Info: Tracing is disabled.
To enable tracing, do any one of these:
• Set tracing=True in your Crew/Flow code

View File

@@ -3546,17 +3546,13 @@ class Flow(BaseModel, Generic[T], metaclass=FlowMeta):
if should_suppress_tracing_messages():
return
# Don't nag users who have explicitly declined tracing
if has_user_declined_tracing():
return
console = Console()
if has_user_declined_tracing():
message = """Info: Tracing is disabled.
To enable tracing, do any one of these:
• Set tracing=True in your Flow code
• Set CREWAI_TRACING_ENABLED=true in your project's .env file
• Run: crewai traces enable"""
else:
message = """Info: Tracing is disabled.
message = """Info: Tracing is disabled.
To enable tracing, do any one of these:
• Set tracing=True in your Flow code

View File

@@ -0,0 +1,259 @@
"""Tests for tracing disabled message suppression (issue #5665).
Verifies that:
- Users who explicitly declined tracing are NOT nagged with the message.
- The CREWAI_SUPPRESS_TRACING_MESSAGES env var suppresses the message.
- The message is shown only when tracing is disabled and user hasn't declined.
"""
from unittest.mock import MagicMock, patch
import pytest
from crewai.events.listeners.tracing.utils import (
set_suppress_tracing_messages,
should_suppress_tracing_messages,
)
class TestShouldSuppressTracingMessages:
"""Tests for the should_suppress_tracing_messages utility function."""
def test_suppress_false_by_default(self):
"""By default, messages should NOT be suppressed."""
token = set_suppress_tracing_messages(False)
try:
assert should_suppress_tracing_messages() is False
finally:
from crewai.events.listeners.tracing.utils import (
_suppress_tracing_messages,
)
_suppress_tracing_messages.reset(token)
def test_suppress_via_context_var(self):
"""Setting the context var should suppress messages."""
token = set_suppress_tracing_messages(True)
try:
assert should_suppress_tracing_messages() is True
finally:
from crewai.events.listeners.tracing.utils import (
_suppress_tracing_messages,
)
_suppress_tracing_messages.reset(token)
@pytest.mark.parametrize("env_value", ["true", "True", "TRUE", "1", "yes", "YES"])
def test_suppress_via_env_var(self, env_value, monkeypatch):
"""CREWAI_SUPPRESS_TRACING_MESSAGES env var should suppress messages."""
token = set_suppress_tracing_messages(False)
try:
monkeypatch.setenv("CREWAI_SUPPRESS_TRACING_MESSAGES", env_value)
assert should_suppress_tracing_messages() is True
finally:
from crewai.events.listeners.tracing.utils import (
_suppress_tracing_messages,
)
_suppress_tracing_messages.reset(token)
@pytest.mark.parametrize("env_value", ["false", "False", "0", "no", ""])
def test_no_suppress_with_falsy_env_var(self, env_value, monkeypatch):
"""Falsy values for the env var should NOT suppress messages."""
token = set_suppress_tracing_messages(False)
try:
monkeypatch.setenv("CREWAI_SUPPRESS_TRACING_MESSAGES", env_value)
assert should_suppress_tracing_messages() is False
finally:
from crewai.events.listeners.tracing.utils import (
_suppress_tracing_messages,
)
_suppress_tracing_messages.reset(token)
def test_context_var_takes_precedence_over_env(self, monkeypatch):
"""Context var set to True should suppress even if env var is false."""
token = set_suppress_tracing_messages(True)
try:
monkeypatch.setenv("CREWAI_SUPPRESS_TRACING_MESSAGES", "false")
assert should_suppress_tracing_messages() is True
finally:
from crewai.events.listeners.tracing.utils import (
_suppress_tracing_messages,
)
_suppress_tracing_messages.reset(token)
class TestShowTracingDisabledMessage:
"""Tests that _show_tracing_disabled_message does not nag declined users."""
@patch(
"crewai.events.listeners.tracing.utils._load_user_data",
return_value={"first_execution_done": True, "trace_consent": False},
)
def test_crew_no_message_when_user_declined(self, mock_load):
"""Crew._show_tracing_disabled_message should not print when user declined."""
from crewai.crew import Crew
with patch("crewai.crew.Console") as MockConsole:
Crew._show_tracing_disabled_message()
MockConsole.return_value.print.assert_not_called()
@patch(
"crewai.events.listeners.tracing.utils._load_user_data",
return_value={"first_execution_done": True, "trace_consent": False},
)
def test_flow_no_message_when_user_declined(self, mock_load):
"""Flow._show_tracing_disabled_message should not print when user declined."""
from crewai.flow.flow import Flow
with patch("crewai.flow.flow.Console") as MockConsole:
Flow._show_tracing_disabled_message()
MockConsole.return_value.print.assert_not_called()
@patch(
"crewai.events.listeners.tracing.utils._load_user_data",
return_value={"first_execution_done": True, "trace_consent": False},
)
def test_trace_listener_no_message_when_user_declined(self, mock_load):
"""TraceCollectionListener._show_tracing_disabled_message should not print when user declined."""
from crewai.events.listeners.tracing.trace_listener import (
TraceCollectionListener,
)
listener = TraceCollectionListener.__new__(TraceCollectionListener)
with patch("rich.console.Console") as MockConsole:
listener._show_tracing_disabled_message()
MockConsole.return_value.print.assert_not_called()
@patch(
"crewai.events.listeners.tracing.utils._load_user_data",
return_value={},
)
def test_crew_shows_message_when_user_has_not_decided(self, mock_load):
"""Crew._show_tracing_disabled_message should print when user hasn't decided yet."""
from crewai.crew import Crew
with patch("crewai.crew.Console") as MockConsole:
mock_console_instance = MockConsole.return_value
Crew._show_tracing_disabled_message()
mock_console_instance.print.assert_called_once()
@patch(
"crewai.events.listeners.tracing.utils._load_user_data",
return_value={},
)
def test_crew_no_message_when_suppress_env_set(self, mock_load, monkeypatch):
"""Crew._show_tracing_disabled_message should not print when env var suppresses."""
from crewai.crew import Crew
monkeypatch.setenv("CREWAI_SUPPRESS_TRACING_MESSAGES", "true")
with patch("crewai.crew.Console") as MockConsole:
Crew._show_tracing_disabled_message()
MockConsole.return_value.print.assert_not_called()
@patch(
"crewai.events.listeners.tracing.utils._load_user_data",
return_value={},
)
def test_flow_no_message_when_suppress_env_set(self, mock_load, monkeypatch):
"""Flow._show_tracing_disabled_message should not print when env var suppresses."""
from crewai.flow.flow import Flow
monkeypatch.setenv("CREWAI_SUPPRESS_TRACING_MESSAGES", "true")
with patch("crewai.flow.flow.Console") as MockConsole:
Flow._show_tracing_disabled_message()
MockConsole.return_value.print.assert_not_called()
class TestConsoleFormatterTracingMessage:
"""Tests for console_formatter._show_tracing_disabled_message_if_needed."""
def _make_formatter(self):
from crewai.events.utils.console_formatter import ConsoleFormatter
formatter = ConsoleFormatter.__new__(ConsoleFormatter)
formatter.console = MagicMock()
formatter.verbose = True
return formatter
@patch(
"crewai.events.listeners.tracing.utils._load_user_data",
return_value={"first_execution_done": True, "trace_consent": False},
)
def test_no_message_when_user_declined(self, mock_load):
"""Console formatter should not show the message when user declined tracing."""
formatter = self._make_formatter()
with patch(
"crewai.events.listeners.tracing.trace_listener.TraceCollectionListener"
) as mock_listener_cls:
mock_listener_cls._instance = None
formatter._show_tracing_disabled_message_if_needed()
formatter.console.print.assert_not_called()
@patch(
"crewai.events.listeners.tracing.utils._load_user_data",
return_value={},
)
def test_no_message_when_suppress_env_set(self, mock_load, monkeypatch):
"""Console formatter should not show the message when env var is set."""
monkeypatch.setenv("CREWAI_SUPPRESS_TRACING_MESSAGES", "true")
formatter = self._make_formatter()
formatter._show_tracing_disabled_message_if_needed()
formatter.console.print.assert_not_called()
@patch(
"crewai.events.listeners.tracing.utils._load_user_data",
return_value={},
)
@patch(
"crewai.events.listeners.tracing.utils.is_tracing_enabled_in_context",
return_value=False,
)
def test_message_shown_when_tracing_disabled_and_not_declined(
self, mock_tracing_ctx, mock_load
):
"""Console formatter should show the message when tracing disabled and user hasn't declined."""
from crewai.events.listeners.tracing.trace_listener import (
TraceCollectionListener,
)
formatter = self._make_formatter()
mock_instance = MagicMock()
mock_instance.first_time_handler.is_first_time = False
original_instance = TraceCollectionListener._instance
try:
TraceCollectionListener._instance = mock_instance # type: ignore[misc]
formatter._show_tracing_disabled_message_if_needed()
formatter.console.print.assert_called_once()
finally:
TraceCollectionListener._instance = original_instance # type: ignore[misc]
@patch(
"crewai.events.listeners.tracing.utils._load_user_data",
return_value={},
)
@patch(
"crewai.events.listeners.tracing.utils.is_tracing_enabled_in_context",
return_value=True,
)
def test_no_message_when_tracing_enabled(self, mock_tracing_ctx, mock_load):
"""Console formatter should not show the message when tracing is enabled."""
from crewai.events.listeners.tracing.trace_listener import (
TraceCollectionListener,
)
formatter = self._make_formatter()
mock_instance = MagicMock()
mock_instance.first_time_handler.is_first_time = False
original_instance = TraceCollectionListener._instance
try:
TraceCollectionListener._instance = mock_instance # type: ignore[misc]
formatter._show_tracing_disabled_message_if_needed()
formatter.console.print.assert_not_called()
finally:
TraceCollectionListener._instance = original_instance # type: ignore[misc]