当前位置: 首页 > article >正文

深入浅出 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 datareturn self.json_dumps(data)def _attempt_serialize_numpy(data: Any) -> Tuple[bool, Any]:try:import numpy as npif 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, Nonereturn False, Nonedef _attempt_serialize_pandas(data: Any) -> Tuple[bool, Any]:try:import pandas as pdif 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, Noneexcept ImportError:# Since we failed to import 'pandas' we don't want to try again.return False, Nonereturn False, Nonedef _attempt_serialize_numpy_or_pandas(data: Any) -> Tuple[bool, Any]:serialized, value = _attempt_serialize_numpy(data)if serialized:return serialized, valueserialized, value = _attempt_serialize_pandas(data)if serialized:return serialized, valuereturn False, NoneTIME_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_dataif 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 valueraise 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 = Falseself.index = indexself.batch_size = batch_sizeself.flush_interval = flush_intervalself.extra_fields = extra_fields if isinstance(extra_fields, dict) else {}self._buffer = []self.url = urlself._timer = Noneself.serializer = EsJsonSerializer()@staticmethoddef __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 valuerec["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 = Trueself._timer.start()def flush(self):# 如果缓存中有日志,则批量写入if self._timer is not None and self._timer.is_alive():self._timer.cancel()self._timer = Noneif self._buffer:try:logs_buffer = self._bufferself._buffer = []self.bulk_data(logs_buffer)except Exception as exception:if self.default_raise:raise exceptiondef 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 += datarequest_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, Tuplefrom threading import Timer
from multiprocessing import Lock
import requests
from uuid import uuid4LOG_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 datareturn self.json_dumps(data)def _attempt_serialize_numpy(data: Any) -> Tuple[bool, Any]:try:import numpy as npif 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, Nonereturn False, Nonedef _attempt_serialize_pandas(data: Any) -> Tuple[bool, Any]:try:import pandas as pdif 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, Noneexcept ImportError:# Since we failed to import 'pandas' we don't want to try again.return False, Nonereturn False, Nonedef _attempt_serialize_numpy_or_pandas(data: Any) -> Tuple[bool, Any]:serialized, value = _attempt_serialize_numpy(data)if serialized:return serialized, valueserialized, value = _attempt_serialize_pandas(data)if serialized:return serialized, valuereturn False, NoneTIME_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_dataif 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 valueraise 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 = Falseself.index = indexself.batch_size = batch_sizeself.flush_interval = flush_intervalself.extra_fields = extra_fields if isinstance(extra_fields, dict) else {}self._buffer = []self.url = urlself._timer = Noneself.serializer = EsJsonSerializer()@staticmethoddef __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 valuerec["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 = Trueself._timer.start()def flush(self):# 如果缓存中有日志,则批量写入if self._timer is not None and self._timer.is_alive():self._timer.cancel()self._timer = Noneif self._buffer:try:logs_buffer = self._bufferself._buffer = []self.bulk_data(logs_buffer)except Exception as exception:if self.default_raise:raise exceptiondef 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 += datarequest_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 = Noneclass 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:# 添加 CMRESHandlerurl = 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)@classmethoddef get_logger(cls):cls._lock.acquire()if cls._instance is None:cls._instance = Logger().loggercls._lock.release()return cls._instanceif __name__ == '__main__':logger = Logger.get_logger()logger.info("INFO")logger.warning("WARNING")logger.error("ERROR")time.sleep(100)

结语

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

相关文章:

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

在 Python 开发过程中,日志(Logging)是不可或缺的调试和监控工具。合理的日志管理不仅能帮助开发者快速定位问题,还能提供丰富的数据支持,让应用更具可观测性。本文将带你全面了解 Python logging 模块,涵盖…...

Android WindowContainer窗口结构

Android窗口是根据显示屏幕来管理,每个显示屏幕的窗口层级分为37层,0-36层。每层可以放置多个窗口,上层窗口覆盖下面的。 要理解窗口的结构,需要学习下WindowContainer、RootWindowContainer、DisplayContent、TaskDisplayArea、T…...

创建一个新的 React Native 项目

之前一直使用 npx react-native init my_app 来创建 RN 项目, 但是新版本会报错: Need to install the following packages: react-native0.77.0 Ok to proceed? (y) y npm ERR! code ERESOLVE npm ERR! ERESOLVE could not resolve npm ERR! npm ERR! While resolving: unde…...

Linux vmstat 命令

作用 性能监控工具。 示例 vmstat 2 5:每 2 秒执行 1 次,共执行 5 次。 下列示例中加了 -w 80 参数增加显示长度,目的是对齐字段值,Ctrl C 可停止输出。 [rootlocalhost ~]# vmstat 2 -w 80 procs -----------------------me…...

2025年最新版1688平台图片搜索接口技术指南及Python实现

随着电商行业的蓬勃发展,1688作为国内领先的B2B交易平台,其商品搜索功能对于买家和卖家而言都至关重要。图片搜索作为其中的一种高级搜索方式,能够极大地提升用户的搜索体验和准确性。本文将详细介绍如何通过API接口实现1688平台的图片搜索功…...

基于A*算法与贝塞尔曲线的路径规划与可视化:从栅格地图到平滑路径生成

引言 在机器人导航、自动驾驶和游戏开发等领域,路径规划是一个核心问题。如何高效地找到从起点到终点的最优路径,并且确保路径的平滑性和安全性,是许多应用场景中的关键挑战。本文将介绍一种结合A算法和贝塞尔曲线的路径规划方法,并通过Pygame实现可视化。我们将从栅格地图…...

使用verilog 实现 cordic 算法 ----- 旋转模式

1-设计流程 ● 了解cordic 算法原理,公式,模式,伸缩因子,旋转方向等,推荐以下链接视频了解 cordic 算法。哔哩哔哩-cordic算法原理讲解 ● 用matlab 或者 c 实现一遍算法 ● 在FPGA中用 verilog 实现,注意…...

【css】width:100%;padding:20px;造成超出100%宽度的解决办法 - box-sizing的使用方法 - CSS布局

问题 修改效果 解决方法 .xx {width: 100%;padding: 0 20px;box-sizing: border-box; } 默认box-sizing: content-box下, width 内容的宽度 height 内容的高度 宽度和高度的计算值都不包含内容的边框(border)和内边距(padding&…...

贪心算法_翻硬币

蓝桥账户中心 依次遍历 不符合条件就反转 题目要干嘛 你就干嘛 #include <bits/stdc.h>#define endl \n using namespace std;int main() {ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); string s; cin >> s;string t; cin >> t;int ret 0;for ( i…...

vue开发06:前端通过webpack配置代理处理跨域问题

1.定义 在浏览器尝试请求不同源&#xff08;域名、协议、端口号不同&#xff09;的资源时&#xff0c;浏览器的同源策略会阻止这种跨域请求。&#xff08;比如前端端口15500&#xff0c;后端端口5050&#xff0c;前端界面不可以直接调用5050端口&#xff09; 2.解决方案 使用前…...

深入HBase——引入

引入 前面我们通过深入HDFS到深入MapReduce &#xff0c;从设计和落地&#xff0c;去深入了解了大数据最底层的基石——存储与计算是如何实现的。 这个专栏则开始来看大数据的三驾马车中最后一个。 通过前面我们对于GFS和MapReduce论文实现的了解&#xff0c;我们知道GFS在数…...

2025年02月12日Github流行趋势

项目名称&#xff1a;data-formulator 项目地址url&#xff1a;https://github.com/microsoft/data-formulator 项目语言&#xff1a;TypeScript 历史star数&#xff1a;4427 今日star数&#xff1a;729 项目维护者&#xff1a;danmarshall, Chenglong-MS, apps/dependabot, mi…...

【落羽的落羽 数据结构篇】双向链表

文章目录 一、链表的分类二、双向链表1. 结构2. 申请一个新节点3. 尾部插入数据4. 头部插入数据5. 尾部删除数据6. 头部删除数据7. 在指定位置之后插入数据8. 删除指定位置节点9. 销毁链表 一、链表的分类 链表的分类实际上要从这三个方向分析&#xff1a;是否带头、单向还是双…...

Golang的并发编程问题解决思路

Golang的并发编程问题解决思路 一、并发编程基础 并发与并行 在计算机领域&#xff0c;“并发”和“并行”经常被混为一谈&#xff0c;但它们有着不同的含义。并发是指一段时间内执行多个任务&#xff0c;而并行是指同时执行多个任务。在 Golang 中&#xff0c;通过 goroutines…...

vsftpd 配置项说明

目录 一&#xff1a;vsftpd 配置文件说明二&#xff1a;vsftpd 服务和连接设置三&#xff1a;vsftpd 用户根目录管理四&#xff1a;vsftpd 匿名用户模式管理五&#xff1a;vsftpd 本地用户模式管理六&#xff1a; vsftpd 虚拟用户模式管理 一&#xff1a;vsftpd 配置文件说明 …...

剑指offer第2版:搜索算法(二分/DFS/BFS)

查找本质就是排除的过程&#xff0c;不外乎顺序查找、二分查找、哈希查找、二叉排序树查找、DFS/BFS查找 一、p39-JZ3 找出数组中重复的数字&#xff08;利用特性&#xff09; 数组中重复的数字_牛客题霸_牛客网 方法1&#xff1a;全部排序再进行逐个扫描找重复。 时间复杂…...

Pytorch与大模型有什么关系

PyTorch 是 深度学习领域最流行的框架之一&#xff0c;在大模型的训练、推理、优化等方面发挥了重要作用。 大模型&#xff08;如 GPT、LLaMA、Stable Diffusion&#xff09;大多是基于 PyTorch 进行开发和训练的。 1. PyTorch 在大模型中的作用 大模型&#xff08;如 ChatGP…...

在 CentOS 上更改 SSH 默认端口以提升服务器安全性

&#x1f680; 作者主页&#xff1a; 有来技术 &#x1f525; 开源项目&#xff1a; youlai-mall ︱vue3-element-admin︱youlai-boot︱vue-uniapp-template &#x1f33a; 仓库主页&#xff1a; GitCode︱ Gitee ︱ Github &#x1f496; 欢迎点赞 &#x1f44d; 收藏 ⭐评论 …...

PyTorch Lightning pytorch.loggers模块介绍

pytorch.loggers 是 PyTorch Lightning 提供的一个模块&#xff0c;用于集成多种日志记录工具&#xff0c;方便开发者在训练过程中记录和监控模型的性能指标、超参数等信息。日志记录器&#xff08;Loggers&#xff09;是 PyTorch Lightning 的重要组成部分&#xff0c;可以通过…...

2025年:边缘计算崛起下运维应对新架构挑战

一、引言 随着科技的飞速发展&#xff0c;2025年边缘计算正以前所未有的速度崛起&#xff0c;给运维行业带来了全新的架构挑战。在这个充满机遇与挑战的时代&#xff0c;美信时代公司的美信监控易运维管理软件成为运维领域应对这些挑战的有力武器。 二、边缘计算崛起带来的运维…...

什么是UV环形光源

UV环形光源是一种用于特定照明需求的设备&#xff0c;以下是其关键点&#xff1a; 定义 UV环形光源&#xff1a;发出紫外光的环形照明装置&#xff0c;常用于机器视觉、工业检测等领域。特点 均匀照明&#xff1a;环形设计确保光线均匀分布&#xff0c;减少阴影。 高亮度&…...

怎么理解 Spring Boot 的约定优于配置 ?

在传统的 Spring 开发中&#xff0c;大家可能都有过这样的经历&#xff1a;项目还没开始写几行核心业务代码&#xff0c;就已经在各种配置文件中耗费了大量时间。比如&#xff0c;要配置数据库连接&#xff0c;不仅要在 XML 文件里编写冗长的数据源配置&#xff0c;还要处理事务…...

学习总结2.14

深搜将题目分配&#xff0c;如果是两个题目&#xff0c;就可以出现左左&#xff0c;左右&#xff0c;右左&#xff0c;右右四种时间分配&#xff0c;再在其中找最小值&#xff0c;即是两脑共同处理的最小值 #include <stdio.h> int s[4]; int sum0; int brain[25][25]; …...

Electron 客户端心跳定时任务调度库调研文档 - Node.js 任务调度库技术调研文档

Electron 客户端心跳定时任务调度库调研文档 - Node.js 任务调度库技术调研文档 本文将对七个流行的定时任务调度库&#xff1a;node-cron、rxjs、bull、node-schedule、agenda、bree、cron。这些库都可以用来处理定时任务&#xff0c;但它们的特点和适用场景有所不同。我们将从…...

【学术投稿-第四届智能电网和绿色能源国际学术会议(ICSGGE 2025)】CSS基本选择器详解:掌握基础,轻松布局网页

可线上 官网&#xff1a;www.icsgge.org 时间&#xff1a;2025年2月28-3月2日 目录 前言 一、基本选择器简介 1. 元素选择器&#xff08;Type Selector&#xff09; 基本语法 示例 注意事项 2. 类选择器&#xff08;Class Selector&#xff09; 基本语法 示例 注意…...

singleTaskAndroid的Activity启动模式知识点总结

一. 前提知识 1.1. 任务栈知识 二. Activity启动模式的学习 2.1 standard 2.2 singleTop 2.3.singleTask 2.4.singleInstance 引言&#xff1a; Activity作为四大组件之一&#xff0c;也可以说Activity是其中最重要的一个组件&#xff0c;其负责调节APP的视图&#xff…...

Java Stream 全面解析

Java Stream 全面解析 Java 8 引入的 Stream API 提供了一种高效且声明式的方式来处理集合数据。Stream 允许你以函数式编程风格操作数据&#xff0c;支持并行处理&#xff0c;并且可以显著简化代码。下面我们将从 创建操作、中间操作 和 终端操作 三个方面进行全面深入的解析…...

OpenCV识别电脑摄像头中的圆形物体

思路步骤 初始化摄像头&#xff1a;使用cv2.VideoCapture打开电脑摄像头。处理每一帧图像&#xff1a;对摄像头捕获的每一帧图像进行处理&#xff0c;包括灰度化、高斯模糊、霍夫圆变换等操作。绘制圆形和圆心&#xff1a;如果检测到圆形&#xff0c;使用cv2.circle函数用黄线…...

如何在 Tomcat 中屏蔽错误报告

Tomcat 屏蔽错误信息 <h1>HTTP状态 400 - 错误的请求</h1><hr class"line" /><p><b>类型</b> 异常报告</p><p><b>消息</b> 在请求目标中找到无效字符。有效字符在RFC 7230和RFC 3986中定义</p>&…...

Vue 入门到实战 十

第10章 Vue Router​​​​​​​ 目录 10.1 什么是路由 10.2 Vue Router的安装 10.2.1 本地独立版本方法 10.2.2 CDN方法 10.2.3 NPM方法 10.2.4 命令行工具&#xff08;Vue CLI&#xff09;方法 10.3 Vue Router的基本用法 10.3.1 跳转与传参 10.3.2 配置路由 10.…...