Improve logging with timestamps, icons, and detailed output

This commit is contained in:
2025-12-19 14:35:22 +01:00
parent 1cbf93c2df
commit 64de4164c4

View File

@@ -32,6 +32,11 @@ CONFIG_DEFAULTS = {
}
class System2MQTT:
@staticmethod
def _timestamp() -> str:
"""Get formatted timestamp for logging."""
return datetime.now().strftime('%Y-%m-%d %H:%M:%S')
def __init__(self, config_path: str = None):
self.config = self._load_config(config_path)
self.hostname = socket.gethostname()
@@ -88,10 +93,11 @@ class System2MQTT:
else:
config[k] = v
except Exception as e:
print(f"Warning: failed to load config file {config_path}: {e}")
print("Proceeding with defaults and environment overrides.")
print(f"⚠️ [{self._timestamp()}] WARNING: Failed to load config file '{config_path}': {e}")
print(f" [{self._timestamp()}] INFO: Proceeding with defaults and environment overrides")
else:
print(f"Config file '{config_path}' not found; using defaults and environment variables if set.")
print(f" [{self._timestamp()}] INFO: Config file '{config_path}' not found")
print(f" [{self._timestamp()}] INFO: Using defaults and environment variables")
# Ensure necessary sub-keys exist
config.setdefault('mqtt', CONFIG_DEFAULTS['mqtt'].copy())
@@ -101,6 +107,9 @@ class System2MQTT:
# Apply environment variable overrides
self._merge_env_overrides(config)
# Log loaded config path
print(f" [{self._timestamp()}] INFO: Loaded configuration from '{config_path}'")
return config
def _merge_env_overrides(self, config: Dict[str, Any]):
@@ -116,7 +125,7 @@ class System2MQTT:
try:
config['mqtt']['port'] = int(os.environ['MQTT_PORT'])
except ValueError:
print("Warning: MQTT_PORT is not an integer; ignoring env override")
print(f"⚠️ [{self._timestamp()}] WARNING: MQTT_PORT environment variable is not an integer; ignoring override")
if 'MQTT_USERNAME' in os.environ:
config['mqtt']['username'] = os.environ['MQTT_USERNAME']
if 'MQTT_PASSWORD' in os.environ:
@@ -131,7 +140,7 @@ class System2MQTT:
try:
config['collectors']['default_interval'] = int(os.environ['COLLECTORS_DEFAULT_INTERVAL'])
except ValueError:
print("Warning: COLLECTORS_DEFAULT_INTERVAL is not an integer; ignoring env override")
print(f"⚠️ [{self._timestamp()}] WARNING: COLLECTORS_DEFAULT_INTERVAL is not an integer; ignoring override")
# Per-collector overrides
for key, val in os.environ.items():
@@ -143,7 +152,7 @@ class System2MQTT:
try:
config['collectors']['intervals'][name] = int(val)
except ValueError:
print(f"Warning: {key} must be an integer; ignoring")
print(f"⚠️ [{self._timestamp()}] WARNING: Collector interval '{key}' must be an integer; ignoring")
def _setup_mqtt_client(self) -> mqtt.Client:
"""Setup paho-mqtt client with configuration (callback API v2 when available)."""
@@ -168,15 +177,21 @@ class System2MQTT:
except Exception:
rc_val = 0
if rc_val == 0:
print("Connected to MQTT broker")
mqtt_host = self.config['mqtt']['host']
mqtt_port = self.config['mqtt']['port']
print(f"✓ [{self._timestamp()}] SUCCESS: Connected to MQTT broker at {mqtt_host}:{mqtt_port}")
self.connected = True
else:
print(f"Failed to connect to MQTT broker with code: {rc_val}")
mqtt_host = self.config['mqtt']['host']
mqtt_port = self.config['mqtt']['port']
print(f"✗ [{self._timestamp()}] ERROR: Failed to connect to MQTT broker at {mqtt_host}:{mqtt_port} (code: {rc_val})")
self.connected = False
def _on_disconnect(self, client, userdata, rc, reason_code=None, properties=None):
"""Callback when disconnected (paho v2)."""
print("Disconnected from MQTT broker")
mqtt_host = self.config['mqtt']['host']
mqtt_port = self.config['mqtt']['port']
print(f"⚠️ [{self._timestamp()}] INFO: Disconnected from MQTT broker at {mqtt_host}:{mqtt_port}")
self.connected = False
def _get_device_info(self) -> Dict[str, Any]:
@@ -226,14 +241,14 @@ class System2MQTT:
'name': module_name,
'interval': interval
})
print(f"Loaded collector: {module_name} (interval: {interval}s)")
print(f"✓ [{self._timestamp()}] SUCCESS: Loaded collector '{module_name}' (update interval: {interval}s)")
return collectors
async def process_collector_data(self, data: Dict[str, Any]):
"""Process data from collectors and publish to MQTT."""
if not self.connected:
print("Not connected to MQTT broker")
print(f"⚠️ [{self._timestamp()}] WARNING: Cannot process collector data - not connected to MQTT broker")
return
# Publish discovery messages for each entity
@@ -295,11 +310,12 @@ class System2MQTT:
try:
data = collector['module'].collect_metrics()
entity_count = len(data.get('entities', []))
await self.process_collector_data(data)
self.last_run[collector['name']] = datetime.now()
print(f"Updated {collector['name']} metrics")
print(f"✓ [{self._timestamp()}] SUCCESS: Updated {entity_count} metrics from '{collector['name']}'")
except Exception as e:
print(f"Error collecting metrics from {collector['name']}: {e}")
print(f"✗ [{self._timestamp()}] ERROR: Failed to collect metrics from '{collector['name']}': {e}")
async def connect(self):
"""Connect to MQTT broker using paho-mqtt and wait briefly for on_connect."""
@@ -313,7 +329,9 @@ class System2MQTT:
break
await asyncio.sleep(0.1)
except Exception as e:
print(f"Error connecting to MQTT broker: {e}")
mqtt_host = self.config['mqtt']['host']
mqtt_port = self.config['mqtt']['port']
print(f"✗ [{self._timestamp()}] FATAL: Cannot connect to MQTT broker at {mqtt_host}:{mqtt_port}: {e}")
sys.exit(1)
async def disconnect(self):
@@ -327,6 +345,7 @@ class System2MQTT:
async def async_main():
"""Async main function."""
print(f"🚀 [{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] INFO: Starting system2mqtt...")
system2mqtt = System2MQTT()
await system2mqtt.connect()
@@ -340,9 +359,10 @@ async def async_main():
await asyncio.sleep(1)
except KeyboardInterrupt:
print("\nShutting down...")
print(f"\n⚠️ [{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] INFO: Received shutdown signal, stopping...")
finally:
await system2mqtt.disconnect()
print(f"✓ [{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] INFO: Shutdown complete")
if __name__ == "__main__":
asyncio.run(async_main())