Info
Nå skal vi bruke alt vi har lært om logging i en faktisk web-applikasjon! Vi bruker FastAPI - et populært Python-rammeverk for å lage API-er. Hvis du har brukt Flask fra før, vil mye av dette føles kjent! 🚀
Les mer: FastAPI dokumentasjon
Del 1 - Oppsett
Vi trenger et par pakker. Lag en requirements.txt:
fastapi
uvicorn
Installer med:
pip install -r requirements.txt
Hva er uvicorn?
uvicorn er en “ASGI server” - altså programmet som faktisk kjører FastAPI-appen din og lytter etter forespørsler. Tenk på det som motoren som driver web-serveren. 🏎️
Del 2 - Grunnleggende FastAPI med Logging
Her er en enkel FastAPI-app med skikkelig logging oppsatt:
Filstruktur
📁fastapi_logging
🐍main.py
🐍logger_config.py
📄requirements.txt
logger_config.py - Sentralisert logging-oppsett
Det er god praksis å ha logging-oppsettet i en egen fil, slik at alle delene av appen bruker samme konfigurasjon:
import logging
from logging.handlers import RotatingFileHandler
class ColorFormatter(logging.Formatter):
_colors = {
logging.CRITICAL: "\033[91m", # Red
logging.ERROR: "\033[91m", # Red
logging.WARNING: "\033[93m", # Yellow
logging.INFO: "\033[92m", # Green
logging.DEBUG: "\033[95m", # Magenta
}
_marks = {
logging.CRITICAL: "!",
logging.ERROR: "E",
logging.WARNING: "W",
logging.INFO: "*",
logging.DEBUG: "D",
}
_reset = "\033[0m"
_blue = "\033[94m"
_bold = "\033[1m"
def format(self, rec):
marks, colors = self._marks, self._colors
return (
f"{self._blue}{self._bold}[{colors[rec.levelno]}{marks[rec.levelno]}"
f"{self._blue}]{self._reset} {super().format(rec)}"
)
def setup_logger(name: str = "app") -> logging.Logger:
"""Sett opp en logger med farge i terminalen og detaljert fil-logging."""
logger = logging.getLogger(name)
logger.setLevel(logging.DEBUG)
# Unngå å legge til handlers flere ganger
if logger.handlers:
return logger
# --- Terminal Handler (INFO og oppover, med farger) ---
stream_handler = logging.StreamHandler()
stream_handler.setLevel(logging.INFO)
stream_formatter = ColorFormatter(
"[%(asctime)s] %(message)s",
datefmt="%Y-%m-%d %H:%M:%S"
)
stream_handler.setFormatter(stream_formatter)
# --- Fil Handler (ALT, inkludert DEBUG) ---
file_handler = RotatingFileHandler(
"app.log",
maxBytes=5_000_000, # 5 MB
backupCount=3
)
file_handler.setLevel(logging.DEBUG)
file_formatter = logging.Formatter(
"[%(asctime)s] %(levelname)-8s %(name)s (%(filename)s:%(lineno)d): %(message)s",
datefmt="%Y-%m-%d %H:%M:%S"
)
file_handler.setFormatter(file_formatter)
# Legg til begge handlers
logger.addHandler(stream_handler)
logger.addHandler(file_handler)
return logger
main.py - FastAPI appen
from fastapi import FastAPI, HTTPException
from logger_config import setup_logger
app = FastAPI()
logger = setup_logger("api")
@app.get("/")
async def root():
logger.info("Noen besøkte forsiden!")
return {"message": "Velkommen til mitt API! 🎉"}
@app.get("/products/{product_id}")
async def get_product(product_id: int):
logger.debug(f"Forespørsel mottatt for product_id={product_id}")
# Simulert database med noen produkter
products = {
1: {"name": "Laptop", "price": 9999},
2: {"name": "Tastatur", "price": 499},
3: {"name": "Mus", "price": 299},
}
if product_id not in products:
logger.warning(f"Produkt {product_id} finnes ikke!")
raise HTTPException(status_code=404, detail="Produkt ikke funnet")
logger.info(f"Returnerer produkt: {products[product_id]}")
return products[product_id]
@app.get("/error")
async def cause_error():
logger.error("Noen trigget en feil med vilje!")
raise HTTPException(status_code=500, detail="Noe gikk galt!")
For å kjøre appen:
uvicorn main:app --reload
--reload flagget
--reload gjør at serveren automatisk restarter når du endrer koden. Perfekt for utvikling! Men IKKE bruk det i produksjon. 🚧
Gå til http://127.0.0.1:8000 i nettleseren for å teste. Du kan også bruke http://127.0.0.1:8000/docs for å se den automatiske API-dokumentasjonen! 🤯
Oppgaver
Oppgave 3.1 - Sett opp prosjektet
- Lag filstrukturen som vist over
- Installer
fastapioguvicorn - Kopier koden fra
logger_config.pyogmain.py - Start appen med
uvicorn main:app --reload - Besøk nettsiden og se hva som vises i terminalen og i
app.log
Hva bør du se?
I terminalen (med farger!):
[*] [2026-04-09 12:00:00] Noen besøkte forsiden!
I app.log (uten farger, med mer detaljer):
[2026-04-09 12:00:00] INFO api (main.py:12): Noen besøkte forsiden!
Oppgave 3.2 - Utvid API-et
Legg til flere endpoints i FastAPI-appen din. For eksempel:
POST /products- Legg til et nytt produkt (logg påINFO-nivå)DELETE /products/{product_id}- Slett et produkt (logg påWARNING-nivå, fordi sletting er potensielt farlig!)GET /products- Hent alle produkter (logg påDEBUG-nivå)
Bruk passende logging-nivåer for hver operasjon. Husk å sjekke app.log for å se at DEBUG-meldinger havner der!
POST i FastAPI
from pydantic import BaseModel
class Product(BaseModel):
name: str
price: float
@app.post("/products")
async def create_product(product: Product):
logger.debug(f"Mottatt POST /products med data: {product.model_dump()}")
# ... lagre produkt
logger.info(f"Nytt produkt opprettet: {product.name} ({product.price} kr)")
return {"message": "Produkt opprettet!", "product": product}
Legg merke til at vi bruker DEBUG for å logge rådataene som kom inn, og INFO for å bekrefte at produktet ble opprettet. I app.log vil du se begge, men i terminalen kun INFO-meldingen!
Oppgave 3.3 - Separer loggfiler
Sett opp logging slik at:
app.log- InneholderINFOog høyere (generell drift)debug.log- InneholderDEBUGog høyere (alt, for feilsøking)error.log- Inneholder kunERRORogCRITICAL(for å raskt finne feil)
Oppdater setup_logger() funksjonen i logger_config.py til å ha tre FileHandler-instanser.
Tips
Du kan lage så mange handlers du vil! Bare lag en ny FileHandler for hver fil og sett riktig nivå med setLevel().