Smart Logging with date time stamps and more.

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Post Reply
Message
Author
Gustaaf
Posts: 6
Joined: 07 May 2019 04:04

Smart Logging with date time stamps and more.

#1 Post by Gustaaf » 15 Jul 2024 12:22

I have created these variables to hold actions to facilitate batch logging features. It relies on NTFS data streams. So FAT32 volumes will fail.
The idea is to prevent duplicate lines of code for those who need to log what they are doing.
See the examples in the code.

Code: Select all

@echo off
prompt $
SETLOCAL ENABLEEXTENSIONS
SETLOCAL ENABLEDELAYEDEXPANSION
SETLOCAL

SET RETURNCODE=99
:: Set Smart logging features, eliminating duplicate code.
:: Avoid using in FOR loop functions for now, hoping to remediate.
::
:: Define temporary files
SET "LogFile=LOGFILE.TXT"
IF NOT EXIST "%temp%" MKDIR "%temp%" 1>NUL 2>&1

SET "FTMP=%TEMP%\FTMP.tmp"
:: Clear earlier ftmp because of ntfs streams associated with the file.
del /q /f "%FTMP%" >nul 2>nul
echo.>"%FTMP%"
SET "FCM0=%TEMP%\FCM0.tmp"
:: Clear earlier fcm0 because of ntfs streams associated with the file.
del /q /f "%FCM0%" >nul 2>nul
echo.>"%FCM0%"


:: echo Prompt 1 %PR1% 
:: Sends echo to LOG file only. (multiline capture possible)
SET PR1=^>"!ftmp!":1 ^& MORE ^<"!ftmp!":1 ^1^>^>"%LogFile%" ^2^>^>^&^1

:: echo Prompt 2 %PR2% 
:: Same as PR1, but now sends echo to console and LOG file. (multiline capture possible)
SET PR2=^>"!ftmp!":2 ^& MORE ^<"!ftmp!":2 ^& MORE ^<"!ftmp!":2 ^1^>^>"%LogFile%" ^2^>^>^&^1

:: echo Prompt 3 %PR3% 
:: Sends echo to LOG file with %DATE% and %TIME% stamp only. (one echo per line; NO multiline capture possible.)
SET PR3=^>"!ftmp!":3 ^& ^( SET /P OUT=MORE ^<"!ftmp!":3 ^)^>nul ^& CALL ECHO [%%PDATE%%;%%TIME%%] %%OUT%% ^1^>^>"%LogFile%" ^2^>^>^&^1

:: echo Prompt 4 %PR4% 
:: Sends echo to console and LOG file with %DATE% and %TIME% stamp for LOG only. (one echo per line; NO multiline capture possible.)
SET PR4=^>"!ftmp!":4 ^& ^( SET /P OUT=MORE ^<"!ftmp!":4 ^)^>nul ^& MORE ^<"!ftmp!":4 ^& CALL ECHO [%%PDATE%%;%%TIME%%] %%OUT%% ^1^>^>"%LogFile%" ^2^>^>^&^1

:: Invoke commands and log them!
rem echo DIR %TEMP% %CM1%
rem Sends command as date-time stamped to log with [START] label, then invokes it, redirects output to `"!FCM0!":run`, displays the result to LOG only, closes with [END] label.
SET CM1=^>"!FCM0!":1 ^& ^( SET /P OUT=MORE ^<"!FCM0!":1 ^)^>nul ^& CALL :CM1

rem echo DIR %TEMP% %CM2%
rem Sends command as date-time stamped to log with [START] label, then invokes it, redirects output to `"!FCM0!":run`, displays the result to CONSOLE and LOG, closes with [END] label.
SET CM2=^>"!FCM0!":2 ^& ^( SET /P OUT=MORE ^<"!FCM0!":2 ^)^>nul ^& CALL :CM2

rem echo DIR C:\WINDOWS %CM3%
rem Same as CM2, but uses powershell to invoke the command and read/write date-time stamps for each item in the loop to the log.
SET CM3=^>"!FCM0!":3 ^& ^( SET /P OUT=MORE ^<"!FCM0!":3 ^)^>nul ^& CALL :CM3
:: ================================================================================



rem Examples:

(
    ECHO.
    ECHO Hello World!
    ECHO These lines only show up in your log file!
) %PR1%

(
    ECHO.
    ECHO Hello World again!!
    ECHO This line should show in the console and the log file!
) %PR2%

ECHO Start logging for app1.exe %PR3%

ECHO Start logging for app2.exe %PR4%

echo DIR %TEMP% %CM1%

echo DIR %TEMP% %CM2%

echo DIR %WINDIR% %CM3%

goto :END




:: ================================================================================
:: SCRIPT FUNCTIONS BELOW.
:: ================================================================================



:: Script FUNCTION :CM1 ================================================================
:CM1
CALL ECHO [%PDATE%;%TIME%] [START]: %OUT% >>"%LogFile%"
ECHO =====CM1======================================================================== >>"%LogFile%"
CALL %OUT% 1>"%FCM0%" 2>&1
type "%FCM0%" >>"%LogFile%"
ECHO =====CM1======================================================================== >>"%LogFile%"
CALL ECHO [%PDATE%,%TIME%] [END] >>"%LogFile%"
GOTO :EOF

:: Script FUNCTION :CM2 ================================================================
:CM2
CALL ECHO [%PDATE%;%TIME%] [START]: %OUT% >>"%LogFile%"
ECHO =====CM2======================================================================== >>"%LogFile%"
CALL %OUT% 1>"%FCM0%" 2>&1
type "%FCM0%"
type "%FCM0%" >>"%LogFile%"
ECHO =====CM2======================================================================== >>"%LogFile%"
CALL ECHO [%PDATE%,%TIME%] [END] >>"%LogFile%"
GOTO :EOF

:: Script FUNCTION :CM3 ================================================================
:CM3
CALL ECHO [%date%;%time%] [START]: %OUT% >>"%LogFile%"
ECHO =====CM3======================================================================== >>"%LogFile%"
CALL :createPowerShellScript "%out%" "%LogFile%"
start /min /wait /b C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -noprofile -noninteractive -ExecutionPolicy ByPass -file "%temp%\cm3.ps1"
SET RETURNCODE=!ERRORLEVEL!
ECHO [RETURN CODE=%RETURNCODE%] %PR2%
CALL ECHO [%date%;%time%] [END] >>"%LogFile%"
GOTO :EOF



:: Private FUNCTION for :CM3 ================================================================
:createPowerShellScript
REM Parameters
REM %1 - Command to execute
REM %2 - Log file to write
set "command=%~1"
set "logFile=%~2"
(
rem echo Set-ItemProperty -Path "Registry::HKEY_CURRENT_USER\Console" -Name "FaceName" -Value "Lucida Console" -Type String
echo . $env:windir\System32\chcp.com 65001
echo '%command%'
echo $Height = 30
echo $Width = 120
echo $console = $host.ui.rawui
echo $ConBuffer = $console.BufferSize
echo $ConSize = $console.WindowSize
echo $currWidth = $ConSize.Width
echo $currHeight = $ConSize.Height
echo # if height is too large, set to max allowed size
echo if ^($Height -gt $host.UI.RawUI.MaxPhysicalWindowSize.Height^)
echo ^{
echo 	$Height = $host.UI.RawUI.MaxPhysicalWindowSize.Height
echo ^}
echo # if width is too large, set to max allowed size
echo if ^($Width -gt $host.UI.RawUI.MaxPhysicalWindowSize.Width^)
echo ^{
echo 	$Width = $host.UI.RawUI.MaxPhysicalWindowSize.Width
echo ^}
echo # If the Buffer is wider than the new console setting, first reduce the width
echo if ^($ConBuffer.Width -gt $Width^)
echo ^{
echo 	$currWidth = $Width
echo ^}
echo # If the Buffer is higher than the new console setting, first reduce the height
echo if ^($ConBuffer.Height -gt $Height^)
echo ^{
echo 	$currHeight = $Height
echo ^}
echo # initial resizing if needed
echo $host.UI.RawUI.WindowSize = New-Object System.Management.Automation.Host.size^($currWidth, $currHeight^)
echo # Set the Buffer
echo $host.UI.RawUI.BufferSize = New-Object System.Management.Automation.Host.size^($Width, 2000^)
echo # Now set the WindowSize
echo $host.UI.RawUI.WindowSize = New-Object System.Management.Automation.Host.size^($Width, $Height^)
echo $command = '%command%'
echo $logFile = '%logFile%'
echo $startInfo = New-Object System.Diagnostics.ProcessStartInfo
echo $startInfo.FileName = 'cmd.exe'
echo $startInfo.Arguments = '/c ^' + $command + ^''
echo $startInfo.RedirectStandardOutput = $true
echo $startInfo.UseShellExecute = $false
echo $startInfo.CreateNoWindow = $true
echo $process = New-Object System.Diagnostics.Process
echo $process.StartInfo = $startInfo
echo $process.Start^(^)^| Out-Null
echo $streamReader = $process.StandardOutput
echo $fs = [System.IO.File]::AppendText^($logFile^)
echo while ^($null -ne ^($line = $streamReader.ReadLine^(^)^)^) ^{
echo ^    $dateTime = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
echo ^    Write-Host $line
echo ^    $fs.WriteLine^("[$dateTime] $line"^)
echo ^}
echo $fs.Close^(^)
echo $process.WaitForExit^(^)
echo exit $process.exitcode
rem echo [System.Environment]::Exit^($process.exitcode^)
) > "%temp%\cm3.ps1"
exit /b

:END
prompt
ENDLOCAL


Post Reply