【Python工具】Python 实现 telnet、loguru 框架下的 DEBUG 分级日志打印

news2024/11/27 22:39:32

文章目录

    • 1、背景
    • 2、轮子
      • 2.1、telnet
      • 2.2、loguru DEBUG 日志分级

1、背景

最近业务这边需要用 Python 起一个 web 服务器,做 LLM 相关的业务处理。后台选用的是 django 框架做 web 框架,现在也算结项了。初次写 Python,造出来的轮子啥的总结一下:

  • telnet 调试工具台
    • 用来控制日志等级、一些开发调试用的接口。
  • loguru DEBUG 分类日志打印
    • 项目发布肯定不能日志乱打,默认开启的是 INFO 日志,但项目开发阶段内部有类似于 RECV、RESP、TIME 等相关的 debug 日志。如果仅有一个 DEBUG 日志的话,开启后 DEBUG 日志就很多,比如我只想看各个步骤的耗时,而不想看中间的内容。所以,我们可以通过 telnet + DEBUG 分级日志的形式,仅开启 DEBUG_LVL_TIME 等级,就可以了。

2、轮子

2.1、telnet

在这里插入图片描述

import sys
import socket
import threading
import traceback
from typing import Any

from logger.logger import logger

DISPATCH_FLAG = "self-"

class TelnetServer:
    def __init__(self):
        self.commands = {
            "log": {
                "handle": self.set_level,
                "desc": "log [all, time, recv, resp, warning, info, error] : set log level, can use '|' (only debug) combine level",
            },
            "h": {
                "handle": self.show_help,
                "desc": "h : show command help",
            },
            "q": {
                "desc": "q : quit the telnet",
            },
        }

        self.host = "127.0.0.1"
        self.port = None
        self.start_port = 10000
        self.end_port = 10015  # 设定端口范围
        self.max_conns = 5
        self.max_buf_size = 1024
        self.stop_server = False  # 停止服务器标志
        self.serv_thread: threading.Thread = None
        self.serv_socket: socket.socket = None
        self.serv_sock_timeout = 5  # 服务器套接字超时时间,单位:秒
        self.cli_sock_timeout = 300  # 服务器套接字超时时间,单位:秒

    def set_level(self, args, **kwargs: Any):
        if len(args) == 0:
            return "Invalid log level"
        lvls = args[0].split("|")
        debug_level = 0
        debug = False
        general = False
        for lvl in lvls:
            if logger.is_debug(lvl):
                debug = True
                debug_level |= logger.level(lvl)
            else:
                general = True
                logger.set_level(lvl)  # 设置info以下级别别
        if debug and general:
            return "Invalid log level, can't use debug and general level together"
        if debug:
            logger.set_level(debug_level)  # 设置指定debug级别

        return f"log set to level[{lvls}]"

    def show_help(self, args, **kwargs: Any):
        help_msg = "======================command help======================\n\n"
        for cmd, info in self.commands.items():
            help_msg += f"{cmd} ---> {info.get('desc')}\n\n"
        help_msg += "========================================================\n"
        return help_msg

    def handle_command(self, command, args):
        cmd = command
        if command.startswith(DISPATCH_FLAG):
            cmd = command[len(DISPATCH_FLAG):]
        if cmd not in self.commands:
            return f"Unknown command[{command}]"

        response = self.commands[cmd]["handle"](args)  # 仅在有参数时传入

        if not command.startswith(DISPATCH_FLAG):
            self._dispatch_command(command, args)

        return response

    def _get_listening_ports(self, start_port, end_port):
        listening_ports = []
        for port in range(start_port, end_port + 1):
            try:
                sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                sock.bind((self.host, port))
                sock.listen(1)
            except OSError:
                listening_ports.append(port)
            finally:
                sock.close()

        return listening_ports

    def _dispatch_command(self, command, args):
        ports = self._get_listening_ports(self.start_port, self.end_port)
        for dst in ports:
            if dst == self.port or command == "h":
                continue
            try:
                client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                client.connect((self.host, dst))
                client.send(f"self-{command} {' '.join(args)}".encode("utf-8"))
                # response = client.recv(self.max_buf_size).decode("utf-8")
                # client.close()
                # return response
            except OSError as e:
                continue
            except Exception as e:
                logger.warning(f"dispatch command catch an exception : {e}")
                traceback.print_exc()

    def _listen_available_port(self):
        for port in range(self.start_port, self.end_port + 1):
            try:
                s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                s.settimeout(self.serv_sock_timeout)
                s.bind((self.host, port))
                s.listen(self.max_conns)
            except OSError:
                continue
            self.port = port
            self.serv_socket = s
            return

    def start(self):
        self.serv_thread = threading.Thread(target=self.serve_loop)
        self.serv_thread.start()

    def stop(self):
        # 等待服务器线程结束
        self.stop_server = True
        self.serv_thread.join()
        logger.info("telnet server stopped")

    def serve_loop(self):
        self._listen_available_port()
        logger.info(f"telnet server started on {self.host}:{self.port}")

        while not self.stop_server:  # 添加终止服务器标志判断
            client = None
            try:
                client, _ = self.serv_socket.accept()
                client.settimeout(self.cli_sock_timeout)

                while True:
                    s = ">>> "
                    client.send(s.encode("utf-8"))

                    data = client.recv(self.max_buf_size).decode('utf-8').strip()
                    if not data:
                        continue

                    command, *args = data.split(" ")
                    if command == "q":
                        break
                    response = self.handle_command(command, args)
                    response += "\n"

                    if command.startswith(DISPATCH_FLAG):
                        client.close()
                        break
                    client.send(response.encode('utf-8'))

            except socket.timeout:
                pass

            except Exception as e:
                logger.error(f"catch an exception : {e}")
                traceback.print_exc()

            finally:
                if client is not None:
                    client.close()

        if self.serv_socket is not None:
            self.serv_socket.close()  # 关闭服务器套接字


telnetServ: TelnetServer = None


def start_telnet():
    global telnetServ
    if telnetServ is None:
        telnetServ = TelnetServer()
        telnetServ.start()


def stop_telnet():
    logger.info("stop_telnet begin")
    global telnetServ
    telnetServ.stop()
    logger.info("stop_telnet success")

在这里插入图片描述
目前只写了对日志的控制,如果需要一些业务自定义函数也可以很方便添加,在这里我就去除了。

因为我们的业务使用了 Python 多进程,涉及到多个进程的 telnet 通信,所以写了一些这里的操作,如果不需要直接去除即可。

2.2、loguru DEBUG 日志分级

import sys
from typing import Any
from loguru import logger

_lg = logger

DEFAULT_DEPTH = 1

DEBUG_LVL_NONE = 0
DEBUG_LVL_TIME = 0x00000001
DEBUG_LVL_RECV = 0x00000002
DEBUG_LVL_RESP = 0x00000004
DEBUG_LVL_ALL = 0xFFFFFFFF

debug_level = DEBUG_LVL_NONE

_level_map = {
    "none": DEBUG_LVL_NONE,
    "time": DEBUG_LVL_TIME,
    "recv": DEBUG_LVL_RECV,
    "resp": DEBUG_LVL_RESP,
    "all": DEBUG_LVL_ALL,
    "info": "INFO",
    "warning": "WARNING",
    "error": "ERROR",
}


def is_debug(level: Any) -> bool:
    return isinstance(level, int) | isinstance(
        _level_map.get(level, _level_map.get("info")), int
    )


def level(level: str):
    lvl = _level_map.get(level, _level_map.get("info"))
    return _level_map.get(level, _level_map.get("info"))


def init():
    global _lg
    _lg.remove()  # 清除当前所有的日志处理器
    _lg = logger.opt(depth=DEFAULT_DEPTH)  # 设置堆栈深度
    _lg.add(sys.stdout, level="INFO")  # 添加一个新的日志处理器,设置指定级别


def debug(level: int, message: str, *args: Any, **kwargs: Any):
    if debug_level & level == level:
        _lg.debug(message, *args, **kwargs)


def info(message: str, *args: Any, **kwargs: Any):
    _lg.info(message, *args, **kwargs)


def warning(message: str, *args: Any, **kwargs: Any):
    _lg.warning(message, *args, **kwargs)


def error(message: str, *args: Any, **kwargs: Any):
    _lg.error(message, *args, **kwargs)


def set_level(lvl):
    global debug_level
    global _lg
    _lg.remove()  # 清除当前所有的日志处理器
    _lg.opt(depth=DEFAULT_DEPTH)  # 设置堆栈深度
    if is_debug(lvl):  # 检查 lvl 是否为 int 类型
        debug_level = lvl
        _lg.add(sys.stdout, level="DEBUG")
    else:
        _lg.add(sys.stdout, level=level(lvl))

日志等级配合 telnet 使用即可。
需要注意的是,我是封装了原生的 logger.info、warning 等接口,所以堆栈深度是深了一层,需要使用 lg.opt 设置一下堆栈深度,才能显示到打印的行,这个自己调试一下就了解了。

日志 DEBUG 分级也是一个比较常见的,按照 二进制 位进行分级的操作,当时网上搜了搜没看见特别合适的,就直接造个轮子用吧。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

FPGA知识基础之--按键控制LED灯项目

文章目录 前言一、按键简介按键:通过按下或者释放来控制电路通断的电子元件按键原理图 二、实验要求三、程序设计3.1思路整理3.2 模型搭建3.3 顶层模块3.4 波形分析 四、代码整理4.1RTL代码4.2 仿真只需在Testbench上增加上述一段代码即可将参数实例化,可达到在Testbench上更改…

随堂测小程序的设计

管理员账户功能包括:系统首页,个人中心,学生管理,教师管理,试题信息管理,标签类型管理,系统管理 微信端账号功能包括:系统首页,考试成绩,试题信息&#xff0…

RDP Microsoft Remote Desktop 优化指南

RDP优化指南 RDP的优化主要从以下几个方面进行: 帧率设置开启硬件加速(使用显卡)传输协议设置链接用户数量nVidia显卡加速 1. 帧率设置(这里我建议可以不去更改) 我更信任UFO Test的FPS值,有人说改完之后…

15.2 zookeeper java client

15.2 zookeeper java client 1. Zookeeper官方1.1 依赖1.2 Zookeeper客户端连接测试1.3 Zookeeper节点操作1.3.1 zooKeeper.create创建节点1.3.2 zooKeeper.exists获取节点详情1.3.3 zooKeeper.getData获取节点数据1.3.4 zooKeeper.getChildren获取节点下所有子节点名称1.3.5 …

网络地址转换(NAT)

文章目录 NAT的作用NAT的实现方式NAT静态转换工作过程 NAT的作用 局域网与互联网的通信需求 重叠网段合并互通 隐藏内部网络的细节 NAT的实现方式 静态转换 "一对一"固定转换 动态转换 Basic NAT "一对一”动态转换。需要创建公网地址池 eNAPT 通过“IP地址端口…

c#调用python代码,实现读取npy的数据并显示图像

本例子实现的功能是: 根据stat.npy、ops.npy两个npy文件的内容,显示图形 1. 用python代码实现读取两个文件,文件名为read_npy.py,代码如下: import numpy as npdef read_npy_files(stat_file, ops_file):stat np.lo…

sqli-labs(6-10)关通关讲解

sqli-labs(6-10)关通关讲解 Less-6 方法一:手工注入 1.判断闭合 http://localhost/sqli-labs/Less-6/?id1" //报错 http://localhost/sqli-labs/Less-6/?id1" -- //正常 http://localhost/sqli-labs/Less-6/?id1" and 11 -- http://localhos…

YOLOv10环境搭建、训练自己的目标检测数据集、实际验证和测试

1 环境搭建 1.1 在官方仓库的给定的使用python3.9版本,则使用conda创建对应虚拟环境。 conda create -n yolov10 python3.9 1.2 切换到对应虚拟环境 conda activate yolov10 1.3 在指定目录下克隆yolov10官方仓库代码 git clone https://github.com/THU-MIG/yo…

手摸手教你撕碎西门子S7通讯协议10--S7Write写入float数据

1、S7通讯回顾 - (1)建立TCP连接 Socket.Connect-》已实现 - (2)发送访问请求 COTP-》已实现 - (3)交换通信信息 Setup Communication-》已实现 - (4)执行相关操作 …

器件学习——磁珠(2024.07.30)

参考链接1: 【器件篇】-25-磁珠的选型 在此感谢各位前辈大佬的总结,写这个只是为了记录学习大佬资料的过程,内容基本都是搬运的大佬博客,觉着有用自己搞过来自己记一下,如果有大佬觉着我搬过来不好,联系我删。 器件学习…

【MyBatis】史上最全的MyBatis执行SQL原理分析

目录 一、前言 二、简介 三、SQL 执行过程分析 3.1 SQL 执行入口分析 3.1.0 获取SqlSession对象 3.1.1 为 Mapper 接口创建代理对象 3.1.2 执行代理逻辑 3.1.2.1 获取 / 创建 MapperMethod 对象 3.1.2.1.1 创建 SqlCommand 对象 3.1.2.1.2 创建 MethodSignature 对象…

华为OD机试 - Wonderland游乐园 - 动态规划(Java 2024 D卷 200分)

华为OD机试 2024D卷题库疯狂收录中,刷题点这里 专栏导读 本专栏收录于《华为OD机试(JAVA)真题(D卷C卷A卷B卷)》。 刷的越多,抽中的概率越大,每一题都有详细的答题思路、详细的代码注释、3个测…

答应我,在量化策略回测里,避开未来函数这4个坑

由于社群的原因,看过不少策略,今天就腆着脸唠唠,量化新手期经常碰到未来函数的4个坑,希望量化萌新们少掉点儿头发。新手向文章,大神请绕行~ 情景1:使用前复权价格数据。 由于股票会存在分红送股的情形,价格…

芋道源码yudao-cloud 二开笔记(Editor富文本本地图片上传报错问题)

: 于是找到富文本的组件代码Editor.vue,检查一下上传的接口地址和token有没有传,如下图: 都没有问题,但还是报错,所以试试自定义上传的方法: // 导入上传文件的接口 import * as FileApi from …

reese84分析

声明 本文以教学为基准、本文提供的可操作性不得用于任何商业用途和违法违规场景。 本人对任何原因在使用本人中提供的代码和策略时可能对用户自己或他人造成的任何形式的损失和伤害不承担责任。 如有侵权,请联系我进行删除。 这里只是我分析的分析过程,以及一些重要点的记录…

最近火爆的GraphRAG是什么?真的那么有用吗?

最近,微软提出的GraphRAG项目引起了广泛关注。那么,GraphRAG究竟是什么?它真的那么实用吗?本文将为您详细解读GraphRAG的概念及其应用。 什么是传统的RAG? 📚 在深入了解GraphRAG之前,我们首先…

掌握AJAX技术:从基础到实战

文章目录 **引言****1. 什么是AJAX?****2. AJAX的工作原理**AJAX 示例使用 Fetch API 实现 AJAX **3. 如何在项目中使用AJAX****4. 处理AJAX请求的常见问题****5. AJAX与JSON的结合****6. 使用AJAX框架和库****7. 实战:创建一个动态表单****8. AJAX中的事…

Python 解决 ImportError: cannot import name ‘example’

Python 解决 ImportError: cannot import name ‘example’ 在Python编程的广阔天地中,ImportError: cannot import name example 是一个令人头疼但又常见的错误。当你试图从某个模块中导入一个不存在的名称时,这个错误就会悄然降临。本文将带你深入探索…

AI推理硬件成本分析:AMD Instinct MI300X与Nvidia GPU比较

随着AI模型训练成本的上升,人们越来越关注推理硬件的成本,尤其是在需要低延迟响应的应用中。Transformer模型需要强大的硬件支持,例如200毫秒以下的响应时间。Artificial Analysis最近分析了AI模型性能和定价,特别指出AMD的“Anta…

「豆包Marscode体验官」AI加持的云端IDE——三种方法高效开发前后端聊天交互功能

豆包 MarsCode 是一个集成了AI功能的编程助手和云端IDE,旨在提高开发效率和质量。它支持多种编程语言和IDE,提供智能代码补全、代码解释、单元测试生成和问题修复等功能,同时具备AI对话视图和开发工具。 豆包 MarsCode 豆包 MarsCode 编程助…