İstisna atma kurucularındaki hataları günlüğe kaydetmeli miyim?


15

Birkaç aydır bir uygulama yapıyordum ve ortaya çıkan bir modeli fark etmeye başladım:

logger.error(ERROR_MSG);
throw new Exception(ERROR_MSG);

Veya yakalarken:

try { 
    // ...block that can throw something
} catch (Exception e) {
    logger.error(ERROR_MSG, e);
    throw new MyException(ERROR_MSG, e);
}

Yani, ne zaman bir istisna atar ya da yakalarsam, onu kaydederdim. Aslında, uygulamada yaptığım neredeyse tüm günlük kaydı (uygulama başlatma için bir şey yanında).

Yani, bir programcı olarak tekrardan kaçındım; Bu yüzden, kaydedici çağrılarını istisna yapımına taşımaya karar verdim, bu nedenle, bir istisna oluştururken işler günlüğe kaydedilir. Kesinlikle, benim için istisnayı fırlatan bir ExceptionHelper oluşturabilirdim, ancak bu kodumu yorumlamayı zorlaştıracak ve daha da kötüsü, derleyici onunla iyi bir şekilde ilgilenmeyecek, bu üyeye yapılan bir çağrının hemen atın.

Peki, bu bir anti-desen mi? Öyleyse neden?


İstisnayı serileştirip serileştirmeyi kaldırırsanız ne olur? Hata kaydedilecek mi?
apocalypse

Yanıtlar:


18

Bir anti-desen olarak nitelendirilip nitelendirilmediğinden emin değilim, ancak IMO bu kötü bir fikir: günlüğe kaydetme ile bir istisnayı iç içe geçirmek için gereksiz bağlantı.

Belirli bir kural dışı durumun tüm örneklerini her zaman günlüğe kaydetmek istemeyebilirsiniz (giriş kaydı hem hacimli hem de ilgisiz olabilir).

İkincisi, farklı günlük kaydı düzeylerinde bir hatanın farklı tekrarlarını günlüğe kaydetmeye karar verebilirsiniz; bu noktada kural dışı durumu oluştururken, yine kural dışı durum oluşturma işleminin günlük tutma davranışıyla çamurlanmasını temsil ettiğini belirtmeniz gerekir.

Son olarak, başka bir istisnanın günlüğü sırasında istisnalar meydana gelirse ne olur? Günlüğe kaydeder misiniz? Dağınık oluyor ...

Seçimleriniz temel olarak:

  • Örneğinizde verdiğiniz gibi yakalayın, kaydedin ve (yeniden) atın
  • Her ikisi de sizin için yapmak bir ExceptionHelper sınıfı oluşturun, ancak yardımcıları bir kod kokusu var ve ben de bunu tavsiye etmem.
  • Tümünü yakalama özel durum işlemesini daha yüksek bir seviyeye taşıma
  • Günlüğe kaydetme ve istisna işleme gibi kesişen endişelere daha sofistike bir çözüm için AOP'yi düşünün (ancak bu iki satırı yakalama bloklarınızda bulundurmaktan çok daha karmaşık;))

"Hacimli ve ilgisiz" için +1. AOP nedir?
Tulains Córdova

@ user61852 En boy yönelimli programlama (Bir bağlantı ekledim). Bu soru, AOP ve Java'da günlüğe kaydetme ile ilgili bir örneği gösterir: stackoverflow.com/questions/15746676/logging-with-aop-in-spring
Dan1701 29:01 '

11

Yani, bir programcı olarak, tekrar tekrar shun [...]

Burada kavramı "don't repeat yourself", koku haline geldiği noktaya biraz fazla ciddiye alındığında bir tehlike vardır .


2
Şimdi, her şey yolundaysa ve birbirlerinin üzerine kurulduğunda doğru cevabı nasıl seçmeliyim? Fanatik bir yaklaşım benimsem DRY'nin nasıl sorun olabileceğine dair mükemmel bir açıklama.
Bruno Brant

1
Bu DRY'de gerçekten iyi bir bıçak, itiraf etmeliyim ki ben DRYholic'ım. Şimdi bu 5 kod satırını DRY uğruna başka bir yere taşımayı düşündüğümde iki kez düşüneceğim.
SZT

@SZaman Başlangıçta çok benzerdim. Parlak tarafta, fazlalık damgalama tarafına çok fazla eğilen bizler için, kopyala ve yapıştır kullanarak 500 satırlık bir işlev yazıp yeniden düzenleme hakkında düşünmeyenlerden çok daha fazla umut olduğunu düşünüyorum. Akılda tutulması gereken en önemli şey, her küçük çoğaltmayı damgaladığınızda, kodu merkezsizleştiriyor ve başka bir yere bağımlılığı yönlendiriyorsunuz. Bu iyi ya da kötü bir şey olabilir. Davranışı değiştirmek için merkezi kontrol sağlar, ancak bu davranışı paylaşmak da ısırmaya başlayabilir ...

@SZaman "Yalnızca bu işlevin buna ihtiyacı var, bu merkezi işlevi kullanan diğerleri buna benzemez" gibi bir değişiklik yapmak istiyorsanız. Her neyse, gördüğüm gibi dengeleyici bir hareket - mükemmel yapmak zor bir şey! Ancak bazen biraz çoğaltma, kodunuzu daha bağımsız ve ayrıştırılmış hale getirmeye yardımcı olabilir. Ve bir kod parçasını test ederseniz ve gerçekten iyi çalışıyorsa, burada ve orada bazı temel mantıkları kopyalasa bile, değişmek için birkaç nedeni olabilir. Bu arada dışsal şeylere bağlı bir şey, değişmek için çok daha dışsal nedenler bulur.

6

@ Dan1701'de yankılanmak için

Bu, endişelerin ayrılmasıyla ilgilidir - günlüğe kaydetmenin istisnaya taşınması, istisna ve günlüğe kaydetme arasında sıkı bir bağlantı oluşturur ve ayrıca günlük tutma istisnasına ek bir sorumluluk eklediğiniz anlamına gelir ve bu da istisna sınıfı için bağımlılıklar oluşturabilir. gerek yok.

Bir bakım açısından, istisnanın sürdürücüden günlüğe kaydedildiğini (en azından örnek gibi bir şey bağlamında) gizlediğinizi iddia edebilirim (ayrıca), içeriği de değiştirirsiniz ( muhtemelen istisna kurucusuna istisna işleyicisi yeri) dan değil ne amaçladığınız.

Son olarak, her zaman bir istisna, tam olarak aynı şekilde, yaratıldığı / yükseltildiği noktada - muhtemelen böyle değil - kaydetmek istediğinizi varsayıyorsunuz. Eğer oldukça hızlı bir şekilde derinden rahatsız edici olacak günlük ve günlük olmayan istisnalar olacak sürece.

Yani bu durumda "SRP" nin "KURU" olduğunu düşünüyorum.


1
[...]"SRP" trumps "DRY"- Bence bu alıntı hemen hemen mükemmel bir şekilde özetliyor.

@Ike'nin dediği gibi ... Bu, aradığım mantık türüydü.
Bruno Brant

Günlüğe kaydetme bağlamının değiştirilmesinin, özel durum sınıfının başlangıç ​​noktası ya da günlük girişi gibi, günlüğe kaydedileceğini belirtmek için +1.
Tulains Córdova

2

Hatanız, işleyemeyeceğiniz bir istisna günlüğe kaydediyor ve bu nedenle günlüğün düzgün bir şekilde işlemenin herhangi bir parçası olup olmadığını bilemiyor.
Günlüğe kaydetme de dahil olmak üzere hatanın bir kısmını işleyebileceğiniz veya işlemeniz gereken çok az durum vardır, ancak yine de hatayı arayan kişiye bildirmelidir. Örnekler, en düşük seviyeli okumayı yaparsanız düzeltilemez okuma hataları ve benzerleridir, ancak genellikle arayana iletilen bilgilerin güvenlik ve kullanılabilirlik için ciddi şekilde filtrelenmesi ortaktır.

Durumunuzda yapabileceğiniz ve bazı nedenlerden dolayı yapabileceğiniz tek şey, uygulamanın attığı istisnayı, arayanın beklediği birine çevirmek, orijinali bağlam için zincirlemek ve başka bir şeyi tek başına bırakmaktır.

Özetlemek gerekirse, kodunuz istisnanın kısmi olarak ele alınması hakkını ve görevini savundu ve böylece SRP'yi ihlal etti.
DRY buna girmiyor.


1

Bir istisnayı yalnızca bir catch bloğu kullanarak günlüğe kaydetmeye karar verdiğiniz için yeniden düzenlemek (istisna hiç değişmediği anlamına gelir) kötü bir fikirdir.

İstisnaları, istisna mesajlarını ve işlenmesini kullanmamızın nedenlerinden biri, neyin yanlış gittiğini ve akıllıca yazılmış istisnaların hatayı büyük bir farkla bulmasını hızlandırabilmemizdir.

Ayrıca, istisnaları ele almanın if, sahip olduğumuzdan çok daha fazla kaynağa mal olduğunu unutmayın; Uygulamanızın performansı üzerinde etkisi vardır.

Ancak, hatanın göründüğü uygulama katmanını işaretlemek için istisnayı bir araç olarak kullanmak iyi bir yaklaşımdır.

Aşağıdaki yarı sahte kodu düşünün:

interface ICache<T, U>
{
    T GetValueByKey(U key); // may throw an CacheException
}

class FileCache<T, U> : ICache<T, U>
{
    T GetValueByKey(U key)
    {
        throw new CacheException("Could not retrieve object from FileCache::getvalueByKey. The File could not be opened. Key: " + key);
    }
}

class RedisCache<T, U> : ICache<T, U>
{
    T GetValueByKey(U key)
    {
        throw new CacheException("Could not retrieve object from RedisCache::getvalueByKey. Failed connecting to Redis server. Redis server timed out. Key: " + key);
    }
}

class CacheableInt
{
    ICache<int, int> cache;
    ILogger logger;

    public CacheableInt(ICache<int, int> cache, ILogger logger)
    {
        this.cache = cache;
        this.logger = logger;
    }

    public int GetNumber(int key) // may throw service exception
    {
        int result;

        try {
            result = this.cache.GetValueByKey(key);
        } catch (Exception e) {
            this.logger.Error(e);
            throw new ServiceException("CacheableInt::GetNumber failed, because the cache layer could not respond to request. Key: " + key);
        }

        return result;
    }
}

class CacheableIntService
{
    CacheableInt cacheableInt;
    ILogger logger;

    CacheableInt(CacheableInt cacheableInt, ILogger logger)
    {
        this.cacheableInt = cacheableInt;
        this.logger = logger;
    }

    int GetNumberAndReturnCode(int key)
    {
        int number;

        try {
            number = this.cacheableInt.GetNumber(key);
        } catch (Exception e) {
            this.logger.Error(e);
            return 500; // error code
        }

        return 200; // ok code
    }
}

Birisinin kodu aradığını ve aldığını GetNumberAndReturnCodeve 500bir hata sinyali aldığını varsayalım . Günlük dosyasını açıp şunu gören desteği arayacaktı:

ERROR: 12:23:27 - Could not retrieve object from RedisCache::getvalueByKey. Failed connecting to Redis server. Redis server timed out. Key: 28
ERROR: 12:23:27 - CacheableInt::GetNumber failed, because the cache layer could not respond to request. Key: 28

Geliştirici daha sonra yazılımın hangi katmanının işlemin iptal edilmesine neden olduğunu hemen bilir ve sorunu tanımlamanın kolay bir yoluna sahiptir. Bu durumda önemlidir, çünkü Redis zaman aşımı asla gerçekleşmemelidir.

Belki başka bir kullanıcı aynı yöntemi çağırır, 500kodu alır, ancak günlük aşağıdakileri gösterir:

INFO: 11:11:11- Could not retrieve object from RedisCache::getvalueByKey. Value does not exist for the key 28.
INFO: 11:11:11- CacheableInt::GetNumber failed, because the cache layer could not find any data for the key 28.

Bu durumda destek, var olmayan bir kimlik için bir değer istediği için kullanıcıya isteğin geçersiz olduğuna yanıt verebilir.


özet

İstisnaları ele alıyorsanız, bunları doğru şekilde ele aldığınızdan emin olun. Ayrıca, istisnalarınızın mimari katmanlarınızı izleyerek ilk etapta doğru verileri / mesajları içerdiğinden emin olun, böylece mesajlar oluşabilecek bir sorunu tanımlamanıza yardımcı olur.


1

Sorunun daha temel bir seviyede olduğunu düşünüyorum: hatayı günlüğe kaydediyor ve aynı yerde bir istisna olarak atıyorsunuz. Bu anti-desen. Bu, aynı hatanın yakalanması, belki de başka bir istisnaya sarılması ve yeniden atılması durumunda birçok kez günlüğe kaydedildiği anlamına gelir.

Bunun yerine, kural dışı durum oluşturulduğunda değil, yakalandığında hataları günlüğe kaydetmenizi öneririm. (Bunun için tabii ki, emin daima yakalanır yerde yapmak gerekir.) Bir istisna yakalandığı zaman, ben sadece onun stacktrace log ediyorum o takdirde değil yeniden atılmış ya da başka bir istisna haline nedeni olarak tamamladı. Yığın istisnaları ve sarılmış istisnaların mesajları yine de yığın izlemelerine "Neden ..." olarak kaydedilir. Ve yakalayıcı ayrıca, örneğin ilk hatadaki hatayı kaydetmeden yeniden denemeye veya sadece bir uyarı veya herhangi bir şey olarak ele almaya karar verebilir.


1

Eski bir iş parçacığı olduğunu biliyorum, ama benzer bir sorunla karşılaştım ve benzer bir çözüm buldum, bu yüzden 2 sent ekleyeceğim.

SRP ihlali argümanını almıyorum. Tamamen değil. 2 şeyi varsayalım: 1. İstisnaları meydana geldikçe günlüğe kaydetmek istersiniz (program düzeyinde yeniden oluşturabilmek için iz düzeyinde). Bunun istisnaları ele almakla hiçbir ilgisi yoktur. 2. Bunun için AOP kullanamazsınız ya da kullanmazsınız - bunun en iyi yol olacağını kabul ediyorum ama maalesef bunun için araç sağlamayan bir dile bağlı kaldım.

Gördüğüm gibi, temelde büyük ölçekli bir SRP ihlaline mahkum edildiniz, çünkü istisnalar atmak isteyen herhangi bir sınıf günlüğün farkında olmalıdır. Günlük kaydını istisna sınıfına taşımak aslında SRP ihlallerini büyük ölçüde azaltır çünkü artık kod tabanındaki her sınıf değil, sadece istisna onu ihlal eder.


0

Bu bir anti-desen.

Kanımca, bir istisna yapıcısında bir günlük çağrısı yapmak aşağıdakilere örnek olacaktır: Kusur: Yapıcı Gerçek İş yapar .

Hiçbir zaman bir kurucu dış servis çağrısı yapmasını beklemem (veya arzu etmem). Bu, Miško Hevery'nin belirttiği gibi, alt sınıfları ve alayları istenmeyen davranışı miras almaya zorlayan çok istenmeyen bir yan etkidir.

Bu nedenle, en az şaşkınlık ilkesini de ihlal edecektir .

Başkalarıyla bir uygulama geliştiriyorsanız, bu yan etki muhtemelen onlar için belirgin olmayacaktır. Yalnız çalışsanız bile, bunu unutabilir ve kendinizi yolda şaşırtabilirsiniz.

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.