万字长文 - Python 日志记录器logging 百科全书 - 高级配置之 日志分层

在这里插入图片描述

万字长文 - Python 日志记录器logging 百科全书 - 高级配置之 日志分层

前言

在 Python 的logging模块中,它不仅提供了基础的日志功能,还拥有一系列高级配置选项来满足复杂应用的日志管理需求。

说到logging 模块的高级配置,必须提及日志分层logging.config配置日志异步操作等关键功能。它们每一项都为开发者提供了强大的调试和监控环境,对于构建可维护和高效的日志系统至关重要。

在接下来的三篇logging高级配置 文章中,我将为读者朋友们介绍 Python logging 模块中的三个高级配置的具体应用:日志分层logging.config 以及 日志异步操作,探讨它们如何优化日志处理流程,并提升应用的整体性能。

本文将首先聚焦于 logging 模块中的日志分层概念,解析其如何使开发者能够构建具有层级结构的日志记录系统,并高效地管理和过滤日志信息。

知识点📖📖

模块释义
loggingPython 的日志记录工具,标准库
logging.getLogger获取日志记录器实例

导入模块

import logging
import logging.handlers


文章脉络:

  • 点击直达:万字长文 - Python 日志记录器logging 百科全书 之 基础配置
  • 点击直达:Python 日志记录器logging 百科全书 之 日志回滚
  • 点击直达:万字长文 - Python 日志记录器logging 百科全书 之 日志过滤
  • 点击直达:万字长文 - Python 日志记录器logging 百科全书 - 高级配置之 日志分层

理解日志分层

日志分层在管理复杂应用的日志中发挥着重要作用。

作用

日志分层在复杂应用程序中提供了高度的组织性和灵活性,允许针对不同模块或组件进行细致的日志管理,从而优化调试、维护效率,并增强整体日志系统的性能和可读性。


将 Python 的 logging 模块中的日志分层类比为 Python 中的类继承(父类和子类关系),这样可以有助于理解日志记录器(logger)之间的继承和行为传播机制。

  1. 继承:在类的继承中,子类继承父类的属性和方法。类似地,在日志分层中,子记录器会继承父记录器的配置,如日志级别和关联的处理器(handlers)。

  2. 重写和自定义:就像子类可以重写或扩展父类的方法和属性,子记录器也可以有自己的日志级别和处理器,甚至可以完全覆盖父记录器的设置。

  3. 层级关系:正如类可以有多层继承关系,日志记录器也可以形成层级结构,允许复杂和细致的控制。

  4. 传播行为:在类继承中,子类的行为可以反映父类的特征,而在日志分层中,日志消息的传播(默认情况下)会从子记录器向上到父记录器,除非显式地将 propagate 属性设置为 False

需要注意的是,这只是一个比喻,实际的实现细节和概念上仍有区别。例如,类继承是面向对象编程中的一个核心概念,涉及到更广泛的编程模式,而日志分层主要是关于信息传递和处理的策略。

简单总结如下:

特性主要作用与实现方式备注
组织性为每个模块或组件设置独立的子记录器日志分层核心优势,提供清晰的日志结构
继承和覆盖子记录器继承父记录器的配置,可根据需要覆盖日志分层核心优势,提高配置灵活性
灵活性为不同模块设置不同的日志级别多日志处理器可实现,但日志分层的优势是更加结构化和直观
性能优化控制哪些消息被记录,减少不必要的日志输出多日志处理器可实现,但日志分层的优势是更加结构化和直观
问题追踪快速定位大型应用中的问题所在模块日志记录自带buff,但日志分层的优势在于它使得定位问题变得更加容易

疑惑

在学习了前面的 万字长文 - Python 日志记录器logging 百科全书 之 日志过滤 和 初步了解到日志分层的作用之后,可能会有读者朋友们有疑问:

?既然能够通过单个记录器和多处理器满足复杂日志需求的情况下,为什么还需要使用分层的日志记录器方法???

以下是我在深入了解日志分层 的作用之后的回答:

  • 单个日志记录器配合多个处理器:在这种情况下,我们可以根据日志的类型(如错误、警告、信息等)将日志定向到不同的处理器(如文件、控制台等)。这种方法在应用结构相对简单,或者当日志需求主要围绕不同类型的日志处理时很有效。
  • 使用分层多个日志记录器和处理器:这种方法允许按模块或组件分别记录日志。每个模块或组件可以有其自定义的日志级别和处理器。这种方式适用于更复杂的应用程序,其中不同部分可能有不同的日志需求。

分层日志在大型和复杂的应用程序中提供了更好的组织性和灵活性,使得对不同模块或组件的日志管理更加精细和高效。


应用场景

日志分层的应用场景主要体现在大型、多模块的应用程序和微服务架构中,以及多团队协作的开发项目里,它帮助各个模块独立地控制日志记录,简化问题追踪和调试,提高维护效率。

  1. 大型应用程序:在大型应用程序(如电子商务平台、企业级软件等)中,应用通常分为多个模块或组件。每个组件可以有自己的日志记录器,这些记录器可以根据组件的具体需求进行配置。
  2. 多团队开发:在多团队协作的项目中,每个团队可能负责应用的不同部分。日志分层允许每个团队为其负责的部分单独配置日志。
  3. 微服务架构:在微服务架构中,每个微服务可以配置自己的日志记录器。这样可以确保每个服务的日志信息都是独立和清晰的。
  4. 调试和维护:在应用程序的维护和调试过程中,可以针对出现问题的特定模块调整日志级别,从而获取更详细的日志信息。
  5. 统一基础配置:在一个复杂的应用中,各个组件可能有不同的日志需求。使用分层结构,可以在顶层记录器上定义通用的日志策略,然后根据需要为特定子记录器定制日志行为。这种方式使得日志系统更易于维护和更新。

日志分层的要点

命名规范和核心概念

以下内容来自官方文档:


getLogger() 返回对具有指定名称的记录器实例的引用(如果已提供),或者如果没有则返回 root 。名称是以句点分隔的层次结构。多次调用 getLogger()具有相同的名称将返回对同一记录器对象的引用。在分层列表中较低的记录器是列表中较高的记录器的子项。例如,给定一个名为 foo 的记录器,名称为 foo.bar 、 和 foo.bam 的记录器都是 foo 子项。

记录器具有 有效等级 的概念。如果未在记录器上显式设置级别,则使用其父记录器的级别作为其有效级别。如果父记录器没有明确的级别设置,则检查 父级。依此类推,搜索所有上级元素,直到找到明确设置的级别。根记录器始终具有明确的级别配置(默认情况下为 WARNING )。在决定是否处理事件时,记录器的有效级别用于确定事件是否传递给记录器相关的处理器。

子记录器将消息传播到与其父级记录器关联的处理器。因此,不必为应用程序使用的所有记录器定义和配置处理器。一般为顶级记录器配置处理器,再根据需要创建子记录器就足够了。(但是,你可以通过将记录器的 propagate 属性设置为 False 来关闭传播。)


我提炼如下,简要说明了关于日志分层中的日志记录器行为和层次结构的核心概念:

  • 记录器实例引用:使用getLogger()函数可以获取具有特定名称的记录器实例。如果使用相同的名称多次调用getLogger(),将返回同一个记录器对象的引用。

  • 命名规范:记录器(logger)的名称通常遵循点号分隔的层级结构,类似于 Python 包和模块的命名方式。例如,'foo.database' 表示 foo 下的 database 子模块的记录器。

  • 层级关系:记录器的层级结构通过命名来实现。在这个层级中,一个记录器可以是另一个记录器的子项。例如,如果有一个名为 'foo' 的记录器,那么 'foo.bar''foo.bar.baz' 就是它的子记录器。

  • 有效级别:如果调用 getLogger() 时不提供名称,将返回根记录器。根记录器是所有记录器的最顶层父记录器,它的默认级别为 WARNING

  • 消息传播:子记录器的日志消息会传播到其父记录器的处理器,除非设置了propagate属性为False。通常只需为顶级记录器配置处理器,子记录器可以继承这些处理器。

这种层级结构和命名规范为大型和复杂的应用程序提供了一种高效和灵活的日志管理方式。通过恰当地命名和配置记录器,可以轻松地管理应用程序的不同部分所生成的日志,确保日志信息的清晰和有序。


命名不规范的问题

在 Python 的 logging 模块中,如果顶级日志记录器的命名与子记录器的命名不遵循统一的层级结构,会出现以下几个问题:

  1. 继承失效:日志记录器之间的层级关系是通过它们的命名来确定的。如果顶级记录器和子记录器的命名不遵循统一的层级前缀,子记录器将无法正确继承顶级记录器的配置(如处理器、级别等)。这意味着我们需要为每个子记录器单独配置处理器和其他设置,增加了配置复杂性。
  2. 日志传播问题:通常,日志消息会从子记录器传播到父记录器。如果子记录器的命名不遵循正确的层级结构,这种传播可能无法发生,导致日志消息不会被预期的父记录器(和其关联的处理器)处理。
  3. 可维护性和可读性降低:在大型项目中,清晰和一致的日志记录器命名极为重要。不遵循统一的命名规范会使代码难以理解和维护,特别是在涉及多个开发者和模块的情况下。

为了避免这些问题,所以需要使用一致的层级命名约定。例如,顶级日志记录器命名为 'foo',那么子记录器应该以 'foo.' 作为前缀,如 'foo.bar''foo.baz' 等,以确保正确的继承和日志消息传播。

什么时候使用 propagate

可能会有读者朋友们有疑问:

?既然设置logger.propagate = False,那为什么还需要使用日志分层呢? 一开就使用 非日志封层的logger不是更合适吗?

其实要回答这个疑问,就应该重新审视日志分层的作用和应用场景,以及为什么即使在某些情况下禁用了传播,日志分层仍然是有价值的。

使用 logger.propagate = False 的情况,

  1. 避免重复日志记录:当在不希望某个子记录器(logger)的日志被其父记录器也处理时。例如,当已经为子记录器配置了特定的处理器(handler)并且不希望同样的日志消息再次由更高级别的记录器处理,这时设置 propagate = False 可以防止日志消息向上传播。

  2. 特定日志处理:当我们希望对某个模块或组件的日志进行特殊处理,与应用程序的其他部分区别开来。例如,我们可能有一个记录安全相关日志的记录器,我们不希望这些日志被常规的应用程序日志处理器处理。

  3. 独立日志流:在构建库或框架时,可能希望库的日志独立于使用该库的应用程序的日志系统。在这种情况下,可以为库设置一个专用的记录器,并设置 propagate = False,以防止库的日志消息污染或干扰主应用程序的日志。

  4. 性能考虑:在一些性能敏感的应用中,防止不必要的日志传播可以减少一些处理开销,特别是当有大量的日志消息和复杂的日志处理器配置时。

即使在上面的这些情况下,分层结构依然有助于维护清晰的组织架构。我们可以为特定的模块创建专用的子记录器,给予它独立的处理器和格式化器,而不必在每个模块中创建和配置新的独立记录器。


示例代码

这里是一份能用的伪代码,用于帮助读者朋友们更好的理解日志分层的具体应用。

代码:

# -*- coding: utf-8 -*-

import logging
import logging.handlers
import sys

import requests

# 全局基础配置, 日志格式化配置
formatter = logging.Formatter('%(levelname)-7s - %(asctime)s - %(name)s - %(message)s')


class RemoteLogHandler(logging.Handler):
    """自定义远程处理器"""

    def __init__(self, remote_url, logger):
        super().__init__()
        self.remote_url = remote_url
        self.error_logger = logger
        self.setFormatter(formatter)

    def emit(self, record):
        # 发送日志记录到远程服务器
        log_entry = self.format(record)  # 格式化日志记录
        try:
            response = requests.post(self.remote_url, data=log_entry)
            response.raise_for_status()
        except Exception as e:
            record.msg = f"Original message: {record.msg}, Failed to send log to remote: {str(e)}"
            print('error_logger 等级是 ', self.error_logger.level)
            self.error_logger.handle(record)


def setup_logger(*handlers, name, level=logging.INFO):
    """
    用于设置特定记录器的函数,支持多个处理器.

    Args:
        *handlers(logging.Handler): 日志处理器
        name(str): 日志记录器名称
        level(int): 日志等级

    Returns:
        日志记录器.
    """
    logger = logging.getLogger(name)
    logger.setLevel(level)
    for handler in handlers:
        handler.setFormatter(formatter)
        logger.addHandler(handler)
    return logger


def setup_file_handler(filename, level=logging.DEBUG):
    """
    setup file handler

    Args:
        filename(str): 日志文件的名称
        level(int): 日志处理器的级别, 默认为logging.DEBUG

    Returns:

    """
    file_handler = logging.FileHandler(filename=filename, delay=True)
    file_handler.setLevel(level=level)
    file_handler.setFormatter(formatter)
    return file_handler


def setup_stream_handler(stream=sys.stdout, level=logging.INFO):
    stream_handler = logging.StreamHandler(stream=stream)
    stream_handler.setLevel(level=level)
    stream_handler.setFormatter(formatter)
    return stream_handler


def create_remote_log_handler(url, logger, level=logging.INFO):
    remote_handler = RemoteLogHandler(url, logger)
    remote_handler.setLevel(level=level)
    remote_handler.setFormatter(formatter)
    return remote_handler


if __name__ == '__main__':
    # 顶层日志记录器
    # 处理器配置, 文件处理器 和 控制台处理器
    file_handler_global = setup_file_handler(filename='ecommerce_global.log')
    stream_handler_global = setup_stream_handler(stream=sys.stdout)
    global_logger = setup_logger(
        file_handler_global, stream_handler_global, name='ecommerce', level=logging.DEBUG
    )

    # 创建错误记录器和处理器
    error_file_handler = setup_file_handler(filename='error.log', level=logging.ERROR)
    error_logger = setup_logger(error_file_handler, name='error', level=logging.WARNING)

    # 实例化RemoteLogHandler
    remote_handler_global = create_remote_log_handler(
        url='http://127.0.0.1:5000/submit_log', logger=error_logger, level=logging.ERROR
    )

    # 订单处理系统记录器和处理器
    order_file_handler = setup_file_handler('orders.log', level=logging.WARNING)
    order_logger = setup_logger(
        order_file_handler, remote_handler_global, name='ecommerce.orders', level=logging.INFO
    )

    # 支付系统记录器和处理器
    payment_file_handler = setup_file_handler(filename='payments.log', level=logging.ERROR)
    payment_logger = setup_logger(
        payment_file_handler, remote_handler_global, name='ecommerce.payments', level=logging.WARNING
    )
    #
    # 测试打印日志
    global_logger.info('Global logger configured')
    order_logger.warning('Order logger configured')
    payment_logger.error('Payment logger configured')

运行效果

如果程序没有出错的话,可以看到会创建orders.log, payments.logecommerce_global.log 三份日志文件,内容分别如下所示:

  • 可以看到 ecommerce_global.log 文件,打印的日志是包含orders.logpayments.log的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用propagate

添加以下两行代码,

  • 设置子记录器的日志消息不传播到父记录器
order_logger.propagate = False
payment_logger.propagate = False

再次运行结果如下:

  • 可以看到没有重复日志记录啦。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

代码释义

这份代码是一个复杂的日志系统的实现,适用于需要将日志信息记录到不同位置(如文件、控制台、远程服务器)的应用程序。展示了如何在 Python 中使用 logging 模块来创建一个分层且灵活的日志处理架构。

  1. 自定义远程日志处理器 (RemoteLogHandler)

    • 定义了一个自定义的日志处理器,用于将日志消息发送到远程服务器。
    • 如果发送失败,它会使用另一个配置的日志记录器(error_logger)来处理这些日志消息。
  2. 灵活的日志配置函数

    • 提供了多个辅助函数(setup_logger, setup_file_handler, setup_stream_handler, create_remote_log_handler),使得创建和配置日志记录器和处理器更加灵活和简洁。
  3. 分层日志记录器

    • 创建了多个日志记录器,每个记录器代表应用程序的不同部分(如全局、订单处理系统、支付系统)。
    • 每个记录器可以有自己的处理器和日志级别。

日志分层的体现

  1. 不同功能的日志记录器

    • global_logger 用于全局日志记录。
    • order_logger 专门用于订单处理系统的日志。
    • payment_logger 专门用于支付系统的日志。
  2. 层级命名

    • 日志记录器的命名体现了应用程序的层级结构。例如,ecommerce.ordersecommerce.payments 表示这些记录器是 ecommerce 的子模块。
  3. 日志传播

    • 在这个例子中,如果 propagate 属性没有被设置为 False,那么日志消息会从子记录器传播到父记录器。这意味着 order_loggerpayment_logger 的日志也可能被 global_logger 所处理,除非显式地关闭了传播。

实际应用

这个日志系统适用于大型或模块化的应用程序,其中需要对不同部分的日志进行精细控制。通过这种方法,我们可以确保不同部分的日志被适当地记录和处理,同时保持日志系统的整洁和可维护性。这对于故障排查、性能监控和安全分析等方面非常有用。

总体来说,这个代码示例展示了一个结构化的日志系统,它既灵活又能够适应不同的日志需求,非常适合复杂的应用场景。

总结🎈🎈

本文详细介绍了 Python logging 模块中的日志分层功能,强调了其在构建复杂应用程序中的重要性。以下是文章的主要要点总结:

  1. 日志分层的作用与优势
    • 提高组织性:允许开发者为不同模块或组件设置独立的子记录器。
    • 继承与覆盖:子记录器可以继承父记录器的配置,同时具备自定义设置的能力。
    • 易于问题追踪:通过层级结构,方便快速定位问题所在模块。
  2. 命名规范与层级关系
    • 通过点号分隔的命名规范,确保记录器之间的层级关系清晰明确。
    • 层级结构提供了继承和消息传播的机制,简化了配置并增加了灵活性。
  3. 传播行为与性能优化
    • 默认情况下,子记录器的日志会传播到父记录器,可通过设置 propagate 属性进行控制。
    • 正确使用日志分层可以减少不必要的日志输出,从而优化性能。
  4. 应用场景
    • 日志分层特别适用于大型、多模块应用程序和微服务架构。
    • 有助于多团队协作的项目中的日志管理和维护。
  5. 实用代码示例
    • 文章通过实际代码展示了如何设置和使用分层日志记录器。
    • 包括自定义处理器和日志记录器的配置方法,增强了文章的实用性和可操作性。

总体来说,这篇文章为理解和应用 Python 的 logging 模块提供了深入的指导,特别是在构建需要细粒度日志管理的复杂应用时。文章的结构清晰,通过逐步深入的方式,使得读者朋友容易跟进和理解。

后话

本次分享到此结束,

see you~~🏹🏹

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/170025.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HDCTF2023 - Reverse方向全WP

文章目录 [HDCTF 2023]easy_re[HDCTF 2023]easy_asm[HDCTF 2023]fake_game[HDCTF 2023]enc[HDCTF 2023]double_code[HDCTF 2023]买了些什么呢[HDCTF2023]basketball [HDCTF 2023]easy_re UPX壳,脱壳 一个base64编码。 [HDCTF 2023]easy_asm ida打开后可以看到xor 10…

Ubuntu18.04运行gazebo的launch文件[model-4] process has died报错

启动gazebo仿真环境报错[model-4] process has died [model-4] process has died [pid 2059, exit code 1, cmd /opt/ros/melodic/lib/gazebo_ros/spawn_model -urdf -model mycar -param robot_description __name:model __log:/root/.ros/log/8842dc14-877c-11ee-a9d9-0242a…

opencv将32位深图片合成视频跳帧解决办法

在合成视频时候,大多数的图片都是24位深度的(即RGB三通道,一个通道8位),但是也存在少量的32位深的图片(RGBA,三个颜色通道加上A这个透明度通道),32位和24位的格式是不一样…

SSM2

DataSource mybatis与Spring整合 事务加载业务层上面 开启事务驱动 上面都是声明式开启事务 图书管理系统 命名规范: java命名规范:驼峰命名法类:大驼峰变量,属性名.方法名:小驼峰 常量使用下划线分割:全大写,单词与单词之间下划线分割数据库命名规范:常用命名规范:下划线…

【Windows 常用工具系列 11 -- 福昕PDF搜索高亮过的文本】

文章目录 福昕 PDF 搜索高亮过的文本 福昕 PDF 搜索高亮过的文本 在 pdf 文档阅读过程中,我们需要经常高亮一些文本,以方便下次阅读时找到重点。我这边使用的是 福昕PDF 阅读器,下面就介绍下如何在福昕阅读器中搜索已经高亮过的文本。

基于单片机PM2.5监测系统仿真设计

**单片机设计介绍, 基于单片机PM2.5监测系统仿真设计 文章目录 一 概要简介设计目标系统组成工作流程仿真设计结论 二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 # 基于单片机PM2.5监测系统仿真设计介绍 简介 PM2.5(可吸…

Django实战:从零到一构建安全高效的Web应用

目录 一、概述 二、版本控制和部署 1、Git版本控制 2、Docker部署 三、数据库配置 1、配置数据库设置 2、创建数据库模型 四、URL路由和视图 1、定义URL路由 2、创建视图 五、模板渲染 1、创建模板 2、在视图中使用模板 总结 一、概述 Django是一个高级Python W…

【机器学习】038_梯度消失、梯度爆炸

一、原因 神经网络梯度 假设现在有一个 层的神经网络,每层的输出为一个对输入作 变换的函数结果 用 来表示第 层的输出,那么有下列公式: 链式法则计算损失 关于某一层某个参数 的梯度: 注意到, 为向量&am…

【NI-RIO入门】CompactRIO介绍及环境安装

CompactRIO是什么? CompactRIO系统提供了高处理性能、传感器专用I/O和紧密集成的软件工具,使其成为工业物联网、监测和控制应用的理想之选。实时处理器提供可靠,可预测的行为,而FPGA在需要高速逻辑和精确定时的较小任务上表现出色…

11.20 知识总结(choices参数、MVC和MTV的模式、Django与Ajax技术)

一、 choices参数的使用 1.1 作用 针对某个可以列举完全的可能性字段,我们应该如何存储 .只要某个字段的可能性是可以列举完全的,那么一般情况下都会采用choices参数 1.2 应用场景 应用场景: 学历: 小学 初中 高中 本科 硕士…

MATLAB | 官方举办的动图绘制大赛 | 第二周赛情回顾

今天带来一下MATHWORKS官方举办的迷你黑客大赛第三期(MATLAB Flipbook Mini Hack)的最新进展!!目前比赛已经进行了两周非常荣幸能够成为第一周的阶段性获奖者: 本来并不打算每周进行一次赛况讲解,但是由于字符限制改成了2000&…

【MySql】13- 实践篇(十一)

文章目录 1. 自增主键为什么不是连续的?1.1 自增值保存在哪儿?1.2 自增值修改机制1.2.1 自增值的修改时机1.2.2 自增值为什么不能回退? 1.3 自增锁的优化1.3.1 自增锁设计历史 2. Insert语句为何很多锁?2.1 insert … select 语句2.2 insert 循环写入2…

Revive开发商加入VR开源标准OpenXR

导读作为一款能让HTC Vive用户玩到Oculus平台游戏的软件,它的开发商CrossVR今日宣布即将加盟为VR和AR应用程序开源组织,即OpenXR。 由Khronos Group引领的OpenXR旨在创建一个标准化且免版税的应用程序编程接口(API),该…

60 权限提升-MYMSORA等SQL数据库提权

目录 数据库应用提权在权限提升中的意义WEB或本地环境如何探针数据库应用数据库提权权限用户密码收集等方法目前数据库提权对应的技术及方法等 演示案例Mysql数据库提权演示-脚本&MSF1.UDF提权知识点: (基于MYSQL调用命令执行函数)读取数据库存储或备份文件 (了…

Linux内核的安装

1.通过tftp 加载内核和根文件系统 即sd内存卡启动: SD卡的存储以扇区为单位,每个扇区的大小为512Byte, 其中零扇区存储分区表(即分区信息),后续的扇区可自行分区和格式化; 若选择SD卡启动,处理器上电后从第一个扇区开…

车载毫米波雷达行业发展3——市场

3.1 车载毫米波雷达市场分析 中国市场乘用车智能化程度不断提高,车载毫米波雷达市场快速增长。2023 年 1-6 月, 毫米波雷达搭载量达到937.92万颗,同比增长26.36%,其中,前向毫米波雷达搭载量459.29 万颗,同…

C++设计模式——单例模式

单例设计模式 应用场景特点设计模式分类懒汉设计模式饿汉设计模式使用编写的测试代码运行结果 应用场景 当多个类都需要调用某一个类的一些公共接口,同时不想创建多个该类的对象,可以考虑将该类封装为一个单例模式。 特点 单例模式的特点:…

Python编程技巧 – 使用字典

Python编程技巧 – 使用字典 Python Programming Skills – Using Dictionary Dictionary, 即字典,这是Python语言的一种重要的数据结构;Python字典是以键(key)值(value)对为元素,来存储数据的集合。 前文提到Python列…

【CHI】Ordering保序

本节介绍CHI协议所包含的支持系统保序需求的机制,包括: • Multi-copy atomicity • Completion response and ordering • Completion acknowledgment • Transaction ordering 一、 Multi-copy atomicity CHI协议中所使用的memory model要求为mu…

uniapp开发小程序,包过大解决方案

1、首先和大家说一下 微信小程序 主包限制不能超过2M 分包一共不能超过8M 然后具体解决优化步骤如下, 将主包进行分包 在pages.json 下subPackages里面进行配置分包 分包配置完 配置过的文件都需要进行修改对应的路径 2 、 在运行的时候 一定要勾选 压缩代码 有…