python: LogHelper
# encoding: utf-8 # 版权所有 2026 ©涂聚文有限公司™ ® # 许可信息查看言語成了邀功盡責的功臣還需要行爲每日來值班嗎 # 描述日志 # Author : geovindu,Geovin Du 涂聚文. # IDE : PyCharm 2024.3.6 python 3.11 # os : windows 10 # database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j # Datetime : 2026/7/31 19:58 # User : geovindu # Product : PyCharm # Project : PyAlgorithms # File : LogHelper.py import os import sys import time import threading import queue import datetime import traceback import json import zipfile import random import re from enum import Enum from typing import Tuple, Optional, Dict, List, Any, Callable import atexit import platform import zoneinfo class LogLevel(Enum): 日志等级枚举与 C# 版本对齐 OFF 0 # 关闭输出 ERROR 1 INFO 2 DEBUG 3 # 类型别名 DesensitizeFunc Callable[[str, str, str], str] AlertCallbackFunc Callable[[str, str, Dict[str, Any]], None] class LogHelper: Python 异步日志工具类 新增特性业务模块分目录隔离存储 完整能力清单 1. 【新增】按业务模块分目录存储日志防止多业务日志混杂 2. 异步队列批量写入多线程安全、优雅退出 3. 文本 / JSON结构化日志动态切换异常堆栈独立字段 stack_trace 4. 控制台彩色输出开关自动按大小切割日志、备份自动zip压缩 5. 过期日志自动清理磁盘超限策略优先删zip再删log 6. 分级独立磁盘上限 全局总磁盘上限兜底 7. 全局/分级日志采样策略减轻高频日志IO压力 8. 日志脱敏回调函数支持携带模块名 9. 告警回调磁盘超限、队列拥堵事件通知 10.自定义时区、自定义时间格式化模板 日志文件生成样式 Logs/ ├─ default/ # 旧代码默认模块 │ ├─ info_20260731.log │ └─ debug_20260731.log ├─ user_service/ # 用户业务模块 │ └─ info_20260731.log ├─ order_service/ # 订单业务模块 │ └─ debug_20260731.log ├─ pay_service/ # 支付业务模块 │ └─ error_20260731.log └─ goods_service/ # 商品业务模块 └─ info_20260731.log # 【全局配置区 按需修改】 LOG_ROOT os.path.join(os.getcwd(), Logs) DEFAULT_MODULE default # 默认业务模块名称 KEEP_DAYS 7 # 日志保留天数 MAX_FILE_SIZE_MB 10 # 单个日志文件最大MB MAX_FILE_SIZE_BYTES MAX_FILE_SIZE_MB * 1024 * 1024 QUEUE_MAXSIZE 10000 QUEUE_ALERT_THRESHOLD 7000 BATCH_WRITE_COUNT 10 ENABLE_CONSOLE_OUTPUT True ENABLE_JSON_FORMAT False COMPRESS_BAK_LOG True # 磁盘容量限制 MB MAX_DISK_TOTAL_MB 500 LEVEL_DISK_LIMIT { debug: 150, info: 250, error: 100 } # 采样配置 0~1.0 GLOBAL_SAMPLING_RATE 1.0 LEVEL_SAMPLING_RATE { debug: 1.0, info: 1.0, error: 1.0 } DISK_CHECK_INTERVAL_SEC 60 TIME_ZONE_NAME Asia/Shanghai TIME_FORMAT_TEMPLATE %Y-%m-%d %H:%M:%S _log_level LogLevel.DEBUG # 外部回调注册 _desensitize_callback: Optional[DesensitizeFunc] None _alert_callback: Optional[AlertCallbackFunc] None # 内部变量 _log_queue: queue.Queue queue.Queue(QUEUE_MAXSIZE) _stop_event threading.Event() _writer_thread: Optional[threading.Thread] None _last_clean_time: datetime.datetime _last_disk_check_time 0.0 _init_finished False _file_operate_lock threading.Lock() _pid os.getpid() _tz_info: Optional[zoneinfo.ZoneInfo] None # 控制台颜色 _COLOR_MAP { DEBUG: \033[34m, INFO: \033[32m, ERROR: \033[31m, RESET: \033[0m } if platform.system() Windows: try: import ctypes kernel32 ctypes.windll.kernel32 kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7) except Exception: for k in _COLOR_MAP: _COLOR_MAP[k] classmethod def _safe_module_name(cls, module: str) - str: 安全过滤模块名称防止路径穿越、非法字符 :param module: :return: if not isinstance(module, str) or module.strip() : return cls.DEFAULT_MODULE # 只允许字母数字下划线横杠 safe_str re.sub(r[^\w\-], _, module.strip()) return safe_str classmethod def get_module_log_dir(cls, module: str) - str: 获取指定业务模块日志根目录 :param module: :return: mod_name cls._safe_module_name(module) return os.path.join(cls.LOG_ROOT, mod_name) classmethod def _init_timezone(cls): :return: try: cls._tz_info zoneinfo.ZoneInfo(cls.TIME_ZONE_NAME) except Exception: cls._tz_info None classmethod def get_now_datetime(cls) - datetime.datetime: :return: if cls._tz_info: return datetime.datetime.now(tzcls._tz_info) return datetime.datetime.now() # 回调注册API classmethod def register_desensitize(cls, func: DesensitizeFunc): 脱敏回调签名func(level:str, module:str, msg:str) - str cls._desensitize_callback func classmethod def register_alert_callback(cls, func: AlertCallbackFunc): :param func: :return: cls._alert_callback func # 动态配置API classmethod def set_log_level(cls, level: LogLevel): :param level: :return: cls._log_level level classmethod def switch_json_format(cls, enable: bool): :param enable: :return: cls.ENABLE_JSON_FORMAT enable classmethod def switch_console(cls, enable: bool): cls.ENABLE_CONSOLE_OUTPUT enable classmethod def set_timezone(cls, tz_name: str): :param tz_name: :return: cls.TIME_ZONE_NAME tz_name cls._init_timezone() classmethod def set_time_format(cls, fmt_str: str): :param fmt_str: :return: cls.TIME_FORMAT_TEMPLATE fmt_str classmethod def set_sampling_rate(cls, level: str, rate: float): :param level: :param rate: :return: rate max(0.0, min(1.0, rate)) cls.LEVEL_SAMPLING_RATE[level.lower()] rate # 采样判断 classmethod def _need_sample(cls, level: str) - bool: :param level: :return: lv level.lower() lv_rate cls.LEVEL_SAMPLING_RATE.get(lv, 1.0) final_rate cls.GLOBAL_SAMPLING_RATE * lv_rate if final_rate 1.0: return True return random.random() final_rate # 磁盘空间管理适配模块目录 classmethod def _get_dir_total_size_mb(cls, folder: str, filter_prefix: str None) - float: :param folder: :param filter_prefix: :return: total_bytes 0 if not os.path.exists(folder): return 0.0 for entry in os.scandir(folder): if entry.is_dir(): # 递归遍历模块子目录 total_bytes cls._get_dir_total_size_mb(entry.path, filter_prefix) continue if not entry.is_file(): continue if not (entry.name.endswith(.log) or entry.name.endswith(.zip)): continue if filter_prefix is not None and not entry.name.startswith(f{filter_prefix}_): continue total_bytes entry.stat().st_size return total_bytes / 1024 / 1024 classmethod def _get_sorted_log_files(cls, folder: str, filter_prefix: str None): :param folder: :param filter_prefix: :return: file_list [] if not os.path.exists(folder): return file_list for entry in os.scandir(folder): if entry.is_dir(): file_list.extend(cls._get_sorted_log_files(entry.path, filter_prefix)) continue if not entry.is_file(): continue if not (entry.name.endswith(.log) or entry.name.endswith(.zip)): continue if filter_prefix is not None and not entry.name.startswith(f{filter_prefix}_): continue file_list.append((entry.stat().st_mtime, entry.path)) file_list.sort(keylambda x: x[0]) return [item[1] for item in file_list] classmethod def _trigger_alert(cls, event_type: str, message: str, ext: Dict[str, Any] None): :param event_type: :param message: :param ext: :return: if cls._alert_callback is None: return try: ext_info ext if ext is not None else {} cls._alert_callback(event_type, message, ext_info) except Exception: pass classmethod def _try_free_disk_space(cls, level: str None) - bool: :param level: :return: while True: if level is not None: usage_mb cls._get_dir_total_size_mb(cls.LOG_ROOT, filter_prefixlevel) limit_mb cls.LEVEL_DISK_LIMIT.get(level, cls.MAX_DISK_TOTAL_MB) if usage_mb limit_mb: return True else: usage_mb cls._get_dir_total_size_mb(cls.LOG_ROOT) if usage_mb cls.MAX_DISK_TOTAL_MB: return True all_files cls._get_sorted_log_files(cls.LOG_ROOT, filter_prefixlevel) if not all_files: return False deleted False # 优先删除zip for fp in all_files: if fp.endswith(.zip): try: os.remove(fp) print(f[LogHelper CLEAN] 磁盘超限删除老旧压缩包{os.path.basename(fp)}, filesys.stderr) deleted True break except Exception: continue if deleted: continue # 删除log for fp in all_files: if fp.endswith(.log): try: os.remove(fp) print(f[LogHelper CLEAN] 磁盘超限删除老旧日志{os.path.basename(fp)}, filesys.stderr) deleted True break except Exception: continue if not deleted: return False classmethod def _check_disk_limit(cls, level: str) - bool: :param level: :return: now_ts time.time() if now_ts - cls._last_disk_check_time cls.DISK_CHECK_INTERVAL_SEC: return False cls._last_disk_check_time now_ts lv level.lower() lv_usage cls._get_dir_total_size_mb(cls.LOG_ROOT, lv) lv_limit cls.LEVEL_DISK_LIMIT.get(lv, cls.MAX_DISK_TOTAL_MB) total_usage cls._get_dir_total_size_mb(cls.LOG_ROOT) over_flag False if lv_usage lv_limit: cls._trigger_alert( DISK_OVER_LIMIT, f日志【{level}】分级磁盘占用超限({lv_usage:.2f}MB/{lv_limit}MB)开始清理, {level: level, usage_mb: lv_usage, limit_mb: lv_limit, total_usage: total_usage} ) success cls._try_free_disk_space(lv) if not success: over_flag True elif total_usage cls.MAX_DISK_TOTAL_MB: cls._trigger_alert( DISK_OVER_LIMIT, f日志总磁盘占用超限({total_usage:.2f}MB/{cls.MAX_DISK_TOTAL_MB}MB)开始清理, {usage_mb: total_usage, limit_mb: cls.MAX_DISK_TOTAL_MB} ) success cls._try_free_disk_space() if not success: over_flag True if over_flag: print(f[LogHelper FATAL] 清理后磁盘依旧超限暂停持久化写入【{level}】日志, filesys.stderr) return over_flag classmethod def _init(cls) - None: :return: if cls._init_finished: return os.makedirs(cls.LOG_ROOT, exist_okTrue) cls._init_timezone() cls._last_clean_time cls.get_now_datetime() cls._stop_event.clear() cls._writer_thread threading.Thread( targetcls._write_log_loop, nameLogHelper-Writer, daemonTrue ) cls._writer_thread.start() atexit.register(cls._shutdown) cls._init_finished True classmethod def _get_caller_info(cls) - Tuple[str, str, str, int]: :return: try: frame sys._getframe(4) while frame: if frame.f_code.co_filename ! __file__: class_name 未知类 if self in frame.f_locals: class_name frame.f_locals[self].__class__.__name__ elif cls in frame.f_locals: class_name frame.f_locals[cls].__name__ return ( os.path.basename(frame.f_code.co_filename), class_name, frame.f_code.co_name, frame.f_lineno ) frame frame.f_back except Exception: pass return (未知文件, 未知类, 未知方法, 0) classmethod def _build_log_data(cls, level: str, module: str, msg: str, stack_trace: str ) - str | Dict[str, Any]: :param level: :param module: :param msg: :param stack_trace: :return: mod_name cls._safe_module_name(module) # 脱敏回调携带模块名 if cls._desensitize_callback is not None: try: msg cls._desensitize_callback(level, mod_name, msg) except Exception: pass filename, class_name, method_name, line_no cls._get_caller_info() now cls.get_now_datetime() log_time_str now.strftime(cls.TIME_FORMAT_TEMPLATE) timestamp now.timestamp() thread_name threading.current_thread().name if cls.ENABLE_JSON_FORMAT: data { time: log_time_str, timestamp: timestamp, level: level, module: mod_name, pid: cls._pid, thread: thread_name, file: filename, class: class_name, function: method_name, line: line_no, message: msg, stack_trace: stack_trace if stack_trace else None } return data else: text ( f[{log_time_str}] [{level}] [MODULE:{mod_name}] [PID:{cls._pid}] [THREAD:{thread_name}] f[{class_name}.{method_name}·{line_no}行·{filename}] {msg} ) if stack_trace: text f\n{stack_trace} text \n return text classmethod def _enqueue_log(cls, level: str, module: str, msg: str, stack_trace: str ) - None: :param level: :param module: :param msg: :param stack_trace: :return: if not cls._need_sample(level): return try: log_item cls._build_log_data(level, module, msg, stack_trace) # 控制台输出 if cls.ENABLE_CONSOLE_OUTPUT: if isinstance(log_item, dict): console_str json.dumps(log_item, ensure_asciiFalse) else: console_str log_item.rstrip(\n) color cls._COLOR_MAP.get(level, ) reset cls._COLOR_MAP[RESET] print(f{color}{console_str}{reset}) queue_size cls._log_queue.qsize() if queue_size cls.QUEUE_ALERT_THRESHOLD: cls._trigger_alert( QUEUE_BLOCKED, f日志队列拥堵当前队列长度:{queue_size}, {queue_size: queue_size, max_queue: cls.QUEUE_MAXSIZE} ) mod_safe cls._safe_module_name(module) cls._log_queue.put_nowait((level.lower(), mod_safe, log_item)) except queue.Full: print([LogHelper WARN] 日志队列已满丢弃日志, filesys.stderr) except Exception as e: print(f[LogHelper ERROR] 组装日志异常:{e}, filesys.stderr) classmethod def _get_log_path(cls, level: str, module: str) - str: :param level: :param module: :return: mod_dir cls.get_module_log_dir(module) os.makedirs(mod_dir, exist_okTrue) date_str cls.get_now_datetime().strftime(%Y%m%d) return os.path.join(mod_dir, f{level}_{date_str}.log) classmethod def _compress_file(cls, src_file: str): :param src_file: :return: if not os.path.exists(src_file) or not cls.COMPRESS_BAK_LOG: return try: zip_path src_file.replace(.log, .zip) with zipfile.ZipFile(zip_path, w, compressionzipfile.ZIP_DEFLATED) as zf: zf.write(src_file, arcnameos.path.basename(src_file)) os.remove(src_file) except Exception as e: print(f[LogHelper] 日志压缩失败:{src_file}, err:{e}, filesys.stderr) classmethod def _check_and_split(cls, log_path: str) - None: :param log_path: :return: with cls._file_operate_lock: if not os.path.exists(log_path): return try: if os.path.getsize(log_path) cls.MAX_FILE_SIZE_BYTES: base os.path.splitext(log_path)[0] bak_path f{base}_{time.strftime(%H%M%S)}.log os.rename(log_path, bak_path) threading.Thread(targetcls._compress_file, args(bak_path,), daemonTrue).start() except Exception as e: print(f[LogHelper] 文件切割异常:{e}, filesys.stderr) classmethod def _clean_old_logs(cls) - None: :return: now cls.get_now_datetime() expire_time now - datetime.timedelta(dayscls.KEEP_DAYS) with cls._file_operate_lock: if not os.path.exists(cls.LOG_ROOT): return for root, _, files in os.walk(cls.LOG_ROOT): for filename in files: if not (filename.endswith(.log) or filename.endswith(.zip)): continue file_path os.path.join(root, filename) try: mtime datetime.datetime.fromtimestamp(os.path.getmtime(file_path), tzcls._tz_info) if mtime expire_time: os.remove(file_path) except Exception as e: print(f[LogHelper] 清理过期文件失败 {filename}:{e}, filesys.stderr) classmethod def _write_log_loop(cls) - None: :return: # buffer key: (level, module) buffer: Dict[Tuple[str, str], List[Any]] {} while not cls._stop_event.is_set(): try: level, module, log_item cls._log_queue.get(timeout0.8) key (level, module) buffer.setdefault(key, []).append(log_item) if len(buffer[key]) cls.BATCH_WRITE_COUNT: cls._flush_buffer(buffer, key) except queue.Empty: if buffer: cls._flush_buffer(buffer) continue except Exception as e: print(f[LogHelper] 写入循环异常: {e}, filesys.stderr) if buffer: cls._flush_buffer(buffer) classmethod def _flush_buffer(cls, buffer: Dict[Tuple[str, str], List[Any]], target_key: Tuple[str, str] None): :param buffer: :param target_key: :return: target_keys [target_key] if target_key else list(buffer.keys()) for key in target_keys: level, module key msg_list buffer.get(key) if not msg_list: continue try: disk_over cls._check_disk_limit(level) if disk_over: buffer[key].clear() continue log_path cls._get_log_path(level, module) cls._check_and_split(log_path) lines [] for item in msg_list: if isinstance(item, dict): lines.append(json.dumps(item, ensure_asciiFalse) \n) else: lines.append(item) with open(log_path, a, encodingutf-8) as f: f.writelines(lines) f.flush() buffer[key].clear() except Exception as e: print(f[LogHelper] 文件写入失败 level:{level}, module:{module}, err:{e}, filesys.stderr) now cls.get_now_datetime() if now.date() ! cls._last_clean_time.date(): cls._clean_old_logs() cls._last_clean_time now # 对外API【兼容旧代码 新增模块版本】 # 原有接口默认模块 default历史代码无需改动 classmethod def debug(cls, msg: str): :param msg: :return: if cls._log_level.value LogLevel.DEBUG.value: cls._enqueue_log(DEBUG, cls.DEFAULT_MODULE, msg) classmethod def info(cls, msg: str): :param msg: :return: if cls._log_level.value LogLevel.INFO.value: cls._enqueue_log(INFO, cls.DEFAULT_MODULE, msg) classmethod def error(cls, msg: str, exc: Optional[Exception] None): :param msg: :param exc: :return: if cls._log_level.value LogLevel.ERROR.value: return stack_str if exc: stack_str .join(traceback.format_exception(type(exc), exc, exc.__traceback__)) cls._enqueue_log(ERROR, cls.DEFAULT_MODULE, msg, stack_str) # 新增支持指定业务模块的日志接口 classmethod def debug_mod(cls, module: str, msg: str): :param module: :param msg: :return: if cls._log_level.value LogLevel.DEBUG.value: cls._enqueue_log(DEBUG, module, msg) classmethod def info_mod(cls, module: str, msg: str): :param module: :param msg: :return: if cls._log_level.value LogLevel.INFO.value: cls._enqueue_log(INFO, module, msg) classmethod def error_mod(cls, module: str, msg: str, exc: Optional[Exception] None): :param module: :param msg: :param exc: :return: if cls._log_level.value LogLevel.ERROR.value: return stack_str if exc: stack_str .join(traceback.format_exception(type(exc), exc, exc.__traceback__)) cls._enqueue_log(ERROR, module, msg, stack_str) classmethod def _shutdown(cls): :return: cls._stop_event.set() try: cls._log_queue.join() except Exception: pass if cls._writer_thread: cls._writer_thread.join(timeout3.0) # 模块导入自动初始化 LogHelper._init()调用# encoding: utf-8 # 版权所有 2026 ©涂聚文有限公司™ ® # 许可信息查看言語成了邀功盡責的功臣還需要行爲每日來值班嗎 # 描述日志 # Author : geovindu,Geovin Du 涂聚文. # IDE : PyCharm 2024.3.6 python 3.11 # os : windows 10 # database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j # Datetime : 2026/7/31 14:16 # User : geovindu # Product : PyCharm # Project : ictsimple # File : logtest.py from loghelper import LogHelper,LogLevel import time import os if __name__ __main__: # 时区、时间格式动态修改示例 # LogHelper.set_timezone(UTC) # LogHelper.set_time_format(%Y-%m-%d %H:%M:%S.%f) # #TIME_ZONE_NAME Asia/Shanghai #TIME_FORMAT_TEMPLATE %Y-%m-%d %H:%M:%S # 1. 脱敏回调新增参数 module def demo_desensitize(level: str, module: str, msg: str) - str: import re msg re.sub(r1[3-9]\d{9}, lambda m: m.group()[:3] **** m.group()[7:], msg) return msg LogHelper.register_desensitize(demo_desensitize) # 2. 告警回调 def demo_alert(event_type, msg, ext): print(f\n【ALERT】{event_type} | {msg} | {ext}\n) LogHelper.register_alert_callback(demo_alert) # 3. 原有调用方式默认模块 default LogHelper.info(【默认模块】系统启动手机号13800138000) # 4. 【核心新增】按业务模块输出日志 LogHelper.info_mod(user_service, 用户模块注册成功 13900139000) LogHelper.debug_mod(order_service, 订单模块创建订单请求) try: 1 / 0 except Exception as e: LogHelper.error_mod(pay_service, 支付模块异常, e) # 切换JSON格式 LogHelper.switch_json_format(True) LogHelper.info_mod(goods_service, 商品模块查询库存) input(回车退出...)

相关新闻

GPT-5.5 516令牌断崖现象:成因、影响与工程应对策略

GPT-5.5 516令牌断崖现象:成因、影响与工程应对策略

1. 当“聪明”的模型突然变“笨”:GPT-5.5的516令牌断崖现象最近,不少深度使用GPT-5.5模型进行代码生成、复杂逻辑推理或长文本创作的朋友,可能都遇到了一个让人挠头又有点哭笑不得的问题:模型在思考到某个特定长度时,…

2026/9/21 19:40:43 阅读更多 →
LeetCode 17. 电话号码的字母组合

LeetCode 17. 电话号码的字母组合

题目描述给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按任意顺序返回。数字到字母的映射与电话按键相同:2 -> abc 3 -> def 4 -> ghi 5 -> jkl 6 -> mno 7 -> pqrs 8 -> tuv 9 -> wxyz注意&#xff1…

2026/9/20 12:44:21 阅读更多 →
吉他扫弦节奏型训练方法论

吉他扫弦节奏型训练方法论

本文以「问题导向 可操作步骤 练习计划表」结构,拆解吉他右手扫弦节奏型训练。适用于能和弦转换、但扫弦断拍的学习者。1. 问题归因(4 类)1.1 以臂代腕。 手腕未放松甩动,整臂抡动难控。1.2 未稳叠和弦。 右手未固化即加左手&am…

2026/9/14 22:38:31 阅读更多 →

最新新闻

OpenClaw 不走 Ollama/混元,模型通道改到 TaoToken 通道行不行?

OpenClaw 不走 Ollama/混元,模型通道改到 TaoToken 通道行不行?

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/21 19:40:07 阅读更多 →
3步搞定smb共享源码解析 彻底解决环境配置卡壳难题

3步搞定smb共享源码解析 彻底解决环境配置卡壳难题

3步搞定smb共享源码解析 彻底解决环境配置卡壳难题 配置环境就卡半天,是不是你的常态?别急着骂系统,多半是你没看懂底层逻辑。今天不聊虚的,直接上 smb共享 的 源码解析 ,带你从 CPython 和 Samba…

2026/9/21 19:40:07 阅读更多 →
2026最新影音先锋av不撸实战:搞定跨省转介与证书下载

2026最新影音先锋av不撸实战:搞定跨省转介与证书下载

2026最新影音先锋av不撸实战:搞定跨省转介与证书下载 刚学会几行代码,或者刚接触工程数字化流程,是不是脑子一团浆糊?你会写 for…

2026/9/21 19:40:06 阅读更多 →
运动心率算法选型3大坑:新手避坑指南

运动心率算法选型3大坑:新手避坑指南

运动心率算法选型3大坑:新手避坑指南 版本升级后 API 全变了,这是很多团队在集成运动心率监测功能时最头疼的问题。尤其是当你从旧版 SDK…

2026/9/21 19:40:06 阅读更多 →
2026最新报警图标面试题:从语法到项目的避坑指南

2026最新报警图标面试题:从语法到项目的避坑指南

2026最新报警图标面试题:从语法到项目的避坑指南 很多后端或全栈工程师都有过这种崩溃时刻:语法书翻烂了,LeetCode题刷了,但一让搭真实项目,脑子就一片空白。特别是处理像【报警图标】这种看似简单却暗藏玄机的业务组件时,往往因为不懂底层…

2026/9/21 19:40:06 阅读更多 →
5个3GNET高频面试题拆解:告别文档迷宫实战指南

5个3GNET高频面试题拆解:告别文档迷宫实战指南

5个3GNET高频面试题拆解:告别文档迷宫实战指南 官方文档太长抓不住重点?别慌,这恰恰是许多开发者卡在 3GNET 技术栈上的死穴。…

2026/9/21 19:39:06 阅读更多 →

日新闻

agents-generator 决策矩阵全解析:从项目检测到 AGENTS.md 规则生成的 16 步判定流程

agents-generator 决策矩阵全解析:从项目检测到 AGENTS.md 规则生成的 16 步判定流程

agents-generator 决策矩阵全解析:从项目检测到 AGENTS.md 规则生成的 16 步判定流程 【免费下载链接】agentic-awesome-skills AAS Core is the local, agent-first control plane for complete catalog discovery, agent-owned selection, stack validation, and …

2026/9/21 0:00:01 阅读更多 →
gin-vue-admin 前端工具函数全景指南:src/utils 复用规范与源码级解析

gin-vue-admin 前端工具函数全景指南:src/utils 复用规范与源码级解析

gin-vue-admin 前端工具函数全景指南:src/utils 复用规范与源码级解析 【免费下载链接】gin-vue-admin 🚀ViteVue3Gin拥有AI辅助的基础开发平台,企业级业务AI开发解决方案,内置mcp辅助服务,内置skills管理,…

2026/9/21 0:00:01 阅读更多 →
Wox 全功能插件开发实战指南:基于 Python / Node.js 宿主与 WebSocket 的持久化插件体系

Wox 全功能插件开发实战指南:基于 Python / Node.js 宿主与 WebSocket 的持久化插件体系

桌面应用AI 应用插件系统 【免费下载链接】Wox A cross-platform launcher that simply works 项目地址: https://gitcode.com/gh_mirrors/wo/Wox 点击查看 免费下载 全功能插件(Full-featured Plugin)是 Wox 三类插件实现方式中能力最完整的…

2026/9/21 0:00:01 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/21 3:13:20 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/21 2:19:36 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/21 4:51:05 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/21 15:36:51 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/21 15:36:51 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/19 23:35:34 阅读更多 →