ReadMe.md
Vrh.Logger
Ez a leírás a komponens v2.1.0 kiadásáig bezáróan naprakész. Igényelt minimális framework verzió: 4.0 Teljes funkcionalitás és hatékonyság kihasználásához szükséges legalacsonyabb framework verzió: 4.5
A komponens arra szolgál, hogy egységes és egyszerű megvalósítása legyen a Logolásnak.
Pluginolható, a létező plugin megoldásaink a Vrh.Logger.PLUGINNAME komponens elnevezésekkel kerülnek implementációra.
Használata logoláshoz
A használathoz a Vrh.Logger névtér using-olandó.
VrhLogger static class:
Ez a static osztály adja a logoláshoz szükséges szűk eszközkészletet.
1. Log():
Ezt hívjuk meg, ha bármit logolni akarunk a környezetben.
Definíciója:
/// <summary>
/// Log bejegyzést ad fel (aszinkron!!!) a logoló modul számára
/// </summary>
/// <typeparam name="T">Típus</typeparam>
/// <param name="data">Log adat, meg kell feleljen a generikusan meghatározott típusnak, vagy ennek a típusa határozza meg a generikust, ha nem jelöljük explicit</param>
/// <param name="dataFields">A log bejegyzés adatai. Mezőnév/Mezőérték párok listájaként</param>
/// <param name="exception">Egy exception adható át a Lognak</param>
/// <param name="level">A logolás szintje. Az itt meghatározott szintűnek, vagy az alattinak kell a beállított logolási szintnek lennie, hogy a logolás meg is történjen a tényleges futási környezetben.</param>
/// <param name="source">Logolás forrása. Mindig egy típust (Type) kell átadni. Instance szintű tagoknál a this.GetType() kifejezést használjuk a paraméter értékadásához!
/// Static tagoknál a typeof(KonkrétClassNév) kifejezéssel adjuk át a típust.</param>
/// <param name="caller">4.5 Frameworktől automatikusan kap értéket a CallerMemberName attribútumon át, sose adjunk meg értéket ennek a paraméternek! 4.5 alatti verzióknál a hívás helyén kell gondoskodni a kitöltéséről, ha használni akarjuk a logban ezt az adatot.</param>
/// <param name="line">4.5 Frameworktől Automatikusan kap értéket a CallerLineNumber attribútumon át, sose adjunk meg kézzel értéket ennek a paraméternek! 4.5 alatti verzióknál a hívás helyén kell gondoskodni a kitöltéséről, ha használni akarjuk a logban ezt az adatot.</param>
static public void Log<T>(T data, IDictionary<string, string> dataFields, Exception exception, LogLevel level, Type source, [CallerMemberName]string caller = "", [CallerLineNumber]int line = 0)
Hogy a paraméterekkel a logolás során mi történik a használt logger pluginban, az erősen függ a használt plugin implementációjától. Az erre vonatkozó részleteket az adott Vrh.Logger Plugin dokumentációja írja le. Alább a A Logger komponens fejlesztő támogatására szolgáló DefaultLogger implementációban megvalósuló viselkedést adjuk meg. (Fontos megjegyzés a DefultLogger pluginnal kapcsolatban, hogy az tényleges logolásra nem alkalmas, csak a consolra és a csatlakoztatott debugger outputjára képes elküldeni a bedobott log bejegyzéseket.)
- A generikusba bármilyen típust bedobhatunk (a konkrét példányt a data paraméterben lehet átadni). A defult logger JSON-ba serializálja a típust, de null is átadható neki.
- A dataFileds-ben kulcs értékpárral megadott adatok adhatóak át. A Defultlogger ezeket kulcs: érték formában jeleníti meg, null érték is átadható neki.
- Az exception-ben egy exceptiont dobhatunk be, amit át kívánunk adni logolásra. A DefultLogger a LogHelper.GetExceptionInfo segíytségével jeleníti meg az exception adatait. (Lásd lejebb a Loghelper dokumentációjában.)
- A level megmondja, hogy a log bejegyzést melyik logszint beállítástól kezdődően kell ténylegesen a logba küldeni. Ha az aktuálisan beállított logolási szint legalább ekkora, akkor történik ténylegesen logolás (a logolásra használt ILogger interfészt implementáló plugin Log metódusának hívása.)
- A source segítségével adhatjuk meg a logolás forrásának a típusát. Mindig adjuk meg a hívás helyén ezt az értéket, hogy a Logolás releváns információkat tartalmazhasson! Static tagoknál a typeof(MyStaticClass) a helyes átadási mód, míg instance szintű tagoknál a this.GetType()-ot használjuk! A Logger a pluginnak ezt az információt nem adja tovább, hanem kibontja belőle a típust definiáló Assembly nevét, annak definiált verzióját, és a típus teljes nevét (névtérrel együtt). Ezen információk adódnak át a használt Logger pluginnak. A DefaultLogger plugin ezeket megjeleníti.
- A caller paramétert 4.5-ös Frameworktől kezdődően sose töltsük ki a hívásokban, az automatikusan értéket kap, és a metódus neve kerül bel, ahonnan a Log metódust meghívtuk. 4.5 alatti Fremwork esetén ezt az információt a hívási helyről át kell adni a hívásban, ha kívánunk a Logger pluginnak átadni ilyen információt.
- A line paramétert 4.5-ös Frameworktől kezdődően sose töltsük ki a hívásokban, az automatikusan értéket kap, és a forrássor száma kerül bel, ahonnan a Log metódust meghívtuk. 4.5 alatti Fremwork esetén ezt az információt a hívási helyről át kell adni a hívásban, ha kívánunk a Logger pluginnak átadni ilyen információt.
Példák a használatára:
catch (Exception ex)
{
VrhLogger.Log<string>("error", null, ex, LogLevel.Error, this.GetType()));
}
VrhLogger.Log<ApplicationTrust>(AppDomain.CurrentDomain.ApplicationTrust, null, null, LogLevel.Verbose, typeof(Program));
Dictionary<string, string> logData = new Dictionary<string, string>()
{
{ "Vrh.Loger Version ", GetModulVersion(typeof(VrhLogger)) },
{ "Used Logger", _logger != null ? _logger.GetType().FullName : String.Format("Not found!!! {0}", loggerTypeSelector) },
{ "Used Logger version", _logger != null ? GetModulVersion(_logger.GetType()) : "?" },
};
logData.Add("Used Plugin == Defult Logger", (_defaultLogger == null).ToString());
Logger.Log<String>("Vrh.Logger Started", logData, null, _logger != null ? LogLevel.Information : LogLevel.Warning, typeof(VrhLogger));
1.1 String logolása
Hogy ne kelljen a bonyolultabb formát beírni, egyszerű szöveges információ logolására használható az alábbi Log metódus is:
/// <summary>
/// Egyszerű string adat logolása.
/// </summary>
/// <param name="logMessage">Log üzenet</param>
/// <param name="level">Logszint</param>
/// <param name="source">Logolás forrása. Mindig egy típust (Type) kell átadni. Instance szintű tagoknál a this.GetType() kifejezést használjuk a paraméter értékadásához!
/// Static tagoknál a typeof(KonkrétClassNév) kifejezéssel adjuk át a típust.</param>
/// <param name="caller">4.5 Frameworktől automatikusan kap értéket a CallerMemberName attribútumon át, sose adjunk meg értéket ennek a paraméternek! 4.5 alatti verzióknál a hívás helyén kell gondoskodni a kitöltéséről, ha használni akarjuk a logban ezt az adatot.</param>
/// <param name="line">4.5 Frameworktől Automatikusan kap értéket a CallerLineNumber attribútumon át, sose adjunk meg kézzel értéket ennek a paraméternek! 4.5 alatti verzióknál a hívás helyén kell gondoskodni a kitöltéséről, ha használni akarjuk a logban ezt az adatot.</param>
static public void Log(string logMessage, LogLevel level, Type source, [CallerMemberName]string caller = "", [CallerLineNumber]int line = 0)
Példák a használatára:
VrhLogger.Log("Simple stringlog", LogLevel.Information, typeof(Program));
1.2. Exception logolása
Hogy ne kelljen a bonyolultabb formát beírni, kivétel logolására használható az alábbi Log metódus is:
/// <summary>
/// Exception egyszerű logolása
/// </summary>
/// <param name="exception">Logolandó kivétel</param>
/// <param name="source">Logolás forrása. Mindig egy típust (Type) kell átadni. Instance szintű tagoknál a this.GetType() kifejezést hazsnáljuk a paraméter értékadásához!
/// Static tagoknál a typeof(KonkrétClassNév) kifejezéssel adjuk át a típust.</param>
/// <param name="level">Logszint</param>
/// <param name="caller">4.5 Frameworktől automatikusan kap értéket a CallerMemberName attribútumon át, sose adjunk meg értéket ennek a paraméternek! 4.5 alatti verzióknál a hívás helyén kell gondoskodni a kitöltéséről, ha használni akarjuk a logban ezt az adatot.</param>
/// <param name="line">4.5 Frameworktől Automatikusan kap értéket a CallerLineNumber attribútumon át, sose adjunk meg kézzel értéket ennek a paraméternek! 4.5 alatti verzióknál a hívás helyén kell gondoskodni a kitöltéséről, ha használni akarjuk a logban ezt az adatot.</param>
static public void Log(Exception exception, Type source, LogLevel level = LogLevel.Error, [CallerMemberName]string caller = "", [CallerLineNumber]int line = 0)
Példák a használatára:
catch (Exception ex)
{
VrhLogger.Log(ex, typeof(Program));
}
2. LoadLoggerPlugin():
Arra használható, hogy anélkül cseréljünk az alkalmazás térben használt Logger plugint, hogy az alkalmazást újraindítanánk. A Vrh.Logger működéséhez nem kell explicit meghívni, mert a Logger static típus static konstruktora is meghívja.
Definiciója:
/// <summary>
/// Betölti a Logger által használt Plugint
/// A static construktor meghívja, azért public, hogy le lehessen programozni egy olyan alkalmazás logikát, ahol app restart nélkül logger plugint cserélünk.
/// </summary>
/// <param name="config">A használt konfigurációt jelöli ki</param>
static public void LoadLoggerPlugin(string config = null)
- Egyetlen paramétert lehet neki itt átadni, azt a konfigurációt definiáló stringet, amelyet a plugin a működéséhez használjon. Ha ilyet nem adunk meg, akkor alapértelmezés szerinti konfigurációt használ. (A Vrh.Logger konfigurációjáról, és annak működéséről a dokumentum lejjebb tartalmaz információkat.)
Használata:
VrhLogger.LoadLoggerPlugin(„LogConfig.xml/Vrh.Logger”);
3. SetNoOfLogEntry():
Segítségével egy olyan szolgáltatást implementálhatunk a felhasználás helyén, amivel egy adott sorszámra állítjuk a Logsorszámot. Az ilyesmi például hibakereséshez jöhet jól.
Definiciója:
/// <summary>
/// Segítségével implementálható a felhasználás helyén egy olyan szolgáltatás, amivel explicit módon beállítható, hogy mennyi legyen a Logsorszám
/// </summary>
/// <param name="newNoOfLogEntry">Erre az értrékre áll be asorszám, ha nincs mergadva, akkor 0</param>
public static void SetNoOfLogEntry(ulong newNoOfLogEntry=0)
- Egyetlen paramétert lehet neki átadni, hogy mennyi legyen a log sorszám aktuális értéke.
Használata:
VrhLogger.SetNoOfLogEntry(9999);
### LogHelper static class: Az olyan hasznos szolgáltatásokat biztosít egyszerű metódusokon keresztül, amelyek a logolással kapcsolatban hasznosak lehetnek. Jelenlegi szolgáltatásai:
GetAccurateDateTimeValue: Egy időbélyeg jellegű adatot nagy pontosságot megjelenítő stringként ad vissza, időzóna információval (yyyy.MM.dd HH:mm:ss.fffffff (zzz)).
/// <summary> /// Visszaadja a kapott időpont nagy pontosságú string reprezentánsát /// </summary> /// <param name="value">Időbélyeg jellegű adat</param> /// <returns>A kapott időbélyeg stringként</returns> public static string GetAccurateDateTimeValue(DateTime value)
- GetExceptionInfo: Az átadott exception adatait emberi olvasásra formázott stringként adja vissza. A megadott indentLevel szerinti tab-ot tesz minden egyes sor elejére.
csharp /// <summary> /// Kiszedi a kapott kivétel adatait egy szöveges leírássá /// </summary> /// <param name="ex">Exception, amelynek az adatait kigyűjtjük</param> /// <param name="indentLevel">Ennyi tab-bal indentálja az információ minden egyes sorát</param> /// <returns>A kapott Exception adatai stringként</returns> public static string GetExceptionInfo(Exception ex, int indentLevel = 0)
>A megjelenített adatok: >* Az Exception konkrét típusa >* A Message field tartalma >* A StackTrace tartalma >* Ha az InnerException nem null, akkor annak tartalma, ugyanígy megformázva, ezzel az adat tartalommal. Tetszőleges mélységig (amíg az InnerException-ök is tartalmaznak InnerExceptiont). Minden egyes InnerExceptiont egyel beljebb indentálva. >* Ha az adott Exceptoion típusa ReflectionTypeLoadException, akkor annak LoaderExceptions gyűjteményén is végigiterál, kigyűjtve azok InnerException-jeit is. - GetExceptionData: Segítségével Exception.Data (vagy bármilyen más Idictionary) adatokat rendezhetünk emberi olvasásra szánt szöveggé, úgy, hogy minden kulcs-érték párnak új sort nyit, és előre a kulcsot, majd mögé az adatot írja.
csharp /// <summary> /// Visszadja egy IDictionary-ben tárolt kulcs érték párokat (pl.: Exception.Data) /// </summary> /// <param name="data">Idictionary gyűjtemény</param> /// <param name="indentLevel">Ennyivel lesz beindentálva</param> /// <returns></returns> public static string GetExceptionData(IDictionary data, byte indentLevel)
- MakeIndent: Segítségével egy strin g minden egyes sorát (/n karakterek, mint sorvégek) a megadott szinttel beljebb indentálja, úgy, hogy a sorok elejére /t karaktereket helyez el az indentLevel-nek megfelelő számban.
csharp /// <summary> /// Tabokkal indentálja a kapott szöveg minden egyes sorát a paraméterben kapott számnak megfelelő számban /// </summary> /// <param name="input">Az indentálandó szöveg</param> /// <param name="indentLevel">Az indentálás szintje</param> /// <returns>Indentált szöveg</returns> public static string MakeIndent(string input, int indentLevel)
- GetDictionaryInfo: Segítségével kulcs értékpárok listájaként átadott adatokat rendezhetünk emberi olvasásra szánt szöveggé, úgy, hogy minden kulcs-érték párnak új sort nyit, és előre a kulcsot, majd mögé az adatot írja.
csharp /// <summary> /// Visszaadja a kapott Dictonary tartalmát emberi olvasásra formázott kulcs értékpárok listájaként /// </summary> /// <param name="dict">Az adatokat tartalmazó Dictionary</param> /// <param name="IndentLevel">Ennyivel lesz beindentálva</param> /// <returns>Az adatok formázott stringként</returns> public static string GetDictionaryInfo(IDictionary<string, string> dict, byte IndentLevel = 0)
- HexControlChars: A kapott szövegben esetleg megtalálható nem vizuális karaktereket hexa karakterkód értékre alakítja, és ezeket a megadott jelek közt, mint keretben illeszti be az a szövegben eredetileg is elfoglalt helyükre.
csharp /// <summary> /// A megadott sztring kontrolkaraktereit hex formátummá alakítja. /// A kontrol karakterek jelölésére használt keret karaktereket (frameStart, frameEnd), szintén hexa karakterkódra alakítja, ha előfordulnak az eredeti szövegben. /// </summary> /// <param name="str">A vezérlőkaraktereket is tartalmazó szöveg (string)</param> /// <param name="indentLevel">A formázott szöveg indentálásának a mértéke</param> /// <param name="frameStart">A vezérlőkarakterek hexakódjainak keretezésére ezt használja nyitó karakternek</param> /// <param name="frameEnd">A vezérlőkarakterek hexakódjainak keretezésére ezt használja záró karakternek</param> /// <returns>A formázott szöveg</returns> public static string HexControlChars(string str, byte indentLevel = 0, char frameStart = '{', char frameEnd = '}')
- ArrayToHumanReadableString: Konvertál egy tömböt emberi olvasásra alkalmas stringgé (a tömb értékei a megadott elválasztó karakterrel kerülnek egymástól elválasztásra). A tömbben tárolt típusnak a tárolt adat szöveges vizualizációjára nézve releváns ToString overloaddal kell rendelkeznie, a helyes működéshez.
csharp /// <summary> /// Konvertál egy tömböt emberi fogyasztásra alkalmas stringgé. /// A T-nek a tárolt értékre nézve releváns ToString overloaddal kell rendelkeznie, a helyes működéshez. /// </summary> /// <typeparam name="T">Típus kijelölő</typeparam> /// <param name="valueArray">Az értékeket tartalmazó tömb</param> /// <param name="indentLevel">A formázott szöveg indentálásának a mértéke</param> /// <param name="valueSeparator">Az értékeket egymástól elválasztó karakter a formázott szövegben.</param> /// <returns>a formázott szöveg</returns> public static string ArrayToHumanReadableString<T>(T[] valueArray, byte indentLevel = 0, char valueSeparator = ';')
- BytesToHumanReadableString: A megadott bájt tömböt ascii encodolással szöveggé alakítja, az esetleges vezérlőkaraktereket hexa értékre cserélve. Tipikus felhasználási területe alacsony szintű kommunikációs pufferek tartalmának logolása.
csharp /// <summary> /// A megadott bájt tömböt Ascii encodolással szöveggé alakítja, az esetleges vezérlőkaraktereket hexa értékre cserélve /// Tipikus felhasználási területe alacsony szintű kommunikációs pufferek tartalmának logolása /// </summary> /// <param name="bytearray">A konvertálandó bytok sorozata</param> /// <param name="indentLevel">A formázott szöveg indentálásának a mértéke</param> /// <returns>A formázott szöveg</returns> public static string BytesToHumanReadableString(byte[] bytearray, byte indentLevel = 0)
- BytesToHexBlock: byte sorozatot HexBlock formázású, emberi olvasásra szánt stringgé alakít.
csharp /// <summary> /// Egy byte sorozatot HexBlock formázású, emberi olvasásra szánt stringgé alakít /// </summary> /// <param name="data">a bemenő bájtsorozat</param> /// <param name="header">megadja, hogy az első sor egy header legyen, amely magyarázza az adattartalmat</param> /// <param name="footer">megadja, hogy legyen e egy hexablock véget értét hangsúlyozó lezárás a block végén</param> /// <param name="encoding">Beállítható vele, a használt karakter encoding, alapértelmezésben ASCII-t fog használni</param> /// <param name="indentLevel">Annyivel indentálja a hexa blockot</param> /// <returns>a formázott szöveg</returns> public static string BytesToHexBlock(byte[] data, bool header = true, bool footer = false, Encoding encoding = null, byte indentLevel = 0)
- ExtractPropertyValuesToDataFields: Visszadja a paraméterben kapott objektum propertijeinek értékeit név-érték párok litsájaként, amit közvetlenül odalehet adni a VrhLogger.Log hívásoknak, hogy kiírja az értékekeket. A property-ken a ToString-et hívja az értékhez, így az érték string reprezentánsa az adott típus ToString megvalósításának megfelelő lesz.
csharp /// <summary> /// Visszadja a paraméterben kapott objektum propertijeinek értékeit név-érték párok litsájaként (a property-n a ToString-et hívja az értékhez) /// </summary> /// <typeparam name="TClass">Ezzel az objektum típussal dolgozik</typeparam> /// <param name="instance">Ezzel a példánnyal</param> /// <returns>név-érték párok litáájaként a property-k neve és értéke</returns> public static Dictionary<string, string> ExtractPropertyValuesToDataFields<TClass>(TClass instance)
### Konfiguráció: A Vrh.Logger konfigurációjának megadásához kétféle módszert támogat: - Vagy egy az alkalmazástér konfigurációjából vesz fel appSettings kulcsokat.
- Vagy egy XML struktúrából olvassa ki a működési paramétereket.
- GetExceptionInfo: Az átadott exception adatait emberi olvasásra formázott stringként adja vissza. A megadott indentLevel szerinti tab-ot tesz minden egyes sor elejére.
Config XML használata:
Az alábbi XML-t érti meg, mint konfigurációt:
<Vrh.Logger>
<LogLevel>Warning</LogLevel>
<EnableConsoleLogging>True</EnableConsoleLogging>
<EnableDebuggerLogging>True</EnableDebuggerLogging>
<EnableFileLogging LogDirectory="Log" LogFile="Vrh.Logger.TxtLog.log">True</EnableFileLogging>
<UsedLogger>DefaultLogger</UsedLogger>
<UsedLoggerConfig>tetszőleges xml szabványos tartalom</UsedLoggerConfig>
<LoggerErrorLogDirectory>Log</LoggerErrorLogDirectory>
<LoggerErrorLogFile>Vrh.Logger.Errors.log</LoggerErrorLogFile>
</Vrh.Logger>
Az XML struktúrát az XmlProcessing modulon keresztül kapja meg. Alapértelmezett XmlParser connection string: file=~LogConfig.xml, azaz az alapértelmezett xml struktúrát az alkalmazás könyvtárában levő LogConfig.xml file tartalmazza.
Ha ettől el akarunk térni, akkor erre két lehetőségünk van:
- Meghívjuk a felhasználás helyén, még az első Log hívás előtt, a Logger class LoadLoggerPlugin() metódusát, paraméternek pedig átadjuk a használandó XmlParser connectionstring-et.
- Az alklamazás standard (App.config, Web.config) konfigurációjában definiálunk egy Vrh.Logger:Config kulcsú appSettings kulcsot és az értékének a használandó XmlParser connectionstring-et adjuk.
xml <appSettings> <add key="Vrh.Logger:Config" value="file=~LogConfig.xml;element=Vrh.Logger;"/> </appSettings>
A fenti példában az xml struktúrát az alkalmazás könyvtárában levő LogConfig.xml file gyökér eleme alatt található Vrh.Logger xml elem tartalmazza.
A következő legyen az alkalmazás könyvtárában levő AppCfg.xml file szerkezete, amelyben a struktúra belsejében található LoggerConfiguration elem tartalmazza a Vrh.Logger paramétereit. Ennek a következő XmlParser connectionstring felel meg: file=~Config.xml;element=ComponentConfiguration\LoggerConfiguration;
<ApplicationConfiguration>
<ComponentConfiguration>
<LoggerConfiguration>
<LogLevel>Warning</LogLevel>
<EnableConsoleLogging>True</EnableConsoleLogging>
<EnableDebuggerLogging>True</EnableDebuggerLogging>
<EnableFileLogging LogDirectory="Log" LogFile="Vrh.Logger.TxtLog.log">True</EnableFileLogging>
<UsedLogger>DefaultLogger</UsedLogger>
<LoggerErrorLogDirectory>Log</LoggerErrorLogDirectory>
<LoggerErrorLogFile>Vrh.Logger.Errors.log</LoggerErrorLogFile>
</LoggerConfiguration>
</ComponentConfiguration>
</ApplicationConfiguration>
Ha a file csak a Vrh.Logger paramétereit tartalmazza, akkor pedig: file=~Config.xml;
<LoggerConfiguration>
<LogLevel>Warning</LogLevel>
<EnableConsoleLogging>True</EnableConsoleLogging>
<EnableDebuggerLogging>True</EnableDebuggerLogging>
<EnableFileLogging LogDirectory="Log" LogFile="Vrh.Logger.TxtLog.log">True</EnableFileLogging>
<UsedLogger>DefaultLogger</UsedLogger>
<LoggerErrorLogDirectory>Log</LoggerErrorLogDirectory>
<LoggerErrorLogFile>Vrh.Logger.Errors.log</LoggerErrorLogFile>
</LoggerConfiguration>
- Megfigyelhetőek az alábbiak: >* Az element tagban a struktúrán belüli kijelölésben a Root elemet nem definiáljuk, csak az azon belüli útvonalat. >* Ha nem adunk meg element tagot, akkor a beállítások tagjeit a root elem alatt keresi. >* A root elem elnevezése tetszőleges lehet a configurációt tartalmazó XML-ben.
Szabvány app settings kulcsok használata:
Ekkor a szabványos config állomáynban szabványos appSettings kulcsok megadásával specifikáljuk a fenti négy beállítást:
<appSettings>
<add key="Vrh.Logger:Config" value="@D:\Temp\_t\LogConfig.xml/Vrh.Logger"/>
<add key="Vrh.Logger:LogLevel" value="debug"/>
<add key="Vrh.Logger:EnableConsoleLogging" value="True"/>
<add key="Vrh.Logger:EnableDebuggerLogging" value="True"/>
<add key="Vrh.Logger:UsedLogger" value="DefaultLogger"/>
<add key="Vrh.Logger:LoggerErrorLogDirectory" value="@D:\Temp\_t\Log"/>
<add key="Vrh.Logger:LoggerErrorLogFile" value="Vrh.Logger.Errors.log"/>
<add key="Vrh.Logger:EnableFileLogging" value="True"/>
<add key="Vrh.Logger:LogDirectory" value="Log"/>
<add key="Vrh.Logger:LogFile" value="Vrh.Logger.TxtLog.log"/>
</appSettings>
Mi mit jelent fentiekből?
A két beállítási mód közül az AppSettings kulcsok használata kap prioritást. Tehát amit megtalál az AppSettingsek közt, azt a beállítást innen kiolvassa, és nem számít, hogy az XML konfigurációban is van-e rá definíció, és mi az ottani értéke. Ez így arra is lehetőséget nyújt, hogy bizonyos beállításokat megadjunk az XML konfiguráció szintjén, míg másokat csak az .config-ban definiáljunk. A fenti beállítások konkrét értelmezése:
- LogLevel: A logolás szintje. A Vrh.Logger koncepciója szerint a logszint kezelése nem a Pluginok hatásköre. A beállításban megadott logszint, vagy az annál magasabbak kerülnek logolásra. Az alapértelmezett érték, ha a beállítás nincs jelen az Error. A használható értékek, magassági sorrendben, és a szándékolt jelentéssel: >* Debug: Azon log bejegyzések, amelyek olyan részletes, a program belső működésével, állapotával kapcsolatos információkat jelentenek, melyek fejlesztői szintű információnak tekinthetőek, értelmezésükhöz a program belső ismerete vagy/és fejlesztői szaktudás szükséges. >* Verbose: Részletes információk a program működéséről és az e-közbeni állapotokról, de tartalmuk a program belső ismerete nélkül is értelmezhető. >* Information: Olyan részletességű információk, amelyek részleteiben tájékoztatnak a program működéséről, de csak a funkciók szempontjából, ezért az üzleti/funkcionális ismeretekkel rendelkezőknek is értelmezhető információkat tartalmaznak csak. >* Warning: Olyan log üzenetek, amelyek a program működésével kapcsolatos figyelmeztetések, fontos információk. Általában valamilyen veszélyre, a működést legalábbis főfunkció szintjén nem befolyásoló hibára, inkonzisztenciára, konfigurációs ellentmondásra ilyesmire figyelmeztetnek. Jellemzően ez az a szint, amivel az üzemeltető rá jöhet, hogy mi az oka annak, hogy nem azt a működést tapasztalja, amit elvárna. >* Error: A program működése során fellépő nem kritikus mértékű hibák. >* Fatal: Olyan kritikus hiba, amely a program működésének leállását okozza, vagy egy főfunkció működésének ellátását akadályozza. >* None: Ez nem egy valódi log szint. Segítségével teljesen kikapcsolható a logolás.
- EnableConsoleLogging: Segítségével azt lehet előírni a Vrh.Logger-nek, hogy a benne található DefultLogger implementációt is üzemeltesse és az alkalmazás Console-ra is jelenítse meg a log bejegyzéseket. Egy logikai érték. Az igaz (True) értéket a true, yes, 1 értékek jelentik (a kis és nagybetűk közt nem tesz különbséget). Minden ezektől eltérő érték hamis értéket (False) jelent. Az alapértelmezett érték a False, ha a beállítás nincs jelen.
- EnableDebuggerLogging: Segítségével azt lehet előítrni a Vrh.Logger-nek, hogy a benne található DefultLogger implementációt is üzemeltesse és az alkalmazáshoz csatlakoztatott Debugger outputján is jelenítse meg a log bejegyzéseket. Egy logikai érték. Az igaz (True) értéket a true, yes, 1 értékek jelentik (a kis és nagybetük közt nem tesz különbséget). Minden ezektől eltérő érték hamis értéket (False) jelenet. Az alapértelmezett érték a False, ha a beállítás nincs jelen.
- EnablEFileLogging: Segítségével azt lehet előítrni a Vrh.Logger-nek, hogy a benne található DefultLogger implementációt is üzemeltesse és egy txt log fájlba írja ki a log bejegyzéseket. Egy logikai érték. Az igaz (True) értéket a true, yes, 1 értékek jelentik (a kis és nagybetük közt nem tesz különbséget). Minden ezektől eltérő érték hamis értéket (False) jelenet. Az alapértelmezett érték a False, ha a beállítás nincs jelen.
- LogDirectory: Ha a txt log file írás engedélyezve van, akkor az itt megadott könyvtárat használja a Log file célkönyvtáraként. Ha a könyvtár nem létezik, akkor létrehozza. Ha nincs megadva, akkor az alkalmazás futási könyvtárában keletkezik a fájl.
- LogFile: Segítségével lehet definiálni, hogy a fent leírt text log fájlt milyen néven hozza létre a Vrh.Logger. Ha nincs megadva, akkor a fájl Vrh.Logger.TxtLog.log néven jön létre.
- UsedLogger: Ha a Vrh.Logger működési környezetében több Logger plugin is található. Akkor ennek segítségével írhatjuk elő, hogy ezek közül melyiket használja a konfiguráció logolásra. Ha ez a beállítás nincs jelen, akkor mindig azt a plugint használja maleiket először megtalál a működési környezetben. Így egyetlen plugin jelenléte esetén a beállítást nem szükséges megadni. Az értéknek a használandó plugin pontos osztály nevét, vagy teljes (névterekkel együtt) osztálynevét kell megadni, ezt mindig az adott Plugin dokumentációjából derül ki.
UsedLoggerConfig: A UsedLogger elemben megadott plugin publikus "Init" metódusának XElement típusú paraméterében átadott xml elem, aminek beltartalmát teljes egészében a betöltött plugin dolgozza fel. Ezt a paramétert nem lehet az appSettings elemeken keresztül átadni!
LoggerErrorLogDirectory: Ha a Vrh.Logger működésében valami hiba lép fel, amely meggátolja a logolást, akkor egy saját text fájlba írja a hiba tényét, és az üzenet segítségével a hiba oka kideríthető. Magasabb logolási szinteken bejegyzéseket készít egyéb információkról is ide, a Logger indulásáról, leállásáról, a használt plugin betöltéséről. Ez a beállítás mondja, meg, hogy melyik könyvtárba kerüljön ez a txt Log. Ha nincs megadva, akkor az alkalmazás futási könyvtárában keletkezik a fájl.
LoggerErrorLogFile: Segítségével lehet definiálni, hogy a fent leírt saját hiba text fájlt milyen néven hozza létre a Vrh.Logger. Ha nincs megadva, akkor a fájl Vrh.Logger.Errors.log néven jön létre.
Információk Plugin fejlesztéshez
Minden Vrh.Logger egyetlen Log metódust valósít meg az Vrh.Logger.Ilogger intefész implementációjával. Az interfész használathoz a Vrh.Logger névtér using-olandó. Az ILogger interfész teljes definíciója:
/// <summary> /// Logger interfész, amelyet a Vrh.Logger használ /// A konkrét logger megoldásnak, vagy annak a wraperének ezt kell implementálnia. /// Az implementzáló típust meg kell jelőlni Export attribútummal! /// </summary> public interface ILogger : IDisposable { /// <summary> /// Azt fogja hívni a Vrh.Logger a logok átadásához. /// A koncepció szerint a LogLevel-t a Vrh.Logger kezeli a konkrét logger plugin felett! /// </summary> /// <typeparam name="T">Típuskijelölő</typeparam> /// <param name="noOfLogEntry">Log sorszáma a Logger létrejötte óta az adott alkalmazástérben</param> /// <param name="data">A kijelölt típusú adat</param> /// <param name="dataFields">Kulcs érték párok listájaként átadott adatok</param> /// <param name="exception">Egy exceptiont lehet ide bedobni, hogy a Logger az alapján logolja az exception fontos részleteit</param> /// <param name="level">A log szintje ennek a bejegyzésnek</param> /// <param name="sourceModul">Forrásmodul a Logger static Log hívásnak átadott típust deklaráló assembly neve kerül bele</param> /// <param name="sourceModulVersion">Forrásmodul verziója. Az assemblyben definiált AssemblyInformationalVersion, annak hiányában a definiált AssemblyVersion</param> /// <param name="sourceClass">Az osztály teljes neve, ahonnan a Log bejegyzést bedobták</param> /// <param name="sourceMethode">A metódus, neve, ahonnan a log bejegyzés érkezett (4.5 Framework-tól automatikus, az alatt a hívási helyen kell gondoskodni az átadásáról)</param> /// <param name="line">A forrás sor száma, ahonnan a Log hívás származik (4.5 Framework felett automatikus, az alatt a hívási helyen kell gondoskodni az átadásáról)</param> /// <param name="callTimeStamp">A hívás időbéjege</param> void Log<T>(UInt64 noOfLogEntry, T data, IDictionary<string, string> dataFields, Exception exception, LogLevel level, string sourceModul, string sourceModulVersion, string sourceClass, string sourceMethode, int line, DateTime callTimeStamp); /// <summary> /// Esemény,a melyen keresztül a plugin visszajelzést tud küldeni a működésében bekövetkező kritikus hibákról. /// Minden esetben Dispose hívást, és recreatet eredményez a Pluginra nézve a Vrh.Logger keret szintjén!!! /// </summary> event FatalErrorInPluginEventHandler FatalErrorInPlugin;
Az alábbi szabályok és elvek elvárások a Pluginnal szemben:
Implementáció:
- A Plugin megvalósítás implementálja az ILoggert, amely egyetlen Log metódus megvalósítását jelenti.
- De az ILogger egy IDispose leszármazott, ezért a plugin disposolható. És kötelezően Dispose mintát implementál.
- A Pluginnak kötelessége megvalósítani tényleges teljes erőforrás felszabadítást a Dispose ágon. A Vrh.Logger keret bizonyos esetekben a publikus Dispose metodusát hívja. Ekkor minden a logoláshoz használt erőforrást el kell engednie, és az objektumnak meg kell semmisülnie a Dispose visszatérésére. Tehát maradéktalanul működnie kell egy ilyen teszt kódnak rá (ahol iterationCount egy tetszőleges számú ismétlést definiál):
csharp for (int i = 0; i < iterationCount; i++) { ILogger logger = new MyLoggerPlugin(); logger.Log<string>((ulong)i, "test", null, null, LogLevel.Debug, "TEST", "V1.0.0", "TestClass", "TestMethode", 1, DateTime.Now); }
Hibavédettség:
- A plugin implementációnak le kell kezelnie, hogy a Log hívás során bármilyen paraméter értéket is kapjon bármelyik paraméterben, beleértve a null értékeket is a nullozható típusok esetén. Megengedett, hogy a Logolás ne sikerüljön (ne keletkezzen log bejegyzés), de hogy a Plugin működése leálljon az nem.
- Fenti paraméterek közül az alábbiak Null értéktől különböző kitöltöttségét mindig biztosítja a Vrh.Logger keret a nullozható típusok esetén is:
>* dataFields (üres Dictonary formájában)
>* sourceModul >* sourceModulVersion >* sourceClass
További részletek:
- Mivel a Vrh.Logger keret a Plugint a MEF keretrendszerrel tölti be, ezért meg kell jelölni Export attribútummal, a plugin Vrh.Logger.ILogger interfészt implementáló típusát, mégpedig az Ilogger contract típussal:
csharp [Export(typeof(ILogger))] public class DefaultLogger : ILogger { ...
- Az Export attribútum a System.ComponentModel.Composition névtérben található.
- Mivel a Vrh.Logger keret a plugint egy egyszerű MEF Import-ként injektálja, így minden működéséhez szükséges inicializációt el kell végezni az alapértelmezett (paraméter nélküli) konstruktorában. Sem konstruktor paraméter átadására, sem inicializáló függvény meghívására nincs lehetőség az automatikus Plugin töltés folyamatában.
- Mivel a Vrh Logger keret a Plugin Log implementációját aszinkron, egy külön háttérszálra dobva hívja (és nem vár a visszatérésére), ezért ez további követelményeket és sajátosságokat jelent: >* A Log metódusnak, illetve a Plugin ennek a hívásnak a kezelésével összefüggő működésének szálvédettnek kell lennie! Hiszen a Logger keret változó számú, párhuzamos konkurens hívást fog intézni a plugin felé a Log híváson keresztül. >* Ennek megfelelően a Log hívással semmilyen mód nem biztosított futási eredmények, vagy hibák visszadobására a logolásból. A Le nem kezelt kivételek annyit eredményeznek, hogy a végrehajtására indított tread nem normál státusszal terminál. Ennek ellenére kívánatos a Plugin Log metódusra hibavédelmet implementálni, és a logolásban fellépő kritikus hibák okáról a lentebb leírt módon értesíteni a Vrh.Logger komponenst.
- Amennyiben a Plugin működését akadályozandó hiba áll fenn, vagy ilyen keletkezik, a Plugin felelőssége, hogy erről értesítse a Vrh.Logger plugint. Ennek a módja, a Vrh.Logger.ILogger interfészben definiált FatalErrorInPlugin esemény implementálása:
csharp /// <summary> /// Esemény,a melyen keresztül a plugin visszajelzést tud küldeni a működésében bekövetkező kritikus hibákról. /// Minden esetben Dispose hívást, és recreatet eredményez a Pluginra nézve a Vrh.Logger keret szintjén!!! /// </summary> event FatalErrorInPluginEventHandler FatalErrorInPlugin;
- Az eseményt a FatalErrorInPluginEventHandler delegate-nek megfelelő signaturával iratkozik fel a Vrh.Logger keret, amely teljes definíciója az alábbi:
csharp /// <summary> /// Delegeta definició, a FatalErrorInPlugin event kezeléséhez /// </summary> /// <param name="pluginType">A plugin típusa, mindig adjuk át, hogy a Vrh.Logger keret releváns információt menthessen a hiba fellépésének helyéről</param> /// <param name="e">Az esemény argumentumai, egy szöveges üzenetet, és egy tetszőleges szabványos Exception átadását támogatja</param> public delegate void FatalErrorInPluginEventHandler(Type pluginType, PluginFatalErrorEventArgs e);
- A használt PluginFatalErrorEventArgs typus teljes definiciója az alábbi: ```csharp /// /// EventArgs a FatalErrorInPlugin eseméynhez /// public class PluginFatalErrorEventArgs : EventArgs { /// /// Szöveges információ a fellépett hibáról /// public string Message { get; set; }
/// <summary> /// Itt adhatjuk át a feléppő, vagy a kiváltott Exception-t /// </summary> public Exception Exception { get; set; } /// <summary> /// Jelzi a logger keretnek, hogy indítsa újra a plugint /// </summary> public bool RestartMe { get; set; }
} >```
Fentiek alapján a Pluginban az alábbi minta alapján kell helyesen implementálni az esemény visszajelzést:
/// <summary> /// FatalErrorInPlugin esemény (ILogger interfész member) /// </summary> public event FatalErrorInPluginEventHandler FatalErrorInPlugin; /// <summary> /// Metódus a FatalErrorInPlugin esemény elsütésére /// </summary> /// <param name="exception">Menteni kívánt kivétel</param> /// <param name="message">Szabadon beállítható üzenet a hibával kapcsolatban</param> private void OnFatalErrorInPlugin(Exception exception = null, string message = "") { var ea = new PluginFatalErrorEventArgs() { Message = message, Exception = exception, RestartMe = true, }; FatalErrorInPlugin?.Invoke(this.GetType(), ea); }
A hiba esemény kiváltásához egyszerűen az OnFatalErrorInPlugin metódust hívjuk a plugin implementációjában azon a helyen, ahonnan a hiba fellépéséről értesítést akarunk küldeni a keretnek.
Cészerű kihasználni, hogy konkrét Exception típust tudunk átadni. Az emellé mellékelt szöveges üzenet inkább egy opcionális lehetőség.
Az esemény kritikus hibák jelzésére szolgál. A Vrh.Logger keret a saját logjába mindig errorként menti az ezen át érkezett információkat. Ne használjuk a plugin működésével kapcsolatos ionformációk átadására, csak ténylegesen olyan súlyos hibák jelzésére, amely megakadályozzák az adott Logger plugin helyes működését. A RestartMe tag segítségével utasíthatjuk a keretet, hogy töltse be újra a Plugint. Célszerű olyan implementáció megvalósítása, amely biztosítja, hogy a Log hívások csak a hiba fellépésekor jelentsenek Error-t, és ne minden egyes Log híváskor, ezzel biztosítani lehet, hogy a Logolás infrastrukturális ellehetetlenítése ne legyen befolyással a program eredeti működésére.
- Mivel a Vrh.Logger keret a Plugint a MEF keretrendszerrel tölti be, ezért meg kell jelölni Export attribútummal, a plugin Vrh.Logger.ILogger interfészt implementáló típusát, mégpedig az Ilogger contract típussal:
Logszintek:
- A koncepció szerint a logolási szintek kezelése nem a Plugin, hanem a Vrh.Logger keret hatásköre. Ezért a Vrh.Logger keret a saját beállításának megfelelően, nem is intéz Log hívást a plugin felé, ha a logolandó információ nem éri le az aktuálisan beállított logolási szintet. Ennek ellenére a Pluginban szükség lehet a Logolási szint szerepeltetésére, például, hogy mentsük a log bejegyzéssel ezt információt. Lehetőleg ne végezzünk szemantikai tranzformációt a logszintekkel kapcsolatban, és törekedjünk rá, hogy konkrétan a Vrh.Logger által definiált LogLevel enumeratort használjuk. Az itt definiált None érték nem egy valós logszint, a logolás teljes kikapcsolására használja a keret. Ennek megfelelően, ilyen log szint megjelöléssel sose fog a Vrh.Logger keret hívást intézni a Plugin felé.
- A logszintek teljes definíciója az alábbi a LogLevel enumban:
csharp /// <summary> /// A logolás lehetséges szintjei /// </summary> public enum LogLevel { /// <summary> /// Debug részletességet célzó logbejegyzések a működés részletes követésére, hibakeresésre /// </summary> Debug = 1, /// <summary> /// Részletes, bőbeszédű információk szintje /// </summary> Verbose = 2, /// <summary> /// Információk a rendszer működéséről /// </summary> Information = 3, /// <summary> /// Figyelmeztetések szintje /// </summary> Warning = 4, /// <summary> /// A hibák szintje /// </summary> Error = 5, /// <summary> /// A kritikus hibák szintje /// </summary> Fatal = 6, /// <summary> /// None segítségével kikapcsolható a teljes logolás, sose használjuk logbejegyzés szintjeként, mert a VRH.Logger az ilyen bejegyzéseket mindig elutasítja! /// </summary> None = 7, }
Version History:
2.1.0 (2019.11.14)
Compatibility API changes:
- A betöltendő logger modulnak a LogConfig xml struktúra UsedLoggerConfig xml elemét átadja paraméterként; ennek tartalmát csak a betöltött logger elemzi és használja;
- A fenti xml struktúrát a logger betöltése után annak "Init" nevű publikus metódusán keresztül adja át, amelynek egyetlen paramétere egy XElement típus.;
- Az "Init" metódus nem kötelező, ha nem létezik, akkor nem okoz hibát.
- Az iLoggerLogger kliens kiegészítésre került az "Init" metódussal, amelyben megkapja a WcfCliens xml struktúrát, ami alapján az XmlProcessing.WCF osztály eszközeivel inicializálja az iLogger szervizhez csatlakozó kliensét, így megadható a connectionstringstore-on keresztül az iLogger szerviz címe.
- Ha nincs UsedLoggerConfig vagy benne WCFClient elem, vagy abban nincsenek megadva adatok, akkor a működés a korábbiak szerinti.
2.0.3 (2019.11.06)
Patches:
- Webes környezetben való működésképtelenség (path felvételek miatt) hibamentessé tétele
- None a defult loglevel, ha config error van
- Nem ír Fatal logot sem, ha none a loglevel ## v2.0.2 (2019.11.04) ### Patches:
- Load error információk megjelenésének javítása (Error log txt-ben) ## v2.0.1 (2019.10.25) ### Patches:
- A nuget csomag telepítéskor elkészíti azt a minimális konfigurációt, ami a telepítés helyén való instant működőképességhez szükséges. (Minden Nuget csomagnak ez lenne a célja a belézárt komponenst illetően: A telepítés teljes automatizálása, ami azonnali használatbavételt tesz lehetővé a telepítés helyén a komponens mélyebb ismerete (pl. konfigurálás módja) nélkül!)
- Főverzió emeléskor az Obsolete-nek jelölt elemeket ki kell dobni a kódból, ezt most pótoltam
- Instanmt kipróbálhatóság futatahatóság a repoba felrakott állapotban (konfig fájlok tartalma, stb.)
v1.5.7 (2019.06.04)
Patch:
- Windows Service-ben a Log bejegyzések előállítása túl időköltséges volt (már a task oldalon) a PID lekérdezések miatt. Állandó lekérdezés helyett áttéve egy Lazy fieldbe. ## v1.5.6 (2019.05.27) ### Patch:
- DefualtLogger Txt log implementáció: Logbejegyzések kiegészítése TimeStamp-pel
- DefualtLogger Txt log implementáció: A létrehozott logfájl nevébe bekerül prefxként a dátumbélyeg (YYYYMMDD_), így naponta új txt fájl keletkezik, és nem lesznek használhatatlanul nagyok a log fájlok. ## v1.5.5 (2019.05.23) ### Patch:
- compatible_Lear branch!!! Nugetek Learhez igazítása ## v1.5.4 (2019.05.21) ### Patch:
- To upgrade all Nugets to latest ## V1.5.2 (2018.12.11) ### Patch:
- Service név lekérdezésében hiba javítása ## V1.5.1 (2018.12.11) ### Patch:
- CallSignature osztály láthatósága .public ## V1.5.0 (2018.12.11) ### Compatibility API changes:
- CallSignature osztály bevezetése. ## V1.4.0 (2017.12.06) ### Compatibility API changes:
- Logger class funkciója áttéve a VrhLogger-class alá. Ott csak annak a public (API) funkcionalitása maradt, oly módon, hogy áthív a VrhLogger-be. (Hogy ne legyen incompatibility a change.)
- A Logger class Obsolote-tal jelölve --> Depcreated! ### Patches:
- LogHelper.ExtractPropertyValuesToDataFields null védelme ## V1.3.0 (2017.12.05) ### Compatibility API changes:
- LogHelper kiegészítése az ExtractPropertyValuesToDataFields metódussal ## V1.2.0 (2017.08.10) ### Compatibility API changes:
- DefaultLogger kiegészítése TXT log file írási kéápességgel
- Lehetséges app settings kulcsok kezelésének beépítése a default plugin txt file logolás képesség konfigurációjához (Kulcsok: "Vrh.Logger:EnableFileLogging", "Vrh.Logger:LogDirectory" "Vrh.Logger:LogFile")
- LogConfig.xml (config xml) feldolgozó kiegészítése a default plugin txt file logolás képesség kapcsán szükésge súj beállítások kezelésével (Tag: EnableFileLogging (Extended boolean: true|yes|1/false|no|0) lehetséges attríbutumai: LogDirectory="Log" LogFile="Vrh.Logger.TxtLog.log) ### Patches:
- LoggerErrorLogFile beállítást valóban figyelembe vegye a belső error log file elnevezésében
- A Default logger valóban figyelembe veszi a három leheteséges log target-re engedélyezés van-e konfigurálva, és csak arra ír, amelyikre igen
## V1.1.0 (2017.03.21)
### Compatibility API changes: - LogHelper.GetExceptionData bevezetése ### Patches:
- Newtonsoft.Json nuget dependency növelése 10.0.1-re
- LogHelper.GetExceptionInfo kiegészítése az Exception.Data tartalmának kibontásával ## V1.0.1 (2017.03.21) ### Patches:
- Új Vrh.LinqXmlProcesser.Base beépítés
- Nuspec fájl javításaok ## V1.0.0 (2017.02.21) Initial version