Skip to content

feat: log management #3189

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 4, 2025
Merged

feat: log management #3189

merged 1 commit into from
Jun 4, 2025

Conversation

shaohuzhang1
Copy link
Contributor

feat: log management

Copy link

f2c-ci-robot bot commented Jun 4, 2025

Adding the "do-not-merge/release-note-label-needed" label because no release-note block was detected, please follow our release note process to remove it.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

Copy link

f2c-ci-robot bot commented Jun 4, 2025

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:

The full list of commands accepted by this bot can be found here.

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@shaohuzhang1 shaohuzhang1 merged commit faeee1b into v2 Jun 4, 2025
3 of 4 checks passed
@shaohuzhang1 shaohuzhang1 deleted the pr@v2@feat_log_manage branch June 4, 2025 06:23

return run

return inner
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Code Review and Suggestions

1. Function Definitions

  • All functions (_get_ip_address, _get_user, log) should be at the top of the file to ensure readability.

2. Class Imports

  • Ensure that Log from system_manage.models.log_management is correctly imported and available in this module scope.

3. Method Decorators

  • The decorator pattern used in the run function can be made more concise using Python's functools.wraps.

  • Remove duplicate variable names like request. For example request can be simply referred to as view.request.

4. String Formatting

  • Use formatted string literals (f-strings) instead of concatenation for cleaner code.

  • Example: Replace "操作菜单" with {operate} if it's supposed to display the result of executing the operate function.

5. Error Handling

  • In rare cases where an error occurs during execution or logging, the exception handling may still lead to data inconsistencies because exceptions might occur within function(view, request, **kwargs) even when wrapped by another try-except block.
# Improve final exception catch statement to handle possible errors outside inner lambda function without interfering with original view logic
except Exception as e:
    status = 500
    print(f"Exception happened while processing {operate}, {e}")

6. Comments for Clarity

  • Add comments explaining the purpose of each part, especially around complex control structures and edge case handles such as when get_operation_object raises an error.

7. Return Statements

  • Consider whether all returned values are necessary or if the function could just return one value.

8. Logging Structure

  • While logging structure seems functional, consider how logs are managed over multiple requests and systems. How will you rotate, back up, and access theselogs efficiently?

9. Security Considerations

  • If sensitive information needs to be logged, ensure no personally identifiable information (PII) is recorded unless absolutely necessary, and make sure there's proper security measures implemented for accessing and managing these logs.

Final Revised Code:

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

from functools import wraps
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 {'error': 'User not found'}
    
    # Simplify dictionary construction for better readability
    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
    
    # Using f-string for clean formatting and improved readability 
    return {
        'path': path,
        'body': body,
        'query': query
    }


def log(menu, 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):
        """Wraps a view function to log audit events."""
            
        @wraps(func)
        def run(view, request, **kwargs):
            try:
                op_object = {}                
                if get_operation_object:
                    try:
                        op_object = get_operation_object(request, kwargs)
                    except Exception as e:
                        print(f"Error occurred fetching operation object - {e}")
                        
                response_obj = None                    
                try:
                    response_obj = func(view, request, **kwargs)
                    status_code = 200
                except Exception as e:
                    print(f"Inner function raised an unexpected error - {e}")
                    status_code = 500
                    
                # Log the audit event        
                Log(
                    menu=menu,
                    operate=getattr(response_obj, 'message', ''),   # Assuming 'response' has a 'message' attribute which holds the actual message
                    user=get_user(request),
                    status=status_code,
                    ip_address=get_ip_address(request),
                    details=get_details(request),
                    operation_object=obj,
                ).save()      
                 
            except Exception as e:
                print(f"Final catch handler failed to complete properly - {e}")
                traceback.print_exc()

        return run

    return inner

Key Changes Made:

  1. Imports: Moved class imports to maintain consistency and clarity.
  2. Function Enhancements:
    • Simplified dictionary contruction.
    • Used f-string format strings for readable output.
  3. Error Handling Improvement: Improved exception handling by catching them specifically in each appropriate context.
  4. Documentation: Added additional explanations about certain parts of the code for better understanding.
  5. Security Note: Highlighted importance of securing Sensitive information in logs.

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

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

Choose a reason for hiding this comment

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

There are several potential improvements and corrections needed in this Python code:

  1. UTF-8 Encoding: Ensure that all strings use UTF-8 encoding to avoid any compatibility issues.

  2. Imports: The uuid module may have been moved or renamed in later versions of Python. It's always safer to explicitly import modules from their original location rather than relying on implicit imports.

  3. Class Inheritance: Ensure that the AppModelMixin is correctly inherited and handles all necessary attributes and methods.

  4. JSON Fields Initialization: Initialize operation_object and user fields with JSON objects, not just empty dictionaries.

  5. Database Table Definition: Double-check the database table name log to ensure it matches your database schema.

Here’s an updated version of your code with these changes:

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

from django.conf import settings
from django.db import models

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


class Log(AppModelMixin):
    """
    审计日志
    """
    # Explicitly specify UUID type
    id = models.UUIDField(primary_key=True, max_length=128)

    # Define char fields properly
    menu = models.CharField(max_length=settings.MAX_LENGTH_MENU, verbose_name="操作菜单")

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

    # Initialize JSON fields properly
    operation_object = models.JSONField(verbose_name="操作对象", default={}, encoder=SystemEncoder)

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

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

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

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

    class Meta:
        db_table = "log"

Key Changes:

  • Used settings.MAX_LENGTH_MENU, settings.MAX_LENGTH_OPERATE, settings.MAX_LENGTH_IP_ADDRESS instead of hardcoding numbers directly.
  • Ensured explicit definition for the UUID field using models.UUIDField.
  • Initialized JSON fields properly with default values {} instead of dict.

Additional Recommendations:

  • Consider adding custom validation and sanitization logic for sensitive data handled by the operation_details field.
  • Review the migration script (if applicable) to ensure there aren't any breaking changes to the database schema. You can generate a new migration if necessary using Django's management commands (makemigrations, migrate).

'db_table': 'log',
},
),
]
Copy link
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 significant issues with the provided code snippet for creating a migration to introduce a new Log model. Here are some minor points you might consider:

  1. Default Value for UUID: You're using default=uuid.uuid1, which is generally fine, but ensure that this value adheres to your project's naming conventions and security requirements.

  2. Use of Django JSONField: In Django, there is specifically an OptionalValueJSONField if empty values should be treated as null instead of defaulting to {}. Consider updating your field declarations accordingly unless you prefer to handle empty objects natively.

  3. Version Check: The comment mentions "Generated by Django 5.2" from June 4th, 2025. If this migration is used in another environment where Django version may differ significantly, it could lead to compatibility issues. Ensure all relevant parts of your project are compatible with Django 5.2.

  4. Verbosity: Using custom encoders can add complexity and might not always perform well in production environments, especially when dealing with very large datasets or complex data structures. Review the efficiency impact of these choices.

Overall, the migration looks clean and straightforward, suitable for adding a logging system to your Django application.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant