Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Logging in powershell with log rotation
# all logging settins are here on top
$logFile = "log-$(gc env:computername).log"
$logLevel = "DEBUG" # ("DEBUG","INFO","WARN","ERROR","FATAL")
$logSize = 1mb # 30kb
$logCount = 10
# end of settings
function Write-Log-Line ($line) {
Add-Content $logFile -Value $Line
Write-Host $Line
}
# http://stackoverflow.com/a/38738942
Function Write-Log {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$True)]
[string]
$Message,
[Parameter(Mandatory=$False)]
[String]
$Level = "DEBUG"
)
$levels = ("DEBUG","INFO","WARN","ERROR","FATAL")
$logLevelPos = [array]::IndexOf($levels, $logLevel)
$levelPos = [array]::IndexOf($levels, $Level)
$Stamp = (Get-Date).toString("yyyy/MM/dd HH:mm:ss:fff")
if ($logLevelPos -lt 0){
Write-Log-Line "$Stamp ERROR Wrong logLevel configuration [$logLevel]"
}
if ($levelPos -lt 0){
Write-Log-Line "$Stamp ERROR Wrong log level parameter [$Level]"
}
# if level parameter is wrong or configuration is wrong I still want to see the
# message in log
if ($levelPos -lt $logLevelPos -and $levelPos -ge 0 -and $logLevelPos -ge 0){
return
}
$Line = "$Stamp $Level $Message"
Write-Log-Line $Line
}
# https://gallery.technet.microsoft.com/scriptcenter/PowerShell-Script-to-Roll-a96ec7d4
function Reset-Log
{
# function checks to see if file in question is larger than the paramater specified
# if it is it will roll a log and delete the oldes log if there are more than x logs.
param([string]$fileName, [int64]$filesize = 1mb , [int] $logcount = 5)
$logRollStatus = $true
if(test-path $filename)
{
$file = Get-ChildItem $filename
if((($file).length) -ige $filesize) #this starts the log roll
{
$fileDir = $file.Directory
#this gets the name of the file we started with
$fn = $file.name
$files = Get-ChildItem $filedir | ?{$_.name -like "$fn*"} | Sort-Object lastwritetime
#this gets the fullname of the file we started with
$filefullname = $file.fullname
#$logcount +=1 #add one to the count as the base file is one more than the count
for ($i = ($files.count); $i -gt 0; $i--)
{
#[int]$fileNumber = ($f).name.Trim($file.name) #gets the current number of
# the file we are on
$files = Get-ChildItem $filedir | ?{$_.name -like "$fn*"} | Sort-Object lastwritetime
$operatingFile = $files | ?{($_.name).trim($fn) -eq $i}
if ($operatingfile)
{$operatingFilenumber = ($files | ?{($_.name).trim($fn) -eq $i}).name.trim($fn)}
else
{$operatingFilenumber = $null}
if(($operatingFilenumber -eq $null) -and ($i -ne 1) -and ($i -lt $logcount))
{
$operatingFilenumber = $i
$newfilename = "$filefullname.$operatingFilenumber"
$operatingFile = $files | ?{($_.name).trim($fn) -eq ($i-1)}
write-host "moving to $newfilename"
move-item ($operatingFile.FullName) -Destination $newfilename -Force
}
elseif($i -ge $logcount)
{
if($operatingFilenumber -eq $null)
{
$operatingFilenumber = $i - 1
$operatingFile = $files | ?{($_.name).trim($fn) -eq $operatingFilenumber}
}
write-host "deleting " ($operatingFile.FullName)
remove-item ($operatingFile.FullName) -Force
}
elseif($i -eq 1)
{
$operatingFilenumber = 1
$newfilename = "$filefullname.$operatingFilenumber"
write-host "moving to $newfilename"
move-item $filefullname -Destination $newfilename -Force
}
else
{
$operatingFilenumber = $i +1
$newfilename = "$filefullname.$operatingFilenumber"
$operatingFile = $files | ?{($_.name).trim($fn) -eq ($i-1)}
write-host "moving to $newfilename"
move-item ($operatingFile.FullName) -Destination $newfilename -Force
}
}
}
else
{ $logRollStatus = $false}
}
else
{
$logrollStatus = $false
}
$LogRollStatus
}
# to null to avoid output
$Null = @(
Reset-Log -fileName $logFile -filesize $logSize -logcount $logCount
)
. .\logger.ps1
Write-Log "Started" "INFO"
for ($i = 100; $i -gt 0; $i--)
{
Write-Log "debug message"
Write-Log "debug message2" "DEBUG"
Write-Log "info message" "INFO"
Write-Log "warn message" "WARN"
Write-Log "error message" "ERROR"
Write-Log "fatal message" "FATAL"
Write-Log "message with wrong level" "WRONG"
}
@kskewes

This comment has been minimized.

Copy link

commented May 3, 2018

Thank you for this.
I had an issue with logs rotating correctly, and the fix here resolves it: https://gallery.technet.microsoft.com/scriptcenter/PowerShell-Script-to-Roll-a96ec7d4/view/Discussions#content

Adding in the log rotation call into the Write-Log function takes care of rotating logs live without requiring application test.ps1 restart.

    # Check log size before writing and rotate it if oversize. May not be very performant. 
    $Null = @(
        Reset-Log -fileName $logFile -filesize $logSize -logcount $logCount
    )
@ICanCodeABit

This comment has been minimized.

Copy link

commented Dec 12, 2018

Thank you very much!

I had to replace line 65:
'$files = Get-ChildItem $filedir | ?{$.name -like "$fn*"} | Sort-Object lastwritetime'
with an explicit array declaration:
'$files = @(Get-ChildItem $filedir | ?{$
.name -like "$fn*"} | Sort-Object lastwritetime)'
because if only one file exists the script on my machine still wrote in the same file although the filesize exceeded.

@yokhoe

This comment has been minimized.

Copy link

commented Mar 3, 2019

I have a need to specify the location of where the log file writes to so I had to modify the Write-Log-Line function slightly. Perhaps it could be of use to some?

# help: https://stackoverflow.com/questions/48425562/powershell-add-content-should-create-path-but-throws-exception-could-not-find-a
function Write-Log-Line ($line) {
    $logFile |% { 
           If (Test-Path -Path $_) { Get-Item $_ } 
           Else { New-Item -Path $_ -Force } 
    } | Add-Content -Value $Line
    Write-Host $Line
}
@arberg

This comment has been minimized.

Copy link

commented Apr 17, 2019

Here's a version which keeps the index, and generates names like 'file.1.log', I also simplified the script a lot, though it might not do quite the same advanced index deletion. It now deletes all above $logcount, and then moves the rest into place

function Roll-logFile
{
    #function checks to see if file in question is larger than the paramater specified if it is it will roll a log and delete the oldes log if there are more than x logs.
    param([string]$fileName, [int64]$maxSize = 1mb, [int] $maxCount = 9)
    $logRollStatus = $true
    if(test-path $filename) {
        $file = Get-ChildItem $filename
        # Start the log-roll if the file is big enough
        if((($file).length) -ige $maxSize) {
            $fileDir = $file.Directory
            $fbase = $file.BaseName
            $fext = $file.Extension
            $fn = $file.name #this gets the name of the file we started with

            function refresh-log-files {
                 Get-ChildItem $filedir | ?{ $_.Extension -eq "$fext" -and $_.basename -like "$fbase*"} | Sort-Object lastwritetime
            }
            function fileByIndex($index) {
                $files | ?{($_.BaseName).trim("$fbase.") -eq $index}
            }
            function getNumberOfFile($theFile) {
                $theFile.BaseName.trim("$fbase.")
            }
            function getFileNameByNumber($index) {
                "$fbase.$index$fext"
            }

            refresh-log-files | %{
                $num=getNumberOfFile $_
                if ($num -ge $maxCount) {
                    write-host "Deleting files above logcount index $_"
                    Remove-Item $_
                }
            }

            $files = refresh-log-files

            # Now there should be atmost $maxCount files, and the highest number is one less than count, unless there are badly named files, eg non-numbers
            for ($i = ($files.count); $i -gt 0; $i--) {
                $newfilename = getFileNameByNumber $i
                if($i -gt 1) {
                    $fileToMove = fileByIndex ($i-1)
                } else {
                    $fileToMove = $file
                }
                if (Test-Path $fileToMove) { # If there are holes in sequence, file by index might not exist. The 'hole' will shift to next number, as files below hole are moved to fill it
                    write-host "moving '$fileToMove' => '$newfilename'"
                    # $fileToMove is a System.IO.FileInfo, but $newfilename is a string. Move-Item takes a string, so we need full path
                    Move-Item ($fileToMove.FullName) -Destination $fileDir\$newfilename -Force
                }
            }
        } else {
            $logRollStatus = $false
        }
    } else {
        $logrollStatus = $false
    }
    $LogRollStatus
}
@mntlfngrs

This comment has been minimized.

Copy link

commented Aug 8, 2019

@arberg
It seems like the roll only works when there are more than 1 file to start. Otherwise I get error "The property 'count' cannot be found on this object." Am I missing something? Sorry I don't have a fix for it at this point (except that I copy/renamed my log so it would have 2) but I'll be working on it as I get time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.