Kotlin tidak memiliki gagasan yang sama tentang bidang statis seperti yang digunakan di Jawa. Di Jawa, cara penebangan yang diterima secara umum adalah:
public class Foo {
private static final Logger LOG = LoggerFactory.getLogger(Foo.class);
}
Pertanyaannya adalah apa cara idiomatis melakukan penebangan di Kotlin?
kotlin
kotlin-logging
mchlstckl
sumber
sumber
Any
(sehingga membutuhkan gips)?this.javaClass
untuk masing-masing. Tapi saya tidak merekomendasikannya sebagai solusi.Jawaban:
Di sebagian besar kode Kotlin dewasa, Anda akan menemukan salah satu pola ini di bawah. Pendekatan menggunakan Delegasi Properti mengambil keuntungan dari kekuatan Kotlin untuk menghasilkan kode terkecil.
Catatan: kode di sini adalah untuk
java.util.Logging
tetapi teori yang sama berlaku untuk pustaka logging manapunSeperti statis (umum, setara dengan kode Java Anda dalam pertanyaan)
Jika Anda tidak dapat mempercayai kinerja pencarian hash di dalam sistem logging, Anda bisa mendapatkan perilaku yang mirip dengan kode Java Anda dengan menggunakan objek pendamping yang dapat menyimpan instance dan terasa seperti statis untuk Anda.
menciptakan output:
Lebih lanjut tentang objek pengiring di sini: Objek Pengiring ... Juga perhatikan bahwa dalam sampel di atas
MyClass::class.java
mendapatkan instance tipeClass<MyClass>
untuk logger, sedangkanthis.javaClass
akan mendapatkan instance tipeClass<MyClass.Companion>
.Per Instance of a Class (umum)
Tapi, sebenarnya tidak ada alasan untuk menghindari panggilan dan mendapatkan logger di tingkat instance. Cara Jawa idiomatis yang Anda sebutkan sudah usang dan didasarkan pada rasa takut akan kinerja, sedangkan penebang per kelas sudah di-cache oleh hampir semua sistem logging yang masuk akal di planet ini. Cukup buat anggota untuk memegang objek logger.
menciptakan output:
Anda dapat menguji kinerja per variasi instance dan per kelas dan melihat apakah ada perbedaan realistis untuk sebagian besar aplikasi.
Delegasi Properti (umum, paling elegan)
Pendekatan lain, yang disarankan oleh @Jire dalam jawaban lain, adalah membuat delegasi properti, yang kemudian dapat Anda gunakan untuk melakukan logika secara seragam di kelas lain yang Anda inginkan. Ada cara yang lebih sederhana untuk melakukan ini karena Kotlin sudah menyediakan
Lazy
delegasi, kita bisa membungkusnya dalam suatu fungsi. Satu trik di sini adalah jika kita ingin mengetahui tipe kelas yang saat ini menggunakan delegate, kita menjadikannya fungsi ekstensi pada kelas apa saja:Kode ini juga memastikan bahwa jika Anda menggunakannya di objek pendamping, nama logger akan sama seperti jika Anda menggunakannya di kelas itu sendiri. Sekarang Anda cukup:
untuk per instance kelas, atau jika Anda ingin agar lebih statis dengan satu instance per kelas:
Dan hasil Anda dari memanggil
foo()
kedua kelas ini adalah:Fungsi Ekstensi (tidak umum dalam kasus ini karena "polusi" dari namespace Apa saja)
Kotlin memiliki beberapa trik tersembunyi yang memungkinkan Anda membuat beberapa kode ini menjadi lebih kecil. Anda dapat membuat fungsi ekstensi di kelas dan karenanya memberi mereka fungsi tambahan. Satu saran dalam komentar di atas adalah untuk memperluas
Any
dengan fungsi logger. Ini dapat membuat noise kapan saja seseorang menggunakan penyelesaian kode dalam IDE mereka di kelas mana pun. Tetapi ada manfaat rahasia untuk memperluasAny
atau antarmuka penanda lain: Anda dapat menyiratkan bahwa Anda memperluas kelas Anda sendiri dan karenanya mendeteksi kelas Anda berada di dalam. Hah? Agar tidak membingungkan, berikut ini kodenya:Sekarang di dalam kelas (atau objek pendamping), saya cukup memanggil ekstensi ini di kelas saya sendiri:
Memproduksi output:
Pada dasarnya, kode ini dilihat sebagai panggilan ke ekstensi
Something.logger()
. Masalahnya adalah bahwa hal-hal berikut ini juga bisa benar menciptakan "polusi" pada kelas lain:Fungsi Ekstensi pada Antarmuka Penanda (tidak yakin seberapa umum, tetapi model umum untuk "ciri")
Untuk membuat penggunaan ekstensi lebih bersih dan mengurangi "polusi", Anda dapat menggunakan antarmuka penanda untuk memperluas:
Atau bahkan menjadikan metode bagian dari antarmuka dengan implementasi default:
Dan gunakan salah satu dari variasi ini di kelas Anda:
Memproduksi output:
Jika Anda ingin memaksa pembuatan bidang yang seragam untuk menahan logger, maka saat menggunakan antarmuka ini Anda dapat dengan mudah meminta pelaksana memiliki bidang seperti
LOG
:Sekarang pelaksana antarmuka harus terlihat seperti ini:
Tentu saja, kelas dasar abstrak dapat melakukan hal yang sama, memiliki opsi antarmuka dan kelas abstrak yang mengimplementasikan antarmuka memungkinkan fleksibilitas dan keseragaman:
Putting It All Together (Perpustakaan pembantu kecil)
Berikut adalah perpustakaan pembantu kecil untuk membuat salah satu opsi di atas mudah digunakan. Adalah umum di Kotlin untuk memperpanjang API agar lebih sesuai dengan keinginan Anda. Baik dalam fungsi ekstensi atau tingkat atas. Berikut adalah campuran untuk memberi Anda opsi cara membuat logger, dan sampel yang menunjukkan semua variasi:
Pilih yang mana yang ingin Anda simpan, dan berikut ini semua opsi yang digunakan:
Ke-13 instance dari logger yang dibuat dalam sampel ini akan menghasilkan nama logger yang sama, dan output:
Catatan: The
unwrapCompanionClass()
Metode memastikan bahwa kita tidak menghasilkan logger dinamai objek pendamping melainkan kelas melampirkan. Ini adalah cara yang disarankan saat ini untuk menemukan kelas yang berisi objek pendamping. Menghapus " $ Companion " dari penggunaan namaremoveSuffix()
tidak berfungsi karena objek pendamping dapat diberikan nama khusus.sumber
ofClass.enclosingClass.kotlin.objectInstance?.javaClass
bukanofClass.enclosingClass.kotlin.companionObject?.java
compile 'org.jetbrains.kotlin:kotlin-reflect:1.0.2'
public fun <R : Any> R.logger(): Lazy<Logger> { return lazy{Logger.getLogger(unwrapCompanionClass(this.javaClass).name)}}
) muncul untuk membuat fungsi ekstensi sehingga"".logger()
sekarang menjadi sesuatu, apakah ini seharusnya berperilaku seperti ini?Silahkan lihat di Kotlin-logging perpustakaan.
Itu memungkinkan logging seperti itu:
Atau seperti itu:
Saya juga menulis posting blog yang membandingkannya dengan
AnkoLogger
: Masuk di Kotlin & Android: AnkoLogger vs kotlin-loggingPenafian: Saya adalah pengelola perpustakaan itu.
Sunting: logging kotlin sekarang memiliki dukungan multi platform: https://github.com/MicroUtils/kotlin-logging/wiki/Multiplatform-support
sumber
logger.info()
panggilan, seperti Jayson lakukan dalam jawaban diterima-Nya.Sebagai contoh implementasi logging yang baik, saya ingin menyebutkan Anko yang menggunakan antarmuka khusus
AnkoLogger
yang harus diimplementasikan oleh kelas. Di dalam antarmuka ada kode yang menghasilkan tag logging untuk kelas. Logging kemudian dilakukan melalui fungsi ekstensi yang dapat dipanggil di dalam implementasi interace tanpa awalan atau bahkan pembuatan instance logger.Saya tidak berpikir ini idiomatik , tetapi tampaknya pendekatan yang bagus karena memerlukan kode minimum, hanya menambahkan antarmuka ke deklarasi kelas, dan Anda mendapatkan logging dengan tag berbeda untuk kelas yang berbeda.
Kode di bawah ini pada dasarnya adalah AnkoLogger , disederhanakan dan ditulis ulang untuk penggunaan Android-agnostik.
Pertama, ada antarmuka yang berperilaku seperti antarmuka penanda:
Itu memungkinkan penerapannya menggunakan fungsi ekstensi untuk
MyLogger
di dalam kode mereka hanya memanggil merekathis
. Dan itu juga berisi tag logging.Berikutnya, ada titik masuk umum untuk berbagai metode logging:
Ini akan dipanggil dengan metode logging. Itu mendapat tag dari
MyLogger
implementasi, memeriksa pengaturan logging dan kemudian memanggil satu dari dua penangan, satu denganThrowable
argumen dan satu tanpa.Kemudian Anda dapat mendefinisikan metode logging sebanyak yang Anda suka, dengan cara ini:
Ini didefinisikan satu kali untuk mencatat pesan dan mencatat
Throwable
juga, ini dilakukan denganthrowable
parameter opsional .Fungsi yang dilewatkan sebagai
handler
danthrowableHandler
dapat berbeda untuk metode logging yang berbeda, misalnya, mereka dapat menulis log untuk file atau mengunggahnya di suatu tempat.isLoggingEnabled
danLoggingLevels
dihilangkan untuk singkatnya, tetapi menggunakannya memberikan lebih banyak fleksibilitas.Ini memungkinkan untuk penggunaan berikut:
Ada kelemahan kecil: objek logger akan diperlukan untuk masuk dalam fungsi tingkat paket:
sumber
android.util.Log
untuk melakukan pendataan. Apa niatmu? gunakan Anko? Dari membangun sesuatu yang mirip saat menggunakan Anko sebagai contoh (lebih baik jika Anda hanya memasukkan kode yang disarankan sebaris dan memperbaikinya untuk non-Android daripada mengatakan "port ini ke non-Android, inilah tautannya". Alih-alih Anda menambahkan kode sampel menelepon Anko)KISS: Untuk Tim Java yang Bermigrasi ke Kotlin
Jika Anda tidak keberatan memberikan nama kelas pada setiap instance logger (seperti halnya java), Anda dapat membuatnya tetap sederhana dengan mendefinisikan ini sebagai fungsi tingkat atas di suatu tempat di proyek Anda:
Ini menggunakan parameter jenis yang direvisi Kotlin .
Sekarang, Anda dapat menggunakan ini sebagai berikut:
Pendekatan ini super-sederhana dan dekat dengan setara java, tetapi hanya menambahkan beberapa gula sintaksis.
Langkah Selanjutnya: Ekstensi atau Delegasi
Saya pribadi lebih suka melangkah lebih jauh dan menggunakan pendekatan ekstensi atau delegasi. Ini dirangkum dengan baik dalam jawaban @ JaysonMinard, tetapi di sini adalah TL; DR untuk pendekatan "Delegasi" dengan API log4j2 ( PEMBARUAN : tidak perlu lagi menulis kode ini secara manual, karena telah dirilis sebagai modul resmi dari proyek log4j2, lihat di bawah). Karena log4j2, tidak seperti slf4j, mendukung pencatatan dengan
Supplier
, saya juga menambahkan delegasi untuk menjadikan penggunaan metode ini lebih sederhana.Log4j2 Kotlin Logging API
Sebagian besar bagian sebelumnya telah disesuaikan secara langsung untuk menghasilkan modul API Logging Kotlin , yang sekarang merupakan bagian resmi dari Log4j2 (penafian: Saya adalah penulis utama). Anda dapat mengunduh ini langsung dari Apache , atau melalui Maven Central .
Penggunaan pada dasarnya seperti yang dijelaskan di atas, tetapi modul mendukung akses logger berbasis antarmuka,
logger
fungsi ekstensi aktifAny
untuk digunakan di manathis
didefinisikan, dan fungsi logger bernama untuk digunakan di mana tidak adathis
yang didefinisikan (seperti fungsi tingkat atas).sumber
T.logger()
- lihat bagian bawah contoh kode.Anko
Anda dapat menggunakan
Anko
perpustakaan untuk melakukannya. Anda akan memiliki kode seperti di bawah ini:kotlin-logging
perpustakaan kotlin-logging ( proyek Github - kotlin-logging ) memungkinkan Anda untuk menulis kode logging seperti di bawah ini:
StaticLog
atau Anda juga bisa menggunakan tulisan kecil ini di perpustakaan Kotlin bernama
StaticLog
maka kode Anda akan terlihat seperti:Solusi kedua mungkin lebih baik jika Anda ingin menentukan format output untuk metode logging seperti:
atau gunakan filter, misalnya:
timberkt
Jika Anda sudah menggunakan
Timber
cek perpustakaan logging Jake Whartontimberkt
.Contoh kode:
Periksa juga: Masuk ke Kotlin & Android: AnkoLogger vs kotlin-logging
Semoga ini bisa membantu
sumber
Apakah sesuatu seperti ini cocok untuk Anda?
sumber
LoggerDelegate
Dan kemudian ia menciptakan fungsi tingkat atas yang membuat lebih mudah untuk membuat instance dari delegasi (tidak jauh lebih mudah, tetapi sedikit). Dan fungsi itu harus diubah menjadiinline
. Kemudian ia menggunakan delegasi untuk menyediakan logger kapan pun diinginkan. Tetapi menyediakan satu untuk temanFoo.Companion
dan bukan untuk kelasFoo
jadi mungkin tidak seperti yang dimaksudkan.logger()
fungsinyainline
jika tidak ada lambda. IntelliJ menyarankan inlining dalam kasus ini tidak perlu: i.imgur.com/YQH3NB1.pngLazy
. Dengan trik untuk mengetahui kelas apa yang ada di dalamnya.Saya belum pernah mendengar ungkapan dalam hal ini. Semakin sederhana semakin baik, jadi saya akan menggunakan properti tingkat atas
Praktek ini berfungsi baik dalam Python, dan berbeda seperti Kotlin dan Python mungkin muncul, saya percaya mereka sangat mirip di sana "roh" (berbicara tentang idiom).
sumber
val log = what?!?
... membuat logger dengan nama? Mengabaikan fakta, pertanyaan itu menunjukkan ia ingin membuat logger untuk kelas tertentuLoggerFactory.getLogger(Foo.class);
Bagaimana dengan fungsi ekstensi di Kelas saja? Dengan begitu Anda berakhir dengan:
Catatan - Saya belum pernah menguji ini sama sekali, jadi mungkin tidak tepat.
sumber
Pertama, Anda dapat menambahkan fungsi ekstensi untuk pembuatan logger.
Maka Anda akan dapat membuat logger menggunakan kode berikut.
Kedua, Anda bisa mendefinisikan antarmuka yang menyediakan logger dan implementasinya.
Antarmuka ini dapat digunakan dengan cara berikut.
sumber
buat objek pengiring dan tandai bidang yang sesuai dengan anotasi @JvmStatic
sumber
Sudah ada banyak jawaban bagus di sini, tetapi semuanya berkenaan dengan menambahkan logger ke kelas, tetapi bagaimana Anda melakukan itu untuk melakukan login di Fungsi Level Top?
Pendekatan ini generik dan cukup sederhana untuk bekerja dengan baik di kedua kelas, objek pendamping dan Fungsi Level Top:
sumber
Itulah tujuan objek pendamping, secara umum: mengganti barang statis.
sumber
JvmStatic
anotasi. Dan di masa depan mungkin ada lebih dari satu yang diizinkan. Plus jawaban ini sangat tidak membantu tanpa lebih banyak informasi atau sampel.Factory
dan yang lainHelpers
Contoh Slf4j, sama untuk orang lain. Ini bahkan berfungsi untuk membuat logger tingkat paket
Pemakaian:
sumber
sumber
Ini masih WIP (hampir selesai) jadi saya ingin membagikannya: https://github.com/leandronunes85/log-format-enforcer#kotlin-soon-to-come-in-version-14
Tujuan utama perpustakaan ini adalah untuk menegakkan gaya log tertentu di seluruh proyek. Dengan membuatnya menghasilkan kode Kotlin saya mencoba untuk mengatasi beberapa masalah yang disebutkan dalam pertanyaan ini. Sehubungan dengan pertanyaan awal yang biasanya saya cenderung lakukan adalah hanya:
sumber
Anda cukup membangun "perpustakaan" utilitas Anda sendiri. Anda tidak memerlukan perpustakaan besar untuk tugas ini yang akan membuat proyek Anda lebih berat dan kompleks.
Misalnya, Anda bisa menggunakan Refleksi Kotlin untuk mendapatkan nama, jenis, dan nilai properti kelas apa pun.
Pertama-tama, pastikan meta-dependensi Anda sudah ada di build.gradle Anda:
Setelah itu, Anda cukup menyalin dan menempelkan kode ini ke proyek Anda:
Contoh penggunaan:
sumber