Coverage for src/couchers/models/logging.py: 100%

25 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-12-25 10:58 +0000

1from datetime import datetime 

2 

3from sqlalchemy import BigInteger, Boolean, DateTime, Float, String, func 

4from sqlalchemy import LargeBinary as Binary 

5from sqlalchemy.orm import Mapped, mapped_column 

6from sqlalchemy.sql import expression 

7 

8from couchers.config import config 

9from couchers.models.base import Base 

10 

11 

12class APICall(Base): 

13 """ 

14 API call logs 

15 """ 

16 

17 __tablename__ = "api_calls" 

18 __table_args__ = {"schema": "logging"} 

19 

20 id: Mapped[int] = mapped_column(BigInteger, primary_key=True) 

21 

22 # whether the call was made using an api key or session cookies 

23 is_api_key: Mapped[bool] = mapped_column(Boolean, server_default=expression.false()) 

24 

25 # backend version (normally e.g. develop-31469e3), allows us to figure out which proto definitions were used 

26 # note that `default` is a python side default, not hardcoded into DB schema 

27 version: Mapped[str] = mapped_column(String, default=config["VERSION"]) 

28 

29 # approximate time of the call 

30 time: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) 

31 

32 # the method call name, e.g. "/org.couchers.api.core.API/ListFriends" 

33 method: Mapped[str] = mapped_column(String) 

34 

35 # gRPC status code name, e.g. FAILED_PRECONDITION, None if success 

36 status_code: Mapped[str | None] = mapped_column(String, nullable=True) 

37 

38 # handler duration (excluding serialization, etc) 

39 duration: Mapped[float] = mapped_column(Float) 

40 

41 # user_id of caller, None means not logged in 

42 user_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True) 

43 

44 # sanitized request bytes 

45 request: Mapped[bytes | None] = mapped_column(Binary, nullable=True) 

46 

47 # sanitized response bytes 

48 response: Mapped[bytes | None] = mapped_column(Binary, nullable=True) 

49 

50 # whether response bytes have been truncated 

51 response_truncated: Mapped[bool] = mapped_column(Boolean, server_default=expression.false()) 

52 

53 # the exception traceback, if any 

54 traceback: Mapped[str | None] = mapped_column(String, nullable=True) 

55 

56 # human readable perf report 

57 perf_report: Mapped[str | None] = mapped_column(String, nullable=True) 

58 

59 # details of the browser, if available 

60 ip_address: Mapped[str | None] = mapped_column(String, nullable=True) 

61 user_agent: Mapped[str | None] = mapped_column(String, nullable=True)