MyLog.pas 9.1 KB

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