Logging¶
logging
¶
Flight logging module for Drone API.
Provides database logging for flight sessions, telemetry, and commands. Supports both SQLite (sync) and PostgreSQL (async) backends.
Example usage:
# SQLite (sync)
from pypack.logging import SQLiteLogger
logger = SQLiteLogger("flights.db")
session_id = logger.start_session(drone_id=1, notes="Test flight")
logger.log_telemetry(session_id, flight_data)
logger.end_session(session_id)
# PostgreSQL (async)
from pypack.logging import PostgresLogger
logger = PostgresLogger("postgresql://user:pass@localhost/drone")
await logger.connect()
session_id = await logger.start_session(drone_id=1)
await logger.log_telemetry(session_id, flight_data)
await logger.end_session(session_id)
FlightLogger
¶
Bases: ABC
Abstract interface for flight logging backends.
Implementations must provide both sync and async versions of methods. The sync versions can be simple wrappers around async versions using asyncio.run() for backends that are natively async.
start_session
abstractmethod
¶
Start a new flight session.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
drone_id
|
int
|
Drone identifier |
required |
notes
|
str | None
|
Optional session notes |
None
|
Returns:
| Type | Description |
|---|---|
str
|
Session ID (UUID string) |
end_session
abstractmethod
¶
End a flight session.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session_id
|
str
|
Session to end |
required |
get_session
abstractmethod
¶
get_session(session_id: str) -> FlightSession | None
Get session metadata.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session_id
|
str
|
Session ID |
required |
Returns:
| Type | Description |
|---|---|
FlightSession | None
|
FlightSession or None if not found |
list_sessions
abstractmethod
¶
list_sessions(drone_id: int | None = None, start_after: datetime | None = None, limit: int = 100) -> list[FlightSession]
List flight sessions.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
drone_id
|
int | None
|
Filter by drone ID |
None
|
start_after
|
datetime | None
|
Filter sessions after this time |
None
|
limit
|
int
|
Maximum sessions to return |
100
|
Returns:
| Type | Description |
|---|---|
list[FlightSession]
|
List of FlightSession |
log_telemetry
abstractmethod
¶
log_telemetry(session_id: str, data: FlightData) -> None
Log a telemetry data point.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session_id
|
str
|
Session ID |
required |
data
|
FlightData
|
Flight data to log |
required |
log_telemetry_batch
abstractmethod
¶
log_telemetry_batch(session_id: str, data: list[FlightData]) -> None
Log multiple telemetry data points in batch.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session_id
|
str
|
Session ID |
required |
data
|
list[FlightData]
|
List of flight data to log |
required |
get_telemetry
abstractmethod
¶
get_telemetry(session_id: str, start: datetime | None = None, end: datetime | None = None, limit: int = 10000) -> list[TelemetryRecord]
Retrieve telemetry for a session.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session_id
|
str
|
Session ID |
required |
start
|
datetime | None
|
Start time filter |
None
|
end
|
datetime | None
|
End time filter |
None
|
limit
|
int
|
Maximum records to return |
10000
|
Returns:
| Type | Description |
|---|---|
list[TelemetryRecord]
|
List of TelemetryRecord |
log_command
abstractmethod
¶
Log a command execution.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session_id
|
str
|
Session ID |
required |
command
|
str
|
Command name |
required |
params
|
dict
|
Command parameters |
required |
result
|
int
|
Result code |
required |
duration_ms
|
float
|
Execution duration in milliseconds |
required |
get_commands
abstractmethod
¶
get_commands(session_id: str, command_filter: str | None = None) -> list[CommandRecord]
Retrieve commands for a session.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session_id
|
str
|
Session ID |
required |
command_filter
|
str | None
|
Filter by command name (substring match) |
None
|
Returns:
| Type | Description |
|---|---|
list[CommandRecord]
|
List of CommandRecord |
FlightSession
¶
TelemetryRecord
¶
Bases: BaseModel
A single telemetry data point.
id
class-attribute
instance-attribute
¶
session_id
class-attribute
instance-attribute
¶
timestamp
class-attribute
instance-attribute
¶
pitch
class-attribute
instance-attribute
¶
altitude
class-attribute
instance-attribute
¶
barrier
class-attribute
instance-attribute
¶
from_flight_data
classmethod
¶
from_flight_data(session_id: str, data: FlightData) -> TelemetryRecord
Create TelemetryRecord from FlightData model.
CommandRecord
¶
SQLiteLogger
¶
Bases: FlightLogger
SQLite-backed flight logger.
Thread-safe for basic operations. Uses WAL mode for better concurrent read performance.
SCHEMA
class-attribute
instance-attribute
¶
SCHEMA = '\n -- Flight sessions table\n CREATE TABLE IF NOT EXISTS sessions (\n session_id TEXT PRIMARY KEY,\n drone_id INTEGER NOT NULL,\n start_time TEXT NOT NULL,\n end_time TEXT,\n notes TEXT\n );\n\n -- Telemetry data table\n CREATE TABLE IF NOT EXISTS telemetry (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n session_id TEXT NOT NULL,\n timestamp TEXT NOT NULL,\n pos_x REAL NOT NULL,\n pos_y REAL NOT NULL,\n pos_z REAL NOT NULL,\n vel_x REAL NOT NULL,\n vel_y REAL NOT NULL,\n vel_z REAL NOT NULL,\n yaw REAL NOT NULL,\n pitch REAL NOT NULL,\n roll REAL NOT NULL,\n altitude REAL NOT NULL,\n battery INTEGER NOT NULL,\n barrier INTEGER DEFAULT 0,\n FOREIGN KEY (session_id) REFERENCES sessions(session_id)\n );\n\n -- Commands table\n CREATE TABLE IF NOT EXISTS commands (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n session_id TEXT NOT NULL,\n timestamp TEXT NOT NULL,\n command TEXT NOT NULL,\n params TEXT,\n result INTEGER NOT NULL,\n duration_ms REAL NOT NULL,\n FOREIGN KEY (session_id) REFERENCES sessions(session_id)\n );\n\n -- Indexes for common queries\n CREATE INDEX IF NOT EXISTS idx_telemetry_session ON telemetry(session_id);\n CREATE INDEX IF NOT EXISTS idx_telemetry_timestamp ON telemetry(timestamp);\n CREATE INDEX IF NOT EXISTS idx_commands_session ON commands(session_id);\n CREATE INDEX IF NOT EXISTS idx_sessions_drone ON sessions(drone_id);\n CREATE INDEX IF NOT EXISTS idx_sessions_start ON sessions(start_time);\n '
start_session
¶
Start a new flight session.
list_sessions
¶
list_sessions(drone_id: int | None = None, start_after: datetime | None = None, limit: int = 100) -> list[FlightSession]
List flight sessions.
log_telemetry
¶
log_telemetry(session_id: str, data: FlightData) -> None
Log a telemetry data point.
log_telemetry_batch
¶
log_telemetry_batch(session_id: str, data: list[FlightData]) -> None
Log multiple telemetry data points in batch.
get_telemetry
¶
get_telemetry(session_id: str, start: datetime | None = None, end: datetime | None = None, limit: int = 10000) -> list[TelemetryRecord]
Retrieve telemetry for a session.
log_command
¶
Log a command execution.
get_commands
¶
get_commands(session_id: str, command_filter: str | None = None) -> list[CommandRecord]
Retrieve commands for a session.
FileLoggerMiddleware
¶
Thread-safe file logger that writes parsed MAVLink messages to JSONL files.
Files are organized as: logs/drone_YYYY-MM-DD.jsonl
Automatically rotates to a new file at midnight.
Usage:
CENTIDEGREE_FIELDS
class-attribute
instance-attribute
¶
log_message
¶
Log a parsed MAVLink message to the current day's log file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
msg
|
Any
|
MAVLink message object with get_msg_id() method |
required |
state
|
Any | None
|
Optional State object from StateProcessor |
None
|
CommandLogger
¶
Thread-safe logger for DroneAPI commands.
Logs every API method call with:
- Timestamp
- Method name
- Arguments (positional and keyword)
- Return value or exception
- Execution time
Files are organized as: logs/commands_YYYY-MM-DD.jsonl
Usage:
logger = CommandLogger("logs")
@logger.log
def move(self, direction: Direction, distance: int) -> CommandResult:
...
# Or wrap an existing instance:
drone = DroneAPI()
logged_drone = logger.wrap(drone)
exclude_methods
instance-attribute
¶
exclude_methods = exclude_methods or {'get_position', 'get_orientation', 'get_battery', 'get_obstacles', 'get_state', 'get_flight_data'}
log
¶
log_call
¶
log_call(method_name: str, args: dict[str, Any] | None = None, result: Any = None, success: bool = True, exception: Exception | None = None, elapsed_sec: float | None = None) -> None
Manually log a command call.
Useful when you can't use the decorator.
PostgresLogger
¶
Bases: FlightLogger
PostgreSQL-backed async flight logger.
Uses asyncpg for non-blocking database operations. Ideal for high-frequency telemetry logging.
Usage:
logger = PostgresLogger("postgresql://user:pass@localhost/drone")
await logger.connect()
session_id = await logger.start_session_async(drone_id=1)
await logger.log_telemetry_async(session_id, flight_data)
await logger.close_async()
SCHEMA
class-attribute
instance-attribute
¶
SCHEMA = '\n -- Flight sessions table\n CREATE TABLE IF NOT EXISTS sessions (\n session_id TEXT PRIMARY KEY,\n drone_id INTEGER NOT NULL,\n start_time TIMESTAMPTZ NOT NULL,\n end_time TIMESTAMPTZ,\n notes TEXT\n );\n\n -- Telemetry data table\n CREATE TABLE IF NOT EXISTS telemetry (\n id SERIAL PRIMARY KEY,\n session_id TEXT NOT NULL REFERENCES sessions(session_id),\n timestamp TIMESTAMPTZ NOT NULL,\n pos_x REAL NOT NULL,\n pos_y REAL NOT NULL,\n pos_z REAL NOT NULL,\n vel_x REAL NOT NULL,\n vel_y REAL NOT NULL,\n vel_z REAL NOT NULL,\n yaw REAL NOT NULL,\n pitch REAL NOT NULL,\n roll REAL NOT NULL,\n altitude REAL NOT NULL,\n battery INTEGER NOT NULL,\n barrier INTEGER DEFAULT 0\n );\n\n -- Commands table\n CREATE TABLE IF NOT EXISTS commands (\n id SERIAL PRIMARY KEY,\n session_id TEXT NOT NULL REFERENCES sessions(session_id),\n timestamp TIMESTAMPTZ NOT NULL,\n command TEXT NOT NULL,\n params JSONB,\n result INTEGER NOT NULL,\n duration_ms REAL NOT NULL\n );\n\n -- Indexes for common queries\n CREATE INDEX IF NOT EXISTS idx_telemetry_session ON telemetry(session_id);\n CREATE INDEX IF NOT EXISTS idx_telemetry_timestamp ON telemetry(timestamp);\n CREATE INDEX IF NOT EXISTS idx_commands_session ON commands(session_id);\n CREATE INDEX IF NOT EXISTS idx_sessions_drone ON sessions(drone_id);\n CREATE INDEX IF NOT EXISTS idx_sessions_start ON sessions(start_time);\n '
start_session_async
async
¶
Start a new flight session (async).
get_session_async
async
¶
get_session_async(session_id: str) -> FlightSession | None
Get session metadata (async).
list_sessions_async
async
¶
list_sessions_async(drone_id: int | None = None, start_after: datetime | None = None, limit: int = 100) -> list[FlightSession]
List flight sessions (async).
log_telemetry_async
async
¶
log_telemetry_async(session_id: str, data: FlightData) -> None
Log a telemetry data point (async).
log_telemetry_batch_async
async
¶
log_telemetry_batch_async(session_id: str, data: list[FlightData]) -> None
Log multiple telemetry data points in batch (async).
get_telemetry_async
async
¶
get_telemetry_async(session_id: str, start: datetime | None = None, end: datetime | None = None, limit: int = 10000) -> list[TelemetryRecord]
Retrieve telemetry for a session (async).
log_command_async
async
¶
log_command_async(session_id: str, command: str, params: dict, result: int, duration_ms: float) -> None
Log a command execution (async).
get_commands_async
async
¶
get_commands_async(session_id: str, command_filter: str | None = None) -> list[CommandRecord]
Retrieve commands for a session (async).
list_sessions
¶
list_sessions(drone_id: int | None = None, start_after: datetime | None = None, limit: int = 100) -> list[FlightSession]
get_telemetry
¶
get_telemetry(session_id: str, start: datetime | None = None, end: datetime | None = None, limit: int = 10000) -> list[TelemetryRecord]
log_command
¶
get_commands
¶
get_commands(session_id: str, command_filter: str | None = None) -> list[CommandRecord]
create_logging_wrapper
¶
create_logging_wrapper(obj: Any, logger: CommandLogger) -> Any
Create a wrapper that logs all method calls on an object.
This wraps ALL public methods (not starting with _) with logging.
For manual_fly(), automatically injects an on_frame callback to log each individual MANUAL_CONTROL frame sent.
Usage: