class SendEmail(object): """发送测试报告""" _instance_lock = threading.Lock() # 设置单例锁 def __new__(cls, *args, **kwargs): """单例模式(支持多线程)""" if not hasattr(cls, "_instance"): with cls._instance_lock: if not hasattr(cls, "_instance"): cls._instance = object.__new__(cls) return cls._instance def __init__(self): try: self.common = Common() # 实例化一个common调用公用函数 self.log = MyLog().get_log().logger # 实例化一个log打印日志 self.config = ReadConfig() # 实例化一个read_config读取email的配置信息 self.msg = email.mime.multipart.MIMEMultipart( 'alternative') # 实例化一个email发送email self.log_dir = self.common.get_result_path() # 获取存储日志的时间目录 self.principal_name_list = [] # 错误用例负责人list self.zip_path = self.log_dir + ".zip" # 设置存放zip的路径 self.result = False # 发送结果标志 self.num = 0 # 发送失败后重试次数 except Exception as e: self.log.error(e) raise Exception("SendEmail.__init__异常!") def with_zip(self): """附件以zip格式发送邮件""" while self.result is False and self.num < 3: # 发送失败后重试3次 try: # 提取错误用例负责人姓名 file_list = os.listdir(self.log_dir) # 获取时间目录下的文件列表 for file in file_list: file_name = os.path.splitext(file)[0] # 文件名 # 用正则表达式查找文件名为汉字的文件(负责人对应的错误日志文件),正则表达式为:非汉字的字符用""替换掉 if file_name == re.sub("[^\u4e00-\u9fa5]+", "", file_name): self.principal_name_list.append( file_name) # 添加负责人姓名到principal_name_list中 # 创建一个写入的zip对象 with zipfile.ZipFile( self.zip_path, mode='w', compression=zipfile.ZIP_DEFLATED) as zip_obj: for path, folders, files in os.walk(self.log_dir): for file in files: zip_obj.write(os.path.join(path, file)) # 将内容写入zip # 添加附件 part = MIMEApplication(open(self.zip_path, 'rb').read()) # 读取内容 part.add_header('Content-Disposition', 'attachment', filename=('gbk', '', "result.zip")) # 设置附件名 self.msg.attach(part) self.send() # 发送邮件 self.result = True except Exception as e: self.log.error("发送失败 %s" % e) self.num += 1 finally: os.remove(self.zip_path) # 删除zip文件 self.remove_result() # 删除之前的结果文件夹 def with_file(self): """附件以单个文件形式发送邮件""" while self.result is False and self.num < 3: # 发送失败后重试3次 try: file_list = os.listdir(self.log_dir) # 获取时间目录下的文件列表 for file in file_list: file_name = os.path.splitext(file)[0] # 文件名 file_type = os.path.splitext(file)[1] # 文件类型 # 用正则表达式查找文件名为汉字的文件(负责人对应的错误日志文件),正则表达式为:非汉字的字符用""替换掉 if file_name == re.sub("[^\u4e00-\u9fa5]+", "", file_name): self.principal_name_list.append( file_name) # 添加负责人姓名到错误负责人list中 current_file = os.path.join(self.log_dir, file) # 拼接当前的日志路径 part = MIMEApplication( open(current_file, 'rb').read()) # 读取当前的日志 part.add_header('Content-Disposition', 'attachment', filename=('gbk', '', file)) # 设置附件名 self.msg.attach(part) elif file_type == ".html": # 查找html文件 current_file = os.path.join(self.log_dir, file) # 拼接当前的日志路径 part = MIMEApplication( open(current_file, 'rb').read()) # 读取当前的日志 part.add_header('Content-Disposition', 'attachment', filename=('gbk', '', file)) # 设置附件名 self.msg.attach(part) elif "error" in file_name: # 查找错误日志文件 current_file = os.path.join(self.log_dir, file) # 拼接当前的日志路径 part = MIMEApplication( open(current_file, 'rb').read()) # 读取当前的日志 part.add_header('Content-Disposition', 'attachment', filename=('gbk', '', file)) # 设置附件名 self.msg.attach(part) self.send() # 发送邮件 self.result = True except Exception as e: self.log.error("发送失败 %s" % e) self.num += 1 finally: self.remove_result() # 删除之前的结果文件夹 def send(self): """发送邮件""" try: # 从配置文件中读取发件人信息 sender_name = "" # 发件人 sender_email = "" # 发件箱 sender_dict = json.loads(self.config.get_email("sender")) for key, value in sender_dict.items(): sender_name = key # 发件人 sender_email = value # 发件箱 # 从配置文件中读取收件人信息 # receivers内容为字典时使用(receivers = {"蓝梦":"*****@*****.**", "孟冰":"*****@*****.**") receivers_dict = json.loads(self.config.get_email("receivers")) name_list = [] # 收件人list receivers = [] # 收件箱list for key, value in receivers_dict.items(): if key in self.principal_name_list: name_list.append(key) receivers.append(value) # 邮件信息 name_list_str = ",".join(name_list) # 收件人姓名,将list转换为str mail_host = self.config.get_email("email_host") # 设置邮箱服务器域名 mail_port = self.config.get_email("email_port") # 设置邮箱服务器接口 mail_user = self.config.get_email("email_user") # 发件人用户名 mail_pass = self.config.get_email("email_pass") # 发件人口令 subject = self.config.get_email("subject") # 主题 content = self.config.get_email("content") # 正文 if len(name_list_str) == 0: self.log.debug("所有用例都正常通过!") else: self.log.debug("发件人:%s" % sender_name) self.log.debug("收件人:%s" % name_list_str) txt = email.mime.text.MIMEText(content, 'plain', 'utf-8') self.msg.attach(txt) self.msg['Subject'] = Header(subject, 'utf-8') self.msg['From'] = Header(sender_name, 'utf-8') self.msg['To'] = Header("%s" % name_list_str, 'utf-8') # 调用邮箱服务器 smt_obj = smtplib.SMTP_SSL(mail_host, mail_port) # 登录邮箱 smt_obj.login(mail_user, mail_pass) # 发送邮件 smt_obj.sendmail(sender_email, receivers, self.msg.as_string()) # 关闭邮箱 smt_obj.quit() self.log.debug("发送成功!") except Exception as e: self.log.error(e) raise Exception("发送email时异常!") def remove_result(self): """发送报告后删除其他的文件夹""" try: result_path = os.path.dirname(self.log_dir) # 获取result目录路径 result_list = os.listdir(result_path) # 获取result下的文夹列表 i = len(result_list) # 统计文件夹数量 for file in result_list: path = os.path.join(result_path, file) # 拼接每个文件夹的路径 if i > 1: # 保留最新的文件夹 shutil.rmtree(path) i -= 1 except Exception as e: self.log.error(e) raise Exception("删除result下文件夹时异常!")
def extraction_error_log(self): """按负责人提取错误日志""" try: log_path = self.common.get_result_path("result.log") # 生成原始日志文件路径 error_log_path = self.common.get_result_path( "error.log") # 生成错误日志文件路径 # 从邮件的收件人信息中读取所有的负责人姓名,实现错误日志按人分类 config = ReadConfig() receivers = config.get_email("receivers") # 收件人列表str类型(姓名,邮箱) receivers_dict = json.loads(receivers) # 收件人列表dict类型(姓名,邮箱) principal_list = [] # 收件人姓名列表 for key, value in receivers_dict.items(): principal_list.append(key) data = "" # 单个错误日志临时存储器 error = False # 错误日志标识 all_error_num = 0 # 错误用例总的编号 # principal_error_num = 0 # 负责人的错误编号 i = 1 # 原始日志中的文本行数 j = 1 # 单个用例内的行数 with open(log_path, "r", encoding="utf-8") as log: # 以read方式打开原始日志 with open(error_log_path, "w", encoding="utf-8") as error_log: # 以write方式打开错误日志 lines = log.readlines() # 读取原始日志 principal = "" # 用例负责人 for line in lines: # 匹配负责人姓名,以便是错误日志时创建负责人对应的错误日志文件名,负责人姓名在单个用例的第一行 if j == 1: for name in principal_list: if name in line: principal = name data = data + line # 临时存储一个用例的日志 if "failed!" in line: # line中包含"ERROR"的标记为错误日志 error = True # "*"不可改,出现"*"表示一个用例结束,一个用例结束并被标记为错误日志的内容被写入error_log和principal_log文件 if "*" * 100 in line and error is True: all_error_num += 1 error_log.write("\n错误用例%s:" % str(all_error_num)) error_log.write(data) # 所有错误日志写入一个文件中 # 将错误日志提取到对应负责人的日志文件中 principal_log_name = "%s.log" % principal # 负责人对应的错误日志名 principal_log_path = self.common.get_result_path( principal_log_name) # 生成负责人对应的错误日志路径 # 按负责人分类写入对应的文件中 if os.path.exists( principal_log_path ): # 判断principal_log_path是否存在,存在时添加,不存在时创建 with open(principal_log_path, "a+", encoding="utf-8") as principal_log: principal_log.write(data) else: with open(principal_log_path, "w+", encoding="utf-8") as principal_log: principal_log.write(data) # 有错误日志时恢复初始化 data = "" error = False j = 1 # 没有错误日志时恢复初始化 elif "*" * 100 in line: # "*"不可改 data = "" error = False j = 1 i += 1 if os.path.exists(error_log_path) and os.path.getsize( error_log_path) == 0: # 如果没有错误日志,就删除空的error_log文件 os.remove(error_log_path) except Exception as e: raise Exception("MyLog.extraction_error_log异常 %s" % e)