Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions apps/common/log/log.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# coding=utf-8
"""
@project: MaxKB
@Author:虎虎
@file: log.py
@date:2025/6/4 14:13
@desc:
"""

from system_manage.models.log_management import Log


def _get_ip_address(request):
"""
获取ip地址
@param request:
@return:
"""
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 _get_user(request):
"""
获取用户
@param request:
@return:
"""
user = request.user
if user is None:
return {

}
return {
"id": str(user.id),
"email": user.email,
"phone": user.phone,
"nick_name": user.nick_name,
"username": user.username,
"role": user.role,
}


def _get_details(request):
path = request.path
body = request.data
query = request.query_params
return {
'path': path,
'body': body,
'query': query
}


def log(menu: str, operate, get_user=_get_user, get_ip_address=_get_ip_address, get_details=_get_details,
get_operation_object=None):
"""
记录审计日志
@param menu: 操作菜单 str
@param operate: 操作 str|func 如果是一个函数 入参将是一个request 响应为str def operate(request): return "操作菜单"
@param get_user: 获取用户
@param get_ip_address:获取IP地址
@param get_details: 获取执行详情
@param get_operation_object: 获取操作对象
@return:
"""

def inner(func):
def run(view, request, **kwargs):
status = 200
operation_object = {}
try:
if get_operation_object is not None:
operation_object = get_operation_object(request, kwargs)
except Exception as e:
pass
try:
return func(view, request, **kwargs)
except Exception as e:
status = 500
raise e
finally:
ip = get_ip_address(request)
user = get_user(request)
details = get_details(request)
_operate = operate
if callable(operate):
_operate = operate(request)
# 插入审计日志
Log(menu=menu, operate=_operate, user=user, status=status, ip_address=ip, details=details,
operation_object=operation_object).save()

return run

return inner
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The provided Python code defines several helper functions and a decorator to record audit logs. Here's an analysis of its content:

Helper Functions:

  1. _get_ip_address(request)

    • Retrieves the IP address from the request metadata, prioritizing HTTP_X_FORWARDED_FOR if available.
  2. _get_user(request)

    • Attempts to retrieve user information using request.user. If no user is found, it returns an empty dictionary.
  3. _get_details(request)

    • Extracts path, body, and query parameters from the request and returns them in a dictionary format.

Decorator Function (log):

  • Parameters:

    • menu: The menu item under which this action was performed.

    • operate: A string representation or function that describes what was done by the operator. The function can accept request as input and should return a string indicating the operation or error message.

    • Optional arguments like _get_user, _get_ip_address, _get_details, and _get_operation_object.

  • Inner Function run(view, request, **kwargs):

    • Executes the view associated with the request and catches exceptions to handle errors gracefully.
    • Logs the details including menu, operation description, status, IP address, additional details, user info, and optionally fetched object.
    • Saves the logging entry into the Log model defined in the imported module.

Potential Issues:

  1. Lack of Input Validation:

    • There's no input validation for important fields such as menu, operate, etc. This could lead to bugs where invalid inputs might result in unexpected behavior or security vulnerabilities.
  2. Error Handling:

    • The current implementation does not explicitly catch certain exceptions (e.g., database related) which might be raised during the execution of the decorated function.
    • It would be beneficial to include more robust error handling to ensure the application doesn't crash unexpectedly due to unhandled exceptions in views.
  3. Code Reusability:

    • The current design allows for flexibility but introduces complexity in understanding and maintaining different use cases when multiple decorators are nested or composed.
  4. Security Concerns:

    • Directly retrieving sensitive information like email addresses from the user without verifying its correctness raises concerns about potential data exposure risks.
  5. Performance Considerations:

    • Inserting records directly into a database in every successful API call adds some overhead; this will need evaluation based on expected load levels.

Optimization Suggestions:

  1. Input Validation and Error Handling:

    • Add comprehensive checks to verify that all necessary parameters are provided before processing requests.
    • Enhance exception handling logic to manage common errors and prevent crashes.
  2. Decomposition of Code:

    • Ensure each piece of functionality has minimal dependencies. Breaking down complex operations can make debugging easier.
    • Use modular patterns where practical.
  3. Logging Best Practices:

    • Implement proper logging strategies to track performance metrics (i.e., time taken), success/failure rates of operations per endpoint, and other relevant diagnostics.
    logger.info(f"Operation {operate} by User ID: {user['id']}")
  4. Database Efficiency:

    • Regularly review database schema changes to optimize write-heavy operations.
    • Explore batching writes for improved throughput.

By addressing these considerations, you can enhance both functional stability and performance while ensuring adherence to best practices for production applications.

23 changes: 19 additions & 4 deletions apps/maxkb/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@ class Config(dict):
# 语言
'LANGUAGE_CODE': 'zh-CN',
"DEBUG": False,
# redis 目前先支持单机 哨兵的配置后期加上
'REDIS_HOST': '127.0.0.1',
# 端口
'REDIS_PORT': 6379,
# 密码
'REDIS_PASSWORD': 'Password123@redis',
# 库
'REDIS_DB': 0,
# 最大连接数
'REDIS_MAX_CONNECTIONS': 100
}

def get_debug(self) -> bool:
Expand All @@ -51,12 +61,17 @@ def get_db_setting(self) -> dict:
}
}

@staticmethod
def get_cache_setting():
def get_cache_setting(self):
return {
'default': {
'BACKEND': 'diskcache.DjangoCache',
'LOCATION': f'{PROJECT_DIR}/data/cache'
'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': f'redis://{self.get("REDIS_HOST")}:{self.get("REDIS_PORT")}',
'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
"DB": self.get("REDIS_DB"),
"PASSWORD": self.get("REDIS_PASSWORD"),
"CONNECTION_POOL_KWARGS": {"max_connections": int(self.get("REDIS_MAX_CONNECTIONS"))}
},
},
}

Expand Down
33 changes: 33 additions & 0 deletions apps/system_manage/migrations/0004_log.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Generated by Django 5.2 on 2025-06-04 06:17

import common.encoder.encoder
import uuid
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('system_manage', '0003_workspaceuserresourcepermission_and_more'),
]

operations = [
migrations.CreateModel(
name='Log',
fields=[
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, verbose_name='修改时间')),
('id', models.UUIDField(default=uuid.uuid1, editable=False, primary_key=True, serialize=False, verbose_name='主键id')),
('menu', models.CharField(max_length=128, verbose_name='操作菜单')),
('operate', models.CharField(max_length=128, verbose_name='操作')),
('operation_object', models.JSONField(default=dict, encoder=common.encoder.encoder.SystemEncoder, verbose_name='操作对象')),
('user', models.JSONField(default=dict, verbose_name='用户信息')),
('status', models.IntegerField(verbose_name='状态')),
('ip_address', models.CharField(max_length=128, verbose_name='ip地址')),
('details', models.JSONField(default=dict, encoder=common.encoder.encoder.SystemEncoder, verbose_name='详情')),
],
options={
'db_table': 'log',
},
),
]
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code appears to be correctly structured for creating a new model called Log with various fields such as creation time, update time, ID, menu, operation, operation object, user information, status, IP address, and details. The use of UUID for the primary key is appropriate, and default values and encoders are set for JSONField.

However, there are a few suggestions for improvement:

  1. Version Information: Ensure that the database migration file includes version information at the top in accordance with Django's naming convention. For example:

    # Generated by django-migrate on 2025-06-04 06:17 UTC
  2. Comments: Add comments to describe each field and their purpose to improve maintainability.

  3. Validation: Consider adding validation checks for certain fields, especially those dealing with dates (update_time) and JSON data types.

Here’s an updated version of the code incorporating these improvements:

# Generated by django-migrate on 2025-06-04 06:17 UTC

import common.encoder.encoder
import uuid
from django.db import migrations, models


class Migration(migrations.Migration):

    dependencies = [
        ('system_manage', '0003_workspaceuserresourcepermission_and_more'),
    ]

    operations = [
        migrations.CreateModel(
            name='Log',
            fields=[
                (
                    'create_time',
                    models.DateTimeField(auto_now_add=True,
                                        verbose_name='创建时间'
                                        )
                ),
                (
                    'update_time',
                    models.DateTimeField(auto_now=True,
                                        verbose_name='修改时间')
                ),
                (
                    'id',
                    models.UUIDField(default=uuid.uuid1,
                                    editable=False,
                                    primary_key=True,
                                    serialize=False,
                                    verbose_name='主键id')
                ),
                (
                    'menu',
                    models.CharField(max_length=128,
                                     verbose_name='操作菜单')
                ),
                (
                    'operate',
                    models.CharField(max_length=128,
                                     verbose_name='操作')
                ),
                (
                    'operation_object',
                    models.JSONField(default=dict,
                                      encoder=common.encoder.encoder.SystemEncoder,
                                      verbose_name='操作对象')
                ),
                (
                    'user',
                    models.JSONField(default=dict,
                                      verbose_name='用户信息')
                ),
                (
                    'status',
                    models.IntegerField(verbose_name='状态')
                ),
                (
                    'ip_address',
                    models.CharField(max_length=128,
                                     verbose_name='IP地址')
                ),
                (
                    'details',
                    models.JSONField(default=dict,
                                      encoder=common.encoder.encoder.SystemEncoder,
                                      verbose_name='详情')
                ),
            ],
            options={
                'db_table': 'log',
            },
        ),
    ]

These changes help ensure clarity and correctness in the database schema definition.

1 change: 1 addition & 0 deletions apps/system_manage/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@
"""
from .workspace_user_permission import *
from .system_setting import *
from .log_management import *
38 changes: 38 additions & 0 deletions apps/system_manage/models/log_management.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# coding=utf-8
"""
@project: MaxKB
@Author:虎虎
@file: log_management.py
@date:2025/6/4 14:15
@desc:
"""
import uuid

from django.db import models

from common.encoder.encoder import SystemEncoder
from common.mixins.app_model_mixin import AppModelMixin


class Log(AppModelMixin):
"""
审计日志
"""
id = models.UUIDField(primary_key=True, max_length=128, default=uuid.uuid1, editable=False, verbose_name="主键id")

menu = models.CharField(max_length=128, verbose_name="操作菜单")

operate = models.CharField(max_length=128, verbose_name="操作")

operation_object = models.JSONField(verbose_name="操作对象", default=dict, encoder=SystemEncoder)

user = models.JSONField(verbose_name="用户信息", default=dict)

status = models.IntegerField(verbose_name="状态")

ip_address = models.CharField(max_length=128, verbose_name="ip地址")

details = models.JSONField(verbose_name="详情", default=dict, encoder=SystemEncoder)

class Meta:
db_table = "log"
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are no apparent irregularities or potential issues with the code provided. However, I can suggest a few minor improvements:

  1. Remove Unnecessary Docstrings: Since you're using docstring annotations (@@@coding:utf-8) instead of traditional comments, it would be better to remove the redundant docstrings within the class.

  2. Consistent Naming Style: Ensure that all variable names (e.g., menu, operate, etc.) follow consistent naming conventions if appropriate for your project.

Here's a revised version of the code without unnecessary changes but still follows good practices:

# coding=utf-8
"""
    @project: MaxKB
    @Author:虎虎
    @file: log_management.py
    @date:2025/6/4 14:15
    @desc:
"""

import uuid

from django.db import models

from common.encoder.encoder import SystemEncoder
from common.mixins.app_model_mixin import AppModelMixin


class Log(AppModelMixin):
    """
    审计日志
    """
    id = models.UUIDField(
        primary_key=True,
        max_length=128,
        default=uuid.uuid1,
        editable=False,
        verbose_name="主键id"
    )

    menu = models.CharField(max_length=128, verbose_name="操作菜单")

    operate = models.CharField(max_length=128, verbose_name="操作")

    operation_object = models.JSONField(
        verbose_name="操作对象",
        default=dict,
        encoder=SystemEncoder
    )

    user = models.JSONField(verbose_name="用户信息", default=dict)

    status = models.IntegerField(verbose_name="状态")

    ip_address = models.CharField(max_length=128, verbose_name="ip地址")

    details = models.JSONField(
        verbose_name="详情",
        default=dict,
        encoder=SystemEncoder
    )

    class Meta:
        db_table = "log"

This slight formatting change enhances readability while adhering to typical coding standards.

Loading