Logging¶
TTLogger provides structured SQL logging for debugging and monitoring. It is defined in Trysil.Logger.pas.
Overview¶
Trysil logs all SQL operations -- transactions, parameters, generated syntax, executed commands, and errors -- through a global TTLogger singleton. Log items carry connection and thread identifiers for multi-threaded correlation.
Log Events¶
Each log item has a TTLoggerEvent type:
| Event | Description |
|---|---|
StartTransaction |
A transaction was started |
Commit |
A transaction was committed |
Rollback |
A transaction was rolled back |
Parameter |
A SQL parameter name and value |
Syntax |
Generated SQL syntax (before execution) |
Command |
Executed SQL command |
Error |
An error occurred during execution |
Log Item Structure¶
TTLoggerItem is a record carrying:
| Field | Type | Description |
|---|---|---|
ID |
TTLoggerItemID |
Correlation identifier |
Event |
TTLoggerEvent |
The event type |
Values |
TArray<String> |
Event-specific string values |
TTLoggerItemID¶
Each TTLoggerItemID contains:
- ConnectionID (
String) -- A UUID generated byTTGenericConnectionthat uniquely identifies the database connection. - ThreadID (
TThreadID) -- The ID of the thread that created the log item.
Together, these allow correlating all SQL operations from a single connection across threads in a multi-threaded server.
Registering a Logger¶
To enable logging, register a custom logger thread class with the global TTLogger.Instance:
Optionally specify the thread pool size:
The default thread pool size is 1. Increasing it distributes log processing across multiple threads using round-robin (TTRoundRobin).
Creating a Custom Logger¶
Extend TTLoggerThread and override the abstract methods:
type
TFileLoggerThread = class(TTLoggerThread)
strict protected
procedure LogStartTransaction(const AID: TTLoggerItemID); override;
procedure LogCommit(const AID: TTLoggerItemID); override;
procedure LogRollback(const AID: TTLoggerItemID); override;
procedure LogParameter(
const AID: TTLoggerItemID;
const AName: String;
const AValue: String); override;
procedure LogSyntax(
const AID: TTLoggerItemID;
const ASyntax: String); override;
procedure LogCommand(
const AID: TTLoggerItemID;
const ASyntax: String); override;
procedure LogError(
const AID: TTLoggerItemID;
const AMessage: String); override;
end;
procedure TFileLoggerThread.LogCommand(
const AID: TTLoggerItemID; const ASyntax: String);
begin
WriteLn(Format('[%s][%d] COMMAND: %s', [
AID.ConnectionID, AID.ThreadID, ASyntax]));
end;
// ... implement other methods similarly
TTLoggerQueue¶
TTLoggerThread uses an internal TTLoggerQueue -- a thread-safe queue for TTLoggerItem records:
// Internal consumer pattern (inside TTLoggerThread.Execute)
while not FQueue.IsEmpty do
begin
LItem := FQueue.Dequeue;
Log(LItem);
end;
The queue uses a critical section for thread safety. The logger thread waits on an event object and wakes up when new items are enqueued.
Global Singleton¶
TTLogger is a class-level singleton created in a class constructor and destroyed in a class destructor:
If no logger thread is registered, log calls are silently ignored (the round-robin returns nil).
Log Methods¶
The TTLogger instance provides convenience methods:
TTLogger.Instance.LogStartTransaction(LConnectionID);
TTLogger.Instance.LogCommit(LConnectionID);
TTLogger.Instance.LogRollback(LConnectionID);
TTLogger.Instance.LogParameter(LConnectionID, 'Lastname', 'Smith');
TTLogger.Instance.LogSyntax(LConnectionID, 'SELECT * FROM Persons WHERE ID = :ID');
TTLogger.Instance.LogCommand(LConnectionID, 'SELECT * FROM Persons WHERE ID = 42');
These are called automatically by TTGenericConnection and its subclasses during normal ORM operations. You typically do not need to call them manually unless you are logging custom SQL executed outside the ORM.