Thứ ba, 02/05/2017 | 00:00 GMT+7

Cách sử dụng tính năng ghi log trong Python 3

Mô-đun logging là một phần của thư viện Python chuẩn và cung cấp khả năng theo dõi các sự kiện xảy ra trong khi phần mềm chạy. Bạn có thể thêm các cuộc gọi ghi log vào mã của bạn để cho biết những sự kiện nào đã xảy ra.

Mô-đun logging cho phép ghi log chẩn đoán ghi lại các sự kiện liên quan đến hoạt động của ứng dụng, cũng như ghi log kiểm tra ghi lại các sự kiện giao dịch của user để phân tích. Nó đặc biệt được sử dụng để ghi lại các sự kiện vào một file .

Tại sao sử dụng Mô-đun logging

Mô-đun logging lưu giữ bản ghi về các sự kiện xảy ra trong một chương trình, giúp bạn có thể xem kết quả liên quan đến bất kỳ sự kiện nào xảy ra trong suốt thời gian chạy của một phần mềm.

Bạn có thể quen thuộc hơn với việc kiểm tra xem các sự kiện có đang xảy ra hay không bằng cách sử dụng câu print() trong suốt mã của bạn. Các print() tuyên bố không cung cấp một cách cơ bản để đi về gỡ lỗi mã của bạn để các vấn đề quyết tâm. Mặc dù việc nhúng các câu print() trong toàn bộ mã của bạn có thể theo dõi stream thực thi và trạng thái hiện tại của chương trình của bạn, giải pháp này được chứng minh là khó bảo trì hơn so với việc sử dụng module logging vì một số lý do:

  • Rất khó để phân biệt giữa kết quả gỡ lỗi và kết quả chương trình bình thường vì cả hai được trộn lẫn
  • Khi sử dụng các câu print() phân tán trong toàn bộ mã, không có cách nào dễ dàng để tắt các câu lệnh cung cấp kết quả gỡ lỗi
  • Rất khó để xóa tất cả các câu print() khi bạn hoàn tất việc gỡ lỗi
  • Không có bản ghi log nào chứa thông tin chẩn đoán sẵn có

Bạn nên có thói quen sử dụng module logging trong mã của bạn vì điều này phù hợp hơn với các ứng dụng phát triển ngoài các tập lệnh Python đơn giản và cung cấp một cách tiếp cận gỡ lỗi bền vững.

Bởi vì log có thể cho bạn thấy hành vi và lỗi theo thời gian, chúng cũng có thể cung cấp cho bạn bức tranh tổng thể tốt hơn về những gì đang diễn ra trong quá trình phát triển ứng dụng của bạn.

In thông báo gỡ lỗi vào console

Nếu bạn đã quen sử dụng câu print() để xem những gì đang xảy ra trong một chương trình, thì bạn có thể quen với việc xem một chương trình xác định một lớp và khởi tạo các đối tượng trông giống như sau:

pizza.py
class Pizza():     def __init__(self, name, price):         self.name = name         self.price = price         print("Pizza created: {} (${})".format(self.name, self.price))      def make(self, quantity=1):         print("Made {} {} pizza(s)".format(quantity, self.name))      def eat(self, quantity=1):         print("Ate {} pizza(s)".format(quantity, self.name))  pizza_01 = Pizza("artichoke", 15) pizza_01.make() pizza_01.eat()  pizza_02 = Pizza("margherita", 12) pizza_02.make(2) pizza_02.eat()  

Đoạn mã trên có phương thức __init__ để xác định nameprice của một đối tượng thuộc lớp Pizza . Sau đó, nó có hai phương thức, một phương thức gọi là make() để làm pizza và một phương thức gọi là eat() để ăn pizza. Hai phương thức này nhận tham số là quantity , được khởi tạo tại 1 .

Bây giờ hãy chạy chương trình:

  • python pizza.py

Ta sẽ nhận được kết quả sau:

Output
Pizza created: artichoke ($15) Made 1 artichoke pizza(s) Ate 1 pizza(s) Pizza created: margherita ($12) Made 2 margherita pizza(s) Ate 1 pizza(s)

Mặc dù câu print() cho phép ta thấy rằng mã đang hoạt động, ta có thể sử dụng module logging để thực hiện việc này thay thế.

Hãy xóa hoặc comment các câu print() trong toàn bộ mã và thêm import logging vào đầu file :

pizza.py
import logging   class Pizza():     def __init__(self, name, value):         self.name = name         self.value = value ... 

Mô-đun loggingmức WARNING mặc định , mức này cao hơn DEBUG . Vì ta sẽ sử dụng module logging để gỡ lỗi trong ví dụ này, ta cần sửa đổi cấu hình để mức độ ghi nhật logging.DEBUG sẽ trả về thông tin cho console cho ta . Ta có thể làm điều đó bằng cách thêm dòng sau vào bên dưới câu lệnh nhập :

pizza.py
import logging  logging.basicConfig(level=logging.DEBUG)   class Pizza(): ... 

Mức độ ghi log logging.DEBUG đề cập đến một giá trị số nguyên không đổi mà ta tham chiếu trong đoạn mã trên để đặt ngưỡng. Mức độ DEBUG là 10.

Bây giờ, ta sẽ thay thế tất cả các câu print() bằng các câu lệnh logging.debug() . Không giống như logging.DEBUG là một hằng số, logging.debug() là một phương thức của module logging . Khi làm việc với phương thức này, ta có thể sử dụng cùng một chuỗi được truyền vào print() , như hình dưới đây.

pizza.py
import logging  logging.basicConfig(level=logging.DEBUG)   class Pizza():     def __init__(self, name, price):         self.name = name         self.price = price         logging.debug("Pizza created: {} (${})".format(self.name, self.price))      def make(self, quantity=1):         logging.debug("Made {} {} pizza(s)".format(quantity, self.name))      def eat(self, quantity=1):         logging.debug("Ate {} pizza(s)".format(quantity, self.name))  pizza_01 = Pizza("artichoke", 15) pizza_01.make() pizza_01.eat()  pizza_02 = Pizza("margherita", 12) pizza_02.make(2) pizza_02.eat()  

Đến đây, khi ta chạy chương trình bằng lệnh python pizza.py , ta sẽ nhận được kết quả sau:

Output
DEBUG:root:Pizza created: artichoke ($15) DEBUG:root:Made 1 artichoke pizza(s) DEBUG:root:Ate 1 pizza(s) DEBUG:root:Pizza created: margherita ($12) DEBUG:root:Made 2 margherita pizza(s) DEBUG:root:Ate 1 pizza(s)

Thông báo log có mức độ nghiêm trọng DEBUG cũng như từ root được nhúng trong chúng, đề cập đến cấp độ của module Python của bạn. Mô-đun logging được dùng với hệ thống phân cấp của các trình ghi log có tên khác nhau, do đó bạn có thể sử dụng một trình ghi log khác nhau cho từng module của bạn .

Ví dụ: bạn có thể đặt trình ghi bằng các trình ghi khác nhau có tên khác nhau và kết quả khác nhau:

logger1 = logging.getLogger("module_1") logger2 = logging.getLogger("module_2")  logger1.debug("Module 1 debugger") logger2.debug("Module 2 debugger") 
Output
DEBUG:module_1:Module 1 debugger DEBUG:module_2:Module 2 debugger

Bây giờ ta đã hiểu về cách sử dụng module logging để in thông báo ra console , hãy chuyển sang sử dụng module logging để in thông báo ra file .

Ghi thông điệp vào một file

Mục đích chính của module logging là ghi thông báo vào một file chứ không phải vào console . Việc lưu giữ một file tin nhắn cung cấp cho bạn dữ liệu theo thời gian mà bạn có thể tham khảo và định lượng để có thể thấy những thay đổi nào cần được thực hiện đối với mã của bạn .

Để bắt đầu đăng nhập vào một file , ta có thể sửa đổi phương thức logging.basicConfig() để bao gồm một tham số filename . Trong trường hợp này, hãy gọi tên file là test.log :

pizza.py
import logging  logging.basicConfig(filename="test.log", level=logging.DEBUG)   class Pizza():     def __init__(self, name, price):         self.name = name         self.price = price         logging.debug("Pizza created: {} (${})".format(self.name, self.price))      def make(self, quantity=1):         logging.debug("Made {} {} pizza(s)".format(quantity, self.name))      def eat(self, quantity=1):         logging.debug("Ate {} pizza(s)".format(quantity, self.name))  pizza_01 = Pizza("artichoke", 15) pizza_01.make() pizza_01.eat()  pizza_02 = Pizza("margherita", 12) pizza_02.make(2) pizza_02.eat()  

Đoạn mã ở trên giống như trong phần trước, ngoại trừ việc bây giờ ta đã thêm tên file cho log để in. Khi ta chạy mã bằng lệnh python pizza.py , ta sẽ có một file mới trong folder của ta có tên là test.log .

Hãy mở file test.log bằng nano (hoặc editor mà bạn chọn):

  • nano test.log

Khi file mở ra, ta sẽ thấy như sau:

test.log
DEBUG:root:Pizza created: artichoke ($15) DEBUG:root:Made 1 artichoke pizza(s) DEBUG:root:Ate 1 pizza(s) DEBUG:root:Pizza created: margherita ($12) DEBUG:root:Made 2 margherita pizza(s) DEBUG:root:Ate 1 pizza(s) 

Điều này tương tự như kết quả console mà ta đã gặp trong phần trước, ngoại trừ bây giờ nó nằm trong file test.log .

Hãy đóng file bằng CTRL + x và chuyển trở lại file pizza.py để ta có thể sửa đổi mã.

Ta sẽ giữ nguyên nhiều mã, nhưng sửa đổi các thông số trong hai version pizza, pizza_01pizza_02 :

pizza.py
import logging  logging.basicConfig(filename="test.log", level=logging.DEBUG)   class Pizza():     def __init__(self, name, price):         self.name = name         self.price = price         logging.debug("Pizza created: {} (${})".format(self.name, self.price))      def make(self, quantity=1):         logging.debug("Made {} {} pizza(s)".format(quantity, self.name))      def eat(self, quantity=1):         logging.debug("Ate {} pizza(s)".format(quantity, self.name))  # Modify the parameters of the pizza_01 object pizza_01 = Pizza("Sicilian", 18) pizza_01.make(5) pizza_01.eat(4)  # Modify the parameters of the pizza_02 object pizza_02 = Pizza("quattro formaggi", 16) pizza_02.make(2) pizza_02.eat(2)  

Với những thay đổi này, hãy chạy lại chương trình bằng lệnh python pizza.py .

Khi chương trình đã chạy, ta có thể mở lại file test.log của test.log bằng nano:

  • nano test.log

Khi xem xét file , ta sẽ thấy rằng một số dòng mới đã được thêm vào và các dòng trước đó từ lần cuối cùng chương trình chạy được giữ lại:

test.log
DEBUG:root:Pizza created: artichoke ($15) DEBUG:root:Made 1 artichoke pizza(s) DEBUG:root:Ate 1 pizza(s) DEBUG:root:Pizza created: margherita ($12) DEBUG:root:Made 2 margherita pizza(s) DEBUG:root:Ate 1 pizza(s) DEBUG:root:Pizza created: Sicilian ($18) DEBUG:root:Made 5 Sicilian pizza(s) DEBUG:root:Ate 4 pizza(s) DEBUG:root:Pizza created: quattro formaggi ($16) DEBUG:root:Made 2 quattro formaggi pizza(s) DEBUG:root:Ate 2 pizza(s) 

Mặc dù thông tin này chắc chắn hữu ích, nhưng ta có thể làm cho log nhiều thông tin hơn bằng cách thêm các thuộc tính LogRecord bổ sung. Trước hết, ta muốn thêm một dấu thời gian mà con người có thể đọc được để cho ta biết khi nào LogRecord được tạo.

Ta có thể thêm thuộc tính đó vào một tham số được gọi là format , tham chiếu nó như trong bảng với chuỗi %(asctime)s . Ngoài ra, để giữ tên cấp DEBUG , ta cần bao gồm chuỗi %(levelname)s và để giữ thông báo chuỗi mà ta yêu cầu trình ghi log in ra, ta sẽ bao gồm %(message)s . Mỗi thuộc tính này sẽ được phân tách bằng colon , như trong đoạn mã được thêm bên dưới.

pizza.py
import logging  logging.basicConfig(     filename="test.log",     level=logging.DEBUG,     format="%(asctime)s:%(levelname)s:%(message)s"     )   class Pizza():     def __init__(self, name, price):         self.name = name         self.price = price         logging.debug("Pizza created: {} (${})".format(self.name, self.price))      def make(self, quantity=1):         logging.debug("Made {} {} pizza(s)".format(quantity, self.name))      def eat(self, quantity=1):         logging.debug("Ate {} pizza(s)".format(quantity, self.name))  pizza_01 = Pizza("Sicilian", 18) pizza_01.make(5) pizza_01.eat(4)  pizza_02 = Pizza("quattro formaggi", 16) pizza_02.make(2) pizza_02.eat(2)  

Khi ta chạy mã ở trên với các thuộc tính được thêm vào bằng lệnh python pizza.py , ta sẽ nhận được các dòng mới được thêm vào file test.log của ta , bao gồm dấu thời gian có thể đọc được của con người ngoài tên cấp của DEBUG và liên kết thông báo được chuyển vào trình ghi dưới dạng chuỗi.

Output
DEBUG:root:Pizza created: Sicilian ($18) DEBUG:root:Made 5 Sicilian pizza(s) DEBUG:root:Ate 4 pizza(s) DEBUG:root:Pizza created: quattro formaggi ($16) DEBUG:root:Made 2 quattro formaggi pizza(s) DEBUG:root:Ate 2 pizza(s) 2017-05-01 16:28:54,593:DEBUG:Pizza created: Sicilian ($18) 2017-05-01 16:28:54,593:DEBUG:Made 5 Sicilian pizza(s) 2017-05-01 16:28:54,593:DEBUG:Ate 4 pizza(s) 2017-05-01 16:28:54,593:DEBUG:Pizza created: quattro formaggi ($16) 2017-05-01 16:28:54,593:DEBUG:Made 2 quattro formaggi pizza(s) 2017-05-01 16:28:54,593:DEBUG:Ate 2 pizza(s)

Tùy thuộc vào nhu cầu của bạn, bạn có thể cần sử dụng các thuộc tính LogRecord bổ sung trong mã của bạn để làm cho log file chương trình phù hợp với bạn.

Ghi log gỡ lỗi và các thông báo khác vào các file riêng biệt cung cấp cho bạn sự hiểu biết toàn diện về chương trình Python của bạn theo thời gian, cho bạn cơ hội khắc phục sự cố và sửa đổi mã của bạn theo cách được thông báo bởi công việc lịch sử được đưa vào chương trình, cũng như các sự kiện và giao dịch xảy ra.

Bảng-cấp quyền quyền độ ghi log

Là một nhà phát triển, bạn có thể quy định mức độ quan trọng của sự kiện được ghi lại trong trình ghi log bằng cách thêm mức độ nghiêm trọng. Các mức độ nghiêm trọng được hiển thị trong bảng dưới đây.

Các cấp độ ghi log về mặt kỹ thuật là các số nguyên (một hằng số) và tất cả chúng đều có số gia là 10, bắt đầu bằng NOTSET khởi tạo bộ ghi log ở giá trị số 0.

Bạn cũng có thể xác định các cấp độ của riêng mình so với các cấp độ được định nghĩa . Nếu bạn xác định một cấp có cùng giá trị số, bạn sẽ overrides tên được liên kết với giá trị đó.

Bảng bên dưới hiển thị các tên cấp độ khác nhau, giá trị số của chúng, hàm nào bạn có thể sử dụng để gọi cấp độ và cấp độ đó được sử dụng để làm gì.

Cấp độ Giá trị số Chức năng Đã từng
CRITICAL 50 logging.critical() Hiển thị một lỗi nghiêm trọng, chương trình có thể không thể tiếp tục chạy
ERROR 40 logging.error() Cho thấy một vấn đề nghiêm trọng hơn
WARNING 30 logging.warning() Cho biết điều gì đó bất ngờ đã xảy ra hoặc có thể xảy ra
INFO 20 logging.info() Xác nhận mọi thứ đang hoạt động như mong đợi
DEBUG 10 logging.debug() Chẩn đoán sự cố, hiển thị thông tin chi tiết

Mô-đun logging đặt mức mặc định là WARNING , vì vậy WARNING , ERRORCRITICAL đều sẽ được ghi lại theo mặc định. Trong ví dụ trên, ta đã sửa đổi cấu hình để bao gồm cấp DEBUG với mã sau:

logging.basicConfig(level=logging.DEBUG) 

Bạn có thể đọc thêm về các lệnh và cách làm việc với trình gỡ lỗi từ tài liệu logging chính thức .

Kết luận

Gỡ lỗi là một bước quan trọng của bất kỳ dự án phát triển phần mềm nào. Mô-đun logging là một phần của thư viện Python chuẩn, cung cấp khả năng theo dõi các sự kiện xảy ra trong khi phần mềm chạy và có thể xuất các sự kiện này ra một file log riêng biệt để cho phép bạn theo dõi những gì xảy ra trong khi mã của bạn chạy. Điều này cung cấp cho bạn cơ hội gỡ lỗi mã của bạn dựa trên việc hiểu các sự kiện khác nhau xảy ra khi chạy chương trình của bạn theo thời gian.


Tags:

Các tin liên quan

Cách gỡ lỗi Python bằng control panel tương tác
2017-04-27
Cách sử dụng trình gỡ lỗi Python
2017-04-25
Cách cài đặt Python 3 và thiết lập môi trường lập trình cục bộ trên CentOS 7
2017-04-20
Cách cài đặt Python 3 và thiết lập môi trường lập trình cục bộ trên Debian 8
2017-04-20
Cách cài đặt Python 3 và thiết lập môi trường lập trình cục bộ trên CentOS 7
2017-04-20
Cách áp dụng tính đa hình cho các lớp trong Python 3
2017-04-13
Hướng dẫn dự báo chuỗi thời gian với prophet bằng Python 3
2017-04-04
Hiểu các biến lớp và phiên bản trong Python 3
2017-03-27
Hướng dẫn Dự báo chuỗi thời gian với ARIMA bằng Python 3
2017-03-23
Cách tạo lớp và xác định đối tượng trong Python 3
2017-03-17