Apakah benar bahwa seseorang tidak boleh menggunakan NSLog () pada kode produksi?

155

Saya diberitahu ini beberapa kali di situs ini, tetapi saya ingin memastikan ini benar-benar terjadi.

Saya berharap dapat menaburkan panggilan fungsi NSLog di seluruh kode saya, dan bahwa Xcode / gcc akan secara otomatis menghapus panggilan-panggilan itu ketika membangun rilis / distribusi saya dibangun.

Haruskah saya menghindari menggunakan ini? Jika demikian, alternatif apa yang paling umum di antara programmer Objective-C berpengalaman?

jpm
sumber
7
Saya tahu pertanyaan ini sekarang sudah sangat tua, tetapi, jika Anda masih bisa, saya akan menandai jawaban Marc Charbonneau diterima. Saya telah memodifikasi jawaban saya untuk menunjuk pada jawabannya, tetapi jawabannya adalah yang benar.
e.James
5
NSLog () di dalam loop yang sering benar-benar akan membunuh kinerja Anda, katanya, setelah menemukan cara yang sulit.
willc2

Jawaban:

197

Makro preprocessor memang bagus untuk debugging. Tidak ada yang salah dengan NSLog (), tetapi mudah untuk mendefinisikan fungsi logging Anda sendiri dengan fungsionalitas yang lebih baik. Ini yang saya gunakan, termasuk nama file dan nomor baris untuk memudahkan melacak pernyataan log.

#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

Saya merasa lebih mudah untuk menempatkan seluruh pernyataan ini di header awalan daripada file sendiri. Anda bisa, jika mau, membangun sistem logging yang lebih rumit dengan membuat DebugLog berinteraksi dengan objek Objective-C yang normal. Misalnya, Anda bisa memiliki kelas logging yang menulis ke file log sendiri (atau database), dan menyertakan argumen 'prioritas' yang dapat Anda atur saat runtime, sehingga pesan debug tidak ditampilkan dalam versi rilis Anda, tetapi pesan kesalahannya adalah ( jika Anda melakukan ini, Anda bisa membuat DebugLog (), WarningLog (), dan sebagainya).

Oh, dan perlu diingat #define DEBUG_MODEdapat digunakan kembali di berbagai tempat dalam aplikasi Anda. Misalnya, dalam aplikasi saya, saya menggunakannya untuk menonaktifkan pemeriksaan kunci lisensi dan hanya mengizinkan aplikasi untuk menjalankannya sebelum tanggal tertentu. Ini memungkinkan saya mendistribusikan salinan beta yang berfungsi penuh dan terbatas waktu dengan sedikit usaha di pihak saya.

Marc Charbonneau
sumber
8
+1 untuk jawaban yang sangat bagus. Saya telah mengubah milik saya untuk menunjukkan bahwa macro #define Anda adalah caranya, dan saya harap OP mengganti jawaban yang diterima (saya meninggalkannya komentar). Saya menggunakan fungsi dummy karena saya tidak tahu bahwa Anda bisa menggunakan ... argumen di makro. Langsung & belajar!
e.James
15
Jawaban yang sangat bagus, meskipun saya sarankan menggunakan awalan pribadi pada definisi "DEBUG_MODE" Anda, seperti menyebutnya "JPM_DEBUG" atau sejenisnya. Terlalu sering saya temui kode pihak ketiga yang juga menggunakan DEBUG atau DEBUG_MODE atau sejenisnya, dan kadang-kadang kode itu tidak akan berfungsi dengan benar dalam mode DEBUG. Jika Anda ingin mengaktifkan debug perpustakaan pihak ketiga, Anda harus melakukannya dengan sengaja. (Tentu saja, penulis perpustakaanlah yang seharusnya mengawali simbol mereka, tetapi banyak kerangka kerja C dan C ++ tidak, terutama untuk definisi ini).
Rob Napier
1
apakah ada makro yang telah ditentukan Xcode yang dapat digunakan untuk mengaktifkan ini hanya ketika konfigurasi diatur ke debug? Saya lebih suka tidak secara manual mengatur makro preprosesor ini sendiri di setiap proyek. dapatkah kita melakukan sesuatu seperti mengikuti kodesemu #jika XCODE_CONFIGURATION == DEBUG?
frankodwyer
1
#include <TargetConditionals.h>
slf
2
Pendekatan ini mengarah ke peringatan "variabel tidak terpakai" palsu dari kompiler dalam mode rilis ketika pernyataan logging menggunakan variabel perantara untuk tujuan tunggal menghitung nilai untuk dicatat. Apa cara paling cerdas untuk menghindarinya jika Anda membenci peringatan kompiler sebanyak yang saya lakukan?
Jean-Denis Muys
78

Letakkan 3 baris ini di akhir file -prefix.pch:

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

Anda tidak perlu mendefinisikan apa pun ke dalam proyek Anda, karena DEBUGditentukan dalam pengaturan build Anda secara default ketika Anda membuat proyek Anda.

roel
sumber
2
Sejauh ini solusi terbaik. Anda perlu menambahkan prefix.pch secara manual dari XCode 6.
Teddy
Masih kita perlu mengubah pengaturan build sebelum rilis yaitu debug untuk melepaskan
UserDev
25

Panggilan NSLog dapat dibiarkan dalam kode produksi, tetapi hanya boleh ada untuk kasus yang benar-benar luar biasa, atau informasi yang diinginkan yang akan dicatat ke log sistem.

Aplikasi yang mengotori log sistem menjengkelkan, dan dianggap tidak profesional.

Matthew Schinckel
sumber
14
Maaf - tampil sebagai tidak profesional untuk siapa? Siapa yang cenderung memeriksa log Anda pada aplikasi yang dirilis dan menilai profesionalisme Anda berdasarkan itu? (Untuk lebih jelasnya, saya setuju sepenuhnya bahwa Anda seharusnya tidak menyimpan satu ton NSLogs dalam versi rilis aplikasi Anda, tetapi saya bingung dengan argumen 'profesionalisme'.)
WendiKidd
4
Pengembang lain akan melakukan apa yang Anda lakukan dan merasa terganggu. Android memiliki masalah serupa dengan beberapa pengembang menjadi plus.google.com/110166527124367568225/posts/h4jK38n4XYR
Roger Binns
24

Saya tidak dapat mengomentari jawaban Marc Charbonneau , jadi saya akan memposting ini sebagai jawaban.

Lebih lanjut untuk menambahkan makro ke header yang sudah dikompilasi sebelumnya, Anda dapat menggunakan konfigurasi build Target untuk mengontrol mendefinisikan (atau kurang mendefinisikan) the DEBUG_MODE.

Jika Anda memilih konfigurasi aktif " Debug ", DEBUG_MODEakan ditentukan, dan makro memperluas ke NSLogdefinisi penuh .

Memilih konfigurasi aktif " Release " tidak akan menentukan DEBUG_MODEdan NSLogging Anda dihilangkan dari rilis build.

Langkah:

  • Target> Dapatkan Info
  • Bangun tab
  • Cari "PreProcessor Macros" (atau GCC_PREPROCESSOR_DEFINITIONS)
  • Pilih Konfigurasi: Debug
  • Edit Definisi pada Tingkat ini
  • Menambahkan DEBUG_MODE=1
  • Pilih Konfigurasi: Lepaskan
  • konfirmasi DEBUG_MODEtidak diaturGCC_PREPROCESSOR_DEFINITIONS

jika Anda menghilangkan karakter '=' dalam definisi, Anda akan mendapatkan kesalahan dari preprocessor

Juga, tempel komentar ini (ditunjukkan di bawah) di atas definisi makro untuk mengingatkan Anda dari mana DEBUG_MACROdefinisi berasal;)

// Target > Get Info > Build > GCC_PREPROCESSOR_DEFINITIONS
// Configuration = Release: <empty>
//               = Debug:   DEBUG_MODE=1
ohhorob
sumber
1
Ini adalah jawaban tambahan yang berharga untuk pertanyaan itu. Pantas menjadi lebih dari sekadar komentar.
morningstar
DEBUG_MODEdan DEBUG_MACROtidak konvensional. Saya hanya menemukan satu referensi DEBUG_MACROdi situs apple ( opensource.apple.com/source/gm4/gm4-15/src/m4.h?txt ). Mungkin lebih standar DEBUGdan NDEBUGakan menjadi pilihan yang lebih baik? NDEBUGditentukan oleh Posix; sementara DEBUGdigunakan oleh konvensi.
jww
+1 Ya ini posting lama, tapi itu intinya ... Di versi Xcode saya (4 tahun kemudian), pencarian untuk GCC_PREPROCESSOR_DEFINITIONS menghasilkan beberapa bahasa yang berbeda. Silakan pertimbangkan untuk memperbarui jawaban yang luar biasa ini untuk kejelasan.
David
11

EDIT: The Metode diposting oleh Marc Charbonneau , dan dibawa ke perhatian saya dengan sho , jauh lebih baik daripada satu ini.

Saya telah menghapus bagian dari jawaban saya yang disarankan menggunakan fungsi kosong untuk menonaktifkan logging ketika mode debug dinonaktifkan. Bagian yang berhubungan dengan pengaturan makro preprosesor otomatis masih relevan, jadi tetap. Saya juga telah mengedit nama makro preprosesor sehingga lebih cocok dengan jawaban Marc Charbonneau.


Untuk mencapai perilaku otomatis (dan yang diharapkan) dalam Xcode:

Di pengaturan proyek, buka tab "Bangun", dan pilih konfigurasi "Debug". Temukan bagian "Preprocessor Macros", dan tambahkan makro bernama DEBUG_MODE.

...

EDIT: Lihat jawaban Marc Charbonneau untuk cara yang tepat untuk mengaktifkan dan menonaktifkan logging dengan DEBUG_MODEmakro.

e.James
sumber
7

Saya setuju dengan Matthew. Tidak ada yang salah dengan NSLog dalam kode produksi. Bahkan, ini bisa bermanfaat bagi pengguna. Yang mengatakan, jika satu-satunya alasan Anda menggunakan NSLog adalah untuk membantu debug, maka, ya, itu harus dihapus sebelum Anda rilis.

Selain itu, karena Anda telah menandai ini sebagai pertanyaan iPhone, NSLog membutuhkan sumber daya, yang merupakan sesuatu yang berharga sedikit dari iPhone. Jika Anda NSLogging apa pun di iPhone, itu menghilangkan waktu prosesor dari aplikasi Anda. Gunakan dengan bijak.

Agustus
sumber
4

Kebenaran sederhana adalah bahwa NSLog sangat lambat.

Tapi kenapa? Untuk menjawab pertanyaan itu, mari cari tahu apa yang dilakukan NSLog, lalu bagaimana cara melakukannya.

Apa yang dilakukan NSLog tepatnya?

NSLog melakukan 2 hal:

Itu menulis pesan log ke fasilitas Apple System Logging (asl). Ini memungkinkan pesan log muncul di Console.app. Itu juga memeriksa untuk melihat apakah aliran stderr aplikasi pergi ke terminal (seperti ketika aplikasi dijalankan melalui Xcode). Jika demikian, ia menulis pesan log ke stderr (sehingga muncul di konsol Xcode).

Menulis ke STDERR tidak terdengar sulit. Itu dapat diselesaikan dengan fprintf dan referensi deskriptor file stderr. Tapi bagaimana dengan dpl?

Dokumentasi terbaik yang saya temukan tentang ASL adalah posting blog 10 bagian dari Peter Hosey: tautan

Tanpa terlalu banyak detail, sorotan (karena menyangkut kinerja) adalah ini:

Untuk mengirim pesan log ke fasilitas ASL, Anda pada dasarnya membuka koneksi klien ke daemon ASL dan mengirim pesan. TETAPI - setiap utas harus menggunakan koneksi klien yang terpisah. Jadi, agar aman thread, setiap kali NSLog dipanggil membuka koneksi klien asl baru, mengirim pesan, dan kemudian menutup koneksi.

Sumber daya dapat ditemukan di sini & di sini .

Andrew
sumber
Mengedit teks. Sumber daya hanya perlu di catatan kaki.
Johan Karlsson
2

Seperti disebutkan dalam jawaban lain, Anda dapat menggunakan #define untuk mengubah apakah NSLog digunakan atau tidak pada waktu kompilasi.

Namun cara yang lebih fleksibel adalah dengan menggunakan perpustakaan logging seperti Cocoa Lumberjack yang memungkinkan Anda untuk mengubah apakah ada sesuatu yang dicatat saat runtime juga.

Dalam kode Anda, ganti NSLog dengan DDLogVerbose atau DDLogError dll, tambahkan #import untuk definisi makro dll dan setup logger, sering kali dalam metode applicationDidFinishLaunching.

Untuk memiliki efek yang sama dengan NSLog, kode konfigurasinya adalah

[DDLog addLogger:[DDASLLogger sharedInstance]];
[DDLog addLogger:[DDTTYLogger sharedInstance]];
mmmmmm
sumber
2

Dari sudut pandang keamanan, itu tergantung pada apa yang sedang dicatat. Jika NSLog(atau penebang lain) sedang menulis informasi sensitif, maka Anda harus menghapus logger dalam kode produksi.

Dari sudut pandang audit, auditor tidak ingin melihat setiap penggunaan NSLog untuk memastikan tidak mencatat informasi sensitif. Dia hanya akan memberitahu Anda untuk menghapus logger.

Saya bekerja dengan kedua kelompok. Kami mengaudit kode, menulis panduan pengkodean, dll. Panduan kami mengharuskan penebangan dinonaktifkan dalam kode produksi. Jadi tim internal tahu untuk tidak mencobanya;)

Kami juga akan menolak aplikasi eksternal yang mencatat produksi karena kami tidak ingin menerima risiko terkait dengan bocornya informasi sensitif. Kami tidak peduli apa yang dikatakan pengembang kepada kami. Sama sekali tidak layak waktu kita untuk menyelidiki.

Dan ingat, kami mendefinisikan 'sensitif', dan bukan pengembang;)

Saya juga melihat aplikasi yang melakukan banyak logging sebagai aplikasi yang siap meledak. Ada alasan mengapa begitu banyak logging dilakukan / dibutuhkan, dan biasanya tidak stabil. Tepat di sana dengan utas pengawas yang memulai kembali layanan yang digantung.

Jika Anda belum pernah melalui tinjauan Arsitektur Keamanan (SecArch), ini adalah beberapa hal yang kami lihat.

jww
sumber
1

Anda seharusnya tidak perlu bertele-tele dengan printf atau NSLog dalam kode rilis. Cobalah hanya melakukan printf atau NSLog jika aplikasi memiliki sesuatu yang buruk terjadi, yaitu kesalahan yang tidak dapat dipulihkan.

MaddTheSane
sumber
1

Perlu diingat bahwa NSLogs dapat memperlambat UI / utas utama. Yang terbaik adalah menghapusnya dari rilis build kecuali benar-benar diperlukan.

psy
sumber
0

Saya akan sangat menyarankan menggunakan TestFlight untuk logging (gratis). Metode mereka akan menimpa NSLog (menggunakan makro) dan memungkinkan Anda untuk mengaktifkan / menonaktifkan logging ke server mereka, log Sistem Apple dan log STDERR, untuk semua panggilan Anda yang ada ke NSLog. Yang menyenangkan tentang ini adalah Anda masih dapat meninjau pesan log Anda untuk aplikasi yang digunakan untuk penguji dan aplikasi yang digunakan di App Store, tanpa log yang muncul di log sistem pengguna. Terbaik dari kedua dunia.

Joel
sumber
Seseorang harus mempertimbangkan overhead yang ditambahkan TestFlight ke aplikasi. Apakah mungkin hanya menambahkan bagian logging dari TestFlight?
Johan Karlsson