Wstęp

Najprościej opisać typy złożone jako typy budowane na bazie typów podstawowych. To w jaki sposób będą one przechowywane, jak będziemy się do nich odnosić, gdzie fizycznie będą przechowywane zależeć już będzie od specyfikacji konkretnego typu złożonego.  W dzisiejszym wpisie omówię pierwszy z serii typ złożony – tablice.

Tablica jest strukturą danych umożliwiającą przechowywanie wielu wartości konkretnego typu. Aby zadeklarować poprawnie tablicę należy:

  • przedstawić typ jej elementów
  • podać jej nazwę
  • sprecyzować jej pojemność.

Posłużę się przykładem aby przybliżyć ten opis.

int numbers[50];

Powyższa deklaracja utworzyła tablicę 50 elementów, do których możemy odwoływać się poprzez indeksy tablicy. Tablica n elementowa ma indeksy od 0 (pierwszy element) do n-1 (ostatni element tablicy).

Inicjalizacja

W c++ tablicę można zainicjalizować w następujący sposób:

// Inicjalizacja tablicy wartościami przypadkowymi, które znajdowały się w pamięci pod adresem utworzonej tablicy
int numbers1[2];

// inicjalizacja tablicy wartościami 100 oraz 200
int numbers2[2] = { 100,200 }; 

// inicjalizacja pierwszego elementu tablicy wartością 100, pozostałe wartości inicjalizowane są automatycznie wartościami 0
int numbers3[2] = { 100 }; 

// wszystkim elementom tablicy przypisywana jest wartość 0
int numbers4[2] = {}; 

// wszystkim elementom tablicy przypisywana jest wartość 0
int numbers5[2]{};

// w tym przypadku mimo braku sprecyzowania liczby elementów w nawiasach, kompilator policzy liczbę elementów inicjalizujących tablicę I stworzy tablicę o takim właśnie rozmiarze
int numbers5[] = {100,200,300}; 

Aby bardziej przybliżyć powyższe formułki, zaprezentuję prosty przykład, w którym zapiszemy 5 liczb całkowitych podanych przez użytkownika do tabeli obliczając jednocześnie ich sumę.

#include "stdafx.h"
#include <iostream>;
using namespace std;

int main()
{

const int count = 5;
int numbers[count];
int sum = 0;
for (int i = 0; i &lt; count; i++)
{
  cout << "numbers[" << i << "] = ";
  cin >> numbers[i];
  sum += numbers[i];
}

cout << endl << "Suma wszystkich podanych liczb wynosi " << sum << endl;
cout << "Rozmiar tablicy w bajtach sizeof(numbers) = " << sizeof(numbers) << endl; // 5 * sizeof(int)

cin.get();
cin.get();
return 0;
}

Podsumowanie

Tablice to struktura danych, która umożliwa nam na przechowywanie wielu wartości tego samego typu w jednym obiekcie. Do jej wartości odwołujemy się poprzez indeksy, gdzie indeks pierwszego elementu to 0, a ostatniego to n-1; gdzie n to liczba elementów. Używając operatora sizeof(nazwa_tablicy) otrzymamy liczbę bajtów jaką zajmuje nasza tablica.

Wstęp

Typy danych w języku C++ dzielą się na dwie kategorie: typy podstawowe oraz typy złożone.
W niniejszym poście pokrótce postaram się przedstawić typy podstawowe.
Typy podstawowe również mają swój podział.  Dzielą się na całkowitoliczbowe (do których nalezą również typy znakowe), zmiennoprzecinkowe oraz typ bool.

 

Liczby całkowite

Liczby całkowite to typy które pozbawione są części ułamkowej np. -123, 0 czy 54321.
Rozróżniamy kilka typów całkowitoliczbowych o różnej wielkości (ilości bajtów), przez co zmienne tych typów mogą przechowywać różne zakresy wartości.
Typy te przedstawia poniższa tabela.

nazwa typu

liczba bajtów

zakres od

zakres do

ograniczenia

short

2

-32768

32 767

przynajmniej 16 bitów

unsigned short

2

0

65 535

przynajmniej 16 bitów

int

4

-2 147 483 648

2 147 483 647

nie mniejsze niż typ short

unsigned int

4

0

4 294 967 295

nie mniejsze niż typ short

long

4

-2 147 483 648

2 147 483 647

przynajmniej 32 bity I nie mniejsze niż int

unsigned long

4

0

4 294 967 295

przyajmniej 32 bity I nie mniejsze niż int

long long

8

9 223 372 036 854 775 808

9 223 372 036 854 775 807

przynajmniej 64 bity I nie mniejsze niż long

unsigned long  long

8

0

18 446 744 073 709 551 615

przynajmniej 64 bity I nie mniejsze niż long

Rzeczywisty rozmiar typów danych zależy od kompilatora, jednakże musi spełniać on przedstawione w powyższej tabeli ograniczenia.
Aby sprawdzić rozmiar zmiennych na swoich maszynach należy zastosować operator sizeof. Zwróci nam on liczbę bajtów danego typu lub zmiennej.

Przykład wywołania:

int intVal = 10;
cout << "intVal: "<< sizeof intVal << endl;
cout << "int: " << sizeof(int) << endl;

 

Inicjalizowanie zmiennych.

Nadszedł czas aby pokrótce pojęcie inicjalizacji. Otóż jest to nadanie wartości początkowej dowolnemu obiektowi/zmiennej. Otóż w języku C++  można to uczynić na kilka sposobów, a oto I one:

int year = 2016;
int month(9);
int day{11};
int age = {30};
int counter{} ; //przypisanie counter wartości 0

Typy znakowe

Typ znakowy char jest również typem całkwitoliczbowym służącym do przechowywana znaków kodowanych kodem ASCII (litery, cyfry, znaki specjalne).

Inicjalizacja zmiennej typu char wygląda następująco:

char example='A';

Inicjalizując zmienną typu char, przypisujemy jej znak podany w pojedyńczym apostrofie.

W rzeczywistości, gdybyśmy zajrzeli do pamięci pod którą znajduje się zmienna example ujrzelibyśmy wartość całkowitą 65 (jest to wartość odpowiadająca literze ‚A’ w kodzie ASCII).

nazwa typu

liczba bajtów

zakres od

zakres do

char

1

-128

127

signed char

1

-128

127

unsigned char

1

0

255

wchar_t

2

0

65 535

char16_t

2

0

65 535

char32_t

4

0

4 294 967 295

W powyższej tabeli zostały przedstawione typy znakowe dostępne w C++. Na szczególną uwagę zasługują trzy ostatnie typy.

Umożliwiają one przechowywanie danych znakowych z większego zakresu niż zwykły typ char. Ze względu na to, że rozmiar typu wchar_t zależny jest od kompilatora,
postanowiono wprowadzić do języka c++ dwa dodatkowe typy o stałym ustalonym rozmiarze występujące jedynie w postaci unsigned.

Inicjalizacja zmiennych znakowych różni się w zależności od używanego typu i przedstawia się następująco:

char c1 = 'A';
wchar_t = L'A';
char16_t = u'A';
char32_t = U'A';

Liczby zmiennoprzecinkowe

Liczby zmiennoprzecinkowe, to takie które oprócz części całkowitej mogą zawierać również część ułamkową, np: -0,5; 3,14159; 7,5e12.
W przykładzie przedstawiłem dwie formy przestawiające liczby zmiennoprzcinkowe, standardową formę oraz notację naukową, w której używane jest oznaczenie ‚e’ lub ‚E’.
W przykładzie 7,5e12 oznacza pomnożenie liczby 5 przez 10 podniesione do potęgi 12.
Można również zastosować chociażby następujący opis 4,32e-5. Znaczy to tyle co pomnożenie 4.32 przez 0,00001.
W notacji naukowej wyróżniamy dwie części składowe. Mantysa to część znajdująca na lewo od oznaczenia ‚e’. Drugą składową jest wykładnik znajdujący się na prawo od oznaczenia ‚e’.

Powyższa tabela przedstawia możliwości poszczególnych typów zmiennoprzecinkowych.

nazwa typu

liczba bajtów

minimalna liczba cyfr znaczących

minimalny zakres wykładników

float

4

6-7

od -37 do 38

double

8

15-16

od -307 do 308

long double

8-9

18

od -4931 do 4932

Typ bool

Jest to ostatni I zarazem najprostszy z typów, który służy do przechowywania wartości logicznej : prawdy bądź fałszu.

nazwa typu

liczba bajtów

dopuszczalne wartości

bool

1

true,false

Przykłady:

bool isDone=false;
bool isCanceled = -100; //zmienna ma wartość true
bool isRunning = 0; //zmienna ma wartość false
bool isEnd = 1; //zmienna ma wartość true

Ciekawostka odnośnie typów bool jest to, że każda liczba może być niejawnie skonwertowana na ten typ wedle prostej reguły.Wszystkie wartości różne od zera konwertowane są na true, natomiast  na 0 false.

 

Podsumowanie

Jak widzimy ilość typów prostych jest dość spora.

Typy całkowite różnią się między sobą ilością użytych bajtów przez co zwiększają swój zakres, oraz posiadaniem znaku bądź jego znaku. Typy znakowe choć są zapisywane jako kody znakowe, to system wejścia/wyjścia prezentuje je jako odpowiednie znaki odpowiadające danemu kodowi znaku. Typy zmiennoprzecinkowe pozwalają na zapisanie części całkowitej wraz z częścią ułamkową. Dzięki formie prezentacji (mantysa I wykładnik) są one w stanie przedstawić liczbę znacznie większej niż long long przy utracie precyzji (liczba cyfr znaczących).

 

Wstęp

Dzisiejszy post jest jedynie wstępem do krótkiego cyklu postów, poświęconych zagadnieniom związanym z wątkami na platformie .NET.

Zajmiemy się dziś budową wątku oraz krótko opiszę sposób w jaki system wykonuje przełączanie kontekstu. Zanim jednak to nastąpi powiedzmy sobie krótko czym są wątki ?

Wątki to części programu wykonywane współbieżnie w ramach jednego procesu. To co je charakteryzuje to to, że współdzielą one pamięć adresową. To z kolei wpływa na to, że są one lżejsze od procesu, czas ich tworzenia jest szybszy i mogą one się ze sobą komunikować.

 

Budowa wątku

Każdy wątek składa się z niżej podanych rodzajów alokowanej pamięci:

Thread kernel object – Jest to specjalna struktura alokowana przez system operacyjny dla każdego tworzonego wątku. Mieści ona między innymi thread context. Thread context  jest to blok w pamięci, który zawiera zbiór wartości rejestrów procesora(o czym wspomnę nieco później). Dla CPU x64 rozmiar kotekstu wątka wynosi ok. 1240 bajtów.

Thread environment block  (Thrad Information Block) – jest to struktura, która przechowuje informacje o obecnie działającym wątku. Między innymi przechowuje ona  dane TLS(thread-local storage). Ponadto, TEB zawiera wskaźnik na „head” łańcucha wywołań obsługi błędów. Znaczy to tyle, że każde wejście w blok try danego wątku, dodaje węzeł do „head” , który jest następnie usuwany w momencie wyjścia z tego bloku. Rozmiar bloku TEB to jedna strona pamięci(4KB).

User mode stack – Jest to pamięć rezerwowana przez system o rozmiarze 1MB przeznaczona do lokalnych zmiennych i argumentów przekazywanych metodom. User mode stack zawiera również adres instrukcji która wątek powinien wykonać zaraz po tym jak zakończy się wywołanie funkcji.

Kernel mode stack  – Ze względów bezpieczeństwa, system Windows kopiuje argumenty z User mode stack do kernel mode stack, które to są przekazywane jako argumenty funkcji systemu operacyjnego. Rozmiar kernel mode stack na x64 wynosi 24 KB

 

Przełączanie kontekstu

Liczba wątków działających jednocześnie w aplikacji zależy od liczby CPU danego komputera. W danym momencie na konkretnym CPU może pracować wyłącznie jeden wątek. W przypadku gdy mamy więcej niż 1 wątków na jednym CPU wówczas w grę wchodzi przełączanie kontekstu, z jednego wątku na drugi.

Sytuacja ta wymaga jednak pomocy systemu operacyjnego, który ma za zadanie zapisanie wartości z rejestrów CPU do struktury thread context który jak wspomniałem powyżej mieści się w Thread kernel object. Następnie planista wybiera jeden wątek ze zbioru skolejkowanych i przywraca uprzednio zapisane dane ze struktury thread context do rejestrów CPU. Po tym jak przełączanie kontekstu dobiegnie końca, procesor wykonuje wątek do czasu wygaśnięcia jego przedziału czasu (ang. time slice) i cały proces przełączania kontekstu jest ponownie powtarzany.

 

Podsumowanie

Moim zdaniem, warto mieć świadomość budowy wątku jak również to w jaki sposób system wykonuje przełączenie kontekstu. Swojego czasu rozmawiałem ze znajomym o pytaniach z jakimi się spotkaliśmy będąc na różnych rozmowach kwalifikacyjnych. Jednym z nich było właśnie pytanie o rozmiar user mode stack 🙂

 

Wstęp

Kodowanie jest formą reprezentacji znaków w formie elektronicznej. Obecnie dominującymi kodowaniami na świecie są UTF-8 oraz UTF-16. Są one różnym implementacjami standardu Unicode, który według założenia powinien obejmować wszystkie pisma na świecie. Czym jest zatem Unicode ? Jest to zestaw znaków, gdzie każdemu znakowi jest przypisany punkt kodu (ang. code point).

Przykładowy punkt kodu dla znaku ‘A’ to U+0041. Gdzie U+ stały sufix,  natomiast pozostała część pisana w systemie szesnastkowym, definiuje nam konkretny znak. Zakres jaki obejmuje standard Unicode to U+0000 U+10FFFF.

 

Co się stało z kodowaniem ASCII ?

Większość z nas pewnie zna odpowiedź na to pytanie. Kodowanie ASCII (ang. American Standard Code for Information Interchange) mieści zaledwie 7 bitów do przedstawienia znaku. Co za tym idzie ma ono jedynie 128 wolnych slotów na prezentację liter alfabetu angielskiego, cyfr, znaków przystankowych czy poleceń sterujących. Nie jest tego dużo, więc standard ASCII został wkrótce rozszerzony o 8 bit dzięki czemu zyskał kolejne 128 miejsc na wykorzystanie. Różne warianty wykorzystania tych miejsc zostały nazwane kodami stron. Np. kodowanie Win-1250 zawierało znaki dla wszystkich języków z krajów Europy  środkowej. Problem jednak pozostawał, gdyż tekst napisany w jednym obszarze świata nie mógł być odpowiednio odczytany w drugim miejscu używającego innego kodu strony.

UTF-8

Tak jak wspomniałem na wstępie każdy znak Unicode ma przypisany do siebie code point. Celem kodowania jest zapisanie tego znaku w odpowiednim formacie. Kodowanie UTF-8 charakteryzuje się tym, że znaki kodowane są na różnej ilości bajtów, od 1 do 6. Początkowe znaki Unicode są zgodne z kodowaniem ASCII, zatem ich reprezentacja w UTF-8 jest identyczna i jest umieszczona na 1 bajcie.

tabela UTF-8

żródło: wikipedia

 

Przyjrzyjmy się powyższej tabeli. Zapisując dane na jednym bajcie mamy do wykorzystania 7 bitów zamiast 8. Natomiast mając dwa bajty mamy do wykorzystania 11 bitów zamiast 16. Z kolei na 3 bajtach mamy jedynie 16 bitów. Wymusza to na nas algorytm który mówi, że pierwszy bity pierwszego bajtu mówią nam na ilu bajtach zapisany jest znak. Kolejne bajty mają natomiast się zaczynać od sekwencji bitów 10, informując nas o tym że jest to kontynuacja algorytmu kodowania.

Jak to wygląda w praktyce ?

Weźmy sobie grecki symbol małej lambdy λ, której przypisany jest code point U+03BB.

W systemie binarnym 0x03BB =  0000 0011 1011 1011

W tabeli powyżej widzimy że w przypadku code point’u z zakresu U+0080 do U+07FF zapisujemy 11 bitów z naszego kodu. Ucinamy więc pierwsze bity naszego binarnego zapisy aby zostało ich 11.

Przed 0000 0011 1011 1011
Po      xxxx  x011 1011 1011

Przepiszę teraz szablon dla naszego code point’u z powyższej tabeli, aby wszystko było bardziej wyraźne.

110xxxx 10xxxxxx

Następnie miejsca x kolejno zaczynając od lewej zastępuje kolejno bitami naszego ciągu składającego się z 11 bitów : 011 1011 1011.

Otrzymujemy:

11001110 10111011 = 0xCEBB

Tym sposobem zakodowaliśmy znak Unicode kodowaniem UTF-8.

UTF-16

Kodowanie UTF-16 charakteryzuje się tym, że znaki są tu zapsiane na 2 bądź 4 bajtach. Dla znaków z zakresu U+0000 – U+FFFF (wykluczając zakres U+D800  – U+DFFF) przy kodowaniu UTF-16 kod znaku zostaje taki sam jak punkt kodowy. Mała lambda λ , której przypisany jest code point U+03BB zostanie zapisana w postaci 03 BB.

Sprawa nieco się komplikuje gdy chcemy zakodować liczbę wykraczającą poza 0xFFFF.  W tym momencie do gry wchodzi algorytm z wykorzystaniem wspomnianego wcześniej wykluczonego zakresu U+D800  – U+DFFF,  do wyliczenia dwóch wartości określanych jako lead surrogate oraz trail surrogate.

Sposób kodowania zobrazuję na przykładzie przykładowego znaku,  którym będzie U+12345.

Algorytm wygląda następująco:

1)  Z racji, iż maksymalny zakres standardu Unicode to U+10FFFF, więc odejmując od naszego wybranego znaku stałą o wartości 0x10000, otrzymamy liczbę którą będzie można zapisać na 20 bitach.

Wykonajmy teraz te obliczenie:
0x12345 =     0001  0010  0011  0100  0101
–                       0001  0000  0000  0000  0000
__________________________________
                          0000 0010 0011 0100 0101

2)   Do górnych 10 bitów wyniku dodajemy stałą  0xD800.

0xD800 =     1101 0100 0000 0000
+                                  00 0000 1000
_______________________________
                      1101 0100 0000 1000 = 0xD808

Tym sposobem otrzymaliśmy pierwsze 2 bajty nazywane lead surrogate.

3)  Do dolnych 10 bitów wyniku z pkt 1 dodajemy stałą 0xDC00.

0xDC00 =     1101 1100 0000 0000
+                                 11  0100  0101
____________________________
                       1101 1111  0100  0101 = 0xDF45

Wykonując te obliczenie otrzymujemy kolejne 2 bajty nazywane trail surrogate.

4)  Gdy połączymy te dwa wyniki otrzymamy poprawnie zakodowany code point U+12345 w systemie UTF-16. Jest to D808 DF45.

Warto zwrócić na uwagę na poniższą tabelę

UTF-16 decoder

źródło: wikipedia

Przedstawia ona zakres ze standardu Unicode który nie zawiera code point’ów a jedynie pomaga nam w ich adresowaniu w przypadku zakresu U+10000 do U+10FFFF. Dzięki zabiegom dodawania stałych w pkt 2 oraz 3 otrzymujemy lead oraz trail surrogate. Każda z nich ma swój zakres, co widać w powyższe tabeli, poprzez łatwo je rozróżnić.

Podsumowując

To co przemawia za używaniem kodowania UTF nad ASCII to:

  • Uniocode wspiera znacznie szerszy zakres znaków
  • w każdej części świata implementacje standardu Unicode są odczytywane w ten sam sposób, dając zawsze te same znaki

 

NLog, Log4net, Logging Application Block z Enterprise Library to chyba trzy najbardziej popularne loggery wśród programistów .net. Każdy z nich ma pewne pozytywne czy to negatywne cechy, które wyróżniają go na tle innych. W dzisiejszym poście skoncentruję się jednak na NLog’u zaczynając od samego początku.

Do solucji za pomocą NuGet’a dodajmy poniższą paczkę Nlog oraz Nlog Configuration.

instalowanie NLog'a z Nuget'a

instalowanie NLog’a z Nuget’a

Ważne jest aby plik w którym umieścimy naszą konfigurację (np. NLog.config) miał ustawioną właściwość Copy to Output Directory na Copy always. Teraz nasza aplikacja będzie mogła używać naszych ustawień.

Lokalizacja pliku konfiguracji

W tym miejscu należy wspomnieć, iż plik z ustawieniami może być lokalizowany z paru miejsc w zależności od rodzaju aplikacji. W przypadku  aplikacji stand-alone (.exe) wygląda to następująco:

  • Plik standardowych ustawień aplikacji (zazwyczaj appname.exe.config)
  • Plik appname.exe.nlog w katalogu aplikacji
  • Plik NLog.config w katalogu aplikacji
  • Plik NLog.dll.nlog w katalogu gdzie umieszczona jest biblioteka NLog.dll, o ile biblioteka ta nie jest zainstalowana w GAC’u

Podobnie wygląda sytuacja w aplikacjach webowych (ASP.NET):

  • Plik ustawień aplikacji (web.config)
  • Plik web.nlog umieszczony w tym samym katalogu co plik web.config
  • Plik NLog.config w katalogu aplikacji
  • Plik NLog.dll.nlog w katalogu gdzie umieszczona jest biblioteka NLog.dll, i ile biblioteka ta nie jest zainstalowana w GAC’u

Konfiguracja

Konfigurację NLoga skupię się opisując 3 jego podstawowe sekcje: targets, rules i variable. A wiec zaczynam…

<targets/> 

 Jest sekcją wymaganą. To właśnie w niej jak nazwa wskazuje konfigurujemy cel naszego loga(czy ma być on zapisywany w pliku, w bazie danych wyświetlany w konsoli, czy może ma być on wysyłany przez sieć).

Istnieje wiele możliwości definiowania źródła target’u. Lista dostępnych dla NLog’a znajduje się tu. Każda z sekcji posiada własne atrybuty, które pomagają nam w jej konfiguracji.

  <targets async="true">      
    <target name="f" xsi:type="File" fileName="${basedir}/logs/log.txt"
           archiveFileName="${basedir}/logs/archives/log.{#####}.txt"
           archiveAboveSize="10240"                     
           archiveNumbering="Rolling"
           keepFileOpen="false"/> 
  </targets>

 Powyższy przykład archiwizuje pliki gdy ich wielkość przekroczy wartość pola archiveAboveSize wyrażonego w bajtach. Z kolei ustawienie pola archiveNumbering na wartość „Rolling” powoduje zapis najnowszego pliku archiwum pod nazwą „log.00000.txt” i przesunięcie już istniejących o wartość 1.

 Sekcja <target> oferuje jednak o wiele więcej. To w niej możemy dodać atrybut layout w którym definiujemy w jakim formacie chcemy logować informacje. Domyślny format, który niektórym z nas może być wystarczający wygląda następująco:

<target name="f" xsi:type="File" fileName="logfile.txt" 
layout="${longdate}|${level:uppercase=true}|${logger}|${message}"/>

 Natomiast wykaz wszystkich możliwych zmienny dla ciekawych, można znaleźć pod tym linkiem.

<rules/>

Jest drugą i ostatnia wymaganą sekcją konfiguracji NLog’a, w której ustawiamy przepływ/warunki logowania. Elementy w tej sekcji noszą nazwę <logger/> i akceptują poniższe atrybuty:

    • name – źródło a zarazem nazwa logger’a(np. „Program. NLogProject”, „*”)
    • minlevel – minimalny poziom logu dla którego pasuje ta reguła
    • maxlevel – maksymalny poziom logu dla którego pasuje ta reguła
    • level  – pojedyńczy poziom logu dla którego pasuje reguła
    • levels – lista poziomów logu oddzielona przecinkiem dla którego pasuje reguła
    • writeTo – lista target’ów oddzielona przecinkami, które będą użyte podczas reguły
    • final – ustawia na wartość true powoduje, że żadna inna reguła nie będzie wykonana, gdy obecna reguła pasuje

  A to lista dostępnych poziomów(levels):

    • Off
    • Fatal
    • Error
    • Warn
    • Info
    • Debug
    • Trace

Jeżeli chcemy zapisywać wszystkie możliwe wiadomości do naszego pliku, nasza reguła wyglądałaby tak:

<logger name="*" minlevel="Trace" writeTo="f" />

 Jeżeli jednak chcemy zapisać wiadomości o poziomie Warn, Error i Fatal ze wszystkich klas z namespace NLogProject wystarczy dodać taki wpis:

<logger name="NLogProject.*" minlevel="Warn" maxlevel="Fatal" writeTo="f" />

 

<variable/>

Sekcja ta służy do definiowania zmiennych używanych w dalszej definicji NLog’a.

  <variable name="logProcess" value="${windows-identity}|${processid}"/>  
  <targets>      
  <target name="f" xsi:type="File" fileName="logfile.txt" layout="${longdate}|${level }|${logger}|${message}|${logProcess}"/>      
</targets>

 

To tyle na temat konfiguracji. Jeśli macie potrzebę głębszego wglądu w konfigurację NLog’a to zachęcam do odwiedzenia strony na github’ie.

Użycie NLog’a

Samo użycie NLog’a w kodzie sprowadza się już tylko do 3 rzeczy:

  • Dodanie using’u NLog
  • Stworzenie instancji naszego loggera
  • Zapisanie informacji do loggera

Najlepiej zobrazuje to trywialny fragment poniższego kodu.

using NLog;

namespace NLogProject
{
    public class Program
    {
        static Logger logger = LogManager.GetCurrentClassLogger();
        static void Main(string[] args)
        {            
                logger.Trace("Trace message");
                logger.Debug("Debug message");
                logger.Info("Informational message");
                logger.Warn("Warning message");
                logger.Error("Error message");
                logger.Fatal("Fatal error message");         
        }
    }
}

 

To co mi najbardziej się podoba w NLog’u to prostota, mam na myśli tu inicjalizację loggera, gdzie wystarczy stworzyć obiekt metodą GetCurrentClassLogger(), jak również same  ustawienia w XML które są czytelne i intuicyjne.

Na dziś to wszystko, dziękuję za uwagę. W następnym poście skupię się na kolejnym loggerze jakim jest log4Net.