Java'da hata ayıklama çıktısını işlemenin doğru yolu nedir?


32

Mevcut Java projelerim büyüdükçe, kodumun birkaç noktasına hata ayıklama çıktısı eklemek için aynı şekilde artan bir ihtiyaç duyuyorum.

Bu özelliği uygun şekilde etkinleştirmek veya devre dışı bırakmak için, test oturumlarının açılmasına veya kapanmasına bağlı olarak, genellikle private static final boolean DEBUG = falsetestlerimin denetlediği sınıfların başında bir koydum ve önemsizce bu şekilde kullanıyorum (örneğin):

public MyClass {
  private static final boolean DEBUG = false;

  ... some code ...

  public void myMethod(String s) {
    if (DEBUG) {
      System.out.println(s);
    }
  }
}

ve benzerleri.

Fakat bu beni rahatsız etmiyor, çünkü elbette işe yarıyor ama eğer sadece birkaçına bakmıyorsanız DEBUG'ı doğru yapmak için çok fazla sınıf olabilir.

Bunun tersine, (sanırım - sanırım - diğerleri) tüm uygulamanın hata ayıklama moduna geçirilmesinden hoşlanmıyor, çünkü çıkarılan metin miktarı çok zor olabilir.

Öyleyse, böyle bir durumu mimari olarak ele almanın doğru bir yolu var mı, yoksa en doğru yol DEBUG sınıf üyesini kullanmak mı?


14
Java’da, doğru şekilde oturum açmak için homebrew kodunu KULLANMAYIN. Yerleşik bir çerçeve seçin, tekerleği yeniden icat etmeyin
gnat

Daha karmaşık derslerimden bazılarında, sizin de belirttiğiniz gibi bir boolean DEBUG kullanıyorum. Genelde tüm uygulamanın hata ayıklamasını istemiyorum, sadece sınıf bana sorun veriyor. Alışkanlık COBOL günlerimden geldi, DISPLAY ifadeleri mevcut tek hata ayıklama biçimiydi.
Gilbert Le Blanc

1
Ayrıca, mümkün olduğunda hata ayıklayıcıya daha fazla güvenmenizi ve kodunuzu hata ayıklama ifadeleriyle karıştırmamanızı tavsiye ederim.
Andrew T Finnell

1
Test Testli Geliştirme (TDD) 'yi birim testleriyle mi uyguluyorsunuz? Bunu yapmaya başladığımda, 'hata ayıklama kodunda' büyük bir azalma olduğunu fark ettim.
JW01

Yanıtlar:


52

Bir kayıt çerçevesine ve belki de kayıt cephesi çerçevesine bakmak istersiniz.

Orada, çoğu zaman birbiriyle örtüşen işlevsellikler içeren çok sayıda kayıt çerçevesi vardır, öyle ki, zamanla birçok kişi ortak bir API'ye güvenmek için gelişti veya kullanımlarını soyutlamak ve yerlerine yerleştirilmelerine izin vermek için bir cephe çerçevesi aracılığıyla kullanılmaya başlandı. gerekirse.

çerçeveler

Bazı Günlük Çerçeveleri

Bazı Günlük Cepheler

kullanım

Temel örnek

Bu çerçevelerin çoğu formda bir şeyler yazmanıza izin verir (burada slf4j-apive kullanarak logback-core):

package chapters.introduction;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

// copied from: http://www.slf4j.org/manual.html
public class HelloWorld {

  public static void main(String[] args) {
    final Logger logger = LoggerFactory.getLogger(HelloWorld.class);

    logger.debug("Hello world, I'm a DEBUG level message");
    logger.info("Hello world, I'm an INFO level message");
    logger.warn("Hello world, I'm a WARNING level message");
    logger.error("Hello world, I'm an ERROR level message");
  }
}

Geçerli bir sınıfın SLF4J / LogBack'in çıktıyı biçimlendirmesine ve kayıt mesajının nereden geldiğini göstermesine izin verecek özel bir günlüğü oluşturmak için kullanıldığını not edin.

SLF4J kılavuzunda belirtildiği gibi, bir sınıftaki tipik kullanım şekli genellikle:

import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  

public class MyClass {

    final Logger logger = LoggerFactory.getLogger(MyCLASS.class);

    public void doSomething() {
        // some code here
        logger.debug("this is useful");

        if (isSomeConditionTrue()) {
            logger.info("I entered by conditional block!");
        }
    }
}

Fakat aslında, kaydediciyi aşağıdaki şekilde beyan etmek daha yaygındır:

private static final Logger LOGGER = LoggerFactory.getLogger(MyClass.class);

Bu, kaydedicinin statik yöntemlerden de kullanılmasına izin verir ve sınıfın tüm örnekleri arasında paylaşılır. Bu sizin tercih ettiğiniz form olabilir. Ancak yorumlarda Brendan Long tarafından belirtildiği gibi , sonuçları anladığınızdan ve buna göre karar verdiğinizden emin olmak istiyorsunuz (bu, bu deyimleri izleyen tüm kayıt çerçeveleri için geçerlidir).

Kaydedicileri başlatmanın başka yolları da vardır, örneğin adlandırılmış bir kaydedici oluşturmak için string parametresini kullanarak:

Logger logger = LoggerFactory.getLogger("MyModuleName");

Hata Ayıklama Seviyeleri

Hata ayıklama düzeyleri bir çerçeveden diğerine değişebilir, ancak ortak olanları (önem sırasına göre, iyi huyludan kötü boka ve muhtemelen çok yaygından umarım çok nadir olana kadar):

  • TRACE Çok detaylı bilgi. Sadece günlüklere yazılmalıdır. Yalnızca programın kontrol noktalarındaki akışını izlemek için kullanılır.

  • DEBUG Detaylı bilgi. Sadece günlüklere yazılmalıdır.

  • INFO Önemli çalışma zamanı olayları. Konsolda hemen görünür olması gerekir, bu nedenle dikkatli kullanın.

  • WARNING Çalışma zamanı tuhaflıkları ve düzeltilebilir hatalar.

  • ERROR Diğer çalışma zamanı hataları veya beklenmeyen koşullar.

  • FATAL Erken sonlandırmaya neden olan ciddi hatalar.

Bloklar ve Muhafızlar

Şimdi, birkaç hata ayıklama ifadesi yazmak üzere olan bir kod bölümünüz olduğunu varsayalım. Bu, hem günlüğe kaydetmenin hem de günlüğe kaydetme yöntemine geçirebileceğiniz herhangi bir parametrenin üretilmesinin etkisinden dolayı performansınızı hızla etkileyebilir.

Bu tür bir sorunu önlemek için, genellikle formdan bir şeyler yazmak istersiniz:

if (LOGGER.isDebugEnabled()) {
   // lots of debug logging here, or even code that
   // is only used in a debugging context.
   LOGGER.debug(" result: " + heavyComputation());
}

Bu korumayı hata ayıklama ifadeleri bloğunuzdan önce kullanmadıysanız, mesajlar çıkmasa bile (örneğin, günlüğe kaydediciniz şu anda yalnızca INFOseviyenin üzerinde bir şeyler yazdıracak şekilde yapılandırılmışsa ), heavyComputation()yöntem hala yürütülecekti .

Yapılandırma

Yapılandırma, kayıt çerçevenize oldukça bağlıdır, ancak bunun için çoğunlukla aynı teknikleri sunarlar:

  • programlı konfigürasyon (çalışma zamanında, bir API üzerinden - çalışma zamanı değişikliklerine izin verir ),
  • statik bildirimsel yapılandırma (başlangıçta genellikle bir XML veya özellik dosyası aracılığıyla - ilk başta ihtiyacınız olan şey olabilir ).

Ayrıca çoğunlukla aynı yetenekleri sunarlar:

  • çıkış mesajının formatının yapılandırılması (zaman damgaları, işaretçiler, vb.),
  • çıkış seviyelerinin yapılandırılması,
  • ince taneli filtrelerin konfigürasyonu (örneğin paketleri veya sınıfları dahil etmek / hariç tutmak),
  • Nerede oturum açacağını (konsola, dosyaya, web servisine…) ve muhtemelen eski kayıtlarla ne yapılacağını (örneğin, otomatik haddeleme dosyalarıyla) belirlemek için eklerin yapılandırılması.

İşte bir logback.xmldosya kullanarak, bildirimsel bir yapılandırmaya genel bir örnek .

<configuration>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <!-- encoders are assigned the type
         ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>

  <root level="debug">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>

Belirtildiği gibi, bu sizin çerçevenize bağlıdır ve başka alternatifler de olabilir (örneğin, LogBack ayrıca bir Groovy betiğinin kullanılmasına da izin verir). XML yapılandırma formatı bir uygulamadan diğerine de değişebilir.

Daha fazla yapılandırma örneği için lütfen (diğerleri arasında) aşağıdakilere bakın:

Bazı Tarihsel Eğlence

O Lütfen not Log4J versiyonu geçiş, şu anda büyük bir güncelleme görüyor 1.x için 2.x . Hem tarihi hem eğlenceli hem de karışıklık için bir göz atmak isteyebilirsiniz ve Log4J'yi seçerseniz muhtemelen 2.x sürümüyle devam etmeyi tercih edebilirsiniz.

Mike Partridge'in yorumlarda belirtildiği gibi, LogBack'in eski bir Log4J ekibi üyesi tarafından yaratıldığını belirtmek gerekir. Java Günlüğü çerçevesinin eksikliklerini gidermek için oluşturuldu. Ve yaklaşmakta olan büyük Log4J 2.x sürümünün kendisi artık LogBack'ten alınan birkaç özelliği birleştiriyor.

Tavsiye

Sonuç olarak, olabildiğince dekolte olun, birkaç kişiyle oynayın ve sizin için en uygun olanı görün. Sonunda bu sadece bir kayıt çerçevesi . Çok özel bir nedeniniz olması dışında, kullanım kolaylığı ve kişisel tercihlerden başka, bunlardan herhangi biri Tamam'ı tercih eder, bu yüzden asılmanın bir anlamı yoktur. Bunların çoğu ihtiyaçlarınız için de genişletilebilir.

Yine de, bugün bir kombinasyon seçmek zorunda olsaydım, LogBack + SLF4J ile giderdim. Fakat birkaç yıl sonra bana sormuş olsaydınız, Apache Commons Logging ile Log4J'i tavsiye ederdim, bu yüzden bağımlılıklarınıza göz atın ve onlarla birlikte evrim geçirin.


1
SLF4J ve LogBack, Log4J'i ilk yazan kişi tarafından yazılmıştır.
Mike Partridge

4
Günlüğe kaydetmenin performans etkileri konusunda endişeli olanlar için: slf4j.org/faq.html#logging_performance
Mike Partridge

2
staticKüçük bir miktar hafıza tasarrufu sağladığından , kaydedicilerinizi yapmanız gerekip gerekmediğinin çok net olmadığını söylemeye değer
Monica

1
@MikePartridge: Bağlantının içeriğinin farkındayım, ancak hala parametre değerlendirmesini engellemiyor. byt parametreli kayıt işleminin sebebi, performans mesajının işlenmesinin gerçekleşmemesidir (özellikle bitiştirme). Ancak, parametre olarak iletilirse herhangi bir yöntem çağrısı gerçekleştirilir. Bu nedenle, kullanım durumunuza bağlı olarak, bloklar yararlı olabilir. Ayrıca gönderide belirtildiği gibi, DEBUG seviyesi etkin olduğunda gerçekleşmesi için sadece diğer hata ayıklama ile ilgili aktiviteleri (sadece günlük kaydı değil) gruplandırmanız için de faydalı olabilirler.
haylem

1
@ haylem - bu doğru, benim hatam.
Mike Partridge

2

bir kayıt çerçevesi kullanın

Çoğu zaman statik bir fabrika metodu vardır.

private static final Logger logger = Logger.create("classname");

o zaman günlük kodunuzu farklı seviyelerde yazdırabilirsiniz:

logger.warning("error message");
logger.info("informational message");
logger.trace("detailed message");

sonra, her bir sınıf için hangi mesajların log çıktısına yazılacağını tanımlayabileceğiniz tek bir dosya olacaktır (dosya veya stderr)


1

Bu tam olarak log4j veya daha yeni slf4j gibi loglama çerçevelerinin ne anlama geldiğidir. Günlük kaydını ayrıntılı olarak kontrol etmenizi ve uygulama çalışırken bile yapılandırmanızı sağlar.


0

Günlük kaydı çerçevesi kesinlikle gidilecek yoldur. Bununla birlikte, iyi bir test odasına da sahip olmalısınız. İyi test kapsamı çoğu zaman hep birlikte hata ayıklama çıktısı ihtiyacını ortadan kaldırabilir.


Bir kayıt çerçevesi kullanıyorsanız ve hata ayıklama günlüğü kullanıyorsanız - bunun sizi gerçekten kötü bir gün geçirmekten kurtaracağı bir zaman gelecek.
Fortyrunner

1
Günlük tutmaman gerektiğini söylemedim. Önce test yaptırman gerektiğini söyledim.
Dima,
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.