异常抓拍与追踪 ---- By.广州组
评论
收藏

异常抓拍与追踪 ---- By.广州组

经验分享
驿站
2023-06-26 09:52·浏览量:1589
驿站
发布于 2023-06-26 09:521589浏览

一、问题背景

场景一:对于数据量比较大的流程,单条数据异常通常会跳过,继续执行下一条数据

如果使用屏幕录制一直录制,视频就会很大,找到对应异常的视频片段也比较麻烦

场景二:对于指令中的异常

显示的异常信息会比较少,看不到指令中发生异常的具体位置信息


二、快速开始

1.新建python模块:err_hanlder.py

2.全部复制拷贝下面的代码到 err_hanlder.py


# 使用提醒:
# 1. xbot包提供软件自动化、数据表格、Excel、日志、AI等功能
# 2. package包提供访问当前应用数据的功能,如获取元素、访问全局变量、获取资源文件等功能
# 3. 当此模块作为流程独立运行时执行main函数
# 4. 可视化流程中可以通过"调用模块"的指令使用此模块

import xbot
import xbot_visual
from xbot import print, sleep
from .import package
from .package import variables as glv

from xbot.win32.screenshot import save_screen_to_file


import os
import re
import json
import datetime
import traceback

from os.path import exists, join
from os import mkdir

def get_xbot_name():
    """
    获取应用名称,主应用名称,非指令
    """
    dir_path = re.search(r'''.+\\apps\\[^\\]+\\(?=xbot_extensions|xbot_robot)''', __file__).group()
    with open(join(dir_path, "xbot_robot", "package.json"), "rb") as f:
        load_dirt = json.load(f)
        xbot_name = load_dirt['name'] 
    return xbot_name


def rename_if_exists(filename):
    basename, ext = os.path.splitext(filename)
    i = 0
    while os.path.isfile(filename):
        i += 1
        filename = f"{basename}({i}){ext}"
    return filename



def exception_screenshot(save_path):
    # 获取主应用名称
    xbot_name = get_xbot_name()

    # 拼接文件夹
    now = datetime.datetime.now()
    date_string = now.strftime("%Y%m%d%H%M%S")
    dir_path = join(save_path, xbot_name, date_string)
    
    def outer(func):  
        def wrapper(*args, **kwargs):
            nonlocal xbot_name
            nonlocal dir_path
            
            # 提取异常信息
            error_msg = re.split(r'(?=File ".+\.py", line \d+, in )', traceback.format_exc())[-1]
            filepath = re.findall(r'File "(.+\.py)", line \d+, in ', error_msg)[0]
            
            # 获取指令所在的流程和行号
            result = re.findall(r'_block=\("(.+)", (\d+),.+\)', error_msg)
 
            if len(result) > 0:
                # 创建保存文件夹
                xbot_visual.dir.makedir(parent=os.path.dirname(dir_path), name=os.path.basename(dir_path))  
                log_path = join(dir_path, f"log.txt")   
                error_app_name = None       
                name, line = result[0]
                error_msg = '【{}】中第【{}】条指令出错:{}'.format(name, line, args[0])   

                if not os.path.dirname(filepath).endswith("\\xbot_robot"):
                    # 读取指令名称
                    with open(join(os.path.dirname(filepath), "package.json"), "rb") as f:
                        json_package = json.load(f)
                        error_app_name = json_package['name']   
          

                if error_app_name:
                    error_msg = '指令【{}】出现异常 - 【{}】中第【{}】条指令出错:{}'.format(error_app_name, name, line, args[0])
                xbot_visual.programing.log(type="info", text=error_msg)
                img_path = join(dir_path, f"{date_string}.png")
                img_path = rename_if_exists(img_path)   # 防止重名
                
                with open(log_path, "a", encoding="u8") as f:
                    f.write(f'异常截图:{os.path.basename(img_path)} - {error_msg}\n')


                save_screen_to_file(img_path, "png")     
            res = func(*args, **kwargs)
            return res
        return wrapper
    return outer


row_trace = xbot_visual.trace

def exception_screenshot_api(save_path):
    if row_trace == xbot_visual.trace:
        # 单例模式,确保函数只被装饰一次
        xbot_visual.trace = exception_screenshot(save_path)(row_trace)

def main(args):
    pass


3.在主流程第一行使用指令【调用模块】,传入图片保存文件夹。如下:


三、实现效果

1.在【普普通通的指令】中写一个异常代码,然后运行,只能看到调用指令的流程和行号信息,看不到指令中具体报错位置

2.在【普普通通的指令】中写一个异常代码,在【当前应用流程】写一个异常代码。运行后如下图日志所示,可以清楚的知道指令中的报错位置和当前流程中的报错位置

在刚刚传入的文件夹中也可以找到具体的异常截图和异常信息

四、实现原理

1.当使用try...catch时,源码中会多出一行代码:exception = xbot_visual.trace(exception)


2.前后打印日志,可以看到这个函数的作用是寻找异常栈信息。但是如果在指令中,这个功能就会失效


3.利用装饰器替换这个函数,就可以在每次异常捕获时自定义实现自己想要的功能。对于主动抛出的异常和设置变量发生的异常则不会触发截图功能。使用python模块traceback获取异常信息如下:

Traceback (most recent call last):
  File "C:\Users\yd\AppData\Local\ShadowBot\users\59882836586950656\apps\65514a74-7c96-4646-b54c-42cbcca1f497\xbot_robot\main.py", line 16, in main
    ], _block=("main", 3, "普普通通的指令"))
  File "<string>", line 89, in wrapper
  File "<string>", line 84, in wrapper
  File "<string>", line 82, in run
  File "C:\Users\yd\AppData\Local\ShadowBot\users\59882836586950656\apps\65514a74-7c96-4646-b54c-42cbcca1f497\xbot_extensions\activity_1cb63042\main.py", line 9, in main
    xbot_visual.programing.log(type="info", text=lambda: 姓名, _block=("main", 1, "打印日志"))
  File "<string>", line 89, in wrapper
  File "<string>", line 81, in wrapper
  File "C:\Users\yd\AppData\Local\ShadowBot\users\59882836586950656\apps\65514a74-7c96-4646-b54c-42cbcca1f497\xbot_extensions\activity_1cb63042\main.py", line 9, in <lambda>
    xbot_visual.programing.log(type="info", text=lambda: 姓名, _block=("main", 1, "打印日志"))
NameError: name '姓名' is not defined

再利用正则提取最后一个py文件下边的_block即可

收藏5
全部评论1
最新
发布评论
评论