✨ Integrate logging
This commit is contained in:
parent
e848601f79
commit
67d89a94f4
4 changed files with 66 additions and 13 deletions
|
|
@ -9,6 +9,9 @@ import typer
|
|||
import uvicorn
|
||||
|
||||
from ohmyapi.core import runtime, scaffolding
|
||||
from ohmyapi.core.logging import setup_logging
|
||||
|
||||
logger = setup_logging()
|
||||
|
||||
app = typer.Typer(
|
||||
help="OhMyAPI — Django-flavored FastAPI scaffolding with tightly integrated TortoiseORM."
|
||||
|
|
@ -19,18 +22,24 @@ app = typer.Typer(
|
|||
def startproject(name: str):
|
||||
"""Create a new OhMyAPI project in the given directory."""
|
||||
scaffolding.startproject(name)
|
||||
logger.info(f"✅ Project '{name}' created successfully.")
|
||||
logger.info(f"🔧 Next, configure your project in {name}/settings.py")
|
||||
|
||||
|
||||
@app.command()
|
||||
def startapp(app_name: str, root: str = "."):
|
||||
"""Create a new app with the given name in your OhMyAPI project."""
|
||||
scaffolding.startapp(app_name, root)
|
||||
print(f"✅ App '{app_name}' created in project '{root}' successfully.")
|
||||
print(f"🔧 Remember to add '{app_name}' to your INSTALLED_APPS!")
|
||||
|
||||
|
||||
@app.command()
|
||||
def dockerize(root: str = "."):
|
||||
"""Create template Dockerfile and docker-compose.yml."""
|
||||
scaffolding.copy_static("docker", root)
|
||||
logger.info(f"✅ Templates created successfully.")
|
||||
logger.info(f"🔧 Next, run `docker compose up -d --build`")
|
||||
|
||||
|
||||
@app.command()
|
||||
|
|
@ -42,7 +51,7 @@ def serve(root: str = ".", host="127.0.0.1", port=8000):
|
|||
project = runtime.Project(project_path)
|
||||
app_instance = project.app()
|
||||
app_instance = project.configure_app(app_instance)
|
||||
uvicorn.run(app_instance, host=host, port=int(port), reload=False)
|
||||
uvicorn.run(app_instance, host=host, port=int(port), reload=False, log_config=None)
|
||||
|
||||
|
||||
@app.command()
|
||||
|
|
@ -107,6 +116,8 @@ def makemigrations(app: str = "*", name: str = "auto", root: str = "."):
|
|||
asyncio.run(project.makemigrations(app_label=app, name=name))
|
||||
else:
|
||||
asyncio.run(project.makemigrations(app_label=app, name=name))
|
||||
logger.info(f"✅ Migrations created successfully.")
|
||||
logger.info(f"🔧 To migrate the DB, run `ohmyapi migrate`, next.")
|
||||
|
||||
|
||||
@app.command()
|
||||
|
|
@ -121,6 +132,7 @@ def migrate(app: str = "*", root: str = "."):
|
|||
asyncio.run(project.migrate(app))
|
||||
else:
|
||||
asyncio.run(project.migrate(app))
|
||||
logger.info(f"✅ Migrations ran successfully.")
|
||||
|
||||
|
||||
@app.command()
|
||||
|
|
|
|||
38
src/ohmyapi/core/logging.py
Normal file
38
src/ohmyapi/core/logging.py
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def setup_logging():
|
||||
"""Configure unified logging for ohmyapi + FastAPI/Uvicorn."""
|
||||
log_level = os.getenv("OHMYAPI_LOG_LEVEL", "INFO").upper()
|
||||
level = getattr(logging, log_level, logging.INFO)
|
||||
|
||||
# Root logger (affects FastAPI, uvicorn, etc.)
|
||||
logging.basicConfig(
|
||||
level=level,
|
||||
format="[%(asctime)s] [%(levelname)s] %(name)s: %(message)s",
|
||||
datefmt="%Y-%m-%d %H:%M:%S",
|
||||
handlers=[
|
||||
logging.StreamHandler(sys.stdout)
|
||||
]
|
||||
)
|
||||
|
||||
# Separate ohmyapi logger (optional)
|
||||
logger = logging.getLogger("ohmyapi")
|
||||
|
||||
# Direct warnings/errors to stderr
|
||||
class LevelFilter(logging.Filter):
|
||||
def filter(self, record):
|
||||
# Send warnings+ to stderr, everything else to stdout
|
||||
if record.levelno >= logging.WARNING:
|
||||
record.stream = sys.stderr
|
||||
else:
|
||||
record.stream = sys.stdout
|
||||
return True
|
||||
|
||||
for handler in logger.handlers:
|
||||
handler.addFilter(LevelFilter())
|
||||
|
||||
logger.setLevel(level)
|
||||
return logger
|
||||
|
|
@ -15,8 +15,11 @@ from aerich.exceptions import NotInitedError
|
|||
from fastapi import APIRouter, FastAPI
|
||||
from tortoise import Tortoise
|
||||
|
||||
from ohmyapi.core.logging import setup_logging
|
||||
from ohmyapi.db.model import Model
|
||||
|
||||
logger = setup_logging()
|
||||
|
||||
|
||||
class Project:
|
||||
"""
|
||||
|
|
@ -29,6 +32,7 @@ class Project:
|
|||
"""
|
||||
|
||||
def __init__(self, project_path: str):
|
||||
logger.debug(f"Loading project: {project_path}")
|
||||
self.project_path = Path(project_path).resolve()
|
||||
self._apps: Dict[str, App] = {}
|
||||
self.migrations_dir = self.project_path / "migrations"
|
||||
|
|
@ -238,6 +242,7 @@ class App:
|
|||
# Import the app, so its __init__.py runs.
|
||||
mod: ModuleType = importlib.import_module(name)
|
||||
|
||||
logger.debug(f"Loading app: {self.name}")
|
||||
self.__load_models(f"{self.name}.models")
|
||||
self.__load_routes(f"{self.name}.routes")
|
||||
self.__load_middlewares(f"{self.name}.middlewares")
|
||||
|
|
@ -258,7 +263,6 @@ class App:
|
|||
try:
|
||||
importlib.import_module(mod_name)
|
||||
except ModuleNotFoundError:
|
||||
print(f"no models detected: {mod_name}")
|
||||
return
|
||||
|
||||
# Acoid duplicates.
|
||||
|
|
@ -276,9 +280,11 @@ class App:
|
|||
and issubclass(value, Model)
|
||||
and not name == Model.__name__
|
||||
):
|
||||
# monkey-patch __module__ to point to well-known aliases
|
||||
value.__module__ = value.__module__.replace("ohmyapi.builtin.", "ohmyapi_")
|
||||
if value.__module__.startswith(mod_name):
|
||||
self._models[mod_name] = self._models.get(mod_name, []) + [value]
|
||||
logger.debug(f" - Model: {mod_name} -> {name}")
|
||||
|
||||
# if it's a package, recurse into submodules
|
||||
if hasattr(mod, "__path__"):
|
||||
|
|
@ -300,7 +306,6 @@ class App:
|
|||
try:
|
||||
importlib.import_module(mod_name)
|
||||
except ModuleNotFound:
|
||||
print(f"no routes detected: {mod_name}")
|
||||
return
|
||||
|
||||
# Avoid duplicates.
|
||||
|
|
@ -315,6 +320,7 @@ class App:
|
|||
for name, value in vars(mod).copy().items():
|
||||
if isinstance(value, APIRouter) and not name == APIRouter.__name__:
|
||||
self._routers[mod_name] = self._routers.get(mod_name, []) + [value]
|
||||
logger.debug(f" - Router: {mod_name} -> {name} -> {value.routes}")
|
||||
|
||||
# if it's a package, recurse into submodules
|
||||
if hasattr(mod, "__path__"):
|
||||
|
|
@ -330,7 +336,6 @@ class App:
|
|||
try:
|
||||
mod = importlib.import_module(mod_name)
|
||||
except ModuleNotFoundError:
|
||||
print(f"no middlewares detected: {mod_name}")
|
||||
return
|
||||
|
||||
getter = getattr(mod, "get", None)
|
||||
|
|
|
|||
|
|
@ -2,12 +2,16 @@ from pathlib import Path
|
|||
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
|
||||
from ohmyapi.core.logging import setup_logging
|
||||
|
||||
import shutil
|
||||
|
||||
# Base templates directory
|
||||
TEMPLATE_DIR = Path(__file__).parent / "templates"
|
||||
env = Environment(loader=FileSystemLoader(str(TEMPLATE_DIR)))
|
||||
|
||||
logger = setup_logging()
|
||||
|
||||
|
||||
def render_template_file(template_path: Path, context: dict, output_path: Path):
|
||||
"""Render a single Jinja2 template file to disk."""
|
||||
|
|
@ -60,7 +64,7 @@ def copy_static(dir_name: str, target_dir: Path):
|
|||
template_dir = TEMPLATE_DIR / dir_name
|
||||
target_dir = Path(target_dir)
|
||||
if not template_dir.exists():
|
||||
print(f"no templates found under: {dir_name}")
|
||||
logger.error(f"no templates found under: {dir_name}")
|
||||
return
|
||||
|
||||
for root, _, files in template_dir.walk():
|
||||
|
|
@ -69,19 +73,15 @@ def copy_static(dir_name: str, target_dir: Path):
|
|||
src = root_path / file
|
||||
dst = target_dir / file
|
||||
if dst.exists():
|
||||
print(f"⛔ File exists, skipping: {dst}")
|
||||
logger.warning(f"⛔ File exists, skipping: {dst}")
|
||||
continue
|
||||
shutil.copy(src, ".")
|
||||
print(f"✅ Templates created successfully.")
|
||||
print(f"🔧 Next, run `docker compose up -d --build`")
|
||||
shutil.copy(src, dst)
|
||||
|
||||
def startproject(name: str):
|
||||
"""Create a new project: flat structure, all project templates go into <name>/"""
|
||||
target_dir = Path(name).resolve()
|
||||
target_dir.mkdir(exist_ok=True)
|
||||
render_template_dir("project", target_dir, {"project_name": name})
|
||||
print(f"✅ Project '{name}' created successfully.")
|
||||
print(f"🔧 Next, configure your project in {target_dir / 'settings.py'}")
|
||||
|
||||
|
||||
def startapp(name: str, project: str):
|
||||
|
|
@ -94,5 +94,3 @@ def startapp(name: str, project: str):
|
|||
{"project_name": target_dir.resolve().name, "app_name": name},
|
||||
subdir_name=name,
|
||||
)
|
||||
print(f"✅ App '{name}' created in project '{target_dir}' successfully.")
|
||||
print(f"🔧 Remember to add '{name}' to your INSTALLED_APPS!")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue