Hayatımda ilk kez kendimi açık kaynaklı bir Java API yazdığım bir konumda buldum. Umarım diğer birçok projeye dahil edilir.
Günlüğe kaydetmek için ben (ve gerçekten birlikte çalıştığım insanlar) her zaman JUL (java.util.logging) kullandım ve bununla ilgili hiçbir sorun yaşamadım. Ancak şimdi API geliştirme için ne yapmam gerektiğini daha ayrıntılı olarak anlamalıyım. Bu konuda biraz araştırma yaptım ve elde ettiğim bilgilerle daha da kafam karıştı. Dolayısıyla bu yazı.
TEMMUZ'dan geldiğimden beri bu konuda önyargılıyım. Gerisi hakkındaki bilgim o kadar büyük değil.
Yaptığım araştırmadan, insanların JUL'u sevmemesinin nedenlerini ortaya çıkardım:
"Ben Cum Tem yayımlanan çok önce Java geliştirmeye başladı ve bana günlük-çerçeve-X ile devam etmek yerine yeni bir şeyler öğrenmek için daha kolay oldu" . Hmm. Şaka yapmıyorum, aslında insanlar böyle söylüyor. Bu argümanla hepimiz COBOL yapabiliriz. (ancak ben kesinlikle bu tembel bir ahbap olmakla ilgili olabilir)
Diyerek şöyle devam etti: "TEMMUZ'daki kayıt seviyelerinin adlarını sevmiyorum" . Tamam, cidden, bu yeni bir bağımlılık getirmek için yeterli bir neden değil.
"TEMMUZ çıktısının standart biçimini sevmiyorum" . Hmm. Bu sadece yapılandırmadır. Kod-bilge bir şey yapmak zorunda bile değilsiniz. (doğru, eski günlerde, doğru olması için kendi Formatter sınıfınızı oluşturmanız gerekebilir).
"Logging-framework-X kullanan diğer kütüphaneleri de kullanıyorum, bu yüzden bunu kullanmayı daha kolay düşündüm" . Bu dairesel bir argüman, değil mi? 'Herkes' neden JUL yerine logging-framework-X kullanıyor?
"Herkes günlük kaydı-çerçeve-X kullanıyor" . Bu benim için yukarıdakilerin özel bir örneğidir. Çoğunluk her zaman doğru değildir.
Yani asıl büyük soru neden TEMMUZ olmasın? . Ne kaçırdım? Tomruk cepheleri (SLF4J, JCL) için varoluş nedeni, tarihsel olarak çok sayıda tomruklama uygulamasının mevcut olması ve bunun sebebinin gördüğüm gibi JUL'dan önceki döneme kadar gitmesidir. JUL mükemmel olsaydı, giriş cepheleri olmazdı, ya da ne? İşleri daha karmaşık hale getirmek için JUL, bir dereceye kadar bir cephenin kendisidir, bu da İşleyicilerin, Biçimlendiricilerin ve hatta LogManager'ın değiştirilmesine izin verir.
Aynı şeyi yapmanın (giriş) birden fazla yolunu benimsemek yerine, neden ilk başta gerekli olduklarını sorgulamalıyız? (ve bu nedenlerin hala var olup olmadığına bakın)
Tamam, şu ana kadar yaptığım araştırma , TEMMUZ ile ilgili gerçek sorunlar olabileceğini görebildiğim birkaç şeye yol açtı :
Performans . Bazıları SLF4J'deki performansın diğerlerinden daha üstün olduğunu söylüyor. Bu bana erken optimizasyon örneği gibi geliyor. Saniyede yüzlerce megabayt kaydetmeniz gerekiyorsa, yine de doğru yolda olduğunuzdan emin değilim. JUL da gelişti ve Java 1.4'te yaptığınız testler artık doğru olmayabilir. Burada bunu okuyabilirsiniz ve bu düzeltme Java 7'ye dönüştürmüştür. Birçoğu, günlükleme yöntemlerinde dize birleştirme yükü hakkında da konuşur. Ancak, şablon tabanlı günlük kaydı bu maliyeti önler ve Temmuz ayında da mevcuttur. Şahsen ben asla şablon tabanlı log yazmam. Bunun için çok tembel. Örneğin, bunu TEMMUZ ile yaparsam:
log.finest("Lookup request from username=" + username + ", valueX=" + valueX + ", valueY=" + valueY));
IDE'm beni uyaracak ve izin vermesi için izin isteyecek:
log.log(Level.FINEST, "Lookup request from username={0}, valueX={1}, valueY={2}", new Object[]{username, valueX, valueY});
.. tabi ki kabul edeceğim. İzin verildi ! Yardımın için teşekkürler.
Yani aslında bu tür ifadeleri kendim yazmıyorum, bu IDE tarafından yapılır.
Performans konusunda sonuç olarak, JUL'un performansının rekabete kıyasla iyi olmadığını gösteren hiçbir şey bulamadım.
Sınıfyolundan yapılandırma . Kullanıma hazır JUL, sınıf yolundan bir yapılandırma dosyası yükleyemiyor. Bunu yapmak için birkaç satır kod var . Bunun neden sinir bozucu olabileceğini görebiliyorum ama çözüm kısa ve basit.
Çıktı işleyicilerin kullanılabilirliği . JUL, kutudan çıkar çıkmaz 5 çıkış işleyicisi ile birlikte gelir: konsol, dosya akışı, soket ve bellek. Bunlar genişletilebilir veya yenileri yazılabilir. Bu, örneğin UNIX / Linux Syslog ve Windows Olay Günlüğüne yazıyor olabilir. Şahsen bu gereksinimi hiç yaşamadım ya da kullanıldığını görmedim ama neden yararlı bir özellik olabileceğini kesinlikle anlatabilirim. Logback, örneğin Syslog için bir ek ile birlikte gelir. Hala tartışırım
- Çıktı destinasyonlarına yönelik ihtiyaçların% 99,5'i, TEMMUZ'da hazır olanlar tarafından karşılanmaktadır.
- Özel ihtiyaçlar, başka bir şey yerine JUL'un üstünde özel işleyiciler tarafından karşılanabilir. Benim için JUL için bir Syslog çıktı işleyicisi yazmanın başka bir günlük çerçevesi için olduğundan daha fazla zaman aldığını gösteren hiçbir şey yok.
Göz ardı ettiğim bir şey olduğundan gerçekten endişeliyim. Log cephelerinin kullanımı ve JUL dışındaki log uygulamalarının o kadar yaygın olduğu sonucuna varmamalıyım ki, sadece anlamayan benim. İlk defa olmaz, korkarım. :-)
Peki API'm ile ne yapmalıyım? Başarılı olmasını istiyorum. Tabii ki sadece "akış ile gitmek" ve SLF4J (bu günlerde en popüler gibi görünüyor) uygulamak olabilir ama kendi iyiliğim için hala hala tüm bulanıklığı garanti JUL bugün tam olarak ne olduğunu anlamak gerekir? Kütüphanem için TEMMUZ seçerek kendimi sabote edecek miyim?
Test performansı
(bölüm 07-TEMMUZ 2012 tarihinde nolan600 tarafından eklendi)
Aşağıda Ceki'den SLF4J'in parametrelendirmesinin JUL'dan 10 kat veya daha hızlı olduğuna dair bir referans var. Bu yüzden bazı basit testler yapmaya başladım. İlk bakışta iddia kesinlikle doğrudur. İşte ön sonuçlar (ancak okumaya devam edin!):
- Yürütme süresi SLF4J, arka uç
- Yürütme süresi SLF4J, arka uç TEMMUZ: 12938
- Yürütme zamanı TEMMUZ: 16911
Yukarıdaki sayılar msec'tir, dolayısıyla daha azı daha iyidir. Yani 10 kat performans farkı ilk bakışta oldukça yakın. İlk tepkim: Bu çok fazla!
İşte testin çekirdeği. Görülebileceği gibi bir tamsayı ve bir dize, daha sonra log deyiminde kullanılan bir döngüde oluşturulmuştur:
for (int i = 0; i < noOfExecutions; i++) {
for (char x=32; x<88; x++) {
String someString = Character.toString(x);
// here we log
}
}
(Günlük deyiminin hem ilkel bir veri türüne (bu durumda int) hem de daha karmaşık bir veri türüne (bu durumda bir String) sahip olmasını istedim.
SLF4J için günlük ifadesi:
logger.info("Logging {} and {} ", i, someString);
TEMMUZ için günlük ifadesi:
logger.log(Level.INFO, "Logging {0} and {1}", new Object[]{i, someString});
JVM, gerçek ölçüm yapılmadan önce bir kez yapılan aynı testle 'ısıtıldı'. Windows 7'de Java 1.7.03 kullanıldı. SLF4J'nin (v1.6.6) ve Logback'in (v1.0.6) son sürümleri kullanıldı. Stdout ve stderr boş cihaza yönlendirildi.
Ancak, şimdi dikkatli olun, JUL zamanının çoğunu harcıyor, getSourceClassName()
çünkü JUL varsayılan olarak çıktıdaki kaynak sınıfı adını yazdırıyor, ancak Logback yapmıyor. Bu yüzden elma ve portakalları karşılaştırıyoruz. Sınamayı yeniden yapmam ve günlük uygulamalarını benzer şekilde yapılandırmam gerekiyor, böylece aynı şeyleri çıktılar. Bununla birlikte, SLF4J + Logback'in hala üstte çıkacağını, ancak yukarıda verilen ilk sayılardan uzak olacağını sanıyorum. Bizi izlemeye devam edin.
Btw: Test aslında SLF4J veya Logback ile ilk kez çalıştım. Hoş bir deneyim. TEMMUZ, başlarken kesinlikle çok daha az hoş.
Test performansı (bölüm 2)
(bölüm 08 Temmuz-2012 tarihinde nolan600 tarafından eklendi)
Anlaşıldığı üzere, deseninizi TEMMUZ'da nasıl yapılandırdığınız, yani kaynak adını içerip içermediği önemli değildir. Çok basit bir desenle denedim:
java.util.logging.SimpleFormatter.format="%4$s: %5$s [%1$tc]%n"
ve bu yukarıdaki zamanlamaları hiç değiştirmedi. Profil oluşturucum, bu benim modelimin bir getSourceClassName()
parçası olmasa bile , kaydedicinin çağrılara hala çok zaman harcadığını açıkladı . Desen önemli değil.
Bu nedenle, en azından test edilen şablon tabanlı günlük ifadesi için kabaca JUL (yavaş) ve SLF4J + Logback (hızlı) arasındaki gerçek performans farkında 10 faktörü olduğu sonucuna varıyorum. Tıpkı Ceki'nin dediği gibi.
SLF4J'in getLogger()
çağrısının JUL'un ditto'sundan çok daha pahalı olduğu başka bir şey de görebiliyorum . (Profilerim doğruysa 95 ms vs 0.3 ms). Bu mantıklı. SLF4J, temeldeki kayıt uygulamasının bağlanması için biraz zaman ayırmalıdır. Bu beni korkutmuyor. Bu çağrılar bir uygulamanın kullanım ömrü boyunca biraz nadir olmalıdır. Haslık gerçek günlük çağrılarında olmalıdır.
Final sonucu
(bölüm 08 Temmuz-2012 tarihinde nolan600 tarafından eklendi)
Tüm cevaplarınız için teşekkür ederim. Başlangıçta benim API için SLF4J kullanmaya karar verdim düşündüm aksine. Bu, bazı şeylere ve girdilerinize dayanmaktadır:
Dağıtım zamanında günlük uygulamasını seçme esnekliği sağlar.
Bir uygulama sunucusunda çalıştırıldığında, JUL'un yapılandırmasında esneklik eksikliği olan sorunlar.
SLF4J, özellikle Logback ile birleştirirseniz, yukarıda ayrıntılı olarak açıklandığı gibi çok daha hızlıdır. Bu sadece kaba bir test olsa bile, SLF4J + Logback üzerinde Temmuz ayına göre optimizasyona çok daha fazla çaba harcandığına inanmak için nedenim var.
Belgeler. SLF4J belgeleri çok daha kapsamlı ve hassastır.
Desen esnekliği. Testleri yaparken JUL uygulamasının Logback'in varsayılan şablonunu taklit etmesini sağladım. Bu desen, iş parçacığının adını içerir. JUL bunu kutunun dışında yapamaz. Tamam, şimdiye kadar kaçırmadım, ama bir günlük çerçevesinden eksik olması gereken bir şey olduğunu düşünmüyorum. Dönem!
Günümüzde çoğu (veya birçok) Java projesi Maven'i kullanıyor, bu nedenle bir bağımlılık eklemek, özellikle bu bağımlılık oldukça kararlıysa, yani sürekli olarak API'sını değiştirmezse, büyük bir şey değildir. Bu SLF4J için geçerli gibi görünüyor. Ayrıca SLF4J kavanozu ve arkadaşları küçük boyutludur.
Yani, garip olan şey, SLF4J ile biraz çalıştıktan sonra aslında JUL ile oldukça üzüldüm. Yine de TEMMUZ ile böyle olması gerektiğine üzülüyorum. JUL mükemmel olmaktan uzaktır, ancak bir tür iş yapar. Yeterince iyi değil. Aynı şey Properties
örnek olarak da söylenebilir , ancak insanların kendi yapılandırma kitaplıklarını ve neleriniz olduğunu takabilmeleri için soyutlamayı düşünmüyoruz. Sanırım bunun sebebi Properties
çubuğun hemen üstündeyken, bunun tam tersi bugünün TEMMUZ için geçerli ... ve geçmişte sıfır geldi çünkü mevcut değildi.
java.lang.System.Logger
, ki bu arayüz yakalandığı ve bu arayüzün bir uygulamasını sağladığı sürece, istediğiniz gerçek günlük çerçevesine yönlendirilebilen bir arayüz. Modülerleştirmeyle birlikte java.util.logging
, farklı bir çerçeveyi tercih ederseniz paketlenmiş JRE içermeyen bir uygulamayı bile dağıtabilirsiniz .