Compare commits

..

8 Commits

Author SHA1 Message Date
4f88ab21ad 上传文件至 Log4p/plugins 2024-03-17 14:08:54 +00:00
34bdabc4db 删除 Log4p/websocketHander.py 2024-03-17 14:08:21 +00:00
7f92741462 删除 Log4p/HttpHander.py 2024-03-17 14:08:14 +00:00
8108e1fc85 删除 Log4p/DecoratorsTools.py 2024-03-17 14:08:09 +00:00
e5aa752c59 上传文件至 Log4p 2024-03-17 14:07:17 +00:00
3866df01a3 上传文件至 Log4p 2024-03-17 14:06:09 +00:00
e86cee955e 上传文件至 / 2024-03-17 14:04:40 +00:00
f1e120c48a 删除 exceptionhooks.rar 2024-03-17 14:03:07 +00:00
12 changed files with 296 additions and 0 deletions

100
Log4p/Log4p.py Normal file
View File

@ -0,0 +1,100 @@
from Log4p.plugins_for_core import *
class LogManager:
def __init__(self) -> None:
self.public_formatter = logging.Formatter(
fmt='[%(asctime)s][%(name)s/%(levelname)s][%(funcName)s]:%(message)s',
datefmt='%H:%M:%S'
)
def GetLogger(self, log_name: str = "default",
out_to_console: bool = True,
web_log_mode: bool = False,
WSpost_url: str = "",
HTTPpost_url: str = "",
http_mode: bool = False,
custom_formatter: logging.Formatter = None):
# 确保日志名称有效
log_name = log_name if log_name else "default"
if out_to_console:
log_folder = f'./logs/{log_name}'
if not os.path.exists(log_folder):
os.makedirs(log_folder, exist_ok=True)
logger = logging.getLogger(log_name)
if logger.hasHandlers():
# Logger已经配置过处理器避免重复配置
return logger
# 颜色配置
log_color_config = {
'DEBUG': 'bold_blue', 'INFO': 'bold_cyan',
'WARNING': 'bold_yellow', 'ERROR': 'red',
'CRITICAL': 'bold_red', 'RESET': 'reset',
'asctime': 'green'
}
if out_to_console:
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)
console_formatter = colorlog.ColoredFormatter(
fmt='%(log_color)s [%(asctime)s][%(name)s/%(levelname)s][%(funcName)s]:%(message)s %(reset)s',
datefmt='%H:%M:%S',
log_colors=log_color_config
)
if custom_formatter:
console_formatter = custom_formatter
if isinstance(console_handler, logging.StreamHandler):
console_formatter = colorlog.ColoredFormatter(fmt=f"%(log_color)s {console_formatter._fmt} %(reset)s",datefmt=console_formatter.datefmt, log_colors=log_color_config)
console_handler.setFormatter(console_formatter)
logger.setLevel(logging.DEBUG)
logger.addHandler(console_handler)
file_handler = logging.FileHandler(
filename=f'logs/{log_name}/{log_name}.log', mode='a', encoding='utf-8')
file_handler.setLevel(logging.INFO)
file_handler.setFormatter(self.public_formatter)
if custom_formatter:
file_handler.setFormatter(custom_formatter)
# 检查代码是否在异步环境中运行
if asyncio.iscoroutinefunction(logging.Handler.emit):
queue = asyncio.Queue()
queue_handler = QueueHandler(queue)
queue_listener = QueueListener(queue, file_handler)
logger.addHandler(queue_handler)
asyncio.ensure_future(queue_listener.start())
else:
logger.addHandler(file_handler)
if web_log_mode and WSpost_url:
websocket_handler = WebsocketHandler(WSpost_url)
websocket_handler.setLevel(logging.INFO)
formatter = self.public_formatter
if custom_formatter:
formatter = custom_formatter
websocket_handler.setFormatter(formatter)
logger.addHandler(websocket_handler)
if http_mode and HTTPpost_url:
# 检查代码是否在异步环境中运行
if asyncio.iscoroutinefunction(logging.Handler.emit):
async_http_hander = AsyncHTTPhandler(HTTPpost_url)
async_http_hander.setLevel(logging.INFO)
formatter = self.public_formatter
if custom_formatter:
formatter = custom_formatter
async_http_hander.setFormatter(formatter)
logger.addHandler(async_http_hander)
http_handler = HTTPhandler(HTTPpost_url)
http_handler.setLevel(logging.INFO)
formatter = self.public_formatter
if custom_formatter:
formatter = custom_formatter
http_handler.setFormatter(formatter)
logger.addHandler(http_handler)
return logger

49
Log4p/Readme.MD Normal file
View File

@ -0,0 +1,49 @@
# Log4p日志库
## 描述
这是一个可以支持异步记录日志,发送到HTTP,websocket服务器,同时配置简单,它还实现了识别代码异步和同步的环境
以支持不同环境下的日志记录,当然了,理论上,这个日志如果2个模式都启用(websocket,http),那么会分别的往2个地方发送日志
### 示例用法
```python
#from core import *
from Log4p import LogManager
logger = LogManager().GetLogger(
log_name='example',
out_to_console=True,
web_log_mode=True,
WSpost_url='ws://localhost:8765',
HTTPpost_url='http://localhost:8765',
http_mode = True
custom_fomatter='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger.info('这是一个成功信息')
logger.debug('这是一个调试信息')
logger.critical('这是一个严重错误信息')
logger.error('这是一个错误信息')
logger.warning('这是一个警告信息')
```
### 参数介绍
- `log_name`: 指定日志记录器的名称
- `out_to_console`: 是否输出到控制台,默认true
- `web_log_mode`:是否启用websocket日志输出模式,默认false
- `WSpost_url`: 如果启用websocket模式,则传入post_url,否则不管,默认None
- `HTTPpost_url`: 如果启用http模式,则传入post_url,否则不管,默认None
- `http_mode`: 是否启用http日志输出模式,默认false
## 更新日志
# 2024/2/20 1:01
更新了自定义格式功能,修复了少量bug
## 添加参数
- `custom_formatter`: 自定义格式化函数,默认None

BIN
Log4p/__init__.py Normal file

Binary file not shown.

1
Log4p/core.py Normal file
View File

@ -0,0 +1 @@
from Log4p.Log4p import LogManager

View File

@ -0,0 +1,11 @@
import warnings
def deprecated(use_func, d_version):
def decorator(func):
async def wrapper(*args, **kwargs):
warnings.warn(f"The '{func.__name__}' function is deprecated, "
f"use '{use_func}' instead. Deprecated in version {d_version}.",
DeprecationWarning, stacklevel=2)
return await func(*args, **kwargs)
return wrapper
return decorator

View File

@ -0,0 +1,35 @@
import logging
import requests
import httpx
class HTTPhandler(logging.Handler):
def __init__(self, url):
super().__init__()
self.url = url
def emit(self, record):
log_entry = self.format(record)
payload = {'log': log_entry}
try:
response = requests.post(self.url, json=payload)
if not response.ok:
raise ValueError(response.text)
except Exception as e:
logging.error("Failed to send log to %s: %s", self.url, e)
class AsyncHTTPhandler(logging.Handler):
def __init__(self, url):
super().__init__()
self.url = url
async def emit(self, record):
log_entry = self.format(record)
payload = {'log': log_entry}
try:
async with httpx.AsyncClient(timeout=120,max_redirects=5) as client:
response = await client.post(self.url, json=payload)
if not response.is_success:
raise ValueError(await response.text())
except Exception as e:
logging.error("Failed to send log to %s: %s", self.url, e)

BIN
Log4p/plugins/__init__.py Normal file

Binary file not shown.

View File

@ -0,0 +1,28 @@
import logging
import asyncio
import websockets
class WebsocketHandler(logging.Handler):
def __init__(self, server_address):
super().__init__()
self.server_address = server_address
async def send_log_async(self, message):
async with websockets.connect(self.server_address) as websocket:
await websocket.send(message)
def send_log_sync(self, message):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(self.send_log_async(message))
def emit(self, record):
log_entry = self.format(record)
if asyncio.get_event_loop().is_running():
asyncio.create_task(self.send_log_async(log_entry))
else:
try:
self.send_log_sync(log_entry)
except Exception as e:
logging.warning("Failed to send log synchronously: %s", e)

View File

@ -0,0 +1,8 @@
import logging
import colorlog
import os
import asyncio
from logging.handlers import QueueHandler, QueueListener
from Log4p.plugins.websocketHander import WebsocketHandler
from Log4p.plugins.HttpHander import HTTPhandler , AsyncHTTPhandler
from Log4p.plugins.DecoratorsTools import *

24
Log4p/setup.py Normal file
View File

@ -0,0 +1,24 @@
from setuptools import setup, find_packages
setup(
name='Log4p',
version='1.0.0',
packages=find_packages(),
author='芙宁娜',
author_email='3072252442@qq.com',
description='A logging library for Python',
long_description=open('Readme.md','r',encoding='utf-8').read(),
long_description_content_type='text/markdown',
url='https://github.com/KOKOMI12345/Log4p',
install_requires=[
'requests',
'websockets',
'colorlog',
'httpx',
],
classifiers=[
'Programming Language :: Python :: 3',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
],
)

Binary file not shown.

40
global_exc_handler.py Normal file
View File

@ -0,0 +1,40 @@
import traceback,sys
from Log4p.core import *
Mainlogger = LogManager().GetLogger("MainThread")
def format_java_stack_trace(exctype, value, tb, nested=False):
tb_list = traceback.extract_tb(tb)
if nested:
exception_info = f"{exctype.__name__}: {value}\n"
else:
exception_info = f"Exception has occurred: {exctype.__name__}: {value}\n"
for filename, lineno, funcname, line in tb_list:
exception_info += f" at {funcname} ({filename}:{lineno})\n"
# 检查是否有原因和其他信息
cause = getattr(value, '__cause__', None)
context = getattr(value, '__context__', None)
if cause:
exception_info += "Caused by: "
exception_info += format_java_stack_trace(type(cause), cause, cause.__traceback__, nested=True)
if context:
exception_info += "Suppressed: "
exception_info += format_java_stack_trace(type(context), context, context.__traceback__, nested=True)
return exception_info
def exception_hook(exctype, value, tb):
# 获取回溯信息并格式化为字符串
tb_str = format_java_stack_trace(exctype, value, tb)
# 记录异常信息到日志
exception_info = "发生异常:\n"
exception_info += tb_str
Mainlogger.critical(exception_info)
sys.excepthook = exception_hook