python模塊之Logging


1. hashlib

hashlib的作用:用於確認文件是否被更改,也可以用來密文存儲密碼等

特點:

1. hash加密之后長度一樣

2. hash加密的東西一樣,則哈希的值是一樣的。

3. 如果使用同一個hash字符編碼表,那么它的結果會不斷的累加

import hashlib

hash_obj = hashlib.md5()

strings = b"panlifu"

hash_obj.update(strings)


# panlifu           6099a053b1a5fd9c87fe34d68d6cc829
print(hash_obj.hexdigest())

str_a = b"lt"
hash_obj.update(str_a)

# panlifult         c64625db271a6b9c5082fca53722e555
print(hash_obj.hexdigest())

可以用來存儲用戶密文密碼:

#用戶的登錄
import hashlib
usr = input('username :')
pwd = input('password : ')
with open('userinfo') as f:
    for line in f:
        user,passwd,role = line.split('|')
        md5 = hashlib.md5()
        md5.update(bytes(pwd,encoding='utf-8'))
        md5_pwd = md5.hexdigest()
        if usr == user and md5_pwd == passwd:
            print('登錄成功')

2. hmac

hmace的作用:跟hashlib用法相同,用於確認文件是否被更改,同時也可以用來密文存儲密碼

#加鹽
import hashlib   # 提供摘要算法的模塊
md5 = hashlib.md5(bytes('鹽',encoding='utf-8'))  #加鹽的步驟
# md5 = hashlib.md5()
md5.update(b'123456')
print(md5.hexdigest())



使用用戶名的一部分或者 直接使用整個用戶名作為鹽


import hashlib   # 提供摘要算法的模塊
md5 = hashlib.md5(bytes('鹽',encoding='utf-8')+b'這里為用戶的信息')
# md5 = hashlib.md5()
md5.update(b'123456')
print(md5.hexdigest())

3. UUID

1. UUID是什么:

通用唯一標識符 ( Universally Unique Identifier ), 對於所有的UUID它可以保證在空間和時間上的唯一性. 它是通過MAC地址, 時間戳, 命名空間, 隨機數, 偽隨機數來保證生成ID的唯一性, 有着固定的大小( 128 bit )

2. UUID特點:

它的唯一性和一致性特點使得可以無需注冊過程就能夠產生一個新的UUID. UUID可以被用作多種用途, 既可以用來短時間內標記一個對象, 也可以可靠的辨別網絡中的持久性對象.

3. 為什么使用UUID?

很多應用場景需要一個id, 但是又不要求這個id 有具體的意義, 僅僅用來標識一個對象. 常見的例子有數據庫表的id 字段. 另一個例子是前端的各種UI庫, 因為它們通常需要動態創建各種UI元素, 這些元素需要唯一的id , 這時候就需要使用UUID了.

4. UUID的用法

**python的uuid模塊提供UUID類和函數uuid1(), uuid3(), uuid4(), uuid5() 來生成1, 3, 4, 5各個版本的UUID ( 需要注意的是: python中沒有uuid2()這個函數). 對uuid模塊中最常用的幾個函數總結如下: **

  1. uuid.uuid1([node[, clock_seq]]) : 基於時間戳

**使用主機ID, 序列號, 和當前時間來生成UUID, 可保證全球范圍的唯一性. 但由於使用該方法生成的UUID中包含有主機的網絡地址, 因此可能危及隱私. 該函數有兩個參數, 如果 node 參數未指定, 系統將會自動調用 getnode() 函數來獲取主機的硬件地址. 如果 clock_seq 參數未指定系統會使用一個隨機產生的14位序列號來代替. **

  1. uuid.uuid3(namespace, name) : 基於名字的MD5散列值

通過計算命名空間和名字的MD5散列值來生成UUID, 可以保證同一命名空間中不同名字的唯一性和不同命名空間的唯一性, 但同一命名空間的同一名字生成的UUID相同.

  1. uuid.uuid4() : 基於隨機數

**通過隨機數來生成UUID. 使用的是偽隨機數有一定的重復概率. **

  1. uuid.uuid5(namespace, name) : 基於名字的SHA-1散列值

4. logging

logging的作用:記錄程序運行期間的狀態

V1版本(直接使用)

import logging

logging.debug('調試信息')
logging.info('正常信息')
logging.warning('警告信息')
logging.error('報錯信息')
logging.critical('嚴重錯誤信息')


'''
WARNING:root:警告信息
ERROR:root:報錯信息
CRITICAL:root:嚴重錯誤信息
'''


'''
	總結:
		 1. V1的版本無法指定日志的級別;
		 2. 無法指定日志的格式;
		 3. 只能往屏幕打印,無法寫入文件

'''

V2版本(半定制)

# V2版本
import logging

# 日志的基本配置
logging.basicConfig(filename='access.log',format='%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s',datefmt='%Y-%m-%d %H:%M:%S %p',level=10)
logging.debug('調試信息')             # 10
logging.info('正常信息')              # 20
logging.warning('警告信息')           # 30
logging.error('報錯信息')             # 40
logging.critical('嚴重錯誤信息')       # 50


'''
	總結:
		1. 所有類別的文件都打印到了一個文件中,同時不能設置日志文件的大小等
		1. V1和V2版本能使我們快速上手logger,但是並不能滿足我們實際的使用,我們還需要自定義Logger

'''

V3版本(半自定義)

import logging
#   0       10       20       30          40          50
# notset-->debug --> info --> warning --> error --> critical

# 1. 設置logging對象
plf_log = logging.getLogger("ATM")

# 2. Filter對象:不常用,略

# 3. Handler對象:接受logger傳來的日志,然后控制輸出
info_log = logging.FileHandler("info.log")  # 打印到文件
error_log = logging.FileHandler("error.log")    # 打印到文件
pm = logging.StreamHandler()        # 打印到終端

# 4. Formatter對象:日志格式
style1 = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(module)s : %(message)s',datefmt='%Y-%m-%d %H:%M:%S %p')
style2 = logging.Formatter('%(asctime)s:%(message)s',datefmt='%Y-%m-%d %H:%M:%S %p')
style3 = logging.Formatter('%(name)s %(message)s')

# 5. 為Handler對象綁定格式
info_log.setFormatter(style1)
error_log.setFormatter(style2)
pm.setFormatter(style3)

# 6. 將Handler添加給logger並設置日志級別
plf_log.addHandler(info_log)
plf_log.addHandler(error_log)
plf_log.addHandler(pm)

# 7. 設置日志級別,可以在兩個關卡進行設置logger與handler
#    plf_log是第一級過濾,然后才能到handler
plf_log.setLevel(9)
info_log.setLevel(8)
error_log.setLevel(8)
pm.setLevel(8)

plf_log.disabled = True
# 8. 測試
plf_log.debug('debug')
plf_log.info('info')
plf_log.warning('warning')
plf_log.error('error')
plf_log.critical('critical')

plf_log.disabled = False

plf_log.debug('debug')
plf_log.info('info')

Formatter對象設置具體的輸出格式,常見變量整理

變量 格式 變量描述
asctime %(asctime)s 將日志的時間構造成可讀的形式,默認情況下是精確到毫秒,如 2018-10-13 23:24:57,832,可以額外指定 datefmt 參數來指定該變量的格式
name %(name) 日志對象的名稱
filename %(filename)s 不包含路徑的文件名
pathname %(pathname)s 包含路徑的文件名
funcName %(funcName)s 日志記錄所在的函數名
levelname %(levelname)s 日志的級別名稱
message %(message)s 具體的日志信息
lineno %(lineno)d 日志記錄所在的行號
pathname %(pathname)s 完整路徑
process %(process)d 當前進程ID
processName %(processName)s 當前進程名稱
thread %(thread)d 當前線程ID
threadName %threadName)s 當前線程名稱

V3版本中,需要注意的幾個小細節

  1. Logger 對象和 Handler 對象都可以設置日志級別,那到底以哪個為准?

    • 默認 Logger 對象級別為 30 ,也即 WARNING。

    • 默認 Handler 對象級別為 0,也即 NOTSET。

    • 日志先經過Logger過濾,然后在經過Handler刷選。什么意思了。直接上代碼吧

      import logging
      
      
      plf_logger = logging.getLogger("ATM")
      
      hander_one = logging.FileHandler("error.log")
      hander_two = logging.FileHandler("info.log")
      
      format_one = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(module)s : %(message)s',datefmt='%Y-%m-%d %H:%M:%S %p')
      format_two = logging.Formatter('%(asctime)s:%(message)s',datefmt='%Y-%m-%d %H:%M:%S %p')
      
      hander_one.setFormatter(format_one)
      hander_two.setFormatter(format_two)
      
      plf_logger.addHandler(hander_one)
      plf_logger.addHandler(hander_two)
      
      # 設置級別
      plf_logger.setLevel(10)	  #logger對象設置級別為10
      hander_one.setLevel(30)		# handler對象設置級別為30
      hander_two.setLevel(40)		# 另一個handler對象設置為40
      
      plf_logger.debug('debug')
      plf_logger.info('info')
      plf_logger.warning('warning')
      plf_logger.error('error')
      plf_logger.critical('critical')
      
      
      # 以下為error.log日志的內容
      2019-06-11 16:26:23 PM - ATM - WARNING - test : warning
      2019-06-11 16:26:23 PM - ATM - ERROR - test : error
      2019-06-11 16:26:23 PM - ATM - CRITICAL - test : critical
      
      # 以下為info.log日志的內容
      2019-06-11 16:26:23 PM:error
      2019-06-11 16:26:23 PM:critical
      
  2. 中文亂碼問題

    • 自定義Logger配置,指定字符編碼

      handler = logging.FileHandler(filename="test.log", encoding="utf-8")
      
    • 使用默認Logger配置,指定字符編碼

      logging.basicConfig(handlers=[logging.FileHandler("test.log", encoding="utf-8")], level=logging.DEBUG)
      
  3. 臨時禁用日志輸出

    • 使用logging時,可設置不輸出某一級別的日志

      import logging
      logging.disable(logging.INFO)
      
    • 使用自定義Logger對象時,直接設置所有日志不輸出

      logger.disabled = True
      
  4. 日志文件按照時間划分或者按照大小划分

    # 第一種:
    # 每隔 1000 Byte 划分一個日志文件,備份文件為 3 個
    file_handler = logging.handlers.RotatingFileHandler("test.log", mode="w", maxBytes=1000, backupCount=3, encoding="utf-8")
    
    
    # 第二種
    # 每隔 1小時 划分一個日志文件,interval 是時間間隔,備份文件為 10 個
    handler2 = logging.handlers.TimedRotatingFileHandler("test.log", when="H", interval=1, backupCount=10)
    
  5. 日志文件如何輪詢?

    #!/usr/bin/env python
    #_*_coding:utf-8_*_
    # vim : set expandtab ts=4 sw=4 sts=4 tw=100 :
    
    import logging
    import time
    import re
    from logging.handlers import TimedRotatingFileHandler
    from logging.handlers import RotatingFileHandler
    
    def main():
        #日志打印格式
        log_fmt = '%(asctime)s\tFile \"%(filename)s\",line %(lineno)s\t%(levelname)s: %(message)s'
        formatter = logging.Formatter(log_fmt)
        #創建TimedRotatingFileHandler對象
        log_file_handler = TimedRotatingFileHandler(filename="ds_update", when="M", interval=2, backupCount=2)
        #log_file_handler.suffix = "%Y-%m-%d_%H-%M.log"
        #log_file_handler.extMatch = re.compile(r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}.log$")
        log_file_handler.setFormatter(formatter)    
        logging.basicConfig(level=logging.INFO)
        log = logging.getLogger()
        log.addHandler(log_file_handler)
        #循環打印日志
        log_content = "test log"
        count = 0
        while count < 30:
            log.error(log_content)
            time.sleep(20)
            count = count + 1
        log.removeHandler(log_file_handler)
    
    
    if __name__ == "__main__":
        main()
        
    '''   
    # 在TimedRotatingFileHandler方法中參數解釋如下:
    
    1. filename:日志文件名的prefix;
    2. when:是一個字符串,用於描述滾動周期的基本單位,字符串的值及意義如下: 
    	“S”: Seconds 
    	“M”: Minutes 
    	“H”: Hours 
    	“D”: Days 
    	“W”: Week day (0=Monday) 
    	“midnight”: Roll over at midnight
    3. interval: 滾動周期,單位有when指定,比如:when=’D’,interval=1,表示每天產生一個日志文件;
    4. backupCount: 表示日志文件的保留個數;
    
    
    除了上述參數之外,TimedRotatingFileHandler還有兩個比較重要的成員變量,它們分別是suffix和extMatch。suffix是指日志文件名的后綴,suffix中通常帶有格式化的時間字符串,filename和suffix由“.”連接構成文件名(例如:filename=“runtime”, suffix=“%Y-%m-%d.log”,生成的文件名為runtime.2015-07-06.log)。extMatch是一個編譯好的正則表達式,用於匹配日志文件名的后綴,它必須和suffix是匹配的,如果suffix和extMatch匹配不上的話,過期的日志是不會被刪除的。比如,suffix=“%Y-%m-%d.log”, extMatch的只應該是re.compile(r”^\d{4}-\d{2}-\d{2}.log$”)。默認情況下,在TimedRotatingFileHandler對象初始化時,suffxi和extMatch會根據when的值進行初始化: 
    ‘S’: suffix=”%Y-%m-%d_%H-%M-%S”, extMatch=r”\^d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}”; 
    ‘M’:suffix=”%Y-%m-%d_%H-%M”,extMatch=r”^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}”; 
    ‘H’:suffix=”%Y-%m-%d_%H”,extMatch=r”^\d{4}-\d{2}-\d{2}_\d{2}”; 
    ‘D’:suffxi=”%Y-%m-%d”,extMatch=r”^\d{4}-\d{2}-\d{2}”; 
    ‘MIDNIGHT’:”%Y-%m-%d”,extMatch=r”^\d{4}-\d{2}-\d{2}”; 
    ‘W’:”%Y-%m-%d”,extMatch=r”^\d{4}-\d{2}-\d{2}”; 
    如果對日志文件名沒有特殊要求的話,可以不用設置suffix和extMatch,如果需要,一定要讓它們匹配上
    ''' 
    
    
    # 該示例主要參考的文獻為:https://blog.csdn.net/ashi198866/article/details/46725813
    

終極版本(全自定義)

以上三個版本的日志只是為了引出我們下面的日志配置文件

import os
import logging.config

# 定義三種日志輸出格式 開始
standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \
                  '[%(levelname)s][%(message)s]'  # 其中name為getLogger()指定的名字;lineno為調用日志輸出函數的語句所在的代碼行

simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'

id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s'
# 定義日志輸出格式 結束

# 定義日志路徑 開始
logfile_dir = os.path.dirname(os.path.abspath(__file__))  # log文件的目錄,需要自定義文件路徑

logfile_name = 'all2.log'  # log文件名,需要自定義路徑名

# 如果不存在定義的日志目錄就創建一個
if not os.path.isdir(logfile_dir):
    os.mkdir(logfile_dir)

# log文件的全路徑
logfile_path = os.path.join(logfile_dir, logfile_name)
# 定義日志路徑 結束

# log配置字典
LOGGING_DIC = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'standard': {
            'format': standard_format
        },
        'simple': {
            'format': simple_format
        },
    },
    'filters': {},  # filter可以不定義
    'handlers': {
        # 打印到終端的日志
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',  # 打印到屏幕
            'formatter': 'simple'
        },
        # 打印到文件的日志,收集info及以上的日志
        'default': {
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件
            'formatter': 'standard',
            'filename': logfile_path,  # 日志文件
            'maxBytes': 1024*1024*5,  # 日志大小 5M
            'backupCount': 5,		 # 文件的備份數量為5個
            'encoding': 'utf-8',  # 日志文件的編碼,再也不用擔心中文log亂碼了
        },
    },
    'loggers': {
        # logging.getLogger(__name__)拿到的logger配置。如果''設置為固定值logger1,則下次導入必須設置成logging.getLogger('logger1')
        '': {
            # 這里把上面定義的兩個handler都加上,即log數據既寫入文件又打印到屏幕
            'handlers': ['default', 'console'],
            'level': 'DEBUG',
            'propagate': False,  # 向上(更高level的logger)傳遞
        },
    }, 
}


def load_my_logging_cfg():
    logging.config.dictConfig(LOGGING_DIC)  # 導入上面定義的logging配置
    logger = logging.getLogger(__name__)  # 生成一個log實例
    logger.info('It works!')  # 記錄該文件的運行狀態


if __name__ == '__main__':
    load_my_logging_cfg()

使用日志

import time
import logging
import my_logging  # 導入自定義的logging配置

logger = logging.getLogger(__name__)  # 生成logger實例


def demo():
    logger.debug("start range... time:{}".format(time.time()))
    logger.info("中文測試開始。。。")
    for i in range(10):
        logger.debug("i:{}".format(i))
        time.sleep(0.2)
    else:
        logger.debug("over range... time:{}".format(time.time()))
    logger.info("中文測試結束。。。")


if __name__ == "__main__":
    my_logging.load_my_logging_cfg()  # 在你程序文件的入口加載自定義logging配置
    demo()

補充知識:

logging中的Filter:

作用:

1. Filter用來過濾日志信息,例如你想輸出A類信息,但不想輸出C類信息,就可以進行過濾

2. 而由於所有的信息都有經過過濾器,也可以使用過濾器來增加一些信息

import logging
class ContextFilter(logging.Filter):		# 自定義類
    def filter(self, record):
        if record.role == "admin":
            return True
        else:
            return False

if __name__ == '__main__':
    logger = logging.getLogger("Wechat")
    logger.setLevel(logging.DEBUG)
    handler = logging.StreamHandler(sys.stdout)
    formatter = logging.Formatter('%(asctime)s %(levelname)s: %(message)s Role: %(role)s')
    handler.setFormatter(formatter)
    logger.addHandler(handler)
    # 創建綁定fiter
    f = ContextFilter()
    logger.addFilter(f)
    logger.info('An info message with %s', 'some parameters',extra={"role":"admin"})
    logger.info('An info message with %s', 'some parameters',extra={"role":"hacker"})
    #hacker的被過濾掉了


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM