Czy to dobra praktyka mieć loggera jako Singletona?

Miałem zwyczaj przekazywać loggera do konstruktora, jak:

public class OrderService : IOrderService {
     public OrderService(ILogger logger) {
     }
}

Ale to jest dość irytujące, więc używam go od jakiegoś czasu:

private ILogger logger = NullLogger.Instance;
public ILogger Logger
{
    get { return logger; }
    set { logger = value; }
}
To też robi się irytujące - nie jest suche, muszę to powtarzać na każdych zajęciach. Mógłbym użyć klasy bazowej, ale z drugiej strony - używam klasy Form, więc potrzebowałbym FormBase, itp. Więc myślę, co byłoby minusem posiadania Singletona z Iloggerem odsłoniętym, więc bardzo dobrze wiedziałoby, skąd wziąć loggera: {]}
    Infrastructure.Logger.Info("blabla");

UPDATE: As Merlyn poprawnie zauważył, powinienem wspomnieć, że w pierwszym i drugim przykładzie używam DI.

Author: Giedrius, 2011-12-12

8 answers

To też robi się denerwujące - to nie jest suche

To prawda. Ale tylko tyle możesz zrobić dla przekrojowej troski, która przenika każdy Twój typ. Musisz używać rejestratora wszędzie, więc musisz mieć właściwość na tych typach. Zobaczmy, co możemy z tym zrobić.

Singleton

Singletony są straszne <flame-suit-on>.

Polecam trzymać się wtrysku nieruchomości, tak jak zrobiłeś to ze swoim drugi przykład. Jest to najlepszy faktoring, który możesz zrobić bez uciekania się do magii. Lepiej jest mieć wyraźną zależność niż ukrywać ją przez singleton.

Ale jeśli singletony zaoszczędzą Ci dużo czasu, w tym wszystkie refaktoryzacje, które kiedykolwiek będziesz musiał wykonać (czas kryształowej kuli!), Przypuszczam, że będziesz mógł z nimi żyć. Jeśli kiedykolwiek przydałby się Singleton, to może to być to. Pamiętaj, że koszt, jeśli kiedykolwiek chcesz zmienić zdanie, będzie tak wysoki, jak to dostaje.

Jeśli to zrobisz, sprawdź odpowiedzi innych osób używając wzorca Registry (zobacz opis) i tych rejestrujących (resetable) singleton factory zamiast instancji singleton logger.

Istnieją inne alternatywy, które mogą działać równie dobrze bez kompromisów, więc powinieneś je najpierw sprawdzić.

Fragmenty kodu Visual Studio

Możesz użyć fragmentów kodu Visual Studio , aby przyspieszyć wejście do ten powtarzający się kod. Będziesz mógł wpisać coś w rodzaju loggertab , A kod pojawi się magicznie dla Ciebie.

Używanie AOP do wyschnięcia

Możesz wyeliminować trochę tego kodu wtrysku właściwości, używając frameworka programowania zorientowanego na aspekt (AOP), takiego jak PostSharp do automatycznego generowania niektórych z nich.

To może wyglądać tak, jak skończysz:

[InjectedLogger]
public ILogger Logger { get; set; }

Możesz również użyć ich metody śledzenia kodu próbki do automatycznie śledź kod wejścia i wyjścia metody, co może wyeliminować potrzebę dodania niektórych właściwości rejestratora razem. Można zastosować atrybut na poziomie klasy lub przestrzeni nazw:

[Trace]
public class MyClass
{
    // ...
}

// or

#if DEBUG
[assembly: Trace( AttributeTargetTypes = "MyNamespace.*",
    AttributeTargetTypeAttributes = MulticastAttributes.Public,
    AttributeTargetMemberAttributes = MulticastAttributes.Public )]
#endif
 33
Author: Merlyn Morgan-Graham,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-15 07:47:49

Umieszczam instancję loggera w moim kontenerze dependency injection, który następnie wstrzykuje logger do klas, które go potrzebują.

 37
Author: Daniel Rose,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-12 10:23:26

Dobre pytanie. Wierzę, że w większości projektów logger jest singletonem.

Niektóre pomysły przychodzą mi do głowy:
  • Użyj ServiceLocator (lub innego Dependency Injection kontenera, jeśli już używasz), który pozwala na współdzielenie loggera między usługami/klasami, w ten sposób możesz utworzyć instancję loggera lub nawet wiele różnych loggerów i udostępnić przez ServiceLocator, który oczywiście byłby singletonem, jakimś rodzajem inwersji loggera. Kontrola . Takie podejście zapewnia dużą elastyczność w procesie tworzenia i inicjalizacji loggera.
  • Jeśli potrzebujesz loggera prawie wszędzie-zaimplementuj metody rozszerzeń dla typu Object, aby każda klasa mogła wywoływać metody loggera jak LogInfo(), LogDebug(), LogError()
 23
Author: sll,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-12 16:43:11

Singleton to dobry pomysł. Jeszcze lepszym pomysłem jest użycie wzorca rejestru , który daje nieco większą kontrolę nad instancją. Moim zdaniem wzór Singletona jest zbyt zbliżony do zmiennych globalnych. W przypadku rejestru obsługującego tworzenie lub ponowne użycie obiektów jest miejsce na przyszłe zmiany reguł tworzenia instancji.

Sam rejestr może być klasą statyczną, która daje prostą składnię dostępu do dziennika:

Registry.Logger.Info("blabla");
 14
Author: Anders Abel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-12 10:14:17

Zwykły singleton nie jest dobrym pomysłem. To utrudnia wymianę rejestratora. Zazwyczaj używam filtrów dla moich loggerów(niektóre "hałaśliwe" klasy mogą rejestrować tylko ostrzeżenia / błędy).

Używam wzoru Singletona połączonego ze wzorem proxy dla logger factory:

public class LogFactory
{
    private static LogFactory _instance;

    public static void Assign(LogFactory instance)
    {
        _instance = instance;
    }

    public static LogFactory Instance
    {
        get { _instance ?? (_instance = new LogFactory()); }
    }

    public virtual ILogger GetLogger<T>()
    {
        return new SystemDebugLogger();
    }
}

Pozwala mi to stworzyć FilteringLogFactory lub po prostu SimpleFileLogFactory bez zmiany kodu (a więc spełniania Zasady Open/Closed).

Przykładowe rozszerzenie

public class FilteredLogFactory : LogFactory
{
    public override ILogger GetLogger<T>()
    {
        if (typeof(ITextParser).IsAssignableFrom(typeof(T)))
            return new FilteredLogger(typeof(T));

        return new FileLogger(@"C:\Logs\MyApp.log");
    }
}

I do korzystania z nowego fabryka

// and to use the new log factory (somewhere early in the application):
LogFactory.Assign(new FilteredLogFactory());

W klasie, która powinna się logować:

public class MyUserService : IUserService
{
    ILogger _logger = LogFactory.Instance.GetLogger<MyUserService>();

    public void SomeMethod()
    {
        _logger.Debug("Welcome world!");
    }
}
 9
Author: jgauffin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-15 07:39:36

Istnieje iniekcja zależności W .NET. na podstawie tego, czego potrzebujesz, powinieneś użyć przechwytywania.

W tej książce znajduje się diagram pomagający zdecydować, czy użyć Injection konstruktora, property injection, method injection, Ambient Context, Interception.

Oto jak jeden z powodów używania tego diagramu:

    Masz zależność czy jej potrzebujesz? - Need it Czy to przekrojowe zmartwienie? - Tak
  1. potrzebujesz odpowiedzi? - Nie

Użyj Przechwytywania

 3
Author: Piotr Perak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-16 08:02:07

Jeśli chcesz spojrzeć na dobre rozwiązanie do logowania proponuję spojrzeć na Google app engine z Pythonem, gdzie logowanie jest tak proste, jak import logging, a następnie można po prostu logging.debug("my message") lub logging.info("my message") co naprawdę trzyma to tak proste, jak powinno.

Java nie miaĹ 'a dobrego rozwiÄ ... zania do logowania ie log4j naleĹźy unikać, poniewaĹź praktycznie zmusza do uĹźycia singletonăłw ktĂłre jak tu odpowiedziaĺ' y sÄ ... "straszne" i miaĹ 'em straszne doĹ" wiadczenie z prăłbowaniem wykonania logowania wyĹ "wietlajÄ ... cego te same wyĹ" wietlajÄ ... ce kiedyś, gdy podejrzewam, że powodem podwójnego logowania było to, że mam jeden Singleton obiektu logowania w dwóch classloaderach na tej samej maszynie wirtualnej (!)

Przepraszam, że nie jestem tak specyficzny dla C#, ale z tego co widziałem rozwiązania z C# wyglądają podobnie do Javy gdzie mieliśmy log4j i też powinniśmy zrobić z niej singleton.

Dlatego bardzo spodobało mi się rozwiązanie z Gae / python, jest tak proste, jak to tylko możliwe i nie musisz się martwić o classloadery, uzyskanie podwójnej deklaracji logowania lub dowolnego wzorca projektowego w ogóle o to chodzi.

Mam nadzieję, że niektóre z tych informacji mogą być istotne dla Ciebie i mam nadzieję, że chcesz spojrzeć na i logowania rozwiązanie polecam zamiast tego dręczyć się na ile problem Singleton się podejrzewać ze względu na niemożność posiadania prawdziwego singleton, gdy musi być instanciating w kilku classloaders.

 0
Author: Niklas Rosencrantz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-13 17:57:07

Innym rozwiązaniem, które osobiście uważam za najłatwiejsze, jest użycie klasy Static Logger. Można ją wywołać z dowolnej metody klasy bez konieczności zmiany klasy, np. add property injection itd. Jest dość prosty i łatwy w użyciu.

Logger::initialize ("filename.log", Logger::LEVEL_ERROR); // only need to be called once in your application

Logger::log ("my error message", Logger::LEVEL_ERROR); // to be used in every method where needed
 0
Author: Erik Kalkoken,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-28 12:44:57