Tasarım açısından, günlüğe kaydetme için en iyi uygulamalar nelerdir? [kapalı]


11

Şu anda üzerinde çalıştığım bir uygulamaya günlük kaydı eklemek istiyorum. Daha önce günlük kaydı ekledim, bu bir sorun değil.

Ancak, nesne yönelimli bir dilde tasarım perspektifinden, kayıt için OOP ve kalıpları takip eden en iyi uygulamalar nelerdir?

Not: Şu anda bunu C # ile yapıyorum, bu yüzden C # örnekleri açıktır. Ayrıca Java ve Ruby örneklerini görmek istiyorum.


Düzenleme: Log4net kullanıyorum. Fişi takmanın en iyi yolunun ne olduğunu bilmiyorum.

Yanıtlar:


6

Ben tavsiye ederim en iyi uygulama kendi haddeleme yerine log4j kullanmaktır . (Java'dan hem C # hem de Ruby'ye taşındı, bu yüzden ilgilendiğiniz 3 dil için de geçerlidir.)

Bu manuel sayfayı okursanız, diğer en iyi uygulamaları keşfedeceksiniz. Hafif olması, uygulamanızın dışında yapılandırılabilir olması, uygulamanızın farklı bölümleri için günlük kaydını bağımsız olarak açıp kapatabilme vb.


5

Çalıştığım yerde bir çok .NET masaüstü uygulaması yazıyoruz. Normalde bileşenlerimizde, biri bilgi kaydı ve diğeri günlük kaydı istisnaları için olmak üzere 2 olay uygularız (ayrı bir olay oluşturmak yerine sık sık istisnaların patlamasına izin versek de, duruma bağlıdır). Bu mimariyi kullanarak, kütüphanelerimizin hiçbiri günlüğün nasıl uygulandığını veya bilginin nasıl kullanıldığını, saklandığını veya işlendiğini bilmeye ihtiyaç duymaz. Daha sonra uygulama, o uygulamaya uygun bir şekilde günlük olaylarını işlemek zorunda. Birkaç yıl önce bu mimari, MS Enterprise Library günlüğünü kullanmaktan BitFactory'nin günlüğe kaydetme bileşenine geçişimizi çok basit bir geçiş haline getirdi.


Olay / Gözlemci kalıbı kullanmak için +1: gözlemciyi değiştirin, günlüğe kaydetmeyi değiştirdiniz
Matthieu M.


2

Şahsen, seçim günlüğü çerçevesini alıyorum (benim durumumda, .NET ile çalıştığım için Entlib) ve günlük kaydı için bir AOP yönü yazıyorum.

Daha sonra, herhangi bir yöntemi / özelliği / sınıfları / ad alanını ilişkilendirebilir ve kaynağı karıştırmadan günlük kaydı ekleyebilirsiniz.


Kulağa çok ilginç geliyor, ancak neyi günlüğe kaydedebileceğiniz ve günlüğün ne kadar bilgilendirici olacağı konusunda çekincelerim var (yani yöntemlerin "sadece" enstrümantasyonundan daha fazlası). Nelerin yapılabileceğini ve nelerin yapılamayacağını görmek için bu yaklaşımın çalışan bir örneğini görmek isterim. Özellikle yeni bir uygulamaya başladım ve bunu nereye / ne kadar uzağa taşıyabileceğimi görmek istiyorum.
Marjan Venema

@marjan Venema: Keskinleştirme sonrası belgelerinde, bir yönteme giren / çıkan günlüklere ilişkin bir örnek vardır. Post sharp'un durumunda, kodu derleme sırasında öznitelikten kaynağa örter , bu nedenle diğerlerinin yaptığı gibi performansı etkilemez.
Steven Evers

1

Şu anda üzerinde çalıştığım sistem, olay odaklı bir mimari ve mesajlaşma kullanır, böylece sistemimizdeki çoğu eylem bir komutun sonucudur ve olaylarla sonuçlanır (standart bir delege olayı yerine gönderilen DTO sınıfları olarak). Tek amacı günlüğü işlemek olan olay işleyicileri ekliyoruz. Bu tasarım kendimizi tekrarlamamıza yardımcı olur ve ayrıca işlevsellik eklemek / değiştirmek için mevcut kodu değiştirmek zorunda kalmazsınız.

Aşağıda, uygulamamızın dar bir bölümünden (içe aktardığımız belirli bir içerik kaynağıyla ilgili olanlar) günlüğe kaydedilecek tüm olayları işleyen böyle bir günlük kaydı sınıfına bir örnek verilmiştir.

Bunun genellikle en iyi uygulama olduğunu söylemeyeceğim, çünkü ne sıklıkta ve nasıl oturum açacağım konusunda fikrimi değiştiriyorum - ve bir sorunu teşhis etmek için her gün bir günlük kullanmam gerektiğinde, kaçınılmaz olarak iyileştirmeler yapmanın yollarını buluyorum kaydettiğim bilgiler.

Bununla birlikte, ilgili bilgilerin kaydedilmesinin (özellikle Ctrl-F / aranabilir bir şekilde bulun) en önemli kısım olduğunu söyleyeceğim.

İkinci en önemli bölüm, günlük kodunu ana mantığınızdan uzaklaştırmaktır - çirkin ve uzun bir yöntem yapabilir ve çok hızlı bir şekilde kıvrılır .

public class MctLogger :
    IEventHandler<StoryImported>,
    IEventHandler<StoryScanned>,
    IEventHandler<SourceDirectoryMissing>,
    IEventHandler<SourceDirectoryAccessError>,
    IEventHandler<CannotCreateScannedStoryDirectory>,
    IEventHandler<CannotReadStoryDocument>,
    IEventHandler<StorySkippedPastCutoff>,
    IEventHandler<StorySkippedDuplicateUniqueId>,
    IEventHandler<StorySkippedByFilter>
{

    public void Observe(StoryImported e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.StoryImported");
        log.Info("Story Unique ID: {Story.UniqueId}, Content ID: {ContentId}, Title: {Story.Headline}".SmartFormat(e));
    }

    public void Observe(StoryScanned e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.StoryScanned");
        log.Info("Story Unique ID: {Story.UniqueId}, File: {FilePath}, Title: {Story.Headline}".SmartFormat(e));
    }

    public void Observe(SourceDirectoryMissing e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.SourceDirectoryMissing");
        log.Error("Directory: " + e.Directory);
    }

    public void Observe(SourceDirectoryAccessError e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.SourceDirectoryAccessError");
        log.Error(e.Exception, "Exception: " + e.Exception.Message);
    }

    public void Observe(CannotCreateScannedStoryDirectory e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.CannotCreateScannedStoryDirectory");
        log.Error(e.Exception, "Directory: {Directory}, Exception: {Exception.Message}".SmartFormat(e));
    }

    public void Observe(CannotReadStoryDocument e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.CannotReadStoryDocument");
        if (e.Exception == null) {
            log.Warn("File: {FilePath}".SmartFormat(e));
        }
        else {
            log.Warn(e.Exception, "File: {FilePath}, Exception: {Exception.Message}".SmartFormat(e));
        }
    }

    public void Observe(StorySkippedPastCutoff e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.StorySkippedPastCutoff");
        log.Warn("Story Unique ID: {Story.UniqueId}, File: {FilePath}, Title: {Story.Headline}".SmartFormat(e));
    }

    public void Observe(StorySkippedDuplicateUniqueId e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.StorySkippedDuplicateUniqueId");
        log.Warn("Story Unique ID: {Story.UniqueId}, File: {FilePath}, Title: {Story.Headline}".SmartFormat(e));
    }

    public void Observe(StorySkippedByFilter e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.StorySkippedByFilter");
        log.Warn("Story Unique ID: {Story.UniqueId}, Reason: {Reason}, File: {FilePath}, Title: {Story.Headline}".SmartFormat(e));
    }
}

1

Diğerlerinin söylediği gibi, log4jya log4netda ya da başka bir iyi inşa edilmiş günlükleme çerçevesini kullanın.

Gerçekten iş mantığının önüne alma günlük kodu sevmeme eğilimindedir. Bu yüzden kullanıyorum Log4PostSharp. Bu, şöyle yöntemlere açıklama eklemek için Aspect Oriented Programming kullanabileceğiniz anlamına gelir :

[Log(LogLevel.Info, "Counting characters.")]
int CountCharacters(string arg) 
{
    return arg.Length;
}

Veya bir montajdaki her yöntem şöyle:

[assembly: Log(AttributeTargetTypes = "*", 
 EntryLevel = LogLevel.Debug, ExitLevel = LogLevel.Debug, 
 ExceptionLevel = LogLevel.Error)]

0

Herhangi bir çerçevenin bunu yapıp yapmadığından emin değilim, ancak tasarım perspektifinden bakıldığında, bilgilerin temel olarak üç kategoriye girilmesi gerektiğini modelledim:

  1. yöntem düzeyinde izleme
  2. istisna günlüğü
  3. ekstra çalışma zamanı bilgi geliştiricileri, çalışma zamanı hatası (veya yalnızca çalışma zamanı durumuyla ilgili herhangi bir davranış) durumunda araştırma için hayati önem taşır.

İlk iki kategori için, ideal günlükleme çerçevem ​​onları post-post süreci olarak ele almalı ve geliştiriciler için şeffaf olmalıdır. Aşağıdaki gibi, derlemelere bildirimleri günlük olarak eklemek güzel olurdu:

Trace YourNamespace.* [public methods|constructors]
{  # options
   ignore trivial methods,
   format: "{time stamp}: {method name}({parameter list})",
   condition: "{Context.Instance.UserID in (12432,23432)}",
}

Exception YourNamespace.Program.Main [unhandled exceptions]
{
  format: "{time stamp}: {Context.Instance.UserId} {exception}",
  action: die,  # options are throw, swallow,
}

3. kategori için, programcılar sadece bir veya daha fazla özel "kayıt" yöntemi oluşturabilir ve ilk kategori için izlemeyi kullanabilirler. Günlüğe kaydetme yöntemleri, izleme kurallarının uygulanabileceği bir saplama noktası sunmaktan başka bir şey yapmaz.

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.