-
Notifications
You must be signed in to change notification settings - Fork 100
Expand file tree
/
Copy pathLoggerPro.MemoryAppender.pas
More file actions
216 lines (189 loc) · 5.33 KB
/
Copy pathLoggerPro.MemoryAppender.pas
File metadata and controls
216 lines (189 loc) · 5.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
// *************************************************************************** }
//
// LoggerPro
//
// Copyright (c) 2010-2026 Daniele Teti
//
// https://github.com/danieleteti/loggerpro
//
// ***************************************************************************
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// ***************************************************************************
unit LoggerPro.MemoryAppender;
interface
uses
LoggerPro,
System.SysUtils,
System.Generics.Collections,
System.SyncObjs;
type
{ @abstract(A thread-safe ring buffer appender that keeps the last N log items in memory)
Useful for:
- Displaying recent logs in a UI (ListView, Memo)
- Dumping logs on crash/error
- Unit testing
- In-app log viewer
}
TLoggerProMemoryRingBufferAppender = class(TLoggerProAppenderBase)
private
FLogItems: TList<TLogItem>;
FMaxSize: Integer;
FLock: TCriticalSection;
public
const DEFAULT_MAX_SIZE = 1000;
constructor Create(aMaxSize: Integer = DEFAULT_MAX_SIZE; aLogItemRenderer: ILogItemRenderer = nil); reintroduce;
destructor Destroy; override;
procedure Setup; override;
procedure TearDown; override;
procedure WriteLog(const aLogItem: TLogItem); override;
{ Returns a copy of all log items currently in the buffer.
Caller is responsible for freeing the returned list and its items. }
function GetLogItems: TList<TLogItem>;
{ Returns a copy of log items filtered by tag.
Caller is responsible for freeing the returned list and its items. }
function GetLogItemsByTag(const aTag: string): TList<TLogItem>;
{ Returns a copy of log items filtered by log type.
Caller is responsible for freeing the returned list and its items. }
function GetLogItemsByType(aLogType: TLogType): TList<TLogItem>;
{ Returns formatted log strings for all items in buffer }
function GetAsStringList: TArray<string>;
{ Returns the number of items currently in the buffer }
function Count: Integer;
{ Clears all items from the buffer }
procedure Clear;
property MaxSize: Integer read FMaxSize;
end;
implementation
{ TLoggerProMemoryRingBufferAppender }
constructor TLoggerProMemoryRingBufferAppender.Create(aMaxSize: Integer; aLogItemRenderer: ILogItemRenderer);
begin
inherited Create(aLogItemRenderer);
if aMaxSize < 1 then
raise ELoggerPro.CreateFmt('MaxSize must be >= 1, got %d', [aMaxSize]);
FMaxSize := aMaxSize;
FLock := TCriticalSection.Create;
FLogItems := TList<TLogItem>.Create;
end;
destructor TLoggerProMemoryRingBufferAppender.Destroy;
begin
Clear;
FLogItems.Free;
FLock.Free;
inherited;
end;
procedure TLoggerProMemoryRingBufferAppender.Setup;
begin
inherited;
end;
procedure TLoggerProMemoryRingBufferAppender.TearDown;
begin
inherited;
end;
procedure TLoggerProMemoryRingBufferAppender.WriteLog(const aLogItem: TLogItem);
var
lOldItem: TLogItem;
begin
FLock.Enter;
try
// If buffer is full, remove oldest item
while FLogItems.Count >= FMaxSize do
begin
lOldItem := FLogItems[0];
FLogItems.Delete(0);
lOldItem.Free;
end;
// Add clone of log item (original will be freed by logger)
FLogItems.Add(aLogItem.Clone);
finally
FLock.Leave;
end;
end;
function TLoggerProMemoryRingBufferAppender.GetLogItems: TList<TLogItem>;
var
I: Integer;
begin
Result := TList<TLogItem>.Create;
FLock.Enter;
try
for I := 0 to FLogItems.Count - 1 do
Result.Add(FLogItems[I].Clone);
finally
FLock.Leave;
end;
end;
function TLoggerProMemoryRingBufferAppender.GetLogItemsByTag(const aTag: string): TList<TLogItem>;
var
I: Integer;
begin
Result := TList<TLogItem>.Create;
FLock.Enter;
try
for I := 0 to FLogItems.Count - 1 do
if FLogItems[I].LogTag = aTag then
Result.Add(FLogItems[I].Clone);
finally
FLock.Leave;
end;
end;
function TLoggerProMemoryRingBufferAppender.GetLogItemsByType(aLogType: TLogType): TList<TLogItem>;
var
I: Integer;
begin
Result := TList<TLogItem>.Create;
FLock.Enter;
try
for I := 0 to FLogItems.Count - 1 do
if FLogItems[I].LogType = aLogType then
Result.Add(FLogItems[I].Clone);
finally
FLock.Leave;
end;
end;
function TLoggerProMemoryRingBufferAppender.GetAsStringList: TArray<string>;
var
I: Integer;
begin
FLock.Enter;
try
SetLength(Result, FLogItems.Count);
for I := 0 to FLogItems.Count - 1 do
Result[I] := FormatLog(FLogItems[I]);
finally
FLock.Leave;
end;
end;
function TLoggerProMemoryRingBufferAppender.Count: Integer;
begin
FLock.Enter;
try
Result := FLogItems.Count;
finally
FLock.Leave;
end;
end;
procedure TLoggerProMemoryRingBufferAppender.Clear;
var
I: Integer;
begin
FLock.Enter;
try
for I := FLogItems.Count - 1 downto 0 do
FLogItems[I].Free;
FLogItems.Clear;
finally
FLock.Leave;
end;
end;
end.