Jumlins TechBlog

The coding hell and the daily IT of Niklas Jumlin

May 24th, 2012 by Niklas Jumlin

At work I was tasked with writing a script that would clean up and archive logs from an application that didn’t have any decent log rotation built-in. The application would however rotate the logs when the application’s service was restarted and then rename the old log with an incremental number suffix. So we added a scheduled task that would restart the service every night in order to get the logs rotated and free from being locked.

So what this script will do is to take any log which contains a number at the end and archive it to another location, it will then replace the archives last modified date to the original last modified date of the actual log file. After that it will simply delete archives older than X-days based on its last modified date and time.

You can of course configure the script to look for logs named in a different way. This is quite simple:

## Define files to match
$LogFileMatch = "*scplog?"
## * = Any or none characters
## ? = Any character (Must be one)

The script will log events to screen and also to a log file.
It will also log every event to Windows built-in EventLog if executed with administrator privileges.
The script will also handle all exit codes from 7-zip command-line which is the program it’s utilizing in order to archive files.

Configuration settings that can and should be edited in the script are commented at the top of the script.

## Set Window Title
$title = $Host.UI.RawUI.WindowTitle
$titdef = $title
$Host.UI.RawUI.WindowTitle = "Running LRCAS. Do NOT close " + "($title)"
"Running LRCAS (Log Rotation Cleaner and Archiver Script) v2.0"
 
## Name this script (Will use this as source in EventLog)
$JOB = "LRCAS"
 
## Get todays date
$date = get-date -uformat "%Y-%m-%d"
$global:time= Set-PSBreakpoint -Variable time -Mode Read -Action { $global:time= Get-Date -uformat "%T" }
 
## Log file
$ScriptLog = "D:\admin\LRCAS\LRCAS.log"
 
## Clear log before adding new data 
## Remove-Item $ScriptLog
 
## Location of 7-zip command-line executable
$7zaPath = "D:\admin\LRCAS"
 
## Folder to save archives in and check for old archives
$TargetFolder = "D:\scplogs\zip"
 
## Days to keep
$Days = "3"
 
## Define where logs are
$LogsFolder = "D:\scplogs\logs"
 
## Define files to match
$LogFileMatch = "*scplog?"
## * = Any or none characters
## ? = Any character (Must be one)
 
## Define extension of logs
$LogFileMatchExtension = ".txt"
 
## 7-zip ExitCodes and Types for EventLog
$MSGType=@{
"255"="Warning"
"8"="Error"
"7"="Error"
"2"="Error"
"1"="Warning"
"0"="Information"
}
 
## Message descriptions for each ExitCode from 7-zip.
## http://sevenzip.sourceforge.jp/chm/cmdline/exit_codes.htm
$MSG=@{
"255"="7-Zip: User stopped the process. 7-zip might not have zipped any files."
"8"="7-Zip: Not enough memory for operation."
"7"="7-Zip: Command line error(s) detected"
"2"="7-Zip: Fatal error"
"1"="7-Zip: One or more files were locked by some other application, so they were not compressed."
"0"="7-Zip: Compression was successful."
}
 
#######################
##                   ##
## DO NOT EDIT BELOW ##
##                   ##
#######################
 
## Function to see if running with administrator privileges
function Test-Administrator  
{  
    $user = [Security.Principal.WindowsIdentity]::GetCurrent();
    (New-Object Security.Principal.WindowsPrincipal $user).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)  
}
 
## If running with administrator privileges
If (Test-Administrator -eq $True) {
	## Create EventLog Source if not already exists
	if ([System.Diagnostics.EventLog]::SourceExists("$JOB") -eq $false) {
		"Creating EventLog Source `"$JOB`""
		[System.Diagnostics.EventLog]::CreateEventSource("$JOB", "Application")
	}
}
 
## Get files/items using above variables
$LogFiles = get-childitem $LogsFolder\$LogFileMatch$LogFileMatchExtension
 
## Initiate $FullMsg arrays
$FullMsg = @()
$FullMsg += "`n"
 
## Reset counter to 0
$Count = 0
 
## Archive scplogX.txt to $TargetFolder
foreach ($LogFile in $LogFiles) {
  ## Check to see if there is anything to archive
  if ($LogFiles -eq $null) {
	$FullMsg += "$date $time     $LogsFolder (`$LogsFolder) did not match any logs by the name of `"$LogFileMatch$LogFileMatchExtension`""
	If (Test-Administrator -eq $True) {
		Write-EventLog -LogName Application -Source $JOB -EventID 1 -EntryType Warning -Message "$LogsFolder (`$LogsFolder) did not match any logs by the name of `"$LogFileMatch$LogFileMatchExtension`""
		Start-Sleep -Seconds 1
	}
  }
  ## Continue if there are files to archive
  else {
	$LogLastWrite = $LogFile.LastWriteTime
	$LogFileName = $LogFile.Name
	$ZipLogFile = "$TargetFolder\$LogFileName`_$date.zip"
 
	## Archive scplogX.txt
	& $7zaPath\7za a -tzip $ZipLogFile $LogFile
	$7zaExitCode = $LastExitCode
 
	$ZipMsg = ("`"$LogFile`" to `"$ZipLogFile`"" + "`n" + $MSG."$7zaExitCode" + "`n" + "7-Zip: ExitCode=$7zaExitCode")
	## Write known 7za ExitCodes
	if ($MSG."$7zaExitCode" -gt $null) {
		$FullMsg += ("$date $time     `"$LogFile`" to `"$ZipLogFile`"`r`n" + "$date $time     " + $MSG."$7zaExitCode" + "`r`n" + "$date $time     7-Zip: ExitCode=$7zaExitCode")
		If (Test-Administrator -eq $True) {
			Write-EventLog -LogName Application -Source $JOB -EventID $7zaExitCode -EntryType $MSGType."$7zaExitCode" -Message "$ZipMsg"
			Start-Sleep -Seconds 1
		}
	}
	## Write unknown 7za ExitCodes
	else {
		$FullMsg += ("$date $time     `"$LogFile`" to `"$ZipLogFile`"`r`n" + "$date $time     7-Zip: Unknown ExitCode`r`n" + "$date $time     7-Zip: ExitCode=$7zaExitCode")
		If (Test-Administrator -eq $True) {
			Write-EventLog -LogName Application -Source $JOB -EventID $7zaExitCode -EntryType Warning -Message "`"$LogFile`" to `"$ZipLogFile`"`nUnknown ExitCode. EventID equals ExitCode"
			Start-Sleep -Seconds 1
		}
	}
 
	## Remove scplogX.txt file after it has been archived
	if ($7zaExitCode -eq "0") {
		Remove-Item $LogFile
		if($?) {
			$FullMsg += ("$date $time     Deleted: $LogFile")
			If (Test-Administrator -eq $True) {
				Write-EventLog -LogName Application -Source $JOB -EventID 0 -EntryType Information -Message "Deleted: $LogFile"
				Start-Sleep -Seconds 1
			}
		}
		else {
			$FullMsg += ("$date $time     Could not Delete: $LogFile")
			If (Test-Administrator -eq $True) {
				Write-EventLog -LogName Application -Source $JOB -EventID 1 -EntryType Error -Message "Could not Delete: $LogFile"
				Start-Sleep -Seconds 1
			}
		}
 
		## Retrieve Last Modified on Zip-files
		$ZipFileFolder = get-childitem $ZipLogFile
		foreach ($zip in $ZipFileFolder) {
			$ZipLastWrite = $zip.LastWriteTime
 
			## Set Last Modified on ZIP-file to match the scplog-file
			$zip.LastWriteTime = $LogLastWrite
			if($?) {
				$FullMsg += "$date $time     Replace `"Last Modified: $ZipLastWrite`" on `"$zip`" to `"$LogLastWrite`""
				If (Test-Administrator -eq $True) {
					Write-EventLog -LogName Application -Source $JOB -EventID 0 -EntryType Information -Message "Replace `"Last Modified: $ZipLastWrite`" on `"$zip`" to `"$LogLastWrite`""
					Start-Sleep -Seconds 1
				}
			}
			else {
				$FullMsg += "$date $time     Could not replace `"Last Modified: $ZipLastWrite`" on `"$zip`" to `"$LogLastWrite`""
				If (Test-Administrator -eq $True) {
					Write-EventLog -LogName Application -Source $JOB -EventID 1 -EntryType Error -Message "Could not replace `"Last Modified: $ZipLastWrite`" on `"$zip`" to `"$LogLastWrite`""
					Start-Sleep -Seconds 1
				}
			}
		}
	}
  }
}
 
## Required stuff
$Now = Get-Date
$OldDays = $Now.AddDays(-$Days)
$Folder = get-childitem $TargetFolder\$LogFileMatch`*
 
## Reset counter to 0
$CountOld = 0
 
## Begin looking for old files
foreach ($File in $Folder) {
  if ($Folder -eq $null) {
	$FullMsg += "$date $time     $TargetFolder (`$TargetFolder) contains no files (No archives to clean up?)"
	If (Test-Administrator -eq $True) {
		Write-EventLog -LogName Application -Source $JOB -EventID 1 -EntryType Warning -Message "$TargetFolder (`$TargetFolder) contains no files (No archives to clean up?)"
		Start-Sleep -Seconds 1
	}
  }
  else {
	$FileLastWrite = $File.LastWriteTime
		if ($File.LastWriteTime -le $OldDays) {
		## Count files. Add 1 if old 
		$CountOld = $CountOld + "1"
 
		## Delete files older than $Days days
		Remove-Item $File
		if($?) {
			$FullMsg += "$date $time     Delete: $File (FileLastWrite: $FileLastWrite is older than $Days days)" 
			If (Test-Administrator -eq $True) {
				Write-EventLog -LogName Application -Source $JOB -EventID 0 -EntryType Information -Message "Delete: $File`nFileLastWrite: $FileLastWrite is older than $Days days"
				Start-Sleep -Seconds 1
			}
		}
		else {
			$FullMsg += "$date $time     Could not Delete: $File (FileLastWrite: $FileLastWrite is older than $Days days)"
			If (Test-Administrator -eq $True) {
				Write-EventLog -LogName Application -Source $JOB -EventID 1 -EntryType Error -Message "Could not Delete: $File`nFileLastWrite: $FileLastWrite is older than $Days days"
				Start-Sleep -Seconds 1
			}
		}
    }
    else {
		## Count files. Add 0 if not old
		$CountOld = $CountOld + "0"
 
		## Keep files not older than $Days days
		$FullMsg += "$date $time     Keep: $File (FileLastWrite: $FileLastWrite is not older than $Days days)"
		If (Test-Administrator -eq $True) {
			Write-EventLog -LogName Application -Source $JOB -EventID 0 -EntryType Information -Message "Keep: $File`nFileLastWrite: $FileLastWrite is not older than $Days days"
			Start-Sleep -Seconds 1
		}
	}
  }
}
 
$FullMsg += "$date $time     Found $CountOld file(s) older than $Days days"
 
## Write to log and screen
Add-Content $ScriptLog $FullMsg -PassThru
 
"Script done"
## restore window title
$Host.UI.RawUI.WindowTitle = $titdef
 
Return
Share on Facebook

Leave a Reply