(* Name: klog Copyright: Copyright (C) SIL International. Documentation: Description: Create Date: 1 Aug 2006 Modified Date: 8 Jun 2012 Authors: mcdurdin Related Files: Dependencies: Bugs: Todo: Notes: History: 01 Aug 2006 - mcdurdin - Disable logging with KLOGGING define 14 Sep 2006 - mcdurdin - Add Current Tick Count to logs (for correlation with system.log) 04 May 2012 - mcdurdin - I3309 - V9.0 - Migrate to Delphi XE2, VS2010, svn 1.7 04 May 2012 - mcdurdin - I3310 - V9.0 - Unicode in Delphi fixes 08 Jun 2012 - mcdurdin - I3310 - V9.0 - Unicode in Delphi fixes *) unit klog; // I3309 interface {DEFINE KLOGGING} {$IFDEF KLOGGING} uses Classes; {$ENDIF} type TKLog = class private {$IFDEF KLOGGING} FAppName: string; FLogFileName: string; FLogFile: TextFile; FMethodStack: TStringList; FFileOpen: Boolean; function VarRecToString(const vr: TVarRec): string; {$ENDIF} public constructor Create; destructor Destroy; override; procedure Log(const msg: string); overload; procedure Log(const msg: string; const fmt: array of const); overload; procedure LogError(const msg: string); overload; procedure LogError(const msg: string; const fmt: array of const); overload; procedure MethodEnter(instance: TObject; const name: string; const parameters: array of const); procedure MethodExit(instance: TObject; const name: string); overload; procedure MethodExit(instance: TObject; const name: string; const result: array of const); overload; end; function KL: TKLog; function KLEnabled: Boolean; implementation {$IFDEF KLOGGING} uses Variants, Windows, SysUtils, ErrorControlledRegistry, KeymanPaths, VersionInfo, Unicode; {$ENDIF} var FKLog: TKLog = nil; function KL: TKLog; begin if not Assigned(FKLog) then FKlog := TKLog.Create; Result := FKlog; end; { TKLog } {$WARN SYMBOL_PLATFORM OFF} constructor TKLog.Create; {$IFDEF KLOGGING} function DirectoryExists(const Name: string): Boolean; var Code: Dword; begin Code := GetFileAttributes(PChar(Name)); Result := (Code <> $FFFFFFFF) and (FILE_ATTRIBUTE_DIRECTORY and Code <> 0); end; var buf: array[0..260] of char; begin inherited Create; FMethodStack := TStringList.Create; GetModuleFileName(hInstance, buf, 260); FAppName := ChangeFileExt(ExtractFileName(buf),''); FLogFileName := TKeymanPaths.ErrorLogPath(FAppName); Log(StringOfChar('=', 160)); Log('Starting application %s, version %s', [FAppName, GetFileVersionString(FAppName)]); Log('Command line: %s', [String(CmdLine)]); {$ELSE} begin inherited Create; {$ENDIF} end; {$WARN SYMBOL_PLATFORM DEFAULT} destructor TKLog.Destroy; {$IFDEF KLOGGING} var i: Integer; begin for i := 0 to FMethodStack.Count - 1 do Log('Unterminated method '+FMethodStack[i]); FMethodStack.Clear; Log('Stopping application '+FAppName); Log(StringOfChar('=', 160)); FMethodStack.Free; if FFileOpen then CloseFile(FLogFile); {$ELSE} begin {$ENDIF} inherited Destroy; end; procedure TKLog.Log(const msg: string); begin {$IFDEF KLOGGING} if not FFileOpen then begin AssignFile(FLogFile, FLogFileName); if FileExists(FLogFileName) then Append(FLogFile) else Rewrite(FLogFile); FFileOpen := True; end; writeln(FLogFile, Format('%12.12d ', [GetTickCount()])+FormatDateTime('yyyy-mm-dd hh:nn:ss.zzz', Now) + ': ' + StringOfChar(' ', FMethodStack.Count*2) + msg); flush(FLogFile); OutputDebugString(PChar('KLog:' + msg + #13#10)); {$ENDIF} end; procedure TKLog.LogError(const msg: string); begin {$IFDEF KLOGGING} Log('###ERROR###: '+msg); {$ENDIF} end; procedure TKLog.LogError(const msg: string; const fmt: array of const); begin {$IFDEF KLOGGING} LogError(Format(msg, fmt)); {$ENDIF} end; procedure TKLog.MethodEnter(instance: TObject; const name: string; const parameters: array of const); {$IFDEF KLOGGING} var s: string; vr: TVarRec; i: Integer; begin s := ''; for i := Low(parameters) to High(parameters) do begin vr := TVarRec(parameters[i]); if i > Low(parameters) then s := s + ', '; s := s + VarRecToString(vr); end; if Assigned(instance) then begin Log(instance.ClassName+'.'+name+'('+s+') enter'); FMethodStack.Add(instance.ClassName+'.'+name); end else begin Log(name+'('+s+') enter'); FMethodStack.Add(name); end; {$ELSE} begin {$ENDIF} end; procedure TKLog.MethodExit(instance: TObject; const name: string; const result: array of const); {$IFDEF KLOGGING} var s: string; begin if Assigned(instance) then s := instance.ClassName+'.'+name else s := name; while (FMethodStack.Count > 0) and (FMethodStack[FMethodStack.Count-1] <> s) do begin Log(FMethodStack[FMethodStack.Count-1] + ' not exiting properly!'); FMethodStack.Delete(FMethodStack.Count-1); end; if FMethodStack.Count > 0 then FMethodStack.Delete(FMethodStack.Count-1); Log(s+'() exit('+VarRecToString(TVarRec(result[0]))+')'); {$ELSE} begin {$ENDIF} end; procedure TKLog.MethodExit(instance: TObject; const name: string); {$IFDEF KLOGGING} var s: string; begin if Assigned(instance) then s := instance.ClassName+'.'+name else s := name; while (FMethodStack.Count > 0) and (FMethodStack[FMethodStack.Count-1] <> s) do begin Log(FMethodStack[FMethodStack.Count-1] + ' not exiting properly!'); FMethodStack.Delete(FMethodStack.Count-1); end; if FMethodStack.Count > 0 then FMethodStack.Delete(FMethodStack.Count-1); Log(s+'() exit'); {$ELSE} begin {$ENDIF} end; procedure TKLog.Log(const msg: string; const fmt: array of const); begin {$IFDEF KLOGGING} Log(Format(msg, fmt)); {$ENDIF} end; {$IFDEF KLOGGING} function TKLog.VarRecToString(const vr: TVarRec): string; begin case vr.VType of vtInteger: Result := IntToStr(vr.VInteger); vtBoolean: if vr.VBoolean then Result := 'True' else Result := 'False'; vtChar: Result := ''''+Char(vr.VChar)+''''; // I3310 vtExtended: Result := FloatToStr(vr.VExtended^); vtString: Result := ''''+String_AtoU(vr.VString^)+''''; // I3310 // I3310 vtPointer: Result := '$'+IntToHex(Integer(vr.VPointer), 8); vtPChar: Result := ''''+String_AtoU(vr.VPChar)+''''; // I3310 vtObject: if vr.VObject = nil then Result := 'object ' else Result := 'object '+vr.VObject.ClassName+' [$'+IntToHex(Integer(Pointer(vr.VObject)), 8)+']'; vtClass: if vr.VClass = nil then Result := 'class ' else Result := 'class '+vr.VClass.ClassName; vtWideChar: Result := ''''+vr.VWideChar+''''; vtPWideChar: Result := ''''+vr.VPWideChar+''''; vtAnsiString: Result := ''''+string(vr.VAnsiString)+''''; vtCurrency: Result := CurrToStr(vr.VCurrency^); vtVariant: try Result := VarToStr(vr.VVariant^); except Result := ''; end; vtInterface: Result := 'interface $'+IntToHex(Integer(vr.VInterface), 8); vtWideString: Result := ''''+widestring(vr.VWideString)+''''; vtInt64: Result := IntToStr(vr.VInt64^); vtUnicodeString: Result := ''''+UnicodeString(vr.VUnicodeString)+''''; // I3309 else Result := '???'; end; end; {$ENDIF} function KLEnabled: Boolean; begin {$IFDEF KLOGGING} Result := True; {$ELSE} Result := False; {$ENDIF} end; initialization KL; finalization FKLog.Free; FKLog := nil; end.