1

アプリケーションログをファイルに記録する TLogger クラスに取り組んでいます...

ファイルから TMemo へのログを取得する方法が必要です。2. GetLogsFromFile() を呼び出します。次に Self.Memo1.Text := TLogger.LogsResult;

以下...コメントされたソリューションは正常に機能します...コメントされていないソリューションは、ボタン4を2回クリックするたびにのみ機能します

procedure TForm1.Button4Click(Sender: TObject);   // get log.file to memo
begin
  // automatic forwarding logs from File to TMemo - it works!
  //logger.DisplayMemo := Self.Memo1;
  //logger.DisplayInMemo := True;
  //logger.GetLogsFromFile();

  // tested - half-automatic method of formwarding logs to TMemo - works every 2 clicks :(
  logger.DisplayInMemo := False;
  logger.GetLogsFromFile();
  Self.Memo1.Text := logger.LogsResult;
end;

TLogger の実装全体:

unit Logger;

interface

uses
  System.IOUtils, System.TypInfo, System.SysUtils, FMX.Forms, FMX.Dialogs, System.Classes, FMX.Graphics, FMX.ExtCtrls, LoggerThread, FMX.Memo;

type

  TLogger = class
  private
    FileName : String;                // name of file to log
    FilePath : String;                // path to app / log-file

    LStringResult : String;           // result of thread log.file reading
    LLoggerMemo : TMemo;              // copy of memo - place where GetLogsFromFile put results

    LDisplayInMemo : Boolean;         // bool - if True  GetLogsFromFile puts results to DisplayMemo, otherwise waiting in LogsResult
    NewLoggerThread : TLoggerThread;  // thread object - created in Create()

    procedure GetLogsFromFileThreadTerminateHandler(sender: TObject);

  public
    constructor Create(); overload;                     // open or create 'development.log'
    constructor Create(LogFileName : String); overload; // open or create LogFileName for logging
    destructor Destroy(); overload;                     // cleaner of TLogger object
    // main procedures
    procedure Log(LogString : String);                  // add line to log file
    procedure GetLogsFromFile();                        // get all logs from log file to string
    // settings, reading results,
    property DisplayInMemo : Boolean read LDisplayInMemo write LDisplayInMemo; //bool - if True  GetLogsFromFile puts results to DisplayMemo, otherwise waiting in LogsResult
    property LogsResult : String read LStringResult write LStringResult;       //string results after Getters from TLogger usage
    property DisplayMemo : TMemo read LLoggerMemo write LLoggerMemo;           // sets TMemo where results will be put if DisplayInMemo set to True
  end;

implementation

  constructor TLogger.Create();
  begin
    {$IFDEF Android}
      FilePath := TPath.GetDownloadsPath + System.SysUtils.PathDelim;
    {$ELSE}
      FilePath := ExtractFilePath(ParamStr(0));
    {$ENDIF}
    FileName := 'development.log';
    LDisplayInMemo := False;
    // inherited
    inherited Create;
  end;

  constructor TLogger.Create(LogFileName : String);
  begin
    {$IFDEF Android}
      FilePath := TPath.GetDownloadsPath + System.SysUtils.PathDelim;
      //TPath.Combine(TPath.GetDocumentsPath,'test.txt');  // to have / \ auto-change
    {$ELSE}
      FilePath := ExtractFilePath(ParamStr(0));
    {$ENDIF}
    FileName := LogFileName;
    LDisplayInMemo := False;
    // inherited
    inherited Create;
  end;

  destructor TLogger.Destroy();
  begin
    inherited Destroy;
  end;

  // adds a sigle line to log file with date time
  procedure TLogger.Log(LogString : String);
  begin
    NewLoggerThread :=  TLoggerThread.Create(True);
    NewLoggerThread.FreeOnTerminate := True;
    NewLoggerThread.Log := LogString;                                    //log to write - date time then added in execute
    NewLoggerThread.LoggerInstruction := TLoggerInstruction.liLogToFile; //set instuction for thread - LogToFile
    NewLoggerThread.FileName := FileName;  //file to write
    NewLoggerThread.FilePath := FilePath;  //path to file

    try
      NewLoggerThread.Start;
    except
      NewLoggerThread.Free();
    end;

  end;

  // results String with LogFile content
  procedure TLogger.GetLogsFromFile();
  begin
    NewLoggerThread :=  TLoggerThread.Create(True);
    NewLoggerThread.FreeOnTerminate := True;
    NewLoggerThread.OnTerminate := GetLogsFromFileThreadTerminateHandler;
    NewLoggerThread.FileName := FileName;  //file to write
    NewLoggerThread.FilePath := FilePath;  //path to file
    NewLoggerThread.LoggerInstruction := TLoggerInstruction.liGetLogsFromFile; //set instuction for thread - GetLogFromFile

    try
      NewLoggerThread.Start;
    except
      NewLoggerThread.Free();
    end;

  end;

  procedure TLogger.GetLogsFromFileThreadTerminateHandler(sender: TObject);
  begin
    LStringResult := (Sender as TLoggerThread).StringResult;
    if LDisplayInMemo then
        LLoggerMemo.Text := (Sender as TLoggerThread).StringResult;
  end;

end.

ご覧のとおり、違いは LDisplayInMemo だけです: True の場合、TMemo はログでいっぱいになります... False の場合、TMemo で結果を取得するには、ボタン 4 を 2 回クリックする必要があります...

  procedure TLogger.GetLogsFromFileThreadTerminateHandler(sender: TObject);
  begin
    LStringResult := (Sender as TLoggerThread).StringResult;
    if LDisplayInMemo then
        LLoggerMemo.Text := (Sender as TLoggerThread).StringResult;
  end;

何か案は?正直なところ、両方のソリューションの違いの理由がわかりません:( Self.Memo1.Text := logger.LogsResult; の後に ProcessMessages も試しました。

4

1 に答える 1

2

次のコードがボタンを 2 回クリックしたときにのみ機能する理由は、実際にログ情報を取得するコードが別のスレッドで実行されるためです...非同期です!

logger.DisplayInMemo := False;
logger.GetLogsFromFile();
Self.Memo1.Text := logger.LogsResult; //This line runs AT THE SAME TIME you're getting logs!

: LoggerThread から値を取得するlogger.LogsResult 前に、 の値を読み取っています。

ボタンを 2 回目にクリックすると、スレッドの実行が終了し (1 回目)、値を読み取ることができるようになります。

コメントされたセクションが機能する理由は、スレッドが終了したとき、つまり作業を終了したときにのみメモテキストを割り当てるためです。

于 2014-02-06T18:41:39.180 に答える