chore: standardize configuration, logging, and error handling across blockchain node and coordinator API

- Add infrastructure.md and workflow files to .gitignore to prevent sensitive info leaks
- Change blockchain node mempool backend default from memory to database for persistence
- Refactor blockchain node logger with StructuredLogFormatter and AuditLogger (consistent with coordinator)
- Add structured logging fields: service, module, function, line number
- Unify coordinator config with Database
This commit is contained in:
oib
2026-02-13 22:39:43 +01:00
parent 0cbd2b507c
commit 06e48ef34b
196 changed files with 4660 additions and 20090 deletions

View File

@@ -32,7 +32,7 @@ class ChainSettings(BaseSettings):
min_fee: int = 0 # Minimum fee to accept into mempool
# Mempool settings
mempool_backend: str = "memory" # "memory" or "database"
mempool_backend: str = "database" # "database" or "memory" (database recommended for persistence)
mempool_max_size: int = 10_000
mempool_eviction_interval: int = 60 # seconds

View File

@@ -1,13 +1,16 @@
from __future__ import annotations
import logging
import sys
from datetime import datetime
from typing import Any, Optional
import json
class JsonFormatter(logging.Formatter):
class StructuredLogFormatter(logging.Formatter):
"""Custom JSON formatter for structured logging - consistent with coordinator API."""
RESERVED = {
"name",
"msg",
@@ -34,8 +37,12 @@ class JsonFormatter(logging.Formatter):
def format(self, record: logging.LogRecord) -> str: # type: ignore[override]
payload: dict[str, Any] = {
"timestamp": datetime.utcnow().isoformat() + "Z",
"service": "aitbc-blockchain-node",
"level": record.levelname,
"logger": record.name,
"module": record.module,
"function": record.funcName,
"line": record.lineno,
"message": record.getMessage(),
}
@@ -45,27 +52,66 @@ class JsonFormatter(logging.Formatter):
payload[key] = value
if record.exc_info:
payload["exc_info"] = self.formatException(record.exc_info)
payload["exception"] = self.formatException(record.exc_info)
if record.stack_info:
payload["stack"] = record.stack_info
return json.dumps(payload, default=str)
def configure_logging(level: Optional[str] = None) -> None:
class AuditLogger:
"""Audit logger for tracking sensitive operations - consistent with coordinator API."""
def __init__(self, logger: logging.Logger):
self.logger = logger
def log(self, action: str, user_id: Optional[str] = None, resource_id: Optional[str] = None,
details: Optional[dict] = None, success: bool = True) -> None:
"""Log an audit event."""
self.logger.info(
"audit_event",
extra={
"audit": {
"action": action,
"user_id": user_id,
"resource_id": resource_id,
"details": details or {},
"success": success
}
}
)
def configure_logging(level: Optional[str] = None, json_format: bool = True) -> None:
"""Configure structured logging for the blockchain node."""
log_level = getattr(logging, (level or "INFO").upper(), logging.INFO)
root = logging.getLogger()
if root.handlers:
return
handler = logging.StreamHandler()
formatter = JsonFormatter()
handler.setFormatter(formatter)
root.handlers.clear()
if json_format:
handler = logging.StreamHandler(sys.stdout)
formatter = StructuredLogFormatter()
handler.setFormatter(formatter)
else:
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(
logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
)
root.addHandler(handler)
root.setLevel(log_level)
logging.getLogger('uvicorn').setLevel(log_level)
logging.getLogger('uvicorn.access').setLevel(log_level)
def get_logger(name: str) -> logging.Logger:
"""Get a logger instance."""
if not logging.getLogger().handlers:
configure_logging()
return logging.getLogger(name)
def get_audit_logger(name: str = "audit") -> AuditLogger:
"""Get an audit logger instance."""
return AuditLogger(get_logger(name))