#PYTHON3 #QT #PYSISE2 #TUTORIAL #LOGGING

PySide #18: 秀出你的生活點滴, Logging to Everywhere!

使用者總說,我要看到log啊!

DekBan
Bucketing

--

Photo by Scheier .hr on Unsplash

Python logging有做過專案的開發者應該都有使用過,畢竟一直print想說的話也不是辦法,要寫成Log檔就更麻煩了。因此,Logging這個 library就佔了非常重要的地位,以下會稍微解釋Logging的觀念,但著重在使用方法。

11什麼?你說這應該是Qt教學?呃…對,Logging當然好用,但卻不能輸出到所有地方,例如:QTextEdit畫面上,本篇也會教大家如何讓Logging可以顯示在畫面上讓使用者看到。

✍🏼 Logging

在什麼都不管的情況下,Logging預設會輸出到console介面(Handler)去,並且有固定個格式(Format),同時也會使用預設的Log顯示等級(Level)。

有於本篇不是Logging的教學篇,同時Logging本來是另一個give away project
,這邊就只簡單介紹Logging所包含的元素,並提供give away的程式碼讓大家自行觀賞。

◍ Usage

首先我們先來試試看logging怎麼操作吧!

import logginglogging.basicConfig()
logging.debug('this is debug level')
logging.info('this is info level')
logging.warning('this is warning level')
logging.error('this is error level')
default logging

利用預設的basicConfig可以發現只能在Terminal中印出WARNING跟ERROR兩種Level的文字。

◍ Level

也就是所謂的debug level主要用於區分log訊息的重要層級
依序為:debug ⇾ info ⇾ warning ⇾ error

只有大等於(≧)設定 Level的訊息,才會被顯示

我們可以在使用 basicConfig()時給予其level值,來修改logging的level

logging.basicConfig(level=logging.DEBUG)
logging level = Debug

◍ Filter

有的人會好奇為什麼,log level可以區分輸出的文字呢?

這主要是因為,logging被呼叫時會包含各種log的資訊,通常會包含debug level, 被呼叫的所屬class、function, 還有最重要的log內容等等,而這些訊息在被丟到輸出前會先經過一個logging.Filter物件,並在其 filter()函式中做預處理。

而我們也可以自行定義一個子類別來改寫(override)其filter函式,以達到我們要的效果。

◍ Handler

簡單來說,就是輸出器,logging內建的handler有 StreamHandlerFileHandler等等,其原理是當logging被呼叫時經過filter後會進入Handler物件的 emit()函式
,並在其中做出相應的輸出動作。

重點:
這也就表示,我們要輸出東西到我們喜歡的地方,只許要覆寫 Handler中的emit即可!!

◍ Example

這邊提供一個基礎但好用的 logging sample

注意! 呼叫setup_loggers()前 一定要先呼叫basicConfig()或是給予預設loggernow = datetime.datetime.now().strftime('%Y%m%d')
logging.basicConfig(level=logging.DEBUG)
print(setup_loggers(now))
logging.debug('this is debug level')
logging.info('this is info level')
logging.warning('this is warning level')
logging.error('this is error level')
Log File

📝 Log It To TextEdit

終於到了重頭戲,但其實這邊劇本很短XD

上面說到Handler時有提到,我們可以經由繼承Handler來實作輸出log到任何我們想要的地方。因此我們將利用這個方法搭配callback function的概念來完成這項操作輸出到TextEditor。

⊙ Handler

首先來實作一下Handler,我將其稱為TextHandler,重點在於實作其 emit函式。

  • 我將額外建立一個function來對log加入text handler
# In logger.pyfrom logging import StreamHandlerclass TextHandler(logging.StreamHandler):
"""Log handler output to QTextEdit in MainWindow, through
callback function."""
def __init__(self, callback=None):
StreamHandler.__init__(self, stream=None)
self._callback = callback

def emit(self, record):
if self._callback:
msg = self.format(record)
self._callback(str(msg.encode('utf-8')))
def add_text_handler(callback=None):
"""Add TextHandler to specific logger, to output log messages to
user interface."""
formatter = logging.Formatter('[%(asctime)s][%(levelname)s]'
'[%(threadName)7s] - %(message)s',
'%Y-%m-%d %H:%M:%S')
slot_logger = logging.getLogger('root.report')

text_handler = TextHandler(callback)
text_handler.setFormatter(formatter)
text_handler.setLevel(logging.DEBUG)
slot_logger.addHandler(text_handler)

基本上這樣就完成了~ 是不是超級簡單呢!

⊙ Callback function

接下來,我們要實作callback function,他才是實際上對logging文字做後處理的函式,這邊我們可以將它變成我們喜歡的樣子!

並且在logging中加入text handler 就大功告成啦!

# in main_window.pydef set_logger(self):
now = datetime.now().strftime('%Y%m%d')
print(setup_loggers(now))
add_text_handler(self.logger_callback)

def logger_callback(self, msg):
self._window.log_text.append(msg)
Logging to Textedit

Source Code

完整代碼請看 :Logging to TextEdit

結論

之前有一段時間,都在苦惱怎麼將logging的訊息輸出到各種不同的地方,包括Tcp、Serial port、UI介面等等, 而透過handler的覆寫我們可以很輕鬆的就達成這樣的效果。
這樣的功能對使用者來說也是有很大的益處,尤其是生產線的作業員或是測試工程師更是極其仰賴即時顯示的訊息來做錯誤排除,畢竟…大家都很懶開個log檔都嫌麻煩XDD

我是夜海中的宅男DekBan,我們下次見,see ya next night.

Next Step : PySide #19: 世上唯一不變的就是變!PySide to PyQt

--

--

DekBan
Bucketing

เด็กบ้าน | 🌃夜裡溜搭的宅男,漂泊於月色鋪成的海