YOUSEFUL / DELPHI / COMPONENTS
TAtlasFileSink
Writes UTF-8 lines to a file, with size rotation, daily files and retention.
Description
Each event becomes one line in the sink's Layout and Pattern, UTF-8 without a byte order mark, ending in CR LF. The file is opened on the first event, appended to if it exists, and kept open with share-deny-write: other programs can read it while it is open (with fmOpenRead or fmShareDenyNone), but not write to it. Missing folders are created.
With FileName = 'logs\app.log':
MaxBytes: before the write that would pass the limit,app.logis renamedapp.1.log, the previousapp.1.logbecomesapp.2.log, and so on; no file exceeds the limit, and a single line longer than the limit gets a file to itself.MaxFiles: an archive that would land beyond this number is deleted instead (0 keeps all).Daily: the active file isapp.2026-09-21.log, by the local date of each event; withMaxFiles, older files are deleted by modification time when a new file is started. Combined withMaxBytes, a day's archives areapp.2026-09-21.1.log, and so on.
A failed write closes the file and raises (the logger counts it); the next event opens it afresh. Not implemented: compression of archives and retention by total size or age. Behaviour on a full disk or with a file locked by another writer has not been tested.
Members
| Member | Kind | What it does |
|---|---|---|
Destroy | destructor | Closes the file and leaves the logger. |
Close | method | Closes the file now, for example so another program can move it; the next event reopens it and appends. |
ActiveFileName | method | The file being written, built from FileName (relative if FileName is), or an empty string while the file is closed. |
FileName | property | The log file. |
MaxBytes | property | The size limit per file in bytes; 0 means no size rotation. |
MaxFiles | property | How many archives to keep besides the active file: rotated .1, .2 ... |
Daily | property | One file per day, named with the local date of each event (app.2026-09-21.log). |
Create | constructor from TAtlasTextSink | Creates the sink with no file name, no limits, Layout = loText and the default pattern. |
Layout | property from TAtlasTextSink | loText (default) writes each event with Pattern; loJson writes one JSON object per event (see TAtlasJsonFormatter). |
Pattern | property from TAtlasTextSink | The text pattern for loText, ignored for loJson. |
Logger | property from TAtlasLogSink | The logger this sink receives events from. |
MinLevel | property from TAtlasLogSink | The sink's own threshold: events below it are ignored by this sink only. |
Write | method from TAtlasLogSink | Writes one event. |
Flush | method from TAtlasLogSink | Pushes buffered output out, under the sink's lock. |
Constructors
Create constructorinherited from TAtlasTextSink
constructor Create(AOwner: TComponent); override;Creates the sink with no file name, no limits, Layout = loText and the default pattern.
Destructors
Destroy destructor
destructor Destroy; override;Closes the file and leaves the logger.
Properties
FileName property
property FileName: string read FFileName write SetFileName;The log file. A relative name is resolved against the current directory when the file is opened. Changing it closes the current file. Without a name, every write raises EAtlasLogError (The file sink has no FileName), which the logger counts in SinkErrors.
Files.FileName := TPath.Combine(TPath.GetDocumentsPath, 'MyApp\app.log');MaxBytes property
property MaxBytes: Int64 read FMaxBytes write SetMaxBytes default 0;The size limit per file in bytes; 0 means no size rotation. Negative values raise EAtlasLogError.
Files.MaxBytes := 10 * 1024 * 1024;MaxFiles property
property MaxFiles: Integer read FMaxFiles write SetMaxFiles default 0;How many archives to keep besides the active file: rotated .1, .2 ... files, or with Daily the older files matching the name. 0 keeps everything. Negative values raise EAtlasLogError.
Files.MaxFiles := 10;Daily property
property Daily: Boolean read FDaily write SetDaily default False;One file per day, named with the local date of each event (app.2026-09-21.log). The switch happens with the first event of a new day. Changing the property closes the current file.
Files.Daily := True;Layout propertyinherited from TAtlasTextSink
property Layout: TAtlasLogLayout read FLayout write FLayout default loText;loText (default) writes each event with Pattern; loJson writes one JSON object per event (see TAtlasJsonFormatter).
Sink.Layout := loJson;Pattern propertyinherited from TAtlasTextSink
property Pattern: string read GetPattern write SetPattern stored IsPatternStored;The text pattern for loText, ignored for loJson. Default {time:yyyy-mm-dd hh:nn:ss.zzz} [{level:short}] {category} {scope} {message}{exception}{props}; assigning an empty string restores it. Tokens are listed under TAtlasTextFormatter. An unknown token, an unterminated { or a single } raises EAtlasLogError and keeps the previous pattern. Line breaks in values are always escaped as \n in sinks. Set it before the sink starts receiving events.
Sink.Pattern := '{time:hh:nn:ss} {level:short} {category} {message}{exception}';Logger propertyinherited from TAtlasLogSink
property Logger: TAtlasLogger read FLogger write SetLogger;The logger this sink receives events from. Setting it registers the sink with that logger (and removes it from the previous one); nil detaches it. The same as calling TAtlasLogger.AddSink and RemoveSink. Either side may be destroyed first: the other is told and lets go.
Sink.Logger := Log;MinLevel propertyinherited from TAtlasLogSink
property MinLevel: TAtlasLogLevel read FMinLevel write FMinLevel default llTrace;The sink's own threshold: events below it are ignored by this sink only. It applies after the logger's threshold, so it can only narrow what the logger lets through. Default llTrace.
Files.MinLevel := llInfo; // the file skips debug and traceMethods
Close method
procedure Close;Closes the file now, for example so another program can move it; the next event reopens it and appends.
ActiveFileName method
function ActiveFileName: string;The file being written, built from FileName (relative if FileName is), or an empty string while the file is closed. With Daily it includes the date.
Writeln(Files.ActiveFileName); // logs\app.2026-09-21.logWrite methodinherited from TAtlasLogSink
procedure Write(const Event: TAtlasLogEvent);Writes one event. The logger calls it; you may call it too, for example to replay events from a TAtlasMemorySink into another sink. Events below MinLevel and events at llOff are ignored; otherwise DoWrite runs under the sink's lock. Unlike the logger, Write itself may raise when the output fails.
Flush methodinherited from TAtlasLogSink
procedure Flush;Pushes buffered output out, under the sink's lock. TAtlasLogger.Flush calls it on every sink and counts failures; called directly it may raise. For a file sink it asks Windows to write the file's buffers to disk (FlushFileBuffers).
Examples
Size rotation with ten archives
At most eleven files of 10 MB each: app.log and app.1.log to app.10.log.
uses System.SysUtils, System.IOUtils, Atlas.Log, Atlas.LogSinks;
function AddRotatingFile(Log: TAtlasLogger): TAtlasFileSink;
begin
Result := TAtlasFileSink.Create(Log);
Result.FileName := TPath.Combine(TPath.GetHomePath, 'MyApp\logs\app.log');
Result.MaxBytes := 10 * 1024 * 1024;
Result.MaxFiles := 10;
Result.MinLevel := llInfo;
Result.Logger := Log;
end;
Daily JSON files
One JSON-lines file per day, keeping fourteen older days.
uses Atlas.Log, Atlas.LogSinks;
function AddDailyJson(Log: TAtlasLogger): TAtlasFileSink;
begin
Result := TAtlasFileSink.Create(Log);
Result.FileName := 'C:\ProgramData\MyApp\logs\events.jsonl';
Result.Daily := True; // events.2026-09-21.jsonl
Result.MaxFiles := 14;
Result.Layout := loJson;
Result.Logger := Log;
end;
Read the file while it is open
The sink denies other writers, so readers must allow writing.
uses System.SysUtils, System.Classes, Atlas.LogSinks;
function ReadActiveLog(Files: TAtlasFileSink): string;
var
Stream: TFileStream;
Bytes: TBytes;
begin
Result := '';
if Files.ActiveFileName = '' then Exit; // closed: nothing written since
Stream := TFileStream.Create(Files.ActiveFileName, fmOpenRead or fmShareDenyNone);
try
SetLength(Bytes, Stream.Size);
if Length(Bytes) > 0 then Stream.ReadBuffer(Bytes[0], Length(Bytes));
finally
Stream.Free;
end;
Result := TEncoding.UTF8.GetString(Bytes);
end;
Related
TAtlasTextSink · TAtlasAsyncSink · TAtlasLogLayout · TAtlasTextFormatter · TAtlasJsonFormatter
Also in Atlas Log
TAtlasLogger · TAtlasMemorySink · TAtlasCallbackSink · TAtlasConsoleSink · TAtlasDebugSink · TAtlasAsyncSink · TAtlasLogView