Search Results for

    Show / Hide Table of Contents

    Using TMSLogger

    Add the unit VCL.TMSLogging or FMX.TMSLogging to the uses list depending on the kind of framework you are creating an application for. Additionally, the units TMSLoggingCore and TMSLoggingUtils are only neccessary, for example when setting the Outputs or Filters property. The TMSLoggingCore and TMSLoggingUtils are shared between VCL and FMX. The IDE Plugin that is available after installation can help you add the missing units in case the application does not compile after adding code related to TMS Logging. More information can be found in the IDE Plugin chapter.

    The function TMSLogger returns a singleton logger instance that can be used throughout the application. By default the logger is active, but can easily be deactivated by using the following code:

    TMSLogger.Active := False;
    

    As already mentioned in the overview topic, the logger makes use of log levels to output values / objects. Each call is a combination of the log level, an optional format and the values / objects to log. The logger outputs to the console by default, and already applies a set of outputs (TMSLogger.Outputs) with a specific format (TMSLogger.OutputFormats). Below is a sample of different log levels with the default logger configuration.

    procedure TForm1.Button1Click(Sender: TObject);
    var
      s: string;
    begin
      s := 'Hello World !';
      TMSLogger.Info(s);
      TMSLogger.Error(s);
      TMSLogger.Warning(s);
      TMSLogger.Trace(s);
      TMSLogger.Debug(s);
    end;
    

    Result:

    logging debug output

    The TMSLogger provides several other features and capabilities you can use, as follows.

    Formatting

    Formatting can be applied in 2 ways: either globally with the TMSLogger.OutputFormats properties or by using one of the log level overloads. The parsing after applying formatting is a custom implementation and supports the Delphi SysUtils Format function. A requirement to successfully execute a log statement with parsing is that the format string contains an opening and closing brace or curly bracket to form a format tag.

    The TMSLogger.OutputFormats property contains a set of predefined formats with their format tags. Below is an overview of the default values for each output format:

    TimeStampFormat := '[{%dt}]';
    ProcessIDFormat := '[{%s}]';
    ThreadIDFormat := '[{%s}]';
    LogLevelFormat := '[{%s}]';
    ValueFormat := '[Value: {%s}]';
    NameFormat := '[Name: {%s}]';
    TypeFormat := '[Type: {%s}]';
    MemoryUsageFormat := '[Memory usage: {%bt} bytes]';
    

    Accompanied with these format properties is the TMSLogger.Outputs property that can determine which information needs to be logged. Below is a sample that combines these 2 properties to create a completely different log output.

    procedure TForm1.FormCreate(Sender: TObject);
    begin
      TMSLogger.Outputs := [loTimeStamp, loLogLevel, loValue];
      TMSLogger.OutputFormats.TimeStampFormat := 'The time is {%"hh:nn:ss"dt}, ';
      TMSLogger.OutputFormats.LogLevelFormat := 'the loglevel is {%s}, ';
      TMSLogger.OutputFormats.ValueFormat := 'and the value is {%s}';
    end;
    
    procedure TForm1.Button1Click(Sender: TObject);
    var
      s: string;
    begin
      s := 'Hello World !';
      TMSLogger.Info(s);
      TMSLogger.Error(s);
      TMSLogger.Warning(s);
      TMSLogger.Trace(s);
      TMSLogger.Debug(s);
    end;
    

    Result will be as following:

    logging formatting example

    Overriding format for a specific log message

    While the above method of formatting already provides a certain flexibility, the value formatting is applied to each log statement. To override this behavior, you can specify a formatting for each value that will be logged. The following sample overrides the output of the previous sample for one of the log statements.

    procedure TForm1.FormCreate(Sender: TObject);
    begin
      TMSLogger.Outputs := [loTimeStamp, loLogLevel, loValue];
      TMSLogger.OutputFormats.TimeStampFormat := 'The time is {%"hh:nn:ss"dt}, ';
      TMSLogger.OutputFormats.LogLevelFormat := 'the loglevel is {%s}, ';
      TMSLogger.OutputFormats.ValueFormat := '{%s}';
    end;
    
    procedure TForm1.Button1Click(Sender: TObject);
    var
      s: string;
      fmt: string;
    begin
      s := 'Hello World !';
      fmt := 'The value is {%s}';
      TMSLogger.Info(s);
      TMSLogger.Error(s);
      TMSLogger.WarningFormat(fmt, [s]);
      TMSLogger.Trace(s);
      TMSLogger.Debug(s);
    end;
    

    Result will be as following:

    logging formatting message

    Format string syntax

    Formatting is not limited to strings only, below is an overview of supported formatting tags that can be used.

    Tag Expected value
    d Decimal (integer)
    e Scientific
    f Fixed
    g General
    m Money
    n Number (floating)
    p Pointer
    s String
    u Unsigned decimal
    x Hexadecimal

    The general format of each formatting tag is as follows:

    {%[Index:][-][Width][.Precision]Tag} 
    

    where the square brackets refer to optional parameters, and the : . - characters are literals, the first 2 of which are used to identify two of the optional arguments. Below is an example of formatting a double value with 2 decimals:

    procedure TForm1.Button1Click(Sender: TObject);
    var
      d: Double;
      fmt: string;
    begin
      d := 123.456;
      fmt := 'The value is {%.2f}';
      TMSLogger.InfoFormat(fmt, [d]);
    end;
    

    Result will be as following:

    logging formatting number

    More information can be found at the following page: http://www.delphibasics.co.uk/RTL.asp?Name=Format

    As an extension to this, the logger supports a set of additional tags that can be used to format the value. Below is an overview of the format tags that can be used.

    Tag Optional parameters (between double quotes) Expected value Output
    a array outputs the array to a string that represents the values
    b output as number or as "true" or "false" string (ex: {%"-1"b}) Boolean outputs the Boolean value to a string represented in numbers or "true" or "false" string
    bin number of decimals (ex: {%"8"bin} TStream object outputs the TStream object as a binary formatted string
    bt output as data size (B, KB, MB, GB, TB) and optional decimals separated with '#' and formatted with the FormatFloat function (ex: '{%"GB#0.00000"bt}' Ordinal outputs the value as a data size formatted value
    dt datetime format (ex: {%"mm-dd-yyyy"dt) TDateTime outputs the TDateTime value to a string
    hex number of digits (ex: {%"2"hex} TStream object outputs the TStream object as a hex formatted string
    pic object with image data outputs image data as a string that is sent to the output handlers
    pichex object with image data outputs image data as a hex string that is sent to the output handlers
    st TStream object outputs the TStream object as a string

    A sample using one of the above tags is demonstrated in the following sample:

    procedure TForm1.Button1Click(Sender: TObject);
    var
      fmt: string;
    begin
      fmt := 'Today is {%"ddd, dd mmm yyyy"dt}';
      TMSLogger.InfoFormat(fmt, [Now]);
    end;
    

    Output:

    logging formatting custom

    OnCustomFormat event

    When the above format tags are not sufficient to output the value, the logger exposes an OnCustomFormat event that passes the format tag it has received and the value it needs to parse. The var AResult parameter of this event can be returned based on the custom format tag. Below is an example which demonstrates this.

    procedure TForm1.Button1Click(Sender: TObject);
    var
      fmt: string;
      d: Double;
    begin
      d := 123.456;
      fmt := 'This is a custom tag with value {%CT}';
      TMSLogger.InfoFormat(fmt, [d]);
    end;
    
    procedure TForm1.DoCustomFormat(Sender: TObject; AValue: TValue;
      AFormat: string; var AResult: string);
    begin
      if AFormat.ToUpper.Contains('CT') and AValue.IsType<Double> then
        AResult := FloatToStr(AValue.AsType<Double>);
    end;
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      TMSLogger.OnCustomFormat := DoCustomFormat;
    end;
    

    Output:

    logging formatting oncustomformat

    The above sample simply verifies whether the custom tag is used. The AFormat parameter contains the tag part of the format string that we pass as a parameter to our TMSLogger.InfoFormat call. The AFormat parameter value is {%CT}. The AValue parameter contains the Double value and the AResult var parameter is assigned a simple FloatToStr of the AValue parameter.

    Validations

    Validations are attributes (based on the Delphi attribute concept) in which a certain comparison is made and a Boolean is returned whether that condition is met. When the result from this comparison is true, the log output will be sent to the output handlers. By default, there are no validations applied, thus the output is always logged.

    When monitoring a certain value, be it a string, a Double, an integer, etc. value, and you only want to output this value when it matches, for example, a string length, a regular expression, or when the value exceeds a minimum or maximum, you can create a validation attribute and add it as a parameter of one of the logger calls. The TMSLoggingCore unit already provides a set of validation classes that are ready to use. Below is an overview of those validation classes and a short explanation.

    • TMSLoggerRangeValidation: returns true if the value that needs to be logged exceeds a certain minimum and maximum.

    • TMSLoggerDateTimeValidation: returns true if the value that needs to be logged exceeds a certain start and end date. The date parameters when creating a TMSLoggerDateTimeValidation attribute are strings and are converted with the DateTimeToStr function.

    • TMSLoggerStringValidation: returns true if the value doesn't match or contain a certain string.

    • TMSLoggerStringLengthValidation: returns true if the value doesn't match or exceed a certain string length.

    • TMSLoggerRegularExpressionValidation: returns true if the value doesn't match a certain regular expression.

    When the condition is met, the value is logged. Each validation has a set of parameters to further fine-tune the condition. Two important properties are the ReverseCondition and the Format properties. When the ReverseCondition property is true, the condition is reversed, for example in case of the TMSLoggerRangeValidation, the condition returns true if the value is within a certain minimum and maximum. The format property can be used to apply a certain formatting when the condition is met and the value is logged. Below is a sample that demonstrates the use of a validation attribute and the ReverseCondition and Format properties.

    procedure TForm1.Button1Click(Sender: TObject);
    var
      fmt: string;
      i: Integer;
      vl: TMSLoggerRangeValidation;
    begin
      fmt := 'The value is {%g}';
      vl := TMSLoggerRangeValidation.Create(110, 130);
      vl.Format := fmt;
      i := 100;
      TMSLogger.Info(i, [], [vl]);
      i := 122;
      TMSLogger.Info(i, [], [vl]);
      i := 140;
      TMSLogger.Info(i, [], [vl]);
      i := 110;
      TMSLogger.Info(i, [], [vl]);
      vl.Free;
    end;
    

    Output:

    logging validation example

    Note that with the above sample, only the values 100 and 140 will be logged, because they exceed the minimum and maximum values that were set as parameters of the TMSLoggerRangeValidation attribute. Setting the ReverseCondition to True, will generate a different output as demonstrated in the following sample.

    procedure TForm1.Button1Click(Sender: TObject);
    var
      fmt: string;
      i: Integer;
      vl: TMSLoggerRangeValidation;
    begin
      fmt := 'The value is {%g}';
      vl := TMSLoggerRangeValidation.Create(110, 130);
      vl.Format := fmt;
      vl.ReverseCondition := True;
      i := 100;
      TMSLogger.Info(i, [], [vl]);
      i := 122;
      TMSLogger.Info(i, [], [vl]);
      i := 140;
      TMSLogger.Info(i, [], [vl]);
      i := 110;
      TMSLogger.Info(i, [], [vl]);
      vl.Free;
    end;
    

    Output:

    logging validation reverse

    In this case, the values 122 and 110 will be logged, because they are within the minimum and maximum values and the reverse condition flag is set to True. Note that the Format parameter is set to 'The value is {%g}' which will then output the values with this specific format. The format string can also be directly passed as a parameter to the InfoFormat call as already demonstrated in one of the previous samples.

    Custom validations

    When the default validation attributes are not sufficient, you can create your own validation by inheriting from the TMSLoggerBaseValidation attribute class and overriding the function Validate(AValue: TValue): Boolean. Below is a sample that demonstrates this.

    type
      MyValidation = class(TMSLoggerBaseValidation)
      protected
        function Validate(AValue: TValue): Boolean; override;
      end;
    
    implementation
    
    procedure TForm1.Button1Click(Sender: TObject);
    var
      d: Double;
      vl: MyValidation;
    begin
      vl := MyValidation.Create;
      d := 3;
      TMSLogger.Debug(d, [], [vl]);
      d := 4.5;
      TMSLogger.Debug(d, [], [vl]);
      vl.Free;
    end;
    
    { MyValidation }
    
    function MyValidation.Validate(AValue: TValue): Boolean;
    begin
      Result := False;
      if AValue.IsType<Double> then
        Result := Frac(AValue.AsType<Double>) = 0;
    end;
    

    Output:

    logging validation custom

    The sample returns a true if the fractional part of the value is 0 which means that the value 3 is a valid value and will not be logged. The properties to control formatting and reverse conditions are not available by default when inheriting from TMSLoggerBaseValidation. When these properties and functionality need to be available in your custom validation class, you can inherit from TMSLoggerValidation instead.

    Logger output statements are not limited to only one validation. Multiple validation attributes can be passed as a parameter. The logger calls have an array of TMSLoggerBaseValidation parameter that can contain multiple values. Only when each validation condition is met, the value will be logged.

    Using declarative attributes

    As explained earlier, the validation attributes are based on the Delphi attribute concept which means that they can also be added to an object's properties. This way, the logger can simply pass the complete object as a parameter, and the values will be logged when the validation condition is met. Below is a sample that demonstrates this.

    type
      TMyObject = class
      private
        FX: string;
        FY: Double;
      public
        [TMSLoggerStringLengthValidation(5)]
        property X: string read FX write FX;
        [TMSLoggerRangeValidation(10, 20)]
        property Y: Double read FY write FY;
      end;
    
    implementation
    
    procedure TForm1.Button1Click(Sender: TObject);
    var
      obj: TMyObject;
    begin
      obj := TMyObject.Create;
      obj.X := 'Hello';
      obj.Y := 30;
      TMSLogger.Warning(obj);
      obj.Free;
    end;
    

    Output:

    logging validation multiple

    The output of this sample exists of the object itself and the Y property. The X property is not logged, because the 'Hello' string has a length of 5.

    The use of validation attributes require the TMSLoggingCore unit to be added to the uses list. If the warning below occurs after compilation, then the attribute is not found and will not be detected by the logger.

    [dcc32 Warning] UDemo.pas(14): W1025 Unsupported language feature: 'custom attribute'
    

    Property Filtering

    The logger supports filtering based on the visibility of the field or property that is being logged. The Filters property can be used to determine if public and/or published properties need to be logged with or without attributes. This way, the logger can analyze and only log the field or property that match the filter. The filter is set to allow all public and published properties with attributes by default. Below is a sample that demonstrates the use of the Filter property.

    type
      TMyObject = class
      private
        FZ: string;
        FX: Double;
        FY: Integer;
      public
        property X: Double read FX write FX;
        [TMSLoggerRangeValidation(0, 10)]
        property Y: Integer read FY write FY;
      published
        property Z: string read FZ write FZ;
      end;
    
    procedure TForm1.Button1Click(Sender: TObject);
    var
      obj: TMyObject;
    begin
      TMSLogger.Filters := [lfPublic, lfPublished];
      obj := TMyObject.Create;
      obj.X := 3.456;
      obj.Y := 10;
      obj.Z := 'Hello World';
      TMSLogger.Warning(obj);
      obj.Free;
    end;
    

    Output:

    logging filtering example

    Notice that the output only shows the X and Z properties, because the Filters property is set the only allow public and published properties without attributes. The Y property has an attribute and therefore not logged. The attributed validates a range between 0 and 10 and the value of the Y property is valid, and thus not logged. If the value would exceed the range, the value would be logged but only if the filters are modified to allow public properties with attributes.

    Using attributes

    Additionally, filtering can be fine-tuned with attributes. The filter attributes add the same kind of filtering as on logger level. The logger parses the attributes and determines if the sub properties / fields of the class or property that has the attribute applied, are valid for logging. There are 2 kinds of filter attributes, TMSLoggerClassFilter and TMSLoggerPropertyFilter. Below is a sample that demonstrates this.

    type
      [TMSLoggerClassFilter([lfPublic, lfPublicWithAttributes])]
      TMyObject = class
      private
        FZ: string;
        FX: Double;
        FY: Integer;
      public
        property X: Double read FX write FX;
        [TMSLoggerRangeValidation(0, 10)]
        property Y: Integer read FY write FY;
      published
        property Z: string read FZ write FZ;
      end;
    
    procedure TForm1.Button1Click(Sender: TObject);
    var
      obj: TMyObject;
    begin
      obj := TMyObject.Create;
      obj.X := 3.456;
      obj.Y := 12;
      obj.Z := 'Hello World';
      TMSLogger.Warning(obj);
      obj.Free;
    end;
    

    Output:

    logging filtering attributes

    In this case, only the X and Y properties will be logged, because the TMSLoggerClassFilter attribute specifies to allow public properties with and without attributes. The value Y is 12 in this case, exceeds the range validation and is logged. If the value would be set to 10, as in the previous sample, the Y property would also not be logged.

    The use of filter attributes requires the TMSLoggingCore unit to be added to the uses list. If the warning below occurs after compilation, then the attribute is not found and will not be detected by the logger.

    [dcc32 Warning] UDemo.pas(14): W1025 Unsupported language feature: 'custom attribute'
    

    Multi-value Logging

    The logger has a set of overloads per log level that can be used to format the output, specify validation attributes for conditional logging and specify an array of property names when an object is logged. One of the overloads is designed to quickly log a set of objects in a single call. Below is a sample that demonstrates this.

    procedure TForm1.Button1Click(Sender: TObject);
    var
      a: string;
      b: Double;
      c: Boolean;
    begin
      a := 'Hello World';
      b := 4.56;
      c := True;
      TMSLogger.DebugValues([a, b, c]);
    end;
    

    Output:

    logging multivalue

    Note that this call does not have a separate format parameter. The formatting is based on the OutputFormats property.

    Timing

    By default the logger outputs the current date/time, but has the capability of formatting the timestamp output as milliseconds, microseconds or ticks depending on the TimeStampOutputMode property. When using an output mode other than the default value for this property, you always need to combine the logger calls with a StartTimer / StopTimer.

    Microseconds

    Example:

    procedure TForm1.Button1Click(Sender: TObject);
    begin
      TMSLogger.TimeStampOutputMode := tsomMicroseconds;
      TMSLogger.StartTimer;
      Sleep(5);
      TMSLogger.Debug(1);
      Sleep(15);
      TMSLogger.Debug(2);
      Sleep(5);
      TMSLogger.Debug(3);
      Sleep(5);
      TMSLogger.Debug(4);
      TMSLogger.StopTimer;
    end;
    

    Output:

    logging timing microseconds

    Delta Microseconds

    Example:

    procedure TForm1.Button1Click(Sender: TObject);
    begin
      TMSLogger.TimeStampOutputMode := tsomMicrosecondsDelta;
      TMSLogger.StartTimer;
      Sleep(5);
      TMSLogger.Debug(1);
      Sleep(15);
      TMSLogger.Debug(2);
      Sleep(5);
      TMSLogger.Debug(3);
      Sleep(5);
      TMSLogger.Debug(4);
      TMSLogger.StopTimer;
    end;
    

    Output:

    logging timing deltamicroseconds

    Direct timing

    Additionally, timing can be done with the StartTimer and StopTimer independent of the TimeStampOutputMode as demonstrated in the following sample:

    procedure TForm1.Button1Click(Sender: TObject);
    begin
      TMSLogger.StartTimer;
      Sleep(500);
      TMSLogger.StopTimer(True, lsmMilliseconds, 'The elapsed time is {%d} ms');
    end;
    

    Output:

    logging timing direct

    Exceptions

    The logger supports automatic handling and logging of exceptions. As an addition to the standard log levels, an exception log level is added to the set. By default, exception handling is not enabled. To enable it set ExceptionHandling to true on logger level.

    TMSLogger1.ExceptionHandling := True;
    

    Whenever an exception occurs, it will be automatically logged with an Exception log level, as demonstrated in the following division by zero exception.

    TMSLogger.RegisterOutputHandlerClass(TTMSLoggerTCPOutputHandler, [Self]);
    TMSLogger.ExceptionHandling := True;
    TMSLogger.Outputs := AllOutputs;
    
    procedure TForm1.Button1Click(Sender: TObject);
    var
      a, b, c: Integer;
    begin
      a := 10;
      b := 0;
      c := a div b;
    end;
    

    logging exception example

    Enabling exception handling will create a TApplicationEvents object in VCL and assign the Application.OnException event. If your application needs to handle additional code when an exception occurs the logger exposes an OnHandleException event.

    HTML Support

    The Browser Output and HTML Output handlers are both using HTML to display the output information. To add additional formatting inside the table or plain HTML formatted viewer, HTML tags can be added to the log statements. Below is a sample that demonstrates this.

    procedure TForm1.Button3Click(Sender: TObject);
    var
      d: Double;
    begin
      d := 4.5;
      TMSLogger.DebugFormat('The <span style=''color:red''><b>value</b></span> is {%f}', [d]);
    end;
    

    Even when specifying HTML tags, the other non-HTML formatted output handlers will still display the content correct, as they strip HTML when receiving output information from the logger.

    Other TTMSLogger methods

    The TMSLogger object has a set of helper methods that can be used to output additional information to the output handlers. Below is an overview of each method and a short explanation.

    Name Description
    Clear Sends a clear instruction to each output handler and removes the log files / log data.
    GetTimer(const AMode: TTMSLoggerTimerMode = lsmMilliseconds; const ALogResult: Boolean = False; const AFormat: string = ''): Int64 Returns an elapsed value in ticks or milliseconds based on the mode parameters after a timer has been started with StartTimer.
    Indent Increases the indent for log statements.
    IsTimerRunning Returns a Boolean if the timer is still running after it was started with the StartTimer call.
    LogCurrentDateTime(const AFormat: string = '') Logs the current date / time with an optional formatting parameter.
    LogCurrentLocale(const AFormat: string = ''); Logs the current language identifier.
    LogProcessID(const AFormat: string = ''); Logs the process id.
    LogScreenShot Logs a screenshot of the main form of the application.
    LogScreenShot(const AControl: TControl) Logs a screenshot of the specified control.
    LogSeparator Logs a separator string.
    LogSystemInformation(const AFormat: string = '') Logs the system information of the operating system.
    LogThreadID(const AFormat: string = ''); Logs the thread id.
    LogMemoryUsage(const AFormat: string = ''); Logs the memory usage of the application.
    LogMemoryUsageDifference(const ALogResult: Boolean = True; const AFormat: string = ''): Cardinal Logs the difference of the memory usage of the application based on the previous call to LogMemoryUsage.
    StartTimer Starts a timer, needs to be paired with StopTimer.
    StopTimer(const AMode: TTMSLoggerTimerMode = lsmMilliseconds; const ALogResult: Boolean = False; const AFormat: string = ''): Int64 Returns an elapsed value in ticks or milliseconds based on the mode parameters and stops the timer, needs to be paired with StartTimer.
    Unindent Decreases the indent for log statements.
    In This Article
    Back to top TMS Logging v2.13.0.1
    © 2002 - 2025 tmssoftware.com