diff --git a/Makefile b/Makefile
index 32198730f..1fbd7403c 100644
--- a/Makefile
+++ b/Makefile
@@ -20,7 +20,7 @@ coverage:
tests:
@make install_backend
- poetry run pytest tests -n auto
+ poetry run pytest tests
format:
poetry run black .
diff --git a/src/backend/langflow/__main__.py b/src/backend/langflow/__main__.py
index 6701d27bc..f42f44a9e 100644
--- a/src/backend/langflow/__main__.py
+++ b/src/backend/langflow/__main__.py
@@ -23,7 +23,7 @@ from rich.table import Table
console = Console()
-app = typer.Typer()
+app = typer.Typer(no_args_is_help=True)
def get_number_of_workers(workers=None):
@@ -141,7 +141,7 @@ def run(
),
):
"""
- Run the Langflow server.
+ Run the Langflow.
"""
# override env variables with .env file
if env_file:
@@ -299,7 +299,14 @@ def superuser(
password: str = typer.Option(
..., prompt=True, hide_input=True, help="Password for the superuser."
),
+ log_level: str = typer.Option(
+ "critical", help="Logging level.", envvar="LANGFLOW_LOG_LEVEL"
+ ),
):
+ """
+ Create a superuser.
+ """
+ configure(log_level=log_level)
initialize_services()
db_service = get_db_service()
with session_getter(db_service) as session:
@@ -321,7 +328,10 @@ def superuser(
@app.command()
-def migration(test: bool = typer.Option(False, help="Run migrations in test mode.")):
+def migration(test: bool = typer.Option(True, help="Run migrations in test mode.")):
+ """
+ Run or test migrations.
+ """
initialize_services()
db_service = get_db_service()
if not test:
diff --git a/src/backend/langflow/interface/custom/custom_component.py b/src/backend/langflow/interface/custom/custom_component.py
index b0c4f8752..ccc0b08b2 100644
--- a/src/backend/langflow/interface/custom/custom_component.py
+++ b/src/backend/langflow/interface/custom/custom_component.py
@@ -102,7 +102,10 @@ class CustomComponent(Component, extra=Extra.allow):
status_code=400,
detail={
"error": "Type hint Error",
- "traceback": "Prompt type is not supported in the build method. Try using PromptTemplate instead.",
+ "traceback": (
+ "Prompt type is not supported in the build method."
+ " Try using PromptTemplate instead."
+ ),
},
)
return args
diff --git a/src/backend/langflow/services/database/manager.py b/src/backend/langflow/services/database/manager.py
index ca7f34d10..3a388f694 100644
--- a/src/backend/langflow/services/database/manager.py
+++ b/src/backend/langflow/services/database/manager.py
@@ -94,9 +94,7 @@ class DatabaseService(Service):
return True
def run_migrations(self):
- logger.info(
- f"Running DB migrations in {self.script_location} on {self.database_url}"
- )
+ logger.info(f"Running DB migrations in {self.script_location}")
alembic_cfg = Config()
alembic_cfg.set_main_option("script_location", str(self.script_location))
alembic_cfg.set_main_option("sqlalchemy.url", self.database_url)
diff --git a/src/backend/langflow/services/task/manager.py b/src/backend/langflow/services/task/manager.py
index ab74d916e..422b34faa 100644
--- a/src/backend/langflow/services/task/manager.py
+++ b/src/backend/langflow/services/task/manager.py
@@ -1,4 +1,5 @@
from typing import Any, Callable, Coroutine, Union
+from langflow.utils.logger import configure
from loguru import logger
from langflow.services.base import Service
from langflow.services.task.backends.anyio import AnyIOBackend
@@ -7,18 +8,19 @@ from langflow.services.task.utils import get_celery_worker_status
def check_celery_availability():
- from langflow.worker import celery_app
-
try:
+ from langflow.worker import celery_app
+
status = get_celery_worker_status(celery_app)
logger.debug(f"Celery status: {status}")
- except Exception as e:
- logger.error(f"An error occurred: {e}")
+ except Exception as exc:
+ logger.debug(f"Celery not available: {exc}")
status = {"availability": None}
return status
try:
+ configure()
status = check_celery_availability()
USE_CELERY = status.get("availability") is not None
diff --git a/src/backend/langflow/utils/logger.py b/src/backend/langflow/utils/logger.py
index 1f616486b..ebc468e7e 100644
--- a/src/backend/langflow/utils/logger.py
+++ b/src/backend/langflow/utils/logger.py
@@ -1,13 +1,44 @@
+from math import log
from typing import Optional
from loguru import logger
from pathlib import Path
from rich.logging import RichHandler
+import os
+import orjson
+import appdirs
-def configure(log_level: str = "DEBUG", log_file: Optional[Path] = None):
- log_format = "{time:HH:mm:ss} - {level: <8} - {message}"
+VALID_LOG_LEVELS = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
+
+
+def serialize(record):
+ subset = {
+ "timestamp": record["time"].timestamp(),
+ "message": record["message"],
+ "level": record["level"].name,
+ "module": record["module"],
+ }
+ return orjson.dumps(subset)
+
+
+def patching(record):
+ record["extra"]["serialized"] = serialize(record)
+
+
+def configure(log_level: Optional[str] = None, log_file: Optional[Path] = None):
+ if os.getenv("LANGFLOW_LOG_LEVEL") in VALID_LOG_LEVELS and log_level is None:
+ log_level = os.getenv("LANGFLOW_LOG_LEVEL")
+ if log_level is None:
+ log_level = "INFO"
+ # Human-readable
+ log_format = (
+ "{time:YYYY-MM-DD HH:mm:ss} - "
+ "{level: <8} - {module} - {message}"
+ )
+
+ # log_format = log_format_dev if log_level.upper() == "DEBUG" else log_format_prod
logger.remove() # Remove default handlers
-
+ logger.patch(patching)
# Configure loguru to use RichHandler
logger.configure(
handlers=[
@@ -19,17 +50,21 @@ def configure(log_level: str = "DEBUG", log_file: Optional[Path] = None):
]
)
- if log_file:
- log_file = Path(log_file)
- log_file.parent.mkdir(parents=True, exist_ok=True)
+ if not log_file:
+ cache_dir = Path(appdirs.user_cache_dir("langflow"))
+ log_file = cache_dir / "langflow.log"
- logger.add(
- sink=str(log_file),
- level=log_level.upper(),
- format=log_format,
- rotation="10 MB", # Log rotation based on file size
- )
+ log_file = Path(log_file)
+ log_file.parent.mkdir(parents=True, exist_ok=True)
- logger.info(f"Logger set up with log level: {log_level}")
+ logger.add(
+ sink=str(log_file),
+ level=log_level.upper(),
+ format=log_format,
+ rotation="10 MB", # Log rotation based on file size
+ serialize=True,
+ )
+
+ logger.debug(f"Logger set up with log level: {log_level}")
if log_file:
logger.info(f"Log file: {log_file}")