⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 stgenlog.pas

📁 条码控件: 一维条码控件 二维条码控件 PDF417Barcode MaxiCodeBarcode
💻 PAS
📖 第 1 页 / 共 2 页
字号:
(* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is TurboPower SysTools
 *
 * The Initial Developer of the Original Code is
 * TurboPower Software
 *
 * Portions created by the Initial Developer are Copyright (C) 1996-2002
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * ***** END LICENSE BLOCK ***** *)

{*********************************************************}
{* SysTools: StGenLog.pas 4.03                           *}
{*********************************************************}
{* SysTools: General Logging                             *}
{*********************************************************}

{$I StDefine.inc}

unit StGenLog;

interface

uses
  Windows, SysUtils, Classes, StBase;

const

  StDefBufferSize = 65536;   { Default buffer size }
  StDefHighLevel = 0;        { Default high level point }
  StMaxLogSize = 16000000;   { Max size of general log buffer }
//  StCRLF = #10#13;                                                      {!!.01}
  StCRLF = #13#10;                                                      {!!.01}
  StLogFileFooter = '';
  StLogFileHeader = 'SysTools General Log' + StCRLF +
    '=============================================================================' +
    StCRLF + StCRLF;

  { General log constants }
  leEnabled   = 1;
  leDisabled  = 2;

  leString    = DWORD($80000000);

type

  TStGetLogStringEvent = procedure(Sender : TObject; const D1, D2, D3, D4 : DWORD;
    var LogString : string) of object;

  TStWriteMode = (wmOverwrite, wmAppend);

  { Record for log entries }
  PStLogRec = ^TStLogRec;
  TStLogRec = record
    lrTime  : DWORD;
    lrData1 : DWORD;
    lrData2 : DWORD;
    lrData3 : DWORD;
    lrData4 : DWORD;
  end;

  PStLogBuffer = ^TStLogBuffer;
  TStLogBuffer = array[0..StMaxLogSize] of Byte;

  StGenOptions = (goSuppressEnableMsg, goSuppressDisableMsg);          {!!.01}
  StGenOptionSet = set of StGenOptions;                                {!!.01}

  TStGeneralLog = class(TStComponent)
  private
    { Property variables }
    FBufferSize : DWORD;
    FEnabled : Boolean;
    FFileName : TFileName;
    FHighLevel : Byte;
    FLogFileFooter : string;
    FLogFileHeader : string;
    FLogOptions : StGenOptionSet;                                      {!!.01}
    FWriteMode : TStWriteMode;
    { Event variables }
    FOnHighLevel : TNotifyEvent;
    FOnGetLogString : TStGetLogStringEvent;
    { Private variables }
    glBuffer : PStLogBuffer;
    glBufferHead : DWORD;
    glBufferTail : DWORD;
    glHighLevelMark : DWORD;
    glHighLevelTriggered : Boolean;
    glLogCS : TRTLCriticalSection;
    glTempBuffer : PByteArray;
    glTempSize : DWORD;
    glTimeBase : DWORD;
  protected
    { Property access methods }
    procedure DoGetLogString(const D1, D2, D3, D4 : DWORD; var LogString : string); virtual;
    function GetBufferEmpty : Boolean;
    function GetBufferFree : DWORD;
    function GetBufferSize : DWORD;
    function GetEnabled : Boolean;
    function GetFileName : TFileName;
    function GetHighLevel : Byte;
    function GetLogOptions : StGenOptionSet;                           {!!.01}
    function GetWriteMode : TStWriteMode;
    procedure SetBufferSize(const Value : DWORD);
    procedure SetEnabled(const Value : Boolean); virtual;
    procedure SetFileName(const Value : TFileName); virtual;
    procedure SetHighLevel(const Value : Byte);
    procedure SetLogOptions(const Value : StGenOptionSet);             {!!.01}
    procedure SetWriteMode(const Value : TStWriteMode);
    { Internal methods }
    procedure glCalcHighLevel;
    procedure glCheckTempSize(SizeReq : DWORD);
    procedure glHighLevelCheck;
    procedure glLockLog;
    function glPopLogEntry(var LogRec : TStLogRec) : Boolean;
    function glTimeStamp(Mark : DWORD) : string;
    procedure glUnlockLog;
  public
    { Public methods }
    constructor Create(Owner : TComponent); override;
    destructor Destroy; override;
    procedure AddLogEntry(const D1, D2, D3, D4 : DWORD);
    procedure ClearBuffer;
    procedure DumpLog; virtual;
    procedure WriteLogString(const LogString : string);
    { Public properties }
    property BufferEmpty : Boolean read GetBufferEmpty;
    property BufferFree : DWORD read GetBufferFree;
  published
    { Published properties }
    property BufferSize : DWORD
      read GetBufferSize write SetBufferSize default StDefBufferSize;
    property Enabled : Boolean read GetEnabled write SetEnabled default True;
    property FileName : TFileName read GetFileName write SetFileName;
    property HighLevel : Byte read GetHighLevel write SetHighLevel default StDefHighLevel;
    property LogFileFooter : string read FLogFileFooter write FLogFileFooter;
    property LogFileHeader : string read FLogFileHeader write FLogFileHeader;
    property LogOptions : StGenOptionSet read GetLogOptions            {!!.01}
      write SetLogOptions default [];                                  {!!.01}
    property WriteMode : TStWriteMode read GetWriteMode write SetWriteMode;
    { Event properties }
    property OnHighLevel : TNotifyEvent read FOnHighLevel write FOnHighLevel;
    property OnGetLogString : TStGetLogStringEvent
      read FOnGetLogString write FOnGetLogString;
  end;

  function HexifyBlock(var Buffer; BufferSize : Integer) : string;

implementation

{ TStGeneralLog }

{ Gives text representation of a block of data }
function HexifyBlock(var Buffer; BufferSize : Integer) : string;
type
  TCastCharArray = array[0..Pred(High(LongInt))] of AnsiChar;
const
  { Starting string to work with - this is directly written to by index }
  { below, so any positional changes here will also have to be made below. }
  StockString = '  %6.6x: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 : 0000000000000000' + StCRLF;
  HexDigits : array[0..$F] of AnsiChar = '0123456789ABCDEF';
var
  I, J, K, Lines : Integer;
  TempStr : string;
  Hex1, Hex2 : array[0..23] of AnsiChar;
  Ascii1, Ascii2 : array[0..7] of AnsiChar;
begin
  K := 0;
  FillChar(Hex1, SizeOf(Hex1), #32);
  FillChar(Hex2, SizeOf(Hex2), #32);

  { Calculate number of lines required }
  Lines := BufferSize div 16;
  if (BufferSize mod 16) <> 0 then Inc(Lines);

  { Process and append lines }
  for I := 0 to Lines-1 do begin

    { Load string, add index marker }
    TempStr := Format(StockString, [I*16]);

    { Format data for first word }
    for J := 0 to 7 do begin
      if J+K >= BufferSize then begin
        Ascii1[J] := ' ';
        Hex1[J*3] := ' ';
        Hex1[J*3+1] := ' ';
      end else begin
        Ascii1[J] := TCastCharArray(Buffer)[J+K];
        Hex1[J*3] := HexDigits[Byte(Ascii1[J]) shr 4];
        Hex1[J*3+1] := HexDigits[Byte(Ascii1[J]) and $F];

        { Clamp Ascii to printable range }
        if (Ascii1[J] < #32) or (Ascii1[J] > #126) then Ascii1[J] := '.';
      end;
    end;
    Inc(K,8);

    { Format data for second word }
    for J := 0 to 7 do begin
      if J+K >= BufferSize then begin
        Ascii2[J] := ' ';
        Hex2[J*3] := ' ';
        Hex2[J*3+1] := ' ';
      end else begin
        Ascii2[J] := TCastCharArray(Buffer)[J+K];
        Hex2[J*3] := HexDigits[Byte(Ascii2[J]) shr 4];
        Hex2[J*3+1] := HexDigits[Byte(Ascii2[J]) and $F];
        { Clamp Ascii to printable range }
        if (Ascii2[J] < #32) or (Ascii2[J] > #126) then Ascii2[J] := '.';
      end;
    end;
    Inc(K,8);

    { Move data to existing temp string }
    Move(Hex1[0], TempStr[11], SizeOf(Hex1));
    Move(Hex2[0], TempStr[36], SizeOf(Hex2));

    Move(Ascii1[0], TempStr[62], SizeOf(Ascii1));
    Move(Ascii2[0], TempStr[70], SizeOf(Ascii2));

    { Append temp string to result }
    Result := Result + TempStr;
  end;
end;

constructor TStGeneralLog.Create(Owner : TComponent);
begin
  inherited Create(Owner);
  InitializeCriticalSection(glLogCS);
  BufferSize := StDefBufferSize;
  FEnabled := True;
  FFileName := 'debug.log';
  FLogFileFooter := StLogFileFooter;
  FLogFileHeader := StLogFileHeader;
  HighLevel := StDefHighLevel;
  glHighLevelTriggered := False;
  glTimeBase := GetTickCount;
end;

destructor TStGeneralLog.Destroy;
begin
  FreeMem(glBuffer);
  FreeMem(glTempBuffer);
  DeleteCriticalSection(glLogCS);
  inherited Destroy;
end;

procedure TStGeneralLog.glLockLog;
begin
  if IsMultiThread then
    EnterCriticalSection(glLogCS);
end;

procedure TStGeneralLog.glUnlockLog;
begin
  if IsMultiThread then
    LeaveCriticalSection(glLogCS);
end;

{ AddLogEntry notes:                                                  }
{                                                                     }
{ D1 = $FFFFFFFF is reserved for internal events                      }
{                                                                     }
{ D1, D2, D3, D4 are "info" fields to be used in the OnGetLogString   }
{ handler to identify the logged event and what type of data would be }
{ appropriate for the corresponding log entry.                        }
{                                                                     }
{ While you're free to come up with your own logging scheme, it was   }
{ envisioned that D1 would identify the logged event in the broadest  }
{ terms, and the event classification would be narrowed further and   }
{ further with D2 --> D4.                                             }
{                                                                     }
{ Special case: If the high bit of D2 is set, D3 becomes a pointer    }
{ to data, and D4 is the size of the data. Make *sure* the high bit   }
{ isn't set unless you are using this special situation.              }
{                                                                     }
{ If you just have a simple case for logging that probably won't get  }
{ used that often, consider adding entries with the WriteDebugString  }
{ method.                                                             }
procedure TStGeneralLog.AddLogEntry(const D1, D2, D3, D4 : DWORD);
var
  LogEntry : TStLogRec;
  EntryPtr : PStLogRec;
  SizeReq, TimeMrk, ChunkSize : DWORD;
  HasData : Boolean;
begin
  glLockLog;
  try
    { Bail if we're not logging }
    if not Enabled then Exit;

    TimeMrk := GetTickCount;

    { Determine size needed }
    SizeReq := SizeOf(TStLogRec);
    if (D2 and $80000000) = $80000000 then begin
      HasData := True;
      Inc(SizeReq, D4);
    end else begin
      HasData := False;
    end;

    { Bail if SizeReq is bigger than the whole buffer }
    if SizeReq > FBufferSize then Exit;

    { Make more room in buffer if necessary }
    while (SizeReq > BufferFree) and glPopLogEntry(LogEntry) do ;

    { Do we need to wrap this entry? }
    if (glBufferTail + SizeReq) <= FBufferSize then begin

      { Wrap not required, write directly to glBuffer }
      EntryPtr := @glBuffer[glBufferTail];
      EntryPtr.lrTime := TimeMrk;
      EntryPtr.lrData1 := D1;
      EntryPtr.lrData2 := D2;
      EntryPtr.lrData3 := D3;
      EntryPtr.lrData4 := D4;

      { Write add'l data if necessary }
      if HasData then begin
        Move(Pointer(D3)^, glBuffer[glBufferTail + SizeOf(TStLogRec)], D4);
      end;
      Inc(glBufferTail, SizeReq);

      { Fix tail if necessary }
      if glBufferTail = FBufferSize then
        glBufferTail := 0;

    end else begin

      { Wrap required, use temp buffer }
      glCheckTempSize(SizeReq);

      EntryPtr := @glTempBuffer[0];
      EntryPtr.lrTime := TimeMrk;
      EntryPtr.lrData1 := D1;
      EntryPtr.lrData2 := D2;
      EntryPtr.lrData3 := D3;
      EntryPtr.lrData4 := D4;

      { Write add'l data if necessary }
      if HasData then begin
        Move(Pointer(D3)^, glTempBuffer[SizeOf(TStLogRec)], D4);
      end;

      { Move first half }
      ChunkSize := FBufferSize - glBufferTail;
      Move(glTempBuffer[0], glBuffer[glBufferTail], ChunkSize);

      { Move second half }
      Move(glTempBuffer[ChunkSize], glBuffer[0], SizeReq - ChunkSize);

      { Set tail }
      glBufferTail := SizeReq - ChunkSize;
    end;
    glHighLevelCheck;
  finally
    glUnlockLog;
  end;
end;

{ Clears all data from buffer (does not write data to disk) }
procedure TStGeneralLog.ClearBuffer;
begin
  glLockLog;
  try
    glBufferHead := 0;
    glBufferTail := 0;
  finally
    glUnlockLog;
  end;
end;

{ Let user fill in the data for the LogString }
procedure TStGeneralLog.DoGetLogString(const D1, D2, D3, D4 : DWORD; var LogString : string);
begin
  if Assigned(FOnGetLogString) then
    FOnGetLogString(Self, D1, D2, D3, D4, LogString);
end;

{ Calculate the BufferFree level, in bytes, to trip the high level alarm }
procedure TStGeneralLog.glCalcHighLevel;
begin
  glLockLog;
  try

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -