import sys
import os
import datetime
import loguru
from pathlib import Path
import configparser
from functools import wraps
# 单例模式装饰器
def singleton_class_decorator(cls):
_instances = {}
@wraps(cls)
def wrapper_class(*args, **kwargs):
if cls not in _instances:
_instances[cls] = cls(*args, **kwargs)
return _instances[cls]
return wrapper_class
@singleton_class_decorator
class Logger:
def __init__(self, config_file="log.ini"):
self.config_file = config_file
self.logger_add()
def read_ini(self):
config = configparser.ConfigParser(inline_comment_prefixes=("#", ";"))
config.read(self.config_file, encoding="UTF-8")
return config
def get_project_path(self, project_path=None):
if project_path is None:
project_path = Path(__file__).parent.parent
return project_path
# def get_log_path(self):
# project_path = self.get_project_path()
# project_log_dir = Path(project_path, 'Logs') # Logs 文件夹路径
# project_log_filename = f'{datetime.date.today()}.log' # 以日期命名日志文件
# project_log_path = Path(project_log_dir, project_log_filename) # 拼接最终日志文件路径
# return project_log_path
def get_log_path(self, level=None):
project_path = self.get_project_path()
# 按照日志级别创建不同的目录
log_dir = (
Path(project_path, "Logs", level) if level else Path(project_path, "Logs")
)
log_dir.mkdir(parents=True, exist_ok=True) # 确保日志目录存在
log_filename = f"{datetime.date.today()}.log" # 日志文件名按日期生成
return Path(log_dir, log_filename)
def ensure_log_dir_exists(self, log_path):
if not os.path.exists(log_path.parent):
os.makedirs(log_path.parent)
def logger_add(self):
# 清空已有的日志处理器
loguru.logger.remove()
# 读取配置文件
config = self.read_ini()
# 控制台日志配置
if config.get("StderrLog", "is_open").lower() == "on":
loguru.logger.add(
sys.stderr,
format="[<green>{time:YYYY-MM-DD HH:mm:ss}</green> {level:<8}| "
"<cyan>{module}</cyan>.<cyan>{function}</cyan>:<cyan>{line}</cyan> | "
"<level>{message}</level>",
level=config.get("StderrLog", "level"),
)
# 文件日志配置
if config.get("FileLog", "is_open").lower() == "on":
# levels = ["DEBUG", "INFO", "SUCCESS", "WARNING", "ERROR", "CRITICAL"]
levels = ["DEBUG"]
for level in levels:
log_path = self.get_log_path(level.lower())
# print('log_path ', log_path)
# project_log_path = self.get_log_path()
# 确保日志目录存在
# self.ensure_log_dir_exists(project_log_path)
# print('>>>>>> ', project_log_path)
loguru.logger.add(
sink=log_path,
rotation=config.get("FileLog", "rotation"),
retention=config.get("FileLog", "retention"),
compression="zip",
encoding="utf-8",
enqueue=True,
format="{level:<8} | {time:YYYY-MM-DD HH:mm:ss} | {message}", # 控制输出格式
level=level,
serialize=config.getboolean("FileLog", "serialize"), # 启用 JSON 输出
)
# 邮件通知配置(可选)
if config.get("EmailLog", "is_open").lower() == "on":
# 假设配置有SMTP邮件通知
from loguru import logger
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
# 获取SMTP配置
smtp_server = config.get("EmailLog", "smtp_server")
smtp_port = config.get("EmailLog", "smtp_port")
smtp_user = config.get("EmailLog", "smtp_user")
smtp_password = config.get("EmailLog", "smtp_password")
to_email = config.get("EmailLog", "to_email")
# 邮件格式配置 没有调测
def send_email(record):
msg = MIMEMultipart()
msg["From"] = smtp_user
msg["To"] = to_email
msg["Subject"] = f"Log - {record['level'].name}"
body = MIMEText(record["message"], "plain", "utf-8")
msg.attach(body)
try:
with smtplib.SMTP(smtp_server, smtp_port) as server:
server.login(smtp_user, smtp_password)
server.sendmail(smtp_user, to_email, msg.as_string())
except Exception as e:
logger.error(f"发送邮件失败: {e}")
# 配置邮件处理器
loguru.logger.add(send_email, level="ERROR", format="{message}")
@property
def get_logger(self):
return loguru.logger
# 实例化日志类
logger = Logger().get_logger
if __name__ == "__main__":
logger.debug("调试代码")
logger.info("输出信息")
logger.success("输出成功")
logger.warning("警告信息")
logger.error("错误信息")
logger.critical("崩溃信息")
#
try:
1 / 0 # 故意抛出异常
except ZeroDivisionError as e:
logger.exception("发生了除零异常")
项目根目录下的:log.ini文件
[StderrLog]
is_open = on
level = DEBUG
[FileLog]
is_open = on
level = INFO
rotation = 500 MB
retention = 10 days
serialize = False ; True False
[EmailLog]
is_open = off
smtp_server = smtp.example.com
smtp_port = 587
smtp_user = your_email@example.com
smtp_password = your_password
to_email = recipient@example.com
import sys
import os
import datetime
import loguru
from pathlib import Path
import configparser
from functools import wraps
# 单例模式装饰器
def singleton_class_decorator(cls):
_instances = {}
@wraps(cls)
def wrapper_class(*args, **kwargs):
if cls not in _instances:
_instances[cls] = cls(*args, **kwargs)
return _instances[cls]
return wrapper_class
@singleton_class_decorator
class Logger:
def __init__(self, config_file="log.ini"):
self.config_file = config_file
self.logger_add()
def read_ini(self):
config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
config.read(self.config_file, encoding="UTF-8")
return config
def get_project_path(self, project_path=None):
if project_path is None:
project_path = Path(__file__).parent.parent
return project_path
def get_log_path(self, level=None):
project_path = self.get_project_path()
# 按照日志级别创建不同的目录
log_dir = Path(project_path, 'Logs', level) if level else Path(project_path, 'Logs')
log_dir.mkdir(parents=True, exist_ok=True) # 确保日志目录存在
log_filename = f'{datetime.date.today()}.log' # 日志文件名按日期生成
return Path(log_dir, log_filename)
def ensure_log_dir_exists(self, log_path):
if not os.path.exists(log_path.parent):
os.makedirs(log_path.parent)
def logger_add(self):
# 清空已有的日志处理器
loguru.logger.remove()
# 读取配置文件
config = self.read_ini()
# 获取 serialize 配置
serialize_logs = config.getboolean('FileLog', 'serialize')
print('serialize ', serialize_logs)
# 控制台日志配置
if config.get('StderrLog', 'is_open').lower() == "on":
loguru.logger.add(
sys.stderr,
format="[<green>{time:YYYY-MM-DD HH:mm:ss}</green> {level:<8}| "
"<cyan>{module}</cyan>.<cyan>{function}</cyan>:<cyan>{line}</cyan> | "
"<level>{message}</level>",
level=config.get('StderrLog', 'level'),
)
# 文件日志配置
if config.get('FileLog', 'is_open').lower() == "on":
project_log_path = self.get_log_path()
# 确保日志目录存在
self.ensure_log_dir_exists(project_log_path)
# 根据配置文件中的 serialize 字段决定输出格式
# if serialize_logs:
# loguru.logger.add(
# sink=project_log_path,
# rotation=config.get('FileLog', 'rotation'),
# retention=config.get('FileLog', 'retention'),
# compression='zip',
# encoding="utf-8",
# enqueue=True,
# level=config.get('FileLog', 'level'),
# serialize=True # 启用 JSON 格式
# )
# else:
# loguru.logger.add(
# sink=project_log_path,
# rotation=config.get('FileLog', 'rotation'),
# retention=config.get('FileLog', 'retention'),
# compression='zip',
# encoding="utf-8",
# enqueue=True,
# format="{level:<8} | {time:YYYY-MM-DD HH:mm:ss} | {message}", # 普通文本格式
# level=config.get('FileLog', 'level')
# )
loguru.logger.add(
sink=project_log_path,
rotation=config.get("FileLog", "rotation"),
retention=config.get("FileLog", "retention"),
compression="zip",
encoding="utf-8",
enqueue=True,
format="{level:<8} | {time:YYYY-MM-DD HH:mm:ss} | {message}", # 控制输出格式
level=config.get("FileLog", "level"),
serialize=config.getboolean("FileLog", "serialize"), # 启用 JSON 输出
)
# 邮件通知配置(可选)
if config.get('EmailLog', 'is_open').lower() == "on":
# 假设配置有SMTP邮件通知
from loguru import logger
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
# 获取SMTP配置
smtp_server = config.get('EmailLog', 'smtp_server')
smtp_port = config.get('EmailLog', 'smtp_port')
smtp_user = config.get('EmailLog', 'smtp_user')
smtp_password = config.get('EmailLog', 'smtp_password')
to_email = config.get('EmailLog', 'to_email')
# 邮件格式配置
def send_email(record):
msg = MIMEMultipart()
msg['From'] = smtp_user
msg['To'] = to_email
msg['Subject'] = f"Log - {record['level'].name}"
body = MIMEText(record['message'], 'plain', 'utf-8')
msg.attach(body)
try:
with smtplib.SMTP(smtp_server, smtp_port) as server:
server.login(smtp_user, smtp_password)
server.sendmail(smtp_user, to_email, msg.as_string())
except Exception as e:
logger.error(f"发送邮件失败: {e}")
# 配置邮件处理器
loguru.logger.add(send_email, level="ERROR", format="{message}")
@property
def get_logger(self):
return loguru.logger
# 实例化日志类
logger = Logger().get_logger
if __name__ == '__main__':
logger.debug('调试代码')
logger.info('输出信息')
logger.success('输出成功')
logger.warning('警告信息')
logger.error('错误信息')
logger.critical('崩溃信息')
try:
1 / 0 # 故意抛出异常
except ZeroDivisionError as e:
logger.exception('发生了除零异常')