nxLogging Source Ansicht

Source Code Ansicht "nxLogging.pas"

Zur Ansicht: die Quellen von nxLogging, direkt im Browser:

{ **************************************************************************** }
{ ***** Dateiname               : nxLogging.pas ****************************** }
{ ***** Autor                   : Navimatix GmbH, Matthias Heunecke ********** }
{ ***** Erstellt am             : 08.07.2012 ********************************* }
{ ***** Letzte Änderung         : 05.05.2015 ********************************* }
{ ***** Zugehörigkeit           : nxLogging ********************************** }
{ ***** Beschreibung            : Enthält Klassen, Komponenten, Funktionen *** }
{ ******************************* für das Logging von Hinweisen, Fehlern,...** }
{ **************************************************************************** }
{ ***** Gruppe                  : Freeware *********************************** }
{ ******************************* Freie Nutzung privat und kommerziell ******* }
{ **************************************************************************** }
{ ***** Version                 : 1.3.1.1 ************************************ }
{ **************************************************************************** }
{ ***** Status                  : aktiv ************************************** }
{ **************************************************************************** }
{ ***** URL                     : https://www.navimatix.de ******************** }
{ ***** URL                     : https://www.navimatix.de/loesungen/log-server }
{ ***** Email                   : info@navimatix.de ************************** }
{ ***** Adresse                 : Navimatix GmbH, Moritz-von-Rohr-Str. 1a, *** }
{ *****                           07745 Jena, Germany ************************ }
{ **************************************************************************** }
{ ***** Tutorial:  https://www.navimatix.de/loesungen/log-server/ ************* }
{ *****            logging-fuer-delphi-nxlogging/tutorial/ ******************* }
{ **************************************************************************** }

unit nxLogging;

interface

uses Classes, Types, SyncObjs, SysUtils, IdTCPClient, IdThreadSafe;

const


  /// 
  /// Diese Konstante gibt die maximale Anzahl der zwischenzuspeichernden (auf Clientseite) Logeinträge an.
  /// Beim Überlauf dieses Puffers gehen die weiteren Logeinträge verloren.
  /// 
  IC_DEFAULTMAXTCPENTRIES           = 5000;   // Maximale Anzahl an Logeinträgen

                                              // für den TCP-Puffer
  /// 
  /// Gibt den Namensteil für eine Logdatei im Modus "NXLFS_SINGLEFILE" an.
  /// 
  SC_ALLFILETAIL                    = 'all';  // Additional Name-Part for single logfiles


  /// 
  /// Gibt den Namensteil der aktuellen Logdatei im Modus "NXLFS_RENAME" an.
  /// 
  SC_CURRENTFILETAIL                = 'current';  // Additional Name-Part for a current logfile


  /// 
  /// Gibt den Namensteil der bereits umbenannten Logdateien im Modus "NXLFS_RENAME" an.
  /// 
  SC_FILEFROM                       = 'from'; // Additional Name-Part for renamed logfiles


  // Konstanten für nxLogProtokoll-TCP...
  NXLOGPTCP_OK                            = 200;
  NXLOGPTCP_BADREQUEST                    = 400;
  NXLOGPTCP_UNAUTHORIZED                  = 401;
  NXLOGPTCP_PAYMENTREQUIRED               = 402;
  NXLOGPTCP_FORBIDDEN                     = 403;
  NXLOGPTCP_NOTFOUND                      = 404;
  NXLOGPTCP_METHODNOTALLOWED              = 405;
  NXLOGPTCP_INTERNALSERVERERROR           = 500;
  NXLOGPTCP_NOTIMPLEMENTED                = 501;
  NXLOGPTCP_SERVICETEMPORARYUNAVAILABLE   = 503;

  SC_WRONGSTRINGLENGTHSHOULDBEEVEN         =
      'the string to convert is of wrong length, should be even.';

type


  ///  
  ///  
  ///  Die Werte dieser Enumeration definieren für einen Logentry die Kategorie, zu dieser gehört.
  ///  Die Kategorie kann optional bei den Methoden zum Loggen angegeben werden, default ist NONE.
  ///  
  ///  
  TNxLoggerCategory = (   NXLCAT_NONE,
                          NXLCAT_OPTIONS,      // Fehler bei den Einstellungen
                          NXLCAT_RUNNING,      // Fehler während des "normalen" Laufens
                          NXLCAT_CALCULATING,  // Fehler während eine komplexen Rechenoperation.
                          NXLCAT_FASTRUNNING,  // Fehler während des Laufens, der jedoch extrem oft, oder ständig auftauchen kann.
                          NXLCAT_STARTUP,      // Fehler beim Erzeugen von Dingen
                          NXLCAT_SHUTDOWN,     // Fehler beim Aufräumen
                          NXLCAT_REFERENCE,    // ungültige, falsche Objektreferenz
                          NXLCAT_COM,          // Fehler im COM-Server
                          NXLCAT_DCOM,         // Fehler im DCOM-Server
                          NXLCAT_PLUGIN        // Fehler in einem Plugin
                      );


  ///  
  ///  
  ///  Die Werte dieser Enumeration definieren für einen Logentry das Level, zu dem dieser gehört.
  ///  Das Level ist nicht optional und muss immer mit angegeben werden.
  ///  Die Methoden trace(), info(), error(), etc... geben das entsprechende Level bereits vor.
  ///  
  ///  
  TNxLoggerLevel    = (   NXLL_TRACE,       // ganz zartes Fehlerchen.. :-)
                          NXLL_DEBUG,       // Fehlerchen zum/beim Debuggen
                          NXLL_INFO,        // Information über Unregelmäßigkeit
                          NXLL_WARN,        // Warnung, es ist etwas unschönes passiert, aber keine Panik..
                          NXLL_ERROR,       // kritischer Fehler, kann Absturz zur Folge haben
                          NXLL_FATAL        // fataler Fehler, eigentlich hat es keinen Zweck mehr, Programm töten !
                      );


  ///  
  ///  
  ///  Das ist eine Menge von Logleveln, diese wird z.B. in Filtern genutzt.
  ///  
  ///  
  TNxLoggerLevelSet = set of TNxLoggerLevel;


  ///  
  ///  
  ///  Diese Klasse kapselt eine Lognachricht. Jede Logausgabe erzeugt eine Instanz dieser Klasse und gibt diese intern an die
  ///  entsprechenden Appender weiter. Hier sind alle wichtigen Informationen zu einer Nachricht enthalten.
  ///  
  ///  
  /// 
  /// Eine solche Instanz kann selbst erzeugt werden, oder sie wird automatisch von den Methoden trace(), info(), etc... erzeugt.
  /// Erbt von TPersistent und kann mit der Methode Assign() Inhalte (nicht nur die Referenz) einer anderen Instanz zuweisen.
  /// 
  TNxLoggerMessage = class(TPersistent)
  private
    fApplicationId      : String;
    fInstanceId         : String;
    fLogUser            : String;
    fThreadId           : String;
    fLevel              : TNxLoggerLevel;
    fCategory           : String;
    fModule             : String;
    fMessage            : String;
    fTimestamp          : TDateTime;
    fException          : Exception;
    fExceptionClassName : String;
    fLanguage           : String;     // ISO 639-2
    fStackTrace         : String;
  protected
    procedure AssignTo(Dest: TPersistent); override;

    procedure setApplicationId(aValue : String);
    procedure setInstanceId(aValue : String);
    procedure setLogUser(aValue : String);
    procedure setThreadId(aValue : String);
    procedure setLevel(aValue  : TNxLoggerLevel);
    procedure setCategory(aValue : String);
    procedure setModule(aValue : String);
    procedure setMessage(aValue  : String);
    procedure setTimestamp(aValue  : TDateTime);
    procedure setException(aValue  : Exception);
    procedure setLanguage(aValue : String);
    procedure setExceptionClassName(aValue : String);
    procedure setStackTrace(aValue : String);
  public
    constructor Create(const aApplicationId, aInstanceId, aLogUser : String; aLevel : TNxLoggerLevel; const aMessage : String); overload; virtual;
    constructor Create(const aApplicationId, aInstanceId, aLogUser : String; aLevel : TNxLoggerLevel; const aModule, aMessage : String; const aCategory, aLanguage : String; const aException : Exception); overload; virtual;
    constructor Create(const aApplicationId, aInstanceId, aLogUser : String; aLevel : TNxLoggerLevel; const aModule, aMessage : String; const aCategory, aLanguage, aThreadId : String; const aException : Exception); overload; virtual;
    destructor Destroy; override;

    function getApplicationId       : String;
    function getInstanceId          : String;
    function getLogUser             : String;
    function getThreadId            : String;
    function getLevel               : TNxLoggerLevel;
    function getCategory            : String;
    function getModule              : String;
    function getMessage             : String;
    function getTimestamp           : TDateTime;
    function getException           : Exception;
    function getLanguage            : String;
    function getExceptionClassName  : String;
    function getStackTrace          : String;

    property ApplicationId          : String read getApplicationId write setApplicationId;
    property InstanceId             : String read getInstanceId write setInstanceId;
    property LogUser                : String read getLogUser write setLogUser;
    property ThreadId               : String read getThreadId write setThreadId;
    property LogLevel               : TNxLoggerLevel read fLevel write setLevel;
    property LogCategory            : String read fCategory write setCategory;
    property LogModule              : String read getModule write setModule;
    property LogMessage             : String read getMessage write setMessage;
    property LogTimestamp           : TDateTime read getTimestamp write setTimestamp;
    property LogException           : Exception read getException write setException;
    property LogExceptionClassName  : String read getExceptionClassName write setExceptionClassName;
    property Language               : String read getLanguage write setLanguage;
    property StackTrace             : String read getStackTrace write setStackTrace;
  end;


  TNxLoggerMessageEvent = procedure(aSender : TObject; aMessage : TNxLoggerMessage) of object;


  ///  
  ///  
  ///  Das ist die Grundklasse für alle Filter. Weitere Filter müssen von dieser Klasse erben.
  ///  
  ///  
  ///  
  ///  
  ///  Die Methode match() muss überschrieben werden. Hier wird per Rückgabe definiert, ob die übergebene Nachricht zum Filter passt oder nicht.
  ///  
  ///  
  ///  Mit loadFromStream() und saveToStream erhält ein Filter die Möglichkeit persistent zu bleiben, oder übermittelt zu werden.
  ///  
  ///  
  TNxLoggerMessageFilter = class(TPersistent)
  public
    procedure reset; virtual; abstract;
    function  match(aMessage : TNxLoggerMessage) : Boolean; virtual; abstract;
    procedure saveToStream(aStream : TStream); virtual; abstract;
    procedure loadFromStream(aStream : TStream); virtual; abstract;
  end;


  ///  
  ///  
  ///  Dieser Filter entscheidet nach einer Menge von LogLeveln. Ist das Loglevel der übergebenen Nachricht in der Menge enthalten, so
  ///  liefert match() true zurück.
  ///  
  ///  
  ///  
  ///  
  ///  Mit setLevelAndAbove() kann einfach eine untere Schwelle angegeben werden.
  ///  
  ///  
  ///  setLevel(), unsetLevel() und setLevelSet() dienen der Mengenverwaltung.
  ///  
  ///  
  TNxLoggerMessageFilterLevelSet = class(TNxLoggerMessageFilter)
  private
    fLevelSet       : TNxLoggerLevelSet;
  protected
    procedure AssignTo(Dest: TPersistent); override;
  public
    constructor Create;
    destructor Destroy; override;

    procedure saveToStream(aStream : TStream); override;
    procedure loadFromStream(aStream : TStream); override;

    procedure setLevel(aLevel : TNxLoggerLevel);
    procedure unsetLevel(aLevel : TNxLoggerLevel);

    procedure setLevelAndAbove(aLevel : TNxLoggerLevel);

    procedure setLevelSet(aLevelSet : TNxLoggerLevelSet);

    function  isLevelInSet(aLevel : TNxLoggerLevel) : Boolean;

    procedure reset; override;
    function  match(aMessage : TNxLoggerMessage) : Boolean; override;

    property LevelSet : TNxLoggerLevelSet read fLevelSet;
  end;


  ///  
  ///  
  ///  Dieser Filter ist eine Grundklasse für alle, welche nach einem gegebenen Text entscheidet
  ///  Die Grundklasse hält lediglich den zu testenden Text als Attribut und Property.
  ///  Die abgeleiteten Klassen müssen die Methode match() überschreiben.
  ///  
  ///  
  ///  
  ///  
  ///  setText(), getText() und reset() dienen der Texthandhabung.
  ///  
  ///  
  TNxLoggerMessageFilterByText = class(TNxLoggerMessageFilter)
  private
    fText     : String;
  protected
    procedure AssignTo(Dest: TPersistent); override;
  public
    constructor Create;
    destructor Destroy; override;

    procedure saveToStream(aStream : TStream); override;
    procedure loadFromStream(aStream : TStream); override;

    procedure setText(aValue : String);
    function  getText : String;

    procedure reset; override;

    property FilterText : String read fText write fText;
  end;


  ///  
  ///  
  ///  Dieser Filter ist ein Textfilter, er entscheidet nach TNxLoggerMessage.LogModule.
  ///  Dabei muss der Text darin gleich zum Filtertext sein.
  ///  
  ///  
  ///  
  ///  
  ///  Testeigenschaft der Nachricht ist: TNxLoggerMessage.LogModule
  ///  
  ///  
  ///  Der Textvergleich ist nicht case sensitive, Groß-Kleinschreibung ist egal.
  ///  
  ///  
  TNxLoggerMessageFilterModuleEquals = class(TNxLoggerMessageFilterByText)
  public
    function  match(aMessage : TNxLoggerMessage) : Boolean; override;
  end;


  ///  
  ///  
  ///  Dieser Filter ist ein Textfilter, er entscheidet nach TNxLoggerMessage.LogModule.
  ///  Dabei muss der Text darin mit dem Filtertext anfangen.
  ///  
  ///  
  ///  
  ///  
  ///  Testeigenschaft der Nachricht ist: TNxLoggerMessage.LogModule
  ///  
  ///  
  ///  Der Textvergleich ist nicht case sensitive, Groß-Kleinschreibung ist egal.
  ///  
  ///  
  TNxLoggerMessageFilterModuleStarting = class(TNxLoggerMessageFilterByText)
  public
    function  match(aMessage : TNxLoggerMessage) : Boolean; override;
  end;


  ///  
  ///  
  ///  Dieser Filter ist ein Textfilter, er entscheidet nach TNxLoggerMessage.LogModule.
  ///  Dabei muss der Filtertext irgendwo im LogModule enthalten sein.
  ///  
  ///  
  ///  
  ///  
  ///  Testeigenschaft der Nachricht ist: TNxLoggerMessage.LogModule
  ///  
  ///  
  ///  Der Textvergleich ist nicht case sensitive, Groß-Kleinschreibung ist egal.
  ///  
  ///  
  TNxLoggerMessageFilterModuleContains = class(TNxLoggerMessageFilterByText)
  public
    function  match(aMessage : TNxLoggerMessage) : Boolean; override;
  end;


  ///  
  ///  
  ///  Dieser Filter ist ein Textfilter, er entscheidet nach TNxLoggerMessage.LogMessage.
  ///  Dabei muss der Text darin gleich zum Filtertext sein.
  ///  
  ///  
  ///  
  ///  
  ///  Testeigenschaft der Nachricht ist: TNxLoggerMessage.LogMessage
  ///  
  ///  
  ///  Der Textvergleich ist nicht case sensitive, Groß-Kleinschreibung ist egal.
  ///  
  ///  
  TNxLoggerMessageFilterMessageEquals = class(TNxLoggerMessageFilterByText)
  public
    function  match(aMessage : TNxLoggerMessage) : Boolean; override;
  end;


  ///  
  ///  
  ///  Dieser Filter ist ein Textfilter, er entscheidet nach TNxLoggerMessage.LogMessage.
  ///  Dabei muss der Text darin mit dem Filtertext anfangen.
  ///  
  ///  
  ///  
  ///  
  ///  Testeigenschaft der Nachricht ist: TNxLoggerMessage.LogMessage
  ///  
  ///  
  ///  Der Textvergleich ist nicht case sensitive, Groß-Kleinschreibung ist egal.
  ///  
  ///  
  TNxLoggerMessageFilterMessageStarting = class(TNxLoggerMessageFilterByText)
  public
    function  match(aMessage : TNxLoggerMessage) : Boolean; override;
  end;


  ///  
  ///  
  ///  Dieser Filter ist ein Textfilter, er entscheidet nach TNxLoggerMessage.LogMessage.
  ///  Dabei muss der Filtertext irgendwo im LogModule enthalten sein.
  ///  
  ///  
  ///  
  ///  
  ///  Testeigenschaft der Nachricht ist: TNxLoggerMessage.LogMessage
  ///  
  ///  
  ///  Der Textvergleich ist nicht case sensitive, Groß-Kleinschreibung ist egal.
  ///  
  ///  
  TNxLoggerMessageFilterMessageContains = class(TNxLoggerMessageFilterByText)
  public
    function  match(aMessage : TNxLoggerMessage) : Boolean; override;
  end;

  ///  
  ///  
  ///  Dieser Filter ist ein Textfilter, er entscheidet nach TNxLoggerMessage.MachineIdent.
  ///  Dabei muss der Text darin gleich zum Filtertext sein.
  ///  
  ///  
  ///  
  ///  
  ///  Testeigenschaft der Nachricht ist: TNxLoggerMessage.MachineIdent
  ///  
  ///  
  ///  Der Textvergleich ist nicht case sensitive, Groß-Kleinschreibung ist egal.
  ///  
  ///  

  TNxLoggerMessageFilterMachineEquals = class;
  TNxLoggerMessageFilterMachineEqualsMIREvent = procedure(Sender : TNxLoggerMessageFilterMachineEquals; LogMessage : TNxLoggerMessage; var MachineIdent : String) of object;

  TNxLoggerMessageFilterMachineEquals = class(TNxLoggerMessageFilterByText)
  private
    fOnMachineIdentRequired       : TNxLoggerMessageFilterMachineEqualsMIREvent;
  protected
    procedure AssignTo(Dest: TPersistent); override;
  public
    function  match(aMessage : TNxLoggerMessage) : Boolean; override;

    property OnMachineIdentRequired       : TNxLoggerMessageFilterMachineEqualsMIREvent read fOnMachineIdentRequired write fOnMachineIdentRequired;
  end;

  ///  
  ///  
  ///  Dieser Filter ist ein Textfilter, er entscheidet nach TNxLoggerMessage.ApplicationId.
  ///  Dabei muss der Text darin gleich zum Filtertext sein.
  ///  
  ///  
  ///  
  ///  
  ///  Testeigenschaft der Nachricht ist: TNxLoggerMessage.ApplicationId
  ///  
  ///  
  ///  Der Textvergleich ist nicht case sensitive, Groß-Kleinschreibung ist egal.
  ///  
  ///  
  TNxLoggerMessageFilterApplicationEquals = class(TNxLoggerMessageFilterByText)
  public
    function  match(aMessage : TNxLoggerMessage) : Boolean; override;
  end;


  ///  
  ///  
  ///  Das ist die Grundklasse aller LogFormater. Diese haben die Aufgabe, eine
  ///  Lognachricht nach bestimmten Kriterien zu formatieren. Entweder eine Lognachricht in
  ///  einen String (formatMessage()), oder einen String zurück in eine Lognachricht (parseMessage()).
  ///  
  ///  
  ///  
  ///  
  ///  Die Klassenmethoden "convert***()" dienen der Umwandlung von Level, bzw. Kategorie in Strings und zurück.
  ///  
  ///  
  TNxLogFormater = class(TPersistent)
  public
    function  formatMessage(aMessage : TNxLoggerMessage) : String; virtual; abstract;
    function  parseMessage(aMessageText : String) : TNxLoggerMessage; virtual; abstract;
    function  formatMessageBase64(aMessage : TNxLoggerMessage) : String; virtual; abstract;
    function  parseMessageBase64(aMessageText : String) : TNxLoggerMessage; virtual; abstract;

    class function convertToLevel(aString : String) : TNxLoggerLevel;
    class function convertLevelToString(const aLevel : TNxLoggerLevel; const isException : Boolean = false) : String;
    class function convertToCategory(aString : String) : TNxLoggerCategory;
    class function convertCategoryToString(const aCategory : TNxLoggerCategory) : String;
  end;

  ///  
  ///  
  ///  Dieser LogFormater dient als Grundlage für unbekannte Log-Formate.
  ///  Die einzelnen Werte einer Lognachricht werden eventuell mit "|" getrennt,
  ///  werden aber nicht danach getrennt.
  ///  die Lognachrichten selbst müssen mit CR LF getrennt sein.
  ///  
  ///  
  ///  Format: Die ganze Zeile geht in "Message"
  ///  
  ///  
  ///  
  ///  
  ///  LogFormater haben die Aufgabe, eine Lognachricht nach bestimmten Kriterien zu formatieren.
  ///  Entweder eine Lognachricht in einen String (formatMessage()), oder einen String zurück in eine Lognachricht (parseMessage()).
  ///  
  ///  
  ///  Es existieren jeweils Varianten zur Base64-kodierung der Teilwerte, ACHTUNG!
  ///  Hier wird nicht der ganze String kodiert, nur die Teile applicationid, module und message.
  ///  
  ///  
  TNxLogFormaterPlain = class(TNxLogFormater)
  private
    fFormatSettings   : TFormatSettings;
  public
    constructor Create;
    destructor Destroy; override;

    function  formatMessage(aMessage : TNxLoggerMessage) : String; override;
    function  formatMessageBase64(aMessage : TNxLoggerMessage) : String; override;
    function  parseMessage(aMessageText : String) : TNxLoggerMessage; override;
    function  parseMessageBase64(aMessageText : String) : TNxLoggerMessage; override;
  end;


  ///  
  ///  
  ///  Dieser LogFormater dient als Standard, z.B. für Dateien.
  ///  Die einzelnen Werte einer Lognachricht werden jeweils mit "|" getrennt,
  ///  die Lognachrichten selbst mit CR LF.
  ///  
  ///  
  ///  Format: applicationid|timestamp|level.module|messagetext
  ///  
  ///  
  ///  
  ///  
  ///  LogFormater haben die Aufgabe, eine Lognachricht nach bestimmten Kriterien zu formatieren.
  ///  Entweder eine Lognachricht in einen String (formatMessage()), oder einen String zurück in eine Lognachricht (parseMessage()).
  ///  
  ///  
  ///  Es existieren jeweils Varianten zur Base64-kodierung der Teilwerte, ACHTUNG!
  ///  Hier wird nicht der ganze String kodiert, nur die Teile applicationid, module und message.
  ///  
  ///  
  TNxLogFormaterDefault = class(TNxLogFormater)
  private
    fFormatSettings   : TFormatSettings;
  public
    constructor Create;
    destructor Destroy; override;

    function  formatMessage(aMessage : TNxLoggerMessage) : String; override;
    function  formatMessageBase64(aMessage : TNxLoggerMessage) : String; override;
    function  parseMessage(aMessageText : String) : TNxLoggerMessage; override;
    function  parseMessageBase64(aMessageText : String) : TNxLoggerMessage; override;
  end;


  ///  
  ///  
  ///  Dieser LogFormater dient zur Übermittlung von Lognachrichten per Netzwerk.
  ///  Die einzelnen Werte einer Lognachricht werden jeweils mit "|" getrennt,
  ///  die Lognachrichten selbst mit CR LF.
  ///  
  ///  
  ///  Dieser Formater führt einen Wert "MachineIdent" neu ein, dieser soll einen
  ///  eindeutigen Wert beinhalten, welcher den Logclient identifiziert, z.B. der Rechnername.
  ///  Da hier die Lognachrichten per Netzwerk übertragen werden, ist es wichtig von wo diese kommen.
  ///  
  ///  
  ///  Format: applicationid|timestamp|level.module|messagetext
  ///  
  ///  
  ///  
  ///  
  ///  Es existieren jeweils Varianten zur Base64-kodierung der Teilwerte, ACHTUNG!
  ///  Hier wird nicht der ganze String kodiert, nur die Teile applicationid, module und message.
  ///  
  ///  
  ///  Bei den format***()-Methoden wird der Wert "MachineIdent" mit in den String kodiert, da dieser beim Parsen auch
  ///  verfügbar sein muss, gibt es die beiden zusätzlichen parse***()-Methoden mir den Rückgabeparametern "ResMachineIdent".
  ///  Beim Aufruf wird hier der im String kodierte MachineIdent-Wert übergeben.
  ///  
  ///  
  ///  LogFormater haben die Aufgabe, eine Lognachricht nach bestimmten Kriterien zu formatieren.
  ///  Entweder eine Lognachricht in einen String (formatMessage()), oder einen String zurück in eine Lognachricht (parseMessage()).
  ///  
  ///  
  TNxLogFormaterTCP = class(TNxLogFormater)
  private
    fFormatSettings   : TFormatSettings;
    fMachineIdent     : String;
  public
    constructor Create;
    destructor Destroy; override;

    function  formatMessage(aMessage : TNxLoggerMessage) : String; override;
    function  formatMessageBase64(aMessage : TNxLoggerMessage) : String; override;
    function  formatMessageExtra(aMessage : TNxLoggerMessage; aExtraDataId : Int64; aExtraData : TBytes) : String;
    function  formatMessageExtraBase64(aMessage : TNxLoggerMessage; aExtraDataId : Int64; aExtraData : TBytes) : String;
    function  parseMessage(aMessageText : String) : TNxLoggerMessage; override;
    function  parseMessageBase64(aMessageText : String) : TNxLoggerMessage; override;
    function  parseMessageMachine(aMessageText : String; var ResMachineIdent : String) : TNxLoggerMessage;
    function  parseMessageMachineBase64(aMessageText : String; var ResMachineIdent : String) : TNxLoggerMessage;
    function  parseMessageMachineExtra(aMessageText : String; var ResMachineIdent : String; var ResExtraID : Int64; var ResExtra : TBytes) : TNxLoggerMessage;
    function  parseMessageMachineExtraBase64(aMessageText : String; var ResMachineIdent : String; var ResExtraID : Int64; var ResExtra : TBytes) : TNxLoggerMessage;

    property MachineIdent     : String read fMachineIdent write fMachineIdent;
  end;

  TNxLogAppender = class(TComponent)
  private

  public
    procedure append(const aEvent : TNxLoggerMessage); virtual; abstract;
  end;

  TNxLogAppenderFileStrategy = (NXLFS_SINGLEFILE, NXLFS_NEWFILES, NXLFS_RENAME);

  TNxLogAppenderFile = class(TNxLogAppender)
  private
    fFormater       : TNxLogFormater;
    fFormatSettings : TFormatSettings;
    fDirectory      : String;
    fFilenameBase   : String;
    fRetryCount     : Integer;
    fStrategy       : TNxLogAppenderFileStrategy;
    fLastDate       : TDate;
  protected
    function  isNextDay : Boolean;

    procedure appendSingleFile(const aEvent : TNxLoggerMessage); virtual;
    procedure appendNewFiles(const aEvent : TNxLoggerMessage); virtual;
    procedure appendRename(const aEvent : TNxLoggerMessage); virtual;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

    procedure append(const aEvent : TNxLoggerMessage); override;

    property Strategy : TNxLogAppenderFileStrategy read fStrategy write fStrategy;
  end;

  TNxLogger = class;

  TNxLogAppenderChain = class(TNxLogAppender)
  private
    fChainedLogger    : TNxLogger;
  protected

  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

    procedure append(const aEvent : TNxLoggerMessage); override;

    property ChainedLogger : TNxLogger read fChainedLogger write fChainedLogger;
  end;

  INxLogAppenderTCPThreadAdapter = interface
  ['{0B98E7CF-DA90-4FC2-A530-F5516DF0CCA7}']
    function  consumeEntry : String;

  end;

  TNxLogAppenderTCPThread = class(TThread)
  private
    fTCP            : TIdTCPClient;
    fFormatSettings : TFormatSettings;
    fMachineIdent   : String;
    fHostOrIP       : String;
    fPort           : Integer;
    fRetryCount     : Integer;
    fUsername       : String;
    fPassword       : String;
    fAdapter        : INxLogAppenderTCPThreadAdapter;
  protected
    procedure Execute; override;
  public
    constructor Create(CreateSuspended: Boolean);
    destructor Destroy; override;

    property FormatSettings : TFormatSettings read fFormatSettings write fFormatSettings;
    property MachineIdent   : String read fMachineIdent write fMachineIdent;
    property HostOrIP       : String read fHostOrIP write fHostOrIP;
    property Port           : Integer read fPort write fPort;
    property RetryCount     : Integer read fRetryCount write fRetryCount;
    property Username       : String read fUsername write fUsername;
    property Password       : String read fPassword write fPassword;
    property Adapter        : INxLogAppenderTCPThreadAdapter read fAdapter write fAdapter;
  end;

  TNxLogAppenderOverflowEvent = procedure(aMaxCount : Integer) of object;

  TNxLogAppenderTCP = class(TNxLogAppender, INxLogAppenderTCPThreadAdapter)
  private
    fFormatSettings : TFormatSettings;
    fMachineIdent   : String;
    fHostOrIP       : String;
    fPort           : Integer;
    fRetryCount     : Integer;
    fUsername       : String;
    fPassword       : String;
    fMaxEntries     : TIdThreadSafeInteger;

    fLevelFilter    : TNxLoggerMessageFilterLevelSet;
    fUserFilter     : TNxLoggerMessageFilter;
    fFormater       : TNxLogFormaterTCP;
    fBuffer         : TIdThreadSafeStringList;
    fWorker         : TNxLogAppenderTCPThread;

    fOnOverflow     : TNxLogAppenderOverflowEvent;

    procedure setMachineIdent(aIdent : String);
    procedure setMaxEntries(aValue : Integer);
    function  getMaxEntries : Integer;
    function  getCurrentEntries : Integer;
  protected
    function  consumeEntry : String; virtual;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

    function  isStarted : Boolean;
    procedure start; virtual;
    procedure stop; virtual;

    procedure clearBuffer; virtual;
    procedure append(const aEvent : TNxLoggerMessage); override;

    property LevelFilter    : TNxLoggerMessageFilterLevelSet read fLevelFilter;
    property UserFilter     : TNxLoggerMessageFilter read fUserFilter write fUserFilter;
    property MaxEntries     : Integer read getMaxEntries write setMaxEntries;
    property CurrentEntries : Integer read getCurrentEntries;

    property OnOverflow     : TNxLogAppenderOverflowEvent read fOnOverflow write fOnOverflow;
  end;

  TNxLoggerCollectionItem = class(TCollectionItem)
  private
    fAppender   : TNxLogAppender;
  public
    constructor Create(Collection: TCollection); override;
    destructor Destroy; override;

    procedure append(const aEvent : TNxLoggerMessage);
  published
    property Appender   : TNxLogAppender read fAppender write fAppender;
  end;

  TNxLoggerCollection = class(TCollection)
  private

  protected

  public

  published

  end;

  TNxLoggerLogEvent = procedure(const aLevel : TNxLoggerLevel; const aModule, aMessage : String; aCategory : TNxLoggerCategory; aException : Exception; aLanguage : String) of object;

  INxLogger = interface
  ['{95CD2F15-2680-4740-9EC2-DEBDC79021DC}']
    procedure log(const aEvent : TNxLoggerMessage); overload;
    procedure log(const aLevel : TNxLoggerLevel; const aMessage : String); overload;
    procedure log(const aLevel : TNxLoggerLevel; const aModule, aMessage : String); overload;
    procedure log(const aLevel : TNxLoggerLevel; const aModule, aMessage : String; const aCategory : TNxLoggerCategory; const aException : exception); overload;
    procedure log(const aLevel : TNxLoggerLevel; const aModule, aMessage : String; const aCategoryAsString : String; const aException : exception); overload;
    procedure log(const aLevel : TNxLoggerLevel; const aModule, aMessage : String; const aCategoryAsString, aLanguage : String; const aException : exception); overload;
    procedure fatal(const aMessage : String); overload;
    procedure fatal(const aModule, aMessage : String); overload;
    procedure fatal(const aModule, aMessage : String; const aCategory : TNxLoggerCategory; const aException : exception = nil); overload;
    procedure error(const aMessage : String); overload;
    procedure error(const aModule, aMessage : String); overload;
    procedure error(const aModule, aMessage : String; const aCategory : TNxLoggerCategory; const aException : exception = nil); overload;
    procedure warn(const aMessage : String); overload;
    procedure warn(const aModule, aMessage : String); overload;
    procedure warn(const aModule, aMessage : String; const aCategory : TNxLoggerCategory; const aException : exception = nil); overload;
    procedure info(const aMessage : String); overload;
    procedure info(const aModule, aMessage : String); overload;
    procedure info(const aModule, aMessage : String; const aCategory : TNxLoggerCategory; const aException : exception = nil); overload;
    procedure debug(const aMessage : String); overload;
    procedure debug(const aModule, aMessage : String); overload;
    procedure debug(const aModule, aMessage : String; const aCategory : TNxLoggerCategory; const aException : exception = nil); overload;
    procedure trace(const aMessage : String); overload;
    procedure trace(const aModule, aMessage : String); overload;
    procedure trace(const aModule, aMessage : String; const aCategory : TNxLoggerCategory; const aException : exception = nil); overload;

    function  isTrace : Boolean;
    function  isDebug : Boolean;
    function  isInfo  : Boolean;
    function  isWarn  : Boolean;
    function  isError : Boolean;
    function  isFatal : Boolean;
  end;

  TNxLogger = class(TComponent, INxLogger)
  private
    fCSLog                : TCriticalSection;
    fLogFormatSettings    : TFormatSettings;
    fApplicationID        : String;
    fInstanceID           : String;
    fUserIdent            : String;
    fLanguage             : String;
    fCurrentLevel         : TNxLoggerLevel;
    fCSCurrentLevel       : TCriticalSection;
    fAppenders            : TNxLoggerCollection;
    fFilters              : TIdThreadSafeList;

    fOnAppend             : TNxLoggerMessageEvent;
    fOnLog                : TNxLoggerMessageEvent;

    function  getAppenderCount : Integer;
    function  getAppender(aIndex : Integer) : TNxLogAppender;

    function  getCurrentLevel : TNxLoggerLevel;
    procedure setCurrentLevel(aLevel : TNxLoggerLevel);
  protected
    function  matchFilters(const aEvent : TNxLoggerMessage) : Boolean; virtual;
    function  getMachineIdent : String; virtual;
  public
    constructor Create(AOwner : TComponent); override;
    destructor  Destroy; override;

    class function isImportantLogLevel(const aRelativeBaseLevel, aLevelToTest : TNxLoggerLevel) : Boolean;

    procedure initializeFileLogging(const aApplicationId, aLogDirectory, aBaseFilename : String; aRetryCount : Integer = 10; aStrategy : TNxLogAppenderFileStrategy = NXLFS_NEWFILES);
    procedure initializeServerTCPLogging(const aApplicationId, aHostOrIP : String; aPort : Integer; aMachineIdent : String = ''; aRetryCount : Integer = 10; aUsername : String = ''; aPassword : String = ''; aLevelFilter : TNxLoggerLevelSet = [NXLL_FATAL, NXLL_ERROR, NXLL_WARN]);

    procedure addAppender(aAppender : TNxLogAppender);
    function  deleteAppender(aIndex : Integer) : TNxLogAppender;
    procedure clearAppenders;

    procedure log(const aEvent : TNxLoggerMessage); overload;
    procedure log(const aLevel : TNxLoggerLevel; const aMessage : String); overload;
    procedure log(const aLevel : TNxLoggerLevel; const aModule, aMessage : String); overload;
    procedure log(const aLevel : TNxLoggerLevel; const aModule, aMessage : String; const aCategory : TNxLoggerCategory; const aException : exception); overload;
    procedure log(const aLevel : TNxLoggerLevel; const aModule, aMessage : String; const aCategoryAsString : String; const aException : exception); overload;
    procedure log(const aLevel : TNxLoggerLevel; const aModule, aMessage : String; const aCategoryAsString, aLanguage : String; const aException : exception); overload;
    procedure fatal(const aMessage : String); overload;
    procedure fatal(const aModule, aMessage : String); overload;
    procedure fatal(const aModule, aMessage : String; const aCategory : TNxLoggerCategory; const aException : exception = nil); overload;
    procedure error(const aMessage : String); overload;
    procedure error(const aModule, aMessage : String); overload;
    procedure error(const aModule, aMessage : String; const aCategory : TNxLoggerCategory; const aException : exception = nil); overload;
    procedure warn(const aMessage : String); overload;
    procedure warn(const aModule, aMessage : String); overload;
    procedure warn(const aModule, aMessage : String; const aCategory : TNxLoggerCategory; const aException : exception = nil); overload;
    procedure info(const aMessage : String); overload;
    procedure info(const aModule, aMessage : String); overload;
    procedure info(const aModule, aMessage : String; const aCategory : TNxLoggerCategory; const aException : exception = nil); overload;
    procedure debug(const aMessage : String); overload;
    procedure debug(const aModule, aMessage : String); overload;
    procedure debug(const aModule, aMessage : String; const aCategory : TNxLoggerCategory; const aException : exception = nil); overload;
    procedure trace(const aMessage : String); overload;
    procedure trace(const aModule, aMessage : String); overload;
    procedure trace(const aModule, aMessage : String; const aCategory : TNxLoggerCategory; const aException : exception = nil); overload;

    function  isTrace : Boolean;
    function  isDebug : Boolean;
    function  isInfo  : Boolean;
    function  isWarn  : Boolean;
    function  isError : Boolean;
    function  isFatal : Boolean;

    property AppenderCount  : Integer read getAppenderCount;
    property Appenders[index : Integer] : TNxLogAppender read getAppender;
    property CurrentLevel         : TNxLoggerLevel read getCurrentLevel write setCurrentLevel;

    property OnAppend             : TNxLoggerMessageEvent read fOnAppend write fOnAppend;
    property OnLog                : TNxLoggerMessageEvent read fOnLog write fOnLog;
  end;

  ENxLoggingConvertError  = class(Exception)

  end;

  // ***** Funktionen und Prozeduren...
  procedure Register;
  procedure initLoggerFormatSettings(var FormatSettings : TFormatSettings);

  function Logger : TNxLogger;

implementation

uses Windows, IdHashMessageDigest, IdGlobal, IdCoderMIME, TypInfo, Math,
      DateUtils;

var

  fDefaultLogger : TNxLogger;


{ **************************************************************************** }
{ ***** allgemeine Funktionen ************************************************ }
{ **************************************************************************** }

procedure Register;
begin
  RegisterComponents('Logging', [TNxLogger]);
end;

function NowUTC: TDateTime;
var
  SystemTime: TSystemTime;
begin
  GetSystemTime(SystemTime);
  Result := SystemTimeToDateTime (SystemTime);
end;

function decodeBase64(const EncodedText: string): String;
var cCoder: TIdDecoderMIME;
    zs8   : UTF8String;
begin
  cCoder := TIdDecoderMIME.Create(nil);
  try
    zs8  := UTF8String(cCoder.DecodeString(EncodedText));
    result  := String(zs8);
  finally
    FreeAndNil(cCoder);
  end;
end;

{$if CompilerVersion > 24}

function encodeBase64(const aText: string): String;
var cCoder: TIdEncoderMIME;
begin
  cCoder := TIdEncoderMIME.Create(nil);
  try
    result  := cCoder.EncodeString(aText, IndyTextEncoding_UTF8);
  finally
    FreeAndNil(cCoder);
  end;
end;

{$else}

function encodeBase64(const aText: string): String;
var cCoder: TIdEncoderMIME;
begin
  cCoder := TIdEncoderMIME.Create(nil);
  try
    result  := cCoder.EncodeString(aText, TEncoding.UTF8);
  finally
    FreeAndNil(cCoder);
  end;
end;

{$ifend}

function convertBytesToString(aBytes : TBytes) : String;
var zbr: RawByteString;
    pas, pad: PAnsiChar;
begin
  setlength(zbr, length(aBytes) * 2);
  pas := PAnsiChar(@aBytes[0]);
  pad := PAnsiChar(@zbr[1]);
  BinToHex(pas, pad, length(aBytes));
  result := trim(String(zbr));
end;

function convertStringToBytes(aString : String) : TBytes;
var size, len     : longint;
    pc            : PByte;
begin
  len := length(aString);
  if (len mod 2) = 0 then
  begin
    // ok...
    size  := len div 2;
    setlength(result, size);
    pc  := @result[0];
    HexToBin(pchar(aString), pc, size);
  end else
  begin
    raise ENxLoggingConvertError.Create(SC_WRONGSTRINGLENGTHSHOULDBEEVEN);
  end;
end;

function _TryStrToDateTime(const S: string; out Value: TDateTime; const aFormatSettings: TFormatSettings) : Boolean;
var ind   : Integer;
    msts  : String;
begin
  result  := TryStrToDateTime(s, Value, aFormatSettings);
  if result then
  begin
    ind := Pos(' ', s);
    if ind > 0 then
    begin
      // mit Zeit...
      msts  := Copy(s, ind+1, length(s)-ind);
      ind := Pos('.', msts);
      if ind > 0 then
      begin
        // Mit Millisekunden...
        msts  := Copy(msts, ind+1, length(msts)-ind);
        Value := DateUtils.IncMilliSecond(Value, StrToIntDef(msts, 0));
      end;
    end;
  end;
end;

function  Logger : TNxLogger;
begin
  result  := fDefaultLogger;
end;

procedure initLoggerFormatSettings(var FormatSettings : TFormatSettings);
begin
  with FormatSettings do
  begin
    CurrencyFormat:=3;
    NegCurrFormat:=8;
    ThousandSeparator:='.';
    DecimalSeparator:=',';
    CurrencyDecimals:=2;
    DateSeparator:='.';
    TimeSeparator:=':';
    ListSeparator:=';';
    CurrencyString:='€';
    ShortDateFormat:='yyyy.MM.dd';//'dd.MM.yyyy';
    LongDateFormat:='dddd, d. MMMM yyyy';
    TimeAMString:='';
    TimePMString:='';
    ShortTimeFormat:='hh:mm';
    LongTimeFormat:='hh:mm:ss.zzz';
    ShortMonthNames[1]:='Jan';
    ShortMonthNames[2]:='Feb';
    ShortMonthNames[3]:='Mrz';
    ShortMonthNames[4]:='Apr';
    ShortMonthNames[5]:='Mai';
    ShortMonthNames[6]:='Jun';
    ShortMonthNames[7]:='Jul';
    ShortMonthNames[8]:='Aug';
    ShortMonthNames[9]:='Sep';
    ShortMonthNames[10]:='Okt';
    ShortMonthNames[11]:='Nov';
    ShortMonthNames[12]:='Dez';
    LongMonthNames[1]:='Januar';
    LongMonthNames[2]:='Februar';
    LongMonthNames[3]:='März';
    LongMonthNames[4]:='April';
    LongMonthNames[5]:='Mai';
    LongMonthNames[6]:='Juni';
    LongMonthNames[7]:='Juli';
    LongMonthNames[8]:='August';
    LongMonthNames[9]:='September';
    LongMonthNames[10]:='Oktober';
    LongMonthNames[11]:='November';
    LongMonthNames[12]:='Dezember';
    ShortDayNames[1]:='So';
    ShortDayNames[2]:='Mo';
    ShortDayNames[3]:='Di';
    ShortDayNames[4]:='Mi';
    ShortDayNames[5]:='Do';
    ShortDayNames[6]:='Fr';
    ShortDayNames[7]:='Sa';
    LongDayNames[1]:='Sonntag';
    LongDayNames[2]:='Montag';
    LongDayNames[3]:='Dienstag';
    LongDayNames[4]:='Mittwoch';
    LongDayNames[5]:='Donnerstag';
    LongDayNames[6]:='Freitag';
    LongDayNames[7]:='Samstag';
    TwoDigitYearCenturyWindow:=0;
  end;
end;

procedure StrResetLength(var S: WideString);
begin
  SetLength(S, StrLen(PWideChar(S)));
end;

function GetLocalComputerName: string;
var
  Count: DWORD;
  zws : widestring;
begin
  Count := MAX_COMPUTERNAME_LENGTH + 1;
  SetLength(zws, Count);
  if GetComputerName(PWideChar(zws), Count) then
    StrResetLength(zws)
  else
    zws := '';
  result := zws;
end;

{ **************************************************************************** }
{ ***** TNxLogFormater ******************************************************* }
{ **************************************************************************** }
class function TNxLogFormater.convertToLevel(aString : String) : TNxLoggerLevel;
var csec  : String;
begin
  csec  := lowercase(aString);
  result  := NXLL_FATAL;
  if csec = 'trace' then
  begin
    result  := NXLL_TRACE;
    exit;
  end;
  if csec = 'finest' then
  begin
    result  := NXLL_TRACE;
    exit;
  end;
  if csec = 'debug' then
  begin
    result  := NXLL_DEBUG;
    exit;
  end;
  if csec = 'info' then
  begin
    result  := NXLL_INFO;
    exit;
  end;
  if csec = 'warn' then
  begin
    result  := NXLL_WARN;
    exit;
  end;
  if csec = 'warning' then
  begin
    result  := NXLL_WARN;
    exit;
  end;
  if csec = 'error' then
  begin
    result  := NXLL_ERROR;
    exit;
  end;
  if csec = 'exception' then
  begin
    result  := NXLL_ERROR;
    exit;
  end;
  if csec = 'fatal' then
  begin
    result  := NXLL_FATAL;
    exit;
  end;
  if csec = 'critical' then
  begin
    result  := NXLL_FATAL;
    exit;
  end;
end;

class function TNxLogFormater.convertLevelToString(const aLevel : TNxLoggerLevel; const isException : Boolean = false) : String;
begin
  case aLevel of
    NXLL_TRACE: result  := 'trace';
    NXLL_DEBUG: result  := 'debug';
    NXLL_INFO: result  := 'info';
    NXLL_WARN: result  := 'warn';
    NXLL_ERROR:
    begin
      if isException then
      begin
        result  := 'exception';
      end else
      begin
        result  := 'error';
      end;
    end;
    NXLL_FATAL: result  := 'fatal';
  end;
end;

class function TNxLogFormater.convertToCategory(aString : String) : TNxLoggerCategory;
var csec  : String;
begin
  csec  := lowercase(aString);
  result  := NXLCAT_NONE;
  if csec = 'options' then
  begin
    result  := NXLCAT_OPTIONS;
    exit;
  end;
  if csec = 'running' then
  begin
    result  := NXLCAT_RUNNING;
    exit;
  end;
  if csec = 'calculating' then
  begin
    result  := NXLCAT_CALCULATING;
    exit;
  end;
  if csec = 'fastrunning' then
  begin
    result  := NXLCAT_FASTRUNNING;
    exit;
  end;
  if csec = 'startup' then
  begin
    result  := NXLCAT_STARTUP;
    exit;
  end;
  if csec = 'shutdown' then
  begin
    result  := NXLCAT_SHUTDOWN;
    exit;
  end;
  if csec = 'reference' then
  begin
    result  := NXLCAT_REFERENCE;
    exit;
  end;
  if csec = 'com' then
  begin
    result  := NXLCAT_COM;
    exit;
  end;
  if csec = 'dcom' then
  begin
    result  := NXLCAT_DCOM;
    exit;
  end;
  if csec = 'plugin' then
  begin
    result  := NXLCAT_PLUGIN;
    exit;
  end;
end;

class function  TNxLogFormater.convertCategoryToString(const aCategory : TNxLoggerCategory) : String;
begin
  case aCategory of
    NXLCAT_NONE: result := 'none';
    NXLCAT_OPTIONS: result := 'options';
    NXLCAT_RUNNING: result := 'running';
    NXLCAT_CALCULATING: result := 'calculating';
    NXLCAT_FASTRUNNING: result := 'fastrunning';
    NXLCAT_STARTUP: result := 'startup';
    NXLCAT_SHUTDOWN: result := 'shutdown';
    NXLCAT_REFERENCE: result := 'reference';
    NXLCAT_COM: result := 'com';
    NXLCAT_DCOM: result := 'dcom';
    NXLCAT_PLUGIN: result := 'plugin';
  else
    result := ''
  end;
end;


{ **************************************************************************** }
{ ***** TNxLogFormaterPlain ************************************************** }
{ **************************************************************************** }
constructor TNxLogFormaterPlain.Create;
begin
  inherited Create;
  initLoggerFormatSettings(fFormatSettings);
  fFormatSettings.ShortDateFormat:='yyyy-MM-dd';
  fFormatSettings.LongTimeFormat:='hh:mm:ss,zzz';
end;

destructor TNxLogFormaterPlain.Destroy;
begin
  inherited Destroy;
end;

function  TNxLogFormaterPlain.formatMessage(aMessage : TNxLoggerMessage) : String;
begin
  if aMessage.LogModule <> '' then
  begin
    result  := Format('1:s|2:s|5:s|7:s|9:s',
        [ StringReplace(aMessage.ApplicationId, '|', '-', [rfReplaceAll]),                        // 0
          TNxLogFormater.convertLevelToString(aMessage.LogLevel, aMessage.LogException <> nil),   // 1
          StringReplace(aMessage.LogModule, '|', '-', [rfReplaceAll]),                            // 2
          DateTimeToStr(aMessage.LogTimestamp, fFormatSettings),                                  // 3
          StringReplace(aMessage.LogMessage, '|', '-', [rfReplaceAll]),                           // 4
          StringReplace(aMessage.InstanceId, '|', '-', [rfReplaceAll]),                           // 5
          StringReplace(aMessage.LogUser, '|', '-', [rfReplaceAll]),                              // 6
          StringReplace(aMessage.LogCategory, '|', '-', [rfReplaceAll]),                          // 7
          StringReplace(aMessage.Language, '|', '-', [rfReplaceAll]),                             // 8
          StringReplace(aMessage.ThreadId, '|', '-', [rfReplaceAll])                              // 9
          ]);
  end else
  begin
    result  := Format('1:s|3:s|5:s|7:s|%8:s',
        [ StringReplace(aMessage.ApplicationId, '|', '-', [rfReplaceAll]),                        // 0
          TNxLogFormater.convertLevelToString(aMessage.LogLevel, aMessage.LogException <> nil),   // 1
          DateTimeToStr(aMessage.LogTimestamp, fFormatSettings),                                  // 2
          StringReplace(aMessage.LogMessage, '|', '-', [rfReplaceAll]),                           // 3
          StringReplace(aMessage.InstanceId, '|', '-', [rfReplaceAll]),                           // 4
          StringReplace(aMessage.LogUser, '|', '-', [rfReplaceAll]),                              // 5
          StringReplace(aMessage.LogCategory, '|', '-', [rfReplaceAll]),                          // 6
          StringReplace(aMessage.Language, '|', '-', [rfReplaceAll]),                             // 7
          StringReplace(aMessage.ThreadId, '|', '-', [rfReplaceAll])                              // 8
          ]);
  end;
end;

function  TNxLogFormaterPlain.formatMessageBase64(aMessage : TNxLoggerMessage) : String;
begin
  if aMessage.LogModule <> '' then
  begin
    result  := Format('1:s|2:s|5:s|7:s|9:s',
        [ encodeBase64(aMessage.ApplicationId),                         // 0
          TNxLogFormater.convertLevelToString(aMessage.LogLevel, aMessage.LogException <> nil),   // 1
          encodeBase64(aMessage.LogModule),                             // 2
          DateTimeToStr(aMessage.LogTimestamp, fFormatSettings),        // 3
          encodeBase64(aMessage.LogMessage),                            // 4
          encodeBase64(aMessage.InstanceId),                            // 5
          encodeBase64(aMessage.LogUser),                               // 6
          encodeBase64(aMessage.LogCategory),                           // 7
          StringReplace(aMessage.Language, '|', '-', [rfReplaceAll]),   // 8
          StringReplace(aMessage.ThreadId, '|', '-', [rfReplaceAll])    // 9
          ]);
  end else
  begin
    result  := Format('1:s|3:s|5:s|7:s|%8:s',
        [ encodeBase64(aMessage.ApplicationId),                        // 0
          TNxLogFormater.convertLevelToString(aMessage.LogLevel, aMessage.LogException <> nil),   // 1
          DateTimeToStr(aMessage.LogTimestamp, fFormatSettings),                                  // 2
          encodeBase64(aMessage.LogMessage),                            // 3
          encodeBase64(aMessage.InstanceId),                            // 4
          encodeBase64(aMessage.LogUser),                               // 5
          encodeBase64(aMessage.LogCategory),                           // 6
          StringReplace(aMessage.Language, '|', '-', [rfReplaceAll]),   // 7
          StringReplace(aMessage.ThreadId, '|', '-', [rfReplaceAll])    // 8
          ]);
  end;
end;

function  TNxLogFormaterPlain.parseMessage(aMessageText : String) : TNxLoggerMessage;
var sl        : TStrings;
    cAppId    : String;
    cInstId   : String;
    cUser     : String;
    cThreadId : String;
    cLevel    : TNxLoggerLevel;
    cModule,
    cMessage  : String;
    cLanguage : String;
    cCategory : String;
    cTime     : TDateTime;
    i         : Integer;
    cLevelWasSet  : Boolean;
begin
  cAppId    := '';
  cInstId   := '';
  cUser     := '';
  cThreadId := '';
  cLevel    := nxLogging.NXLL_FATAL;
  cModule   := '';
  cMessage  := '';
  cLanguage := '';
  cCategory := '';
  cTime     := nan;
  result    := nil;
  if aMessageText <> '' then
  begin
    sl  := TStringList.Create;
    try
      sl.LineBreak  := ' ';
      sl.Text := aMessageText;
      if sl.Count >= 2 then
      begin
        // Timestamp...
        if not _TryStrToDateTime(sl[0], cTime, fFormatSettings) then
        begin
          // irgendwo eine Zeit suchen...
          for i := 1 to sl.Count - 1 do
          begin
            if _TryStrToDateTime(sl[i], cTime, fFormatSettings) then
            begin
              break;
            end;
          end;
          if IsNan(cTime) then
          begin
            //exit;
          end;
        end;
        // Level...
        cLevelWasSet  := false;
        for i := 1 to sl.Count - 1 do
        begin
          cLevel  := nxLogging.TNxLogFormater.convertToLevel(sl[i]);
          if cLevel <> NXLL_FATAL then
          begin
            cLevelWasSet  := true;
            break;
          end;
        end;
        if cLevelWasSet then
        begin
          cCategory := nxLogging.TNxLogFormater.convertLevelToString(cLevel);
        end;
        cMessage  := aMessageText;
        result  := TNxLoggerMessage.Create(cAppId, cInstId, cUser, cLevel, cModule, cMessage, cCategory, cLanguage, cThreadId, nil);
        result.fTimestamp := cTime;
      end else
      begin
        cMessage  := aMessageText;
        cLevelWasSet  := false;
        if (not cLevelWasSet) and (Pos('fatal', cMessage) > 0) then begin cLevel := NXLL_FATAL; cLevelWasSet := true; end;
        if (not cLevelWasSet) and (Pos('error', cMessage) > 0) then begin cLevel := NXLL_ERROR; cLevelWasSet := true; end;
        if (not cLevelWasSet) and (Pos('warn', cMessage) > 0) then begin cLevel := NXLL_WARN; cLevelWasSet := true; end;
        if (not cLevelWasSet) and (Pos('warning', cMessage) > 0) then begin cLevel := NXLL_WARN; cLevelWasSet := true; end;
        if (not cLevelWasSet) and (Pos('info', cMessage) > 0) then begin cLevel := NXLL_INFO; cLevelWasSet := true; end;
        if (not cLevelWasSet) and (Pos('debug', cMessage) > 0) then begin cLevel := NXLL_DEBUG; cLevelWasSet := true; end;
        if (not cLevelWasSet) and (Pos('fine', cMessage) > 0) then begin cLevel := NXLL_DEBUG; cLevelWasSet := true; end;
        if (not cLevelWasSet) and (Pos('trace', cMessage) > 0) then begin cLevel := NXLL_TRACE; cLevelWasSet := true; end;
        if (not cLevelWasSet) and (Pos('finer', cMessage) > 0) then begin cLevel := NXLL_TRACE; cLevelWasSet := true; end;
        if (not cLevelWasSet) and (Pos('finest', cMessage) > 0) then begin cLevel := NXLL_TRACE; cLevelWasSet := true; end;
        if cLevelWasSet then
        begin
          cCategory := nxLogging.TNxLogFormater.convertLevelToString(cLevel);
        end;
        result  := TNxLoggerMessage.Create(cAppId, cInstId, cUser, cLevel, cModule, cMessage, cCategory, cLanguage, cThreadId, nil);
        result.fTimestamp := nan;
      end;
    finally
      FreeAndNil(sl);
    end;
  end;
end;

function  TNxLogFormaterPlain.parseMessageBase64(aMessageText : String) : TNxLoggerMessage;
var sl        : TStrings;
    cAppId    : String;
    cInstId   : String;
    cUser     : String;
    cThreadId : String;
    cLevel    : TNxLoggerLevel;
    cModule,
    cMessage  : String;
    cLanguage : String;
    cCategory : String;
    cTime     : TDateTime;
    i         : Integer;
    cLevelWasSet  : Boolean;
begin
  cAppId    := '';
  cInstId   := '';
  cUser     := '';
  cThreadId := '';
  cLevel    := nxLogging.NXLL_FATAL;
  cModule   := '';
  cMessage  := '';
  cLanguage := '';
  cCategory := '';
  cTime     := nan;
  result    := nil;
  if aMessageText <> '' then
  begin
    sl  := TStringList.Create;
    try
      sl.LineBreak  := '|';
      try
        sl.Text := decodeBase64(aMessageText);
      except
        on e : exception do
        begin
          // vermutlich kein Base64...
          sl.Text := aMessageText;
        end;
      end;
      if sl.Count >= 1 then
      begin
        // Timestamp...
        if not _TryStrToDateTime(sl[0], cTime, fFormatSettings) then
        begin
          // irgendwo eine Zeit suchen...
          for i := 1 to sl.Count - 1 do
          begin
            if _TryStrToDateTime(sl[i], cTime, fFormatSettings) then
            begin
              break;
            end;
          end;
          if IsNan(cTime) then
          begin
            exit;
          end;
        end;
        // Level...
        cLevelWasSet  := false;
        for i := 1 to sl.Count - 1 do
        begin
          cLevel  := nxLogging.TNxLogFormater.convertToLevel(sl[i]);
          if cLevel <> NXLL_FATAL then
          begin
            cLevelWasSet  := true;
            break;
          end;
        end;
        if cLevelWasSet then
        begin
          cCategory := nxLogging.TNxLogFormater.convertLevelToString(cLevel);
        end;
        result  := TNxLoggerMessage.Create(cAppId, cInstId, cUser, cLevel, cModule, cMessage, cCategory, cLanguage, cThreadId, nil);
        result.fTimestamp := cTime;
      end else
      begin
        cMessage  := decodeBase64(aMessageText);
        cLevelWasSet  := false;
        if (not cLevelWasSet) and (Pos('fatal', cMessage) > 0) then begin cLevel := NXLL_FATAL; cLevelWasSet := true; end;
        if (not cLevelWasSet) and (Pos('error', cMessage) > 0) then begin cLevel := NXLL_ERROR; cLevelWasSet := true; end;
        if (not cLevelWasSet) and (Pos('warn', cMessage) > 0) then begin cLevel := NXLL_WARN; cLevelWasSet := true; end;
        if (not cLevelWasSet) and (Pos('warning', cMessage) > 0) then begin cLevel := NXLL_WARN; cLevelWasSet := true; end;
        if (not cLevelWasSet) and (Pos('info', cMessage) > 0) then begin cLevel := NXLL_INFO; cLevelWasSet := true; end;
        if (not cLevelWasSet) and (Pos('debug', cMessage) > 0) then begin cLevel := NXLL_DEBUG; cLevelWasSet := true; end;
        if (not cLevelWasSet) and (Pos('fine', cMessage) > 0) then begin cLevel := NXLL_DEBUG; cLevelWasSet := true; end;
        if (not cLevelWasSet) and (Pos('trace', cMessage) > 0) then begin cLevel := NXLL_TRACE; cLevelWasSet := true; end;
        if (not cLevelWasSet) and (Pos('finer', cMessage) > 0) then begin cLevel := NXLL_TRACE; cLevelWasSet := true; end;
        if (not cLevelWasSet) and (Pos('finest', cMessage) > 0) then begin cLevel := NXLL_TRACE; {cLevelWasSet := true;} end;
        result  := TNxLoggerMessage.Create(cAppId, cInstId, cUser, cLevel, cModule, cMessage, cCategory, cLanguage, cThreadId, nil);
        result.fTimestamp := nan;
      end;
    finally
      FreeAndNil(sl);
    end;
  end;
end;



{ **************************************************************************** }
{ ***** TNxLogFormaterDefault ************************************************ }
{ **************************************************************************** }
constructor TNxLogFormaterDefault.Create;
begin
  inherited Create;
  initLoggerFormatSettings(fFormatSettings);
end;

destructor TNxLogFormaterDefault.Destroy;
begin
  inherited Destroy;
end;

function  TNxLogFormaterDefault.formatMessage(aMessage : TNxLoggerMessage) : String;
begin
  if aMessage.LogModule <> '' then
  begin
    result  := Format('3:s|%1:s.4:s|6:s|8:s|%9:s',
        [ StringReplace(aMessage.ApplicationId, '|', '-', [rfReplaceAll]),                        // 0
          TNxLogFormater.convertLevelToString(aMessage.LogLevel, aMessage.LogException <> nil),   // 1
          StringReplace(aMessage.LogModule, '|', '-', [rfReplaceAll]),                            // 2
          DateTimeToStr(aMessage.LogTimestamp, fFormatSettings),                                  // 3
          StringReplace(aMessage.LogMessage, '|', '-', [rfReplaceAll]),                           // 4
          StringReplace(aMessage.InstanceId, '|', '-', [rfReplaceAll]),                           // 5
          StringReplace(aMessage.LogUser, '|', '-', [rfReplaceAll]),                              // 6
          StringReplace(aMessage.LogCategory, '|', '-', [rfReplaceAll]),                          // 7
          StringReplace(aMessage.Language, '|', '-', [rfReplaceAll]),                             // 8
          StringReplace(aMessage.ThreadId, '|', '-', [rfReplaceAll])                              // 9
          ]);
  end else
  begin
    result  := Format('2:s|3:s|5:s|7:s|%8:s',
        [ StringReplace(aMessage.ApplicationId, '|', '-', [rfReplaceAll]),                        // 0
          TNxLogFormater.convertLevelToString(aMessage.LogLevel, aMessage.LogException <> nil),   // 1
          DateTimeToStr(aMessage.LogTimestamp, fFormatSettings),                                  // 2
          StringReplace(aMessage.LogMessage, '|', '-', [rfReplaceAll]),                           // 3
          StringReplace(aMessage.InstanceId, '|', '-', [rfReplaceAll]),                           // 4
          StringReplace(aMessage.LogUser, '|', '-', [rfReplaceAll]),                              // 5
          StringReplace(aMessage.LogCategory, '|', '-', [rfReplaceAll]),                          // 6
          StringReplace(aMessage.Language, '|', '-', [rfReplaceAll]),                             // 7
          StringReplace(aMessage.ThreadId, '|', '-', [rfReplaceAll])                              // 8
          ]);
  end;
end;

function  TNxLogFormaterDefault.formatMessageBase64(aMessage : TNxLoggerMessage) : String;
begin
  if aMessage.LogModule <> '' then
  begin
    result  := Format('3:s|%1:s.4:s|6:s|8:s|%9:s',
        [ encodeBase64(aMessage.ApplicationId),                         // 0
          TNxLogFormater.convertLevelToString(aMessage.LogLevel, aMessage.LogException <> nil),   // 1
          encodeBase64(aMessage.LogModule),                             // 2
          DateTimeToStr(aMessage.LogTimestamp, fFormatSettings),        // 3
          encodeBase64(aMessage.LogMessage),                            // 4
          encodeBase64(aMessage.InstanceId),                            // 5
          encodeBase64(aMessage.LogUser),                               // 6
          encodeBase64(aMessage.LogCategory),                           // 7
          StringReplace(aMessage.Language, '|', '-', [rfReplaceAll]),   // 8
          StringReplace(aMessage.ThreadId, '|', '-', [rfReplaceAll])    // 9
          ]);
  end else
  begin
    result  := Format('2:s|3:s|5:s|7:s|%8:s',
        [ encodeBase64(aMessage.ApplicationId),                        // 0
          TNxLogFormater.convertLevelToString(aMessage.LogLevel, aMessage.LogException <> nil),   // 1
          DateTimeToStr(aMessage.LogTimestamp, fFormatSettings),                                  // 2
          encodeBase64(aMessage.LogMessage),                            // 3
          encodeBase64(aMessage.InstanceId),                            // 4
          encodeBase64(aMessage.LogUser),                               // 5
          encodeBase64(aMessage.LogCategory),                           // 6
          StringReplace(aMessage.Language, '|', '-', [rfReplaceAll]),   // 7
          StringReplace(aMessage.ThreadId, '|', '-', [rfReplaceAll])    // 8
          ]);
  end;
end;

function  TNxLogFormaterDefault.parseMessage(aMessageText : String) : TNxLoggerMessage;
var sl, sli   : TStrings;
    cAppId    : String;
    cInstId   : String;
    cUser     : String;
    cThreadId : String;
    cLevel    : TNxLoggerLevel;
    cModule,
    cMessage  : String;
    cLanguage : String;
    cCategory : String;
    cTime     : TDateTime;
    i         : Integer;
begin
  cAppId    := '';
  cInstId   := '';
  cUser     := '';
  cThreadId := '';
  //cLevel    := nxLogging.NXLL_FATAL;
  cModule   := '';
  cMessage  := '';
  cLanguage := '';
  cCategory := '';
  cTime     := nan;
  result    := nil;
  if aMessageText <> '' then
  begin
    sl  := TStringList.Create;
    sli := TStringList.Create;
    try
      sl.LineBreak  := '|';
      sli.LineBreak := '.';
      sl.Text := aMessageText;
      if sl.Count >= 4 then
      begin
        // ApplicationId...
        cAppId  := sl[0];
        // Timestamp...
        if not _TryStrToDateTime(sl[1], cTime, fFormatSettings) then
        begin
          exit;
        end;
        // Level und Modules...
        sli.Text  := sl[2];
        if sli.Count > 1 then
        begin
          cLevel  := TNxLogFormater.convertToLevel(sli[0]);
          cModule := sli[1];
          for i := 2 to sli.Count - 1 do
          begin
            cModule := cModule + '.' + sli[i];
          end;
        end else
        begin
          if sli.Count > 0 then
          begin
            cLevel  := TNxLogFormater.convertToLevel(sli[0]);
          end else
          begin
            exit;
          end;
        end;
        // Message...
        cMessage  := sl[3];
        if sl.Count > 4 then
        begin
          cInstId := sl[4];
          if sl.Count > 5 then
          begin
            cUser := sl[5];
            if sl.Count > 6 then
            begin
              cCategory := sl[6];
              if sl.Count > 7 then
              begin
                cLanguage := sl[7];
                if sl.Count > 8 then
                begin
                  cThreadId := sl[8];
                end;
              end;
            end;
          end;
        end;
        result  := TNxLoggerMessage.Create(cAppId, cInstId, cUser, cLevel, cModule, cMessage, cCategory, cLanguage, cThreadId, nil);
        result.fTimestamp := cTime;
      end;
    finally
      FreeAndNil(sl);
      FreeAndNil(sli);
    end;
  end;
end;

function  TNxLogFormaterDefault.parseMessageBase64(aMessageText : String) : TNxLoggerMessage;
var sl, sli   : TStrings;
    cAppId    : String;
    cInstId   : String;
    cUser     : String;
    cThreadId : String;
    cLevel    : TNxLoggerLevel;
    cModule,
    cMessage  : String;
    cCategory : String;
    cLanguage : String;
    cTime     : TDateTime;
begin
  cAppId    := '';
  cInstId   := '';
  cUser     := '';
  cThreadId := '';
  //cLevel    := nxLogging.NXLL_FATAL;
  cModule   := '';
  cMessage  := '';
  cLanguage := '';
  cCategory := '';
  cTime     := nan;
  result    := nil;
  if aMessageText <> '' then
  begin
    sl  := TStringList.Create;
    sli := TStringList.Create;
    try
      sl.LineBreak  := '|';
      sli.LineBreak := '.';
      sl.Text := aMessageText;
      if sl.Count >= 4 then
      begin
        // ApplicationId...
        cAppId  := decodeBase64(sl[0]);
        // Timestamp...
        if not _TryStrToDateTime(sl[1], cTime, fFormatSettings) then
        begin
          exit;
        end;
        // Level und Modules...
        sli.Text  := sl[2];
        if sli.Count > 1 then
        begin
          cLevel  := TNxLogFormater.convertToLevel(sli[0]);
          cModule := decodeBase64(sli[1]);
          {for i := 2 to sli.Count - 1 do
          begin
            cModule := cModule + '.' + sli[i];
          end;}
        end else
        begin
          if sli.Count > 0 then
          begin
            cLevel  := TNxLogFormater.convertToLevel(sli[0]);
          end else
          begin
            exit;
          end;
        end;
        // Message...
        cMessage  := decodeBase64(sl[3]);
        if sl.Count > 4 then
        begin
          cInstId := decodeBase64(sl[4]);
          if sl.Count > 5 then
          begin
            cUser := decodeBase64(sl[5]);
            if sl.Count > 6 then
            begin
              cCategory := decodeBase64(sl[6]);
              if sl.Count > 7 then
              begin
                cLanguage := sl[7];
                if sl.Count > 8 then
                begin
                  cThreadId := sl[8];
                end;
              end;
            end;
          end;
        end;
        result  := TNxLoggerMessage.Create(cAppId, cInstId, cUser, cLevel, cModule, cMessage, cCategory, cLanguage, cThreadId, nil);
        result.fTimestamp := cTime;
      end;
    finally
      FreeAndNil(sl);
      FreeAndNil(sli);
    end;
  end;
end;

{ **************************************************************************** }
{ ***** TNxLogFormaterTCP **************************************************** }
{ **************************************************************************** }
constructor TNxLogFormaterTCP.Create;
begin
  inherited Create;
  initLoggerFormatSettings(fFormatSettings);
  fMachineIdent := '';
end;

destructor TNxLogFormaterTCP.Destroy;
begin
  inherited Destroy;
end;

function  TNxLogFormaterTCP.formatMessage(aMessage : TNxLoggerMessage) : String;
var cClassName  : String;
    stt         : String;
begin
  if aMessage.fException <> nil then
  begin
    cClassName  := aMessage.fException.ClassName;
  end else
  begin
    cClassName  := aMessage.fExceptionClassName;
  end;
  stt := aMessage.StackTrace;
  stt := StringReplace(stt, '|', '-', [rfReplaceAll]);
  stt := StringReplace(stt, #10, '~#10~', [rfReplaceAll]);
  stt := StringReplace(stt, #13, '~#13~', [rfReplaceAll]);
  result  := Format('3:s|2:s|5:s|7:s|9:s|11:s|%12:s',
      [ StringReplace(aMessage.ApplicationId, '|', '-', [rfReplaceAll]),                          // 0
        TNxLogFormater.convertLevelToString(aMessage.LogLevel, aMessage.LogException <> nil),     // 1
        StringReplace(aMessage.LogModule, '|', '-', [rfReplaceAll]),                              // 2
        DateTimeToStr(aMessage.LogTimestamp, fFormatSettings),                                    // 3
        StringReplace(aMessage.LogMessage, '|', '-', [rfReplaceAll]),                             // 4
        aMessage.LogCategory,                                                                     // 5
        StringReplace(aMessage.Language, '|', '-', [rfReplaceAll]),                               // 6
        StringReplace(cClassName, '|', '-', [rfReplaceAll]),                                      // 7
        stt,                                                                                      // 8
        StringReplace(aMessage.InstanceId, '|', '-', [rfReplaceAll]),                             // 9
        StringReplace(aMessage.LogUser, '|', '-', [rfReplaceAll]),                                // 10
        StringReplace(aMessage.ThreadId, '|', '-', [rfReplaceAll]),                               // 11
        fMachineIdent                                                                             // 12
        ]);
end;

function  TNxLogFormaterTCP.formatMessageBase64(aMessage : TNxLoggerMessage) : String;
var cClassName  : String;
    stt         : String;
begin
  if aMessage.fException <> nil then
  begin
    cClassName  := aMessage.fException.ClassName;
  end else
  begin
    cClassName  := aMessage.fExceptionClassName;
  end;
  stt := aMessage.StackTrace;
  stt := StringReplace(stt, '|', '-', [rfReplaceAll]);
  stt := StringReplace(stt, #10, '~#10~', [rfReplaceAll]);
  stt := StringReplace(stt, #13, '~#13~', [rfReplaceAll]);
  result  := Format('3:s|2:s|5:s|7:s|9:s|11:s|%12:s',
      [ encodeBase64(aMessage.ApplicationId),                           // 0
        TNxLogFormater.convertLevelToString(aMessage.LogLevel, aMessage.LogException <> nil),  // 1
        encodeBase64(aMessage.LogModule),                               // 2
        DateTimeToStr(aMessage.LogTimestamp, fFormatSettings),          // 3
        encodeBase64(aMessage.LogMessage),                              // 4
        encodeBase64(aMessage.LogCategory),                             // 5
        aMessage.Language,                                              // 6
        encodeBase64(cClassName),                                       // 7
        encodeBase64(stt),                                              // 8
        encodeBase64(aMessage.InstanceId),                              // 9
        encodeBase64(aMessage.LogUser),                                 // 10
        encodeBase64(aMessage.ThreadId),                                // 11
        encodeBase64(fMachineIdent)                                     // 12
        ]);
end;

function  TNxLogFormaterTCP.formatMessageExtra(aMessage : TNxLoggerMessage; aExtraDataId : Int64; aExtraData : TBytes) : String;
var zs, cData  : String;
begin
  zs  := formatMessage(aMessage);
  if length(aExtraData) > 0 then
  begin
    cData := convertBytesToString(aExtraData);
  end else
  begin
    cData := '';
  end;
  result  := Format('1:s|%2:s', [IntToStr(aExtraDataId), cData]);
end;

function  TNxLogFormaterTCP.formatMessageExtraBase64(aMessage : TNxLoggerMessage; aExtraDataId : Int64; aExtraData : TBytes) : String;
var zs, cData  : String;
begin
  zs  := formatMessage(aMessage);
  if length(aExtraData) > 0 then
  begin
    cData := convertBytesToString(aExtraData);
  end else
  begin
    cData := '';
  end;
  result  := Format('1:s|%2:s', [IntToStr(aExtraDataId), cData]);
end;

function  TNxLogFormaterTCP.parseMessage(aMessageText : String) : TNxLoggerMessage;
var sl          : TStrings;
    cAppId      : String;
    cLevel      : TNxLoggerLevel;
    cModule,
    cMessage,
    cLng,
    cClassName,
    cStackTrace : String;
    cThreadId   : String;
    cInstId     : String;
    cUser       : String;
    cCategory   : String;
    cTime       : TDateTime;
begin
  result  := nil;
  if aMessageText <> '' then
  begin
    sl  := TStringList.Create;
    try
      sl.LineBreak  := '|';
      sl.Text := aMessageText;
      if sl.Count >= 9 then
      begin
        // ApplicationId...
        cAppId  := sl[0];
        // Timestamp...
        if not _TryStrToDateTime(sl[1], cTime, fFormatSettings) then
        begin
          exit;
        end;
        // Level...
        cLevel  := TNxLogFormater.convertToLevel(sl[2]);
        // Modules...
        cModule := sl[3];
        // Message...
        cMessage  := sl[4];
        // Category...
        cCategory := sl[5];
        // Language...
        cLng  := sl[6];
        // Classname...
        cClassName  := sl[7];
        // StackTrace...
        cStackTrace := sl[8];
        cStackTrace := StringReplace(cStackTrace, '~#10~', #10, [rfReplaceAll]);
        cStackTrace := StringReplace(cStackTrace, '~#13~', #13, [rfReplaceAll]);
        // InstanceId...
        if sl.Count > 9 then
        begin
          cInstId := sl[9];
        end;
        // LogUser...
        if sl.Count > 10 then
        begin
          cUser   := sl[10];
        end;
        // ThreadId...
        if sl.Count > 11 then
        begin
          cThreadId := sl[11];
        end;
        result  := TNxLoggerMessage.Create(cAppId, cInstId, cUser, cLevel, cModule, cMessage, cCategory, cLng, cThreadId, nil);
        result.fTimestamp := cTime;
        result.fLanguage  := cLng;
        result.fExceptionClassName  := cClassName;
        result.fStackTrace  := cStackTrace;
      end;
    finally
      FreeAndNil(sl);
    end;
  end;
end;

function  TNxLogFormaterTCP.parseMessageBase64(aMessageText : String) : TNxLoggerMessage;
var sl          : TStrings;
    cAppId      : String;
    cInstId     : String;
    cUser       : String;
    cLevel      : TNxLoggerLevel;
    cModule,
    cMessage,
    cLng,
    cClassName,
    cStackTrace : String;
    cThreadId   : String;
    cCategory   : String;
    cTime       : TDateTime;
begin
  result  := nil;
  if aMessageText <> '' then
  begin
    sl  := TStringList.Create;
    try
      sl.LineBreak  := '|';
      sl.Text := aMessageText;
      if sl.Count >= 9 then
      begin
        // ApplicationId...
        cAppId  := decodeBase64(sl[0]);
        // Timestamp...
        if not _TryStrToDateTime(sl[1], cTime, fFormatSettings) then
        begin
          exit;
        end;
        // Level...
        cLevel  := TNxLogFormater.convertToLevel(sl[2]);
        // Modules...
        cModule := decodeBase64(sl[3]);
        // Message...
        cMessage  := decodeBase64(sl[4]);
        // Category...
        cCategory := decodeBase64(sl[5]);
        // Language...
        cLng  := sl[6];
        // Classname...
        cClassName  := decodeBase64(sl[7]);
        // StackTrace...
        cStackTrace := decodeBase64(sl[8]);
        // InstanceId...
        if sl.Count > 9 then
        begin
          cInstId := decodeBase64(sl[9]);
        end;
        // LogUser...
        if sl.Count > 10 then
        begin
          cUser   := decodeBase64(sl[10]);
        end;
        // ThreadId...
        if sl.Count > 11 then
        begin
          cThreadId := decodeBase64(sl[11]);
        end;
        result  := TNxLoggerMessage.Create(cAppId, cInstId, cUser, cLevel, cModule, cMessage, cCategory, cLng, cThreadId, nil);
        result.fTimestamp := cTime;
        result.fLanguage  := cLng;
        result.fExceptionClassName  := cClassName;
        result.fStackTrace  := cStackTrace;
      end;
    finally
      FreeAndNil(sl);
    end;
  end;
end;

function  TNxLogFormaterTCP.parseMessageMachine(aMessageText : String; var ResMachineIdent : String) : TNxLoggerMessage;
var sl  : TStrings;
begin
  ResMachineIdent := '';
  result  := parseMessage(aMessageText);
  if result <> nil then
  begin
    if aMessageText <> '' then
    begin
      sl  := TStringList.Create;
      try
        sl.LineBreak  := '|';
        sl.Text := aMessageText;
        if sl.Count > 12 then
        begin
          ResMachineIdent := sl[12];
        end;
      finally
        FreeAndNil(sl);
      end;
    end;
  end;
end;

function  TNxLogFormaterTCP.parseMessageMachineBase64(aMessageText : String; var ResMachineIdent : String) : TNxLoggerMessage;
var sl  : TStrings;
begin
  ResMachineIdent := '';
  result  := parseMessageBase64(aMessageText);
  if result <> nil then
  begin
    if aMessageText <> '' then
    begin
      sl  := TStringList.Create;
      try
        sl.LineBreak  := '|';
        sl.Text := aMessageText;
        if sl.Count > 12 then
        begin
          ResMachineIdent := decodeBase64(sl[12]);
        end;
      finally
        FreeAndNil(sl);
      end;
    end;
  end;
end;

function  TNxLogFormaterTCP.parseMessageMachineExtra(aMessageText : String; var ResMachineIdent : String; var ResExtraID : Int64; var ResExtra : TBytes) : TNxLoggerMessage;
var sl  : TStrings;
begin
  result  := parseMessageMachine(aMessageText, ResMachineIdent);
  if result <> nil then
  begin
    if aMessageText <> '' then
    begin
      sl  := TStringList.Create;
      try
        sl.LineBreak  := '|';
        sl.Text := aMessageText;
        if sl.Count > 13 then
        begin
          ResExtraID := StrToIntDef(sl[13], 0);
        end;
        if sl.Count > 14 then
        begin
          ResExtra  := convertStringToBytes(sl[14]);
        end;
      finally
        FreeAndNil(sl);
      end;
    end;
  end;
end;

function  TNxLogFormaterTCP.parseMessageMachineExtraBase64(aMessageText : String; var ResMachineIdent : String; var ResExtraID : Int64; var ResExtra : TBytes) : TNxLoggerMessage;
var sl  : TStrings;
begin
  result  := parseMessageMachineBase64(aMessageText, ResMachineIdent);
  if result <> nil then
  begin
    if aMessageText <> '' then
    begin
      sl  := TStringList.Create;
      try
        sl.LineBreak  := '|';
        sl.Text := aMessageText;
        if sl.Count > 13 then
        begin
          ResExtraID := StrToIntDef(sl[13], 0);
        end;
        if sl.Count > 14 then
        begin
          ResExtra  := convertStringToBytes(sl[14]);
        end;
      finally
        FreeAndNil(sl);
      end;
    end;
  end;
end;


{ **************************************************************************** }
{ ***** TNxLogAppenderFile *************************************************** }
{ **************************************************************************** }
constructor TNxLogAppenderFile.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  fRetryCount   := 10;
  fFormater     := TNxLogFormaterDefault.Create;
  fDirectory    := ExtractFilePath(ParamStr(0));
  fFilenameBase := ChangeFileExt(ExtractFileName(ParamStr(0)), '');
  initLoggerFormatSettings(fFormatSettings);
  fStrategy     := NXLFS_NEWFILES;
  fLastDate     := trunc(NowUTC);
end;

destructor TNxLogAppenderFile.Destroy;
begin
  FreeAndNil(fFormater);
  inherited Destroy;
end;

function  TNxLogAppenderFile.isNextDay : Boolean;
var cd  : TDate;
begin
  cd  := trunc(NowUTC);
  if cd > fLastDate then
  begin
    fLastDate := cd;
    result  := true;
  end else
  begin
    result  := false;
  end;
end;

procedure TNxLogAppenderFile.append(const aEvent : TNxLoggerMessage);
begin
  case fStrategy of
    NXLFS_SINGLEFILE:
    begin
      appendSingleFile(aEvent);
    end;
    NXLFS_NEWFILES:
    begin
      appendNewFiles(aEvent);
    end;
    NXLFS_RENAME:
    begin
      appendRename(aEvent);
    end;
  else
    appendNewFiles(aEvent);
  end;
end;

procedure TNxLogAppenderFile.appendSingleFile(const aEvent : TNxLoggerMessage);
var cFilename     : String;
    retrycount    : Integer;
    logout        : UTF8String;
    zs            : UTF8String;
    fs            : TFileStream;
label lblretry;
begin
  retrycount := fRetryCount;
  lblretry:
  try
    if not DirectoryExists(fDirectory) then ForceDirectories(fDirectory);
    cFilename := fDirectory + fFilenameBase + '_' + SC_ALLFILETAIL + '.nxlog';
    logout  := UTF8String(fFormater.formatMessage(aEvent));
    if FileExists(cFilename) then
    begin
      fs := TFileStream.Create(cFilename, fmOpenWrite + fmShareDenyWrite);
      fs.Position := fs.Size;
    end
    else
    begin
      fs := TFileStream.Create(cFilename, fmCreate);
    end;
    try
      zs := logout + #13#10;
      fs.Write(zs[1], length(zs));
      retrycount := 0;
    finally
      FreeAndNil(fs);
    end;
  except
    on e : Exception do
    begin
      zs := zs + UTF8String(#13#10 + '   ###logfail: "' + e.Message + '"');
    end;
  end;
  if retrycount > 0 then
  begin
    dec(retrycount);
    sleep(50);
    goto lblretry;
  end;
end;

procedure TNxLogAppenderFile.appendNewFiles(const aEvent : TNxLoggerMessage);
var cFilename     : String;
    retrycount    : Integer;
    logout        : UTF8String;
    zs            : UTF8String;
    fs            : TFileStream;
label lblretry;
begin
  retrycount := fRetryCount;
  lblretry:
  try
    if not DirectoryExists(fDirectory) then ForceDirectories(fDirectory);
    cFilename := fDirectory + fFilenameBase + '_' + DateToStr(nowUTC, fFormatSettings) + '.nxlog';
    logout  := UTF8String(fFormater.formatMessage(aEvent));
    if FileExists(cFilename) then
    begin
      fs := TFileStream.Create(cFilename, fmOpenWrite + fmShareDenyWrite);
      fs.Position := fs.Size;
    end
    else
    begin
      fs := TFileStream.Create(cFilename, fmCreate);
    end;
    try
      zs := logout + #13#10;
      fs.Write(zs[1], length(zs));
      retrycount := 0;
    finally
      FreeAndNil(fs);
    end;
  except
    on e : Exception do
    begin
      zs := zs + UTF8String(#13#10 + '   ###logfail: "' + e.Message + '"');
    end;
  end;
  if retrycount > 0 then
  begin
    dec(retrycount);
    sleep(50);
    goto lblretry;
  end;
end;

procedure TNxLogAppenderFile.appendRename(const aEvent : TNxLoggerMessage);
var cFilename     : String;
    cNewFilename  : String;
    retrycount    : Integer;
    logout        : UTF8String;
    zs            : UTF8String;
    fs            : TFileStream;
label lblretry;
begin
  retrycount := fRetryCount;
  lblretry:
  try
    if not DirectoryExists(fDirectory) then ForceDirectories(fDirectory);
    cFilename := fDirectory + fFilenameBase + '_' + SC_CURRENTFILETAIL + '.nxlog';
    if isNextDay then
    begin
      if FileExists(cFilename) then
      begin
        cNewFilename := fDirectory + fFilenameBase + '_' + SC_FILEFROM + '_'+ DateToStr(nowUTC, fFormatSettings) + '.nxlog';
        if not RenameFile(cFilename, cNewFilename) then
        begin
          raise Exception.Create(Format('could not rename logfile "%0:s" to "%1:s".', [cFilename, cNewFilename]));
        end;
      end;
    end;
    logout  := UTF8String(fFormater.formatMessage(aEvent));
    if FileExists(cFilename) then
    begin
      fs := TFileStream.Create(cFilename, fmOpenWrite + fmShareDenyWrite);
      fs.Position := fs.Size;
    end
    else
    begin
      fs := TFileStream.Create(cFilename, fmCreate);
    end;
    try
      zs := logout + #13#10;
      fs.Write(zs[1], length(zs));
      retrycount := 0;
    finally
      FreeAndNil(fs);
    end;
  except
    on e : Exception do
    begin
      zs := zs + UTF8String(#13#10 + '   ###logfail: "' + e.Message + '"');
    end;
  end;
  if retrycount > 0 then
  begin
    dec(retrycount);
    sleep(50);
    goto lblretry;
  end;
end;


{ **************************************************************************** }
{ ***** TNxLogAppenderChain ************************************************** }
{ **************************************************************************** }

constructor TNxLogAppenderChain.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  fChainedLogger  := nil;
end;

destructor TNxLogAppenderChain.Destroy;
begin
  fChainedLogger  := nil;
  inherited Destroy;
end;

procedure TNxLogAppenderChain.append(const aEvent : TNxLoggerMessage);
begin
  if fChainedLogger <> nil then
  begin
    fChainedLogger.log(aEvent);
  end;
end;

{ **************************************************************************** }
{ ***** TNxLogAppenderTCPThread ********************************************** }
{ **************************************************************************** }

{$if CompilerVersion > 24}
function getMD5OfString(const aString : string) : string;
var idmd5 : TIdHashMessageDigest5;
begin
  idmd5 := TIdHashMessageDigest5.Create;
  try
    result  := idmd5.HashStringAsHex(aString, IndyTextEncoding_UTF8);
  finally
   idmd5.Free;
  end;
end;
{$else}
function getMD5OfString(const aString : string) : string;
var idmd5 : TIdHashMessageDigest5;
begin
  idmd5 := TIdHashMessageDigest5.Create;
  try
    result  := idmd5.HashStringAsHex(aString, TEncoding.UTF8);
  finally
   idmd5.Free;
  end;
end;
{$ifend}

constructor TNxLogAppenderTCPThread.Create(CreateSuspended: Boolean);
begin
  inherited Create(CreateSuspended);
  fTCP            := nil;
  fMachineIdent   := '';
  fHostOrIP       := '';
  fPort           := 0;
  fRetryCount     := 10;
  fAdapter        := nil;
  fUsername       := '';
  fPassword       := '';
end;

destructor TNxLogAppenderTCPThread.Destroy;
begin
  inherited Destroy;
end;

procedure TNxLogAppenderTCPThread.Execute;

var cEntry      : String;
    cRes        : String;
    cResCode    : Integer;
    cCurrentTry : Integer;
    _cPWD       : String;
    cResCodeS   : String;
    cResCodeT   : String;
    ind         : Integer;

  procedure _connect;
  begin
    fTCP  := TIdTCPClient.Create(nil);
    try
      fTCP.Host := fHostOrIP;
      fTCP.Port := fPort;
      fTCP.Connect;
      if (fUsername <> '') then
      begin
        if fPassword <> '' then
        begin
          _cPWD  := getMD5OfString(fPassword);
        end else
        begin
          _cPWD := '';
        end;
        {$if CompilerVersion > 24}
        fTCP.IOHandler.WriteLn(fUsername, IndyTextEncoding_UTF8);
        fTCP.IOHandler.WriteLn(_cPWD, IndyTextEncoding_UTF8);
        {$else}
        fTCP.IOHandler.WriteLn(fUsername, TEncoding.UTF8);
        fTCP.IOHandler.WriteLn(_cPWD, TEncoding.UTF8);
        {$ifend}
      end;
    except
      FreeAndNil(fTCP);
    end;
  end;

  procedure _disconnect;
  begin
    if fTCP <> nil then
    begin
      try
        fTCP.Disconnect;
      except
      end;
    end;
    FreeAndNil(fTCP);
  end;

begin
  while not Terminated do
  begin
    if fTCP = nil then
    begin
      // verbinden...
      _connect;
    end;
    if fTCP <> nil then
    begin
      // dann sind wir auch verbunden...
      cEntry  := fAdapter.consumeEntry;
      if cEntry <> '' then
      begin
        cCurrentTry := 0;
        repeat
          try
            {$if CompilerVersion > 24}
            fTCP.IOHandler.WriteLn('log:'+cEntry, IndyTextEncoding_UTF8);
            cRes  := fTCP.IOHandler.ReadLn(idglobal.EOL, 5000, -1, IndyTextEncoding_UTF8);
            {$else}
            fTCP.IOHandler.WriteLn('log:'+cEntry, TEncoding.UTF8);
            cRes  := fTCP.IOHandler.ReadLn(idglobal.EOL, 5000, -1, TEncoding.UTF8);
            {$ifend}
            ind := Pos(':', cRes);
            if ind > 0 then
            begin
              cResCodeS := Copy(cRes, 1, ind);
              cResCodeT := Copy(cRes, ind+1, length(cRes)-ind);
              if cResCodeS = '' then raise Exception.Create('server result code was empty');
              if not TryStrToInt(cResCodeS, cResCode) then raise Exception.Create('server result contained no result code');
              case cResCode of
                NXLOGPTCP_OK                            :
                begin
                  // Alles gut...
                end;
                NXLOGPTCP_BADREQUEST                    :
                begin
                  raise Exception.Create('NXLOGPTCP_BADREQUEST'+':'+cResCodeT);
                end;
                NXLOGPTCP_UNAUTHORIZED                  :
                begin
                  raise Exception.Create('NXLOGPTCP_UNAUTHORIZED'+':'+cResCodeT);
                end;
                NXLOGPTCP_PAYMENTREQUIRED               :
                begin
                  raise Exception.Create('NXLOGPTCP_PAYMENTREQUIRED'+':'+cResCodeT);
                end;
                NXLOGPTCP_FORBIDDEN                     :
                begin
                  raise Exception.Create('NXLOGPTCP_FORBIDDEN'+':'+cResCodeT);
                end;
                NXLOGPTCP_NOTFOUND                      :
                begin
                  raise Exception.Create('NXLOGPTCP_NOTFOUND'+':'+cResCodeT);
                end;
                NXLOGPTCP_METHODNOTALLOWED              :
                begin
                  raise Exception.Create('NXLOGPTCP_METHODNOTALLOWED'+':'+cResCodeT);
                end;
                NXLOGPTCP_INTERNALSERVERERROR           :
                begin
                  raise Exception.Create('NXLOGPTCP_INTERNALSERVERERROR'+':'+cResCodeT);
                end;
                NXLOGPTCP_NOTIMPLEMENTED                :
                begin
                  raise Exception.Create('NXLOGPTCP_NOTIMPLEMENTED'+':'+cResCodeT);
                end;
                NXLOGPTCP_SERVICETEMPORARYUNAVAILABLE   :
                begin
                  raise Exception.Create('NXLOGPTCP_SERVICETEMPORARYUNAVAILABLE'+':'+cResCodeT);
                end;
              else
                raise Exception.Create('unknown result code in server result');
              end;
            end else
            begin
              if cRes = '' then raise Exception.Create('server result was empty');
              if not TryStrToInt(cRes, cResCode) then raise Exception.Create('server result contained no result code');
              case cResCode of
                NXLOGPTCP_OK                            :
                begin
                  // Alles gut...
                  cCurrentTry := 0;
                end;
                NXLOGPTCP_BADREQUEST                    :
                begin
                  raise Exception.Create('NXLOGPTCP_BADREQUEST');
                end;
                NXLOGPTCP_UNAUTHORIZED                  :
                begin
                  raise Exception.Create('NXLOGPTCP_UNAUTHORIZED');
                end;
                NXLOGPTCP_PAYMENTREQUIRED               :
                begin
                  raise Exception.Create('NXLOGPTCP_PAYMENTREQUIRED');
                end;
                NXLOGPTCP_FORBIDDEN                     :
                begin
                  raise Exception.Create('NXLOGPTCP_FORBIDDEN');
                end;
                NXLOGPTCP_NOTFOUND                      :
                begin
                  raise Exception.Create('NXLOGPTCP_NOTFOUND');
                end;
                NXLOGPTCP_METHODNOTALLOWED              :
                begin
                  raise Exception.Create('NXLOGPTCP_METHODNOTALLOWED');
                end;
                NXLOGPTCP_INTERNALSERVERERROR           :
                begin
                  raise Exception.Create('NXLOGPTCP_INTERNALSERVERERROR');
                end;
                NXLOGPTCP_NOTIMPLEMENTED                :
                begin
                  raise Exception.Create('NXLOGPTCP_NOTIMPLEMENTED');
                end;
                NXLOGPTCP_SERVICETEMPORARYUNAVAILABLE   :
                begin
                  raise Exception.Create('NXLOGPTCP_SERVICETEMPORARYUNAVAILABLE');
                end;
              else
                raise Exception.Create('unknown result code in server result');
              end;
            end;
          except
            on e : exception do
            begin
              inc(cCurrentTry);
              sleep(50);
            end;
          end;
        until (cCurrentTry = 0) or (cCurrentTry > fRetryCount);
        if cCurrentTry > fRetryCount then
        begin
          // Das waren also mehrere Fehler hintereinander...
          _disconnect;
          sleep(1500);
        end;
      end else
      begin
        sleep(1000);
      end;
    end else
    begin
      // Verbindung steht nicht, was machen wir?
      sleep(1500);
    end;
  end;
  FreeAndNil(fTCP);
end;


{ **************************************************************************** }
{ ***** TNxLogAppenderTCP **************************************************** }
{ **************************************************************************** }
constructor TNxLogAppenderTCP.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  initLoggerFormatSettings(fFormatSettings);
  fMaxEntries     := TIdThreadSafeInteger.Create;
  fMaxEntries.Value := IC_DEFAULTMAXTCPENTRIES;
  fHostOrIP       := '';
  fPort           := 0;
  fRetryCount     := 10;
  fBuffer         := TIdThreadSafeStringList.Create;
  fWorker         := nil;
  fFormater       := TNxLogFormaterTCP.Create;
  fLevelFilter    := TNxLoggerMessageFilterLevelSet.Create;
  fLevelFilter.unsetLevel(NXLL_TRACE);
  fLevelFilter.unsetLevel(NXLL_DEBUG);
  fLevelFilter.unsetLevel(NXLL_INFO);
  fUserFilter     := nil;
  fUsername       := '';
  fPassword       := '';
end;

destructor TNxLogAppenderTCP.Destroy;
begin
  if isStarted then
  begin
    stop;
  end;
  clearBuffer;
  FreeAndNil(fBuffer);
  FreeAndNil(fFormater);
  fUserFilter     := nil;
  FreeAndNil(fLevelFilter);
  FreeAndNil(fMaxEntries);
  inherited Destroy;
end;

function  TNxLogAppenderTCP.consumeEntry : String;
var lst : TStringList;
begin
  lst := fBuffer.Lock;
  try
    if lst.Count > 0 then
    begin
      result  := lst[0];
      lst.Delete(0);
    end else
    begin
      result  := '';
    end;
  finally
    fBuffer.Unlock;
  end;
end;

procedure TNxLogAppenderTCP.setMachineIdent(aIdent : String);
begin
  if fMachineIdent <> aIdent then
  begin
    fMachineIdent := aIdent;
    if fFormater <> nil then
    begin
      fFormater.MachineIdent  := fMachineIdent;
    end;
  end;
end;

procedure TNxLogAppenderTCP.setMaxEntries(aValue : Integer);
begin
  fMaxEntries.Value := aValue;
end;

function  TNxLogAppenderTCP.getMaxEntries : Integer;
begin
  result  := fMaxEntries.Value;
end;

function  TNxLogAppenderTCP.getCurrentEntries : Integer;
var s   : TStringList;
begin
  s := fBuffer.Lock;
  try
    result  := s.Count;
  finally
    fBuffer.Unlock;
  end;
end;

function  TNxLogAppenderTCP.isStarted : Boolean;
begin
  result  := fWorker <> nil;
end;

procedure TNxLogAppenderTCP.start;
begin
  if not isStarted then
  begin
    fWorker := TNxLogAppenderTCPThread.Create(true);
    fWorker.FormatSettings  := self.fFormatSettings;
    fWorker.MachineIdent    := self.fMachineIdent;
    fWorker.HostOrIP        := self.fHostOrIP;
    fWorker.Port            := self.fPort;
    fWorker.RetryCount      := self.fRetryCount;
    fWorker.Username        := self.fUsername;
    fWorker.Password        := self.fPassword;
    fWorker.Adapter         := self;
    fWorker.Start;
  end;
end;

procedure TNxLogAppenderTCP.stop;
begin
  if isStarted then
  begin
    fWorker.Terminate;
    FreeAndNil(fWorker);
  end;
end;

procedure TNxLogAppenderTCP.clearBuffer;
begin
  if fBuffer <> nil then fBuffer.Clear;
end;

procedure TNxLogAppenderTCP.append(const aEvent : TNxLoggerMessage);
var zs    : String;

  procedure _doAppend;
  var _strs   : TStringList;
      _maxc   : Integer;
      _wasbo  : Boolean;
  begin
    _maxc := fMaxEntries.Value;
    _strs := fBuffer.Lock;
    try
      if _strs.Count < _maxc then
      begin
        // Alles OK...
        zs  := fFormater.formatMessage(aEvent);
        _strs.Add(zs);
        _wasbo  := false;
      end else
      begin
        // Pufferüberlauf...
        _wasbo  := true;
      end;
    finally
      fBuffer.Unlock;
    end;
    if _wasbo then
    begin
      if assigned(fOnOverflow) then
      begin
        fOnOverflow(_maxc);
      end;
    end;
  end;

begin
  if not isStarted then
  begin
    start;
  end;
  if fLevelFilter.match(aEvent) then
  begin
    if fUserFilter <> nil then
    begin
      if fUserFilter.match(aEvent) then
      begin
        _doAppend;
      end;
    end else
    begin
      _doAppend;
    end;
  end;
end;

{ **************************************************************************** }
{ ***** TNxLoggerCollectionItem ********************************************** }
{ **************************************************************************** }
constructor TNxLoggerCollectionItem.Create(Collection: TCollection);
begin
  inherited Create(Collection);
  fAppender := nil;
end;

destructor TNxLoggerCollectionItem.Destroy;
begin
  fAppender := nil;
  inherited Destroy;
end;

procedure TNxLoggerCollectionItem.append(const aEvent : TNxLoggerMessage);
begin
  if fAppender <> nil then
  begin
    fAppender.append(aEvent);
  end;
end;



{ **************************************************************************** }
{ ***** TNxLogger ************************************************************ }
{ **************************************************************************** }
constructor TNxLogger.Create(AOwner : TComponent);

  function _GetUserFromWindows: string;
  var
    UserName    : string;
    UserNameLen : Dword;
  begin
    UserNameLen := 255;
    SetLength(userName, UserNameLen);
    if GetUserName(PChar(UserName), UserNameLen) then
    begin
      result := Copy(UserName,1,UserNameLen-1);
    end else
    begin
      result := '';
    end;
  end;

begin
  inherited Create(AOwner);
  fAppenders  := TNxLoggerCollection.Create(TNxLoggerCollectionItem);
  fCSLog  := TCriticalSection.Create;
  fCSCurrentLevel := TCriticalSection.Create;
  initLoggerFormatSettings(fLogFormatSettings);
  fApplicationID    := ChangeFileExt(ExtractFileName(ParamStr(0)), '');
  fInstanceID       := IntToStr(Windows.GetCurrentProcessId);
  fUserIdent        := _GetUserFromWindows;
  fLanguage         := '';
  fCurrentLevel     := NXLL_INFO;
  fFilters  := TIdThreadSafeList.Create;
  fOnAppend := nil;
  fOnLog    := nil;
end;

destructor  TNxLogger.Destroy;
begin
  fOnAppend := nil;
  fOnLog    := nil;
  FreeAndNil(fCSCurrentLevel);
  FreeAndNil(fCSLog);
  FreeAndNil(fAppenders);
  FreeAndNil(fFilters);
  inherited Destroy;
end;

class function TNxLogger.isImportantLogLevel(const aRelativeBaseLevel, aLevelToTest : TNxLoggerLevel) : Boolean;
begin
  result  := aLevelToTest >= aRelativeBaseLevel;
end;

function  TNxLogger.matchFilters(const aEvent : TNxLoggerMessage) : Boolean;
var lst : TList;
    i   : Integer;
    flt : TNxLoggerMessageFilter;
begin
  result  := true;
  lst := fFilters.LockList;
  try
    for i := 0 to lst.Count - 1 do
    begin
      flt := TNxLoggerMessageFilter(lst[i]);
      result  := result and flt.match(aEvent);
    end;
  finally
    fFilters.UnlockList;
  end;
end;

function  TNxLogger.getMachineIdent : String;
begin
  result  := GetLocalComputerName;
end;

procedure TNxLogger.initializeFileLogging(const aApplicationId, aLogDirectory, aBaseFilename : String; aRetryCount : Integer = 10; aStrategy : TNxLogAppenderFileStrategy = NXLFS_NEWFILES);
var i     : TCollectionItem;
    x     : TNxLoggerCollectionItem;
    found : Boolean;
    fapp  : TNxLogAppenderFile;
begin
  found := false;
  fCSLog.Enter;
  try
    for i in fAppenders do
    begin
      x := i as TNxLoggerCollectionItem;
      if x.fAppender is TNxLogAppenderFile then
      begin
        fapp  := (x.fAppender as TNxLogAppenderFile);
        fapp.fDirectory    := aLogDirectory;
        fapp.fFilenameBase := aBaseFilename;
        fapp.fRetryCount   := aRetryCount;
        fapp.Strategy      := aStrategy;
        found := true;
      end;
    end;
    if not found then
    begin
      fapp  := TNxLogAppenderFile.Create(self);
      fapp.fDirectory    := aLogDirectory;
      fapp.fFilenameBase := aBaseFilename;
      fapp.fRetryCount   := aRetryCount;
      fapp.Strategy      := aStrategy;
      addAppender(fapp);
    end;
    fApplicationID := aApplicationId;
  finally
    fCSLog.Leave;
  end;
end;

procedure TNxLogger.initializeServerTCPLogging(const aApplicationId, aHostOrIP : String; aPort : Integer; aMachineIdent : String = ''; aRetryCount : Integer = 10; aUsername : String = ''; aPassword : String = ''; aLevelFilter : TNxLoggerLevelSet = [NXLL_FATAL, NXLL_ERROR, NXLL_WARN]);
var i     : TCollectionItem;
    x     : TNxLoggerCollectionItem;
    found : Boolean;
    fapp  : TNxLogAppenderTCP;
begin
  found := false;
  fCSLog.Enter;
  try
    if aMachineIdent = '' then
    begin
      aMachineIdent := getMachineIdent;
    end;
    for i in fAppenders do
    begin
      x := i as TNxLoggerCollectionItem;
      if x.fAppender is TNxLogAppenderTCP then
      begin
        fapp  := (x.fAppender as TNxLogAppenderTCP);
        fapp.fMachineIdent  := aMachineIdent;
        fapp.fHostOrIP      := aHostOrIP;
        fapp.fPort          := aPort;
        fapp.fRetryCount    := aRetryCount;
        fapp.fUsername      := aUsername;
        fapp.fPassword      := aPassword;
        fapp.fLevelFilter.setLevelSet(aLevelFilter);
        found := true;
      end;
    end;
    if not found then
    begin
      fapp  := TNxLogAppenderTCP.Create(self);
      fapp.setMachineIdent(aMachineIdent);
      fapp.fHostOrIP      := aHostOrIP;
      fapp.fPort          := aPort;
      fapp.fRetryCount    := aRetryCount;
      fapp.fUsername      := aUsername;
      fapp.fPassword      := aPassword;
      fapp.fLevelFilter.setLevelSet(aLevelFilter);
      addAppender(fapp);
    end;
    fApplicationID := aApplicationId;
  finally
    fCSLog.Leave;
  end;
end;

procedure TNxLogger.log(const aEvent : TNxLoggerMessage);
var i : TCollectionItem;
    x : TNxLoggerCollectionItem;
begin
  if isImportantLogLevel(getCurrentLevel, aEvent.LogLevel) and matchFilters(aEvent) then
  begin
    fCSLog.Enter;
    try
      for i in fAppenders do
      begin
        x := i as TNxLoggerCollectionItem;
        x.append(aEvent);
      end;
    finally
      fCSLog.Leave;
    end;
    if assigned(fOnAppend) then
    begin
      fOnAppend(self, aEvent);
    end;
  end;
  if assigned(fOnLog) then
  begin
    fOnLog(self, aEvent);
  end;
end;

procedure TNxLogger.log(const aLevel : TNxLoggerLevel; const aMessage : String);
var cMessage  : TNxLoggerMessage;
begin
  cMessage  := TNxLoggerMessage.Create(fApplicationID, fInstanceId, fUserIdent, aLevel, aMessage);
  log(cMessage);
  FreeAndNil(cMessage);
end;

procedure TNxLogger.log(const aLevel : TNxLoggerLevel; const aModule, aMessage : String);
var cMessage  : TNxLoggerMessage;
begin
  cMessage  := TNxLoggerMessage.Create(fApplicationID, fInstanceId, fUserIdent, aLevel, aModule, aMessage, TNxLogFormater.convertCategoryToString(NXLCAT_NONE), fLanguage, nil);
  log(cMessage);
  FreeAndNil(cMessage);
end;

procedure TNxLogger.log(const aLevel : TNxLoggerLevel; const aModule, aMessage : String; const aCategory : TNxLoggerCategory; const aException : exception);
var cMessage  : TNxLoggerMessage;
begin
  cMessage  := TNxLoggerMessage.Create(fApplicationID, fInstanceId, fUserIdent, aLevel, aModule, aMessage, TNxLogFormater.convertCategoryToString(aCategory), fLanguage, aException);
  log(cMessage);
  FreeAndNil(cMessage);
end;

procedure TNxLogger.log(const aLevel : TNxLoggerLevel; const aModule, aMessage : String; const aCategoryAsString : String; const aException : exception);
var cMessage  : TNxLoggerMessage;
begin
  cMessage  := TNxLoggerMessage.Create(fApplicationID, fInstanceId, fUserIdent, aLevel, aModule, aMessage, aCategoryAsString, fLanguage, aException);
  log(cMessage);
  FreeAndNil(cMessage);
end;

procedure TNxLogger.log(const aLevel : TNxLoggerLevel; const aModule, aMessage : String; const aCategoryAsString, aLanguage : String; const aException : exception);
var cMessage  : TNxLoggerMessage;
begin
  cMessage  := TNxLoggerMessage.Create(fApplicationID, fInstanceId, fUserIdent, aLevel, aModule, aMessage, aCategoryAsString, aLanguage, aException);
  log(cMessage);
  FreeAndNil(cMessage);
end;

procedure TNxLogger.fatal(const aMessage : String);
begin
  log(NXLL_FATAL, aMessage);
end;

procedure TNxLogger.fatal(const aModule, aMessage : String);
begin
  log(NXLL_FATAL, aModule, aMessage, NXLCAT_NONE, nil);
end;

procedure TNxLogger.fatal(const aModule, aMessage : String; const aCategory : TNxLoggerCategory; const aException : exception = nil);
begin
  log(NXLL_FATAL, aModule, aMessage, aCategory, aException);
end;

procedure TNxLogger.error(const aMessage : String);
begin
  log(NXLL_ERROR, aMessage);
end;

procedure TNxLogger.error(const aModule, aMessage : String);
begin
  log(NXLL_ERROR, aModule, aMessage, NXLCAT_NONE, nil);
end;

procedure TNxLogger.error(const aModule, aMessage : String; const aCategory : TNxLoggerCategory; const aException : exception = nil);
begin
  log(NXLL_ERROR, aModule, aMessage, aCategory, aException);
end;

procedure TNxLogger.warn(const aMessage : String);
begin
  log(NXLL_WARN, aMessage);
end;

procedure TNxLogger.warn(const aModule, aMessage : String);
begin
  log(NXLL_WARN, aModule, aMessage, NXLCAT_NONE, nil);
end;

procedure TNxLogger.warn(const aModule, aMessage : String; const aCategory : TNxLoggerCategory; const aException : exception = nil);
begin
  log(NXLL_WARN, aModule, aMessage, aCategory, aException);
end;

procedure TNxLogger.info(const aMessage : String);
begin
  log(NXLL_INFO, aMessage);
end;

procedure TNxLogger.info(const aModule, aMessage : String);
begin
  log(NXLL_INFO, aModule, aMessage, NXLCAT_NONE, nil);
end;

procedure TNxLogger.info(const aModule, aMessage : String; const aCategory : TNxLoggerCategory; const aException : exception = nil);
begin
  log(NXLL_INFO, aModule, aMessage, aCategory, aException);
end;

procedure TNxLogger.debug(const aMessage : String);
begin
  log(NXLL_DEBUG, aMessage);
end;

procedure TNxLogger.debug(const aModule, aMessage : String);
begin
  log(NXLL_DEBUG, aModule, aMessage, NXLCAT_NONE, nil);
end;

procedure TNxLogger.debug(const aModule, aMessage : String; const aCategory : TNxLoggerCategory; const aException : exception = nil);
begin
  log(NXLL_DEBUG, aModule, aMessage, aCategory, aException);
end;

procedure TNxLogger.trace(const aMessage : String);
begin
  log(NXLL_TRACE, aMessage);
end;

procedure TNxLogger.trace(const aModule, aMessage : String);
begin
  log(NXLL_TRACE, aModule, aMessage, NXLCAT_NONE, nil);
end;

procedure TNxLogger.trace(const aModule, aMessage : String; const aCategory : TNxLoggerCategory; const aException : exception = nil);
begin
  log(NXLL_TRACE, aModule, aMessage, aCategory, aException);
end;

function  TNxLogger.getAppenderCount : Integer;
begin
  result  := fAppenders.Count;
end;

function  TNxLogger.getAppender(aIndex : Integer) : TNxLogAppender;
begin
  if (aIndex >= 0) and (aIndex < fAppenders.Count) then
  begin
    result  := TNxLoggerCollectionItem(fAppenders.Items[aIndex]).Appender;
  end else
  begin
    result  := nil;
  end;
end;

procedure TNxLogger.addAppender(aAppender : TNxLogAppender);
begin
  TNxLoggerCollectionItem(fAppenders.Add).Appender  := aAppender;
end;

function  TNxLogger.deleteAppender(aIndex : Integer) : TNxLogAppender;
begin
  if (aIndex >= 0) and (aIndex < fAppenders.Count) then
  begin
    result  := TNxLoggerCollectionItem(fAppenders.Items[aIndex]).Appender;
    fAppenders.Delete(aIndex);
  end else
  begin
    result  := nil;
  end;
end;

procedure TNxLogger.clearAppenders;
begin
  fAppenders.Clear;
end;

function  TNxLogger.getCurrentLevel : TNxLoggerLevel;
begin
  fCSCurrentLevel.Enter;
  try
    result  := fCurrentLevel;
  finally
    fCSCurrentLevel.Leave;
  end;
end;

procedure TNxLogger.setCurrentLevel(aLevel : TNxLoggerLevel);
begin
  fCSCurrentLevel.Enter;
  try
    fCurrentLevel := aLevel;
  finally
    fCSCurrentLevel.Leave;
  end;
end;

function  TNxLogger.isTrace : Boolean;
begin
  result  := CurrentLevel <= NXLL_TRACE;
end;

function  TNxLogger.isDebug : Boolean;
begin
  result  := CurrentLevel <= NXLL_DEBUG;
end;

function  TNxLogger.isInfo  : Boolean;
begin
  result  := CurrentLevel <= NXLL_INFO;
end;

function  TNxLogger.isWarn  : Boolean;
begin
  result  := CurrentLevel <= NXLL_WARN;
end;

function  TNxLogger.isError : Boolean;
begin
  result  := CurrentLevel <= NXLL_ERROR;
end;

function  TNxLogger.isFatal : Boolean;
begin
  result  := CurrentLevel <= NXLL_FATAL;
end;



{ **************************************************************************** }
{ ***** TNxLoggerMessage ***************************************************** }
{ **************************************************************************** }
constructor TNxLoggerMessage.Create(const aApplicationId, aInstanceId, aLogUser : String; aLevel : TNxLoggerLevel; const aMessage : String);
begin
  self.Create(aApplicationId, aInstanceId, aLogUser, aLevel, '', aMessage, '', '', nil);
end;

constructor TNxLoggerMessage.Create(const aApplicationId, aInstanceId, aLogUser : String; aLevel : TNxLoggerLevel; const aModule, aMessage : String; const aCategory, aLanguage : String; const aException : Exception);
begin
  self.Create(aApplicationId, aInstanceId, aLogUser, aLevel, aModule, aMessage, '', '', '', nil);
end;

constructor TNxLoggerMessage.Create(const aApplicationId, aInstanceId, aLogUser : String; aLevel : TNxLoggerLevel; const aModule, aMessage : String; const aCategory, aLanguage, aThreadId : String; const aException : Exception);
begin
  inherited Create;
  fApplicationId  := aApplicationId;
  fInstanceId     := aInstanceId;
  fLogUser        := aLogUser;
  if aThreadId = '' then
  begin
    fThreadId     := IntToStr(Windows.GetCurrentThreadId);
  end else
  begin
    fThreadId     := aThreadId;
  end;
  fLevel          := aLevel;
  fCategory       := aCategory;
  fModule         := aModule;
  fMessage        := aMessage;
  fTimestamp      := nowUTC;
  fLanguage       := aLanguage;
  fException      := aException;
  if fException <> nil then
  begin
    fExceptionClassName := fException.ClassName;
  end else
  begin
    fExceptionClassName := '';
  end;
end;

destructor TNxLoggerMessage.Destroy;
begin
  inherited Destroy;
end;

procedure TNxLoggerMessage.AssignTo(Dest: TPersistent);
var d : TNxLoggerMessage;
begin
  if Dest is TNxLoggerMessage then
  begin
    d := Dest as TNxLoggerMessage;
    d.fApplicationId      := self.fApplicationId;
    d.fInstanceId         := self.fInstanceId;
    d.fLogUser            := self.fLogUser;
    d.fLevel              := self.fLevel;
    d.fCategory           := self.fCategory;
    d.fModule             := self.fModule;
    d.fMessage            := self.fMessage;
    d.fTimestamp          := self.fTimestamp;
    d.fException          := self.fException;
    d.fExceptionClassName := self.fExceptionClassName;
    d.fLanguage           := self.fLanguage;
    d.fStackTrace         := self.fStackTrace;
    d.fThreadId           := self.fThreadId;
  end else
  begin
    inherited Assign(Dest);
  end;
end;

function TNxLoggerMessage.getApplicationId : String;
begin
  result  := fApplicationId;
end;

function TNxLoggerMessage.getInstanceId : String;
begin
  result  := fInstanceId;
end;

function TNxLoggerMessage.getLogUser : String;
begin
  result  := fLogUser;
end;

function TNxLoggerMessage.getThreadId : String;
begin
  result  := fThreadId;
end;

function TNxLoggerMessage.getLevel : TNxLoggerLevel;
begin
  result  := fLevel;
end;

function TNxLoggerMessage.getCategory : String;
begin
  result  := fCategory;
end;

function TNxLoggerMessage.getModule : String;
begin
  result  := fModule;
end;

function TNxLoggerMessage.getMessage : String;
begin
  result  := fMessage;
end;

function TNxLoggerMessage.getTimestamp : TDateTime;
begin
  result  := fTimestamp;
end;

function TNxLoggerMessage.getException : Exception;
begin
  result  := fException;
end;

function TNxLoggerMessage.getLanguage : String;
begin
  result  := fLanguage;
end;

function TNxLoggerMessage.getExceptionClassName : String;
begin
  result  := fExceptionClassName;
end;

function TNxLoggerMessage.getStackTrace : String;
begin
  result  := fStackTrace;
end;

procedure TNxLoggerMessage.setApplicationId(aValue : String);
begin
  fApplicationId  := aValue;
end;

procedure TNxLoggerMessage.setInstanceId(aValue : String);
begin
  fInstanceId  := aValue;
end;

procedure TNxLoggerMessage.setLogUser(aValue : String);
begin
  fLogUser  := aValue;
end;

procedure TNxLoggerMessage.setThreadId(aValue : String);
begin
  fThreadId := aValue;
end;

procedure TNxLoggerMessage.setLevel(aValue  : TNxLoggerLevel);
begin
  fLevel  := aValue;
end;

procedure TNxLoggerMessage.setCategory(aValue : String);
begin
  fCategory := aValue;
end;

procedure TNxLoggerMessage.setModule(aValue : String);
begin
  fModule := aValue;
end;

procedure TNxLoggerMessage.setMessage(aValue  : String);
begin
  fMessage  := aValue;
end;

procedure TNxLoggerMessage.setTimestamp(aValue  : TDateTime);
begin
  fTimestamp  := aValue;
end;

procedure TNxLoggerMessage.setException(aValue  : Exception);
begin
  fException  := aValue;
end;

procedure TNxLoggerMessage.setLanguage(aValue : String);
begin
  fLanguage := aValue;
end;

procedure TNxLoggerMessage.setExceptionClassName(aValue : String);
begin
  fExceptionClassName := aValue;
end;

procedure TNxLoggerMessage.setStackTrace(aValue : String);
begin
  fStackTrace := aValue;
end;


{ **************************************************************************** }
{ ***** TNxLoggerMessageFilterLevelSet *************************************** }
{ **************************************************************************** }
constructor TNxLoggerMessageFilterLevelSet.Create;
begin
  inherited Create;
  fLevelSet := [NXLL_TRACE, NXLL_DEBUG, NXLL_INFO, NXLL_WARN, NXLL_ERROR, NXLL_FATAL ];
end;

destructor TNxLoggerMessageFilterLevelSet.Destroy;
begin
  inherited Destroy;
end;

procedure TNxLoggerMessageFilterLevelSet.AssignTo(Dest: TPersistent);
begin
  if Dest is TNxLoggerMessageFilterLevelSet then
  begin
    (Dest as TNxLoggerMessageFilterLevelSet).fLevelSet := fLevelSet;
  end;
end;

function  TNxLoggerMessageFilterLevelSet.match(aMessage : TNxLoggerMessage) : Boolean;
begin
  result  := false;
  if aMessage <> nil then
  begin
    if aMessage.LogLevel in fLevelSet then
    begin
      result  := true;
    end;
  end;
end;

procedure TNxLoggerMessageFilterLevelSet.saveToStream(aStream : TStream);
var wr  : TWriter;
    i   : TNxLoggerLevel;
    zs  : String;
begin
  wr  := TWriter.Create(aStream, 1024);
  try
    wr.WriteListBegin;
    for i := Low(TNxLoggerLevel) to High(TNxLoggerLevel) do
    begin
      zs  := TypInfo.GetEnumName(System.TypeInfo(TNxLoggerLevel), Integer(i));
      wr.WriteIdent(zs);
      if TNxLoggerLevel(i) in fLevelSet then
      begin
        wr.WriteBoolean(true);
      end else
      begin
        wr.WriteBoolean(false);
      end;
    end;
    wr.WriteListEnd;
    wr.FlushBuffer;
  finally
    FreeAndNil(wr);
  end;
end;

procedure TNxLoggerMessageFilterLevelSet.loadFromStream(aStream : TStream);
var rd  : TReader;
    i   : TNxLoggerLevel;
    zs  : String;
begin
  rd  := TReader.Create(aStream, 1024);
  try
    fLevelSet := [];
    rd.ReadListBegin;
    for i := Low(TNxLoggerLevel) to High(TNxLoggerLevel) do
    begin
      zs  := rd.ReadIdent;
      if rd.ReadBoolean then
      begin
        fLevelSet := fLevelSet + [i];
      end else
      begin
      end;
    end;
    rd.ReadListEnd;
  finally
    FreeAndNil(rd);
  end;
end;

procedure TNxLoggerMessageFilterLevelSet.setLevel(aLevel : TNxLoggerLevel);
begin
  fLevelSet := fLevelSet + [aLevel];
end;

procedure TNxLoggerMessageFilterLevelSet.unsetLevel(aLevel : TNxLoggerLevel);
begin
  fLevelSet := fLevelSet - [aLevel];
end;

procedure TNxLoggerMessageFilterLevelSet.setLevelAndAbove(aLevel : TNxLoggerLevel);
var cl  : TNxLoggerLevel;
begin
  for cl := low(TNxLoggerLevel) to high(TNxLoggerLevel) do
  begin
    if cl < aLevel then
    begin
      unsetLevel(cl);
    end else
    begin
      setLevel(cl);
    end;
  end;
end;

procedure TNxLoggerMessageFilterLevelSet.setLevelSet(aLevelSet : TNxLoggerLevelSet);
begin
  fLevelSet := aLevelSet;
end;

function  TNxLoggerMessageFilterLevelSet.isLevelInSet(aLevel : TNxLoggerLevel) : Boolean;
begin
  result  := aLevel in fLevelSet;
end;

procedure TNxLoggerMessageFilterLevelSet.reset;
begin
  fLevelSet := [NXLL_TRACE, NXLL_DEBUG, NXLL_INFO, NXLL_WARN, NXLL_ERROR, NXLL_FATAL ];
end;

{ **************************************************************************** }
{ ***** TNxLoggerMessageFilterByText ***************************************** }
{ **************************************************************************** }
constructor TNxLoggerMessageFilterByText.Create;
begin
  inherited Create;
  fText  := '';
end;

destructor TNxLoggerMessageFilterByText.Destroy;
begin
  inherited Destroy;
end;

procedure TNxLoggerMessageFilterByText.AssignTo(Dest: TPersistent);
begin
  if Dest is TNxLoggerMessageFilterByText then
  begin
    (Dest as TNxLoggerMessageFilterByText).fText := fText;
  end;
end;

procedure TNxLoggerMessageFilterByText.saveToStream(aStream : TStream);
var wr  : TWriter;
begin
  wr  := TWriter.Create(aStream, 1024);
  try
    wr.WriteString('FilterText');
    wr.WriteString(fText);
    wr.FlushBuffer;
  finally
    FreeAndNil(wr);
  end;
end;

procedure TNxLoggerMessageFilterByText.loadFromStream(aStream : TStream);
var rd  : TReader;
    zs  : String;
begin
  rd  := TReader.Create(aStream, 1024);
  try
    zs  := rd.ReadString;
    if lowercase(zs) = 'filtertext' then
    begin
      fText := rd.ReadString;
    end else
    begin
      raise Exception.Create('parse error: "FilterText" expected!');
    end;
  finally
    FreeAndNil(rd);
  end;
end;

procedure TNxLoggerMessageFilterByText.setText(aValue : String);
begin
  fText  := aValue;
end;

function  TNxLoggerMessageFilterByText.getText : String;
begin
  result  := fText;
end;

procedure TNxLoggerMessageFilterByText.reset;
begin
  fText  := '';
end;

{ **************************************************************************** }
{ ***** TNxLoggerMessageFilterModuleEquals *********************************** }
{ **************************************************************************** }
function  TNxLoggerMessageFilterModuleEquals.match(aMessage : TNxLoggerMessage) : Boolean;
begin
  result  := false;
  if aMessage <> nil then
  begin
    if lowercase(aMessage.LogModule) = lowercase(fText)  then
    begin
      result  := true;
    end;
  end;
end;

{ **************************************************************************** }
{ ***** TNxLoggerMessageFilterModuleStarting ********************************* }
{ **************************************************************************** }
function  TNxLoggerMessageFilterModuleStarting.match(aMessage : TNxLoggerMessage) : Boolean;
begin
  result  := false;
  if aMessage <> nil then
  begin
    if Pos(lowercase(fText), lowercase(aMessage.LogModule)) = 1 then
    begin
      result  := true;
    end;
  end;
end;

{ **************************************************************************** }
{ ***** TNxLoggerMessageFilterModuleContains ********************************* }
{ **************************************************************************** }
function  TNxLoggerMessageFilterModuleContains.match(aMessage : TNxLoggerMessage) : Boolean;
begin
  result  := false;
  if aMessage <> nil then
  begin
    if Pos(lowercase(fText), lowercase(aMessage.LogModule)) > 0 then
    begin
      result  := true;
    end;
  end;
end;

{ **************************************************************************** }
{ ***** TNxLoggerMessageFilterMessageEquals ********************************** }
{ **************************************************************************** }
function  TNxLoggerMessageFilterMessageEquals.match(aMessage : TNxLoggerMessage) : Boolean;
begin
  result  := false;
  if aMessage <> nil then
  begin
    if lowercase(aMessage.LogMessage) = lowercase(fText)  then
    begin
      result  := true;
    end;
  end;
end;

{ **************************************************************************** }
{ ***** TNxLoggerMessageFilterMessageStarting ******************************** }
{ **************************************************************************** }
function  TNxLoggerMessageFilterMessageStarting.match(aMessage : TNxLoggerMessage) : Boolean;
begin
  result  := false;
  if aMessage <> nil then
  begin
    if Pos(lowercase(fText), lowercase(aMessage.LogMessage)) = 1 then
    begin
      result  := true;
    end;
  end;
end;

{ **************************************************************************** }
{ ***** TNxLoggerMessageFilterMessageContains ******************************** }
{ **************************************************************************** }
function  TNxLoggerMessageFilterMessageContains.match(aMessage : TNxLoggerMessage) : Boolean;
begin
  result  := false;
  if aMessage <> nil then
  begin
    if Pos(lowercase(fText), lowercase(aMessage.LogMessage)) > 0 then
    begin
      result  := true;
    end;
  end;
end;

{ **************************************************************************** }
{ ***** TNxLoggerMessageFilterMachineEquals ********************************** }
{ **************************************************************************** }
function  TNxLoggerMessageFilterMachineEquals.match(aMessage : TNxLoggerMessage) : Boolean;
var cs  : String;
begin
  result  := false;
  if aMessage <> nil then
  begin
    if assigned(fOnMachineIdentRequired) then
    begin
      cs  := '';
      fOnMachineIdentRequired(self, aMessage, cs);
      if lowercase(fText) = lowercase(cs) then
      begin
        result  := true;
      end;
    end;
  end;
end;

procedure TNxLoggerMessageFilterMachineEquals.AssignTo(Dest: TPersistent);
begin
  if Dest is TNxLoggerMessageFilterMachineEquals then
  begin
    (Dest as TNxLoggerMessageFilterMachineEquals).fOnMachineIdentRequired := fOnMachineIdentRequired;
  end;
end;

{ **************************************************************************** }
{ ***** TNxLoggerMessageFilterApplicationEquals ****************************** }
{ **************************************************************************** }
function  TNxLoggerMessageFilterApplicationEquals.match(aMessage : TNxLoggerMessage) : Boolean;
begin
  result  := false;
  if aMessage <> nil then
  begin
    if lowercase(fText) = lowercase(aMessage.ApplicationId) then
    begin
      result  := true;
    end;
  end;
end;








initialization

  Classes.RegisterClass(TNxLoggerMessageFilterLevelSet);
  Classes.RegisterClass(TNxLoggerMessageFilterModuleEquals);
  Classes.RegisterClass(TNxLoggerMessageFilterModuleStarting);
  Classes.RegisterClass(TNxLoggerMessageFilterModuleContains);
  Classes.RegisterClass(TNxLoggerMessageFilterMessageEquals);
  Classes.RegisterClass(TNxLoggerMessageFilterMessageStarting);
  Classes.RegisterClass(TNxLoggerMessageFilterMessageContains);
  Classes.RegisterClass(TNxLoggerMessageFilterMachineEquals);
  Classes.RegisterClass(TNxLoggerMessageFilterApplicationEquals);

  Classes.RegisterClass(TNxLogFormaterDefault);
  Classes.RegisterClass(TNxLogFormaterTCP);

  Classes.RegisterClass(TNxLogAppenderFile);
  Classes.RegisterClass(TNxLogAppenderTCP);

  fDefaultLogger := TNxLogger.Create(nil);
  fDefaultLogger.addAppender(TNxLogAppenderFile.Create(fDefaultLogger));

finalization

  FreeAndNil(fDefaultLogger);

  Classes.UnRegisterClass(TNxLoggerMessageFilterLevelSet);
  Classes.UnRegisterClass(TNxLoggerMessageFilterModuleEquals);
  Classes.UnRegisterClass(TNxLoggerMessageFilterModuleStarting);
  Classes.UnRegisterClass(TNxLoggerMessageFilterModuleContains);
  Classes.UnRegisterClass(TNxLoggerMessageFilterMessageEquals);
  Classes.UnRegisterClass(TNxLoggerMessageFilterMessageStarting);
  Classes.UnRegisterClass(TNxLoggerMessageFilterMessageContains);
  Classes.UnRegisterClass(TNxLoggerMessageFilterMachineEquals);
  Classes.UnRegisterClass(TNxLoggerMessageFilterApplicationEquals);

  Classes.UnRegisterClass(TNxLogFormaterDefault);
  Classes.UnRegisterClass(TNxLogFormaterTCP);

  Classes.UnRegisterClass(TNxLogAppenderFile);
  Classes.UnRegisterClass(TNxLogAppenderTCP);

end.

Ein neues Projekt?

Wir sind gespannt!

Senden Sie uns eine Nachricht und wir informieren Sie näher über unsere individuellen Software-Lösungen!

    Hinweise zum Datenschutz gelesen und akzeptiert.

    Kontaktieren Sie uns
    close slider

      Wir freuen uns auf den Kontakt mit Ihnen

      Hinterlassen Sie eine Nachricht für uns und wir treten schnellst möglich mit Ihnen in Verbindung.