深入浅出 Python Logging:从基础到进阶日志管理

在这里插入图片描述
在 Python 开发过程中,日志(Logging)是不可或缺的调试和监控工具。合理的日志管理不仅能帮助开发者快速定位问题,还能提供丰富的数据支持,让应用更具可观测性。本文将带你全面了解 Python logging 模块,涵盖日志输出到控制台、文件、Elasticsearch(ES),以及日志格式的介绍。

一、日志输出到控制台

最简单的日志管理方式就是将日志信息输出到控制台(标准输出)。这种方式在调试时非常方便,能够实时查看程序运行状态。默认情况下,Python logging 模块会以 WARNING 级别及以上的日志输出到控制台。

  def add_stream_handle(self):
        if LOG_TO_CONSOLE and LOG_ENABLED:
            handler = logging.StreamHandler(sys.stdout)
            handler.setLevel(level=CONSOLE_LEVEL)
            handler.setFormatter(logging.Formatter(LOG_FORMAT))
            self.logger.addHandler(handler)

二、日志输出到文件

在实际开发中,日志不仅仅是为了调试,更重要的是长期存储以便后续分析。因此,我们通常需要将日志写入文件,以便在程序运行后仍能追踪问题。通过 FileHandler,我们可以轻松地将日志写入指定的文件,并支持日志轮转(如按日期或文件大小分割)。

    def add_file_handle(self):
        if LOG_ENABLED and LOG_TO_FILE:
            logs_dir = os.path.join(LOG_DIR, "logs")
            os.makedirs(logs_dir, exist_ok=True)
            file = os.path.join(logs_dir, f'{datetime.now().strftime("%Y%m%d_%H%M%S")}.log')
            handler = logging.FileHandler(file, encoding="utf-8")
            handler.setLevel(level=FILE_LEVEL)
            handler.setFormatter(logging.Formatter(LOG_FORMAT))
            self.logger.addHandler(handler)

三、日志输出到 Elasticsearch(ES)

对于需要集中式日志管理的场景,可以将日志直接存储到 Elasticsearch 中,配合 Kibana 进行可视化分析。通过 logging 模块的自定义 Handler 或者 elastic-apm 等第三方库,我们可以将 Python 产生的日志数据直接推送到 ES,并结合全文检索和仪表盘进行日志分析。

class JsonSerializer:
    """JSON serializer relying on the standard library json module."""

    mimetype: ClassVar[str] = "application/json"

    def default(self, data: Any) -> Any:
        if isinstance(data, date):
            return data.isoformat()
        elif isinstance(data, uuid.UUID):
            return str(data)
        elif isinstance(data, Decimal):
            return float(data)
        raise Exception(f"Unable to serialize to JSON: {data!r} (type: {type(data).__name__})", )

    def json_dumps(self, data: dict) -> str:
        return json.dumps(
            data, default=self.default, ensure_ascii=False, separators=(",", ":")
        )

    def json_loads(self, data: str) -> dict:
        return json.loads(data)

    def loads(self, data: str) -> dict:
        return self.json_loads(data)

    def dumps(self, data: dict) -> str:
        # The body is already encoded to bytes
        # so we forward the request body along.
        if isinstance(data, str):
            return data
        return self.json_dumps(data)


def _attempt_serialize_numpy(data: Any) -> Tuple[bool, Any]:
    try:
        import numpy as np

        if isinstance(
                data,
                (
                        np.int_,
                        np.intc,
                        np.int8,
                        np.int16,
                        np.int32,
                        np.int64,
                        np.uint8,
                        np.uint16,
                        np.uint32,
                        np.uint64,
                ),
        ):
            return True, int(data)
        elif isinstance(
                data,
                (
                        np.float16,
                        np.float32,
                        np.float64,
                ),
        ):
            return True, float(data)
        elif isinstance(data, np.bool_):
            return True, bool(data)
        elif isinstance(data, np.datetime64):
            return True, data.item().isoformat()
        elif isinstance(data, np.ndarray):
            return True, data.tolist()

    except ImportError:
        # Since we failed to import 'numpy' we don't want to try again.
        return False, None

    return False, None


def _attempt_serialize_pandas(data: Any) -> Tuple[bool, Any]:
    try:
        import pandas as pd

        if isinstance(data, (pd.Series, pd.Categorical)):
            return True, data.tolist()
        elif isinstance(data, pd.Timestamp) and data is not getattr(pd, "NaT", None):
            return True, data.isoformat()
        elif data is getattr(pd, "NA", None):
            return True, None

    except ImportError:
        # Since we failed to import 'pandas' we don't want to try again.
        return False, None

    return False, None


def _attempt_serialize_numpy_or_pandas(data: Any) -> Tuple[bool, Any]:
    serialized, value = _attempt_serialize_numpy(data)
    if serialized:
        return serialized, value

    serialized, value = _attempt_serialize_pandas(data)
    if serialized:
        return serialized, value

    return False, None


TIME_TYPES = (date, datetime)
FLOAT_TYPES = (Decimal,)
INTEGER_TYPES = ()


class EsJsonSerializer(JsonSerializer):
    mimetype: ClassVar[str] = "application/json"

    def default(self, data: Any) -> Any:
        if isinstance(data, TIME_TYPES):
            # Little hack to avoid importing pandas but to not
            # return 'NaT' string for pd.NaT as that's not a valid
            # Elasticsearch date.
            formatted_data = data.isoformat()
            if formatted_data != "NaT":
                return formatted_data

        if isinstance(data, uuid.UUID):
            return str(data)
        elif isinstance(data, FLOAT_TYPES):
            return float(data)

        # This is kept for backwards compatibility even
        # if 'INTEGER_TYPES' isn't used by default anymore.
        elif INTEGER_TYPES and isinstance(data, INTEGER_TYPES):
            return int(data)

        # Special cases for numpy and pandas types
        # These are expensive to import so we try them last.
        serialized, value = _attempt_serialize_numpy_or_pandas(data)
        if serialized:
            return value

        raise TypeError(f"Unable to serialize {data!r} (type: {type(data)})")


class EsHandler(logging.Handler):

    def __init__(self, url, index, batch_size=100, flush_interval=6, extra_fields=None):
        super().__init__()
        self.default_raise = False
        self.index = index
        self.batch_size = batch_size
        self.flush_interval = flush_interval
        self.extra_fields = extra_fields if isinstance(extra_fields, dict) else {}
        self._buffer = []
        self.url = url
        self._timer = None
        self.serializer = EsJsonSerializer()

    @staticmethod
    def __get_es_datetime_str(timestamp):
        current_date = datetime.utcfromtimestamp(timestamp)
        return "{0!s}.{1:03d}Z".format(current_date.strftime('%Y-%m-%dT%H:%M:%S'), int(current_date.microsecond / 1000))

    def emit(self, record: logging.LogRecord) -> None:
        self.format(record)
        rec = self.extra_fields.copy()
        for key, value in record.__dict__.items():
            rec[key] = "" if value is None else value
        rec["timestamp"] = self.__get_es_datetime_str(record.created)

        self._buffer.append(rec)
        # 如果缓存已满,直接刷新
        if len(self._buffer) >= self.batch_size:
            self.flush()
        else:
            self.schedule_flush()

    def schedule_flush(self):
        if self._timer is None:
            self._timer = Timer(self.flush_interval, self.flush)
            self._timer.daemon = True
            self._timer.start()

    def flush(self):
        # 如果缓存中有日志,则批量写入
        if self._timer is not None and self._timer.is_alive():
            self._timer.cancel()
        self._timer = None
        if self._buffer:
            try:
                logs_buffer = self._buffer
                self._buffer = []
                self.bulk_data(logs_buffer)
            except Exception as exception:
                if self.default_raise:
                    raise exception

    def bulk_data(self, logs_buffer):
        print("批量写入 Elasticsearch")
        request_body = ""
        for log_record in logs_buffer:
            actions = {"index": {'_index': self.index, "_id": str(uuid4())}}
            data = json.dumps(actions)
            request_body += data
            request_body += "\n"
            request_body += EsJsonSerializer().json_dumps(log_record)
            request_body += "\n"
        request_body += "\n"
        headers = {'content-type': 'application/json'}
        requests.post(self.url, data=request_body, headers=headers)

    def close(self):
        if self._timer is not None:
            self.flush()
        self._timer = None

四、日志格式介绍

字段作用
%(levelname)s日志级别(如 DEBUG、INFO、WARNING、ERROR、CRITICAL),用于标识日志的严重程度。
%(asctime)s日志的时间戳,默认格式为 YYYY-MM-DD HH:MM:SS,mmm(可自定义 datefmt)。
%(process)d进程 ID,表示当前日志属于哪个进程(适用于多进程应用)。
%(filename)s记录日志的 Python 文件名(不包含路径)。
%(name)s记录日志的 Logger 名称,通常是 logging.getLogger(name) 设定的名称。
%(lineno)d代码中的行号,表示日志记录发生的确切位置。
%(module)s模块名称,即 filename 去掉 .py 的部分。
%(message)s实际的日志信息,即 logger.info(“内容”) 传入的内容。
%(levelno)s日志级别的数值(如 DEBUG=10, INFO=20, WARNING=30, ERROR=40, CRITICAL=50)
%(pathname)s记录日志的 Python 文件完整路径。
%(funcName)s记录日志所在的函数名。
%(thread)d线程 ID(适用于多线程应用)。
%(threadName)s线程名称。
%(processName)s记录日志时距 logging 模块加载后的毫秒数。
%(msecs)d记录日志的时间戳的毫秒部分。
# 示例
LOG_FORMAT = '%(levelname)s - %(asctime)s - [PID: %(process)d] [Thread: %(threadName)s] - %(pathname)s - %(funcName)s - Line: %(lineno)d - %(message)s'

五、完整的代码

import logging
import sys
import os
import json
import socket
import time
import uuid
from datetime import date, datetime
from decimal import Decimal
from typing import Any, ClassVar, Mapping, Optional, Tuple

from threading import Timer
from multiprocessing import Lock
import requests
from uuid import uuid4

LOG_ENABLED = True  # 是否开启日志
LOG_TO_CONSOLE = True  # 是否输出到控制台
CONSOLE_LEVEL = "INFO"  # 控制台的日志等级
LOG_TO_FILE = True  # 是否输出到文件
FILE_LEVEL = "INFO"  # 文件的日志等级
LOG_TO_ES = True  # 是否输出到 Elasticsearch
ES_LEVEL = "INFO"  # 输出到ES的日志等级

LOG_LEVEL = 'DEBUG'  # 日志级别

LOG_FORMAT = '%(levelname)s - %(asctime)s - process: %(process)d - %(filename)s - %(name)s - %(lineno)d - %(module)s - %(message)s'  # 每条日志输出格式

ELASTIC_SEARCH_HOST = 'http://192.168.3.200:9200'  # Elasticsearch Host
ELASTIC_SEARCH_INDEX = 'python'  # Elasticsearch Index Name
APP_NAME = "demo-fastapi"
APP_ENVIRONMENT = 'dev'  # 运行环境,如测试环境还是生产环境
LOG_DIR = os.getcwd()


class JsonSerializer:
    """JSON serializer relying on the standard library json module."""

    mimetype: ClassVar[str] = "application/json"

    def default(self, data: Any) -> Any:
        if isinstance(data, date):
            return data.isoformat()
        elif isinstance(data, uuid.UUID):
            return str(data)
        elif isinstance(data, Decimal):
            return float(data)
        raise Exception(f"Unable to serialize to JSON: {data!r} (type: {type(data).__name__})", )

    def json_dumps(self, data: dict) -> str:
        return json.dumps(
            data, default=self.default, ensure_ascii=False, separators=(",", ":")
        )

    def json_loads(self, data: str) -> dict:
        return json.loads(data)

    def loads(self, data: str) -> dict:
        return self.json_loads(data)

    def dumps(self, data: dict) -> str:
        # The body is already encoded to bytes
        # so we forward the request body along.
        if isinstance(data, str):
            return data
        return self.json_dumps(data)


def _attempt_serialize_numpy(data: Any) -> Tuple[bool, Any]:
    try:
        import numpy as np

        if isinstance(
                data,
                (
                        np.int_,
                        np.intc,
                        np.int8,
                        np.int16,
                        np.int32,
                        np.int64,
                        np.uint8,
                        np.uint16,
                        np.uint32,
                        np.uint64,
                ),
        ):
            return True, int(data)
        elif isinstance(
                data,
                (
                        np.float16,
                        np.float32,
                        np.float64,
                ),
        ):
            return True, float(data)
        elif isinstance(data, np.bool_):
            return True, bool(data)
        elif isinstance(data, np.datetime64):
            return True, data.item().isoformat()
        elif isinstance(data, np.ndarray):
            return True, data.tolist()

    except ImportError:
        # Since we failed to import 'numpy' we don't want to try again.
        return False, None

    return False, None


def _attempt_serialize_pandas(data: Any) -> Tuple[bool, Any]:
    try:
        import pandas as pd

        if isinstance(data, (pd.Series, pd.Categorical)):
            return True, data.tolist()
        elif isinstance(data, pd.Timestamp) and data is not getattr(pd, "NaT", None):
            return True, data.isoformat()
        elif data is getattr(pd, "NA", None):
            return True, None

    except ImportError:
        # Since we failed to import 'pandas' we don't want to try again.
        return False, None

    return False, None


def _attempt_serialize_numpy_or_pandas(data: Any) -> Tuple[bool, Any]:
    serialized, value = _attempt_serialize_numpy(data)
    if serialized:
        return serialized, value

    serialized, value = _attempt_serialize_pandas(data)
    if serialized:
        return serialized, value

    return False, None


TIME_TYPES = (date, datetime)
FLOAT_TYPES = (Decimal,)
INTEGER_TYPES = ()


class EsJsonSerializer(JsonSerializer):
    mimetype: ClassVar[str] = "application/json"

    def default(self, data: Any) -> Any:
        if isinstance(data, TIME_TYPES):
            # Little hack to avoid importing pandas but to not
            # return 'NaT' string for pd.NaT as that's not a valid
            # Elasticsearch date.
            formatted_data = data.isoformat()
            if formatted_data != "NaT":
                return formatted_data

        if isinstance(data, uuid.UUID):
            return str(data)
        elif isinstance(data, FLOAT_TYPES):
            return float(data)

        # This is kept for backwards compatibility even
        # if 'INTEGER_TYPES' isn't used by default anymore.
        elif INTEGER_TYPES and isinstance(data, INTEGER_TYPES):
            return int(data)

        # Special cases for numpy and pandas types
        # These are expensive to import so we try them last.
        serialized, value = _attempt_serialize_numpy_or_pandas(data)
        if serialized:
            return value

        raise TypeError(f"Unable to serialize {data!r} (type: {type(data)})")


class EsHandler(logging.Handler):

    def __init__(self, url, index, batch_size=100, flush_interval=6, extra_fields=None):
        super().__init__()
        self.default_raise = False
        self.index = index
        self.batch_size = batch_size
        self.flush_interval = flush_interval
        self.extra_fields = extra_fields if isinstance(extra_fields, dict) else {}
        self._buffer = []
        self.url = url
        self._timer = None
        self.serializer = EsJsonSerializer()

    @staticmethod
    def __get_es_datetime_str(timestamp):
        current_date = datetime.utcfromtimestamp(timestamp)
        return "{0!s}.{1:03d}Z".format(current_date.strftime('%Y-%m-%dT%H:%M:%S'), int(current_date.microsecond / 1000))

    def emit(self, record: logging.LogRecord) -> None:
        self.format(record)
        rec = self.extra_fields.copy()
        for key, value in record.__dict__.items():
            rec[key] = "" if value is None else value
        rec["timestamp"] = self.__get_es_datetime_str(record.created)

        self._buffer.append(rec)
        # 如果缓存已满,直接刷新
        if len(self._buffer) >= self.batch_size:
            self.flush()
        else:
            self.schedule_flush()

    def schedule_flush(self):
        if self._timer is None:
            self._timer = Timer(self.flush_interval, self.flush)
            self._timer.daemon = True
            self._timer.start()

    def flush(self):
        # 如果缓存中有日志,则批量写入
        if self._timer is not None and self._timer.is_alive():
            self._timer.cancel()
        self._timer = None
        if self._buffer:
            try:
                logs_buffer = self._buffer
                self._buffer = []
                self.bulk_data(logs_buffer)
            except Exception as exception:
                if self.default_raise:
                    raise exception

    def bulk_data(self, logs_buffer):
        print("批量写入 Elasticsearch")
        request_body = ""
        for log_record in logs_buffer:
            actions = {"index": {'_index': self.index, "_id": str(uuid4())}}
            data = json.dumps(actions)
            request_body += data
            request_body += "\n"
            request_body += EsJsonSerializer().json_dumps(log_record)
            request_body += "\n"
        request_body += "\n"
        headers = {'content-type': 'application/json'}
        requests.post(self.url, data=request_body, headers=headers)

    def close(self):
        if self._timer is not None:
            self.flush()
        self._timer = None


class Logger:
    _instance = None
    _lock = Lock()

    def __init__(self, ):
        self.logger = logging.getLogger("__file__")
        self.logger.setLevel(LOG_LEVEL)
        self.add_stream_handle()
        self.add_file_handle()
        self.add_es_handle()

    def add_stream_handle(self):
        if LOG_TO_CONSOLE and LOG_ENABLED:
            handler = logging.StreamHandler(sys.stdout)
            handler.setLevel(level=CONSOLE_LEVEL)
            handler.setFormatter(logging.Formatter(LOG_FORMAT))
            self.logger.addHandler(handler)

    def add_file_handle(self):
        if LOG_ENABLED and LOG_TO_FILE:
            logs_dir = os.path.join(LOG_DIR, "logs")
            os.makedirs(logs_dir, exist_ok=True)
            file = os.path.join(logs_dir, f'{datetime.now().strftime("%Y%m%d_%H%M%S")}.log')
            handler = logging.FileHandler(file, encoding="utf-8")
            handler.setLevel(level=FILE_LEVEL)
            handler.setFormatter(logging.Formatter(LOG_FORMAT))
            self.logger.addHandler(handler)

    def add_es_handle(self):
        if LOG_ENABLED and LOG_TO_ES:
            # 添加 CMRESHandler
            url = f"{ELASTIC_SEARCH_HOST}/_bulk"
            extra_fields = {"app_name": APP_NAME, "app_env": APP_ENVIRONMENT, 'host': socket.gethostname(),
                            'host_ip': socket.gethostbyname(socket.gethostname())}
            es_handler = EsHandler(url, index=ELASTIC_SEARCH_INDEX, extra_fields=extra_fields)
            es_handler.setLevel(level=LOG_LEVEL)
            formatter = logging.Formatter(LOG_FORMAT)
            es_handler.setFormatter(formatter)
            self.logger.addHandler(es_handler)

    @classmethod
    def get_logger(cls):
        cls._lock.acquire()
        if cls._instance is None:
            cls._instance = Logger().logger
        cls._lock.release()
        return cls._instance


if __name__ == '__main__':
    logger = Logger.get_logger()
    logger.info("INFO")
    logger.warning("WARNING")
    logger.error("ERROR")
    time.sleep(100)

结语

掌握 Python 的 logging 模块,能让你的日志管理更加高效,无论是简单的控制台调试,还是日志文件持久化存储,甚至是对接 ELK 进行集中管理,都是开发者必备的技能。

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

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

相关文章

设计模式15:中介者模式

系列总链接:《大话设计模式》学习记录_net 大话设计-CSDN博客 1.概述 中介者模式(Mediator Pattern)是一种行为设计模式,旨在通过一个中介对象来封装一系列对象之间的交互方式,从而减少这些对象间的直接依赖。在该模式…

爬取网站内容转为markdown 和 html(通常模式)

我们遇到一些自己喜欢内容,想保存下来,手动复制粘贴很麻烦,我们使用 python 来爬取这些内容。 一、代码 downlod.py import os import requests from bs4 import BeautifulSoup from urllib.parse import urljoin# 目标网页(可…

【Linux】命令操作、打jar包、项目部署

阿华代码,不是逆风,就是我疯 你们的点赞收藏是我前进最大的动力!! 希望本文内容能够帮助到你!! 目录 一:Xshell下载 1:镜像设置 二:阿里云设置镜像Ubuntu 三&#xf…

Unity合批处理优化内存序列帧播放动画

Unity合批处理序列帧优化内存 介绍图片导入到Unity中的处理Unity中图片设置处理Unity中图片裁剪 创建序列帧动画总结 介绍 这里是针对Unity序列帧动画的优化内容,将多个图片合批处理然后为了降低Unity的内存占用,但是相对的质量也会稍微降低。可自行进行…

day4 多连联表慢查询sql查询优化

1.Explain分析sql语句出现的字段是什么意思 id: 查询的序列号,表示查询中 select 子句或操作表的顺序。 如果 id 相同,则执行顺序从上到下。 如果 id 不同,如果是子查询,id 的值会递增,id 值越大优先级越高&#xff0c…

基于豆瓣2025电影数据可视化分析系统的设计与实现

✔️本项目旨在通过对豆瓣电影数据进行综合分析与可视化展示,构建一个基于Python的大数据可视化系统。通过数据爬取收集、清洗、分析豆瓣电影数据,我们提供了一个全面的电影信息平台,为用户提供深入了解电影产业趋势、影片评价与演员表现的工…

力扣高频sql 50题(基础版) :NULL, 表连接,子查询,case when和avg的结合

NULL的处理 nvl(字段,num) 和数字进行比较需要先使用nvl(字段,num)函数处理空值 思路: 没有被id 2 的客户推荐>> 过滤条件 referee_id !2 没有被id 2 的客户推荐>>被其他客户推荐, 但是也有可能没有被任何客户推荐>>NULL 考点: NULL是 不一个具体的数…

夜莺监控发布 v8.beta5 版本,优化 UI,新增接口认证方式便于鉴权

以防读者不了解夜莺,开头先做个介绍: 夜莺监控,英文名字 Nightingale,是一款侧重告警的监控类开源项目。类似 Grafana 的数据源集成方式,夜莺也是对接多种既有的数据源,不过 Grafana 侧重在可视化&#xff…

Python - 爬虫利器 - BeautifulSoup4常用 API

文章目录 前言BeautifulSoup4 简介主要特点:安装方式: 常用 API1. 创建 BeautifulSoup 对象2. 查找标签find(): 返回匹配的第一个元素find_all(): 返回所有匹配的元素列表select_one() & select(): CSS 选择器 3. 访问标签内容text 属性: 获取标签内纯文本get_t…

认识 ADB(Android Debug Bridge,Android SDK 中的一个工具)

一、ADB 概述 ADB,全称 Android Debug Bridge,是 Android SDK 中的一个工具 ADB 位于 Android SDK 下 platform-tools 目录中 ADB 起到调试桥的作用,ADB 可以让开发者通过 USB 连接安卓设备,并在电脑上执行各种命令,…

模拟解决哈希表冲突

目录 解决哈希表冲突原理: 模拟解决哈希表冲突代码: 负载因子: 动态扩容: 总结: HashMap和HashSet的总结: 解决哈希表冲突原理: 黑色代表一个数组,当 出现哈希冲突时&#xff0…

FPGA简介|结构、组成和应用

Field Programmable Gate Arrays(FPGA,现场可编程逻辑门阵列),是在PAL、GAL、CPLD等可编程器件的基础上进一步发展的产物, 是作为专用集成电路(ASIC)领域中的一种半定制电路而出现的&#xff0c…

【机器学习】超参数调优指南:交叉验证,网格搜索,混淆矩阵——基于鸢尾花与数字识别案例的深度解析

一、前言:为何要学交叉验证与网格搜索? 大家好!在机器学习的道路上,我们经常面临一个难题:模型调参。比如在 KNN 算法中,选择多少个邻居(n_neighbors)直接影响预测效果。 • 蛮力猜…

UGUI RectTransform的SizeDelta属性

根据已知内容,SizeDelta offsetMax - offsetMin 1.锚点聚拢情况下 输出 那么此时SizeDelta就是UI元素的长宽大小 2. 锚点分散时 引用自此篇文章中的描述 揭秘!anchoredPosition的几何意义! SizeDelta offsetMax - offsetMin (rectMax…

51单片机入门_10_数码管动态显示(数字的使用;简单动态显示;指定值的数码管动态显示)

接上篇的数码管静态显示,以下是接上篇介绍到的动态显示的原理。 动态显示的特点是将所有位数码管的段选线并联在一起,由位选线控制是哪一位数码管有效。选亮数码管采用动态扫描显示。所谓动态扫描显示即轮流向各位数码管送出字形码和相应的位选&#xff…

mybatis使用typeHandler实现类型转换

使用mybatis作为操作数据库的orm框架,操作基本数据类型时可以通过内置的类型处理器完成java数据类型和数据库类型的转换,但是对于扩展的数据类型要实现与数据库类型的转换就需要自定义类型转换器完成,比如某个实体类型存储到数据库&#xff0…

瑞萨RA-T系列芯片ADCGPT功能模块的配合使用

在马达或电源工程中,往往需要采集多路AD信号,且这些信号的优先级和采样时机不相同。本篇介绍在使用RA-T系列芯片建立马达或电源工程时,如何根据需求来设置主要功能模块ADC&GPT,包括采样通道打包和分组,GPT触发启动…

最新智能优化算法:牛优化( Ox Optimizer,OX)算法求解经典23个函数测试集,MATLAB代码

一、牛优化算法 牛优化( OX Optimizer,OX)算法由 AhmadK.AlHwaitat 与 andHussamN.Fakhouri于2024年提出,该算法的设计灵感来源于公牛的行为特性。公牛以其巨大的力量而闻名,能够承载沉重的负担并进行远距离运输。这种…

【linux】在 Linux 服务器上部署 DeepSeek-r1:70b 并通过 Windows 远程可视化使用

【linux】在 Linux 服务器上部署 DeepSeek-r1:70b 并通过 Windows 远程可视化使用 文章目录 【linux】在 Linux 服务器上部署 DeepSeek-r1:70b 并通过 Windows 远程可视化使用个人配置详情一、安装ollama二、下载deepseek版本模型三、在 Linux 服务器上配置 Ollama 以允许远程访…

【Linux网络编程】应用层协议HTTP(请求方法,状态码,重定向,cookie,session)

🎁个人主页:我们的五年 🔍系列专栏:Linux网络编程 🌷追光的人,终会万丈光芒 🎉欢迎大家点赞👍评论📝收藏⭐文章 ​ Linux网络编程笔记: https://blog.cs…