keil debug如何在watch直接修改变量值_python日志记录系列教程,内置logging模块(一),直接使用logging模块的基础日志记录
前言:成熟的軟件開發不可避免的要進行日志記錄,python內置模塊logging提供了強大的日志記錄能力,本文將從多個角度,由淺入深的介紹logging的常見使用方法和一些基本概念,本此系列文章分為兩篇,本文為系列文章第一篇,介紹實用logging模塊進行基本的日志記錄操作。本文主要都是直接使用 logging.xxxx() 的方式,這是最簡單初步的日志記錄,不涉及任何復雜的類和對象,這些將會在下一篇文章中來說明。
一、什么是日志記錄
所謂的日志記錄就是對軟件執行時所發生事件的一種追蹤方式。軟件開發人員對他們的代碼添加日志調用,借此來指示某事件的發生。一個事件通過一些包含變量數據的描述信息來描述(比如:每個事件發生時的數據都是不同的)。開發者還會區分事件的重要性,重要性也被稱為 等級 或 嚴重性
1.1 什么時候使用 Logging
對于簡單的日志使用來說日志功能提供了一系列便利的函數。它們是 debug(),info(),warning(),error() 和 critical()。想要決定何時使用日志,請看下表,其中顯示了對于每個通用任務集合來說最好的工具。
| 對于命令行或程序的應用,結果顯示在控制臺。 | print(),這是我們自己常用的,顯示某一些信息,直接打印結果,但是比較低級 |
| 在對程序的普通操作發生時提交事件報告(比如:狀態監控和錯誤調查) | http://logging.info() 函數(當有診斷目的需要詳細輸出信息時使用 logging.debug() 函數) |
| 提出一個警告信息基于一個特殊的運行時事件 | warnings.warn() 位于代碼庫中,該事件是可以避免的,需要修改客戶端應用以消除告警 logging.warning() 不需要修改客戶端應用,但是該事件還是需要引起關注 |
| 對一個特殊的運行時事件報告錯誤 | 引發異常 |
| 報告錯誤而不引發異常(如在長時間運行中的服務端進程的錯誤處理) | logging.error(), logging.exception() 或 logging.critical() 分別適用于特定的錯誤及應用領域 |
1.2 日志記錄的5個等級
日志功能應以所追蹤事件級別或嚴重性而定。各級別適用性如下(以嚴重程度遞增):
| DEBUG | 細節信息,僅當診斷問題時適用。 |
| INFO | 確認程序按預期運行,但是要提示一些相關的信息, |
| WARNING | 表明有已經或即將發生的意外(例如:磁盤空間不足)。程序仍按預期進行,但是會有警告,可能出現什么錯誤。 |
| ERROR | 由于嚴重的問題,程序的某些功能已經不能正常執行 |
| CRITICAL | 嚴重的錯誤,表明程序已不能繼續執行 |
以及嚴重程度排序為:
debug<info<warning<error<critical默認的級別是``WARNING``,意味著只會追蹤該級別以及該級別以上的事件,當然我們可以更改日志配置,重新設置默認值。
另外,我們需要查看所記錄的日志,一般日志輸出會有兩種形式,
所追蹤事件可以以不同形式處理。最簡單的方式是輸出到控制臺。另一種常用的方式是寫入磁盤文件。默認情況下,日志輸出信息會顯示在控制臺上。下面也會以這兩種方式來加以說明。
1.3 logging模塊初識
二、logging模塊的常規基礎操作
2.1 logging日志記錄的基本配置
函數原型如下:
該函數支持以下關鍵字參數。
| filename | 使用指定的文件名而不是 StreamHandler 創建 FileHandler。在需要將日志寫入到日志文件的時候使用 |
| filemode | 日志文件的寫入模式.,默認的是"a",默認是追加的方式,若要每一次都重新寫入,則使用"w" |
| format | 使用的指定格式字符串。 |
| datefmt | 時間格式的字符串 |
| style | If format is specified, use this style for the format string. One of '%', '{' or '$' for printf-style, str.format() or string.Template respectively. Defaults to '%'. 字符串的格式化方式的選擇,參考下面的style參數詳解。 |
| level | 設置根記錄器級別去指定 level.默認的是warning,可以重新指定,比如http://logging.INFO logging.DEBUG 等等, |
| stream | Use the specified stream to initialize the StreamHandler. Note that this argument is incompatible with filename - if both are present, a ValueError is raised.(后面用到了再說) |
| handlers | If specified, this should be an iterable of already created handlers to add to the root logger. Any handlers which don't already have a formatter set will be assigned the default formatter created in this function. Note that this argument is incompatible with filename or stream - if both are present, a ValueError is raised.(后面用到了再說) |
對basicConfig的調用應該在 debug、info等函數的前面。因為它被設計為一次性的配置,只有第一次調用會進行操作,隨后的調用不會產生有效操作。
2.2 將日志寫入文件
2.3 多個模塊中使用日志記錄
如果你的程序包含多個模塊,這里有一個如何組織日志記錄的示例:
如果你運行 myapp.py ,你應該在 myapp.log 中看到:
我們發現,不同模塊中所記錄的日志信息都被保存到了同一個日志文件里面了。
2.4 格式化輸出以及記錄相關的變量值到日志文件
前面有文章專門介紹了python中的字符串格式化的幾種方法,包括使用百分號%、str.format、string.Template模板等方法,可以參考前面的文章:
python字符串格式化深入詳解(四種方法)
所以,要定義自己的字符串輸出格式,要記錄相關的變量到字符串中,我們可以這樣做。
要記錄變量數據,請使用格式字符串作為事件描述消息,并將變量數據作為參數附加。 例如:
如你所見,
logging日志記錄的字符串格式化,將數據合并到字符串中所采用的方式是 百分號% 的形式,但是如果我想要使用其他的字符串格式化的方法怎么辦呢?就需要通過指定style參數來決定了,style的取值可以是三個,即
- '%':這是默認的,即默認使用百分號%格式的字符串格式化方法;
- '{':表示使用str.format()的字符串格式化方法;
- '$':表示使用string.Template的格式化方法;
為什么是默認使用百分號 % 格式化工具呢?主要是因為logging的出現時間比這兩種格式化方法要早一些,但是并不是說完全不支持,現在完全有其他的方法讓logging支持str.format()和string.Template()這些格式,只需要設置不同的style參數值即可。
三、logging模塊的格式化設置以及時間格式化
當我們看之前的記錄日志的時候,我們發現,記錄的日志總是下面的格式,如下:
即所謂的:
日志類別嚴重程度:root:message這樣的格式,那么能不能進行更改呢?這個自然是可以的,怎么更改呢,如下:
3.1 消息顯示的格式設置——basicConfig的 format 參數
要更改用于顯示消息的格式,你需要指定要使用的格式:
這將輸出:
請注意,前面示例中出現的“root”已消失。
對于可以出現在格式字符串中的全部內容,你可以參考以下文檔 LogRecord 屬性 ,在后面的系列文章第二篇中,還會深入探討日志記錄的高級主題,來說明的。
下面是常見的格式設置的字段標識:
- %(asctime)s:日志創建時的普通時間;
- %(created)f:日志創建時的時間(由time.time()返回);
- %(filename)s:文件名;
- %(funcName)s:調用日志記錄的函數;
- %(levelname)s:日志消息的文本級別;
- %(levelno)s:日志消息的數字級別;
- %(lineno)d:調用日志消息的行號;
- %(msecs)d:創建時間的毫秒部分;
- %(message)s:日志消息;
- %(name)s:日志器的名稱;
- %(pathname)s:記錄日志的源文件的路徑名;
- %(process)d:進程ID;
- %(processName)s:進程名;
- %(thread)d:線程ID;
- %(threadName)s:線程名;
- %(relativeCreated)d:創建日志記錄的時間(以毫秒為單位)
借助于這些格式,我們可以自定義日志記錄,比如顯示時間:
這樣,日志中除了記錄消息等級、消息信息外,還會記錄上消息創建的時間。
3.2 設置日志記錄的時間顯示的格式——basicConfig的datefmt參數
那我們可不可以更改時間顯示的格式呢?這個自然是能的,
日期/時間顯示的默認格式(如上所示)類似于 ISO8601 或 RFC 3339 。 如果你需要更多地控制日期/時間的格式,請為 basicConfig 提供 datefmt 參數,
需要格外注意的是:datefmt 參數的格式與 time.strftime() 支持的格式相同。如下例所示:
那到底有哪一些格式呢?可以參考官網:https://docs.python.org/zh-cn/3.7/library/time.html#time.strftime
函數原型為:
轉換一個元組或 struct_time 表示的由 gmtime() 或 localtime() 返回的時間到由 format 參數指定的字符串。如果未提供 t ,則使用由 localtime() 返回的當前時間。 format 必須是一個字符串。如果 t 中的任何字段超出允許范圍,則引發 ValueError 。
0是時間元組中任何位置的合法參數;如果它通常是非法的,則該值被強制改為正確的值。
以下指令可以嵌入 format 字符串中。它們顯示時沒有可選的字段寬度和精度規范,并被 strftime() 結果中的指示字符替換:
| %a | 本地化的縮寫星期中每日的名稱。 | |
| %A | 本地化的星期中每日的完整名稱。 | |
| %b | 本地化的月縮寫名稱。 | |
| %B | 本地化的月完整名稱。 | |
| %c | 本地化的適當日期和時間表示。 | |
| %d | 十進制數 [01,31] 表示的月中日。 | |
| %H | 十進制數 [00,23] 表示的小時(24小時制)。 | |
| %I | 十進制數 [01,12] 表示的小時(12小時制)。 | |
| %j | 十進制數 [001,366] 表示的年中日。 | |
| %m | 十進制數 [01,12] 表示的月。 | |
| %M | 十進制數 [00,59] 表示的分鐘。 | |
| %p | 本地化的 AM 或 PM 。 | (1) |
| %S | 十進制數 [00,61] 表示的秒。 | (2) |
| %U | 十進制數 [00,53] 表示的一年中的周數(星期日作為一周的第一天)作為。在第一個星期日之前的新年中的所有日子都被認為是在第0周。 | (3) |
| %w | 十進制數 [0(星期日),6] 表示的周中日。 | |
| %W | 十進制數 [00,53] 表示的一年中的周數(星期一作為一周的第一天)作為。在第一個星期一之前的新年中的所有日子被認為是在第0周。 | (3) |
| %x | 本地化的適當日期表示。 | |
| %X | 本地化的適當時間表示。 | |
| %y | 十進制數 [00,99] 表示的沒有世紀的年份。 | |
| %Y | 十進制數表示的帶世紀的年份。 | |
| %z | 時區偏移以格式 +HHMM 或 -HHMM 形式的 UTC/GMT 的正或負時差指示,其中H表示十進制小時數字,M表示小數分鐘數字 [-23:59, +23:59] 。 | |
| %Z | 時區名稱(如果不存在時區,則不包含字符)。 | |
| %% | 字面的 '%' 字符。 |
注釋:
下面是一個示例,一個與 RFC 2822 Internet電子郵件標準以兼容的日期格式。
個人總結:
個人習慣使用的格式為,年月日,時分秒,星期數,如下:
四、日志記錄的堆棧追蹤
前面都是一些最簡單的日志記錄操作與格式設置,但是我們在編寫python代碼的時候,往往涉及到多個函數之間的調用,我們需要跟蹤函數調用的堆棧信息,這樣才能定位到到底是哪一個函數的哪一個位置出現了錯誤,這才更加合理。
再次回顧日志記錄實際上是做一件什么事?
- 即當滿足什么條件的時候、或者是當某一件事情發生的時候,開始記錄日志,明白這思想很重要
- 現在我們要做的就是,在程序執行出發了異常的時候,就開始記錄日志,并且記錄程序的堆棧信息,思想是相通的,只不過條件現在變成了,在異常發生的時候。
首先其實我們發現,http://logging.xxx()的幾個函數(info、debug、warning、error、critical)他們的參數格式是一樣的,以error為例:
有兩個非常關鍵的參數,它們是
- stack_info:
- exc_info:
下面將分別來說明這兩個參數各自的作用
(1)exc_info=True——出現異常時記錄堆棧調用信息
先從一個簡單的函數調用例子來看
有一個math_func.py模塊,實現了兩個函數,如下:
然后有一個operation_func.py模塊,再進一步對這兩個函數進行包裝,以便進行選擇性的調用哪一個函數,如下:
最后再主模塊main.py里面進行調用,如下:
運行之后,打開日志文件,得到如下的結果
從上面我們發現,錯誤信息以及堆棧信息全部清楚地記錄在了里面。這對于程序調試是至關重要的。
以上就是出現錯誤的時候logging堆棧追蹤的全部實現,一般的調用模板如下:
前面提到了由兩個參數,那么這兩個參數到底有什么區別呢?現總結如下:
- exe_info參數:在搜索異常處理程序時,跟蹤異常而打開的堆棧幀的信息,這個是專門針對異常發生的時候記錄日志的;
- stack_info參數:默認為 False。如果為 True,則將堆棧信息添加到日志消息中,包括實際的日志調用。請注意,這與通過指定 exc_info 顯示的堆棧信息不同:他主要是從堆棧底部到當前線程中的日志記錄調用的堆棧幀,什么意思呢?即使在未引發任何異常的情況下,也可以顯示如何到達代碼中的特定點。堆棧幀在標題行(即每一句日志記錄行)之后打印:
- stack_info:簡單來說就是,根據每一個日志記錄語句所在的位置,打印出這一句日志記錄信息,并且在后面顯示這句日志記錄所在的函數堆棧的位置是在哪里。
(2)stack_info=True——記錄每一句日志記錄語句所在代碼中的堆棧信息
參考下面的例子:
調用b函數,b中有調用a函數,得到下面的日志信息:
從上面可以看出,stack_info它會詳細記錄每一條日志打印語句所在的位置,然后在日志信息后面加以顯示,這跟exe_info是有顯著區別的。
(3)補充參數說明——extra參數的說明
前面的3.1中在說明日志記錄的格式的時候,給出了很多預先定義好的格式,比如
- %(asctime)s:日志創建時的普通時間;
- %(created)f:日志創建時的時間(由time.time()返回);
- %(filename)s:文件名;
等等,有時候我們想定義自己的信息怎么辦呢?可以采用下面的方式,extra參數接受的參數是一個字典類型,如下例子所示:
我們發現,自己定義的clientip、user、work都顯示了出來!
實際上,上面的這種操作稱之為,向日志輸出中添加上下文信息
除了傳遞給日志記錄函數的參數外,有時候我們還想在日志輸出中包含一些額外的上下文信息。比如,在一個網絡應用中,可能希望在日志中記錄客戶端的特定信息,如:遠程客戶端的IP地址和用戶名。這里我們來介紹以下幾種實現方式:
- 通過向日志記錄函數傳遞一個extra參數引入上下文信息
- 使用LoggerAdapters引入上下文信息
- 使用Filters引入上下文信息
具體說明請參考博文 《Python之向日志輸出中添加上下文信息》
關于Python logging的更多高級用法,請參考文檔<< Logging CookBook >>。
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的keil debug如何在watch直接修改变量值_python日志记录系列教程,内置logging模块(一),直接使用logging模块的基础日志记录的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 来自网页的消息服务器繁处理忙,Event
- 下一篇: xshell怎么让程序后台运行_使程序在