Bất kỳ ứng dụng sản xuất nào cũng có thể có một số hướng dẫn về cách thức và những gì cần được đăng nhập vào ứng dụng của bạn. Thông thường, những hướng dẫn này bắt nguồn từ các mẫu phổ biến trong ngành, chẳng hạn như “ghi nhật ký tất cả các trường hợp ngoại lệ”. Tuy nhiên, việc triển khai các hướng dẫn này được dành cho từng nhà phát triển và dẫn đến cùng một tập hợp các câu lệnh ghi nhật ký được lặp lại trong toàn bộ cơ sở mã. Chẳng hạn, để ghi nhật ký tất cả các ngoại lệ, bạn sẽ có một câu lệnh ghi nhật ký trong mỗi khối
>>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
6 ghi lại ngoại lệ và ghi nhật ký dưới cấp độ >>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
7. Nhưng câu lệnh ghi nhật ký cho cùng một kịch bản có thể khác nhau giữa các nhà phát triển do phong cách phát triển cá nhân của họ. Ngoài giờ, điều này dẫn đến việc đăng nhập bị phân mảnh và không nhất quán trong ứng dụng. Hơn nữa, các nhà phát triển có thể mắc lỗi và bỏ lỡ việc ghi nhật ký ở những nơi cần thiếtMột cách tiếp cận để giảm bớt vấn đề này là sử dụng tính năng trang trí của Python. Bài viết này sẽ cung cấp một cái nhìn tổng quan ngắn gọn về các bộ trang trí và trình bày cách tạo một bộ trang trí để trừu tượng hóa các câu lệnh ghi nhật ký phổ biến này. Bạn có thể đọc thêm về các công cụ trang trí và nhiều cách chúng có thể được sử dụng trong Tài liệu cơ bản tuyệt vời này về Công cụ trang trí Python
trang trí là gì
Trình trang trí là một chức năng nhận một chức năng khác và mở rộng hành vi của nó mà không sửa đổi nó một cách rõ ràng. Chúng còn được gọi là hàm bậc cao
Các chức năng của Python là công dân hạng nhất. Điều này có nghĩa là các hàm có thể được truyền dưới dạng đối số hoặc có thể là đối tượng của phép gán. Vì vậy, nếu bạn có một hàm
>>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
8, bạn có thể sử dụng nó như bất kỳ đối tượng nào khác và đi sâu vào các thuộc tính của nódef sum[a, b=10]:
return a+b
>>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
Vì các chức năng hoạt động giống như một đối tượng, bạn có thể gán
>>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
9 cho một chức năng khác. Sau đó, gọi >>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
9 sẽ gọi hàm khác này thay vì hàm mà chúng ta đã xác định trước đó. Các bộ trang trí sử dụng hành vi này bằng cách gán cho >>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
9 một chức năng mới lấy >>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
9 làm tham số và bao bọc một số logic bổ sung xung quanh nó, do đó mở rộng nó mà không sửa đổi chính chức năng đódef my_decorator[func]:
def wrapper[*args, **kwargs]:
# do something before `sum`
result = func[*args, **kwargs]
# do something after `sum`
return result
return wrapper
sum = my_decorator[sum]
>>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
0Mẫu này phổ biến đến mức Python có một đường cú pháp để trang trí một chức năng. Vì vậy, thay vì
def my_decorator[func]:
def wrapper[*args, **kwargs]:
# do something before `sum`
result = func[*args, **kwargs]
# do something after `sum`
return result
return wrapper
sum = my_decorator[sum]
3, chúng ta có thể sử dụng ký hiệu def my_decorator[func]:
def wrapper[*args, **kwargs]:
# do something before `sum`
result = func[*args, **kwargs]
# do something after `sum`
return result
return wrapper
sum = my_decorator[sum]
4 trên phương thức >>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
9 như thế này ->>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
4Trình trang trí nhật ký
Chúng ta sẽ tạo một trình trang trí xử lý hai tình huống ghi nhật ký phổ biến - ghi nhật ký ngoại lệ dưới dạng LỖI và đối số phương thức ghi nhật ký dưới dạng nhật ký GỠ LỖI
Hãy bắt đầu bằng cách nắm bắt các ngoại lệ và ghi nhật ký bằng thư viện python
def my_decorator[func]:
def wrapper[*args, **kwargs]:
# do something before `sum`
result = func[*args, **kwargs]
# do something after `sum`
return result
return wrapper
sum = my_decorator[sum]
6>>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
6>>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
7Ngoài việc thiết lập
def my_decorator[func]:
def wrapper[*args, **kwargs]:
# do something before `sum`
result = func[*args, **kwargs]
# do something after `sum`
return result
return wrapper
sum = my_decorator[sum]
7, chúng tôi cũng đã sử dụng @funcools. kết thúc tốt đẹp trang trí. Trình trang trí def my_decorator[func]:
def wrapper[*args, **kwargs]:
# do something before `sum`
result = func[*args, **kwargs]
# do something after `sum`
return result
return wrapper
sum = my_decorator[sum]
8 cập nhật chức năng def my_decorator[func]:
def wrapper[*args, **kwargs]:
# do something before `sum`
result = func[*args, **kwargs]
# do something after `sum`
return result
return wrapper
sum = my_decorator[sum]
9 để trông giống như >>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
00. Trình trang trí >>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
01 của chúng tôi hiện có thể được sử dụng trên bất kỳ chức năng nào để nắm bắt mọi ngoại lệ từ chức năng được bao bọc và ghi lại nó một cách nhất quánVì hàm
def my_decorator[func]:
def wrapper[*args, **kwargs]:
# do something before `sum`
result = func[*args, **kwargs]
# do something after `sum`
return result
return wrapper
sum = my_decorator[sum]
9 chấp nhận tất cả các đối số [>>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
03], trình trang trí >>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
01 có thể được mở rộng để nắm bắt tất cả các tham số được truyền cho hàm được trang trí. Chúng ta có thể làm điều này bằng cách lặp qua args và kwargs và nối chúng để tạo thành thông báo chuỗi để ghi nhật ký>>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
6>>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
7Chúng tôi ghi nhật ký các tham số ở mức GỠ LỖI vì chúng tôi không muốn nhật ký của mình lộn xộn với tất cả các đối số chức năng. Ghi nhật ký gỡ lỗi có thể được bật trên hệ thống của chúng tôi khi cần thiết. Hãy nhớ rằng điều này sẽ ghi tất cả các giá trị đối số vào nhật ký bao gồm mọi dữ liệu PII hoặc bí mật
Trình trang trí ghi nhật ký cơ bản này có vẻ tốt và đã thực hiện những gì chúng tôi đặt ra ban đầu để đạt được. Miễn là một phương thức được trang trí bằng trình trang trí
>>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
01, chúng tôi sẽ ghi lại bất kỳ ngoại lệ nào được đưa ra bên trong nó và tất cả các đối số được truyền cho nóTuy nhiên, trong một dự án thực tế, bản thân
def my_decorator[func]:
def wrapper[*args, **kwargs]:
# do something before `sum`
result = func[*args, **kwargs]
# do something after `sum`
return result
return wrapper
sum = my_decorator[sum]
7 có thể được trừu tượng hóa thành lớp riêng của nó để khởi tạo trình ghi nhật ký dựa trên cấu hình nhất định [chẳng hạn như đẩy nhật ký lên đám mây chìm]. Trong trường hợp này, thật vô ích khi đăng nhập vào bảng điều khiển bằng cách tạo trình ghi nhật ký của riêng chúng tôi trong trình trang trí >>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
01. Chúng tôi cần một cách để chuyển một def my_decorator[func]:
def wrapper[*args, **kwargs]:
# do something before `sum`
result = func[*args, **kwargs]
# do something after `sum`
return result
return wrapper
sum = my_decorator[sum]
7 hiện có vào trình trang trí của chúng tôi khi chạy. Để làm điều này, chúng ta có thể mở rộng trình trang trí >>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
01 để chấp nhận def my_decorator[func]:
def wrapper[*args, **kwargs]:
# do something before `sum`
result = func[*args, **kwargs]
# do something after `sum`
return result
return wrapper
sum = my_decorator[sum]
7 làm đối sốĐể bắt chước kịch bản này, chúng ta sẽ bắt đầu với việc có một lớp tạo bộ ghi nhật ký cho chúng ta. Bây giờ chúng ta sẽ tạo bộ ghi cơ bản nhưng bạn có thể tưởng tượng lớp cấu hình hành vi của bộ ghi theo yêu cầu
def sum[a, b=10]:
return a+b
4Vì tại thời điểm viết trình trang trí, chúng tôi không biết liệu chức năng cơ bản sẽ chuyển cho chúng tôi
>>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
41 hoặc >>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
42 hay không có trình ghi nào cả, trình trang trí chung của chúng tôi sẽ có thể xử lý tất cả chúng>>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
0Đoạn mã trên trông khá đáng sợ nhưng hãy để tôi tóm tắt nó. Trình trang trí
>>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
01 hiện xử lý ba tình huống khác nhau -Không có logger nào được thông qua. Đây là kịch bản tương tự những gì chúng tôi đã làm cho đến trước đó. Trình trang trí được sử dụng đơn giản như câu lệnh
01 ở đầu hàm. Trong trường hợp này, decorator lấy một logger bằng cách gọi phương thức>>> sum >>> sum.__code__.co_varnames # Names of local variables ['a', 'b']
45 và sử dụng nó cho phần còn lại của phương thức>>> sum >>> sum.__code__.co_varnames # Names of local variables ['a', 'b']
41 được thông qua. Trình trang trí>>> sum >>> sum.__code__.co_varnames # Names of local variables ['a', 'b']
01 của chúng tôi hiện có thể chấp nhận phiên bản của>>> sum >>> sum.__code__.co_varnames # Names of local variables ['a', 'b']
41 làm đối số. Sau đó, nó có thể gọi phương thức>>> sum >>> sum.__code__.co_varnames # Names of local variables ['a', 'b']
49 để tạo bộ ghi nhật ký lồng nhau và sử dụng phần còn lại của nó>>> sum >>> sum.__code__.co_varnames # Names of local variables ['a', 'b']
>>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
1
42 được thông qua. Trong trường hợp thứ ba này, chúng ta có thể chuyển chính bộ ghi nhật ký thay vì chuyển lớp>>> sum >>> sum.__code__.co_varnames # Names of local variables ['a', 'b']
41>>> sum >>> sum.__code__.co_varnames # Names of local variables ['a', 'b']
>>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
2Chúng tôi vẫn chưa hoàn thành. Ngay cả ở dạng hiện tại, trình trang trí nhật ký của chúng tôi bị hạn chế. Một hạn chế là chúng ta phải có sẵn
def my_decorator[func]:
def wrapper[*args, **kwargs]:
# do something before `sum`
result = func[*args, **kwargs]
# do something after `sum`
return result
return wrapper
sum = my_decorator[sum]
7 hoặc >>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
41 trước phương pháp mà chúng ta muốn trang trí. Nói cách khác, tham chiếu đến logger phải tồn tại trước khi phương thức tồn tại. Điều này có thể hoạt động trong trường hợp hàm mục tiêu là một phần của lớp và phương thức lớp >>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
64 có thể khởi tạo trình ghi, nhưng nó sẽ không hoạt động với các hàm bên ngoài ngữ cảnh của lớp. Trong nhiều ứng dụng trong thế giới thực, chúng tôi sẽ không có từng mô-đun hoặc chức năng tạo trình ghi nhật ký của riêng chúng. Thay vào đó, chúng tôi có thể muốn chuyển bộ ghi nhật ký cho chính chức năng đó. def my_decorator[func]:
def wrapper[*args, **kwargs]:
# do something before `sum`
result = func[*args, **kwargs]
# do something after `sum`
return result
return wrapper
sum = my_decorator[sum]
7 hoặc >>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
41 sẽ là phần phụ thuộc được đưa vào các phương thức xuôi dòng. Nói cách khác, một chức năng có thể có bộ ghi được truyền cho nó trong tham số của nóNhưng nếu hàm là một phần của lớp thì
def my_decorator[func]:
def wrapper[*args, **kwargs]:
# do something before `sum`
result = func[*args, **kwargs]
# do something after `sum`
return result
return wrapper
sum = my_decorator[sum]
7 sẽ được đưa vào chính lớp đó chứ không phải vào mọi phương thức của lớp. Trong trường hợp này, chúng tôi muốn sử dụng bộ ghi có sẵn cho lớp của chúng tôi để thay thếVì vậy, mục tiêu của chúng tôi là nắm bắt
def my_decorator[func]:
def wrapper[*args, **kwargs]:
# do something before `sum`
result = func[*args, **kwargs]
# do something after `sum`
return result
return wrapper
sum = my_decorator[sum]
7 được truyền dưới dạng đối số cho hàm được trang trí hoặc được truyền cho hàm tạo lớp của hàm được trang trí của chúng tôi và sử dụng nó để ghi nhật ký từ chính trình trang trí. Bằng cách này, trình trang trí của chúng tôi có thể được tách rời hoàn toàn khỏi chính trình ghi nhật ký và sẽ sử dụng bất kỳ trình ghi nhật ký nào có sẵn cho phương thức cơ bản khi chạyĐể làm điều này, chúng tôi sẽ lặp lại đối số
>>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
69 và >>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
70 và kiểm tra xem chúng tôi có nhận được def my_decorator[func]:
def wrapper[*args, **kwargs]:
# do something before `sum`
result = func[*args, **kwargs]
# do something after `sum`
return result
return wrapper
sum = my_decorator[sum]
7 trong bất kỳ đối số nào không. Để kiểm tra xem hàm có phải là một phần của lớp hay không, chúng ta có thể kiểm tra xem đối số đầu tiên của >>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
69 có thuộc tính >>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
73 hay không. Nếu đối số đầu tiên có thuộc tính >>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
73, chúng tôi sẽ lặp lại trên >>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
75 và kiểm tra xem một trong những giá trị này có phải là bộ ghi của chúng tôi không. Cuối cùng nếu không có gì hoạt động, chúng tôi sẽ mặc định là phương thức >>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
45>>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
3Trình trang trí ở trên đủ chung để hoạt động cho 2 kịch bản khác ngoài 3 kịch bản mà chúng ta đã thảo luận trước đây -
7 hoặcdef my_decorator[func]: def wrapper[*args, **kwargs]: # do something before `sum` result = func[*args, **kwargs] # do something after `sum` return result return wrapper sum = my_decorator[sum]
41 được chuyển sang phương thức trang trí>>> sum >>> sum.__code__.co_varnames # Names of local variables ['a', 'b']
>>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
4
7 hoặcdef my_decorator[func]: def wrapper[*args, **kwargs]: # do something before `sum` result = func[*args, **kwargs] # do something after `sum` return result return wrapper sum = my_decorator[sum]
41 được truyền cho phương thức lớp>>> sum >>> sum.__code__.co_varnames # Names of local variables ['a', 'b']
64 lưu trữ chức năng được trang trí>>> sum >>> sum.__code__.co_varnames # Names of local variables ['a', 'b']
>>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
5Một điều bổ sung mà chúng tôi đã làm là bọc tất cả mã trước khi gọi hàm được trang trí
>>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
00 trong một khối >>> sum
>>> sum.__code__.co_varnames # Names of local variables
['a', 'b']
63. Chúng tôi không muốn việc thực thi thất bại do sự cố khi ghi nhật ký ngay cả trước khi chức năng mục tiêu được gọi. Trong mọi trường hợp, logic ghi nhật ký của chúng tôi sẽ gây ra lỗi trong hệ thốngSự kết luậnTrình trang trí ở trên là một điểm khởi đầu tốt và có thể được mở rộng hoặc đơn giản hóa theo yêu cầu. Nó làm giảm khả năng bỏ lỡ việc ghi nhật ký ngoại lệ và chuẩn hóa các thông báo lỗi trên toàn ứng dụng