# encoding: ascii # api: powershell # title: Write-Log # description: A first draft at a logging function that can rotate log files as they “fill up” (that is, not by date, but by size). # version: 0.1 # type: function # author: Joel Bennett # license: CC0 # function: Write-Log # x-poshcode-id: 4066 # x-archived: 2015-05-04T21:47:18 # x-published: 2015-04-03T04:40:00 # # function Write-Log { #.Synopsis # Write to a rotating log file #.Example # Get-ChildItem C:\ -Recurse | Select FullName, CreationTimeUtc, LastWriteTimeUtc | Write-Log Files.txt -Rotate 5mb # # Writes a file list to disc, breaking it into multiple files of approximately 5MB of data each [CmdletBinding()] param( # The file path to log to [Parameter(ValueFromPipelineByPropertyName=$true, Mandatory=$true, Position=0)] [Alias("PSPath")] $FilePath, [Parameter(Position=1)] [ValidateSet('unknown','string','unicode','bigendianunicode','utf8','utf7','utf32','ascii','default','oem')] [ValidateNotNullOrEmpty()] [string] ${Encoding} = 'utf8', [ValidateRange(2, 2147483647)] [int] ${Width}, # A max file size at which to rotate the log file [Parameter()] [Alias("Rotate","Length")] [int]$SizeLimit = 10MB, # A max file size at which to rotate the log file [Parameter()] [Alias("Count")] [int]$FileCountLimit = 5, # The data to log [Parameter(ValueFromPipeline=$true, Mandatory=$true)] $InputObject ) begin { try { if($PSBoundParameters.ContainsKey('OutBuffer')) { $PSBoundParameters.Remove('OutBuffer') } if($PSBoundParameters.ContainsKey('SizeLimit')) { $PSBoundParameters.Remove('SizeLimit') } if(!$PSBoundParameters.ContainsKey('Encoding')) { $PSBoundParameters.Add('Encoding', $Encoding) } # Pipeline-bound parameters will be set on $PSBoundParameters later (in the process block) # Since we have to (re)create the steppablePipeline, we need a copy of them as they are now $Parameters = $PSBoundParameters.Clone() $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand( 'Microsoft.PowerShell.Utility\Out-File', [System.Management.Automation.CommandTypes]::Cmdlet) $scriptCmd = {& $wrappedCmd @Parameters -Append } $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin) $steppablePipeline.Begin($PSCmdlet) } catch { throw } } process { try { $steppablePipeline.Process($_) # If the file triggers rotation: if(($file = Get-Item $FilePath) -and $file.Length -gt $SizeLimit) { # Remove the last item if it would go over the limit $steppablePipeline.End() if(Test-Path "$FilePath.$FileCountLimit)") { Write-Verbose "Removing $FilePath.$FileCountLimit)" Remove-Item "$FilePath.$FileCountLimit)" } foreach($i in ($FileCountLimit)..1) { if(test-path "$FilePath.$($i-1)") { Move-Item "$FilePath.$($i-1)" "$FilePath.$i" } } Move-Item $FilePath "$FilePath.$i" $null = New-Item $FilePath -Type File $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin) $steppablePipeline.Begin($PSCmdlet) } } catch { throw } } end { try { $steppablePipeline.End() } catch { throw } } } ## Bonus: the log rotation logic as a stand alone function... function Rotate-Log { #.Synopsis # Rotate a log file if it's gotten too big [CmdletBinding()] param( # The file of the path to test and rotate [Parameter(ValueFromPipelineByPropertyName=$true, Mandatory=$true, Position=0)] [Alias("PSPath")] $FilePath, # When a log file goes over this size, rotate it $SizeLimit = 10MB, # How many backup logs (besides the primary log file) to keep $LogLimit = 5 ) process { foreach($LogFile in Resolve-Path $FilePath) { # If the file triggers rotation: if(($file = Get-Item $LogFile) -and $file.Length -gt $SizeLimit) { # Remove the last item if it would go over the limit if(Test-Path "$LogFile.$LogLimit") { Remove-Item "$LogFile.$LogLimit" } foreach($i in $LogLimit..1) { if(test-path "$LogFile.$($i-1)") { Move-Item "$LogFile.$($i-1)" "$LogFile.$i" } } Move-Item $LogFile "$LogFile.$i" $null = New-Item $LogFile -Type File } } } }