logging.config --- Cấu hình ghi nhật ký¶
Source code: Lib/logging/config.py
Phần này mô tả API để định cấu hình mô-đun ghi nhật ký.
Chức năng cấu hình¶
Các chức năng sau cấu hình mô-đun ghi nhật ký. Chúng nằm trong mô-đun logging.config. Việc sử dụng chúng là tùy chọn --- bạn có thể định cấu hình mô-đun ghi nhật ký bằng cách sử dụng các hàm này hoặc bằng cách thực hiện lệnh gọi đến API chính (được xác định trong chính logging) và xác định các trình xử lý được khai báo trong logging hoặc logging.handlers.
- logging.config.dictConfig(config)¶
Lấy cấu hình ghi nhật ký từ từ điển. Nội dung của từ điển này được mô tả trong Lược đồ từ điển cấu hình bên dưới.
Nếu gặp lỗi trong quá trình cấu hình, chức năng này sẽ đưa ra
ValueError,TypeError,AttributeErrorhoặcImportErrorvới thông báo mô tả phù hợp. Sau đây là danh sách (có thể không đầy đủ) các điều kiện sẽ gây ra lỗi:Một
levelkhông phải là một chuỗi hoặc là một chuỗi không tương ứng với mức ghi nhật ký thực tế.Giá trị
propagatekhông phải là giá trị boolean.Một id không có đích tương ứng.
Đã tìm thấy id trình xử lý không tồn tại trong cuộc gọi gia tăng.
Tên nhật ký không hợp lệ.
Không có khả năng giải quyết một đối tượng bên trong hoặc bên ngoài.
Việc phân tích cú pháp được thực hiện bởi lớp
DictConfigurator, hàm tạo của nó được truyền từ điển được sử dụng để cấu hình và có phương thứcconfigure(). Mô-đunlogging.configcó thuộc tính có thể gọi đượcdictConfigClass, ban đầu được đặt thànhDictConfigurator. Bạn có thể thay thế giá trị củadictConfigClassbằng cách triển khai phù hợp của riêng bạn.dictConfig()gọidictConfigClasschuyển qua từ điển đã chỉ định, sau đó gọi phương thứcconfigure()trên đối tượng được trả về để đặt cấu hình có hiệu lực:def dictConfig(cấu hình): dictConfigClass(config).configure()
Ví dụ: một lớp con của
DictConfiguratorcó thể gọiDictConfigurator.__init__()bằng__init__()của chính nó, sau đó thiết lập các tiền tố tùy chỉnh có thể sử dụng được trong lệnh gọiconfigure()tiếp theo.dictConfigClasssẽ được liên kết với lớp con mới này và sau đódictConfig()có thể được gọi chính xác như ở trạng thái mặc định, chưa tùy chỉnh.Added in version 3.2.
- logging.config.fileConfig(fname, defaults=None, disable_existing_loggers=True, encoding=None)¶
Đọc cấu hình ghi nhật ký từ tệp
configparser-format. Định dạng của tệp phải như được mô tả trong Định dạng tập tin cấu hình. Chức năng này có thể được gọi nhiều lần từ một ứng dụng, cho phép người dùng cuối chọn từ nhiều cấu hình có sẵn khác nhau (nếu nhà phát triển cung cấp cơ chế trình bày các lựa chọn và tải cấu hình đã chọn).Nó sẽ tăng
FileNotFoundErrornếu tệp không tồn tại vàRuntimeErrornếu tệp không hợp lệ hoặc trống.- Tham số:
fname -- Tên tệp hoặc đối tượng giống tệp hoặc phiên bản bắt nguồn từ
RawConfigParser. Nếu một phiên bản có nguồn gốc từRawConfigParserđược thông qua, nó sẽ được sử dụng nguyên trạng. Nếu không,ConfigParsersẽ được khởi tạo và cấu hình được nó đọc từ đối tượng được truyền trongfname. Nếu phương thức đó có phương thứcreadline()thì nó được coi là một đối tượng giống như tệp và được đọc bằngread_file(); mặt khác, nó được coi là tên tệp và được chuyển tớiread().defaults -- Các giá trị mặc định được chuyển tới
ConfigParsercó thể được chỉ định trong đối số này.disable_existing_loggers -- Nếu được chỉ định là
False, các trình ghi nhật ký tồn tại khi cuộc gọi này được thực hiện sẽ được bật. Giá trị mặc định làTruevì điều này cho phép hoạt động cũ theo cách tương thích ngược. Hành vi này nhằm vô hiệu hóa mọi trình ghi nhật ký không phải gốc hiện có trừ khi chúng hoặc tổ tiên của chúng được đặt tên rõ ràng trong cấu hình ghi nhật ký.encoding -- Mã hóa được sử dụng để mở tệp khi fname là tên tệp.
Thay đổi trong phiên bản 3.4: Một phiên bản của lớp con của
RawConfigParserhiện được chấp nhận làm giá trị chofname. Điều này tạo điều kiện thuận lợi:Sử dụng tệp cấu hình trong đó cấu hình ghi nhật ký chỉ là một phần của cấu hình ứng dụng tổng thể.
Việc sử dụng cấu hình được đọc từ một tệp, sau đó được sửa đổi bởi ứng dụng đang sử dụng (ví dụ: dựa trên các tham số dòng lệnh hoặc các khía cạnh khác của môi trường thời gian chạy) trước khi được chuyển tới
fileConfig.
Thay đổi trong phiên bản 3.10: Đã thêm tham số encoding.
Thay đổi trong phiên bản 3.12: Một ngoại lệ sẽ được đưa ra nếu tệp được cung cấp không tồn tại hoặc không hợp lệ hoặc trống.
- logging.config.listen(port=DEFAULT_LOGGING_CONFIG_PORT, verify=None)¶
Khởi động máy chủ socket trên cổng được chỉ định và lắng nghe cấu hình mới. Nếu không có cổng nào được chỉ định,
DEFAULT_LOGGING_CONFIG_PORTmặc định của mô-đun sẽ được sử dụng. Cấu hình ghi nhật ký sẽ được gửi dưới dạng tệp phù hợp đểdictConfig()hoặcfileConfig()xử lý. Trả về một phiên bảnThreadmà bạn có thể gọistart()để khởi động máy chủ và bạn có thể gọijoin()khi thích hợp. Để dừng máy chủ, hãy gọistopListening().Đối số
verify, nếu được chỉ định, phải là một đối số có thể gọi được để xác minh xem các byte nhận được qua ổ cắm có hợp lệ hay không và có nên được xử lý hay không. Điều này có thể được thực hiện bằng cách mã hóa và/hoặc ký những gì được gửi qua ổ cắm, sao choverifycó thể gọi được có thể thực hiện xác minh và/hoặc giải mã chữ ký. Có thể gọiverifyvới một đối số duy nhất - các byte nhận được qua ổ cắm - và sẽ trả về các byte cần xử lý hoặcNoneđể chỉ ra rằng các byte này sẽ bị loại bỏ. Các byte được trả về có thể giống với các byte được truyền (ví dụ: khi chỉ xác minh được thực hiện) hoặc chúng có thể hoàn toàn khác nhau (có thể nếu quá trình giải mã được thực hiện).Để gửi cấu hình đến ổ cắm, hãy đọc tệp cấu hình và gửi nó đến ổ cắm dưới dạng một chuỗi byte trước chuỗi có độ dài bốn byte được đóng gói ở dạng nhị phân bằng
struct.pack('>L', n).Ghi chú
Vì các phần của cấu hình được chuyển qua
eval()nên việc sử dụng chức năng này có thể khiến người dùng gặp rủi ro bảo mật. Mặc dù hàm này chỉ liên kết với ổ cắm trênlocalhostvà do đó không chấp nhận kết nối từ các máy từ xa, nhưng có những trường hợp mã không đáng tin cậy có thể chạy trong tài khoản của quy trình gọilisten(). Cụ thể, nếu quá trình gọilisten()chạy trên một máy nhiều người dùng mà người dùng không thể tin tưởng lẫn nhau thì người dùng độc hại có thể sắp xếp để chạy mã tùy ý trong quy trình của nạn nhân, chỉ bằng cách kết nối với ổ cắmlisten()của nạn nhân và gửi cấu hình chạy bất kỳ mã nào mà kẻ tấn công muốn thực thi trong quy trình của nạn nhân. Điều này đặc biệt dễ thực hiện nếu sử dụng cổng mặc định nhưng không khó ngay cả khi sử dụng cổng khác. Để tránh nguy cơ điều này xảy ra, hãy sử dụng đối sốverifycholisten()để ngăn áp dụng các cấu hình không được nhận dạng.Thay đổi trong phiên bản 3.4: Đối số
verifyđã được thêm vào.Ghi chú
Nếu bạn muốn gửi cấu hình tới trình nghe mà không tắt trình ghi nhật ký hiện có, bạn sẽ cần sử dụng định dạng JSON cho cấu hình, định dạng này sẽ sử dụng
dictConfig()cho cấu hình. Phương pháp này cho phép bạn chỉ địnhdisable_existing_loggerslàFalsetrong cấu hình bạn gửi.
Cân nhắc về bảo mật¶
Chức năng cấu hình ghi nhật ký cố gắng mang lại sự thuận tiện và một phần điều này được thực hiện bằng cách cung cấp khả năng chuyển đổi văn bản trong tệp cấu hình thành đối tượng Python được sử dụng trong cấu hình ghi nhật ký - ví dụ: như được mô tả trong Đối tượng do người dùng xác định. Tuy nhiên, các cơ chế tương tự này (nhập các lệnh gọi từ các mô-đun do người dùng xác định và gọi chúng bằng các tham số từ cấu hình) có thể được sử dụng để gọi bất kỳ mã nào bạn thích và vì lý do này, bạn nên xử lý các tệp cấu hình từ các nguồn không đáng tin cậy bằng extreme caution và tự thỏa mãn rằng không có gì xấu có thể xảy ra nếu bạn tải chúng, trước khi thực sự tải chúng.
Lược đồ từ điển cấu hình¶
Việc mô tả cấu hình ghi nhật ký yêu cầu liệt kê các đối tượng khác nhau cần tạo và các kết nối giữa chúng; ví dụ: bạn có thể tạo một trình xử lý có tên là 'console' và sau đó nói rằng trình ghi nhật ký có tên là 'startup' sẽ gửi thông báo của nó đến trình xử lý 'console'. Các đối tượng này không bị giới hạn ở những đối tượng được mô-đun logging cung cấp vì bạn có thể viết lớp trình định dạng hoặc trình xử lý của riêng mình. Các tham số cho các lớp này cũng có thể cần bao gồm các đối tượng bên ngoài như sys.stderr. Cú pháp mô tả các đối tượng và kết nối này được xác định trong Kết nối đối tượng bên dưới.
Chi tiết lược đồ từ điển¶
Từ điển được truyền tới dictConfig() phải chứa các khóa sau:
version - được đặt thành giá trị số nguyên đại diện cho phiên bản lược đồ. Giá trị hợp lệ duy nhất hiện tại là 1, nhưng việc có khóa này cho phép lược đồ phát triển trong khi vẫn duy trì khả năng tương thích ngược.
Tất cả các khóa khác là tùy chọn, nhưng nếu có thì chúng sẽ được hiểu như mô tả bên dưới. Trong tất cả các trường hợp bên dưới có đề cập đến 'lệnh định cấu hình', nó sẽ được kiểm tra khóa '()' đặc biệt để xem liệu có cần khởi tạo tùy chỉnh hay không. Nếu vậy, cơ chế được mô tả trong Đối tượng do người dùng xác định bên dưới sẽ được sử dụng để tạo một phiên bản; mặt khác, ngữ cảnh được sử dụng để xác định nội dung cần khởi tạo.
formatters - giá trị tương ứng sẽ là một lệnh trong đó mỗi khóa là id trình định dạng và mỗi giá trị là một lệnh mô tả cách định cấu hình phiên bản
Formattertương ứng.Lệnh cấu hình được tìm kiếm các khóa tùy chọn sau tương ứng với các đối số được truyền để tạo đối tượng
Formatter:formatdatefmtstylevalidate(kể từ phiên bản >=3.8)defaults(kể từ phiên bản >=3.12)
Khóa
classtùy chọn cho biết tên lớp của trình định dạng (dưới dạng mô-đun có dấu chấm và tên lớp). Các đối số khởi tạo giống như đối vớiFormatter, do đó khóa này hữu ích nhất để khởi tạo một lớp con tùy chỉnh củaFormatter. Ví dụ: lớp thay thế có thể trình bày các dấu vết ngoại lệ ở định dạng mở rộng hoặc cô đọng. Nếu trình định dạng của bạn yêu cầu các phím cấu hình khác hoặc bổ sung, bạn nên sử dụng Đối tượng do người dùng xác định.filters - giá trị tương ứng sẽ là một dict trong đó mỗi khóa là id bộ lọc và mỗi giá trị là một dict mô tả cách định cấu hình phiên bản Bộ lọc tương ứng.
Lệnh cấu hình được tìm kiếm khóa
name(mặc định là chuỗi trống) và khóa này được sử dụng để xây dựng một phiên bảnlogging.Filter.handlers - giá trị tương ứng sẽ là một lệnh trong đó mỗi khóa là một id trình xử lý và mỗi giá trị là một lệnh mô tả cách định cấu hình phiên bản Trình xử lý tương ứng.
Lệnh cấu hình được tìm kiếm các khóa sau:
class(bắt buộc). Đây là tên đầy đủ của lớp xử lý.level(tùy chọn). Trình độ của người xử lý.formatter(tùy chọn). Id của trình định dạng cho trình xử lý này.filters(tùy chọn). Danh sách id của bộ lọc cho trình xử lý này.Thay đổi trong phiên bản 3.11:
filterscó thể lấy các phiên bản bộ lọc ngoài id.
Tất cả các khóa other được chuyển qua dưới dạng đối số từ khóa tới hàm tạo của trình xử lý. Ví dụ: đưa ra đoạn trích:
người xử lý: bảng điều khiển: lớp: log.StreamHandler định dạng: ngắn gọn cấp độ: INFO bộ lọc: [allow_foo] luồng: ext://sys.stdout tập tin: lớp: log.handlers.RotatingFileHandler định dạng: chính xác tên tập tin: logconfig.log maxByte: 1024 số lượng dự phòng: 3
trình xử lý có id
consoleđược khởi tạo dưới dạnglogging.StreamHandler, sử dụngsys.stdoutlàm luồng cơ bản. Trình xử lý có idfileđược khởi tạo dưới dạnglogging.handlers.RotatingFileHandlervới các đối số từ khóafilename='logconfig.log', maxBytes=1024, backupCount=3.loggers - giá trị tương ứng sẽ là một dict trong đó mỗi khóa là một tên logger và mỗi giá trị là một dict mô tả cách định cấu hình phiên bản Logger tương ứng.
Lệnh cấu hình được tìm kiếm các khóa sau:
level(tùy chọn). Cấp độ của logger.propagate(tùy chọn). Cài đặt lan truyền của logger.filters(tùy chọn). Danh sách id của bộ lọc cho trình ghi nhật ký này.Thay đổi trong phiên bản 3.11:
filterscó thể lấy các phiên bản bộ lọc ngoài id.handlers(tùy chọn). Danh sách id của trình xử lý cho trình ghi nhật ký này.
Các logger được chỉ định sẽ được cấu hình theo cấp độ, mức độ lan truyền, bộ lọc và trình xử lý được chỉ định.
root - đây sẽ là cấu hình cho trình ghi nhật ký gốc. Việc xử lý cấu hình sẽ giống như đối với bất kỳ trình ghi nhật ký nào, ngoại trừ cài đặt
propagatesẽ không được áp dụng.incremental - liệu cấu hình có được hiểu là tăng dần so với cấu hình hiện tại hay không. Giá trị này mặc định là
False, có nghĩa là cấu hình được chỉ định sẽ thay thế cấu hình hiện tại có cùng ngữ nghĩa như được sử dụng bởifileConfig()API hiện có.Nếu giá trị được chỉ định là
True, cấu hình sẽ được xử lý như mô tả trong phần trên Cấu hình gia tăng.disable_existing_loggers - liệu có bất kỳ trình ghi nhật ký không phải root hiện có nào bị vô hiệu hóa hay không. Cài đặt này phản ánh tham số cùng tên trong
fileConfig(). Nếu vắng mặt, tham số này mặc định làTrue. Giá trị này bị bỏ qua nếu incremental làTrue.
Cấu hình gia tăng¶
Rất khó để cung cấp sự linh hoạt hoàn toàn cho cấu hình gia tăng. Ví dụ: vì các đối tượng như bộ lọc và trình định dạng là ẩn danh nên khi cấu hình được thiết lập, không thể tham chiếu đến các đối tượng ẩn danh đó khi tăng cấu hình.
Hơn nữa, không có trường hợp thuyết phục nào cho việc tự ý thay đổi biểu đồ đối tượng của trình ghi nhật ký, trình xử lý, bộ lọc, trình định dạng trong thời gian chạy, sau khi cấu hình được thiết lập; mức độ chi tiết của trình ghi nhật ký và trình xử lý có thể được kiểm soát chỉ bằng cách cài đặt các mức (và, trong trường hợp trình ghi nhật ký, cờ truyền bá). Việc thay đổi đồ thị đối tượng một cách tùy ý theo cách an toàn là vấn đề khó khăn trong môi trường đa luồng; mặc dù không phải là không thể, nhưng những lợi ích không xứng đáng với sự phức tạp mà nó mang lại khi triển khai.
Do đó, khi có khóa incremental của lệnh cấu hình và là True, hệ thống sẽ hoàn toàn bỏ qua mọi mục nhập formatters và filters và chỉ xử lý cài đặt level trong các mục nhập handlers cũng như cài đặt level và propagate trong các mục nhập loggers và root.
Việc sử dụng một giá trị trong lệnh cấu hình cho phép các cấu hình được gửi qua dây dưới dạng các lệnh được chọn đến trình nghe ổ cắm. Do đó, mức độ chi tiết của nhật ký của một ứng dụng chạy dài có thể được thay đổi theo thời gian mà không cần phải dừng và khởi động lại ứng dụng.
Kết nối đối tượng¶
Lược đồ mô tả một tập hợp các đối tượng ghi nhật ký - trình ghi nhật ký, trình xử lý, trình định dạng, bộ lọc - được kết nối với nhau trong biểu đồ đối tượng. Vì vậy, lược đồ cần thể hiện các kết nối giữa các đối tượng. Ví dụ: giả sử sau khi được định cấu hình, một trình ghi nhật ký cụ thể sẽ gắn vào nó một trình xử lý cụ thể. Với mục đích của cuộc thảo luận này, chúng ta có thể nói rằng trình ghi nhật ký đại diện cho nguồn và trình xử lý là đích của kết nối giữa hai bên. Tất nhiên, trong các đối tượng được định cấu hình, điều này được thể hiện bằng trình ghi nhật ký có tham chiếu đến trình xử lý. Trong lệnh cấu hình, điều này được thực hiện bằng cách cung cấp cho mỗi đối tượng đích một id xác định rõ ràng, sau đó sử dụng id trong cấu hình của đối tượng nguồn để chỉ ra rằng có kết nối tồn tại giữa nguồn và đối tượng đích với id đó.
Vì vậy, ví dụ: hãy xem xét đoạn mã YAML sau:
trình định dạng:
ngắn gọn:
# configuration cho trình định dạng có id 'tóm tắt' ở đây
chính xác:
# configuration cho trình định dạng có id 'chính xác' ở đây
người xử lý:
h1: #This là id
# configuration của trình xử lý có id 'h1' ở đây
định dạng: ngắn gọn
h2: #This là một id khác
# configuration của trình xử lý có id 'h2' ở đây
định dạng: chính xác
người khai thác gỗ:
foo.bar.baz:
Cấu hình # other cho logger 'foo.bar.baz'
trình xử lý: [h1, h2]
(Lưu ý: YAML được sử dụng ở đây vì nó dễ đọc hơn một chút so với dạng nguồn Python tương đương cho từ điển.)
Id cho trình ghi nhật ký là tên trình ghi nhật ký sẽ được sử dụng theo chương trình để lấy tham chiếu đến các trình ghi nhật ký đó, ví dụ: foo.bar.baz. Id cho Trình định dạng và Bộ lọc có thể là bất kỳ giá trị chuỗi nào (chẳng hạn như brief, precise ở trên) và chúng mang tính tạm thời, trong đó chúng chỉ có ý nghĩa để xử lý từ điển cấu hình và được sử dụng để xác định kết nối giữa các đối tượng và không được duy trì ở bất kỳ đâu khi lệnh gọi cấu hình hoàn tất.
Đoạn mã trên chỉ ra rằng trình ghi nhật ký có tên foo.bar.baz phải có hai trình xử lý gắn liền với nó, được mô tả bởi id trình xử lý h1 và h2. Trình định dạng cho h1 được mô tả bởi id brief và trình định dạng cho h2 được mô tả bởi id precise.
Đối tượng do người dùng xác định¶
Lược đồ hỗ trợ các đối tượng do người dùng xác định cho trình xử lý, bộ lọc và trình định dạng. (Trình ghi nhật ký không cần phải có các loại khác nhau cho các phiên bản khác nhau, vì vậy không có hỗ trợ nào trong lược đồ cấu hình này cho các lớp trình ghi nhật ký do người dùng định nghĩa.)
Các đối tượng được cấu hình sẽ được mô tả bằng từ điển mô tả chi tiết cấu hình của chúng. Ở một số nơi, hệ thống ghi nhật ký sẽ có thể suy ra từ ngữ cảnh cách khởi tạo một đối tượng, nhưng khi một đối tượng do người dùng xác định được khởi tạo, hệ thống sẽ không biết cách thực hiện việc này. Để cung cấp sự linh hoạt hoàn toàn cho việc khởi tạo đối tượng do người dùng xác định, người dùng cần cung cấp một 'nhà máy' - một lệnh gọi được gọi bằng từ điển cấu hình và trả về đối tượng được khởi tạo. Điều này được báo hiệu bằng một đường dẫn nhập tuyệt đối vào nhà máy được cung cấp theo khóa đặc biệt '()'. Đây là một ví dụ cụ thể:
trình định dạng:
ngắn gọn:
định dạng: '%(tin nhắn)s'
mặc định:
định dạng: '%(asctime)s %(levelname)-8s %(name)-15s %(message)s'
datefmt: '%Y-%m-%d %H:%M:%S'
tùy chỉnh:
(): my.package.customFormatterFactory
thanh: baz
thư rác: 99,9
trả lời: 42
Đoạn mã YAML ở trên xác định ba trình định dạng. Đầu tiên, với id brief, là phiên bản logging.Formatter tiêu chuẩn với chuỗi định dạng được chỉ định. Thứ hai, với id default, có định dạng dài hơn và cũng xác định rõ ràng định dạng thời gian và sẽ dẫn đến logging.Formatter được khởi tạo với hai chuỗi định dạng đó. Được hiển thị ở dạng nguồn Python, trình định dạng brief và default có các từ điển phụ cấu hình:
{
'định dạng' : '%(tin nhắn)s'
}
và:
{
'format' : '%(asctime)s %(levelname)-8s %(name)-15s %(message)s',
'datefmt' : '%Y-%m-%d %H:%M:%S'
}
tương ứng, và vì các từ điển này không chứa khóa đặc biệt '()' nên việc khởi tạo được suy ra từ ngữ cảnh: kết quả là các phiên bản logging.Formatter tiêu chuẩn được tạo. Từ điển phụ cấu hình cho bộ định dạng thứ ba, với id custom, là:
{
'()' : 'my.package.customFormatterFactory',
'thanh' : 'baz',
'thư rác' : 99,9,
'câu trả lời' : 42
}
và nó chứa khóa đặc biệt '()', có nghĩa là cần có sự khởi tạo do người dùng xác định. Trong trường hợp này, lệnh gọi gốc được chỉ định sẽ được sử dụng. Nếu nó là một lệnh gọi thực tế thì nó sẽ được sử dụng trực tiếp - nếu không, nếu bạn chỉ định một chuỗi (như trong ví dụ), thì lệnh gọi thực tế đó sẽ được định vị bằng các cơ chế nhập thông thường. Có thể gọi được sẽ được gọi với các mục remaining trong từ điển phụ cấu hình làm đối số từ khóa. Trong ví dụ trên, trình định dạng có id custom sẽ được coi là được trả về bởi lệnh gọi
my.package.customFormatterFactory(bar='baz', spam=99.9, answer=42)
Cảnh báo
Các giá trị cho các khóa như bar, spam và answer trong ví dụ trên không được là từ điển cấu hình hoặc tham chiếu như cfg://foo hoặc ext://bar, vì chúng sẽ không được máy cấu hình xử lý mà được chuyển đến bộ phận có thể gọi được.
Khóa '()' đã được sử dụng làm khóa đặc biệt vì đây không phải là tên tham số từ khóa hợp lệ và do đó sẽ không xung đột với tên của đối số từ khóa được sử dụng trong cuộc gọi. Zz001zz cũng đóng vai trò như một cách ghi nhớ rằng giá trị tương ứng có thể gọi được.
Thay đổi trong phiên bản 3.11: Thành viên filters của handlers và loggers có thể lấy các phiên bản bộ lọc ngoài id.
Bạn cũng có thể chỉ định một khóa đặc biệt '.' có giá trị là ánh xạ tên thuộc tính thành giá trị. Nếu tìm thấy, các thuộc tính đã chỉ định sẽ được đặt trên đối tượng do người dùng xác định trước khi nó được trả về. Như vậy, với cấu hình sau:
{
'()' : 'my.package.customFormatterFactory',
'thanh' : 'baz',
'thư rác' : 99,9,
'câu trả lời' : 42,
'.' {
'foo': 'thanh',
'baz': 'bozz'
}
}
trình định dạng được trả về sẽ có thuộc tính foo được đặt thành 'bar' và thuộc tính baz được đặt thành 'bozz'.
Cảnh báo
Các giá trị cho các thuộc tính như foo và baz trong ví dụ trên không được là từ điển cấu hình hoặc tham chiếu như cfg://foo hoặc ext://bar, vì chúng sẽ không được máy cấu hình xử lý mà được đặt làm giá trị thuộc tính nguyên trạng.
Thứ tự cấu hình xử lý¶
Trình xử lý được định cấu hình theo thứ tự bảng chữ cái của các khóa và trình xử lý được định cấu hình sẽ thay thế từ điển cấu hình trong (bản sao hoạt động của) từ điển handlers trong lược đồ. Nếu bạn sử dụng một cấu trúc như cfg://handlers.foo thì ban đầu handlers['foo'] trỏ đến từ điển cấu hình cho trình xử lý có tên foo và sau đó (khi trình xử lý đó đã được định cấu hình), nó sẽ trỏ đến phiên bản trình xử lý đã định cấu hình. Do đó, cfg://handlers.foo có thể phân giải thành từ điển hoặc phiên bản xử lý. Nói chung, nên đặt tên cho các trình xử lý theo cách sao cho các trình xử lý phụ thuộc được định cấu hình after bất kỳ trình xử lý nào mà chúng phụ thuộc vào; cho phép sử dụng thứ gì đó như cfg://handlers.foo để định cấu hình trình xử lý phụ thuộc vào trình xử lý foo. Nếu trình xử lý phụ thuộc đó được đặt tên là bar thì sẽ xảy ra sự cố vì cấu hình của bar sẽ được thử trước cấu hình của foo và foo vẫn chưa được định cấu hình. Tuy nhiên, nếu trình xử lý phụ thuộc được đặt tên là foobar, thì nó sẽ được định cấu hình sau foo, kết quả là cfg://handlers.foo sẽ phân giải thành trình xử lý được định cấu hình foo chứ không phải từ điển cấu hình của nó.
Truy cập vào các đối tượng bên ngoài¶
Đôi khi một cấu hình cần tham chiếu đến các đối tượng bên ngoài cấu hình, ví dụ như sys.stderr. Nếu lệnh cấu hình được xây dựng bằng mã Python thì điều này rất đơn giản nhưng sẽ phát sinh vấn đề khi cấu hình được cung cấp qua tệp văn bản (ví dụ: JSON, YAML). Trong tệp văn bản, không có cách tiêu chuẩn nào để phân biệt sys.stderr với chuỗi ký tự 'sys.stderr'. Để tạo điều kiện thuận lợi cho sự phân biệt này, hệ thống cấu hình tìm kiếm các tiền tố đặc biệt nhất định trong các giá trị chuỗi và xử lý chúng một cách đặc biệt. Ví dụ: nếu chuỗi ký tự 'ext://sys.stderr' được cung cấp dưới dạng giá trị trong cấu hình thì ext:// sẽ bị loại bỏ và phần còn lại của giá trị được xử lý bằng cơ chế nhập thông thường.
Việc xử lý các tiền tố như vậy được thực hiện theo cách tương tự như xử lý giao thức: có một cơ chế chung để tìm kiếm các tiền tố khớp với biểu thức chính quy ^(?P<prefix>[a-z]+)://(?P<suffix>.*)$, theo đó, nếu prefix được nhận dạng, suffix sẽ được xử lý theo cách phụ thuộc vào tiền tố và kết quả của quá trình xử lý sẽ thay thế giá trị chuỗi. Nếu tiền tố không được nhận dạng thì giá trị chuỗi sẽ được giữ nguyên.
Truy cập vào các đối tượng bên trong¶
Cũng như các đối tượng bên ngoài, đôi khi cũng có nhu cầu tham chiếu đến các đối tượng trong cấu hình. Điều này sẽ được hệ thống cấu hình ngầm thực hiện đối với những thứ mà nó biết. Ví dụ: giá trị chuỗi 'DEBUG' cho level trong trình ghi nhật ký hoặc trình xử lý sẽ tự động được chuyển đổi thành giá trị logging.DEBUG và các mục nhập handlers, filters và formatter sẽ lấy id đối tượng và phân giải thành đối tượng đích thích hợp.
Tuy nhiên, cần có một cơ chế chung hơn cho các đối tượng do người dùng xác định mà mô-đun logging chưa biết đến. Ví dụ: hãy xem xét logging.handlers.MemoryHandler, nó lấy đối số target là một trình xử lý khác để ủy quyền. Vì hệ thống đã biết về lớp này nên trong cấu hình, target đã cho chỉ cần là id đối tượng của trình xử lý mục tiêu có liên quan và hệ thống sẽ phân giải thành trình xử lý từ id. Tuy nhiên, nếu người dùng xác định my.package.MyHandler có trình xử lý alternate thì hệ thống cấu hình sẽ không biết rằng alternate đề cập đến một trình xử lý. Để phục vụ cho việc này, một hệ thống phân giải chung cho phép người dùng chỉ định:
người xử lý:
tập tin:
# configuration của trình xử lý tập tin ở đây
tùy chỉnh:
(): my.package.MyHandler
thay thế: cfg://handlers.file
Chuỗi ký tự 'cfg://handlers.file' sẽ được phân giải theo cách tương tự như các chuỗi có tiền tố ext://, nhưng nhìn vào chính cấu hình thay vì không gian tên nhập. Cơ chế cho phép truy cập theo dấu chấm hoặc theo chỉ mục, tương tự như cơ chế được cung cấp bởi str.format. Vì vậy, đưa ra đoạn mã sau:
người xử lý:
email:
lớp: log.handlers.SMTPHandler
máy chủ thư: localhost
từaddr: my_app@domain.tld
cóc:
- support_team@domain.tld
- dev_team@domain.tld
chủ đề: Houston, chúng ta có một vấn đề.
trong cấu hình, chuỗi 'cfg://handlers' sẽ phân giải thành lệnh có khóa handlers, chuỗi 'cfg://handlers.email sẽ phân giải thành lệnh có khóa email trong lệnh handlers, v.v. Chuỗi 'cfg://handlers.email.toaddrs[1] sẽ phân giải thành 'dev_team@domain.tld' và chuỗi 'cfg://handlers.email.toaddrs[0]' sẽ phân giải thành giá trị 'support_team@domain.tld'. Giá trị subject có thể được truy cập bằng 'cfg://handlers.email.subject' hoặc tương đương là 'cfg://handlers.email[subject]'. Dạng sau chỉ cần được sử dụng nếu khóa chứa dấu cách hoặc ký tự không phải chữ và số. Xin lưu ý rằng các ký tự [ và ] không được phép sử dụng trong các phím. Nếu giá trị chỉ mục chỉ bao gồm các chữ số thập phân, quyền truy cập sẽ được thử bằng giá trị số nguyên tương ứng, quay trở lại giá trị chuỗi nếu cần.
Cho một chuỗi cfg://handlers.myhandler.mykey.123, chuỗi này sẽ phân giải thành config_dict['handlers']['myhandler']['mykey']['123']. Nếu chuỗi được chỉ định là cfg://handlers.myhandler.mykey[123], hệ thống sẽ cố gắng truy xuất giá trị từ config_dict['handlers']['myhandler']['mykey'][123] và quay lại config_dict['handlers']['myhandler']['mykey']['123'] nếu thất bại.
Độ phân giải nhập khẩu và nhà nhập khẩu tùy chỉnh¶
Theo mặc định, độ phân giải nhập sử dụng hàm __import__() tích hợp để thực hiện việc nhập. Bạn có thể muốn thay thế điều này bằng cơ chế nhập của riêng mình: nếu vậy, bạn có thể thay thế thuộc tính importer của DictConfigurator hoặc siêu lớp của nó, lớp BaseConfigurator. Tuy nhiên, bạn cần phải cẩn thận vì cách các hàm được truy cập từ các lớp thông qua các bộ mô tả. Nếu bạn đang sử dụng một Python có thể gọi được để thực hiện nhập và bạn muốn xác định nó ở cấp lớp thay vì cấp phiên bản, bạn cần bọc nó bằng staticmethod(). Ví dụ:
từ importlib nhập import_module
từ log.config nhập BaseConfigurator
BaseConfigurator.importer = staticmethod(import_module)
Bạn không cần phải bọc bằng staticmethod() nếu bạn đang đặt lệnh nhập có thể gọi trên bộ cấu hình instance.
Định cấu hình QueueHandler và QueueListener¶
Nếu bạn muốn định cấu hình QueueHandler, lưu ý rằng điều này thường được sử dụng cùng với QueueListener, bạn có thể định cấu hình cả hai cùng nhau. Sau khi định cấu hình, phiên bản QueueListener sẽ có sẵn dưới dạng thuộc tính listener của trình xử lý đã tạo và đến lượt bạn sẽ có sẵn khi sử dụng getHandlerByName() và chuyển tên bạn đã sử dụng cho QueueHandler trong cấu hình của mình. Lược đồ từ điển để định cấu hình cặp được hiển thị trong đoạn mã YAML ví dụ bên dưới.
người xử lý:
qhand:
lớp: log.handlers.QueueHandler
hàng đợi: my.module.queue_factory
người nghe: my.package.CustomListener
người xử lý:
- tay_name_1
- tay_name_2
...
Các phím queue và listener là tùy chọn.
Nếu có khóa queue, giá trị tương ứng có thể là một trong những giá trị sau:
Một đối tượng triển khai
Queue.put_nowaitvàQueue.getAPI công khai. Ví dụ: đây có thể là một phiên bản thực tế củaqueue.Queuehoặc một lớp con của nó hoặc một proxy đượcmultiprocessing.managers.SyncManager.Queue()thu được.Tất nhiên, điều này chỉ có thể thực hiện được nếu bạn đang xây dựng hoặc sửa đổi từ điển cấu hình trong mã.
Một chuỗi phân giải thành một chuỗi có thể gọi được, khi được gọi mà không có đối số, sẽ trả về phiên bản hàng đợi để sử dụng. Lệnh gọi đó có thể là lớp con
queue.Queuehoặc một hàm trả về một thể hiện hàng đợi phù hợp, chẳng hạn nhưmy.module.queue_factory().Một lệnh có khóa
'()'được xây dựng theo cách thông thường như đã thảo luận trong Đối tượng do người dùng xác định. Kết quả của việc xây dựng này phải là một phiên bảnqueue.Queue.
Nếu không có khóa queue, một phiên bản queue.Queue không giới hạn tiêu chuẩn sẽ được tạo và sử dụng.
Nếu có khóa listener, giá trị tương ứng có thể là một trong những giá trị sau:
Một lớp con của
logging.handlers.QueueListener. Tất nhiên, điều này chỉ có thể thực hiện được nếu bạn đang xây dựng hoặc sửa đổi từ điển cấu hình trong mã.Một chuỗi phân giải thành một lớp là lớp con của
QueueListener, chẳng hạn như'my.package.CustomListener'.Một lệnh có khóa
'()'được xây dựng theo cách thông thường như đã thảo luận trong Đối tượng do người dùng xác định. Kết quả của việc xây dựng này phải là một lệnh gọi có cùng chữ ký với trình khởi tạoQueueListener.
Nếu không có phím listener thì logging.handlers.QueueListener sẽ được sử dụng.
Các giá trị bên dưới khóa handlers là tên của các trình xử lý khác trong cấu hình (không được hiển thị trong đoạn mã trên) sẽ được chuyển đến trình nghe hàng đợi.
Bất kỳ lớp trình xử lý hàng đợi và trình nghe tùy chỉnh nào cũng cần phải được xác định bằng các chữ ký khởi tạo giống nhau như QueueHandler và QueueListener.
Added in version 3.12.
Định dạng tập tin cấu hình¶
Định dạng tệp cấu hình được fileConfig() hiểu dựa trên chức năng configparser. Tệp phải chứa các phần được gọi là [loggers], [handlers] và [formatters] xác định theo tên các thực thể của từng loại được xác định trong tệp. Đối với mỗi thực thể như vậy, có một phần riêng biệt xác định cách thực thể đó được đặt cấu hình. Do đó, đối với trình ghi nhật ký có tên log01 trong phần [loggers], các chi tiết cấu hình liên quan sẽ được lưu giữ trong phần [logger_log01]. Tương tự, trình xử lý có tên hand01 trong phần [handlers] sẽ có cấu hình được giữ trong phần có tên là [handler_hand01], trong khi trình xử lý có tên form01 trong phần [formatters] sẽ có cấu hình được chỉ định trong phần có tên là [formatter_form01]. Cấu hình trình ghi nhật ký gốc phải được chỉ định trong phần có tên [logger_root].
Ghi chú
fileConfig() API cũ hơn dictConfig() API và không cung cấp chức năng để bao gồm một số khía cạnh nhất định của việc ghi nhật ký. Ví dụ: bạn không thể định cấu hình các đối tượng Filter, cung cấp khả năng lọc các tin nhắn vượt quá mức số nguyên đơn giản, bằng cách sử dụng fileConfig(). Nếu bạn cần có các phiên bản của Filter trong cấu hình ghi nhật ký của mình, bạn sẽ cần sử dụng dictConfig(). Lưu ý rằng các cải tiến trong tương lai đối với chức năng cấu hình sẽ được thêm vào dictConfig(), vì vậy bạn nên cân nhắc chuyển sang API mới hơn này khi thấy thuận tiện.
Ví dụ về các phần này trong tệp được đưa ra dưới đây.
[người khai thác gỗ]
phím = gốc, log02, log03, log04, log05, log06, log07
[người xử lý]
phím=hand01,hand02,hand03,hand04,hand05,hand06,hand07,hand08,hand09
[trình định dạng]
phím=form01,form02,form03,form04,form05,form06,form07,form08,form09
Trình ghi nhật ký gốc phải chỉ định cấp độ và danh sách các trình xử lý. Một ví dụ về phần ghi nhật ký gốc được đưa ra dưới đây.
[logger_root]
cấp độ=NOTSET
người xử lý=hand01
Mục level có thể là một trong DEBUG, INFO, WARNING, ERROR, CRITICAL hoặc NOTSET. Chỉ dành cho trình ghi nhật ký gốc, NOTSET có nghĩa là tất cả các tin nhắn sẽ được ghi lại. Giá trị cấp độ là evaluated trong ngữ cảnh không gian tên của gói logging.
Mục handlers là danh sách tên trình xử lý được phân tách bằng dấu phẩy, phải xuất hiện trong phần [handlers]. Những tên này phải xuất hiện trong phần [handlers] và có các phần tương ứng trong file cấu hình.
Đối với các trình ghi nhật ký không phải là trình ghi nhật ký gốc, cần có một số thông tin bổ sung. Điều này được minh họa bằng ví dụ sau.
[logger_parser]
cấp độ=DEBUG
người xử lý=hand01
tuyên truyền=1
qualname=compiler.parser
Các mục nhập level và handlers được hiểu là dành cho trình ghi nhật ký gốc, ngoại trừ việc nếu cấp độ của trình ghi nhật ký không phải gốc được chỉ định là NOTSET, thì hệ thống sẽ tư vấn các trình ghi nhật ký ở cấp bậc cao hơn để xác định mức hiệu quả của trình ghi nhật ký. Mục nhập propagate được đặt thành 1 để cho biết rằng các thông báo phải truyền đến các trình xử lý cao hơn trong hệ thống phân cấp của trình ghi nhật ký từ trình ghi nhật ký này hoặc 0 để cho biết rằng các thông báo được truyền not đến các trình xử lý theo cấp bậc cao hơn. Mục nhập qualname là tên kênh phân cấp của trình ghi nhật ký, nghĩa là tên được ứng dụng sử dụng để lấy trình ghi nhật ký.
Các phần chỉ định cấu hình trình xử lý được minh họa bằng cách sau.
[xử lý_hand01]
class=StreamHandler
cấp độ=NOTSET
trình định dạng=form01
args=(sys.stdout,)
Mục nhập class cho biết lớp của trình xử lý (như được xác định bởi eval() trong không gian tên của gói logging). level được hiểu là dành cho trình ghi nhật ký và NOTSET được hiểu là 'ghi nhật ký mọi thứ'.
Mục nhập formatter cho biết tên khóa của trình định dạng cho trình xử lý này. Nếu trống, bộ định dạng mặc định (logging._defaultFormatter) sẽ được sử dụng. Nếu tên được chỉ định, tên đó phải xuất hiện trong phần [formatters] và có phần tương ứng trong tệp cấu hình.
Mục args, khi evaluated trong ngữ cảnh không gian tên của gói logging, là danh sách các đối số cho hàm tạo của lớp xử lý. Hãy tham khảo các hàm tạo cho các trình xử lý có liên quan hoặc tham khảo các ví dụ bên dưới để xem các mục nhập điển hình được xây dựng như thế nào. Nếu không được cung cấp, nó sẽ mặc định là ().
Mục nhập kwargs tùy chọn, khi evaluated trong ngữ cảnh không gian tên của gói logging, là đối số từ khóa dẫn đến hàm tạo cho lớp xử lý. Nếu không được cung cấp, nó sẽ mặc định là {}.
[xử lý_hand02]
class=Trình xử lý tệp
cấp độ=DEBUG
trình định dạng=form02
args=('python.log', 'w')
[xử lý_hand03]
class=handlers.SocketHandler
cấp độ=INFO
trình định dạng=form03
args=('localhost', handlers.DEFAULT_TCP_LOGGING_PORT)
[xử lý_hand04]
class=handlers.DatagramHandler
cấp độ=WARN
trình định dạng=form04
args=('localhost', handlers.DEFAULT_UDP_LOGGING_PORT)
[handler_hand05]
class=handlers.SysLogHandler
cấp độ=ERROR
trình định dạng=form05
args=(('localhost', handlers.SYSLOG_UDP_PORT), handlers.SysLogHandler.LOG_USER)
[handler_hand06]
class=handlers.NTEventLogHandler
cấp độ=CRITICAL
trình định dạng=form06
args=('Ứng dụng Python', '', 'Ứng dụng')
[xử lý_hand07]
class=handlers.SMTPHandler
cấp độ=WARN
trình định dạng=form07
args=('localhost', 'from@abc', ['user1@abc', 'user2@xyz'], 'Chủ đề nhật ký')
kwargs={'hết thời gian': 10.0}
[handler_hand08]
class=handlers.MemoryHandler
cấp độ=NOTSET
trình định dạng=form08
mục tiêu=
args=(10, ERROR)
[handler_hand09]
class=handlers.HTTPHandler
cấp độ=NOTSET
trình định dạng=form09
args=('localhost:9022', '/log', 'GET')
kwargs={'secure': True}
Các phần chỉ định cấu hình bộ định dạng được đánh dấu như sau.
[formatter_form01]
format=F1 %(asctime)s %(levelname)s %(message)s %(customfield)s
ngàyfmt=
kiểu =%
xác thực=Đúng
mặc định={'customfield': 'defaultvalue'}
class=logging.Formatter
Các đối số cho cấu hình bộ định dạng giống như các khóa trong lược đồ từ điển formatters section.
Mục defaults, khi evaluated trong ngữ cảnh không gian tên của gói logging, là từ điển chứa các giá trị mặc định cho các trường định dạng tùy chỉnh. Nếu không được cung cấp, nó sẽ mặc định là None.
Ghi chú
Do việc sử dụng eval() như mô tả ở trên nên có thể tiềm ẩn những rủi ro bảo mật do sử dụng listen() để gửi và nhận cấu hình qua ổ cắm. Rủi ro được giới hạn ở chỗ nhiều người dùng không có mã chạy tin cậy lẫn nhau trên cùng một máy; xem tài liệu listen() để biết thêm thông tin.
Xem thêm
- Mô-đun
logging tham chiếu API cho mô-đun ghi nhật ký.
- Mô-đun
logging.handlers Trình xử lý hữu ích đi kèm với mô-đun ghi nhật ký.