Just to clarify, a framework is the basic structure underlying a concept or to paraphrase, a framework defines a set of guidelines which when implemented are known to produce beneficial results.
In the first part; we looked a little bit at why logging was important, established a rudimentary of set guidelines to help us log the script actions and also started taking first steps in our journey to creating a logging framework. So, onwards we move.
The last script block we looked at in the first part, we defined various message types that we wanted to log, we also define an array of severity descriptors and we defined a default logging level that can be overridden during initialization.
Since, one of the goals we set at the onset was to keep it simple; we should not be asking the user where the log file will be created by default, but, at the same time we should be flexible enough to accept any valid path they provide to setup the log file. So, let us by default use the module name and location for log file name. Oh! and the log file always have ‘.log’ extension. The code to do this would be:
# By Default use module name and location for LogFile Name and Script Name. # Caller should instantiate Logger object and set file properties based on script name. $SCRIPT:LogFileName = [System.IO.Path]::GetFilenameWithoutExtension($MyInvocation.MyCommand.Path.ToString()) $SCRIPT:ScriptName = [System.IO.Path]::GetFilenameWithoutExtension($MyInvocation.MyCommand.Path.ToString()) $SCRIPT:LogFileName += '.log'
The other thing we could do is maintain only a limited number of logfiles. Truth be told, having 200 logfiles is not going to help unless, you are looking for an anamoly that concerns counting. So, our code above gets a new line that can tell us how many log files we want. Again, we can use a default value if the user cannot be bothered or allow the user to manage this number if they are interested in this.
[int]$SCRIPT:NumOfArchives = 10
Now that we have some of the groundwork prepared, let us attmept to write a funciton that will eventually write the messages to the log file. At this point you might be thinking, should I be logging everything? Not necessarily. In order to avoid an overflow of information we will only log messages that have a severity level greater than or equal to the logging level we defined earlier. Also, not only should we write the messages to the log file, we also need to display the information on the host as well. In order to display the information on the host, we will write Information in magenta, warnings in yellow and errors in red.
Function Write-Log {
<#
.SYNOPSIS
Write a message to the Log file.
.DESCRIPTION
Logs a message to the logfile if the severity is higher than or equal to $LogLevel.
Default severity level is information.
.PARAMETER scriptName
Name of the script/program to be used with logged messages.
Use the $MSGTYPE_XXXX constants.
.PARAMETER logName
Full Name of the file where messages will be written.
.PARAMETER severity
The severity of the message. Can be Information, Warning, or Error.
Use the $MSGTYPE_XXXX constants.
.PARAMETER message
A string to be printed to the log.
.EXAMPLE
Write-Log $MSGTYPE_ERROR "Something has gone terribly wrong!"
#>
param(
[string]
$scriptName = $SCRIPT:ScriptName,
[string]
$logName = $SCRIPT:LogFileName,
[Parameter(Mandatory=$true)]
[int]
[ValidateScript({$MSGTYPE_INFORMATION, $MSGTYPE_WARNING, $MSGTYPE_ERROR -contains $_})]
$severity,
[Parameter(Mandatory=$true)]
[string]
$message
)
try
{
if ($severity -ge $LogLevel)
{
$timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
$callerName = (Get-PSCallStack)[2].InvocationInfo.MyCommand.Name
$output = "$timestamp`t`[$($SEVERITY_DESC[$severity])`]: ($callerName)`t$message"
Write-Output $output >> $logName
switch($severity)
{
$MSGTYPE_INFORMATION {Write-Host $output -Fore Magenta; break}
$MSGTYPE_WARNING {Write-Host $output -Fore Yellow; break}
$MSGTYPE_ERROR {Write-Host $output -Fore Red; break}
}
}
}
catch
{
$ex = $_.Exception
$excMsg = $ex.Message.ToString()
Write-Host "[Write-Log]: "$excMsg -Fore Red
while ($ex.InnerException)
{
$ex = $ex.InnerException
$excMsg = $ex.InnerException.Message.ToString()
Write-Host "`t" $ex.InnerException.Message -Fore Red
}
}
}
In the next part we will talk about managing the log files. All the fun stuff with sweeping the files and keeping the correct number of log files available, so on and so forth.
Leave a comment