những gì nên được vị trí của logger trong danh sách tham số [đóng]


12

Trong mã của tôi, tôi tiêm một logger cho nhiều lớp của mình thông qua danh sách tham số của hàm tạo của chúng

Tôi nhận thấy rằng tôi đặt nó một cách ngẫu nhiên: đôi khi nó là cái đầu tiên trong danh sách, đôi khi cuối cùng và đôi khi ở giữa

Bạn có sở thích nào không? Trực giác của tôi nói rằng tính nhất quán là hữu ích trong trường hợp này và sở thích cá nhân của tôi là đặt nó lên hàng đầu để dễ nhận thấy hơn khi thiếu và dễ bỏ qua hơn khi ở đó.

Câu trả lời:


31

Loggers là những gì chúng ta gọi là "mối quan tâm xuyên suốt." Họ nhường cho các kỹ thuật như Lập trình hướng theo khía cạnh; nếu bạn có cách trang trí các lớp của mình bằng một thuộc tính hoặc thực hiện một số mã dệt, thì đó là một cách tốt để có được khả năng ghi nhật ký trong khi giữ cho các đối tượng và danh sách tham số của bạn "thuần túy".

Lý do duy nhất bạn có thể muốn chuyển vào một logger là nếu bạn muốn chỉ định các triển khai ghi nhật ký khác nhau, nhưng hầu hết các khung ghi nhật ký có tính linh hoạt để cho phép bạn định cấu hình chúng, cho các mục tiêu ghi nhật ký khác nhau. (tệp nhật ký, Trình quản lý sự kiện Windows, v.v.)

Vì những lý do này, tôi thích làm cho việc ghi nhật ký trở thành một phần tự nhiên của hệ thống, thay vì chuyển một trình ghi nhật ký vào mọi lớp cho mục đích ghi nhật ký. Vì vậy, những gì tôi thường làm là tham chiếu không gian tên ghi nhật ký thích hợp và chỉ cần sử dụng trình ghi nhật ký trong các lớp của tôi.

Nếu bạn vẫn muốn truyền vào một logger, sở thích của tôi là bạn biến nó thành tham số cuối cùng trong danh sách tham số (biến nó thành một tham số tùy chọn, nếu có thể). Có nó là tham số đầu tiên không có ý nghĩa nhiều; tham số đầu tiên phải là tham số quan trọng nhất, tham số phù hợp nhất với hoạt động của lớp.


11
+! giữ logger ra khỏi parms xây dựng; Tôi thích một logger tĩnh vì vậy tôi biết tất cả mọi người đang sử dụng cùng một thứ
Steven A. Lowe

1
@ StevenA.Lowe Lưu ý rằng việc sử dụng bộ ghi tĩnh có thể dẫn đến các vấn đề khác nếu bạn không cẩn thận (ví dụ: fiasco thứ tự khởi tạo tĩnh trong C ++). Tôi đồng ý rằng việc logger như một thực thể có thể truy cập toàn cầu có những điểm hấp dẫn của nó, nhưng nó cần được đánh giá cẩn thận xem liệu thiết kế như vậy có phù hợp với kiến ​​trúc tổng thể hay không.
ComicSansMS 10/03/2015

@ComicSansMS: tất nhiên, cộng với các vấn đề luồng, v.v. Chỉ là một sở thích cá nhân - "đơn giản nhất có thể, nhưng không đơn giản hơn";)
Steven A. Lowe

Có một logger tĩnh có thể là một vấn đề. Nó làm cho việc tiêm phụ thuộc trở nên khó khăn hơn (trừ khi bạn có nghĩa là một logger đơn được khởi tạo bởi bộ chứa DI của bạn) và nếu bạn muốn thay đổi kiến ​​trúc của mình thì điều đó có thể gây đau đớn. Ví dụ, ngay bây giờ, tôi đang sử dụng các hàm Azure truyền vào bộ ghi như một tham số cho mỗi lần thực thi chức năng, vì vậy tôi rất tiếc khi chuyển bộ ghi chép của mình qua hàm tạo.
Slothario

@Slothario: Đó là vẻ đẹp của logger tĩnh. Không cần tiêm bất kỳ loại nào.
Robert Harvey

7

Trong các ngôn ngữ có chức năng quá tải chức năng, tôi cho rằng càng có nhiều khả năng là một đối số là tùy chọn thì càng phải đúng. Điều này tạo ra sự nhất quán khi bạn tạo quá tải khi thiếu:

foo(mandatory);
foo(mandatory, optional);
foo(mandatory, optional, evenMoreOptional);

Trong các ngôn ngữ chức năng, việc đảo ngược sẽ hữu ích hơn - bạn càng có nhiều khả năng chọn một số mặc định, thì càng phải để lại. Điều này giúp việc chuyên môn hóa hàm dễ dàng hơn bằng cách áp dụng các đối số cho nó:

addThreeToList = map (+3)

Tuy nhiên, như đã đề cập trong các câu trả lời khác, có lẽ bạn không muốn vượt qua logger một cách rõ ràng trong danh sách đối số của mọi lớp trong hệ thống.


3

Bạn chắc chắn có thể dành nhiều thời gian quá kỹ thuật cho vấn đề này.

Đối với các ngôn ngữ có triển khai ghi nhật ký chính tắc, chỉ cần khởi tạo trình ghi nhật ký chính tắc trực tiếp trong mỗi lớp.

Đối với các ngôn ngữ không có triển khai chính tắc, hãy thử tìm một khung mặt tiền đăng nhập và tuân theo nó. slf4j là một lựa chọn tốt trong Java.

Cá nhân tôi muốn gắn bó với một triển khai khai thác cụ thể và gửi mọi thứ đến syslog. Tất cả các công cụ phân tích nhật ký tốt có khả năng kết hợp nhật ký sysout từ nhiều máy chủ ứng dụng thành một báo cáo toàn diện.

Khi chữ ký hàm bao gồm một hoặc hai dịch vụ phụ thuộc cũng như một số đối số "thực", tôi đặt các phụ thuộc cuối cùng:

int calculateFooBarSum(int foo, int bar, IntegerSummationService svc)

Vì các hệ thống của tôi có xu hướng chỉ có năm hoặc ít hơn các dịch vụ như vậy, tôi luôn luôn đảm bảo các dịch vụ được bao gồm theo cùng một thứ tự trên tất cả các chữ ký chức năng. Thứ tự chữ cái là tốt như bất kỳ. (Ngoài ra: duy trì phương pháp tiếp cận phương pháp này để xử lý mutex cũng sẽ làm giảm cơ hội phát triển bế tắc của bạn.)

Nếu bạn thấy mình tiêm hơn một tá phụ thuộc vào ứng dụng của mình, thì hệ thống có thể cần được chia thành các hệ thống con riêng biệt (tôi có dám nói là microservice không?).


Tôi có vẻ lúng túng khi sử dụng tính năng tiêm tài sản để gọi tính toánFooBarSum.
một phu

2

Loggers là một chút của một trường hợp đặc biệt bởi vì chúng phải có sẵn theo nghĩa đen ở mọi nơi.

Nếu bạn đã quyết định rằng bạn muốn chuyển một logger vào hàm tạo của mọi lớp, thì bạn chắc chắn nên đặt một quy ước nhất quán cho cách bạn làm điều đó (ví dụ: luôn luôn là tham số đầu tiên, luôn được truyền bởi tham chiếu, danh sách khởi tạo hàm tạo luôn bắt đầu với m_logger (theLogger), v.v.). Bất cứ điều gì sẽ được sử dụng trong toàn bộ cơ sở mã của bạn sẽ được hưởng lợi từ tính nhất quán một ngày nào đó.

Ngoài ra, bạn có thể yêu cầu mọi lớp khởi tạo đối tượng logger của riêng mình mà không cần thông qua bất kỳ thứ gì. Trình ghi nhật ký có thể cần biết một số điều "bằng phép thuật" để làm việc đó, nhưng mã hóa một filepath trong định nghĩa lớp có khả năng là một có thể duy trì nhiều hơn và ít tẻ nhạt hơn so với việc chuyển nó một cách chính xác cho hàng trăm lớp khác nhau, và được cho là ít tệ hơn nhiều so với việc sử dụng một biến toàn cục để bỏ qua tedium nói. (Phải thừa nhận rằng, logger là một trong số rất ít trường hợp sử dụng hợp pháp cho các biến toàn cục)


1

Tôi đồng ý với những người đề xuất rằng logger nên được truy cập tĩnh hơn là được truyền vào các lớp. Tuy nhiên, nếu có một lý do mạnh mẽ mà bạn muốn vượt qua nó trong (trường hợp có lẽ khác nhau muốn đăng nhập đến các địa điểm khác nhau hoặc một cái gì đó) sau đó tôi sẽ đề nghị bạn không vượt qua nó bằng cách sử dụng constructor nhưng thay vì thực hiện cuộc gọi riêng biệt để làm như vậy, ví dụ như Class* C = new C(); C->SetLogger(logger);thay vì hơnClass* C = new C(logger);

Lý do thích phương thức này là logger không phải là một phần cơ bản của lớp mà là một tính năng được sử dụng cho một số mục đích khác. Đặt nó trong danh sách hàm tạo làm cho nó trở thành một yêu cầu của lớp và ngụ ý nó là một phần của trạng thái logic thực tế của lớp. Chẳng hạn, điều hợp lý là mong đợi (với hầu hết các lớp mặc dù không phải tất cả), nếu X != Ysau đó C(X) != C(Y)nhưng không chắc là bạn sẽ kiểm tra bất đẳng thức logger nếu bạn so sánh quá các thể hiện của cùng một lớp.


1
Tất nhiên điều này có nhược điểm là logger không có sẵn cho hàm tạo.
Ben Voigt

1
Tôi thực sự thích câu trả lời này. Nó làm cho việc ghi nhật ký trở thành mối quan tâm phụ của lớp mà bạn phải quan tâm riêng biệt với việc chỉ sử dụng nó. Rất có thể là nếu bạn thêm logger vào hàm tạo của một số lượng lớn các lớp, có lẽ bạn đang sử dụng phép nội xạ phụ thuộc. Tôi không thể nói cho tất cả các ngôn ngữ, nhưng tôi biết trong C #, một số triển khai DI cũng sẽ tiêm trực tiếp vào các thuộc tính (getter / setter).
jpmc26

@BenVoigt: Đó là sự thật, và đó có thể là một lý do giết người không làm theo cách này, nhưng, thông thường, bạn có thể thực hiện việc ghi nhật ký mà bạn làm trong trình xây dựng để đáp ứng với trình ghi nhật ký được đặt.
Jack Aidley 10/03/2015

0

Điều đáng nói, một điều mà tôi chưa thấy các câu trả lời khác chạm đến ở đây, đó là bằng cách làm cho logger được tiêm thông qua thuộc tính hoặc tĩnh, điều đó khiến cho đơn vị kiểm tra lớp khó khăn. Ví dụ: nếu bạn thực hiện logger của mình thông qua thuộc tính, bây giờ bạn sẽ phải tiêm logger đó mỗi khi bạn kiểm tra một phương thức sử dụng logger. Điều này có nghĩa là bạn cũng có thể có thể đặt nó làm phụ thuộc hàm tạo bởi vì lớp này yêu cầu nó.

Tĩnh cho vay cùng một vấn đề; nếu logger không hoạt động, thì toàn bộ lớp của bạn sẽ thất bại (nếu lớp của bạn sử dụng logger) - mặc dù logger không nhất thiết là 'một phần' trách nhiệm của lớp - mặc dù nó gần như không tệ như tiêm tài sản vì bạn ít nhất biết rằng logger luôn luôn "ở đó" theo một nghĩa nào đó.

Chỉ cần một số thực phẩm cho suy nghĩ, đặc biệt là nếu bạn sử dụng TDD. Theo tôi, một logger thực sự không phải là một phần của một phần có thể kiểm tra được của một lớp (khi bạn kiểm tra lớp đó, bạn cũng không nên kiểm tra việc ghi nhật ký của mình).


1
hmmm ... vì vậy bạn muốn lớp của mình thực hiện ghi nhật ký (ghi nhật ký phải nằm trong đặc tả) nhưng bạn không muốn kiểm tra bằng logger. Điều này có thể không? Tôi nghĩ rằng quan điểm của bạn là không đi. Rõ ràng nếu các công cụ kiểm tra của bạn bị hỏng, bạn không thể kiểm tra - để thiết kế theo cách không phụ thuộc vào công cụ kiểm tra là một chút IMHO quá kỹ thuật
Hogan

1
Quan điểm của tôi là nếu tôi sử dụng một hàm tạo và gọi một phương thức trên một lớp và nó vẫn thất bại vì tôi không đặt thuộc tính, thì người thiết kế của lớp đã hiểu nhầm khái niệm đằng sau một hàm tạo. Nếu một lớp logger được yêu cầu bởi lớp, nó sẽ được chèn vào hàm tạo - đó là những gì hàm tạo có sẵn cho.
Dan Pantry

umm .. không không thực sự. Nếu bạn xem xét phần hệ thống Ghi nhật ký của "khung" thì nó không có ý nghĩa như là một phần của hàm tạo. Nhưng điều này đã được nêu trong các câu trả lời khác rõ ràng.
Hogan

1
Tôi đang tranh cãi chống lại tiêm tài sản. Tôi không nhất thiết ủng hộ việc sử dụng tiêm nó trong hàm tạo. Tôi chỉ nói rằng theo ý kiến ​​của tôi, đó là thích hợp hơn để tiêm tài sản.
Dan Pantry

"Điều này có thể không?" Ngoài ra, vâng, dệt IL là một thứ tồn tại và được đề cập trong câu trả lời hàng đầu ... mono-project.com/docs/tools+lologists/lologists/Mono.Cecil
Dan Pantry

0

Tôi quá lười biếng để chuyển xung quanh một đối tượng logger cho mỗi thể hiện của lớp. Vì vậy, trong mã của tôi, những thứ này hoặc nằm trong một trường tĩnh hoặc một biến cục bộ của luồng trong trường tĩnh. Loại thứ hai này rất tuyệt và cho phép bạn sử dụng một bộ ghi khác nhau cho mỗi luồng và cho phép bạn thêm các phương thức để bật và tắt đăng nhập để làm điều gì đó có ý nghĩa và được mong đợi trong một ứng dụng đa luồng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.