From 67ad6afbde76a80c2d292fc86954f8e19abefa87 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 26 Mar 2025 09:33:54 +0000 Subject: [PATCH] Fix logger not working in FastAPI projects after upgrading to 0.108 (#2473) Co-Authored-By: Joe Moura --- src/crewai/utilities/printer.py | 48 +++++++++++++------------ tests/utilities/test_fastapi_logger.py | 50 ++++++++++++++++++++++++++ tests/utilities/test_logger.py | 41 +++++++++++++++++++++ 3 files changed, 116 insertions(+), 23 deletions(-) create mode 100644 tests/utilities/test_fastapi_logger.py create mode 100644 tests/utilities/test_logger.py diff --git a/src/crewai/utilities/printer.py b/src/crewai/utilities/printer.py index edb339c29..7a3b2f7d8 100644 --- a/src/crewai/utilities/printer.py +++ b/src/crewai/utilities/printer.py @@ -1,42 +1,44 @@ from typing import Optional +import sys class Printer: def print(self, content: str, color: Optional[str] = None): + output = content if color == "purple": - self._print_purple(content) + output = self._format_purple(content) elif color == "red": - self._print_red(content) + output = self._format_red(content) elif color == "bold_green": - self._print_bold_green(content) + output = self._format_bold_green(content) elif color == "bold_purple": - self._print_bold_purple(content) + output = self._format_bold_purple(content) elif color == "bold_blue": - self._print_bold_blue(content) + output = self._format_bold_blue(content) elif color == "yellow": - self._print_yellow(content) + output = self._format_yellow(content) elif color == "bold_yellow": - self._print_bold_yellow(content) - else: - print(content) + output = self._format_bold_yellow(content) + sys.stdout.write(f"{output}\n") + sys.stdout.flush() - def _print_bold_purple(self, content): - print("\033[1m\033[95m {}\033[00m".format(content)) + def _format_bold_purple(self, content): + return "\033[1m\033[95m {}\033[00m".format(content) - def _print_bold_green(self, content): - print("\033[1m\033[92m {}\033[00m".format(content)) + def _format_bold_green(self, content): + return "\033[1m\033[92m {}\033[00m".format(content) - def _print_purple(self, content): - print("\033[95m {}\033[00m".format(content)) + def _format_purple(self, content): + return "\033[95m {}\033[00m".format(content) - def _print_red(self, content): - print("\033[91m {}\033[00m".format(content)) + def _format_red(self, content): + return "\033[91m {}\033[00m".format(content) - def _print_bold_blue(self, content): - print("\033[1m\033[94m {}\033[00m".format(content)) + def _format_bold_blue(self, content): + return "\033[1m\033[94m {}\033[00m".format(content) - def _print_yellow(self, content): - print("\033[93m {}\033[00m".format(content)) + def _format_yellow(self, content): + return "\033[93m {}\033[00m".format(content) - def _print_bold_yellow(self, content): - print("\033[1m\033[93m {}\033[00m".format(content)) + def _format_bold_yellow(self, content): + return "\033[1m\033[93m {}\033[00m".format(content) diff --git a/tests/utilities/test_fastapi_logger.py b/tests/utilities/test_fastapi_logger.py new file mode 100644 index 000000000..dc4957ddd --- /dev/null +++ b/tests/utilities/test_fastapi_logger.py @@ -0,0 +1,50 @@ +import sys +import unittest +from unittest.mock import patch +import asyncio +from io import StringIO + +try: + import fastapi + from fastapi.testclient import TestClient + FASTAPI_AVAILABLE = True +except ImportError: + FASTAPI_AVAILABLE = False + +from crewai.utilities.logger import Logger + + +@unittest.skipIf(not FASTAPI_AVAILABLE, "FastAPI not installed") +class TestFastAPILogger(unittest.TestCase): + def setUp(self): + if not FASTAPI_AVAILABLE: + self.skipTest("FastAPI not installed") + + from fastapi import FastAPI + + self.app = FastAPI() + self.logger = Logger(verbose=True) + + @self.app.get("/") + async def root(): + self.logger.log("info", "This is a test log message from FastAPI") + return {"message": "Hello World"} + + self.client = TestClient(self.app) + + self.output = StringIO() + self.old_stdout = sys.stdout + sys.stdout = self.output + + def tearDown(self): + sys.stdout = self.old_stdout + + def test_logger_in_fastapi_context(self): + response = self.client.get("/") + + output = self.output.getvalue() + self.assertIn("[INFO]: This is a test log message from FastAPI", output) + self.assertIn("\n", output) + + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json(), {"message": "Hello World"}) diff --git a/tests/utilities/test_logger.py b/tests/utilities/test_logger.py new file mode 100644 index 000000000..b53222016 --- /dev/null +++ b/tests/utilities/test_logger.py @@ -0,0 +1,41 @@ +import sys +import unittest +from unittest.mock import patch +from io import StringIO + +from crewai.utilities.logger import Logger + + +class TestLogger(unittest.TestCase): + def setUp(self): + self.logger = Logger(verbose=True) + self.output = StringIO() + self.old_stdout = sys.stdout + sys.stdout = self.output + + def tearDown(self): + sys.stdout = self.old_stdout + + def test_log_in_sync_context(self): + self.logger.log("info", "Test message") + output = self.output.getvalue() + self.assertIn("[INFO]: Test message", output) + self.assertIn("\n", output) + + @patch('sys.stdout.flush') + def test_stdout_is_flushed(self, mock_flush): + self.logger.log("info", "Test message") + mock_flush.assert_called_once() + + +class TestFastAPICompatibility(unittest.TestCase): + def test_import_in_fastapi(self): + try: + import fastapi + from crewai.utilities.logger import Logger + logger = Logger(verbose=True) + self.assertTrue(True) + except ImportError: + self.skipTest("FastAPI not installed") + except Exception as e: + self.fail(f"Unexpected error: {e}")