- Added cleanup_old_logs() function to app_v3_simplified.py - Deletes log.txt if older than 15 days at app startup - Sends notification to monitoring server when cleanup occurs - Archived all legacy modules and documentation to oldcode/ - Updated device_info.txt with correct IP (192.168.1.104) - All changes validated and tested
224 lines
6.8 KiB
Python
224 lines
6.8 KiB
Python
"""
|
|
Enhanced Logging with Batch Queue
|
|
Groups multiple logs and sends them efficiently to reduce network traffic
|
|
- Sends logs in batches every 5 seconds or when queue reaches 10 items
|
|
- Reduces 3-4 logs/sec to 1 batch/5 sec (~75% reduction)
|
|
- Deduplicates repetitive events
|
|
"""
|
|
|
|
import logging
|
|
import os
|
|
from datetime import datetime, timedelta
|
|
import requests
|
|
import threading
|
|
import time
|
|
from queue import Queue
|
|
from config_settings import LOG_FILENAME, LOG_FORMAT, LOG_RETENTION_DAYS, MONITORING_SERVER_URL, REQUEST_TIMEOUT
|
|
|
|
# Global batch queue
|
|
log_batch_queue = Queue()
|
|
batch_thread = None
|
|
BATCH_TIMEOUT = 5 # Send batch every 5 seconds
|
|
MAX_BATCH_SIZE = 10 # Send if queue reaches 10 items
|
|
last_event_hash = {} # Track repeated events to avoid duplicates
|
|
|
|
|
|
def setup_logging():
|
|
"""Configure the logging system"""
|
|
logging.basicConfig(
|
|
filename=LOG_FILENAME,
|
|
level=logging.INFO,
|
|
format=LOG_FORMAT
|
|
)
|
|
return logging.getLogger(__name__)
|
|
|
|
|
|
def read_masa_name():
|
|
"""Read the table/room name (idmasa) from file"""
|
|
from config_settings import ID_MASA_FILE
|
|
try:
|
|
with open(ID_MASA_FILE, "r") as file:
|
|
n_masa = file.readline().strip()
|
|
return n_masa if n_masa else "unknown"
|
|
except FileNotFoundError:
|
|
logging.error(f"File {ID_MASA_FILE} not found.")
|
|
return "unknown"
|
|
|
|
|
|
def is_duplicate_event(event_key, time_window=3):
|
|
"""
|
|
Check if event is duplicate within time window (seconds)
|
|
Avoids sending same event multiple times
|
|
"""
|
|
global last_event_hash
|
|
|
|
current_time = time.time()
|
|
|
|
if event_key in last_event_hash:
|
|
last_time = last_event_hash[event_key]
|
|
if current_time - last_time < time_window:
|
|
return True # Duplicate within time window
|
|
|
|
last_event_hash[event_key] = current_time
|
|
return False
|
|
|
|
|
|
def send_batch_to_server(batch_logs, hostname, device_ip):
|
|
"""
|
|
Send batch of logs to monitoring server efficiently
|
|
Groups all logs in one HTTP request
|
|
"""
|
|
if not batch_logs:
|
|
return True
|
|
|
|
try:
|
|
n_masa = read_masa_name()
|
|
|
|
# Create batch payload
|
|
batch_payload = {
|
|
"hostname": str(hostname),
|
|
"device_ip": str(device_ip),
|
|
"nume_masa": str(n_masa),
|
|
"batch_timestamp": datetime.now().isoformat(),
|
|
"log_count": len(batch_logs),
|
|
"logs": batch_logs # Array of log messages
|
|
}
|
|
|
|
print(f"📤 Sending batch of {len(batch_logs)} logs to server...")
|
|
|
|
# Send batch
|
|
response = requests.post(
|
|
MONITORING_SERVER_URL,
|
|
json=batch_payload,
|
|
timeout=REQUEST_TIMEOUT
|
|
)
|
|
response.raise_for_status()
|
|
|
|
logging.info(f"Batch of {len(batch_logs)} logs sent successfully")
|
|
print(f"✓ Batch sent successfully")
|
|
return True
|
|
|
|
except requests.exceptions.Timeout:
|
|
logging.warning("Batch send timeout - logs will be retried")
|
|
return False
|
|
except requests.exceptions.ConnectionError:
|
|
logging.error("Connection error sending batch - logs queued for retry")
|
|
return False
|
|
except Exception as e:
|
|
logging.error(f"Failed to send batch: {e}")
|
|
return False
|
|
|
|
|
|
def batch_worker(hostname, device_ip):
|
|
"""
|
|
Background worker thread that processes log queue
|
|
Groups logs and sends them in batches
|
|
"""
|
|
print("✓ Log batch worker started")
|
|
|
|
current_batch = []
|
|
last_send_time = time.time()
|
|
|
|
while True:
|
|
try:
|
|
# Try to get log from queue (timeout after 1 second)
|
|
try:
|
|
log_entry = log_batch_queue.get(timeout=1)
|
|
current_batch.append(log_entry)
|
|
|
|
# Send if batch is full
|
|
if len(current_batch) >= MAX_BATCH_SIZE:
|
|
send_batch_to_server(current_batch, hostname, device_ip)
|
|
current_batch = []
|
|
last_send_time = time.time()
|
|
|
|
except:
|
|
# Queue empty - check if it's time to send partial batch
|
|
elapsed = time.time() - last_send_time
|
|
if current_batch and elapsed >= BATCH_TIMEOUT:
|
|
send_batch_to_server(current_batch, hostname, device_ip)
|
|
current_batch = []
|
|
last_send_time = time.time()
|
|
|
|
except Exception as e:
|
|
logging.error(f"Batch worker error: {e}")
|
|
time.sleep(1)
|
|
|
|
|
|
def start_batch_logger(hostname, device_ip):
|
|
"""Start the background batch processing thread"""
|
|
global batch_thread
|
|
|
|
if batch_thread is None or not batch_thread.is_alive():
|
|
batch_thread = threading.Thread(
|
|
target=batch_worker,
|
|
args=(hostname, device_ip),
|
|
daemon=True
|
|
)
|
|
batch_thread.start()
|
|
return True
|
|
return False
|
|
|
|
|
|
def queue_log_message(log_message, hostname, device_ip, event_key=None):
|
|
"""
|
|
Queue a log message for batch sending
|
|
|
|
Args:
|
|
log_message: Message to log
|
|
hostname: Device hostname
|
|
device_ip: Device IP
|
|
event_key: Optional unique key to detect duplicates
|
|
"""
|
|
# Check for duplicates
|
|
if event_key and is_duplicate_event(event_key):
|
|
logging.debug(f"Skipped duplicate event: {event_key}")
|
|
return
|
|
|
|
# Add to local log file
|
|
n_masa = read_masa_name()
|
|
formatted_message = f"{log_message} (n_masa: {n_masa})"
|
|
logging.info(formatted_message)
|
|
|
|
# Queue for batch sending
|
|
log_batch_queue.put({
|
|
"timestamp": datetime.now().isoformat(),
|
|
"message": log_message,
|
|
"event_key": event_key or log_message
|
|
})
|
|
|
|
|
|
def log_with_server(message, hostname, device_ip, event_key=None):
|
|
"""
|
|
Log message and queue for batch sending to server
|
|
|
|
Args:
|
|
message: Message to log
|
|
hostname: Device hostname
|
|
device_ip: Device IP
|
|
event_key: Optional unique event identifier for deduplication
|
|
"""
|
|
queue_log_message(message, hostname, device_ip, event_key)
|
|
|
|
|
|
def delete_old_logs():
|
|
"""Delete log files older than LOG_RETENTION_DAYS"""
|
|
from config_settings import LOG_FILE
|
|
|
|
if os.path.exists(LOG_FILE):
|
|
file_mod_time = datetime.fromtimestamp(os.path.getmtime(LOG_FILE))
|
|
if datetime.now() - file_mod_time > timedelta(days=LOG_RETENTION_DAYS):
|
|
try:
|
|
os.remove(LOG_FILE)
|
|
logging.info(f"Deleted old log file: {LOG_FILE}")
|
|
except Exception as e:
|
|
logging.error(f"Failed to delete log file: {e}")
|
|
else:
|
|
logging.info(f"Log file is not older than {LOG_RETENTION_DAYS} days")
|
|
else:
|
|
logging.info(f"Log file does not exist: {LOG_FILE}")
|
|
|
|
|
|
# Initialize logger at module load
|
|
logger = setup_logging()
|