• 运维开发笔记整理-django日志配置


                    运维开发笔记整理-django日志配置

                                           作者:尹正杰 

    版权声明:原创作品,谢绝转载!否则将追究法律责任。

    一.Django日志

      Django使用python内建的logging模块打印日志,Python的logging配置由四个部分组成:

    1>.记录器(Logger)

    2>.处理程序(Handler)

    3>.过滤器(Filter)

    4>.格式化(Formatter)

      更多关于logging模块的配置信息,详情请参考:https://yiyibooks.cn/xx/python_352/library/logging.html

    二.记录器(Logger)

    1>.Logger为日志系统的入口。每个logger命名都是bucket,你可以向这个bucket写入需要处理的消息;

    2>.每个logger都有一个日志级别。日志级别表示该logger将要处理的消息的严重性。Python定义以下几种日志级别:

      DEBUG:
          用于调试目的的底层系统信息   INFO:
          普通的系统信息   WARNING:
          表示出现一个较小的问题   ERROR:
          表示出现一个较大的问题   CRITICAL:
          表示出现一个致命的问题

    3>.写入logger的每条消息都是一条日志。每条日志也具有一个日志级别,它表示对应的消息的严重性。每个日志记录还可以包含描述正在打印的时间的元信息;

    4>.当一条消息传递给logger是,消息的日志级别将与logger的日志级别进行比较。如果消息的日志级别大雨等于logger的日志级别,该消息将会往下继续处理。如果小雨,该消息将被忽略;

    5>.Logger一旦决定消息需要处理,它将传递消息给一个Handler;

    三.logger的日志级别

    级别            值          描述
    CRITICAL     
    50     关键错误/消息
    ERROR       
    40     错误
    WARNING       
    30     警告消息
    INFO        
    20     通知消息
    DEBGU        
    10     调试
    NOTSET        0      无级别

    四.Logger配置

       logger对应的值是个字典,其每一个键都是logger的名字,每一个值又是个字典,描述了如何配置对应的Logger实力。

    level(可选的):
        logger的级别。
    
    propagate(可选的):
        logger的传播设置。
    
    filters(可选的):
        logger的filter的标识符的列表。
    
    handllers(可选的):
        logger的handler的标识符的列表。
    LOGGING = {
        'loggers': {
            'devops': {
                'handlers': ['file_handler', 'console_handler'],
                'level': 'DEBUG',
            },
        },
    }
    Logger配置示例

      想要了解更多logger,详情请参考:https://yiyibooks.cn/xx/python_352/library/logging.html#logger-objects

     

    五.处理程序(Handler)

    1>.Handler决定如何处理logger中的每条消息。它表示一个特定的日志行为,例如将消息写到屏幕上,写到文件中或者写到网络socket。

    2>.与logger一样,handler也有一个日志级别。如果消息的日志级别小雨handler的级别,handler将忽略该消息。

    3>.Logger可以有多个handler,而每个handler可以有不同的日志级别。利用这种方式,可以根据消息的重要性提供不同形式的处理。

    "handlers": {
            "file": {
                "level": "DEBUG",
                "class": "logging.handlers.TimedRotatingFileHandler",
                "filename": os.path.join(BASE_DIR, "logs", "debug.log"),
                "when":"S",
                "interval":5,
                "formatter": "default"
            },
    },
    Handler示例

      想要了解更多handler,详情请参考:https://yiyibooks.cn/xx/python_352/library/logging.html#handler-objects

      想要了解更多loggin-handler,详情请参考:https://yiyibooks.cn/xx/python_352/library/logging.handlers.html

    六.过滤器(Filters)

    1>.Filter用于对从logger传递给handler的日志记录进行额外的控制。

    2>.默认情况下,满足日志级别的任何消息都将被处理。通过安装一个filter,你可以对日志处理添加额外的条件。例如,你可以安装一个filter,只允许处理来自特定源的ERROR消息;

    3>.Filters还可以用于修改将要处理的日志记录的优先级。例如,如果日志记录满足特定的条件,你可以编写一个filter将日志记录从ERROR将为WARNING(我想说然而并没有什么乱用!);

    4>.Filters可以安装在logger是那个或者多个handler上;多个filter可以串联起来实现多层filter行为;

    七.格式化(Formatters)

      日志记录需要转换成文本。Formatter表示文本的格式。Fomatter通常由包含日志记录属性的Python格式字符串组成;你也可以编写自定义的fomatter来实现自己的格式。

    LOGGING = { 
        'formatters': { 
            ‘devops’:{ 
                'format': '%(asctime)s - %(pathname)s:%(lineno)d[%(levelname)s] - %(message)s' 
            } 
            'simple': { 
                'format': '%(asctime)s %(levelname)s %(message)s' 
            },
         }, 
    } 
    Formatters的案例展示

      更多formatters,详情请参考:https://yiyibooks.cn/xx/python_352/library/logging.html#formatter-objects 

    Format日志消息格式                        描述
    %(name)s                                记录器的名称
    %(levelno)s                                数字形式的日志记录级别
    %(levelname)s                              日志记录级别的文本名称
    %(filename)s                              执行日志记录调用的源文件的文件名称
    %(pathname)s                              执行日志记录调用的源文件的路径名称
    %(funcName)s                              执行日志记录调用的函数名称
    %(module)s                               执行日志记录调用的模块名称
    %(lineno)s                               执行日志记录调用的行号
    %(created)s                              执行日志记录的时间
    %(asctime)s                              日期和时间
    %(msecs)s                               毫秒部分
    %(thread)s                               线程ID
    %(threadName)s                           线程名称
    %(process)s                              进程ID
    %(message)s                              记录的消息 

    八.Django内置logger

    1>.django

      获取所有日志。

    2>.django.request

      处理与请求相关的日志,5xx响应报出error,400报出WARNING日志。

    3>.django.db.backends

      处理与数据库之间的交互的日志。

    4>.django.security.*

       处理与安全相关的日志。

    5>.django.db.backends.schemea

      处理数据库迁移时的日志。

      想要了解更多Django内置的logger详情请参考:https://docs.djangoproject.com/en/1.11/topics/logging/#id3

     

    九.使用python的logging模块案例展示

    """DevOps URL Configuration
    
    The `urlpatterns` list routes URLs to views. For more information please see:
        https://docs.djangoproject.com/en/1.11/topics/http/urls/
    Examples:
    Function views
        1. Add an import:  from my_app import views
        2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
    Class-based views
        1. Add an import:  from other_app.views import Home
        2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
    Including another URLconf
        1. Import the include() function: from django.conf.urls import url, include
        2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))
    """
    from django.conf.urls import url,include
    from django.contrib import admin
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r"^dashboard/",include("dashboard.urls"))
    ]
    DevOps中的urls.py文件内容 
    #!/usr/bin/env python
    #_*_conding:utf-8_*_
    #@author :yinzhengjie
    #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
    
    from django.conf.urls import url
    from . import  views
    
    urlpatterns = [
        url(r"^login/",views.LoginView.as_view()),
        url(r"^index/", views.MyPageView.as_view()),
    ]
    dashboard中的urls.py文件内容
    """
    Django settings for DevOps project.
    
    Generated by 'django-admin startproject' using Django 1.11.11.
    
    For more information on this file, see
    https://docs.djangoproject.com/en/1.11/topics/settings/
    
    For the full list of settings and their values, see
    https://docs.djangoproject.com/en/1.11/ref/settings/
    """
    
    import os
    
    # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    
    
    # Quick-start development settings - unsuitable for production
    # See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/
    
    # SECURITY WARNING: keep the secret key used in production secret!
    SECRET_KEY = 'fobwai!6%)9$$-*+&5v9-s_p3-e5=5jb9%7ko131o_g&eu8d+@'
    
    # SECURITY WARNING: don't run with debug turned on in production!
    DEBUG = True
    
    ALLOWED_HOSTS = ["*"]
    
    
    # Application definition
    
    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'dashboard.apps.DashboardConfig',
    ]
    
    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        # 'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]
    
    ROOT_URLCONF = 'DevOps.urls'
    
    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [os.path.join(BASE_DIR, 'templates')]
            ,
            'APP_DIRS': True,
            'OPTIONS': {
                'context_processors': [
                    'django.template.context_processors.debug',
                    'django.template.context_processors.request',
                    'django.contrib.auth.context_processors.auth',
                    'django.contrib.messages.context_processors.messages',
                ],
            },
        },
    ]
    
    WSGI_APPLICATION = 'DevOps.wsgi.application'
    
    
    # Database
    # https://docs.djangoproject.com/en/1.11/ref/settings/#databases
    
    DATABASES = {
        'default': {
              'ENGINE': 'django.db.backends.mysql',  # 连接的数据库类型
              'HOST': '127.0.0.1',                      # 连接数据库的地址
              'PORT': 3306,                             # 端口
              'NAME': "devops",                         # 数据库名称
              'USER': 'root',                          # 用户
              'PASSWORD': 'yinzhengjie'               # 密码
        }
    }
    
    
    # Password validation
    # https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators
    
    AUTH_PASSWORD_VALIDATORS = [
        {
            'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
        },
    ]
    
    
    # Internationalization
    # https://docs.djangoproject.com/en/1.11/topics/i18n/
    
    LANGUAGE_CODE = 'en-us'
    
    TIME_ZONE = 'UTC'
    
    USE_I18N = True
    
    USE_L10N = True
    
    USE_TZ = True
    
    
    # Static files (CSS, JavaScript, Images)
    # https://docs.djangoproject.com/en/1.11/howto/static-files/
    
    STATIC_URL = '/static/'
    
    
    
    LOGGING = {
        "version": 1,
        'disable_existing_loggers': False,          #一般情况下设置为False,表示不禁用Django的默认配置
    
        'formatters': {
            'devops': {
                'format': '[%(asctime)s] [%(process)d] [%(thread)d] [%(filename)16s:%(lineno)4d] [%(levelname)-6s] %(message)s'
            },
            'simple': {
                'format': '%(asctime)s %(levelname)s %(message)s'
            },
            'default': {
                'format': '%(asctime)s %(levelname)s  %(pathname)s [ %(lineno)s] %(message)s',
                "datefmt":"%Y-%m-%d %H:%M:%S"
            }
        },
    
        "handlers": {
            "console": {
                "level": "DEBUG",
                "class": "logging.StreamHandler",
                "formatter": "simple"
            },
            "file": {
                "level": "DEBUG",
                "class": "logging.FileHandler",
                "filename": os.path.join(BASE_DIR, "logs", "django.log"),
                "formatter": "default"
            },
        },
    
    
        "loggers":{
            "yinzhnegjie-devops": {
                "level": "DEBUG",                           #配置日志级别
                "handlers": ["console","file"],            #指定具体的对象去写
                'propagate': False,                         #是否向上传播日志
            },
        },
    
    }
    DevOps中的settings.py文件内容

      dashboard的APP对应的views.py视图函数编写的代码如下:

    #!/usr/bin/env python
    #_*_conding:utf-8_*_
    #@author :yinzhengjie
    #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
    
    from django.http import HttpResponse,JsonResponse
    from django.shortcuts import  render
    from django.views import View
    from django.contrib.auth.models import User
    import logging
    
    # 注意,下面的“yinzhnegjie-devops”字符串摘自“setting.py”中LOGGING对应的loggers所属的value值。换句话说,就是这个字符串必须在setting.py中定义!
    logger = logging.getLogger("yinzhnegjie-devops")
    
    class LoginView(View):
    
        def get(self,request):
            return render(request,"login.html")
    
        def post(self,request):
            print("调用了POST方法!")
            return HttpResponse("post...")
    
    
    
    class MyPageView(View):
        def get(self,request,*args,**kwargs):
            #我们在代码中写入日志
            logger.debug("这是第一条日志")
            #定义每个页码显示信息的条数
            Number_of_per_page = 10
            try:
                #获取到用户传递过来的查询页码
                page = int(request.GET.get("page",1))
            except:
                #如果用户没有传递要查询的页码,我们这里给其设置一个默认值1,即显示第一页。
                page = 1
    
            #定义查询的起始位置
            end = page * 10
            start = end - 10
    
            #定义查询的语句,注意这里并没有去数据库直接查询语句,这里只是定义好了要查询的SQL语句
            queryset =  User.objects.all()[start:end]
    
            #触发SQL语句,并将查询的结果转换成JSON格式
            data = list(queryset.values("id","username","email"))
    
            return JsonResponse(data,safe=False)

      访问浏览器,查看对应的服务器URL配置,如下图所示:

    十.使用Django内置的logger案例展示

    """DevOps URL Configuration
    
    The `urlpatterns` list routes URLs to views. For more information please see:
        https://docs.djangoproject.com/en/1.11/topics/http/urls/
    Examples:
    Function views
        1. Add an import:  from my_app import views
        2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
    Class-based views
        1. Add an import:  from other_app.views import Home
        2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
    Including another URLconf
        1. Import the include() function: from django.conf.urls import url, include
        2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))
    """
    from django.conf.urls import url,include
    from django.contrib import admin
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r"^dashboard/",include("dashboard.urls"))
    ]
    DevOps中的urls.py文件内容
    """
    Django settings for DevOps project.
    
    Generated by 'django-admin startproject' using Django 1.11.11.
    
    For more information on this file, see
    https://docs.djangoproject.com/en/1.11/topics/settings/
    
    For the full list of settings and their values, see
    https://docs.djangoproject.com/en/1.11/ref/settings/
    """
    
    import os
    
    # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    
    
    # Quick-start development settings - unsuitable for production
    # See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/
    
    # SECURITY WARNING: keep the secret key used in production secret!
    SECRET_KEY = 'fobwai!6%)9$$-*+&5v9-s_p3-e5=5jb9%7ko131o_g&eu8d+@'
    
    # SECURITY WARNING: don't run with debug turned on in production!
    DEBUG = True
    
    ALLOWED_HOSTS = ["*"]
    
    
    # Application definition
    
    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'dashboard.apps.DashboardConfig',
    ]
    
    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        # 'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]
    
    ROOT_URLCONF = 'DevOps.urls'
    
    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [os.path.join(BASE_DIR, 'templates')]
            ,
            'APP_DIRS': True,
            'OPTIONS': {
                'context_processors': [
                    'django.template.context_processors.debug',
                    'django.template.context_processors.request',
                    'django.contrib.auth.context_processors.auth',
                    'django.contrib.messages.context_processors.messages',
                ],
            },
        },
    ]
    
    WSGI_APPLICATION = 'DevOps.wsgi.application'
    
    
    # Database
    # https://docs.djangoproject.com/en/1.11/ref/settings/#databases
    
    DATABASES = {
        'default': {
              'ENGINE': 'django.db.backends.mysql',  # 连接的数据库类型
              'HOST': '127.0.0.1',                      # 连接数据库的地址
              'PORT': 3306,                             # 端口
              'NAME': "devops",                         # 数据库名称
              'USER': 'root',                          # 用户
              'PASSWORD': 'yinzhengjie'               # 密码
        }
    }
    
    
    # Password validation
    # https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators
    
    AUTH_PASSWORD_VALIDATORS = [
        {
            'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
        },
    ]
    
    
    # Internationalization
    # https://docs.djangoproject.com/en/1.11/topics/i18n/
    
    LANGUAGE_CODE = 'en-us'
    
    TIME_ZONE = 'UTC'
    
    USE_I18N = True
    
    USE_L10N = True
    
    USE_TZ = True
    
    
    # Static files (CSS, JavaScript, Images)
    # https://docs.djangoproject.com/en/1.11/howto/static-files/
    
    STATIC_URL = '/static/'
    
    
    
    LOGGING = {
        "version": 1,
        'disable_existing_loggers': False,          #一般情况下设置为False,表示不禁用Django的默认配置
    
        'formatters': {
            'devops': {
                'format': '[%(asctime)s] [%(process)d] [%(thread)d] [%(filename)16s:%(lineno)4d] [%(levelname)-6s] %(message)s'
            },
            'simple': {
                'format': '%(asctime)s  %(message)s'
            },
            'default': {
                'format': '%(asctime)s  %(name)s %(levelno)s  %(levelname)s %(message)s',
                "datefmt":"%Y-%m-%d %H:%M:%S"
            }
        },
    
        "handlers": {
            "console": {
                "level": "DEBUG",
                "class": "logging.StreamHandler",
                "formatter": "simple"
            },
            "file": {
                "level": "DEBUG",
                "class": "logging.FileHandler",
                "filename": os.path.join(BASE_DIR, "logs", "debug.log"),
                "formatter": "default"
            },
            "request":{
                "level": "DEBUG",
                "class": "logging.FileHandler",
                "filename": os.path.join(BASE_DIR, "logs", "request.log"),
                "formatter": "default"
            },
            "server": {
                "level": "DEBUG",
                "class": "logging.FileHandler",
                "filename": os.path.join(BASE_DIR, "logs", "server.log"),
                "formatter": "default"
            },
    
            "root": {
                "level": "DEBUG",
                "class": "logging.FileHandler",
                "filename": os.path.join(BASE_DIR, "logs", "root.log"),
                "formatter": "default"
            },
    
            "db_backends":{
                "level": "DEBUG",
                "class": "logging.FileHandler",
                "filename": os.path.join(BASE_DIR, "logs", "db_backends.log"),
                "formatter": "default"
            }
        },
    
    
        "loggers":{
            "yinzhnegjie-devops": {
                "level": "DEBUG",                           #配置日志级别
                "handlers": ["console","file"],            #指定具体的对象去写
                'propagate': False,                         #是否向上传播日志
            },
    
            "django":{
                "level": "DEBUG",
                "handlers": [ "console","file"],
                'propagate': False,
            },
    
            "django.request": {
                "level": "DEBUG",
                "handlers": ["request"],
                'propagate': False,
            },
    
            "django.sever": {
                "level": "DEBUG",
                "handlers": ["server"],
                'propagate': False,
            },
    
            "django.db.backends":{
                "level": "DEBUG",
                "handlers": ["db_backends"],
                'propagate': False,
            }
        },
        #定义自己的日志,我们需要下级的 'propagate'的值设置为False。
         "root": {
            "level": "DEBUG",
            "handlers": ["root"],
        },
    }
    DevOps中的settings.py文件内容
    #!/usr/bin/env python
    #_*_conding:utf-8_*_
    #@author :yinzhengjie
    #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
    
    from django.conf.urls import url
    from . import  views
    
    urlpatterns = [
        url(r"^login/",views.LoginView.as_view()),
        url(r"^index/", views.MyPageView.as_view()),
    ]
    dashboard中的urls.py文件内容

       dashboard的APP对应的views.py视图函数编写的代码如下:

    #!/usr/bin/env python
    #_*_conding:utf-8_*_
    #@author :yinzhengjie
    #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
    
    from django.http import HttpResponse,JsonResponse
    from django.shortcuts import  render
    from django.views import View
    from django.contrib.auth.models import User
    import logging
    
    # 定义logger的名字,默认情况下我们都会写成“__name__”,表示当前模块或者当前文件的相对路径。
    logger = logging.getLogger(__name__)
    
    class LoginView(View):
    
        def get(self,request):
            return render(request,"login.html")
    
        def post(self,request):
            print("调用了POST方法!")
            return HttpResponse("post...")
    
    
    
    class MyPageView(View):
        def get(self,request,*args,**kwargs):
            #我们在代码中写入日志
            logger.debug("这是第一条日志!")
            #定义每个页码显示信息的条数
            Number_of_per_page = 10
            try:
                #获取到用户传递过来的查询页码
                page = int(request.GET.get("page",1))
            except:
                #如果用户没有传递要查询的页码,我们这里给其设置一个默认值1,即显示第一页。
                page = 1
    
            #定义查询的起始位置
            end = page * 10
            start = end - 10
            logger.warning("再来一条日志!")
            #定义查询的语句,注意这里并没有去数据库直接查询语句,这里只是定义好了要查询的SQL语句
            queryset =  User.objects.all()[start:end]
    
            #触发SQL语句,并将查询的结果转换成JSON格式
            data = list(queryset.values("id","username","email"))
    
            return JsonResponse(data,safe=False)

      访问浏览器,查看对应的服务器URL配置,如下图所示:

  • 相关阅读:
    团队项目的NABC(截图软件)
    《梦断代码》读后感_3
    《梦断代码》读后感_2
    毕设今日总结(二)
    毕业设计今日总结(一)
    QT中文乱码解决方法
    课堂练习——最大联通之数组
    《浪潮之巅》读书笔记3
    《浪潮之巅》读书笔记2
    《浪潮之巅》读书笔记1
  • 原文地址:https://www.cnblogs.com/yinzhengjie/p/10272913.html
Copyright © 2020-2023  润新知