Có đúng là người ta không nên sử dụng NSLog () trên mã sản xuất?


155

Tôi đã nói điều này một vài lần trong chính trang web này, nhưng tôi muốn chắc chắn rằng đây thực sự là trường hợp.

Tôi đã mong đợi có thể rắc các lệnh gọi hàm NSLog trong toàn bộ mã của mình và Xcode / gcc sẽ tự động loại bỏ các cuộc gọi đó khi xây dựng các bản dựng Phát hành / Phân phối của tôi.

Tôi có nên tránh sử dụng này? Nếu vậy, lựa chọn thay thế nào phổ biến nhất giữa các lập trình viên Objective-C có kinh nghiệm?


7
Tôi biết câu hỏi này bây giờ rất cũ, nhưng, nếu bạn vẫn có thể, tôi sẽ đánh dấu câu trả lời của Marc Charbonneau là được chấp nhận. Tôi đã sửa đổi câu trả lời của mình để chỉ cho anh ấy, nhưng câu trả lời của anh ấy là câu trả lời đúng.
e.James

5
NSLog () bên trong một vòng lặp thường xuyên sẽ giết chết hoàn toàn hiệu suất của bạn, ông nói, đã tìm ra cách khó khăn.
willc2

Câu trả lời:


197

Các macro tiền xử lý thực sự tuyệt vời để gỡ lỗi. Không có gì sai với NSLog (), nhưng thật đơn giản để xác định chức năng ghi nhật ký của riêng bạn với chức năng tốt hơn. Đây là cái tôi sử dụng, nó bao gồm tên tệp và số dòng để giúp theo dõi các báo cáo nhật ký dễ dàng hơn.

#define DEBUG_MODE

#ifdef DEBUG_MODE
    #define DebugLog( s, ... ) NSLog( @"<%p %@:(%d)> %@", self, [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] )
#else
    #define DebugLog( s, ... ) 
#endif

Tôi thấy dễ dàng hơn khi đặt toàn bộ câu lệnh này trong tiêu đề tiền tố thay vì tập tin riêng của nó. Nếu bạn muốn, bạn có thể xây dựng một hệ thống ghi nhật ký phức tạp hơn bằng cách để DebugLog tương tác với các đối tượng Objective-C bình thường. Chẳng hạn, bạn có thể có một lớp ghi nhật ký ghi vào tệp nhật ký (hoặc cơ sở dữ liệu) của riêng nó và bao gồm một đối số 'ưu tiên' bạn có thể đặt trong thời gian chạy, vì vậy các thông báo gỡ lỗi không được hiển thị trong phiên bản phát hành của bạn, nhưng thông báo lỗi là ( nếu bạn đã làm điều này, bạn có thể tạo DebugLog (), WarningLog (), v.v.).

Ồ, và hãy nhớ rằng #define DEBUG_MODEcó thể được sử dụng lại ở những nơi khác nhau trong ứng dụng của bạn. Ví dụ: trong ứng dụng của mình, tôi sử dụng nó để vô hiệu hóa kiểm tra khóa giấy phép và chỉ cho phép ứng dụng chạy nếu trước một ngày nhất định. Điều này cho phép tôi phân phối một bản sao beta đầy đủ thời gian, đầy đủ chức năng với nỗ lực tối thiểu từ phía tôi.


8
+1 cho một câu trả lời xuất sắc. Tôi đã thay đổi của tôi để chỉ ra rằng các macro #define của bạn là con đường để đi và tôi hy vọng OP chuyển câu trả lời được chấp nhận (tôi đã để lại cho anh ấy một nhận xét). Tôi đã sử dụng một hàm giả vì tôi không biết rằng bạn có thể sử dụng ... đối số trong một macro. Sống và học hỏi!
e.James

15
Một câu trả lời tuyệt vời, mặc dù tôi khuyên bạn nên sử dụng tiền tố cá nhân trong định nghĩa "DEBUG_MODE" của mình, chẳng hạn như gọi nó là "JPM_DEBUG" hoặc tương tự. Rất thường xuyên, tôi đã gặp mã bên thứ ba cũng sử dụng DEBUG hoặc DEBUG_MODE hoặc tương tự, và đôi khi mã đó sẽ không hoạt động chính xác trong chế độ DEBUG. Nếu bạn muốn bật gỡ lỗi thư viện của bên thứ ba, bạn nên cố tình làm điều đó. (Tất nhiên, đó là những người viết thư viện nên đặt tiền tố vào biểu tượng của họ, nhưng nhiều khung C và C ++ thì không, đặc biệt là đối với định nghĩa này).
Rob Napier

1
Có một macro Xcode được xác định trước chỉ có thể được sử dụng để bật tính năng này khi cấu hình được đặt thành gỡ lỗi không? Tôi không muốn tự mình thiết lập macro tiền xử lý này trong mỗi dự án. chúng ta có thể làm gì đó như sau mã giả #if XCODE_CONFIGURATION == DEBUG không?
frankodwyer

1
#include <TargetCond điều
kiện.h

2
Cách tiếp cận này dẫn đến các cảnh báo "biến không sử dụng" giả từ trình biên dịch trong chế độ phát hành khi các câu lệnh ghi nhật ký sử dụng các biến trung gian cho mục đích duy nhất là tính toán các giá trị để ghi nhật ký. Điều gì sẽ là cách thông minh nhất để tránh điều đó nếu bạn ghét các cảnh báo của trình biên dịch nhiều như tôi làm?
Jean-Denis Muys

78

Đặt 3 dòng này vào cuối tệp -prefix.pch:

#ifndef DEBUG
  #define NSLog(...) /* suppress NSLog when in release mode */
#endif

Bạn không cần xác định bất cứ điều gì vào dự án của mình, bởi vì DEBUGđược xác định trong cài đặt bản dựng của bạn theo mặc định khi bạn tạo dự án của mình.


2
Giải pháp tốt nhất. Bạn cần thêm prefix.pch theo cách thủ công từ XCode 6.
Teddy

Tuy nhiên, chúng ta vẫn cần thay đổi cài đặt bản dựng trước khi phát hành, tức là gỡ lỗi để phát hành
UserDev

25

Các cuộc gọi NSLog có thể được để lại trong mã sản xuất, nhưng chỉ nên có ở đó cho các trường hợp thực sự đặc biệt hoặc thông tin mà nó mong muốn sẽ được ghi vào nhật ký hệ thống.

Các ứng dụng xả rác nhật ký hệ thống gây phiền nhiễu và đi qua là không chuyên nghiệp.


14
Xin lỗi - đi qua như không chuyên nghiệp với ai? Ai có khả năng kiểm tra nhật ký của bạn trên một ứng dụng được phát hành và đánh giá tính chuyên nghiệp của bạn dựa trên điều đó? (Để rõ ràng, tôi hoàn toàn đồng ý rằng bạn không nên giữ một tấn NSLogs trong phiên bản phát hành của ứng dụng của mình, nhưng tôi bối rối bởi lập luận 'tính chuyên nghiệp'.)
WendiKidd

4
Các nhà phát triển khác sẽ làm những gì bạn đang làm và cảm thấy khó chịu. Android có một vấn đề tương tự với một số nhà phát triển thực sự tồi tệ plus.google.com/110166527124367568225/posts/h4jK38n4XYR
Roger Binns

24

Tôi không thể nhận xét về câu trả lời của Marc Charbonneau , vì vậy tôi sẽ đăng bài này dưới dạng câu trả lời.

Ngoài ra để thêm macro vào tiêu đề được biên dịch trước, bạn có thể sử dụng các cấu hình xây dựng Target để kiểm soát việc xác định (hoặc thiếu định nghĩa) DEBUG_MODE.

Nếu bạn chọn cấu hình hoạt động " Gỡ lỗi ", DEBUG_MODEsẽ được xác định và macro sẽ mở rộng thành NSLogđịnh nghĩa đầy đủ .

Chọn cấu hình hoạt động " Phát hành " sẽ không xác định DEBUG_MODENSLogging của bạn bị bỏ qua khỏi bản dựng phát hành.

Các bước:

  • Mục tiêu> Nhận thông tin
  • Xây dựng tab
  • Tìm kiếm "Macro PreProcessor" (hoặc GCC_PREPROCESSOR_DEFINITIONS)
  • Chọn cấu hình: Gỡ lỗi
  • Chỉnh sửa định nghĩa ở cấp độ này
  • Thêm vào DEBUG_MODE=1
  • Chọn cấu hình: Phát hành
  • xác nhận DEBUG_MODEkhông được đặt trongGCC_PREPROCESSOR_DEFINITIONS

nếu bạn bỏ qua ký tự '=' trong định nghĩa, bạn sẽ gặp lỗi từ bộ xử lý trước

Ngoài ra, dán nhận xét này (hiển thị bên dưới) phía trên định nghĩa macro để nhắc bạn DEBUG_MACROđịnh nghĩa đến từ đâu;)

// Target > Get Info > Build > GCC_PREPROCESSOR_DEFINITIONS
// Configuration = Release: <empty>
//               = Debug:   DEBUG_MODE=1

1
Đó là một câu trả lời bổ sung có giá trị cho câu hỏi. Xứng đáng được nhiều hơn một bình luận.
morningstar

DEBUG_MODEDEBUG_MACROlà độc đáo. Tôi chỉ tìm thấy một tài liệu tham khảo DEBUG_MACROtrên trang web của apple ( opensource.apple.com/source/gm4/gm4-15/src/m4.h?txt ). Có lẽ tiêu chuẩn hơn DEBUGNDEBUGsẽ là lựa chọn tốt hơn? NDEBUGđược chỉ định bởi Posix; trong khi DEBUGđược sử dụng bởi quy ước.
21 giờ 44 phút

+1 Có, đây là một bài viết cũ, nhưng đó là điểm ... Trong phiên bản Xcode của tôi (4 năm sau), tìm kiếm GCC_PREPROCESSOR_DEFINITIONS trả về một số ngôn ngữ khác. Hãy xem xét cập nhật câu trả lời tuyệt vời này cho rõ ràng.
David

11

EDIT: Các phương pháp đăng bởi Marc Charbonneau , và mang đến sự chú ý của tôi bằng cách sho , là tốt hơn nhiều so thế này.

Tôi đã xóa phần câu trả lời của mình, gợi ý sử dụng hàm trống để tắt ghi nhật ký khi chế độ gỡ lỗi bị tắt. Phần liên quan đến việc thiết lập macro tiền xử lý tự động vẫn có liên quan, vì vậy nó vẫn còn. Tôi cũng đã chỉnh sửa tên của macro tiền xử lý để phù hợp hơn với câu trả lời của Marc Charbonneau.


Để đạt được hành vi tự động (và dự kiến) trong Xcode:

Trong cài đặt dự án, chuyển đến tab "Xây dựng" và chọn cấu hình "Gỡ lỗi". Tìm phần "Macro tiền xử lý" và thêm một macro có tênDEBUG_MODE .

...

EDIT: Xem câu trả lời của Marc Charbonneau để biết cách thích hợp để bật và tắt ghi nhật ký với DEBUG_MODEmacro.


7

Tôi đồng ý với Matthew. Không có gì sai với NSLog trong mã sản xuất. Trong thực tế, nó có thể hữu ích cho người dùng. Điều đó nói rằng, nếu lý do duy nhất bạn đang sử dụng NSLog là để giúp gỡ lỗi, thì, vâng, điều đó nên được xóa trước khi bạn phát hành.

Hơn nữa, vì bạn đã gắn thẻ câu hỏi này dưới dạng câu hỏi về iPhone, NSLog lấy tài nguyên, đây là điều mà iPhone có rất ít. Nếu bạn NSLogging bất cứ thứ gì trên iPhone, điều đó sẽ lấy đi thời gian của bộ xử lý từ ứng dụng của bạn. Sử dụng nó một cách rộng rãi.


4

Sự thật đơn giản là NSLog chỉ đơn giản là chậm.

Nhưng tại sao? Để trả lời câu hỏi đó, chúng ta hãy tìm hiểu NSLog làm gì, và sau đó làm thế nào.

NSLog làm gì chính xác?

NSLog làm 2 việc:

Nó viết thông điệp tường trình đến cơ sở Nhật ký hệ thống Apple (asl). Điều này cho phép các thông điệp tường trình hiển thị trong Console.app. Nó cũng kiểm tra xem liệu luồng stderr của ứng dụng có đi đến một thiết bị đầu cuối không (chẳng hạn như khi ứng dụng đang được chạy qua Xcode). Nếu vậy nó ghi thông điệp tường trình vào stderr (để nó hiển thị trong bảng điều khiển Xcode).

Viết thư cho STDERR không có vẻ khó khăn. Điều đó có thể được thực hiện với fprintf và tham chiếu mô tả tệp stderr. Nhưng còn asl thì sao?

Tài liệu tốt nhất tôi tìm thấy về ASL là một bài đăng trên blog gồm 10 phần của Peter Hosey: link

Không đi sâu vào chi tiết, điểm nổi bật (vì nó liên quan đến hiệu suất) là:

Để gửi một thông điệp tường trình đến cơ sở ASL, về cơ bản bạn mở một kết nối máy khách đến daemon ASL và gửi tin nhắn. NHƯNG - mỗi luồng phải sử dụng một kết nối máy khách riêng biệt. Vì vậy, để an toàn cho chuỗi, mỗi khi NSLog được gọi, nó sẽ mở kết nối máy khách asl mới, gửi tin nhắn và sau đó đóng kết nối.

Tài nguyên có thể được tìm thấy ở đâyở đây .


Chỉnh sửa văn bản. Các tài nguyên không cần phải ở chân trang.
Johan Karlsson

2

Như đã lưu ý trong các câu trả lời khác, bạn có thể sử dụng #define để thay đổi xem NSLog có được sử dụng hay không tại thời điểm biên dịch.

Tuy nhiên, cách linh hoạt hơn là sử dụng thư viện ghi nhật ký như Cacao Lumberjack phép bạn thay đổi liệu có thứ gì đó được ghi lại trong thời gian chạy hay không.

Trong mã của bạn thay thế NSLog bằng DDLogVerbose hoặc DDLogError, v.v., hãy thêm #import cho các định nghĩa macro, v.v. và thiết lập các loggers, thường là trong phương thức applicationDidFinishLaunching.

Để có tác dụng tương tự như NSLog, mã cấu hình là

[DDLog addLogger:[DDASLLogger sharedInstance]];
[DDLog addLogger:[DDTTYLogger sharedInstance]];

2

Từ quan điểm bảo mật, nó phụ thuộc vào những gì đang được ghi lại. NếuNSLog (hoặc các logger khác) đang ghi thông tin nhạy cảm, thì bạn nên xóa logger trong mã sản xuất.

Từ quan điểm kiểm toán, kiểm toán viên không muốn xem xét mỗi lần sử dụng NSLog để đảm bảo thông tin không nhạy cảm của mình. Anh ấy / cô ấy sẽ chỉ cần nói với bạn để loại bỏ logger.

Tôi làm việc với cả hai nhóm. Chúng tôi kiểm toán mã, viết hướng dẫn mã hóa, v.v ... Hướng dẫn của chúng tôi yêu cầu đăng nhập bị vô hiệu hóa trong mã sản xuất. Vì vậy, các nhóm nội bộ biết không nên thử nó;)

Chúng tôi cũng sẽ từ chối một ứng dụng bên ngoài đăng nhập vào sản xuất vì chúng tôi không muốn chấp nhận rủi ro liên quan đến việc vô tình rò rỉ thông tin nhạy cảm. Chúng tôi không quan tâm những gì nhà phát triển nói với chúng tôi. Nó chỉ đơn giản là không có giá trị thời gian của chúng tôi để điều tra.

Và hãy nhớ rằng, chúng tôi định nghĩa 'nhạy cảm', chứ không phải nhà phát triển;)

Tôi cũng nhận thấy một ứng dụng thực hiện nhiều hoạt động đăng nhập như một ứng dụng đã sẵn sàng để phát nổ. Có một lý do rất nhiều đăng nhập được thực hiện / cần thiết, và nó thường không ổn định. Nó ở ngay trên đó với các chủ đề 'watchdog' khởi động lại các dịch vụ treo.

Nếu bạn chưa từng xem qua đánh giá Kiến trúc bảo mật (SecArch), đây là những thứ chúng tôi xem xét.


1

Bạn không nên dài dòng với printf hoặc NSLog trong mã phát hành. Chỉ thử thực hiện printf hoặc NSLog nếu ứng dụng có lỗi gì đó xảy ra với nó, IE là một lỗi không thể khắc phục.


1

Hãy nhớ rằng NSLogs có thể làm chậm UI / luồng chính. Tốt nhất là loại bỏ chúng khỏi các bản dựng phát hành trừ khi thực sự cần thiết.


0

Tôi rất khuyên bạn nên sử dụng TestFlight để đăng nhập (miễn phí). Phương pháp của họ sẽ ghi đè NSLog (sử dụng macro) và cho phép bạn bật / tắt ghi nhật ký vào máy chủ của họ, nhật ký hệ thống Apple và nhật ký STDERR, cho tất cả các cuộc gọi hiện tại của bạn tới NSLog. Điều thú vị ở đây là bạn vẫn có thể xem lại thông điệp tường trình của mình cho các ứng dụng được triển khai cho người kiểm tra và ứng dụng được triển khai trong App Store, mà không có nhật ký xuất hiện trên nhật ký hệ thống của người dùng. Tốt nhất của cả hai thế giới.


Bạn nên xem xét chi phí mà TestFlight thêm vào ứng dụng. Có thể chỉ thêm phần đăng nhập của TestFlight không?
Johan Karlsson
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.