Tin vui là bạn không cần phải làm gì thêm để đảm bảo an toàn cho luồng, và bạn không cần phải làm gì thêm hoặc điều gì đó gần như tầm thường để tắt máy sạch. Tôi sẽ đến chi tiết sau.
Tin xấu là mã của bạn có vấn đề nghiêm trọng ngay cả trước khi bạn đến điểm đó: fileLogger
và consoleLogger
là cùng một đối tượng. Từ the documentation for getLogger()
:
Trả lại trình ghi nhật ký với tên được chỉ định hoặc, nếu không có tên được chỉ định, hãy trả lại nhật ký là trình ghi gốc của cấu trúc phân cấp.
Vì vậy, bạn sẽ nhận được bộ ghi gốc và lưu trữ nó dưới dạng fileLogger
và sau đó bạn sẽ nhận được bộ ghi gốc và lưu trữ nó là consoleLogger
. Vì vậy, trong LoggingInit
, bạn khởi tạo fileLogger
, sau đó khởi tạo lại cùng một đối tượng dưới một tên khác với các giá trị khác nhau.
Bạn có thể thêm nhiều trình xử lý vào cùng một trình ghi - và do khởi tạo duy nhất bạn thực sự làm cho mỗi lần là addHandler
, mã của bạn sẽ hoạt động như dự định, nhưng chỉ do ngẫu nhiên. Và chỉ là loại. Bạn sẽ nhận được hai bản sao của mỗi thư trong cả hai nhật ký nếu bạn vượt qua print_screen=True
và bạn sẽ nhận được bản sao trong bảng điều khiển ngay cả khi bạn vượt qua print_screen=False
.
Thực sự không có lý do gì cho các biến toàn cầu; toàn bộ các điểm của getLogger()
là bạn có thể gọi nó mỗi khi bạn cần nó và nhận được logger gốc toàn cầu, vì vậy bạn không cần phải lưu trữ nó bất cứ nơi nào.
Một vấn đề nhỏ nữa là bạn không thoát văn bản bạn chèn vào HTML. Tại một số điểm bạn sẽ cố gắng để đăng nhập chuỗi "a < b"
và kết thúc trong rắc rối.
Ít nghiêm trọng hơn, một chuỗi <p>
thẻ không nằm trong một sốbên trong một <html>
không phải là tài liệu HTML hợp lệ. Nhưng nhiều người xem sẽ tự động xử lý điều đó hoặc bạn có thể xử lý nhật ký của mình một cách trivially trước khi hiển thị chúng.Nhưng nếu bạn thực sự muốn điều này là chính xác, bạn cần phải phân loại FileHandler
và thêm __init__
thêm tiêu đề nếu được cung cấp tệp trống và xóa chân trang nếu có, sau đó thêm close
thêm chân trang.
Bắt trở lại câu hỏi thực tế của bạn:
Bạn không cần bất kỳ khóa bổ sung. Nếu trình xử lý thực hiện chính xác createLock
, acquire
và release
(và được gọi trên nền tảng có chủ đề), máy ghi nhật ký sẽ tự động đảm bảo có được khóa khi cần để đảm bảo mỗi thư được ghi nguyên tử.
Theo như tôi biết, các tài liệu không trực tiếp nói rằng StreamHandler
và FileHandler
thực hiện phương pháp này, nó không bao hàm sự mạnh mẽ nó (the text you mentioned in the question nói "Các module khai thác gỗ được thiết kế để được thread-safe mà không cần bất kỳ công việc đặc biệt cần được thực hiện bởi khách hàng của mình ", v.v ...). Và bạn có thể xem nguồn để triển khai (ví dụ: CPython 3.3) và thấy rằng cả hai đều kế thừa các phương pháp được triển khai chính xác từ logging.Handler
.
Tương tự, nếu một handler thực hiện một cách chính xác flush
và close
, các máy móc thiết bị khai thác gỗ sẽ đảm bảo nó hoàn thành một cách chính xác trong quá trình tắt máy bình thường.
Ở đây, tài liệu hướng dẫn giải thích những gì StreamHandler.flush()
, FileHandler.flush()
và FileHandler.close()
. Chúng hầu hết là những gì bạn mong đợi, ngoại trừ việc StreamHandler.close()
là một không-op, có nghĩa là các thông điệp tường trình cuối cùng có thể bị mất. Từ các tài liệu:
Lưu ý rằng phương pháp close()
được thừa hưởng từ Handler
và do đó, không có đầu ra, vì vậy một rõ ràng flush()
cuộc gọi có thể cần thiết vào những thời điểm.
Nếu đây quan trọng đối với bạn, và bạn muốn sửa chữa nó, bạn cần phải làm điều gì đó như thế này:
class ClosingStreamHandler(logging.StreamHandler):
def close(self):
self.flush()
super().close()
Và sau đó sử dụng ClosingStreamHandler()
thay vì StreamHandler()
.
FileHandler
không có vấn đề gì như vậy.
Cách thông thường để gửi nhật ký tới hai địa điểm là chỉ sử dụng trình ghi gốc với hai trình xử lý, mỗi trình xử lý có định dạng riêng.
Ngoài ra, ngay cả khi bạn muốn có hai trình ghi nhật ký, bạn không cần các bản đồ console_logging_level_switch
và file_logging_level_switch
riêng biệt; gọi số Logger.debug(msg)
chính xác giống như gọi số Logger.log(DEBUG, msg)
. Bạn vẫn sẽ cần một số cách để ánh xạ tên cấp tùy chỉnh debug
, v.v. thành các tên chuẩn DEBUG
, v.v., nhưng bạn chỉ có thể thực hiện một lần tra cứu, thay vì thực hiện một lần cho mỗi lần ghi nhật ký (cộng với, nếu tên của bạn chỉ là tên tiêu chuẩn với dàn diễn viên khác nhau, bạn có thể ăn gian).
Đây là tất cả được mô tả khá tốt trong phần `Multiple handlers and formatters và phần còn lại của sổ tay ghi nhật ký.
Vấn đề duy nhất với cách làm tiêu chuẩn này là bạn không thể dễ dàng tắt đăng nhập bàn điều khiển trên cơ sở từng tin nhắn. Đó là bởi vì nó không phải là một điều bình thường để làm. Thông thường, bạn chỉ cần đăng nhập theo cấp độ, và đặt mức nhật ký cao hơn trên nhật ký tệp.
Nhưng nếu muốn kiểm soát nhiều hơn, bạn có thể sử dụng bộ lọc. Ví dụ: cung cấp cho bạn FileHandler
bộ lọc chấp nhận mọi thứ và ConsoleHandler
bộ lọc yêu cầu thứ gì đó bắt đầu bằng console
, sau đó sử dụng bộ lọc 'console' if print_screen else ''
. Điều đó làm giảm WriteLog
xuống gần một lớp lót.
Bạn vẫn cần thêm hai dòng để xóa dòng mới — nhưng thậm chí bạn có thể làm rằng trong bộ lọc hoặc qua bộ điều hợp, nếu bạn muốn. (Một lần nữa, xem sách dạy nấu ăn.) Và sau đó WriteLog
thực sự là một lớp lót.
Nếu bạn không đặt khóa khi bạn muốn viết, bản ghi nhật ký của bạn có thể trộn và tạo nhật ký không đọc được. – AliBZ
@AliBZ - bản ghi nhật ký nào? ở cấp độ khai thác gỗ? chúng không được bảo vệ bởi dịch vụ ghi nhật ký python? http://stackoverflow.com/questions/2973900/is-pythons-logging-module-thread-safe –
khi bạn sử dụng "fileLogger.info ('1 2 3 4')" trong hai chủ đề khác nhau, nhật ký cuối cùng của bạn có thể là một kết hợp của họ, một cái gì đó như thế này "1 2 1 2 3 3 4 4" – AliBZ