1. 程式人生 > >第22天 常用模塊三

第22天 常用模塊三

規範 消息 logging 目錄 eat 兩種 所在 efault 進行

介紹的模塊

logging
hashlib

模塊一:logging

人生三問

什麽是日誌
  對每天所發生的事情做的記錄就是日誌。
為什麽要用日誌
  日誌記錄了我們程序每天發生了什麽事情,這個對於我們程序的維護有很大的幫助。例如每天都會有同一個ip在嘗試登陸我們的
網站,如果我們沒有日誌就不會知道有這樣一個現象發生,可能在多年之後程序就會被攻破,但是如果有了日誌,我們就能即使的發
現程序的異常,並及時的修復它。
怎麽使用日誌?
預備知識:日誌級別
  1. debug  日常的調試信息 數字表示code為10
  2. info   常規信息  code為20
  3. warning  提醒信息  code為30
  4. error  錯誤信息  code為40
  5. critical  常規的錯誤信息  code為50
系統默認的級別是30

系統默認級別為warning,並打印到終端中:

# 錯誤信息
logging.debug(調試信息)
logging.info(常規信息)
logging.warning(提醒信息)
logging.error(錯誤信息)
logging.critical(重大錯誤信息)

# 結果:
# WARNING:root:提醒信息
# ERROR:root:錯誤信息
# CRITICAL:root:重大錯誤信息

日誌的基礎配置信息

import logging
# 配置日誌格式的時候無法通過basicConfig來配置編碼方式
format = %(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s
logging.basicConfig( filename=d.log, # 日誌的輸出的文件,不寫默認是終端 filemode=a, # 這個是打開文件的格式,默認是追加寫入 level=logging.DEBUG, # 日誌的顯示級別,默認是warning以上 datefmt=%Y, # 顯示日期的格式,和format配套使用 format=format # 顯示日誌的格式 # stream:用指定的stream創建StreamHandler。 # 可以指定輸出到sys.stderr,sys.stdout或者文件,默認為sys.stderr。
# 若同時列出了filename和stream兩個參數,則stream參數會被忽略。 ) logging.debug(這是一個調試信息!) # 打開文件d.log,發現有亂碼
技術分享圖片
format參數中可能用到的格式化串:
%(name)s Logger的名字
%(levelno)s 數字形式的日誌級別
%(levelname)s 文本形式的日誌級別
%(pathname)s 調用日誌輸出函數的模塊的完整路徑名,可能沒有
%(filename)s 調用日誌輸出函數的模塊的文件名
%(module)s 調用日誌輸出函數的模塊名
%(funcName)s 調用日誌輸出函數的函數名
%(lineno)d 調用日誌輸出函數的語句所在的代碼行
%(created)f 當前時間,用UNIX標準的表示時間的浮 點數表示
%(relativeCreated)d 輸出日誌信息時的,自Logger創建以 來的毫秒數
%(asctime)s 字符串形式的當前時間。默認格式是 “2003-07-08 16:49:45,896”。逗號後面的是毫秒
%(thread)d 線程ID。可能沒有
%(threadName)s 線程名。可能沒有
%(process)d 進程ID。可能沒有
%(message)s用戶輸出的消息
format參數中可能用到的格式化串

日誌系統的四個核心組件

1. logger 日誌生成器   
2. filter 日誌過濾器
3. handler 日誌處理器,主要是用來調用formatter控制打印輸出
4. formatter 日誌格式定義

創建一個自己的日誌生成器:

import logging
# 1. 創建一個日誌生成器
my_logger = logging.getLogger(my_logger)
# 1.1 設置日誌生成器的級別
my_logger.setLevel(logging.DEBUG)
# 2. 創建一個日誌處理器, 此處可以設置編碼方式
my_handler = logging.FileHandler(f.log, a, encoding=utf-8)
# 3. 創建一個日誌格式定義
my_formatter = logging.Formatter(format)
# 4. 關聯生成器處理器
my_logger.addHandler(my_handler)
# 5. 關聯處理器和格式定義
my_handler.setFormatter(my_formatter)

my_logger.debug(這是一個debug信息!)
# 文件信息:
2018-10-18 15:26:37,668 - my_logger - DEBUG -log模塊練習:  這是一個debug信息!

日誌的繼承:

import logging
# 創建三個日誌生成器
log1 = logging.getLogger(father)
log2 = logging.getLogger(father.son)
log3 = logging.getLogger(father.son.chiren)


# 創建日誌處理器
log1_handler = logging.FileHandler(g.log, a, encoding=utf-8)
# log2_handler = logging.FileHandler(g.log, a, encoding=utf-8)
# log3_handler = logging.FileHandler(g.log, a, encoding=utf-8)

# 創建一個formatter
format1 = %(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s
log_format = logging.Formatter(format1)

# 關聯生成器,處理器和定義格式
log1.addHandler(log1_handler)
log2.addHandler(log1_handler)
log3.addHandler(log1_handler)

log1_handler.setFormatter(log_format)

# 生成日誌
# log1.debug(father_log)  # 此時是創建了g.log文件,但是文件是空的,因為默認的級別是warning
# log1.warning(father_log) # 此時文件內容:2018-10-18 18:15:09,940 - father - WARNING -log模塊練習:  father_log

# log2.warning(father.son.log)
# 此時文件內容為:因為此時father繼承了son的日誌
# 2018-10-18 18:15:09,940 - father - WARNING -log模塊練習:  father_log
# 2018-10-18 18:16:20,146 - father.son - WARNING -log模塊練習:  father.son.log
# 2018-10-18 18:16:20,146 - father.son - WARNING -log模塊練習:  father.son.log

log3.warning(father.son.chiren.log)
# 此時文件內容又增加了三條,因為此時繼承log3日誌有兩個父級
# 2018-10-18 18:15:09,940 - father - WARNING -log模塊練習:  father_log
# 2018-10-18 18:16:20,146 - father.son - WARNING -log模塊練習:  father.son.log
# 2018-10-18 18:16:20,146 - father.son - WARNING -log模塊練習:  father.son.log
# 2018-10-18 18:18:32,956 - father.son.chiren - WARNING -log模塊練習:  father.son.chiren.log
# 2018-10-18 18:18:32,956 - father.son.chiren - WARNING -log模塊練習:  father.son.chiren.log
# 2018-10-18 18:18:32,956 - father.son.chiren - WARNING -log模塊練習:  father.son.chiren.log

練習: 創建一個日誌生成器,讓日誌可以以三種不同的格式寫入文件和打印到終端中

技術分享圖片
import logging
# 創建一個日誌生成器
logger = logging.getLogger(__file__)

# 創建三個不同的處理器打印到終端和文件中
logger_handler1 = logging.FileHandler(ceshi1.log, encoding=utf-8)
logger_handler2 = logging.FileHandler(ceshi2.log, encoding=utf-8)  # 日誌輸出到兩個文件中
logger_handler3 = logging.StreamHandler()  # 打印到終端

# 創建三種格式,格式對應
logger_format1 = logging.Formatter(
    %(asctime)s - %(name)s - %(funcName)s - %(module)s - %(message)s,
    datefmt=%Y-%m-%d %X %p
)
logger_format2 = logging.Formatter(
    %(asctime)s - %(name)s - %(funcName)s - %(module)s - %(lineno)s - %(message)s,
    datefmt=%Y-%m-%d %X %p
)
logger_format3 = logging.Formatter(%(module)s - %(message)s )

# 處理器關聯定義格式
logger_handler1.setFormatter(logger_format1)
logger_handler2.setFormatter(logger_format2)
logger_handler3.setFormatter(logger_format3)

# 設置日誌級別
logger.setLevel(logging.DEBUG)
# 添加處理器
logger.addHandler(logger_handler1)
logger.addHandler(logger_handler2)
logger.addHandler(logger_handler3)

# 輸出日誌
logger.debug(這是一個debug信息! )


# 輸出信息:
‘‘‘
終端:log模塊練習 - 這是一個debug信息! 
ceshi1.log文件:2018-10-18 18:36:19 PM - H:/python_study/day22/log模塊練習.py - <module> - log模塊練習 - 這是一個debug信息! 
ceshi2.log文件:2018-10-18 18:36:19 PM - H:/python_study/day22/log模塊練習.py - <module> - log模塊練習 - 95 - 這是一個debug信息! 

‘‘‘
測試代碼

案例分享:

通過logging讀出來日誌配置文件並可以使用

創建規範化目錄的小工具:

技術分享圖片
import os, sys

dir_list = [conf, core, bin, lib, db, log]

def create_dir(parent_path, dir_list):
    ‘‘‘
    在一個目錄下面創建規範目錄
    :param parent_path: 需要創建的文件夾, 是全局路徑
    :param dir_list: 創建的規範目錄列表
    :return: 創建成功返回True
    ‘‘‘
    # 先規範化目錄
    parent_path = os.path.normpath(parent_path)
    # 循環列表拼接目錄並且創建
    for dir in dir_list:
        # 當前需要創建的目錄路徑
        current_dir = os.path.join(parent_path, dir)
        # 路徑不存在則創建目錄
        if not os.path.exists(current_dir):
            os.mkdir(current_dir)

if os.path.isdir(sys.argv[1]):
    create_dir(sys.argv[1], dir_list)
else:
    print(Usag: python current_exec_file parent_dir_path!)
在一個目錄下面創建規範目錄

初始化操作: 創建規範目錄,並創建logging配置文件

技術分享圖片

文件內容:

技術分享圖片
# star.py文件內容

# 添加環境變量
import sys, os

BASE_DIR = os.path.normpath(os.path.join(
    __file__,
    os.pardir,
    os.pardir
))

sys.path.append(BASE_DIR)

# 導入核心邏輯代碼程序並運行
import core.atm
import core.shop
core.atm.login()
core.shop.register()

# atm.py文件內容
def login():
    print(loggin)
# shop.py文件內容
def register():
    print(register)
文件內容

問題1:此時執行核心代碼的login和register都是可以正常運行的,但是當他們執行的時候我想去往日誌文件中寫入日誌,但是我又不想每次通過上面的方式手動的創建一個日誌生成器,然後配置一些內容。說白了就是想把日誌信息保存到配置文件中,每次想用的時候調用一下就可以了,因此寫入配置文件settings的內容如下:

日誌字典的解析

技術分享圖片

技術分享圖片
import os, sys
STANDARD_FORMAT = %(asctime)s %(name)s %(funcName)s %(module)s %(message)s
SIMPLE_FORMAT = %(asctime)s %(module)s %(message)s
COMPLETE_FORMAT = %(asctime)s %(name)s %(funcName)s %(lineon)s %(module)s %(message)s

# 拼接日誌文件存儲路徑
LOGFILE_PATH = os.path.normpath(os.path.join(
    __file__,
    os.pardir,
    os.pardir,
    log,
    a.log))

print(LOGFILE_PATH)

# 這個就是之前通過手工的方式創建的四個日誌核心組件
LOGGING_DIC = {
    # 日誌字典的版本,這個自己設置的
    version: 1,
    # 日誌格式定義字段,通過一定格式的字符串進行創建
    formatters: {
        # 定義了日誌的表示格式標準格式,簡單格式和完整格式
        standard: {
            format: STANDARD_FORMAT
        },
        # 簡單格式
        simple: {
            format: SIMPLE_FORMAT
        },
        # 完整格式
        "complete":{
            "format": COMPLETE_FORMAT
        }
    },
    # 日誌過濾器,暫時設置成空
    filters: {},
    # 日誌處理器
    handlers: {
        # 定義了兩種日誌處理器
        # 把日誌內容打印到終端
        console: {
            level: DEBUG,  # 日誌級別
            class: logging.StreamHandler,  # 日誌流處理器
            formatter: simple   # 使用的打印格式是上面設置的simple
        },
        default: {
            level: DEBUG,
            class: logging.handlers.RotatingFileHandler,   # 設置文件通過一定大小之後就換文件
            formatter: standard,  # 寫入文件的格式是上面定義的標準格式
            filename: LOGFILE_PATH,  # 寫入文件的路徑
            maxBytes: 1024 * 1024 * 5,  # 日誌文件的最大大小為5M 超出後 換文件
            backupCount: 5, # 最多留五個日誌文件
            encoding: utf-8, # 寫入的時候編碼方式
        },
    },
    # 日誌生成器
    loggers: {
        # 在getLogger的時候  如果指定的名稱 不存在 或者不給名稱 用的就是默認的
        # 在這裏如果key為空 它就是默認的
        # 你可以自己定義生成器的名稱 並且他們還能使用相同的默認配置
        ‘‘: {
            handlers: [default, console],
            level: DEBUG,
            propagate: False,
        },
    },
}
日誌配置字典

問題2:日誌文件配置完成之後,首先我們應該考慮的時候日誌生成器應該放在哪個文件裏面,atm或者shop核心代碼在運行的時候都是要進行記錄日誌的,因此我們應該把日誌生成器函數寫在一個公共組件裏面以便於後續的調用,但是我們應該怎麽去創建一個日誌生成器呢?首先在common裏面寫入我們的日誌生成器代碼

# 導入日誌模塊和配置文件
import logging.config
import conf.settings


def create_logger():
    # 將配置文件導入到logging中
    logging.config.dictConfig(config=conf.settings.LOGGING_DIC)
    # 根據配置文件獲得一個關聯了日誌處理器和格式的生成器
    logger = logging.getLogger(loggers)
    logger.debug(這是一個debug文件)

配置atm.py文件為下:

# 導入日誌模塊
import lib.common

def login():
    print(loggin)
    # 當函數執行完成之後寫入日誌
    lib.common.create_logger()

執行start文件之後發現

終端中輸出:
2018-10-18 19:54:25,557 common 這是一個debug文件
log目錄下創建了一個文件內容為:
2018-10-18 19:54:25,557 loggers create_logger common 這是一個debug文件

問題3:此時我們的日誌模塊基本上是已經創建完成了,但是還有一些小小的問題,就是我們希望的是可以在函數內自定義輸入日誌的內容,而不是由公共組件給我們定義輸入的內容。也就是說我們的公共組件create_logger應該給我們返回一個日誌生成器,然後在函數中獲得此生成器,往文件中寫入我們想要的內容。

修改common.py文件如下:

# 導入日誌模塊和配置文件
import logging.config
import conf.settings


def create_logger():
    # 將配置文件導入到logging中
    logging.config.dictConfig(config=conf.settings.LOGGING_DIC)
    # 根據配置文件獲得一個關聯了日誌處理器和格式的生成器
    return logging.getLogger(loggers)

修改atm.py文件內容如下:

# 導入日誌模塊
import lib.common

def login():
    print(loggin)
    # 當函數執行完成之後寫入日誌
    lib.common.create_logger().debug(今天我登陸了atm取款機!)

重新執行start.py文件發現沒有問題,至此我們通過配置文件創建日誌格式的內容就算大功告成了!

模塊二:hashlib

人生三問

什麽是hash
  hash是一種將任意長度的數據經過計算返回固定長度特征碼的算法。 為什麽要用hash
  1. 無論值多大,hash值都是一樣長度的
  2. 同一個值hash值是一樣的, 不同的值hash是不一樣的
  3. 不會被反解
  基於上述特點,我們可以使用hash對數據進行完整性校驗。
怎麽使用hashlib
  hashlib封裝了一系列的哈希算法,我們可以通過hexdigest得到相應的的散列值

註意的是:hash算法只能計算字節,也就是說傳入的值必須是字節類型的。

使用方法:

# 把一段很長的數據update多次與一次update這段數據是一樣的
import hashlib
# 一段很長的數據helloalvin
m2 = hashlib.md5()
m2.update(helloalvin.encode(utf-8))
print(m2.hexdigest())  # 92a7e713c30abbb0319fa07da2a5c4af

# 直接當參數傳遞進去也是一樣的
m3 = hashlib.md5(helloalvin.encode(utf-8))
print(m3.hexdigest())  # 92a7e713c30abbb0319fa07da2a5c4af


# 拆分開之後計算hash值
m = hashlib.md5()
print(m.hexdigest()) # d41d8cd98f00b204e9800998ecf8427e

m.update(hello.encode(utf-8)) # 5d41402abc4b2a76b9719d911017c592
print(m.hexdigest())

m.update(alvin.encode(utf-8))  # 92a7e713c30abbb0319fa07da2a5c4af
print(m.hexdigest())

問題:撞庫

撞庫
  因為散列計算的其中一個特點是一樣的值通過一樣的方法得到的值一定是一樣的,也就是說密碼123的散列值一定是202cb962ac59075b964b07152d234b70
這樣子如果有人事先存儲了值這一對值,當出現這個散列值的時候我們就會知道你使用的密碼是123。
  但是庫也是有限的,也是大部分經常出現的數據,因此我們只要把密碼設置的相對復雜一點,反解是肯定反解不出來的。

模擬撞庫:

技術分享圖片
# 模擬撞庫
import hashlib
# 密碼
password = [
    hello,
    alexhello,
    egonhello,
    nihaohello,
    hello,world,
]


def create_password_dict(password):
    temp_dict = {}
    for password_item in password:
        # 計算密碼的md5校驗值
        hash = hashlib.md5(password_item.encode(utf-8))
        temp_dict[hash.hexdigest()] = password_item
    # 返回一個密碼與password的一個字典庫
    print(temp_dict)
    return temp_dict

def pojie_password(hash, password_dict):
    # 如果密碼庫中存在則返回密碼
    if hash in password_dict:
        return password_dict[hash]


# 得到密碼庫
password_lib = create_password_dict(password)
# 獲得密碼
ps = pojie_password(5d41402abc4b2a76b9719d911017c592, password_lib)
if ps:
    print(密碼為===》%s % ps)
else:
    print(密碼庫中暫時還沒有,請稍等!)
撞庫的實現

另一個加密模塊:hmac必須要加鹽

技術分享圖片
import hmac
# 這個裏面傳入的值是秘鑰
h = hmac.new(miyao.encode(utf-8))
# 這個是密碼
h.update(henchangdemima.encode(utf-8))
print(h.hexdigest())   # c3ffabf0cf7eef648ba783c9673a54d1


# 拆分密碼
# 密碼不能改變,否則出來的結果不一樣
h1 = hmac.new(miyao.encode(utf-8))

h1.update(henchang.encode(utf-8))
h1.update(de.encode(utf-8))
h1.update(mima.encode(utf-8))

print(h1.hexdigest())  # c3ffabf0cf7eef648ba783c9673a54d1
hmac

第22天 常用模塊三