MyLog.pas 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. {$I+}
  2. unit MyLog;
  3. {*******************************} interface {**********************************}
  4. Uses
  5. System.SysUtils,
  6. System.Variants,
  7. System.IOUtils,
  8. System.SyncObjs,
  9. System.Diagnostics,
  10. System.Classes,
  11. System.TypInfo;
  12. const
  13. UseAppendMode = False;
  14. // (true) Append mode: Single-file mode. The file is opened only when writing.
  15. // (false) Normal mode: The file is open all the time
  16. var
  17. WriteTimeToLog :Boolean = True;
  18. WriteMillisecondsToLog :Boolean = False;
  19. WriteTreadIDToLog :Boolean = False;
  20. LogElementsSeparator :String = ' ';
  21. Procedure WriteMyLog(const aStr :String); overload;
  22. Procedure WriteMyLog(const aArgs :array of const; const aHeader :String = ''); overload;
  23. // Using: WriteMyLogEx(['vMin',vMin,'vMax',vMax], 'Testing') -> Testing vMin=20 vMax=40
  24. // Set<->String miscellaneous functions
  25. function SetToString(Info :PTypeInfo; const SetParam; Brackets :Boolean = true) :String;
  26. // Using: S:=SetToString(TypeInfo(TMySet), MSet, True);
  27. procedure StringToSet(Info :PTypeInfo; var SetParam; const Value :String);
  28. {*****************************} implementation {*******************************}
  29. var
  30. fInLog : TCriticalSection;
  31. fFS : TFormatSettings;
  32. fStopWatch : TStopwatch;
  33. fLogFile : TextFile;
  34. fLogOpened : Boolean;
  35. fFileBuf : array[1..4096] of Char;
  36. fLogFileName : string;
  37. procedure AddStr(var aStr :String; const aAddStr :String); inline;
  38. begin
  39. aStr := aStr + aAddStr;
  40. end;
  41. {$region 'WriteMyLog'}
  42. Procedure WriteMyLog(const aStr :String); overload;
  43. begin
  44. if not fLogOpened then
  45. Exit;
  46. var vFullStr :String;
  47. if WriteTimeToLog or WriteMillisecondsToLog then begin
  48. vFullStr := '[';
  49. if WriteTimeToLog then
  50. AddStr(vFullStr, DateTimeToStr(Now, fFS));
  51. if WriteMillisecondsToLog then begin
  52. if WriteTimeToLog then
  53. AddStr(vFullStr, '-');
  54. AddStr(vFullStr, IntToStr(fStopWatch.ElapsedMilliseconds));
  55. end;
  56. AddStr(vFullStr, ']' + aStr);
  57. end else
  58. vFullStr := aStr;
  59. if WriteTreadIDToLog then
  60. AddStr(vFullStr, '{' + TThread.CurrentThread.ToString + '_' + UIntToStr(TThread.CurrentThread.ThreadID) + '}');
  61. fInLog.Acquire;
  62. try
  63. try
  64. if UseAppendMode then begin // Append mode
  65. AssignFile(fLogFile, fLogFileName);
  66. if FileExists(fLogFileName) then
  67. Append(fLogFile)
  68. else
  69. Rewrite(fLogFile);
  70. try
  71. Writeln(fLogFile, vFullStr);
  72. finally
  73. CloseFile(fLogFile);
  74. end;
  75. end else begin // Normal mode
  76. Writeln(fLogFile, vFullStr);
  77. Flush(fLogFile);
  78. end;
  79. except
  80. // Ignoring all exceptions
  81. end;{try}
  82. finally
  83. fInLog.Release;
  84. end;
  85. end;
  86. Procedure WriteMyLog(const aArgs :array of const; const aHeader :String = ''); overload;
  87. var
  88. vArgument :Boolean;
  89. vStr :String;
  90. procedure locIncStr(const aAddStr :String);
  91. begin
  92. if vArgument then
  93. AddStr(vStr, aAddStr)
  94. else
  95. AddStr(vStr, '=' + aAddStr + LogElementsSeparator);
  96. end;
  97. function locPointerToStr(aPtr :Pointer) :String;
  98. begin
  99. if aPtr = nil then
  100. Exit('nil')
  101. else
  102. Exit(IntToHex(UIntPtr(aPtr)));
  103. end;
  104. function locObjectToStr(aObj :TObject) :String;
  105. begin
  106. if aObj=nil then
  107. Exit('[]nil')
  108. else
  109. Exit('[' + aObj.ClassName + ']' + locPointerToStr(aObj));
  110. end;
  111. begin
  112. if not fLogOpened then
  113. Exit;
  114. vArgument := True;
  115. vStr := aHeader;
  116. if vStr<>'' then
  117. AddStr(vStr, LogElementsSeparator);
  118. for var I := Low(aArgs) to High(aArgs) do begin
  119. with aArgs[I] do begin
  120. case VType of
  121. vtUnicodeString : locIncStr(String(UnicodeString(VUnicodeString)));
  122. vtAnsiString : locIncStr(String(AnsiString(VAnsiString)));
  123. vtWideString : locIncStr(String(WideString(VWideString)));
  124. vtString : locIncStr(String(VString^));
  125. vtInteger : locIncStr(IntToStr(VInteger));
  126. vtInt64 : locIncStr(IntToStr(VInt64^));
  127. vtExtended : locIncStr(FloatToStr(VExtended^, fFS));
  128. vtBoolean : locIncStr(BoolToStr(VBoolean, true));
  129. vtCurrency : locIncStr(CurrToStr(VCurrency^, fFS));
  130. vtObject : locIncStr(locObjectToStr(VObject));
  131. vtClass : locIncStr(VClass.ClassName);
  132. vtPointer : locIncStr(locPointerToStr(vPointer));
  133. vtVariant : locIncStr(VarToStr(VVariant^));
  134. vtInterface : locIncStr(locPointerToStr(vInterface));
  135. vtChar : locIncStr(String(AnsiString(VChar)));
  136. vtPChar : locIncStr(String(AnsiString(VPChar^)));
  137. vtWideChar : locIncStr(String(WideString(VWideChar)));
  138. vtPWideChar : locIncStr(String(WideString(VPWideChar^)));
  139. end;{case}
  140. end;{with}
  141. vArgument := not vArgument;
  142. end;{for i}
  143. WriteMyLog(vStr);
  144. end;
  145. {$endregion 'WriteMyLog'}
  146. {$region 'Init/Done'}
  147. procedure InitLogSubSystem;
  148. begin
  149. fLogOpened := True;
  150. try
  151. var vTmpPath := TPath.GetTempPath + PathDelim + 'MyLog';
  152. CreateDir(vTmpPath);
  153. AddStr(vTmpPath, PathDelim + ExtractFileName(GetModuleName(HInstance)));
  154. if UseAppendMode then
  155. fLogFileName := vTmpPath + '.log'
  156. else begin
  157. var vInd := 0;
  158. repeat
  159. fLogFileName := vTmpPath + '.' + IntToStr(vInd) + '.log';
  160. inc(vInd);
  161. until not FileExists(fLogFileName);
  162. AssignFile(fLogFile, fLogFileName);
  163. System.SetTextBuf(fLogFile, fFileBuf);
  164. ReWrite(fLogFile);
  165. end;
  166. fInLog := TCriticalSection.Create;
  167. except
  168. fLogOpened := False;
  169. end;{try}
  170. if fLogOpened then begin
  171. fFS := FormatSettings;
  172. fFS.DecimalSeparator := '.';
  173. fFS.DateSeparator := '/';
  174. fFS.ShortDateFormat := 'dd/mm/yy';
  175. fFS.LongDateFormat := fFS.ShortDateFormat;
  176. fFS.TimeSeparator := ':';
  177. fFS.ShortTimeFormat := 'hh:nn:ss';
  178. fFS.LongTimeFormat := 'hh:nn:ss';
  179. fStopWatch := TStopwatch.StartNew;
  180. end;
  181. end;
  182. procedure DoneLogSystem;
  183. begin
  184. try
  185. if fLogOpened then begin
  186. fLogOpened := False;
  187. if not UseAppendMode then
  188. CloseFile(fLogFile);
  189. fInLog.Free;
  190. end;
  191. except
  192. {Ignoring}
  193. end;
  194. end;
  195. {$endregion 'Init/Done'}
  196. {$region 'Set<->String conversion'}
  197. function GetOrdValue(Info: PTypeInfo; const SetParam): Integer;
  198. begin
  199. Result := 0;
  200. case GetTypeData(Info)^.OrdType of
  201. otSByte, otUByte:
  202. Result := Byte(SetParam);
  203. otSWord, otUWord:
  204. Result := Word(SetParam);
  205. otSLong, otULong:
  206. Result := Integer(SetParam);
  207. end;
  208. end;
  209. procedure SetOrdValue(Info: PTypeInfo; var SetParam; Value: Integer);
  210. begin
  211. case GetTypeData(Info)^.OrdType of
  212. otSByte, otUByte:
  213. Byte(SetParam) := Value;
  214. otSWord, otUWord:
  215. Word(SetParam) := Value;
  216. otSLong, otULong:
  217. Integer(SetParam) := Value;
  218. end;
  219. end;
  220. function SetToString(Info :PTypeInfo; const SetParam; Brackets :Boolean = true): String;
  221. begin
  222. Result := '';
  223. var S :TIntegerSet;
  224. Integer(S) := GetOrdValue(Info, SetParam);
  225. var TypeInfo := GetTypeData(Info)^.CompType^;
  226. for var I := 0 to SizeOf(Integer) * 8 - 1 do
  227. if I in S then begin
  228. if Result <> '' then
  229. Result := Result + ',';
  230. Result := Result + GetEnumName(TypeInfo, I);
  231. end;
  232. if Brackets then
  233. Result := '[' + Result + ']';
  234. end;
  235. procedure StringToSet(Info: PTypeInfo; var SetParam; const Value: String);
  236. var
  237. P :PChar;
  238. function locNextWord(var aP :PChar): String;
  239. begin
  240. var I := 0;
  241. // scan til whitespace
  242. while not CharInSet(aP[I],[',', ' ', #0,']']) do
  243. Inc(I);
  244. SetString(Result, aP, I);
  245. // skip whitespace
  246. while CharInSet(aP[I],[',', ' ',']']) do
  247. Inc(I);
  248. Inc(aP, I);
  249. end;
  250. begin
  251. SetOrdValue(Info, SetParam, 0);
  252. if Value = '' then
  253. Exit;
  254. P := PChar(Value);
  255. // skip leading bracket and whitespace
  256. while CharInSet(P^,['[',' ']) do
  257. Inc(P);
  258. var EnumInfo :PTypeInfo := GetTypeData(Info)^.CompType^;
  259. var SetValue :Longint := 0;
  260. var EnumName := locNextWord(P);
  261. while EnumName <> '' do begin
  262. var EnumValue :Longint := GetEnumValue(EnumInfo, EnumName);
  263. if EnumValue < 0 then begin
  264. SetOrdValue(Info, SetParam, 0);
  265. Exit;
  266. end;
  267. Include(TIntegerSet(SetValue), EnumValue);
  268. EnumName := locNextWord(P);
  269. end;
  270. SetOrdValue(Info, SetParam, SetValue);
  271. end;
  272. {$endregion 'Set<->String conversion'}
  273. initialization
  274. InitLogSubSystem;
  275. WriteMyLog('===================== Start of log ============================');
  276. finalization
  277. WriteMyLog('===================== End of log ==============================');
  278. DoneLogSystem;
  279. end.