ioe/inventory/utils/logging.py
2025-04-27 14:50:32 +08:00

155 lines
5.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Logging utilities for the inventory system.
"""
import logging
import json
import traceback
import functools
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.db import transaction
logger = logging.getLogger(__name__)
def get_client_ip(request):
"""Get client IP address from request."""
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ip = x_forwarded_for.split(',')[0]
else:
ip = request.META.get('REMOTE_ADDR')
return ip
def log_action(user, operation_type, details, related_object=None):
"""
Log an action in the system.
Args:
user (User): The user performing the action
operation_type (str): The type of operation (from OperationLog.OPERATION_TYPES)
details (str): Details about the operation
related_object (Model, optional): The object related to this operation
"""
from inventory.models import OperationLog
# Create operation log
log_entry = OperationLog(
operator=user,
operation_type=operation_type,
details=details
)
# If related object is provided, link it
if related_object:
content_type = ContentType.objects.get_for_model(related_object)
log_entry.related_content_type = content_type
log_entry.related_object_id = related_object.id
else:
# Handle the case when no related object is provided
# Get the default content type (User model can be used)
content_type = ContentType.objects.get_for_model(User)
log_entry.related_content_type = content_type
log_entry.related_object_id = user.id # Use the user's ID as fallback
log_entry.save()
return log_entry
def log_operation(user, operation_type, details, related_object=None, request=None):
"""
记录系统操作日志的主要入口函数
参数:
user (User): 执行操作的用户
operation_type (str): 操作类型来自OperationLog.OPERATION_TYPES
details (str): 操作详情
related_object (Model, optional): 与操作相关的对象
request (HttpRequest, optional): 当前请求对象用于获取IP等信息
返回:
OperationLog: 创建的日志记录对象
"""
from inventory.models import OperationLog
try:
# 准备日志内容
log_details = details
# 如果提供了请求对象,添加额外信息
if request:
ip = get_client_ip(request)
agent = request.META.get('HTTP_USER_AGENT', 'Unknown')
path = request.path
# 将额外信息添加到详细信息中
if isinstance(details, dict):
details.update({
'ip': ip,
'user_agent': agent,
'path': path
})
log_details = json.dumps(details)
else:
# 如果详细信息是字符串,附加额外信息
log_details = f"{details} [IP: {ip}, 路径: {path}]"
# 使用事务保证日志记录的原子性
with transaction.atomic():
return log_action(user, operation_type, log_details, related_object)
except Exception as e:
# 记录错误但不影响主程序流程
logger.error(f"记录操作日志时出错: {str(e)}", exc_info=True)
return None
def log_view_access(operation_type):
"""Decorator to log access to views."""
def decorator(view_func):
@functools.wraps(view_func)
def wrapper(request, *args, **kwargs):
# Get the result first
result = view_func(request, *args, **kwargs)
# Don't log for anonymous users
if request.user.is_authenticated:
try:
# Prepare details
details = {
'view': view_func.__name__,
'path': request.path,
'method': request.method,
'ip': get_client_ip(request)
}
# Log the access
log_action(
user=request.user,
operation_type=operation_type,
details=f"Accessed {view_func.__name__}: {json.dumps(details)}"
)
except Exception as e:
# Just log the error but don't affect the view's execution
logger.error(f"Error logging view access: {str(e)}", exc_info=True)
return result
return wrapper
return decorator
def log_exception(func):
"""Decorator to log exceptions in functions."""
@functools.wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
# 修复不再尝试在extra中传递args参数以避免与LogRecord内部的args冲突
logger.error(
f"Exception in {func.__name__}: {str(e)}",
exc_info=True,
extra={
'function_name': func.__name__,
'error_message': str(e),
'traceback_str': traceback.format_exc()
}
)
raise
return wrapper