2012-07-03 5 views
18

EDIT 2: Mam rozwiązać problem (patrz odpowiedź poniżej) Należy pamiętać, że problem dotyczy potencjalnie wszystkich appenders ozdobione BufferingForwardingAppender jak również wszystkich appenders dziedziczących z BufferingAppenderSkeleton (odpowiednio: AdoNetAppender, RemotingAppender, SmtpAppender i SmtpPickupDirAppender) *log4net problem wydajność BufferingForwardingAppender

Robiłem kilka bardzo podstawowych ławek log4net i próbowałem udekorować RollingFileAppender za pomocą BufferingForwardingAppender.

Przeżyłem straszny występ przechodząc przez BufferingForwardingAppender zamiast bezpośrednio przez RollingFileAppender i naprawdę nie rozumiem powodu.

Oto moja konfiguracja:

<appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender"> 
    <file value="c:\" /> 
    <appendToFile value="false" /> 
    <rollingStyle value="Composite" /> 
    <datePattern value="'.'MMdd-HH'.log'" /> 
    <maxSizeRollBackups value="168" /> 
    <staticLogFileName value="false" /> 
    <layout type="log4net.Layout.PatternLayout">  
    <conversionPattern value="%date [%thread] %-5level %logger - %message%newline" /> 
    </layout> 
</appender> 

<appender name="BufferingForwardingAppender" type="log4net.Appender.BufferingForwardingAppender"> 
    <bufferSize value="512" /> 
    <appender-ref ref="RollingLogFileAppender" /> 
</appender> 

<root> 
    <level value="DEBUG" /> 
    <appender-ref ref="BufferingForwardingAppender" />  
</root> 

I tu jest punkt odniesienia (bardzo prosty kod):

var stopWatch = new Stopwatch(); 
stopWatch.Start();    
for (int i = 0; i < 100000; i++)    
{ 
    Log.Debug("Hello"); 
} 
stopWatch.Stop(); 
Console.WriteLine("Done in {0} ms", stopWatch.ElapsedMilliseconds); 

przechodząc bezpośrednio przez RollingFileAppender wyjście jest:

odbywa się w 511 ms

Podczas gdy przechodzi BufferingForwardingAppender dekorowanie RollingFileAppender:

Sporządzono w 14261 ms

to około 30 razy wolniej.

Pomyślałem, że zyskałbym pewną prędkość, buforując pewną ilość dzienników przed zapisaniem ich do pliku, jednak z jakiegoś powodu sytuacja znacznie się pogorszy.

Wydaje mi się, że konfiguracja jest OK, więc to naprawdę dziwne.

Ktoś ma jakąś wskazówkę?

Dzięki!

EDIT 1:

Zachowanie jest ściśle ten sam owijając/zdobienia FileAppender lub nawet ConsoleAppender (wciąż tam jest przykładem podstawowy BufferingForwardingAppender owijania/zdobienia ConsoleAppender w urzędowych próbek log4net config .. i nic konkretne wspomniane kwestie dotyczące wydajności).

Po pewnym dochodzeniu/profilowaniu, widzę, że większość czasu jest zepsute wewnątrz BufferingForwardingAppender bardziej szczegółowo w wywołaniu WindowsIdentity.GetCurrent() ... jest nazywane KAŻDYM czasem, gdy wykonamy połączenie z Log.Debug() .. w poprzedniej próbce (100 000 razy w powyższym źródle próbki).

Połączenia z tą metodą są bardzo kosztowne i należy ich unikać lub minimalizować, naprawdę nie rozumiem, dlaczego są wywoływane w przypadku każdego zdarzenia dziennika. Czy naprawdę całkowicie błędnie coś konfiguruję/nie widzę czegoś oczywistego, czy jest to jakoś gdzieś błąd, to właśnie próbuję rozgryźć teraz ...

Częściowa stos połączenie jest:

  • AppenderSkeleton.DoAppend
  • BufferingAppenderSkeleton.Append
  • LoggingEvent.FixVolatileData
  • LoggingEvent.get_UserName()

Wywołanie jest get_LocationInformation() również wykonane w FixVolatileData, ponosząc również wysokie koszty perf (uchwycić s ślad za każdym razem).

Teraz próbuję zrozumieć, dlaczego to niezwykle kosztowne wywołanie FixVolatileData (przynajmniej dla poprawki) dzieje się dla każdego zdarzenia dziennika w tym kontekście, podczas gdy przechodzenie bezpośrednio przez zapakowanego appender (bezpośrednio przez ConsoleAppender/FileAppender ..) nie wykonać tego rodzaju operację.

Nadchodzące aktualizacja do naśladowania, chyba że ktoś dostał odpowiedź na to wszystko;)

Dzięki!

Odpowiedz

37

dowiedziałem się problem.

BufferingForwardingAppender dziedziczy z BufferingAppenderSkeleton (podobnie jak inne appenders korzystający z rejestrowania zdarzeń buforujące takie jak AdoNetAppender, RemotingAppender, SmtpAppender ..).

W rzeczywistości BufferingAppenderSkeleton buforuje zdarzenia rejestrowania przed dostarczeniem ich do docelowego elementu docelowego po spełnieniu określonego warunku (na przykład pełnego bufora).

Według dokumentacji klasy LoggingEvent (reprezentujący rejestrowanie zdarzeń, a zawierający wszystkie wartości (komunikat, ThreadID ...) zdarzenia):

Niektóre zdarzenia rejestrowania właściwości uznawane są za „lotny” to jest wartości są poprawne w momencie dostarczenia zdarzenia do nadawców, , ale później nie będą spójne. Jeśli zdarzenie ma być przechowywane i przetwarzane zdarzenie , te zmienne wartości muszą zostać naprawione podczas biwalowania FixVolatileData. Jest to spektakl kary poniesione przez wywołanie FixVolatileData ale jest to niezbędne do utrzymać spójność danych

Te „lotne” właściwości są reprezentowane przez FixFlags wyliczeniowych zawierający flagi, takich jak wiadomości, ThreadName, nazwę użytkownika, tożsamości ... więc wszystkie lotne właściwości. Zawiera również flagę "Brak" (popraw brak właściwości), "Wszystkie" (napraw wszystkie właściwości) i "Częściowe" (popraw tylko określone predefiniowane zestawienie właściwości).

Whem BufferingAppenderSkeleton jest instancjonowany, przez DEFAULT ustawia fixing na "All", co oznacza, że ​​wszystkie "lotne" właściwości powinny zostać naprawione.

W tym kontekście dla każdego LoggingEvent dołączonego do BufferingAppenderSkeleton, WSZYSTKIE "lotne" właściwości zostaną naprawione zanim zdarzenie zostanie wstawione do bufora. Obejmuje to właściwości Tożsamość (nazwa użytkownika) i Informacje o lokalizacji (śledzenie stosu), nawet jeśli te właściwości nie są zawarte w układzie (ale wydaje mi się, że ma to jakiś sens, jeśli układ zostanie zmieniony, aby uwzględnić te właściwości w późniejszym czasie, podczas gdy bufor został już wypełniony LoggingEvents).

Jednak w moim przypadku jest to naprawdę HURTS. Nie jestem w tym tożsamość i LocationInformation w moim układzie i nie planuje (głównie do problemów z wydajnością)

Teraz dla rozwiązania ...

Istnieją dwa obiekty w BufferingAppenderSkeleton które mogą być wykorzystane do kontrolowania wartości flagi FixFlags z BufferingAppenderSkeleton (po raz kolejny domyślnie jest ustawiona na "ALL", co nie jest bardzo miłe!). Te dwie właściwości to Fix (typ FixFlags) i OnlyFixPartialEventData (typ bool).

Aby uzyskać precyzyjną regulację wartości flagi lub wyłączyć wszystkie poprawki, należy użyć właściwości Fix. Dla określonej częściowej predefiniowanej kombinacji flag (bez tożsamości lub LocationInfo) można zamiast tego użyć OnlyFixPartialEventData, ustawiając ją na "true".

Gdybym ponowne próbkę konfiguracji powyżej (w moim pytaniu), jedyna zmiana wprowadzona do konfiguracji na wyższą wydajność podana jest poniżej:

<appender name="BufferingForwardingAppender" type="log4net.Appender.BufferingForwardingAppender"> 
    <bufferSize value="512" /> 
    <appender-ref ref="RollingLogFileAppender" /> 
    <Fix value="0"/> <!-- Set Fix flag to NONE --> 
</appender> 

Użycie tej zmodyfikowanej konfiguracji, wykonanie odniesienia kod podany w moim powyższe pytanie spada z ok. 14000ms do 230ms (60X szybciej)! A jeśli używam <OnlyFixPartialEventData value="true"/> zamiast wyłączać wszystkie poprawki, trwa to około 350ms.

Niestety, ta flaga nie jest bardzo dobrze udokumentowana (z wyjątkiem dokumentacji SDK, trochę) .. więc musiałem głęboko zagłębić się w źródła log4net, aby znaleźć problem.

Jest to szczególnie problematyczne, szczególnie w przykładach konfiguracji "odniesienia", ta flaga nie pojawia się nigdzie (http://logging.apache.org/log4net/release/config-examples.html). Tak więc próbki dostarczone dla BufferingForwardingAppender i AdoNetAppender (i innych dostawców dziedziczących z BufferingAppenderSkeleton) dadzą użytkownikom TERRIBLE wydajność, nawet jeśli układ, którego używają, jest dość minimalny.

+2

Witam, Logujemy wątek, wyjątek i niestandardowy identyfikator pola. Używamy log4net.Appender.AdoNetAppender. Próbowaliśmy określić , a wynikiem było zatrzymanie rejestrowania wartości wątku i wyjątku. Również nieprawidłowe pole identyfikatora zostało uruchomione niepoprawnie. Chociaż wydajność poprawiła się prawie 10 razy. Czy możesz podać jakąkolwiek wskazówkę? –

+0

W przypadku AdoNetAppender należy się upewnić, że te rzeczy zostały naprawione. Dodaj wartości rzeczy, które musisz poprawić w tej dokumentacji referencyjnej: http://logging.apache.org/log4net/release/sdk/log4net.Core.FixFlags.html –

+0

Dzięki za rozwiązanie! Tak więc numery _final_ będą miały postać 'RollingFileAppender': 511 ms vs 230 ms lub 350 ms (w zależności od konfiguracji) dla' BufferingForwardingAppender'? Z pewnością poprawa, ale nie tak imponująca, jak myślałam ... – Josep

0

Czy to możliwe, że to dlatego, że nie są określenia wzoru układu w BufferingForwardingAppender ale jesteś w RollingLogFileAppender dlatego BufferingForwardingAppender jest w tym wszystko w jest to wyjście w tym nazwę użytkownika (% username)

Poniżej znajduje się ciekawy artykuł na blogu, który zawiera listę opcji w układach wzorów i wygląda na to, że kilka z nich zostało oznaczonych jako wolne.

http://www.beefycode.com/post/Log4Net-Tutorial-pt-4-Layouts-and-Patterns.aspx

+0

Witam i dziękuję za odpowiedź! Myślałem o tym już i próbowałem określić ten sam wzór układu, który miałem w moim RollingFileAppender wewnątrz BufferingForwardingAppender. Niestety, to nie rozwiązuje problemu. Znalazłem sposób, aby to naprawić, musiał przyjrzeć się bliżej kodowi źródłowemu log4net. W rzeczywistości właściwość BufferForwardingAppender (lub raczej BufferingAppenderSkeleton), która powinna być ustawiona z określoną wartością. Jednak ta właściwość absolutnie nie jest udokumentowana (z wyjątkiem dokumentacji SDK, ale niejasna). Szybko odpowie na moje pytanie z wszystkimi szczegółami – darkey