【大爆炸】log 模块

# -*- coding: utf-8 -*-
"""
-------------------------------------------------
   File Name:     hcs-log
   Description :   log 模块
   Author :       jiaoyaxiong
   date:          2019/7/22
-------------------------------------------------
   Change Activity:
                   2019/7/22:
-------------------------------------------------
"""
__author__ = 'jiaoyaxiong'

import logging
import time
import os
import yaml
import conf
import functools


"""
从环境变量中获取如下信息:

jenkins 启动时注入:
testEnv  = daily|pre|online
jenBuild =
jenJobName =
needUpdate = yes|no

从代码路径获取信息:
codeCID =
codeBName = 

需要大的组件:
jenkins (docker 启动时开启jenkins )
git (需要内网访问支持)
python
...

log 存放:---亦可以上传至sls(待验证是否支持通配符模式)
本地需要存放一份----allure 附件上传的方式推送到 allure report 
每个用例跑的时候生成一份,附送到allure 


"""

"""
在jenkins 构建脚本中注入环境变量
#注入环境变量
export testEnv=daily
export jenBuild=${BUILD_ID}
export jenJobName=${JOB_NAME}
export needUpdate=no

jenkins 启动测试进程可以获取到环境变量 os.environ.get()
logging 日志可以传入多余的格式
            file_formatter=logging.Formatter('[%(asctime)s][%(filename)s:%(lineno)d][%(levelname)s][%(testEnv)s][%(jenBuild)s][%(jenJobName)s][%(codeCID)s][%(codeBName)s][%(casename)s][%(message)s]')


def getEnv():
    testEnv=os.environ.get("testEnv","null-testEnv")
    jenBuild=os.environ.get("jenBuild","null-jenBuild")
    jenJobName=os.environ.get("jenJobName","null-jenJobName")
    needUpdate=os.environ.get("needUpdate","no")
    codeCID=os.environ.get("codeCID","null-codeCID")
    codeBName=os.environ.get("codeBName","null-codeBName")
    return {"testEnv":testEnv,
            "jenBuild":jenBuild,
            "jenJobName":jenJobName,
            "needUpdate":needUpdate,
            "codeCID":codeCID,
            "codeBName":codeBName
            }

self.logger.info=functools.partial(self.logger.info,extra=results_env)

"""

def getEnv():
    testEnv=os.environ.get("testEnv","null-testEnv")
    jenBuild=os.environ.get("jenBuild","null-jenBuild")
    jenJobName=os.environ.get("jenJobName","null-jenJobName")
    needUpdate=os.environ.get("needUpdate","no")
    codeCID=os.environ.get("codeCID","null-codeCID")
    codeBName=os.environ.get("codeBName","null-codeBName")
    return {"testEnv":testEnv,
            "jenBuild":jenBuild,
            "jenJobName":jenJobName,
            "needUpdate":needUpdate,
            "codeCID":codeCID,
            "codeBName":codeBName
            }


# os.environ 设置的是临时环境变量,只能在一次运行中使用
# 这个环境变量只在进程和子进程中可以获取到
# 这里所谓的修改环境变量,仅仅影响当前python进程后续启动的子进程,对当前python进程以外的环境是没有任何影响的。
# https://www.zhihu.com/question/55937152



# linux source

    # source命令(从 C Shell 而来)是bash shell的内置命令. 点命令,就是一个点符号,是source的另一名称。这两个命令都以一个脚本为参数,
    # 该脚本将在当前shell的环境执行,即不会启动一个新的子shell。所有在脚本中设置的变量都将成为当前Shell的一部分。
    # 不会启动子shell, 在source filename 与 sh filename 及. / filename执行脚本的区别  #
    # 当shell脚本具有可执行权限时,用sh
    # filename与. / filename执行脚本是没有区别得。./ filename是因为当前目录没有在PATH中,所有”.”是用来表示当前目录的。
    # sh
    # filename
    # 重新建立一个子shell,在子shell中执行脚本里面的语句,该子shell继承父shell的环境变量,但子shell新建的、改变的变量不会被带回父shell,除非使用export。
    # source
    # filename:这个命令其实只是简单地读取脚本里面的语句依次在当前shell里面执行,没有建立新的子shell。那么脚本里面所有新建、改变变量的语句都会保存在当前shell里面。

    # 在shell中执行程序时,shell会提供一组环境变量。 export可新增,修改或删除环境变量,供后续执行的程序使用。export的效力仅及于该此登陆操作

    # 一个变量创建时,它不会自动地为在它之后创建的shell进程所知。而命令export可以向后面的shell传递变量的值。当一个shell脚本调用并执行时,它不会自动得到原为脚本(调用者)里定义的变量的访问权,除非这些变量已经被显式地设置为可用。export命令可以用于传递一个或多个变量的值到任何后继脚本。

    # 要想永久生效,需要把这行添加到环境变量文件里。有两个文件可选:“/etc/profile”和用户主目录下的“.bash_profile”,“/etc/profile”对系统里所有用户都有效

    # 登陆系统才会执行/etc/profile (login 初始化环境变量等   登陆shell  非登陆shell ),不登陆系统就会执行 /etc/rc.d/rc.local (开机自动启动)

    # https://www.cnblogs.com/syavingcs/p/14058134.html
    #   设置环境变量的脚本,可以放在profile.d目录下面,但开机执行任务不应该放在profile.d目录下,因为每次登陆都会执行profile.d目录下的文件,会导致重复执行,
    # 设置开机启动任务,应该放在rc.local中执行,它只会在系统启动时执行一次。
    # Linux登录shell和非登录(交互式shell)环境变量配置 https://www.cnblogs.com/woshimrf/p/shell-conf.html

    # 单用户模式进行救援

# subprocess multiprocessing
    # subprocess is 用来执行其他的可执行程序的,即执行外部命令。 他是os.fork() 和 os.execve() 的封装。 他启动的进程不会把父进程的模块加载一遍。使用subprocess的通信机制比较少,通过管道或者信号机制.

    # subprocess.run 功能,执行args参数所表示的命令,等待命令结束,返回一个CompletedProcess类型对象
    # subprocess.Popen()  用法和参数run()方法基本相同,但是他的返回值是一个Popen对象,而不是CompletedProcess对象  异步执行
    # multiprocessing 用来执行python的函数,他启动的进程会重新加载父进程的代码。可以通过Queue、Array、Value等对象来通信。
    # from multiprocessing import Process   p1 = Process(target=run_proc1,args=(‘test’,))

    # 1 p.daemon:默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置


class hcs_logger():
    def __init__(self,casename):
        base_log_path=conf.get_conf()["global"]["base_log_path"]
        self.logger = logging.getLogger(casename)
        self.logger.setLevel(level = logging.INFO)
        file_formate=base_log_path+os.sep+time.strftime('%Y%m%d',time.localtime(time.time()))
        try:
            if not os.path.exists(file_formate):
                os.makedirs(file_formate)
        except:
            time.sleep(1)
            pass
        timestamp=time.strftime('%H%M%S',time.localtime(time.time()))
        handler = logging.FileHandler("%s//%s%s.txt"%(file_formate,str(casename),timestamp),encoding="utf-8")
        handler.setLevel(logging.DEBUG)
        formatter = logging.Formatter('%(asctime)s - %(filename)s:%(lineno)d - %(levelname)s - %(message)s')
        #写复杂点,给sls 分析log 使用
        file_formatter=logging.Formatter('[%(asctime)s][%(filename)s:%(lineno)d][%(levelname)s][%(testEnv)s][%(jenBuild)s][%(jenJobName)s][%(codeCID)s][%(codeBName)s][%(casename)s][%(message)s]')

        handler.setFormatter(file_formatter)



        console = logging.StreamHandler()
        console.setLevel(logging.DEBUG)
        console.setFormatter(formatter)
        # 给上传allure 附件使用
        self.txt_log_path="%s//%s%s.txt" % ( file_formate,str(casename),timestamp)
        self.logger.addHandler(handler)
        self.logger.addHandler(console)
        results_env=getEnv()
        results_env["casename"]=casename
        #传点参数
        """
        从名字可以看出,该函数的作用就是部分使用某个函数,即冻结住某个函数的某些参数,让它们保证为某个值,并生成一个可调用的新函数对象,这样你就能够直接调用该新对象,并且仅用使用很少的参数
        functools.partial 偏函数的使用
        
        多余参数:
        import logging

        FORMAT = '%(asctime)s %(clientip)s %(user)-8s %(message)s %(haiyoushui)s'
        
        logging.basicConfig(format=FORMAT)
        
        d={'clientip':'192.168.0.1','user':'fbloggs', "haiyoushui":"12312313113213"}
        
        logger=logging.getLogger('tcpserver')
        
        logger.warning('Protocolproblem:%s','connectionreset',extra=d)
        
        extra是用户自定义的dict. 这些key/value在格式化的时候可以直接引用。


        用Python的logging模块记录日志时,遇到了重复记录日志的问题,第一条记录写一次,第二条记录写两次,第三条记录写三次。。。很头疼,这样记日志可不行。网上搜索到了原因与解决方案:
        原因:没有移除handler 
        解决:在日志记录完之后removeHandler
            logger.removeHandler(streamhandler)

        所以这里有以下几个解决办法:

            每次创建不同name的logger,每次都是新logger,不会有添加多个handler的问题。(ps:这个办法太笨,不过我之前就是这么干的。。)
            像上面一样每次记录完日志之后,调用removeHandler()把这个logger里的handler移除掉。
            在log方法里做判断,如果这个logger已有handler,则不再添加handler。
            与方法2一样,不过把用pop把logger的handler列表中的handler移除。
        
        """

        self.logger.info=functools.partial(self.logger.info,extra=results_env)
        self.logger.error=functools.partial(self.logger.error,extra=results_env)
        self.logger.debug=functools.partial(self.logger.debug,extra=results_env)
        self.logger.warning=functools.partial(self.logger.warning,extra=results_env)

    def __call__(self, *args, **kwargs):
        return self.logger

    # __call__  使得类实例对象可以像调用普通函数那样,以“对象名()”的形式使用

if __name__ == "__main__":
    logging1=hcs_logger("casename123")()
    # logging1.info("123",extra=getEnv())
    logging1.info("123")
    logging1.error("321")
    logging1.debug("12553")
    logging1.warning("124443")
    # DEBUG 有问题!
    logging1.debug("12553")
    file_name = os.path.split(os.path.abspath(__file__))[-1]
    logging1.info(file_name.replace(".py",""))

  

# -*- coding: utf-8 -*-"""-------------------------------------------------   File Name:     hcs-log   Description :   log 模块   Author :       wb-jyx515728   date:          2019/7/22-------------------------------------------------   Change Activity:                   2019/7/22:-------------------------------------------------"""__author__ = 'wb-jyx515728'
import loggingimport timeimport osimport yamlimport confimport functools

"""从环境变量中获取如下信息:
jenkins 启动时注入:testEnv  = daily|pre|onlinejenBuild =jenJobName =needUpdate = yes|no
从代码路径获取信息:codeCID =codeBName = 
需要大的组件:jenkins (docker 启动时开启jenkins )git (需要内网访问支持)python...
log 存放:---亦可以上传至sls(待验证是否支持通配符模式)本地需要存放一份----allure 附件上传的方式推送到 allure report 每个用例跑的时候生成一份,附送到allure 

"""
"""在jenkins 构建脚本中注入环境变量#注入环境变量export testEnv=dailyexport jenBuild=${BUILD_ID}export jenJobName=${JOB_NAME}export needUpdate=no
jenkins 启动测试进程可以获取到环境变量 os.environ.get()logging 日志可以传入多余的格式            file_formatter=logging.Formatter('[%(asctime)s][%(filename)s:%(lineno)d][%(levelname)s][%(testEnv)s][%(jenBuild)s][%(jenJobName)s][%(codeCID)s][%(codeBName)s][%(casename)s][%(message)s]')

def getEnv():    testEnv=os.environ.get("testEnv","null-testEnv")    jenBuild=os.environ.get("jenBuild","null-jenBuild")    jenJobName=os.environ.get("jenJobName","null-jenJobName")    needUpdate=os.environ.get("needUpdate","no")    codeCID=os.environ.get("codeCID","null-codeCID")    codeBName=os.environ.get("codeBName","null-codeBName")    return {"testEnv":testEnv,            "jenBuild":jenBuild,            "jenJobName":jenJobName,            "needUpdate":needUpdate,            "codeCID":codeCID,            "codeBName":codeBName            }
self.logger.info=functools.partial(self.logger.info,extra=results_env)
"""
def getEnv():    testEnv=os.environ.get("testEnv","null-testEnv")    jenBuild=os.environ.get("jenBuild","null-jenBuild")    jenJobName=os.environ.get("jenJobName","null-jenJobName")    needUpdate=os.environ.get("needUpdate","no")    codeCID=os.environ.get("codeCID","null-codeCID")    codeBName=os.environ.get("codeBName","null-codeBName")    return {"testEnv":testEnv,            "jenBuild":jenBuild,            "jenJobName":jenJobName,            "needUpdate":needUpdate,            "codeCID":codeCID,            "codeBName":codeBName            }

# os.environ 设置的是临时环境变量,只能在一次运行中使用# 这个环境变量只在进程和子进程中可以获取到# 这里所谓的修改环境变量,仅仅影响当前python进程后续启动的子进程,对当前python进程以外的环境是没有任何影响的。# https://www.zhihu.com/question/55937152


# linux source
    # source命令(从 C Shell 而来)是bash shell的内置命令. 点命令,就是一个点符号,是source的另一名称。这两个命令都以一个脚本为参数,    # 该脚本将在当前shell的环境执行,即不会启动一个新的子shell。所有在脚本中设置的变量都将成为当前Shell的一部分。    # 不会启动子shell, 在source filename 与 sh filename 及. / filename执行脚本的区别  #    # 当shell脚本具有可执行权限时,用sh    # filename与. / filename执行脚本是没有区别得。./ filename是因为当前目录没有在PATH中,所有”.”是用来表示当前目录的。    # sh    # filename    # 重新建立一个子shell,在子shell中执行脚本里面的语句,该子shell继承父shell的环境变量,但子shell新建的、改变的变量不会被带回父shell,除非使用export。    # source    # filename:这个命令其实只是简单地读取脚本里面的语句依次在当前shell里面执行,没有建立新的子shell。那么脚本里面所有新建、改变变量的语句都会保存在当前shell里面。
    # 在shell中执行程序时,shell会提供一组环境变量。 export可新增,修改或删除环境变量,供后续执行的程序使用。export的效力仅及于该此登陆操作
    # 一个变量创建时,它不会自动地为在它之后创建的shell进程所知。而命令export可以向后面的shell传递变量的值。当一个shell脚本调用并执行时,它不会自动得到原为脚本(调用者)里定义的变量的访问权,除非这些变量已经被显式地设置为可用。export命令可以用于传递一个或多个变量的值到任何后继脚本。
    # 要想永久生效,需要把这行添加到环境变量文件里。有两个文件可选:“/etc/profile”和用户主目录下的“.bash_profile”,“/etc/profile”对系统里所有用户都有效
    # 登陆系统才会执行/etc/profile (login 初始化环境变量等   登陆shell  非登陆shell ),不登陆系统就会执行 /etc/rc.d/rc.local (开机自动启动)
    # https://www.cnblogs.com/syavingcs/p/14058134.html    #   设置环境变量的脚本,可以放在profile.d目录下面,但开机执行任务不应该放在profile.d目录下,因为每次登陆都会执行profile.d目录下的文件,会导致重复执行,    # 设置开机启动任务,应该放在rc.local中执行,它只会在系统启动时执行一次。    # Linux登录shell和非登录(交互式shell)环境变量配置 https://www.cnblogs.com/woshimrf/p/shell-conf.html
    # 单用户模式进行救援
# subprocess multiprocessing    # subprocess is 用来执行其他的可执行程序的,即执行外部命令。 他是os.fork() 和 os.execve() 的封装。 他启动的进程不会把父进程的模块加载一遍。使用subprocess的通信机制比较少,通过管道或者信号机制.
    # subprocess.run 功能,执行args参数所表示的命令,等待命令结束,返回一个CompletedProcess类型对象    # subprocess.Popen()  用法和参数run()方法基本相同,但是他的返回值是一个Popen对象,而不是CompletedProcess对象  异步执行    # multiprocessing 用来执行python的函数,他启动的进程会重新加载父进程的代码。可以通过Queue、Array、Value等对象来通信。    # from multiprocessing import Process   p1 = Process(target=run_proc1,args=(‘test’,))
    # 1 p.daemon:默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置

class hcs_logger():    def __init__(self,casename):        base_log_path=conf.get_conf()["global"]["base_log_path"]        self.logger = logging.getLogger(casename)        self.logger.setLevel(level = logging.INFO)        file_formate=base_log_path+os.sep+time.strftime('%Y%m%d',time.localtime(time.time()))        try:            if not os.path.exists(file_formate):                os.makedirs(file_formate)        except:            time.sleep(1)            pass        timestamp=time.strftime('%H%M%S',time.localtime(time.time()))        handler = logging.FileHandler("%s//%s%s.txt"%(file_formate,str(casename),timestamp),encoding="utf-8")        handler.setLevel(logging.DEBUG)        formatter = logging.Formatter('%(asctime)s - %(filename)s:%(lineno)d - %(levelname)s - %(message)s')        #写复杂点,给sls 分析log 使用        file_formatter=logging.Formatter('[%(asctime)s][%(filename)s:%(lineno)d][%(levelname)s][%(testEnv)s][%(jenBuild)s][%(jenJobName)s][%(codeCID)s][%(codeBName)s][%(casename)s][%(message)s]')
        handler.setFormatter(file_formatter)


        console = logging.StreamHandler()        console.setLevel(logging.DEBUG)        console.setFormatter(formatter)        # 给上传allure 附件使用        self.txt_log_path="%s//%s%s.txt" % ( file_formate,str(casename),timestamp)        self.logger.addHandler(handler)        self.logger.addHandler(console)        results_env=getEnv()        results_env["casename"]=casename        #传点参数        """        从名字可以看出,该函数的作用就是部分使用某个函数,即冻结住某个函数的某些参数,让它们保证为某个值,并生成一个可调用的新函数对象,这样你就能够直接调用该新对象,并且仅用使用很少的参数        functools.partial 偏函数的使用                多余参数:        import logging
        FORMAT = '%(asctime)s %(clientip)s %(user)-8s %(message)s %(haiyoushui)s'                logging.basicConfig(format=FORMAT)                d={'clientip':'192.168.0.1','user':'fbloggs', "haiyoushui":"12312313113213"}                logger=logging.getLogger('tcpserver')                logger.warning('Protocolproblem:%s','connectionreset',extra=d)                extra是用户自定义的dict. 这些key/value在格式化的时候可以直接引用。

        用Python的logging模块记录日志时,遇到了重复记录日志的问题,第一条记录写一次,第二条记录写两次,第三条记录写三次。。。很头疼,这样记日志可不行。网上搜索到了原因与解决方案:        原因:没有移除handler         解决:在日志记录完之后removeHandler            logger.removeHandler(streamhandler)
        所以这里有以下几个解决办法:
            每次创建不同name的logger,每次都是新logger,不会有添加多个handler的问题。(ps:这个办法太笨,不过我之前就是这么干的。。)            像上面一样每次记录完日志之后,调用removeHandler()把这个logger里的handler移除掉。            在log方法里做判断,如果这个logger已有handler,则不再添加handler。            与方法2一样,不过把用pop把logger的handler列表中的handler移除。                """
        self.logger.info=functools.partial(self.logger.info,extra=results_env)        self.logger.error=functools.partial(self.logger.error,extra=results_env)        self.logger.debug=functools.partial(self.logger.debug,extra=results_env)        self.logger.warning=functools.partial(self.logger.warning,extra=results_env)
    def __call__(self, *args, **kwargs):        return self.logger
    # __call__  使得类实例对象可以像调用普通函数那样,以“对象名()”的形式使用
if __name__ == "__main__":    logging1=hcs_logger("casename123")()    # logging1.info("123",extra=getEnv())    logging1.info("123")    logging1.error("321")    logging1.debug("12553")    logging1.warning("124443")    # DEBUG 有问题!    logging1.debug("12553")    file_name = os.path.split(os.path.abspath(__file__))[-1]    logging1.info(file_name.replace(".py",""))

posted @ 2021-03-04 17:21  峡谷恶霸  阅读(70)  评论(0编辑  收藏  举报