Force log file

From ISXKB

Jump to: navigation, search

Contents

Introduction

An installer compiled with Inno Setup version 5.2.0 (2007-09-19) and newer can force the creation of an installation log file. For older versions of Inno Setup see the article Force log file with Inno Setup versions before 5.2.0.

The [Setup] section

To force the creation of a log file during the installation an entry 'SetupLogging = yes' is required in the [Setup] section:

[Setup]
SetupLogging=yes

This creates log files with the name 'Setup Log ' + today's date + ' #' + a three-digit number with leading zeroes + '.txt' in the temporary directory of the user running the installation program (which is not the same as the constant {tmp}, by the way).

The log files stay in the temporary directory after Setup has finished. Because the current log file is locked it's not possible to remove the file within the installer. Every installer that uses 'SetupLogging=yes' should therefore contain something like this

procedure DeinitializeSetup();
begin
   RestartReplace (ExpandConstant ('{log}'), '');
end;

to remove the file during the next system restart. See Inno Setup Help - RestartReplace for the definition of this procedure.

Copying the log file

Since the log file by default remains in the user's temporary directory it may be desirable to copy it into the application folder just before Setup finishes. The detour with CurStepChanged is necessary to ensure that {app} really expands without a run-time error.

var
  OkToCopyLog : Boolean;

procedure CurStepChanged(CurStep: TSetupStep);
begin
  if CurStep = ssDone then
    OkToCopyLog := True;
end;

procedure DeinitializeSetup();
begin
  if OkToCopyLog then
    FileCopy (ExpandConstant ('{log}'), ExpandConstant ('{app}\InstallationLogFile.log'), FALSE);
  RestartReplace (ExpandConstant ('{log}'), '');
end;

That would preserve the most recent installation log. Since the installer has not finished completely before the file is copied the last log file entry ('Log closed.') is missing.

Don't forget to remove the file upon uninstallation:

[UninstallDelete]
Type: files; Name: "{app}\InstallationLogFile.log"

There is no way from inside the installer to copy the entire file including the last line ('Log closed.').


Here are the last few lines of the real log file:

yyyy-mm-dd hh:mi:ss.mis   Installation process succeeded.
yyyy-mm-dd hh:mi:ss.mis   Need to restart Windows? No
yyyy-mm-dd hh:mi:ss.mis   Deinitializing Setup.
yyyy-mm-dd hh:mi:ss.mis   Log closed.

Here are the last few lines of the copied log file:

yyyy-mm-dd hh:mi:ss.mis   Installation process succeeded.
yyyy-mm-dd hh:mi:ss.mis   Need to restart Windows? No
yyyy-mm-dd hh:mi:ss.mis   Deinitializing Setup.

This should be sufficient enough in most cases. To get the entire log file some more sophisticated tricks are necessary.

Copying the entire log file

As mentioned above the log file is locked by the installer during the whole installation process, plus the file is only written completely after Setup has terminated.

That means that the file needs to be copied after the installer has finished. Theoretically the application could handle the log file but if the application is not started before the next system restart the file's deleted already because of the RestartReplace () call.

Some process, script, or program, is required to do the following:

  • Wait a few seconds to give the installer some time to exit.
  • Copy the log file into the application directory.
  • Delete the log file from the temporary directory.

A little command line script can handle this process. Copy the following script into a file called 'MoveLogFile.cmd' and put it in your Inno Setup script's directory.

@ECHO OFF
REM MoveLogFile.cmd
REM Parameters:
REM  First  : Source log file path
REM  Second : Target log file path
REM  Third  : Delay time in seconds
REM  Fourth : '/Delete' to delete the log file and this batch script (!)
IF %1.==. GOTO :Usage
IF NOT EXIST %1. (
	ECHO Error: Source log file '%1' not found.
	GOTO :End
)
IF %2.==. GOTO :Usage
IF NOT %3.==. PING -n %3 127.0.0.1
ECHO.>>%2
ECHO ***********************   New installation starts here   ***********************>>%2
ECHO.>>%2
TYPE %1>>%2
IF %4.==/Delete. (
	DEL /F/Q %1
	IF EXIST %1 ECHO Error deleting '%1'.
	DEL /F/Q "%0"
)
GOTO :End
:Usage
ECHO Log file mover - Appends a source log file to
ECHO  a target log file.
ECHO.
ECHO  MoveLogFile.cmd source target delay [/Delete]
ECHO   source  = Full path name of source log file
ECHO   target  = Full path name of target log file
ECHO   delay   = Delay time in seconds
ECHO   /Delete = Removes source file and this batch script (!)
ECHO.
:End

The file can be placed in the application directory:

[Files]
Source: MoveLogFile.cmd; DestDir: {app}; Flags: overwritereadonly ignoreversion uninsremovereadonly

'MoveLogFile.cmd' actually explains itself but here is an example of how it can be invoked:

MoveLogFile.cmd "{log}" "{app}\InstallationLogFile.log" 5 /Delete

The parameter '/Delete' makes sure that the batch file deletes itself from the application directory, so don't try it with your original copy ;-). It's only required during the installation.

In the example the log file in the application's folder grows every time the installer runs because the current log file is appended. If you would like to only keep the log file of the most recent run change 'ECHO.>>%2' to 'ECHO.>%2' in the first line where it occurs.

Append to log file in '{app}':

IF NOT %3.==. PING -n %3 127.0.0.1
ECHO.>>%2

Overwrite log file in '{app}':

IF NOT %3.==. PING -n %3 127.0.0.1
ECHO.>%2

Another option to overwrite the log file in '{app}':

IF NOT %3.==. PING -n %3 127.0.0.1
REM Remove the 3 lines that where in here before.
TYPE %1>%2

From the Inno Setup script the batch file is called just before the installer exits:

procedure DeinitializeSetup();
var
  Errorcode : Integer;
begin
  if OkToCopyLog then
    Exec (ExpandConstant ('{sys}\CMD.EXE'),
      '/C ' +
      'MoveLogFile.cmd ' +
      '"' + ExpandConstant ('{log}') + '" ' +
      '"' + ExpandConstant ('{app}\InstallationLogFile.log') + '" ' +
      '5 /Delete',
      ExpandConstant ('{app}'),
      SW_HIDE, ewNoWait, Errorcode);
end;

The user who runs the Setup can't see this process because the command line window is hidden during its lifetime (SW_HIDE).

Don't forget to remove the file when the application is uninstalled:

[UninstallDelete]
Type: files; Name: "{app}\InstallationLogFile.log"

Copying the entire log file and keep older versions

Suppose you would like to keep the log files of several consecutive installations in the application directory. This could be required for complicated installations with frequent updates.

In this case we should keep the application folder tidy and put the log files in a subfolder of '{app}':

[Dirs]
Name: {app}\InstallationLogs

Every day we create a new log file. If an installer is run several times on the same day all logs go into the same file of that day. 'MoveLogFile.cmd' can be used unchanged for that.

The [Code] section:

#define MyAppName "My Application"

[Code]
var
  TodaysName  : String;
  OkToCopyLog : Boolean;

function GetToday : String;
begin
  Result := GetDateTimeString ('yyyy/mm/dd', '-', #0);
end;

function GetTodaysName (Param: String): String;
begin
  if ('' = TodaysName) then
  begin
    TodaysName := GetToday ();
    end;
    Result := TodaysName;
end;

procedure CurStepChanged(CurStep: TSetupStep);
begin
  if CurStep = ssDone then
    OkToCopyLog := True;
end;

procedure DeinitializeSetup();
var
  Errorcode : Integer;
begin
  if OkToCopyLog then
    Exec (ExpandConstant ('{sys}\CMD.EXE'),
      '/C ' +
      'MoveLogFile.cmd ' +
      '"' + ExpandConstant ('{log}') + '" ' +
      '"' + ExpandConstant ('{app}\InstallationLogs\{#MyAppName} ') + GetTodaysName ('') + '.log" ' +
      '5 /Delete',
      ExpandConstant ('{app}'),
      SW_HIDE, ewNoWait, Errorcode);
end;

Again, don't forget to delete the log files when the application is uninstalled:

[UninstallDelete]
Type: files; Name: "{app}\InstallationLogs\{#MyAppName} ????-??-??.log"
Type: dirifempty; Name: "{app}\InstallationLogs"

Copying the entire log file, keep some versions, and zip and remove older versions

To prepare the process for eternity (nice one, right? ;-) it would be good to keep a couple of log files in '{app}\InstallationLogs', hold maybe 5 in a compressed version, and dump any log file that's older.

There's certainly more than one approach for this. We could pack a compression tool like ZIP.EXE with the installer and use it in the batch file.

Since it can't be done without additional programs we can use Logtext. It does this almost automatically after the batch script has been adjusted accordingly. Download Logtext (logtext.exe) and put it in the same directory as the Inno Setup script.

The new [Files] section:

[Files]
Source: MoveLogFileLogText.cmd; DestDir: {app}; Flags: overwritereadonly ignoreversion uninsremovereadonly
Source: logtext.exe; DestDir: {app}; Flags: overwritereadonly uninsremovereadonly

File 'MoveLogFileLogText.cmd':

@ECHO OFF
REM MoveLogFileLogText.cmd
REM Parameters:
REM  First  : Source log file path
REM  Second : Target log file path
REM  Third  : Target log file path for Logtext
REM  Fourth : Delay time in seconds
REM  Fifth  : '/Delete' to delete the log file and this batch script (!)
IF %1.==. GOTO :Usage
IF NOT EXIST %1. (
	ECHO Error: Source log file '%1' not found.
	GOTO :End
)
IF %2.==. GOTO :Usage
IF %3.==. GOTO :Usage
IF NOT %4.==. PING -n %4 127.0.0.1
ECHO.>>%2
ECHO ***********************   New installation starts here   ***********************>>%2
ECHO.>>%2
TYPE %1>>%2
LOGTEXT.EXE %3 "MoveLogFileLogText.cmd finished." THREE_BLANKS 2 5
IF %5.==/Delete. (
	DEL /F/Q %1
	IF EXIST %1 ECHO Error deleting '%1'.
	DEL /F/Q LOGTEXT.EXE
	IF EXIST LOGTEXT.EXE ECHO Error deleting 'logtext.exe'.
	DEL /F/Q "%0"
)
GOTO :End
:Usage
ECHO Log file mover - Appends a source log file to
ECHO  a target log file.
ECHO.
ECHO  MoveLogFileLogText.cmd source target logtextf delay [/Delete]
ECHO   source   = Full path name of source log file
ECHO   target   = Full path name of target log file
ECHO   logtextf = Full path name of target log file without date
ECHO               and '.log' for Logtext
ECHO   delay    = Delay time in seconds
ECHO   /Delete  = Removes source file and this batch script (!)
ECHO.
:End

Remove the two lines

	DEL /F/Q LOGTEXT.EXE
	IF EXIST LOGTEXT.EXE ECHO Error deleting 'logtext.exe'.

if you would like to keep the program in the application directory for whatever reason.

The numbers '2' and '5' in the line

LOGTEXT.EXE %3 "MoveLogFileLogText.cmd finished." THREE_BLANKS 2 5

define how many files we'd like to keep in an uncompressed state ('2') and how many in a compressed version ('5').

For example, change the line to

LOGTEXT.EXE %3 "MoveLogFileLogText.cmd finished." THREE_BLANKS 5 20

if you would like to keep 5 days and an additional 20 days compressed.

Since the batch script's parameters have changed DeinitializeSetup () needs to be changed as well:

procedure DeinitializeSetup();
var
  Errorcode : Integer;
begin
  if OkToCopyLog then
    Exec (ExpandConstant ('{sys}\CMD.EXE'),
      '/C ' +
      'MoveLogFileLogText.cmd ' +
      '"' + ExpandConstant ('{log}') + '" ' +
      '"' + ExpandConstant ('{app}\InstallationLogs\{#MyAppName} ') + GetTodaysName ('') + '.log" ' +
      '"' + ExpandConstant ('{app}\InstallationLogs\{#MyAppName} ') + '" ' +
      '5 /Delete',
      ExpandConstant ('{app}'),
      SW_HIDE, ewNoWait, Errorcode);
end;

The complete [Code] section:

#define MyAppName "My Application"

[Code]
var
  TodaysName  : String;
  OkToCopyLog : Boolean;

function GetToday : String;
begin
  Result := GetDateTimeString ('yyyy/mm/dd', '-', #0);
end;

function GetTodaysName (Param: String): String;
begin
  if ('' = TodaysName) then
  begin
    TodaysName := GetToday ();
    end;
    Result := TodaysName;
end;

procedure CurStepChanged(CurStep: TSetupStep);
begin
  if CurStep = ssDone then
    OkToCopyLog := True;
end;

procedure DeinitializeSetup();
var
  Errorcode : Integer;
begin
  if OkToCopyLog then
    Exec (ExpandConstant ('{sys}\CMD.EXE'),
      '/C ' +
      'MoveLogFileLogText.cmd ' +
      '"' + ExpandConstant ('{log}') + '" ' +
      '"' + ExpandConstant ('{app}\InstallationLogs\{#MyAppName} ') + GetTodaysName ('') + '.log" ' +
      '"' + ExpandConstant ('{app}\InstallationLogs\{#MyAppName} ') + '" ' +
      '5 /Delete',
      ExpandConstant ('{app}'),
      SW_HIDE, ewNoWait, Errorcode);
end;

Every application will be uninstalled at some point:

[UninstallDelete]
Type: files; Name: "{app}\InstallationLogs\{#MyAppName} ????-??-??.log"
Type: files; Name: "{app}\InstallationLogs\{#MyAppName} ????-??-??.log.gz"
Type: dirifempty; Name: "{app}\InstallationLogs"

See also

External links

Personal tools
Ads: