OokamiPupV2/globals.py

124 lines
4.7 KiB
Python

import time
import json
import sys
import traceback
# Store the start time globally
_bot_start_time = time.time()
def get_bot_start_time():
"""Retrieve the bot's start time globally."""
return _bot_start_time
def load_config_file():
CONFIG_PATH = "config.json"
try:
with open(CONFIG_PATH, "r") as f:
config_data = json.load(f)
return config_data
except FileNotFoundError:
print("Error: config.json not found.")
sys.exit(1)
except json.JSONDecodeError as e:
print(f"Error parsing config.json: {e}")
sys.exit(1)
# Load configuration file
config_data = load_config_file()
###############################
# Simple Logging System
###############################
def log(message, level="INFO", exec_info=False):
"""
A simple logging function with adjustable log levels.
Logs messages in a structured format.
Available levels:\n
DEBUG = Information useful for debugging\n
INFO = Informational messages\n
WARNING = Something happened that may lead to issues\n
ERROR = A non-critical error has happened\n
CRITICAL = A critical, but non-fatal, error\n
FATAL = Fatal error. Program exits after logging this\n\n
See 'config.json' for disabling/enabling logging levels
"""
# Initiate logfile
lfp = config_data["logging"]["logfile_path"] # Log File Path
clfp = f"cur_{lfp}" # Current Log File Path
if not config_data["logging"]["terminal"]["log_to_terminal"] and not config_data["logging"]["file"]["log_to_file"]:
print(f"!!! WARNING !!! CONSOLE AND LOGFILE OUTPUT DISABLED !!!\n!!! NO LOGS WILL BE PROVIDED !!!")
from modules import utility
log_levels = {"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL", "FATAL"}
if level not in log_levels:
level = "INFO" # Default to INFO if an invalid level is provided
if level in config_data["logging"]["log_levels"] or level == "FATAL":
elapsed = time.time() - get_bot_start_time()
uptime_str, _ = utility.format_uptime(elapsed)
timestamp = time.strftime('%Y-%m-%d %H:%M:%S')
log_message = f"[{timestamp} - {uptime_str}] [{level}] {message}"
# Include traceback for certain error levels
if exec_info or level in ["CRITICAL", "FATAL"]:
log_message += f"\n{traceback.format_exc()}"
# Print to terminal if enabled
# 'FATAL' errors override settings
# Checks config file to see enabled/disabled logging levels
if config_data["logging"]["terminal"]["log_to_terminal"] or level == "FATAL":
config_level_format = f"log_{level.lower()}"
if config_data["logging"]["terminal"][config_level_format] or level == "FATAL":
print(log_message)
# Write to file if enabled
# 'FATAL' errors override settings
# Checks config file to see enabled/disabled logging levels
if config_data["logging"]["file"]["log_to_file"] or level == "FATAL":
config_level_format = f"log_{level.lower()}"
if config_data["logging"]["file"][config_level_format] or level == "FATAL":
try:
lfp = config_data["logging"]["logfile_path"]
clfp = f"cur_{lfp}"
with open(lfp, "a", encoding="utf-8") as logfile: # Write to permanent logfile
logfile.write(f"{log_message}\n")
logfile.flush() # Ensure it gets written immediately
with open(clfp, "a", encoding="utf-8") as c_logfile: # Write to this-run logfile
c_logfile.write(f"{log_message}\n")
c_logfile.flush() # Ensure it gets written immediately
except Exception as e:
print(f"[WARNING] Failed to write to logfile: {e}")
# Handle fatal errors with shutdown
if level == "FATAL":
print(f"!!! FATAL ERROR LOGGED, SHUTTING DOWN !!!")
sys.exit(1)
def reset_curlogfile():
# Initiate logfile
lfp = config_data["logging"]["logfile_path"] # Log File Path
clfp = f"cur_{lfp}" # Current Log File Path
try:
open(clfp, "w")
#log(f"Current-run logfile cleared", "DEBUG")
except Exception as e:
#log(f"Failed to clear current-run logfile: {e}")
pass
def init_db_conn():
try:
import modules.db
db_conn = modules.db.init_db_connection(config_data)
if not db_conn:
# If we get None, it means FATAL. We might sys.exit(1) or handle it differently.
log("Terminating bot due to no DB connection.", "FATAL")
sys.exit(1)
return db_conn
except Exception as e:
log(f"Unable to initialize database!: {e}", "FATAL")
return None