W jednym z projektów mam serwis, który zajmuje się przetwarzaniem plików i dla każdego takiego przetwarzania muszę dostarczyć log tego co się zadziało, począwszy od rzeczy typu:
_log.Info("Task id: {0}", taskId);
a skończywszy na wyjątkach, zarówno tych, które dotyczą funkcjonowania samego serwisu, a skończywszy na tych, które wystąpiły w kontekście danego zadania. Najważniejsze jednak jest to, że muszą być dwa pliki logu:
1. dla użytkownika z informacją co się działo w każdym zadaniu,
2. dla wygody administratora chcę mieć jeden wór, w którym będzie zalogowane wszystko bez konieczności latania po wszystkich plikach z pkt 1.
Konfiguracja mojego NLoggera wygląda następująco:
<?xml version="1.0" encoding="utf-8" ?> <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <targets> <target name="TaskLogFile" xsi:type="File" fileName="" layout="${longdate}|${level:uppercase=true}|${message}" /> <target name="ServiceLogFile" xsi:type="File" fileName="c:\Temp\Log/${date:format=yyyyMMdd}_FilesProcessingService.txt" layout="${longdate}|${level:uppercase=true}|${message}" /> </targets> <rules> <logger name="*" minlevel="Trace" writeTo="TaskLogFile" /> </rules> <rules> <logger name="*" minlevel="Trace" writeTo="ServiceLogFile" /> </rules> </nlog>
Mam dwa loggery, których nazwy wyjaśniają do czego każdy służy, zaś w kodzie mam metodę, która dla każdego przetwarzanego zadania konfiguruje target na nowo. Pusty fileName w targecie „TaskLogFile” powoduje, że nic do niego nie jest logowane. Użyteczny feature.
private string SetLogFilePath() { var target = (FileTarget)LogManager.Configuration.FindTargetByName("TaskLogFile"); var fileName = $@"{Path.Combine( ConfigurationManager.AppSettings["TaskLogFileDirectory"], Guid.NewGuid().ToString())}.txt"; target.FileName = fileName; return fileName; }
Jak widać cała rekonfiguracja ma na celu utworzenie nowego pliku, do którego będą logowane dane danego zadania. A teraz główna metoda serwisu:
private void ProcessFiles(object obj) { _log.Info("Service has started ..."); try { /*łączenie się z bazą danych*/ foreach (var taskId in db.Fetch<int>(ProcessingFileTaskQueries.GetNotProcessedQuery())) { var task = db.SingleById<ProcessingFileTask>(taskId); if (task != null) { //do tego miejsca wszystko jest logowane do pliku //${date:format=yyyyMMdd}_FilesProcessingService.txt task.LogFilePath = SetLogFilePath(); //wszelkie logowanie dla danego tasku robimy od tego miejsca, po ustawieniu loggera. //ale te same informacje pojawiają się także w pliku //${date:format=yyyyMMdd}_FilesProcessingService.txt } } } catch (Exception e) { _log.Error(e); } }
W ten sposób osiągnąłem funkcjonalność, która loguje do dwóch osobnych plików
1. dla konkretnego zadania do udostępnienia dla użytkownika w aplikacji
2. jeden „wór” z logami wszystkich zadań i samego serwisu.
Czy można inaczej, lepiej? Pewnie można.