2014-05-18 18:09:07 +00:00
|
|
|
unit X2Log.Observer.LogFile;
|
|
|
|
|
|
|
|
interface
|
|
|
|
uses
|
|
|
|
System.Classes,
|
|
|
|
System.Generics.Collections,
|
|
|
|
System.SyncObjs,
|
|
|
|
|
|
|
|
X2Log.Intf,
|
|
|
|
X2Log.Observer.Custom,
|
|
|
|
X2Log.Observer.CustomThreaded;
|
|
|
|
|
|
|
|
|
|
|
|
type
|
|
|
|
TX2LogFileObserver = class(TX2LogCustomThreadedObserver)
|
|
|
|
private
|
|
|
|
FFileName: string;
|
|
|
|
protected
|
|
|
|
function CreateWorkerThread: TX2LogObserverWorkerThread; override;
|
|
|
|
public
|
|
|
|
constructor Create(const AFileName: string; ALogLevels: TX2LogLevels = X2LogLevelsDefault);
|
|
|
|
constructor CreateInProgramData(const AFileName: string; ALogLevels: TX2LogLevels = X2LogLevelsDefault);
|
|
|
|
constructor CreateInUserAppData(const AFileName: string; ALogLevels: TX2LogLevels = X2LogLevelsDefault);
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
implementation
|
|
|
|
uses
|
|
|
|
System.IOUtils,
|
|
|
|
System.SysUtils,
|
|
|
|
System.Win.ComObj,
|
|
|
|
Winapi.SHFolder,
|
|
|
|
Winapi.Windows,
|
|
|
|
|
|
|
|
X2Log.Constants;
|
|
|
|
|
|
|
|
|
|
|
|
type
|
|
|
|
TX2LogFileWorkerThread = class(TX2LogObserverWorkerThread)
|
|
|
|
private
|
|
|
|
FFileName: string;
|
|
|
|
protected
|
|
|
|
procedure ProcessEntry(AEntry: TX2LogQueueEntry); override;
|
|
|
|
|
|
|
|
property FileName: string read FFileName;
|
|
|
|
public
|
|
|
|
constructor Create(const AFileName: string);
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{ TX2LogFileObserver }
|
|
|
|
constructor TX2LogFileObserver.Create(const AFileName: string; ALogLevels: TX2LogLevels);
|
|
|
|
begin
|
|
|
|
FFileName := AFileName;
|
|
|
|
|
|
|
|
inherited Create(ALogLevels);
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
constructor TX2LogFileObserver.CreateInProgramData(const AFileName: string; ALogLevels: TX2LogLevels);
|
|
|
|
var
|
|
|
|
path: PWideChar;
|
|
|
|
|
|
|
|
begin
|
|
|
|
GetMem(path, MAX_PATH);
|
|
|
|
try
|
|
|
|
OleCheck(SHGetFolderPath(0, CSIDL_COMMON_APPDATA, 0, SHGFP_TYPE_CURRENT, path));
|
|
|
|
Create(IncludeTrailingPathDelimiter(path) + AFileName, ALogLevels);
|
|
|
|
finally
|
|
|
|
FreeMem(path);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
constructor TX2LogFileObserver.CreateInUserAppData(const AFileName: string; ALogLevels: TX2LogLevels);
|
|
|
|
var
|
|
|
|
path: PWideChar;
|
|
|
|
|
|
|
|
begin
|
|
|
|
GetMem(path, MAX_PATH);
|
|
|
|
try
|
|
|
|
OleCheck(SHGetFolderPath(0, CSIDL_APPDATA, 0, SHGFP_TYPE_CURRENT, path));
|
|
|
|
Create(IncludeTrailingPathDelimiter(path) + AFileName, ALogLevels);
|
|
|
|
finally
|
|
|
|
FreeMem(path);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TX2LogFileObserver.CreateWorkerThread: TX2LogObserverWorkerThread;
|
|
|
|
begin
|
|
|
|
Result := TX2LogFileWorkerThread.Create(FFileName);
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
{ TX2LogFileWorkerThread }
|
|
|
|
constructor TX2LogFileWorkerThread.Create(const AFileName: string);
|
|
|
|
begin
|
|
|
|
FFileName := AFileName;
|
|
|
|
|
|
|
|
inherited Create;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TX2LogFileWorkerThread.ProcessEntry(AEntry: TX2LogQueueEntry);
|
|
|
|
var
|
|
|
|
baseReportFileName: string;
|
|
|
|
errorMsg: string;
|
|
|
|
detailsExtension: string;
|
|
|
|
detailsFile: THandle;
|
|
|
|
detailsFileStream: THandleStream;
|
|
|
|
detailsFileName: string;
|
|
|
|
detailsNumber: Integer;
|
|
|
|
writer: TStreamWriter;
|
2014-05-30 12:51:01 +00:00
|
|
|
logDetailsStreamable: IX2LogDetailsStreamable;
|
2014-05-18 18:09:07 +00:00
|
|
|
|
|
|
|
begin
|
|
|
|
ForceDirectories(ExtractFilePath(FileName));
|
|
|
|
errorMsg := AEntry.Message;
|
|
|
|
|
2014-05-30 12:51:01 +00:00
|
|
|
if Supports(AEntry.Details, IX2LogDetailsStreamable, logDetailsStreamable) then
|
2014-05-18 18:09:07 +00:00
|
|
|
begin
|
|
|
|
detailsExtension := ExtractFileExt(FileName);
|
2014-10-08 12:33:11 +00:00
|
|
|
baseReportFileName := ChangeFileExt(FileName, '_' + FormatDateTime(GetLogResourceString(@LogFileNameDateFormat), AEntry.DateTime));
|
2014-05-18 18:09:07 +00:00
|
|
|
detailsFileName := baseReportFileName + detailsExtension;
|
|
|
|
detailsNumber := 0;
|
|
|
|
|
|
|
|
if ForceDirectories(ExtractFilePath(detailsFileName)) then
|
|
|
|
begin
|
|
|
|
repeat
|
|
|
|
{ TFileStream lacks the ability to create a file only when it does not exist }
|
|
|
|
detailsFile := CreateFile(PChar(detailsFileName), GENERIC_READ or GENERIC_WRITE,
|
|
|
|
FILE_SHARE_READ or FILE_SHARE_WRITE, nil, CREATE_NEW,
|
|
|
|
FILE_ATTRIBUTE_NORMAL, 0);
|
|
|
|
|
|
|
|
if detailsFile = INVALID_HANDLE_VALUE then
|
|
|
|
begin
|
|
|
|
if GetLastError = ERROR_FILE_EXISTS then
|
|
|
|
begin
|
|
|
|
{ Generate a new file name }
|
|
|
|
Inc(detailsNumber);
|
|
|
|
detailsFileName := Format('%s_%d%s', [baseReportFileName, detailsNumber,
|
|
|
|
detailsExtension]);
|
|
|
|
end else
|
|
|
|
break;
|
|
|
|
end else
|
|
|
|
begin
|
|
|
|
{ Details file succesfully generated }
|
|
|
|
try
|
|
|
|
detailsFileStream := THandleStream.Create(detailsFile);
|
|
|
|
try
|
2014-05-30 12:51:01 +00:00
|
|
|
logDetailsStreamable.SaveToStream(detailsFileStream);
|
2014-05-18 18:09:07 +00:00
|
|
|
finally
|
|
|
|
FreeAndNil(detailsFileStream);
|
|
|
|
end;
|
|
|
|
finally
|
|
|
|
CloseHandle(detailsFile);
|
|
|
|
end;
|
|
|
|
|
|
|
|
// ErrorLogs.Add(reportFileName);
|
|
|
|
|
|
|
|
errorMsg := errorMsg + Format(GetLogResourceString(@LogFileLineDetails), [ExtractFileName(detailsFileName)]);
|
|
|
|
break;
|
|
|
|
end;
|
|
|
|
until False;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
{ Append line to log file }
|
|
|
|
writer := TFile.AppendText(FileName);
|
|
|
|
try
|
2014-10-08 12:33:11 +00:00
|
|
|
writer.WriteLine('[' + FormatDateTime(GetLogResourceString(@LogFileLineDateFormat), AEntry.DateTime) + '] ' +
|
2014-05-18 18:09:07 +00:00
|
|
|
GetLogLevelText(AEntry.Level) + ': ' + errorMsg);
|
|
|
|
finally
|
|
|
|
FreeAndNil(writer);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
end.
|