
Purpose: Framework Core Logging
- this unit is a part of the Open Source Synopse mORMot framework 2, licensed under a MPL/GPL/LGPL three license - see LICENSE.md
| Unit Name | Description | |
|---|---|---|
| mormot.core.base | Framework Core Shared Types and RTL-like Functions | |
| mormot.core.buffers | Framework Core Low-Level Memory Buffer Process | |
| mormot.core.data | Framework Core Low-Level Data Processing Functions | |
| mormot.core.datetime | Framework Core Low-Level Date and Time Support | |
| mormot.core.json | Framework Core Low-Level JSON Processing | |
| mormot.core.os | Framework Core Low-Level Wrappers to the Operating-System API | |
| mormot.core.rtti | Framework Core Low-Level Cross-Compiler RTTI Definitions | |
| mormot.core.text | Framework Core Low-Level Text Processing | |
| mormot.core.unicode | Framework Core Low-Level Unicode UTF-8 UTF-16 Ansi Conversion |
| Objects | Description | |
|---|---|---|
| ESynLogException | Class of Exceptions raised by this unit | |
| ESynLogSilent | An exception which wouldn't be logged and intercepted by this unit | |
| ISynLog | A generic interface used for logging a method | |
| ISynLogCallback | A mORMot-compatible calback definition | |
| TDebugFile | Process a .map/.dbg file content, to be used e.g. with TSynLog to provide additional debugging information for a given executable | |
| TDebugSymbol | A debugger symbol, as decoded by TDebugFile from a .map/.dbg file | |
| TDebugUnit | A debugger unit, as decoded by TDebugFile from a .map/.dbg file | |
| TSynLog | A per-family and/or per-thread log file content | |
| TSynLogCallback | Store a subscribe to ISynLogCallback | |
| TSynLogCallbacks | Can manage a list of ISynLogCallback registrations | |
| TSynLogExceptionInfo | Storage of the information associated with an intercepted exception | |
| TSynLogFamily | Regroup several logs under an unique family name | |
| TSynLogFile | Used to parse a .log file, as created by TSynLog, into high-level data | |
| TSynLogFileProc | Used by TSynLogFile to refer to a method profiling in a .log file | |
| TSynLogFileView | Used to parse a .log file and process into VCL/LCL/FMX | |
| TSynLogSettings | Store simple log-related settings | |
| TSynLogThreadContext | Thread-specific internal context used during logging | |
| TSynLogThreadRecursion | TSynLogThreadContext will define a dynamic array of such information |
TDebugSymbol = packed record
A debugger symbol, as decoded by TDebugFile from a .map/.dbg file
Name: RawUtf8;
Symbol internal name
Start: integer;
Starting offset of this symbol in the executable
Stop: integer;
End offset of this symbol in the executable
TDebugUnit = packed record
A debugger unit, as decoded by TDebugFile from a .map/.dbg file
Addr: TIntegerDynArray;
Start code address of each source code line
FileName: RawUtf8;
Associated source file name
Line: TIntegerDynArray;
List of all mapped source code lines of this unit
Symbol: TDebugSymbol;
Name, Start and Stop of this Unit
TDebugFile = class(TSynPersistent)
Process a .map/.dbg file content, to be used e.g. with TSynLog to provide additional debugging information for a given executable
- debug info can be saved as .mab file in a much more optimized format (e.g. mormot2tests 4MB .map into a 280KB .mab, 13MB .dbg into a 290KB .mab)
- on FPC, DWARF symbols embedded to the executable can also be retrieved - but you would better use an external .dbg file then convert it into a .mab
- on FPC, you don't need to specifly the -gl compiler switch
- location of a source code information from its address is below 10us
constructor Create(const aExeName: TFileName = ''; MabCreate: boolean = true; DebugToConsole: boolean = false); reintroduce;
Get the available debugging information
- if aExeName is specified, will use it in its search for .map/.dbg/.mab
- if aExeName is not specified, will use the currently running .exe/.dll
- it will first search for a .map/.dbg matching the file name: if found, will be read to retrieve all necessary debugging information - a .mab file will be also created in the same directory (if MabCreate is TRUE)
- if .map/.dbg is not not available, will search for the .mab file
- if no .mab is available, will search for a .mab appended to the .exe/.dll
- if nothing is available, will log as hexadecimal pointers, without debugging information
function AbsoluteToOffset(aAddressAbsolute: PtrUInt): integer;
Compute the relative memory address from its absolute (pointer) value
class function FindFileName(const unitname: RawUtf8): TFileName;
Load .map/.gdb info and returns the file name of a given unit
- if unitname = '', returns the main file name of the current executable
function FindLocation(aAddressAbsolute: PtrUInt): RawUtf8; overload;
Return the symbol location according to the supplied absolute address
- filename, symbol name and line number (if any), as plain text, e.g. '4cb765 ../src/core/mormot.core.base.pas statuscodeissuccess (11183)' on FPC
- returns only the hexadecimal value if no match is found in .map/.gdb info
class function FindLocation(exc: ESynException): RawUtf8; overload;
Load .map/.gdb info and return the symbol location according to the supplied ESynException
- i.e. unit name, symbol name and line number (if any), as plain text
function FindLocationShort(aAddressAbsolute: PtrUInt): ShortString;
Return the symbol location according to the supplied absolute address
- filename, symbol name and line number (if any), as plain text, e.g. '4cb765 ../src/core/mormot.core.base.pas statuscodeissuccess (11183)' on FPC
- returns only the hexadecimal value if no match is found in .map/.gdb info
- won't allocate any heap memory during the text creation
- mormot.core.os.pas' GetExecutableLocation() redirects to this method
function FindSymbol(aAddressOffset: integer): PtrInt;
Retrieve a symbol according to a relative code address
- use fast O(log n) binary search
function FindUnit(aAddressOffset: integer; out LineNumber: integer): PtrInt; overload;
Retrieve an unit and source line, according to a relative code address
- use fast O(log n) binary search
function FindUnit(const aUnitName: RawUtf8): PtrInt; overload;
Retrieve an unit information, according to the unit name
- will search within Units array
function FindUnit(aAddressOffset: integer): PtrInt; overload;
Retrieve an unit, according to a relative code address
- use fast O(log n) binary search
function IsCode(aAddressAbsolute: PtrUInt): boolean;
Check if this memory address is part of the code segments
class function Log(W: TTextWriter; aAddressAbsolute: PtrUInt; AllowNotCodeAddr: boolean; SymbolNameNotFilename: boolean = false): boolean;
Add some debugging information about the supplied absolute memory address
- create a global TDebugFile instance for the current process, if needed
- if no debugging information is available (.map/.dbg/.mab), will write the raw address pointer as hexadecimal
function SaveToFile(const aFileName: TFileName = ''): TFileName;
Save all debugging information in the .mab custom binary format
- if no file name is specified, it will be saved as ExeName.mab or DllName.mab
- this file content can be appended to the executable via SaveToExe method
- this function returns the created file name
procedure SaveToExe(const aExeName: TFileName);
Append all debugging information to an executable (or library)
- the executable name must be specified, because it's impossible to write to the executable of a running process
- this method will work for .exe and for .dll (or .ocx)
procedure SaveToJson(const aJsonFile: TFileName; aJsonFormat: TTextWriterJsonFormat = jsonCompact); overload;
Save all debugging information as a JSON file
- may be useful from debugging purposes
procedure SaveToJson(W: TTextWriter); overload;
Save all debugging information as JSON content
- may be useful from debugging purposes
procedure SaveToStream(aStream: TStream);
Save all debugging informat in our custom binary format
property FileName: TFileName read fDebugFile;
The associated file name
- e.g. 'exec.map', 'exec.dbg' or even plain 'exec'/'exec.exe'
property HasDebugInfo: boolean read fHasDebugInfo;
Equals true if a .map/.dbg or .mab debugging information has been loaded
property Symbols: TDebugSymbolDynArray read fSymbol;
All symbols, mainly function and method names and addresses
property Units: TDebugUnitDynArray read fUnit;
All units, including line numbers, associated to the executable
ESynLogException = class(ESynException)
Class of Exceptions raised by this unit
ESynLogSilent = class(ESynException)
An exception which wouldn't be logged and intercepted by this unit
- only this exact class will be recognized by TSynLog: inheriting it will trigger the interception, as any other regular exception
ISynLog = interface(IUnknown)
A generic interface used for logging a method
- you should create one TSynLog instance at the beginning of a block code using TSynLog.Enter: the ISynLog will be released automaticaly by the compiler at the end of the method block, marking it's executation end
- all logging expect UTF-8 encoded text, i.e. usualy English text
function Instance: TSynLog;
Retrieve the associated logging instance
procedure Log(Level: TSynLogLevel; const aName: RawUtf8; aTypeInfo: PRttiInfo; const aValue; Instance: TObject); overload;
Call this method to add the content of most low-level types to the log at a specified level
- TSynLog will handle enumerations and dynamic array; TSqlLog will be able to write TObject/TOrm and sets content as JSON
procedure Log(Level: TSynLogLevel = sllTrace); overload;
Call this method to add the caller address to the log at the specified level
- if the debugging info is available from TDebugFile, will log the unit name, associated symbol and source code line
procedure Log(Level: TSynLogLevel; const TextFmt: RawUtf8; const TextArgs: array of const; Instance: TObject = nil); overload;
Call this method to add some information to the log at a specified level
- will use TTextWriter.Add(...,twOnSameLine) to append its content
- % = #37 indicates a string, integer, floating-point, class parameter to be appended as text (e.g. class name), any variant as JSON...
- note that cardinal values should be type-casted to Int64() (otherwise the integer mapped value will be transmitted, therefore wrongly)
- if Instance is set, it will log the corresponding class name and address (to be used if you didn't call TSynLog.Enter() method first)
procedure Log(Level: TSynLogLevel; const Text: RawUtf8; Instance: TObject = nil; TextTruncateAtLength: integer = maxInt); overload;
Call this method to add some information to the log at a specified level
- if Instance is set and Text is not '', it will log the corresponding class name and address (to be used e.g. if you didn't call TSynLog.Enter() method first)
- if Instance is set and Text is '', will behave the same as Log(Level,Instance), i.e. write the Instance as JSON content
procedure Log(Level: TSynLogLevel; Instance: TObject); overload;
Call this method to add the content of an object to the log at a specified level
- TSynLog will write the class and hexa address - TSqlLog will write the object JSON content
procedure LogLines(Level: TSynLogLevel; LinesToLog: PUtf8Char; aInstance: TObject = nil; const IgnoreWhenStartWith: PAnsiChar = nil);
Call this method to add some multi-line information to the log at a specified level
- LinesToLog content will be added, one line per one line, delimited by #13#10 (CRLF)
- if a line starts with IgnoreWhenStartWith (already uppercase), it won't be added to the log content (to be used e.g. with '--' for SQL statements)
TSynLogFamily = class(TObject)
Regroup several logs under an unique family name
- you should usualy use one family per application or per architectural module: e.g. a server application may want to log in separate files the low-level Communication, the DB access, and the high-level process
- initialize the family settings before using them, like in this code:
with TSynLogDB.Family do begin Level := LOG_VERBOSE; PerThreadLog := ptOneFilePerThread; DestinationPath := 'C:\Logs'; end;
- then use the logging system inside a method:
procedure TMyDB.MyMethod; var ILog: ISynLog; begin ILog := TSynLogDB.Enter(self,'MyMethod'); // do some stuff ILog.Log(sllInfo,'method called'); end; // when ILog is out-of-scope, will log the method leaving
constructor Create(aSynLog: TSynLogClass);
Intialize for a TSynLog class family
- add it in the global SynLogFileFamily[] list
destructor Destroy; override;
Close any console echo, and release associated memory
function Add: TSynLog;
Retrieve the corresponding log file of this thread and family
- calls GetLog if needed (e.g. at startup or if fGlobalLog is not set)
function GetExistingLog(MaximumKB: cardinal): RawUtf8;
Can be used to retrieve up to a specified amount of KB of existing log
- expects a single file to be opened for this family
- will retrieve the log content for the current file, truncating the text up to the specified number of KB (an up to 128 MB at most)
procedure ArchiveOldFiles(sourcePath: TFileName = ''; destPath: TFileName = ''; archiveDays: integer = -1);
Clean up *.log file by running OnArchive() on deprecated files
- will find and archive DestinationPath\*.log (or sourcePath\*.log) files older than ArchiveAfterDays (or archiveDays), into the ArchivePath (or destPath) folder
- was previously done in Destroy, but it makes better sense to run it only when needed (least astonishment principle), and with customization
procedure EchoRemoteStart(aClient: TObject; const aClientEvent: TOnTextWriterEcho; aClientOwnedByFamily: boolean);
Register one object and one echo callback for remote logging
- aClient is typically a mORMot's TRestHttpClient or a TSynLogCallbacks instance as defined in this unit
- if aClientOwnedByFamily is TRUE, its life time will be manage by this TSynLogFamily: it will stay alive until this TSynLogFamily is destroyed, or the EchoRemoteStop() method called
- aClientEvent should be able to send the log row to the remote server
procedure EchoRemoteStop;
Stop echo remote logging
- will free the aClient instance supplied to EchoRemoteStart
procedure OnThreadEnded(Sender: TThread);
Callback to notify the current logger that its thread is finished
- method follows TOnNotifyThread signature, which can be assigned to TSynBackgroundThreadAbstract.OnAfterExecute
- is called e.g. by TRest.EndCurrentThread
- just a wrapper around TSynLog.NotifyThreadEnded
property ArchiveAfterDays: integer read fArchiveAfterDays write fArchiveAfterDays;
Number of days before OnArchive event will be called to compress or delete deprecated files
- will be set by default to 7 days
- will be used by Destroy to call OnArchive event handler on time
property ArchivePath: TFileName read fArchivePath write fArchivePath;
The folder where old log files must be compressed
- by default, is in the executable folder, i.e. the same as DestinationPath
- you can use a remote folder (e.g. on a file server) as backup target
- the 'log\' sub folder name will always be appended to this value
- will then be used by OnArchive event handler to produce, with the current file date year and month, the final path (e.g. 'ArchivePath\Log\YYYYMM\*.log.synlz' or 'ArchivePath\Log\YYYYMM.zip')
property AutoFlushTimeOut: cardinal read fAutoFlushTimeOut write fAutoFlushTimeOut;
The time (in seconds) after which the log content must be written on disk, whatever the current content size is
- by default, the log file will be written for every 4 KB of log (see BufferSize property) - this will ensure that the main application won't be slow down by logging
- in order not to loose any log, a background thread can be created and will be responsible of flushing all pending log content every period of time (e.g. every 10 seconds)
property BufferSize: integer read fBufferSize write fBufferSize;
The internal in-memory buffer size, in bytes
- this is the number of bytes kept in memory before flushing to the hard drive; you can call TSynLog.Flush method or set AutoFlushTimeOut > 0 in order to force the writing to disk
- is set to 4096 by default (4 KB is the standard hard drive cluster size)
property CustomFileName: TFileName read fCustomFileName write fCustomFileName;
Can be used to customized the default file name
- by default, the log file name is computed from the executable name (and the computer/user name if IncludeComputerNameInFileName or IncludeUserNameInFileName are true)
- you can specify your own file name here, to be used instead
- this file name should not contain any folder, nor file extension (which are set by DestinationPath and DefaultExtension properties)
property DefaultExtension: TFileName read fDefaultExtension write fDefaultExtension;
The file extension to be used
- is '.log' by default
property DestinationPath: TFileName read fDestinationPath write SetDestinationPath;
The folder where the log must be stored
- by default, is in the executable folder
property EchoCustom: TOnTextWriterEcho read fEchoCustom write SetEchoCustom;
Can be set to a callback which will be called for each log line
- could be used with a third-party logging system
- EchoToConsole or EchoCustom can be activated separately
- you may even disable the integrated file output, via NoFile := true
property EchoToConsole: TSynLogLevels read fEchoToConsole write SetEchoToConsole;
If the some kind of events shall be echoed to the console
- note that it will slow down the logging process a lot (console output is slow by nature under Windows, but may be convenient for interactive debugging of services, for instance) - see EchoToConsoleBackground
- this property shall be set before any actual logging, otherwise it will have no effect
- can be set e.g. to LOG_VERBOSE in order to echo every kind of events
- EchoCustom or EchoToConsole can be activated separately
property EchoToConsoleBackground: boolean read fEchoToConsoleBackground write fEchoToConsoleBackground;
EchoToConsole output is sent from the flush background thread
- enabled by default on Windows, since its console output is very slow
property EchoToConsoleUseJournal: boolean read fEchoToConsoleUseJournal write SetEchoToConsoleUseJournal;
Redirect all EchoToConsole logging into the Linux journald service
- do nothing on Windows or BSD systems
- such logs can be exported into a format which can be viewed by our LogView tool using the following command (replacing UNIT with your unit name and PROCESS with the executable name):
"journalctl -u UNIT --no-hostname -o short-iso-precise --since today | grep "PROCESS\[.*\]: . " > todaysLog.log"
property EndOfLineCRLF: boolean read fEndOfLineCRLF write fEndOfLineCRLF;
Define how the logger will emit its line feed
- by default (FALSE), a single LF (#10) char will be written, to save storage space
- you can set this property to TRUE, so that CR+LF (#13#10) chars will be appended instead
- TSynLogFile class and our LogView tool will handle both patterns
property ExceptionIgnore: TList read fExceptionIgnore;
You can add some exceptions to be ignored to this list
- for instance, EConvertError may be added to the list, as such:
TSqlLog.Family.ExceptionIgnore.Add(EConvertError);
- you may also trigger ESynLogSilent exceptions for silent process
- see also ExceptionIgnoreCurrentThread property, if you want a per-thread filtering of all exceptions
property ExceptionIgnoreCurrentThread: boolean read GetExceptionIgnoreCurrentThread write SetExceptionIgnoreCurrentThread;
Allow to (temporarly) ignore exceptions in the current thread
- this property will affect all TSynLogFamily instances, for the current thread
- may be used in a try...finally block e.g. when notifying the exception to a third-party service, or during a particular process
- see also ExceptionIgnore property - which is also checked in addition to this flag
property FileExistsAction: TSynLogExistsAction read fFileExistsAction write fFileExistsAction;
How existing log file shall be handled
property HighResolutionTimestamp: boolean read fHighResolutionTimestamp write fHighResolutionTimestamp;
If TRUE, will log high-resolution time stamp instead of ISO 8601 date and time
- this is less human readable, but allows performance profiling of your application on the customer side (using TSynLog.Enter methods)
- set to FALSE by default, or if RotateFileCount and RotateFileSizeKB / RotateFileDailyAtHour are set (the high resolution frequency is set in the log file header, so expects a single file)
property Ident: integer read fIdent;
Index in global SynLogFileFamily[] and SynLogLookupThreadVar[] lists
property IncludeComputerNameInFileName: boolean read fIncludeComputerNameInFileName write fIncludeComputerNameInFileName;
If TRUE, the log file name will contain the Computer name
- as '(MyComputer)' or '(UserName@MyComputer)' patterns
property IncludeUserNameInFileName: boolean read fIncludeUserNameInFileName write fIncludeUserNameInFileName;
If TRUE, the log file name will contain the User name
- as '(UserName)' or '(UserName@MyComputer)' patterns
property Level: TSynLogLevels read fLevel write SetLevel;
The current level of logging information for this family
- can be set e.g. to LOG_VERBOSE in order to log every kind of events
property LevelStackTrace: TSynLogLevels read fLevelStackTrace write fLevelStackTrace;
The levels which will include a stack trace of the caller
- by default, contains sllStackTrace,sllException,sllExceptionOS plus sllError,sllFail,sllLastError,sllDDDError
- exceptions will always trace the stack
property LocalTimestamp: boolean read fLocalTimestamp write fLocalTimestamp;
By default, time logging will use error-safe UTC values as reference
- you may set this property to TRUE to store local time instead
property NoEnvironmentVariable: boolean read fNoEnvironmentVariable write fNoEnvironmentVariable;
Force no environment variables to be written to the log file
- may be usefull if they contain some sensitive information
property NoFile: boolean read fNoFile write fNoFile;
Force no log to be written to any file
- may be usefull in conjunction e.g. with EchoToConsole or any other third-party logging component
property OnArchive: TSynLogArchiveEvent read fOnArchive write fOnArchive;
Event called to archive - i.e. compress and delete - .log files
- called by TSynLogFamily.Destroy with files older than ArchiveAfterDays, or by TSynLog.PerformRotation when some rotated files need to be deleted
- set this property to EventArchiveDelete in order to delete deprecated files, or EventArchiveSynLZ/EventArchiveLizard to archive the .log files into our proprietary SynLZ/Lizard format: resulting file name will be 'ArchivePath\log\YYYYMM\*.log.synlz/synliz' - use AlgoSynLZ.FileUnCompress or AlgoLizard.FileUnCompress functions to uncompress them
- if you use EventArchiveZip from mormot.core.zip, the log files will be archived in 'ArchivePath\log\YYYYMM.zip'
- this event handler will be called one time per .log file to archive, then one last time with aOldLogFileName='' in order to close any pending archive (used e.g. by EventArchiveZip to open the .zip only once)
property OnBeforeException: TOnBeforeException read fOnBeforeException write fOnBeforeException;
You can let exceptions be ignored from a callback
- if set and returns true, the given exception won't be logged
- execution of this event handler is protected via the logs global lock
- may be handy e.g. when working with code triggerring a lot of exceptions (e.g. Indy), where ExceptionIgnore could be refined
property OnRotate: TSynLogRotateEvent read fOnRotate write fOnRotate;
Event called to perform a custom file rotation
- will be checked by TSynLog.PerformRotation to customize the rotation process and do not perform the default step, if the callback returns TRUE
property PerThreadLog: TSynLogPerThreadMode read fPerThreadLog write fPerThreadLog;
Define how thread will be identified during logging process
- by default, ptMergedInOneFile will indicate that all threads are logged in the same file, in occurrence order (so multi-thread process on server side may be difficult to interpret)
- if RotateFileCount and RotateFileSizeKB/RotateFileDailyAtHour are set, will be ignored (internal thread list shall be defined for one process)
property RotateFileCount: cardinal read fRotateFileCount write fRotateFileCount;
Auto-rotation of logging files
- set to 0 by default, meaning no rotation
- can be set to a number of rotating files: rotation and compression will happen, and main file size will be up to RotateFileSizeKB number of bytes, or when RotateFileDailyAtHour time is reached
- if set to 1, no .synlz backup will be created, so the main log file will be restarted from scratch when it reaches RotateFileSizeKB size or when RotateFileDailyAtHour time is reached
- if set to a number > 1, some rotated files will be compressed using the LogCompressAlgo algorithm (i.e. AlgoSynLZ by default but consider AlgoGZFast from mormot.core.zip to generate .gz files), and will be named e.g. as <MainLogFileName>.0.synlz .. <MainLogFileName>.7.synlz for RotateFileCount as 9 (i.e. total count = 9, including 1 main log file and 8 .synlz files)
property RotateFileDailyAtHour: integer read fRotateFileDailyAtHour write fRotateFileDailyAtHour;
Fixed hour of the day where logging files rotation should be performed
- by default, equals -1, meaning no rotation
- you can set a time value between 0 and 23 to force the rotation at this specified local (not UTC) hour
- is not used if RotateFileCount is left to its default 0
property RotateFileSizeKB: cardinal read fRotateFileSizeKB write fRotateFileSizeKB;
Maximum size of auto-rotated logging files, in kilo-bytes (per 1024 bytes)
- specify the maximum file size upon which .synlz rotation takes place
- is not used if RotateFileCount is left to its default 0
property StackTraceLevel: byte read fStackTraceLevel write fStackTraceLevel;
The recursive depth of stack trace symbol to write
- used only if exceptions are handled, or by sllStackTrace level
- default value is 30, maximum is 255 (but API may never reach so high)
property StackTraceUse: TSynLogStackTraceUse read fStackTraceUse write fStackTraceUse;
How the stack trace shall use only the Windows API
- default is stOnlyAPI, i.e. use RtlCaptureStackBackTrace() API with no manual stack walk (which tends to report wrong calls)
- on FPC, this property is ignored in favor of RTL CaptureBacktrace()
property SynLogClass: TSynLogClass read fSynLogClass;
The associated TSynLog class
property SynLogClassName: string read GetSynLogClassName;
The associated TSynLog class
property WithInstancePointer: boolean read fWithInstancePointer write fWithInstancePointer;
If TRUE, will log the pointer with an object instance class if available
- set to TRUE by default, for better debugging experience
property WithUnitName: boolean read fWithUnitName write fWithUnitName;
If TRUE, will log the unit name with an object instance if available
- unit name is available from RTTI if the class has published properties
- set to TRUE by default, for better debugging experience
property ZonedTimestamp: boolean read fZonedTimestamp write fZonedTimestamp;
By default, UTC values have no time zone
- you may set this property to TRUE to append a Z after the timestamp
TSynLogThreadRecursion = record
TSynLogThreadContext will define a dynamic array of such information
- used by TSynLog.Enter methods to handle recursivity calls tracing
Caller: PtrUInt;
The caller address, ready to display stack trace dump if needed
EnterTimestamp: Int64;
The high-resolution QueryPerformanceMicroSeconds timestamp at enter time
Instance: TObject;
Associated class instance to be displayed
MethodName: PUtf8Char;
Method name (or message) to be displayed
- may be a RawUtf8 if MethodNameLocal=mnEnterOwnMethodName
MethodNameLocal: TSynLogThreadMethodName;
If the method name is local, i.e. shall not be displayed at Leave()
RefCount: integer;
Internal reference count used at this recursion level by TSynLog._AddRef
TSynLogThreadContext = record
Thread-specific internal context used during logging
- this structure is a hashed-per-thread variable
ID: TThreadID;
The corresponding Thread ID
Recursion: array of TSynLogThreadRecursion;
Used by TSynLog.Enter methods to handle recursive calls tracing
RecursionCapacity: integer;
Number of items available in Recursion[]
- faster than length(Recursion)
RecursionCount: integer;
Number of items stored in Recursion[]
ThreadName: RawUtf8;
The associated thread name
- is probably more complete than CurrentThreadNameShort^ threadvar
TSynLog = class(TObject)
A per-family and/or per-thread log file content
- you should create a sub class per kind of log file
TSynLogDB = class(TSynLog);
- the TSynLog instance won't be allocated in heap, but will share a per-thread (if Family.PerThreadLog=ptOneFilePerThread) or global private log file instance
- was very optimized for speed, if no logging is written, and even during log write (using an internal TTextWriter)
- can use available debugging information via the TDebugFile class, for stack trace logging for exceptions, sllStackTrace, and Enter/Leave labelling
constructor Create(aFamily: TSynLogFamily = nil); virtual;
Intialize for a TSynLog class instance
- WARNING: not to be called directly! Use TSynLog.Enter or TSynLog.Add class functions instead
destructor Destroy; override;
Release all memory and internal handles
class function Add: TSynLog;
Retrieve the current instance of this TSynLog class
- to be used for direct logging, without any Enter/Leave:
TSynLogDB.Add.Log(llError,'The % statement didn''t work',[SQL]);
- to be used for direct logging, without any Enter/Leave (one parameter version - just the same as previous):
TSynLogDB.Add.Log(llError,'The % statement didn''t work',SQL);
- is just a wrapper around Family.SynLog - the same code will work:
TSynLogDB.Family.SynLog.Log(llError,'The % statement didn''t work',[SQL]);
class function Enter(aInstance: TObject = nil; aMethodName: PUtf8Char = nil; aMethodNameLocal: boolean = false): ISynLog; overload;
Handle generic method enter / auto-leave tracing
- returning a ISynLog interface will allow you to have an automated sllLeave log created when the method is left (thanks to the hidden try..finally block generated by the compiler to protect the ISynLog var)
- WARNING: due to a limitation (feature?) of the FPC compiler and Delphi 10.4 and later, you NEED to hold the returned value into a local ISynLog variable, as such:
procedure TMyDB.SQLFlush; var log: ISynLog; begin log := TSynLogDB.Enter(self); // do some stuff end; // here log will be released, and method leaving will be logged
otherwise, the ISynLog instance would be released just after the Enter() call, so the timing won't match the method execution
- as a benefit, it is convenient to define a local variable to store the returned ISynLog and use it for any specific logging within the method execution
- on Delphi earlier than 10.4 (and not FPC), you could just call Enter() inside the method block, without any ISynLog interface variable - but it is not very future-proof to write the following code:
procedure TMyDB.SQLFlush; begin TSynLogDB.Enter(self,'SQLFlush'); // do some stuff end;
- if no Method name is supplied, it will use the caller address, and will write it as hexa and with full unit and symbol name, if the debugging information is available (i.e. if TDebugFile retrieved the .map/.dbg content; note that this is not available yet on FPC):
procedure TMyDB.SQLFlush; var log: ISynLog; begin log := TSynLogDB.Enter(self); // do some stuff end;
- note that supplying a method name is faster than using the .map/.dbg content: if you want accurate profiling, or expect to support FPC, it's better to use a method name or not to relying on the .map/.dbg file - note that this method name shall be a constant, and not a locally computed variable, since it may trigger some random GPF at runtime - if it is a local variable, you can set aMethodNameLocal=true
- if TSynLogFamily.HighResolutionTimestamp is TRUE, high-resolution time stamp will be written instead of ISO 8601 date and time: this will allow performance profiling of the application on the customer side
- Enter() will write the class name (and the unit name for classes with published properties, if TSynLogFamily.WithUnitName is true) for both enter (+) and leave (-) events:
20110325 19325801 + MyDBUnit.TMyDB(004E11F4).SQLExecute 20110325 19325801 info SQL=SELECT * FROM Table; 20110325 19325801 - 01.512.320
class function Enter(const TextFmt: RawUtf8; const TextArgs: array of const; aInstance: TObject = nil): ISynLog; overload;
Handle method enter / auto-leave tracing, with some custom text arguments
- this overloaded method would not write the method name, but the supplied text content, after expanding the parameters like FormatUtf8()
- it will append the corresponding sllLeave log entry when the method ends
class function Family: TSynLogFamily; overload;
Retrieve the family of this TSynLog class type
function LastQueryPerformanceMicroSeconds: Int64;
Low-level latest value returned by QueryPerformanceMicroSeconds()
- is only accurate after Enter() or if HighResolutionTimestamp is set
function LogClass: TSynLogClass;
The associated TSynLog class
class function Void: TSynLogClass;
Returns a logging class which will never log anything
- i.e. a TSynLog sub-class with Family.Level := []
procedure CloseLogFile;
Flush all log content to file and close the file
class procedure DebuggerNotify(Level: TSynLogLevel; const Format: RawUtf8; const Args: array of const);
Low-level method helper which can be called to make debugging easier
- log some warning message to the TSynLog family
- will force a manual breakpoint if tests are run from the IDE
procedure DisableRemoteLog(entervalue: boolean);
Allow to temporary disable remote logging
- will enter the GlobalThreadLock - and is NOT rentrant
- to be used within a try ... finally section:
log.DisableRemoteLog(true); try log.Log(....); // won't be propagated to the remote log finally log.DisableRemoteLog(false); end;
class procedure DoLog(Level: TSynLogLevel; const Fmt: RawUtf8; const Args: array of const; Instance: TObject = nil);
Low-level class method which can be assigned to TSynLogProc callback signature, or used instead of Add.Log
- will flush the content to disk and avoid any memory reallocation if Level is sllExceptionOS, e.g. on SIGABRT/SIGQUIT/SIGINT
procedure Flush(ForceDiskWrite: boolean);
Flush all log content to file
- if ForceDiskWrite is TRUE, will wait until written on disk (slow)
procedure ForceRotation;
Force log rotation; Can be used for example inside SUGHUP signal handler
procedure Log(Level: TSynLogLevel; const aName: RawUtf8; aTypeInfo: PRttiInfo; const aValue; Instance: TObject); overload;
Call this method to add the content of most low-level types to the log at a specified level
- this overridden implementation will write the value content, written as human readable JSON: handle dynamic arrays and enumerations
procedure Log(Level: TSynLogLevel); overload;
Call this method to add the caller address to the log at the specified level
- if the debugging info is available from TDebugFile, will log the unit name, associated symbol and source code line
procedure Log(Level: TSynLogLevel; const TextFmt: RawUtf8; const TextArg: Int64; aInstance: TObject = nil); overload;
Same as Log(Level,TextFmt,[]) but with one Int64 parameter
procedure Log(Level: TSynLogLevel; const TextFmt: RawUtf8; const TextArgs: array of const; aInstance: TObject = nil); overload;
Call this method to add some information to the log at the specified level
- will use TTextWriter.Add(...,twOnSameLine) to append its content
- % = #37 indicates a string, integer, floating-point, class parameter to be appended as text (e.g. class name), any variant as JSON...
- note that cardinal values should be type-casted to Int64() (otherwise the integer mapped value will be transmitted, therefore wrongly)
procedure Log(Level: TSynLogLevel; const TextFmt: RawUtf8; const TextArg: RawUtf8; aInstance: TObject = nil); overload;
Same as Log(Level,TextFmt,[]) but with one RawUtf8 parameter
procedure Log(Level: TSynLogLevel; aInstance: TObject); overload;
Call this method to add the content of an object to the log at a specified level
- this default implementation will just write the class name and its hexa pointer value, and handle TList, TCollections and TStrings - for instance:
TSynLog.Add.Log(sllDebug,GarbageCollector);
will append this line to the log:
20110330 10010005 debug {"TObjectList(00B1AD60)":["TObjectList(00B1AE20)","TObjectList(00B1AE80)"]}- if aInstance is an Exception, it will handle its class name and Message:
20110330 10010005 debug "EClassName(00C2129A)":"Exception message"
procedure Log(Level: TSynLogLevel; const Text: RawUtf8; aInstance: TObject = nil; TextTruncateAtLength: integer = maxInt); overload;
Call this method to add some information to the log at the specified level
- if Instance is set and Text is not '', it will log the corresponding class name and address (to be used e.g. if you didn't call TSynLog.Enter() method first) - for instance
TSqlLog.Add.Log(sllDebug,'GarbageCollector',GarbageCollector);
will append this line to the log:
0000000000002DB9 debug TObjectList(00425E68) GarbageCollector
- if Instance is set and Text is '', will behave the same as Log(Level,Instance), i.e. write the Instance as JSON content
procedure LogLines(Level: TSynLogLevel; LinesToLog: PUtf8Char; aInstance: TObject = nil; const IgnoreWhenStartWith: PAnsiChar = nil);
Call this method to add some multi-line information to the log at a specified level
- LinesToLog content will be added, one line per one line, delimited by
13#10 (CRLF)
- if a line starts with IgnoreWhenStartWith (already uppercase), it won't be added to the log content (to be used e.g. with '--' for SQL statements)
procedure LogThreadName(const Name: RawUtf8);
Allows to identify the current thread with a textual representation
- would append an sllInfo entry with "SetThreadName ThreadID=Name" text
- entry would also be replicated at the begining of any rotated log file
- is called automatically by SetThreadName() global function
- if Name='', will use CurrentThreadNameShort^ threadvar
procedure ManualEnter(aMethodName: PUtf8Char; aInstance: TObject = nil; aMethodNameLocal: TSynLogThreadMethodName = mnEnter);
Manual low-level TSynLog.Enter execution without the ISynLog
- may be used to log Enter/Leave stack from non-pascal code
- each call to ManualEnter should be followed by a matching ManualLeave
- aMethodName should be a not nil constant text
procedure ManualLeave;
Manual low-level ISynLog release after TSynLog.Enter execution
- each call to ManualEnter should be followed by a matching ManualLeave
procedure NotifyThreadEnded;
To be called when a thread is ended, if SetThreadName() was also made
- should be called in the thread context which is about to terminate, in a situation where no other logging may occur from this thread any more
- it will release all thread-specific resource used by this TSynLog
- called e.g. by TRest.EndCurrentThread, via TSynLogFamily.OnThreadEnded
class procedure ProgressInfo(Sender: TObject; Info: PProgressInfo);
Low-level class method which can be assigned to a TOnInfoProgress callback
- as used e.g. by TStreamRedirect.OnInfoProgress or TZipAbstract.OnProgress
procedure Release;
Flush all log content to file, close the file, and release the instance
- you should never call the Free method directly, since the instance is registered in a global TObjectList and an access violation may occur at application closing: you can use this Release method if you are sure that you won't need this TSynLog instance any more
- ensure there is no pending Leave element in a stack-allocated ISynLog (see below)
- can be used e.g. to release the instance when finishing a thread when Family.PerThreadLog=ptOneFilePerThread:
var TThreadLogger : TSynLogClass = TSynLog;
procedure TMyThread.Execute; var log : ISynLog; begin log := TThreadLogger.Enter(self); ... log := nil; // to force logging end of method TThreadLogger.SynLog.Release; end;
property FileName: TFileName read fFileName;
The associated file name containing the log
- this is accurate only with the default implementation of the class: any child may override it with a custom logging mechanism
property FileSize: Int64 read GetFileSize;
The current size, in bytes, of the associated file containing the log
property GenericFamily: TSynLogFamily read fFamily;
The associated logging family
property ThreadContextCount: integer read fThreadContextCount;
The current number of thread contexts associated with this instance
- doesn't match necessary the number of threads of the process, but the threads which are still marked as active for this TSynLog
- a huge number may therefore not indicate a potential "out of memory" error, but a broken logic with missing NotifyThreadEnded calls
property Writer: TJsonWriter read fWriter;
Direct access to the low-level writing content
- should usually not be used directly, unless you ensure it is safe
TSynLogExceptionInfo = record
Storage of the information associated with an intercepted exception
- as returned by GetLastException() function
Context: TSynLogExceptionContext;
Low-level calling context
- as used by TSynLogExceptionToStr callbacks
Message: string;
Associated Exception.Message content (if any)
ISynLogCallback = interface(IInvokable)
A mORMot-compatible calback definition
- used to notify a remote mORMot server via interface-based serivces for any incoming event, using e.g. TSynLogCallbacks.Subscribe
procedure Log(Level: TSynLogLevel; const Text: RawUtf8);
Each line of the TTextWriter internal instance will trigger this method
- similar to TOnTextWriterEcho, as defined in mormot.core.text
- an initial call with Level=sllNone and the whole previous Text may be transmitted, if ReceiveExistingKB is set for TSynLogCallbacks.Subscribe()
TSynLogCallback = record
Store a subscribe to ISynLogCallback
TSynLogCallbacks = class(TSynLocked)
Can manage a list of ISynLogCallback registrations
Registration: TSynLogCallbackDynArray;
Direct access to the registration storage
Registrations: TDynArray;
High-level access to the registration storage
TrackedLog: TSynLogFamily;
The TSynLog family actually associated with those callbacks
constructor Create(aTrackedLog: TSynLogFamily); reintroduce;
Initialize the registration storage for a given TSynLogFamily instance
destructor Destroy; override;
Finalize the registration storage for a given TSynLogFamily instance
function OnEcho(Sender: TEchoWriter; Level: TSynLogLevel; const Text: RawUtf8): boolean;
Notify a given log event
- matches the TOnTextWriterEcho signature
function Subscribe(const Levels: TSynLogLevels; const Callback: ISynLogCallback; ReceiveExistingKB: cardinal = 0): integer; virtual;
Register a callback for a given set of log levels
- you can specify a number of KB of existing log content to send to the monitoring tool, before the actual real-time process
procedure Unsubscribe(const Callback: ISynLogCallback); virtual;
Unregister a callback previously registered by Subscribe()
property Count: integer read fCount;
How many registrations are currently defined
TSynLogSettings = class(TSynPersistent)
Store simple log-related settings
- see also TDDDLogSettings in dddInfraSettings.pas and TSynDaemonSettings in mORMotService.pas, which may be more integrated
constructor Create; override;
Set some default values
procedure SetLog(aLogClass: TSynLogClass = nil);
Define the log information into the supplied TSynLog class
- if you don't call this method, the logging won't be initiated
property DestinationPath: TFileName read fDestinationPath write fDestinationPath;
Allow to customize where the logs should be written
- default is the system log folder (e.g. /var/log on Linux)
property Levels: TSynLogLevels read fLevels write fLevels;
The log levels to be used for the log file
- i.e. a combination of none or several logging event
- if "*" is serialized, unneeded sllNone won't be part of the set
- default is LOG_STACKTRACE
property LogClass: TSynLogClass read fLogClass;
Read-only access to the TSynLog class, if SetLog() has been called
property RotateFileCount: integer read fRotateFileCount write fRotateFileCount;
How many files will be rotated (default is 2)
TSynLogFileProc = record
Used by TSynLogFile to refer to a method profiling in a .log file
- i.e. map a sllEnter/sllLeave event in the .log file
Index: cardinal;
The index of the sllEnter event in the TSynLogFile.fLevels[] array
ProperTime: cardinal;
The time elapsed in this method and not in nested methods
- computed from Time property, minus the nested calls
Time: cardinal;
The associated time elapsed in this method (in micro seconds)
- computed from the sllLeave time difference (high resolution timer)
TSynLogFile = class(TMemoryMapText)
Used to parse a .log file, as created by TSynLog, into high-level data
- this particular TMemoryMapText class will retrieve only valid event lines (i.e. will fill EventLevel[] for each line <> sllNone)
- Count is not the global text line numbers, but the number of valid events within the file (LinePointers/Line/Strings will contain only event lines) - it will not be a concern, since the .log header is parsed explicitly
constructor Create; override;
Initialize internal structure
function EventCount(const aSet: TSynLogLevels): integer;
Return the number of matching events in the log
function EventDateTime(aIndex: integer): TDateTime;
Retrieve the date and time of an event
- returns 0 in case of an invalid supplied index
function EventString(index: integer; const replaceTabs: RawUtf8 = ''; maxutf8len: integer = 0; includeFirstColumns: boolean = false): string;
Retrieve the description text of an event, as native RTL string
- returns '' if supplied index is out of range
- if the text is not truly UTF-8 encoded, would use the current system codepage to create a valid string
- you may specify a text to replace all #9 characters occurrences
- is used e.g. in TMainLogView.ListDrawCell
function LineContains(const aUpperSearch: RawUtf8; aIndex: integer): boolean; override;
Returns TRUE if the supplied text is contained in the corresponding line
function ThreadName(ThreadID, CurrentLogIndex: integer): RawUtf8;
Returns the name of a given thread, according to the position in the log
function ThreadNames(CurrentLogIndex: integer): TRawUtf8DynArray;
Returns the name of all threads, according to the position in the log
- result[0] stores the name of ThreadID = 1
function ThreadRows(ThreadID: integer): cardinal;
Returns the number of occurrences of a given thread
procedure AddInMemoryLine(const aNewLine: RawUtf8); override;
Add a new line to the already parsed content
- overriden method which would identify the freq=%,%,% pseudo-header
procedure GetDays(out Days: TDateTimeDynArray);
Returns all days of this log file
- only available for low-resolution timestamp, i.e. Freq=0
procedure LogProcSort(Order: TLogProcSortOrder);
Sort the LogProc[] array according to the supplied order
property Arm32CPU: TArm32HwCaps read fArm32CPU;
The available 32-bit ARM CPU features, as recognized at program startup
property Arm64CPU: TArm64HwCaps read fArm64CPU;
The available 64-bit ARM CPU features, as recognized at program startup
property ComputerHost: RawUtf8 read fHost;
The computer host name in which the process was running on
property CPU: RawUtf8 read fCPU;
The computer CPU in which the process was running on
- returns e.g. '1*0-15-1027'
property DayChangeIndex: TIntegerDynArray read fDayChangeIndex;
The row indexes where the day changed
- only available for low-resolution timestamp, i.e. Freq=0
- if set, contains at least [0] if the whole log is over a single day
property DayCount: TIntegerDynArray read fDayCount;
The number of rows for each DayChangeIndex[] value
property DetailedOS: RawUtf8 read fOSDetailed;
The computer Operating System in which the process was running on
- returns e.g. '2.3=5.1.2600' for Windows XP
- under Linux, it will return the full system version, e.g. 'Ubuntu=Linux-3.13.0-43-generic#72-Ubuntu-SMP-Mon-Dec-8-19:35:44-UTC-2014'
property EventLevel: TSynLogLevelDynArray read fLevels;
Retrieve the level of an event
- is calculated by Create() constructor
- EventLevel[] array index is from 0 to Count-1
property EventLevelUsed: TSynLogLevels read fLevelUsed;
Retrieve all used event levels
- is calculated by Create() constructor
property EventText[index: integer]: RawUtf8 read GetEventText;
Retrieve the description text of an event
- returns '' if supplied index is out of range
- see also EventString() function, for direct UI display as RTL string
property EventThread: TWordDynArray read fThreads;
Retrieve all event thread IDs
- contains something if TSynLogFamily.PerThreadLog was ptIdentifiedInOneFile
- for ptMergedInOneFile (default) or ptOneFilePerThread logging process, the array will be void (EventThread=nil)
property ExecutableDate: TDateTime read fExeDate;
The associated executable build date and time
property ExecutableName: RawUtf8 read fExeName;
The associated executable name (with path)
- returns e.g. 'C:\Dev\lib\SQLite3\exe\TestSQL3.exe'
property ExecutableVersion: RawUtf8 read fExeVersion;
The associated executable version
- returns e.g. '0.0.0.0'
property Framework: RawUtf8 read fFramework;
The associated framework information
- returns e.g. 'TSynLog 2.0.1 x64MMs'
property Freq: Int64 read fFreq;
High-resolution time stamp frequence, as retrieved from log file header
- equals 0 if date time resolution, >0 if high-resolution time stamp
property Headers: RawUtf8 read fHeaders;
Custom headers, to be searched as .ini content
property InstanceName: RawUtf8 read fInstanceName;
For a library, the associated instance name (with path)
- returns e.g. 'C:\Dev\lib\SQLite3\exe\TestLibrary.dll'
- for an executable, will be left void
property IntelCPU: TIntelCpuFeatures read fIntelCPU;
The available Intel/AMD CPU features, as recognized at program startup
- is extracted from the last part of the CPU property text
- you could use the overloaded ToText() function to show it in an human-friendly way
property LevelUsed: TSynLogLevels read fLevelUsed;
All used event levels, as retrieved at log file content parsing
property LogProc: PSynLogFileProcArray read fLogProcCurrent;
Profiled methods information
- is calculated by Create() constructor
- will contain the sllEnter index, with the associated elapsed time
- number of items in the array is retrieved by the LogProcCount property
property LogProcCount: integer read fLogProcCurrentCount;
Number of profiled methods in this .log file
- i.e. number of items in the LogProc[] array
property LogProcMerged: boolean read fLogProcIsMerged write SetLogProcMerged;
If the method information must be merged for the same method name
property LogProcOrder: TLogProcSortOrder read fLogProcSortInternalOrder;
The current sort order
property OS: TWindowsVersion read fOS;
The computer Operating System in which the process was running on
- equals wUnknown on Linux or BSD - use DetailedOS instead
property RunningUser: RawUtf8 read fUser;
The computer user name who launched the process
property ServicePack: integer read fOSServicePack;
The Operating System Service Pack number
- not defined on Linux or BSD - use DetailedOS instead
property StartDateTime: TDateTime read fStartDateTime;
The date and time at which the log file was started
property ThreadsCount: cardinal read fThreadMax;
The number of threads
property Wow64: boolean read fWow64;
If the 32 bit process was running under WOW 64 virtual emulation
TSynLogFileView = class(TSynLogFile)
Used to parse a .log file and process into VCL/LCL/FMX
- would handle e.g. selection and search feature
function GetCell(aCol, aRow: integer; out aLevel: TSynLogLevel): string;
Returns the ready-to-be text of a cell of the main TDrawGrid
function GetLineForClipboard(aRow: integer): string;
Returns the ready-to-be copied text of a selected row
function GetLineForMemo(aRow, aTop, aBottom: integer): string;
Returns the ready-to-be displayed text of one or several selected rows
function SearchEnterLeave(aRow: integer): PtrInt;
Search for the matching Enter/Leave item, from the current row index
- returns -1 if no match was found
function SearchNextEvent(aEvent: TSynLogLevel; aRow: integer): PtrInt;
Search for the next matching TSynLogLevel, from the current row index
- returns -1 if no match was found
function SearchNextSameThread(aRow: integer): PtrInt;
Search for the next matching thread, from the current row index
- returns -1 if no match was found
function SearchNextSelected(aIndex: integer): PtrInt;
Search for the next row index, appearing after the supplied item index
- returns -1 if no match was found
function SearchNextText(const aPattern: RawUtf8; aRow, aDelta: integer): PtrInt;
Search for the next matching text, from the current row index
- returns -1 if no match was found
function SearchNextThread(aRow: integer): PtrInt;
Search for the next diverse thread, from the current row index
- returns -1 if no match was found
function SearchPreviousSameThread(aRow: integer): PtrInt;
Search for the previous matching thread, from the current row index
- returns -1 if no match was found
function SearchPreviousText(const aPattern: RawUtf8; aRow: integer): PtrInt;
Search for the previous matching text, from the current row index
- returns -1 if no match was found
function SearchThread(aThreadID: word; aRow: integer): PtrInt;
Search for the next specified thread, from the current row index
- returns -1 if no match was found
function Select(aRow: integer): integer; virtual;
Fill all rows matching Events and Threads[] properties in Selected[]
- you may specify the current selected row index, which would return the closest one after the selection has been applied
procedure AddInMemoryLine(const aNewLine: RawUtf8); override;
Add a new line to the already parsed content
- overriden method would add the inserted index to Selected[]
procedure SetAllThreads(enabled: boolean);
Set all Threads[] to a specified value
property Events: TSynLogLevels read fEvents write fEvents;
Define the current selection range, according to event kinds
- once you have set Events and Threads[], call Select() to fill Selected[]
property Selected: TIntegerDynArray read fSelected;
The row indexes of the selected entries
property SelectedCount: integer read fSelectedCount;
How many entries are currently stored in Selected[]
property Threads[thread: integer]: boolean read GetThreads write SetThreads;
Define the current selection range, according to a thread ID
- here the supplied thread ID starts at 1
- once you have set Events and Threads[], call Select() to fill Selected[]
PSynLogThreadContext = ^TSynLogThreadContext;
Pointer to thread-specific context information
TDebugSymbolDynArray = array of TDebugSymbol;
A dynamic array of symbols, as decoded by TDebugFile from a .map/.dbg file
TDebugUnitDynArray = array of TDebugUnit;
A dynamic array of units, as decoded by TDebugFile from a .map/.dbg file
TLogProcSortOrder = ( soNone, soByName, soByOccurrence, soByTime, soByProperTime );
Used by TSynLogFile.LogProcSort method
TOnBeforeException = function(const Context: TSynLogExceptionContext; const ThreadName: RawUtf8): boolean of object;
Callback signature used by TSynLogFamilly.OnBeforeException
- should return false to log the exception, or true to ignore it
TSynLogArchiveEvent = function(const aOldLogFileName, aDestinationPath: TFileName): boolean;
This event can be set for a TSynLogFamily to archive any deprecated log into a custom compressed format, i.e. compress and delete them
- called by TSynLogFamily.Destroy with files older than ArchiveAfterDays, or by TSynLog.PerformRotation when some rotated files need to be deleted
- the aOldLogFileName will contain the .log file with full path
- the aDestinationPath parameter will contain 'ArchivePath\log\YYYYMM\'
- should return true on success, false on error
- example of matching event handler are EventArchiveDelete, EventArchiveSynLZ, EventArchiveLizard or EventArchiveZip
- this event handler will be called one time per .log file to archive, then one last time with aOldLogFileName='' in order to close any pending archive (used e.g. by EventArchiveZip to open the .zip only once)
TSynLogCallbackDynArray = array of TSynLogCallback;
Store several subscribed ISynLogCallback
TSynLogClass = class of TSynLog;
Class-reference type (metaclass) of a TSynLog family
- since TSynLog classes store their information per type, you usually will store a reference to a logging family (i.e. logging settings) using a TSynLogClass variable, whereas TSynLog would point to the active logging instance
TSynLogExceptionInfoDynArray = array of TSynLogExceptionInfo;
Storage of information associated with one or several exceptions
- as returned by GetLastExceptions() function
TSynLogExistsAction = ( acOverwrite, acAppend );
How file existing shall be handled during logging
TSynLogFileProcDynArray = array of TSynLogFileProc;
Used by TSynLogFile to refer to global method profiling in a .log file
- i.e. map all sllEnter/sllLeave event in the .log file
TSynLogFilter = ( lfNone, lfAll, lfErrors, lfExceptions, lfProfile, lfDatabase, lfClientServer, lfDebug, lfCustom, lfDDD );
A list of lof events families, used to gather events by type
TSynLogPerThreadMode = ( ptMergedInOneFile, ptOneFilePerThread, ptIdentifiedInOneFile, ptNoThreadProcess );
How threading is handled by the TSynLogFamily
- proper threading expects the TSynLog.NotifyThreadEnded method to be called when a thread is about to terminate, e.g. from TRest.EndCurrentThread
- by default, ptMergedInOneFile will indicate that all threads are logged in the same file, in occurrence order
- if set to ptOneFilePerThread, it will create one .log file per thread
- if set to ptIdentifiedInOneFile, a new column will be added for each log row, with the corresponding ThreadID - LogView tool will be able to display per-thread logging, if needed - note that your application shall use a thread pool (just like all mORMot servers classes do), otherwise some random hash collision may occur if Thread IDs are not recycled enough
- if set to ptNoThreadProcess, no thread information is gathered, and all Enter/Leave would be merged into a single call - but it may be mandatory to use this option if TSynLog.NotifyThreadEnded is not called (e.g. from legacy code), and that your process experiment instability issues
TSynLogRotateEvent = function(aLog: TSynLog; const aOldLogFileName: TFileName): boolean;
This event can be set for a TSynLogFamily to customize the file rotation
- will be called by TSynLog.PerformRotation
- should return TRUE if the function did process the file name
- should return FALSE if the function did not do anything, so that the caller should perform the rotation as usual
TSynLogStackTraceUse = ( stOnlyAPI, stManualAndAPI, stOnlyManual );
How stack trace shall be computed during logging
- stOnlyAPI is the first (and default) value, since manual stack makes unexpected detections, and was reported as very slow on Windows 11
- on FPC, these values are ignored, because RTL CaptureBacktrace() is used
TSynLogThreadMethodName = ( mnAlways, mnEnter, mnLeave, mnEnterOwnMethodName );
Available options for TSynLogThreadRecursion.MethodNameLocal
- define if the method name is local, i.e. shall not be displayed at Leave()
TSynMapFile = TDebugFile;
Backward compatibility type redirection
TSyslogFacility = ( sfKern, sfUser, sfMail, sfDaemon, sfAuth, sfSyslog, sfLpr, sfNews, sfUucp, sfClock, sfAuthpriv, sfFtp, sfNtp, sfAudit, sfAlert, sfCron, sfLocal0, sfLocal1, sfLocal2, sfLocal3, sfLocal4, sfLocal5, sfLocal6, sfLocal7 );
Syslog message facilities as defined by RFC 3164
TSyslogSeverity = ( ssEmerg, ssAlert, ssCrit, ssErr, ssWarn, ssNotice, ssInfo, ssDebug );
Syslog message severities as defined by RFC 5424
LOG_DEBUGERROR: array[boolean] of TSynLogLevel = ( sllDebug, sllError);
LfNone lfAll lfErrors lfExceptions lfProfile lfDatabase lfClientServer lfDebug lfCustom lfDDD may be used to log as Debug or Error event, depending on an Error: boolean
LOG_FILTER: array[TSynLogFilter] of TSynLogLevels = ( [], [succ(sllNone) .. high(TSynLogLevel)], [sllError, sllLastError, sllException, sllExceptionOS], [sllException, sllExceptionOS], [sllEnter, sllLeave], [sllSQL, sllCache, sllDB], [sllClient, sllServer, sllServiceCall, sllServiceReturn], [sllDebug, sllTrace, sllEnter], [sllCustom1 .. sllCustom4], [sllDDDError, sllDDDInfo]);
SllNone sllInfo sllDebug sllTrace sllWarning sllError sllEnter sllLeave sllLastError sllException sllExceptionOS sllMemory sllStackTrace sllFail sllSQL sllCache sllResult sllDB sllHTTP sllClient sllServer sllServiceCall sllServiceReturn sllUserAuth sllCustom1 sllCustom2 sllCustom3 sllCustom4 sllNewRun sllDDDError sllDDDInfo sllMonitoring how TLogFilter map TSynLogLevel events
LOG_INFOWARNING: array[boolean] of TSynLogLevel = ( sllInfo, sllWarning);
May be used to log as Info or Warning event, depending on an Error: boolean
LOG_LEVEL_TEXT: array[TSynLogLevel] of string[7] = ( ' ', ' info ', ' debug ', ' trace ', ' warn ', ' ERROR ', ' + ', ' - ', ' OSERR ', ' EXC ', ' EXCOS ', ' mem ', ' stack ', ' fail ', ' SQL ', ' cache ', ' res ', ' DB ', ' http ', ' clnt ', ' srvr ', ' call ', ' ret ', ' auth ', ' cust1 ', ' cust2 ', ' cust3 ', ' cust4 ', ' rotat ', ' dddER ', ' dddIN ', ' mon ');
The text equivalency of each logging level, as written in the log file
- PCardinal(@LOG_LEVEL_TEXT[L][3])^ will be used for fast level matching so text must be unique for characters [3..6] -> e.g. 'UST4'
LOG_STACKTRACE: TSynLogLevels = [sllException, sllExceptionOS, sllLastError, sllError, sllDDDError];
Contains the logging levels for which stack trace should be dumped
- which are mainly exceptions or application errors
LOG_TO_SYSLOG: array[TSynLogLevel] of TSyslogSeverity = ( ssDebug, ssInfo, ssDebug, ssDebug, ssNotice, ssWarn, ssDebug, ssDebug, ssWarn, ssErr, ssErr, ssDebug, ssDebug, ssNotice, ssDebug, ssDebug, ssDebug, ssDebug, ssDebug, ssDebug, ssDebug, ssDebug, ssDebug, ssDebug, ssDebug, ssDebug, ssDebug, ssDebug, ssNotice, ssWarn, ssInfo, ssDebug);
Used to convert a TSynLog event level into a syslog message severity
LOG_TRACEERROR: array[boolean] of TSynLogLevel = ( sllTrace, sllError);
May be used to log as Trace or Error event, depending on an Error: boolean
LOG_VERBOSE: TSynLogLevels = [succ(sllNone)..high(TSynLogLevel)];
Can be set to TSynLogFamily.Level in order to log all available events
MAX_SYNLOGFAMILY = 15;
Up to 16 TSynLogFamily, i.e. TSynLog children classes can be defined
| Functions or procedures | Description | |
|---|---|---|
| EventArchiveDelete | A TSynLogArchiveEvent handler which will just delete older .log files | |
| EventArchiveSynLZ | A TSynLogArchiveEvent handler which will compress older .log files using our proprietary SynLZ format | |
| GetLastException | Makes a thread-safe copy of the latest intercepted exception | |
| GetLastExceptions | Makes a thread-safe copy of the latest intercepted exceptions | |
| GetLastExceptionText | Returns some text about the latest intercepted exception | |
| RetrieveMemoryManagerInfo | Retrieve a one-line of text including detailed heap information | |
| SyslogMessage | SllNone sllInfo sllDebug sllTrace sllWarning sllError sllEnter sllLeave sllLastError sllException sllExceptionOS sllMemory sllStackTrace sllFail sllSQL sllCache sllResult sllDB sllHTTP sllClient sllServer sllServiceCall sllServiceReturn sllUserAuth sllCustom1 sllCustom2 sllCustom3 sllCustom4 sllNewRun sllDDDError sllDDDInfo sllMonitoring append some information to a syslog message memory buffer | |
| ToCaption | Returns the ready-to-be displayed text of a TSynLogLevel value | |
| ToCaption | Returns the ready-to-be displayed text of a TSynLogFilter value | |
| ToText | Returns a method event as text, using the .map/.dbg/.mab information if available | |
| ToText | Convert low-level exception information into some human-friendly text | |
| ToText | Returns the trimmed text value of a logging level | |
| ToText | Returns the trimmed text value of a logging levels set |
function EventArchiveDelete( const aOldLogFileName, aDestinationPath: TFileName): boolean;
A TSynLogArchiveEvent handler which will just delete older .log files
function EventArchiveSynLZ( const aOldLogFileName, aDestinationPath: TFileName): boolean;
A TSynLogArchiveEvent handler which will compress older .log files using our proprietary SynLZ format
- resulting file will have the .synlz extension and will be located in the aDestinationPath directory, i.e. TSynLogFamily.ArchivePath+'\log\YYYYMM\'
- use UnSynLZ.dpr tool to uncompress it into .log textual file
- SynLZ is much faster than zip for compression content, but proprietary
function GetLastException(out info: TSynLogExceptionInfo): boolean;
Makes a thread-safe copy of the latest intercepted exception
procedure GetLastExceptions(out result: TSynLogExceptionInfoDynArray; Depth: integer = 0); overload;
Makes a thread-safe copy of the latest intercepted exceptions
function GetLastExceptionText: RawUtf8;
Returns some text about the latest intercepted exception
function RetrieveMemoryManagerInfo: RawUtf8;
Retrieve a one-line of text including detailed heap information
- will use the RTL status entrypoint, or detect mormot.core.fpcx64mm and retrieve all its available information
- as used by TSynLog.AddMemoryStats
function SyslogMessage(facility: TSyslogFacility; severity: TSyslogSeverity; const msg, procid, msgid: RawUtf8; destbuffer: PUtf8Char; destsize: PtrInt; trimmsgfromlog: boolean): PtrInt;
SllNone sllInfo sllDebug sllTrace sllWarning sllError sllEnter sllLeave sllLastError sllException sllExceptionOS sllMemory sllStackTrace sllFail sllSQL sllCache sllResult sllDB sllHTTP sllClient sllServer sllServiceCall sllServiceReturn sllUserAuth sllCustom1 sllCustom2 sllCustom3 sllCustom4 sllNewRun sllDDDError sllDDDInfo sllMonitoring append some information to a syslog message memory buffer
- following https://tools.ietf.org/html/rfc5424 specifications
- ready to be sent via UDP to a syslog remote server
- returns the number of bytes written to destbuffer (which should have destsize > 127)
function ToCaption(filter: TSynLogFilter): string; overload;
Returns the ready-to-be displayed text of a TSynLogFilter value
function ToCaption(event: TSynLogLevel): string; overload;
Returns the ready-to-be displayed text of a TSynLogLevel value
function ToText(event: TSynLogLevel): RawUtf8; overload;
Returns the trimmed text value of a logging level
- i.e. 'Warning' for sllWarning
function ToText(events: TSynLogLevels): ShortString; overload;
Returns the trimmed text value of a logging levels set
function ToText(const Event: TMethod): RawUtf8; overload;
Returns a method event as text, using the .map/.dbg/.mab information if available
function ToText(var info: TSynLogExceptionInfo): RawUtf8; overload;
Convert low-level exception information into some human-friendly text
GlobalCurrentHandleExceptionSynLog: TSynLog;
Low-level variable used internally by this unit
- do not access this variable in your code: defined here to allow inlining
GlobalThreadLock: TRTLCriticalSection;
Low-level variable used internally by this unit
- we use a process-wide giant lock to avoid proper multi-threading of logs
- do not access this variable in your code: defined here to allow inlining
LOG_CONSOLE_COLORS: array[TSynLogLevel] of TConsoleColor = ( ccLightGray, ccWhite, ccLightGray, ccLightBlue, ccBrown, ccLightRed, ccGreen, ccGreen, ccLightRed, ccLightRed, ccLightRed, ccLightGray, ccCyan, ccLightRed, ccBrown, ccBlue, ccLightCyan, ccMagenta, ccCyan, ccLightCyan, ccLightCyan, ccLightMagenta, ccLightMagenta, ccMagenta, ccLightGray, ccLightGray, ccLightGray, ccLightGray, ccLightMagenta, ccLightRed, ccWhite, ccLightBlue);
SllNone sllInfo sllDebug sllTrace sllWarning sllError sllEnter sllLeave sllLastError sllException sllExceptionOS sllMemory sllStackTrace sllFail sllSQL sllCache sllResult sllDB sllHTTP sllClient sllServer sllServiceCall sllServiceReturn sllUserAuth sllCustom1 sllCustom2 sllCustom3 sllCustom4 sllNewRun sllDDDError sllDDDInfo sllMonitoring black/white text corresponding to each colored background: sllNone sllInfo sllDebug sllTrace sllWarning sllError sllEnter sllLeave sllLastError sllException sllExceptionOS sllMemory sllStackTrace sllFail sllSQL sllCache sllResult sllDB sllHTTP sllClient sllServer sllServiceCall sllServiceReturn sllUserAuth sllCustom1 sllCustom2 sllCustom3 sllCustom4 sllNewRun sllDDDError sllDDDInfo sllMonitoring console colors corresponding to each logging level
- to be used with mormot.core.os TextColor()
- is defined as var and not const to allow customization at runtime
LOG_LEVEL_COLORS: array[boolean, TSynLogLevel] of integer = ( ($FFFFFF, $DCC0C0, $DCDCDC, $C0C0C0, $8080C0, $8080FF, $C0DCC0, $DCDCC0, $C0C0F0, $C080FF, $C080F0, $C080C0, $C080C0, $4040FF, $B08080, $B0B080, $8080DC, $80DC80, $DC8080, $DCFF00, $DCD000, $DCDC80, $DC80DC, $DCDCDC, $D0D0D0, $D0D0DC, $D0D0C0, $D0D0E0, $20E0D0, $8080FF, $DCCDCD, $C0C0C0), ($000000, $000000, $000000, $000000, $000000, $FFFFFF, $000000, $000000, $FFFFFF, $FFFFFF, $FFFFFF, $000000, $000000, $FFFFFF, $FFFFFF, $000000, $FFFFFF, $000000, $000000, $000000, $000000, $000000, $000000, $000000, $000000, $000000, $000000, $000000, $000000, $FFFFFF, $000000, $000000));
SllNone sllInfo sllDebug sllTrace sllWarning sllError sllEnter sllLeave sllLastError sllException sllExceptionOS sllMemory sllStackTrace sllFail sllSQL sllCache sllResult sllDB sllHTTP sllClient sllServer sllServiceCall sllServiceReturn sllUserAuth sllCustom1 sllCustom2 sllCustom3 sllCustom4 sllNewRun sllDDDError sllDDDInfo sllMonitoring RGB colors corresponding to each logging level
- matches the TColor values, as used by the VCL/LCL
- first array is for the background, second is for the text (black/white)
- is defined as var and not const to allow customization at runtime
SynLogFileFreeing: boolean;
Is set to TRUE before ObjArrayClear(SynLogFile) in unit finalization
- defined here to avoid unexpected GPF at shutdown