Skip to content
Browse files

Initial checkin of Logging module

  • Loading branch information...
1 parent 3820072 commit 26c4fbddf67182ac231381014c778cf06c0971db @russellseymour committed Mar 10, 2014
View
BIN Logging.psd1
Binary file not shown.
View
19 Logging.psm1
@@ -0,0 +1,19 @@
+# Get a list of the functions that need to be sourced
+$Functions = Get-ChildItem -Recurse "$PSScriptRoot\functions" -Include *.ps1
+
+# source each of the individual scripts
+foreach ($function in $functions) {
+ . $function.FullName
+}
+
+# get a list of the functions that need to be expotred
+$functions_to_export = $Functions | Where-Object { $_.FullName -match "Exported"} | ForEach-Object { $_.BaseName }
+
+$functions_to_export
+
+# Export the accessible functions
+Export-ModuleMember -function ( $functions_to_export )
+
+# Declare variable that will hold the log targets etc when other functions need
+# to use Write-Log
+$Logging = @{}
View
33 README.md
@@ -0,0 +1,33 @@
+# Logging
+
+One of the issues with PowerShell's built in 'Write-' commands is that it is not easy to capture the output from them and then parse that log. So if, for example, there was the following code in a script:
+
+ Write-Verbose "Creating new directory"
+
+This would be seen on the screen, but it is not available to put into a log file, or sent to the Event Log or even sent to Elastic Search.
+
+This module aims to fix that but creating a set of functions that add to the existing functionality of the standard 'Write-' commands. The commands from the module are 'Write-VerboseLog' which mimics 'Write-Verbose' and so on.
+
+## Credits
+
+My thanks to David Wyatt, whose own Logging module showed me how to extend the built in functions. (http://gallery.technet.microsoft.com/scriptcenter/Write-timestamped-output-4ff1565f). This provided me with the basis to capture the logs and then add the extra capabilities I wanted.
+
+## Features
+
+This is still being developed, but the main features are:
+
+ - Capture the message from any 'Write-*Log' function and process it as required
+ - Set an XML file as the resource to extract messages from using an EventId
+ - Process all messages as an object by calling 'Get-Messages' - useful to send to a different location such as Elastic Search.
+
+## To Do
+
+The following is a list of the things that are left to do, but it is probably not exhaustive:
+
+ - Not all the 'Write-' commands are supported yet. Only Write-Host and Write-Verbose are
+ - Add ability to add a global parameter for a log tag or prefix
+ - Log levels are currently linked to the appropriate 'Write-' command, however there maybe times when the 'Write-Host' function is required but with a different level and therefore differnt colour coding.
+ - Add comment based help to all the exported functions
+
+
+
View
17 functions/exported/Get-LogParameters.ps1
@@ -0,0 +1,17 @@
+function Get-LogParameters {
+
+ <#
+
+ .SYNOPSIS
+ Returns the current logging parameters
+
+ .DESCRIPTION
+ When a module is unloaded that has links to another module, functions within that module
+ can cease to be accessible. In this case the only way to get them back is to reimport the module.
+ This function will return the current logging parameters so that when a module is unlaoded, and it
+ takes out Logging then the parameters can be sent back in
+
+ #>
+
+ $Logging
+}
View
18 functions/exported/Get-Logs.ps1
@@ -0,0 +1,18 @@
+
+function Get-Logs {
+
+ <#
+
+ .SYNOPSIS
+ Returns the messages that have been stored in the logging object
+
+ #>
+
+ # Get the messages from the script:logging
+ $messages = $script:logging.messages
+
+ # ensure the logging messages are cleared out
+ $script:logging.messages = @()
+
+ $messages
+}
View
136 functions/exported/Set-LogParameters.ps1
@@ -0,0 +1,136 @@
+
+function Set-LogParameters {
+
+ <#
+
+ .SYNOPSIS
+ Sets various settings for the Write-Log function
+
+ .DESCRIPTION
+ Write-Log needs to know what resources file to use, the location of the additional providers directory,
+ and the options that need to passed. This function sets the module variable that can be read by the
+ Write-Log function. This avoids the need for global variables
+
+ The function adds a custom hash within the logging object. This is so that configuration data can be set
+ for additional providers that Write-Log might consume
+
+ #>
+
+ [CmdletBinding()]
+ param (
+
+ [string[]]
+ [Parameter(ParameterSetName="switches")]
+ # Output targets
+ # This list denotes the targets that the message for write-log should be sent to
+ $targets = @("screen"),
+
+ [string]
+ [Parameter(ParameterSetName="switches")]
+ # Log directory
+ # The path to the directory that LogFiles should be save in
+ $directory = [String]::Empty,
+
+ [string]
+ [Parameter(ParameterSetName="switches")]
+ # Filename
+ # The filename of the log file
+ $filename = [String]::Empty,
+
+ [string]
+ [Parameter(ParameterSetName="switches")]
+ [alias("helpfile")]
+ # Help Resources
+ # Path to the resources file that contains the messages to be used when invoking write-log
+ $resource_path = [String]::Empty,
+
+ [string[]]
+ [Parameter(ParameterSetName="switches")]
+ # Providers
+ # Path to another directory that contains providers that Write-Log can use
+ $providers = @(),
+
+ [Parameter(ParameterSetName="switches")]
+ # Custom
+ # This allows extra configuration to be passed to the Write-Log function
+ # Such use cases will be for custom providers that have been written
+ # If not set this item will not appear in the logging object
+ $custom = $false,
+
+ [Parameter(ParameterSetName="object")]
+ # Parameters
+ # This is an object that contains all of the settings that need to be defined in the module
+ $parameters
+ )
+
+ # Create the logging object
+ $Logging = $script:Logging
+
+ # set the logging hashtable up based on the paramater set
+ switch ($PsCmdlet.ParameterSetName) {
+
+ "switches" {
+
+ # Set the logging variable accordingly
+
+ # Log Targets
+ if (!($Logging.ContainsKey("targets")) -and ![String]::IsNullOrEmpty($targets)) {
+ $Logging.targets = $targets
+ }
+
+ # Options
+ if (!($Logging.ContainsKey("options")) -and ![String]::IsNullOrEmpty($options)) {
+ $Logging.options = $options
+ }
+
+ # Log directory and filename
+ if (!($Logging.ContainsKey("directory")) -and ![String]::IsNullOrEmpty($directory)) {
+ $Logging.directory = $directory
+ }
+ if (!($Logging.ContainsKey("filename")) -and ![String]::IsNullOrEmpty($filename)) {
+ $Logging.filename = $filename
+ }
+
+ # Set additional providers
+ if (!($Logging.ContainsKey("providers")) -and ![String]::IsNullOrEmpty($providers)) {
+ $Logging.providers_path = $providers
+ }
+
+ # Attempt to load the specified resources file
+ if (![String]::IsNullOrEmpty($resource_path) -and ($Logging.ContainsKey("resource")) -eq $false) {
+
+ # If the file exists then read it in as a XML object
+ if (Test-Path -Path $resource_path) {
+
+ [xml] $Logging.resource = Get-Content -Path $resource_path -Raw
+
+ } else {
+
+ Write-Warning -Message ("Unable to load helpfile as it cannot be located.`n`t{0}" -f $resource_path)
+ }
+
+ }
+
+ # Set a user attribute that can be set by parameters
+ if (!($Logging.ContainsKey("custom")) -and $custom -ne $false) {
+ $Logging.custom = $custom
+ }
+
+ }
+
+ "object" {
+
+ $Logging = $parameters
+
+ }
+
+ }
+
+ # Add an array that will hold any messages that are passed to the module
+ # so that they can be output as part of a pipeline at the end
+ $Logging.messages = @()
+
+ # The module variable is accessible at the script scope level
+ $script:Logging = $Logging
+
+}
View
56 functions/exported/Write-HostLog.ps1
@@ -0,0 +1,56 @@
+function Write-HostLog
+{
+ <#
+ .Synopsis
+ Proxy function for Write-Host. Optionally, also directs the output to a log file.
+ .DESCRIPTION
+ Has the same definition as Write-Host, with the addition of a -LogFile parameter. If this
+ argument has a value, it is treated as a file path, and the function will attempt to write
+ the output to that file as well (including creating the parent directory, if it doesn't
+ already exist). If the path is malformed or the user does not have permission to create or
+ write to the file, New-Item and Add-Content will send errors back through the output stream.
+
+ Non-blank lines in the log file are automatically prepended with a culture-invariant date
+ and time.
+ .PARAMETER LogFile
+ Specifies the full path to the log file. If this value is not specified, it will default to
+ the variable $LogFilePreference, which is provided for the user's convenience in redirecting
+ output from all of the Write-*Log functions to the same file.
+ .NOTES
+ unlike Write-Host, this function defaults the value of the -Separator parameter to
+ "`r`n". This is to make the console output consistent with what is sent to the log file,
+ where array elements are always written to separate lines (regardless of the value of the
+ -Separator parameter; if that argument is specified, it just gets passed on to Write-Host).
+ .LINK
+ Write-Host
+ #>
+
+ [CmdletBinding()]
+ param(
+ [Parameter(Position = 0, ValueFromPipeline = $true)]
+ [System.Object]
+ $Object,
+
+ [Switch]
+ $NoNewline,
+
+ [System.Object]
+ $Separator = "`r`n",
+
+ [System.ConsoleColor]
+ $ForegroundColor,
+
+ [System.ConsoleColor]
+ $BackgroundColor,
+
+ [System.String]
+ $LogFile = $null,
+
+ [System.Management.Automation.ScriptBlock]
+ $Prepend = { PrependString -Line $args[0] }
+ )
+
+ # Call the write-log function with all the parameters that have been passed
+ $PSBoundParameters.Add("host", $true)
+ Write-Log @PSBoundParameters
+}
View
326 functions/exported/Write-Log.ps1
@@ -0,0 +1,326 @@
+function Write-Log {
+
+
+ # Ensure the function picks up all the CommonParameters and set
+ # the default parameter set which will be used if none is specified
+ [CmdletBinding(DefaultParameterSetName="Host")]
+ param (
+
+ # Common Parameters ----------------------------------------
+
+ # The default prepend string to use if not specified
+ $Prepend,
+
+ [System.String]
+ # Path to logfile
+ $LogFile = $null,
+
+ [System.String[]]
+ # String array of targets to send the log information to
+ # These relate to the providers that can be found
+ $Targets,
+
+ [System.String]
+ # Event ID to extract from the resources file, if one has been specified
+ $EventId = [String]::Empty,
+
+ [int]
+ # Sevrity of the message
+ $severity = 0,
+
+ [System.String]
+ # Path to the resources file from which to load the messages
+ # Anything specified here will overwrite what has been spcified in Set-LogParameters
+ $resource = [String]::Empty,
+
+ # variable to hold extra information to be added to the message
+ # this is how default messages can be enchances
+ $extra = $false,
+
+#region Write-Host Parameters
+
+ [Parameter(ParameterSetName="Host")]
+ [switch]
+ # State if using Host output
+ $Host = [switch]::present,
+
+ [Parameter(Position = 0, ValueFromPipeline = $true, ParameterSetName = "Host")]
+ [System.Object]
+ $Object,
+
+ [Parameter(ParameterSetName="Host")]
+ [Switch]
+ $NoNewline,
+
+ [Parameter(ParameterSetName="Host")]
+ [System.Object]
+ $Separator = "`r`n",
+
+ [Parameter(ParameterSetName="Host")]
+ [System.ConsoleColor]
+ $ForegroundColor,
+
+ [Parameter(ParameterSetName="Host")]
+ [System.ConsoleColor]
+ $BackgroundColor,
+
+#endregion
+
+#region Write-Verbose parameters
+ [Parameter(Mandatory = $true, Position=0, ValueFromPipeline = $true, ParameterSetName="Verbose")]
+ [Alias('Msg')]
+ [AllowEmptyString()]
+ [System.String]
+ $Message
+#endregion
+
+ )
+
+ # Configure the function
+ begin
+ {
+
+ if (!($PSBoundParameters.ContainsKey("Host"))) {
+ $PSBoundParameters.Add("Host", $Host)
+ }
+
+ # determine the cmdlet being used and the PS command equivalent
+ # this is hierarchal so the order is Error, Warn, Info, Debug, Host
+ if ($PSCmdlet.MyInvocation.BoundParameters.Verbose.IsPresent) {
+ $powershellCommand = "Write-Verbose"
+
+ # it is possible that will be in the wrong parameterset if Write-Log command has been called
+ # directly without specifyin the switch for Message
+ # sort out the parameters
+ if ($PSCmdlet.ParameterSetName -ieq "host") {
+ $PSBoundParameters.Remove("Object") | out-Null
+ $PSBoundParameters.Add("Message", $object)
+ } else {
+ # set the object to be the message so that it can be processed
+ $Object = $Message
+ }
+
+ # if the prepend has not been set, set it here
+ if ([String]::IsNullOrEmpty($Prepend)) {
+ $Prepend = { PrependString -Line $args[0] -Flag '[V]' }
+ }
+
+ # set the log level
+ $level = "VERBOSE"
+ } elseif ($Host) {
+ $powershellCommand = "Write-Host"
+
+ # if the prepend has not been set, set it here
+ if ([String]::IsNullOrEmpty($Prepend)) {
+ $Prepend = { PrependString -Line $args[0] -Flag '[H]' }
+ }
+ $level = "INFO"
+ }
+
+
+ # Remove the host from the boundparameters
+ $PSBoundParameters.Remove("Host") | Out-Null
+
+ Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+
+ # Determine the location of the log file
+ if ($PSBoundParameters.ContainsKey('LogFile'))
+ {
+ # Log file location has come from the parameters
+ $_logFile = $LogFile
+ $null = $PSBoundParameters.Remove('LogFile')
+ }
+ elseif (![String]::IsNullOrEmpty($script:logging.directory) -and ![String]::IsNullOrEmpty($script:logging.filename))
+ {
+ $_logFile = "{0}\{1}" -f $script:logging.directory, $script:logging.filename
+ }
+ else
+ {
+ # has been set in the variables
+ $_logFile = $PSCmdlet.GetVariableValue("LogFilePreference")
+ }
+
+ # set the targets to use if it has not been spcified on the command line
+ if (!($PSBoundParameters.ContainsKey("Targets"))) {
+ if ([String]::IsNullOrEmpty($script:Logging.targets)) {
+ $Targets = @("screen")
+ } else {
+ $Targets = $script:Logging.targets
+ }
+ }
+
+ # if a log file has been set and not in the targets then append to the targets
+ if (![String]::IsNullOrEmpty($_logFile) -and $Targets -notcontains "logfile") {
+ $Targets += "logfile"
+ }
+
+ # Remove any parameters that would cause the default PowerShell functions to fail
+ $null = $PSBoundParameters.Remove('Prepend')
+ $null = $PSBoundParameters.Remove('Targets')
+ $null = $PSBoundParameters.Remove('EventId')
+ $null = $PSBoundParameters.Remove('Severity')
+ $null = $PSBoundParameters.Remove('Resource')
+ $null = $PSBoundParameters.Remove('Extra')
+
+ $outBuffer = $null
+
+ if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
+ {
+ $PSBoundParameters['OutBuffer'] = 1
+ }
+
+ # if the Logging object does not have a messages array add it now
+ if (!$script:Logging.ContainsKey("messages")) {
+ $script:Logging.messages = @()
+ }
+
+ # Build up array of paths to look for providers in
+ # Work out the path to the built in providers
+ $module_path = Split-Path -Parent -Path (Get-Module -Name Logging).Path
+
+ # if the script logging providers does not exist then build the array
+ $provider_paths = @()
+ if ([String]::IsNullOrEmpty($script:Logging.providers) -and $script:Logging.providers.count -gt 0) {
+ $provider_paths = $provider_paths + $script:Logging.providers
+ }
+ $provider_paths += "{0}\providers" -f $module_path
+
+
+ }
+
+ process
+ {
+ # Attempt to load the resources file
+ $resource = Load-Help -Path $resource
+
+ # Find the message from the eventid and return as a message object
+ $msg, $formatting = Get-HelpMessage -EventId $EventId -Resource $resource -Message $Object -ForegroundColor $ForegroundColor -BackgroundColor $BackgroundColor -Prepend $Prepend
+
+ # Get the message structure to work with
+ $structure = Format-Message -Level $level -EventId $EventId -Message $msg -Severity $severity
+
+ # add the message to the logging.messages
+ $script:logging.messages += $structure
+
+ # if in Host parameter set then format the object regarding indents
+ if ($PSCmdlet.ParameterSetname -ieq "host") {
+ # determine the indent
+ $prefix = ""
+ for ($i = 0; $i -lt $formatting.indent; $i ++) {
+ $prefix += $formatting.indent_string
+ }
+
+ # now set the object to include the prefix
+ $PSBoundParameters.Object = "{0}{1}" -f $prefix, $PSBoundParameters.Object
+ }
+
+ # Get the powershell built in command
+ try
+ {
+ $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand($powershellCommand, [System.Management.Automation.CommandTypes]::Cmdlet)
+ $scriptCmd = {& $wrappedCmd @PSBoundParameters }
+ $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
+ $steppablePipeline.Begin($PSCmdlet)
+ }
+ catch
+ {
+ throw
+ }
+
+ # build up parameters that are to be passed to each of the providers
+ $parameters = @{
+
+ # the actual message
+ structure = $structure
+
+ # set the formatting to be applied
+ formatting = $formatting
+
+ # if the message should be output without a newline
+ nonewline = $nonewline
+
+ # the path to the logfile
+ logfile = $_logFile
+ }
+
+ # iterate around the priovider paths and find each of the providers
+ $providers = @()
+ foreach ($provider_path in $provider_paths) {
+
+ # continue onto the next iteration if the path does not exist
+ if (!(Test-Path -Path $provider_path)) {
+ Write-Warning -Message ("Provider path does not exist: {0}" -f $provider_path)
+ continue
+ }
+
+ # get items from each path
+ $items = Get-ChildItem -Path $provider_paths -Include *.ps1 -Recurse
+
+ # add to the providers
+ $providers = $providers + $items
+ }
+
+ # ensue that there are only a list of unique items in the array
+ $providers = $providers | Select -Unique
+
+ # iterate around the targets that have been specified
+ foreach ($Target in $Targets) {
+
+ # if target is screen then continue to next iteration, this is because
+ # this function will handle screen
+ if ($Target -ieq "screen") {
+ continue
+ }
+
+ # check that the provider exists
+ $provider_file = $providers | Where-Object { $_.name -match ("^{0}" -f $Target) }
+
+ # source the file if it has been found
+ if (![String]::IsNullOrEmpty($provider_file)) {
+
+ # source the file
+ . ($provider_file.Fullname)
+
+ # determine the parameters that the provider expects
+ $splat = @{}
+ foreach ($param in (Get-Command Set-Message).Parameters.Keys) {
+
+ # only add parameters to the splat that are expected by the function
+ if (![String]::IsNullOrEmpty($parameters.$param)) {
+ $splat.$param = $parameters.$param
+ }
+ }
+
+ # Invoke the provider by splatting the function
+ "Set-Message @splat" | Invoke-Expression
+ }
+ }
+
+
+
+ if ($Targets -contains "screen") {
+ try
+ {
+ $steppablePipeline.Process($_)
+ }
+ catch
+ {
+ throw
+ }
+ }
+ }
+
+ end
+ {
+ if ($Targets -contains "screen") {
+ try
+ {
+ $steppablePipeline.End()
+ }
+ catch
+ {
+ throw
+ }
+ }
+ }
+}
View
40 functions/exported/Write-VerboseLog.ps1
@@ -0,0 +1,40 @@
+function Write-VerboseLog {
+ <#
+ .Synopsis
+ Proxy function for Write-Verbose. Optionally, also directs the verbose output to a log file.
+ .DESCRIPTION
+ Has the same definition as Write-Verbose, with the addition of a -LogFile parameter. If this
+ argument has a value, it is treated as a file path, and the function will attempt to write
+ the debug output to that file as well (including creating the parent directory, if it doesn't
+ already exist). If the path is malformed or the user does not have permission to create or
+ write to the file, New-Item and Add-Content will send errors back through the output stream.
+
+ Non-blank lines in the log file are automatically prepended with a culture-invariant date
+ and time, and with the text [V] to indicate this output came from the verbose stream.
+ .PARAMETER LogFile
+ Specifies the full path to the log file. If this value is not specified, it will default to
+ the variable $LogFilePreference, which is provided for the user's convenience in redirecting
+ output from all of the Write-*Log functions to the same file.
+ .LINK
+ Write-Verbose
+ #>
+
+ [CmdletBinding()]
+ param(
+ [Parameter(Mandatory = $true, Position=0, ValueFromPipeline = $true)]
+ [Alias('Msg')]
+ [AllowEmptyString()]
+ [System.Object]
+ $Message,
+
+ [System.String]
+ $LogFile = $null,
+
+ [System.Management.Automation.ScriptBlock]
+ $Prepend = { PrependString -Line $args[0] -Flag '[V]' }
+ )
+
+ # Call the write-log function with all the parameters that have been passed
+ $PSBoundParameters.Add("verbose", $true)
+ Write-Log @PSBoundParameters
+}
View
32 functions/formatting/PrependString.ps1
@@ -0,0 +1,32 @@
+function PrependString
+{
+ [CmdletBinding()]
+ param (
+ [System.String]
+ $Line,
+
+ [System.String]
+ $Flag
+ )
+
+ if ($null -eq $Line)
+ {
+ $Line = [System.String]::Empty
+ }
+
+ if ($null -eq $Flag)
+ {
+ $Flag = [System.String]::Empty
+ }
+
+ if ($Line.Trim() -ne '')
+ {
+ $prependString = "[$(Get-Date -Date ([DateTime]::UTCNow) -uformat "+%Y-%m-%dT%H:%M:%SZ")] - "
+ if (-not [System.String]::IsNullOrEmpty($Flag))
+ {
+ $prependString += "$Flag "
+ }
+
+ Write-Output $prependString
+ }
+}
View
157 functions/preferences/Get-CallerPreference.ps1
@@ -0,0 +1,157 @@
+function Get-CallerPreference
+{
+ <#
+ .Synopsis
+ Fetches "Preference" variable values from the caller's scope.
+ .DESCRIPTION
+ Script module functions do not automatically inherit their caller's variables, but they can be
+ obtained through the $PSCmdlet variable in Advanced Functions. This function is a helper function
+ for any script module Advanced Function; by passing in the values of $ExecutionContext.SessionState
+ and $PSCmdlet, Get-CallerPreference will set the caller's preference variables locally.
+ .PARAMETER Cmdlet
+ The $PSCmdlet object from a script module Advanced Function.
+ .PARAMETER SessionState
+ The $ExecutionContext.SessionState object from a script module Advanced Function. This is how the
+ Get-CallerPreference function sets variables in its callers' scope, even if that caller is in a different
+ script module.
+ .PARAMETER Name
+ Optional array of parameter names to retrieve from the caller's scope. Default is to retrieve all
+ Preference variables as defined in the about_Preference_Variables help file (as of PowerShell 4.0)
+ This parameter may also specify names of variables that are not in the about_Preference_Variables
+ help file, and the function will retrieve and set those as well.
+ .EXAMPLE
+ Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+
+ Imports the default PowerShell preference variables from the caller into the local scope.
+ .EXAMPLE
+ Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -Name 'ErrorActionPreference','SomeOtherVariable'
+
+ Imports only the ErrorActionPreference and SomeOtherVariable variables into the local scope.
+ .EXAMPLE
+ 'ErrorActionPreference','SomeOtherVariable' | Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+
+ Same as Example 2, but sends variable names to the Name parameter via pipeline input.
+ .INPUTS
+ String
+ .OUTPUTS
+ None. This function does not produce pipeline output.
+ .LINK
+ about_Preference_Variables
+ #>
+
+ [CmdletBinding(DefaultParameterSetName = 'AllVariables')]
+ param (
+ [Parameter(Mandatory = $true)]
+ [ValidateScript({ $_.GetType().FullName -eq 'System.Management.Automation.PSScriptCmdlet' })]
+ $Cmdlet,
+
+ [Parameter(Mandatory = $true)]
+ [System.Management.Automation.SessionState]
+ $SessionState,
+
+ [Parameter(ParameterSetName = 'Filtered', ValueFromPipeline = $true)]
+ [string[]]
+ $Name
+ )
+
+ begin
+ {
+ $filterHash = @{}
+ }
+
+ process
+ {
+ if ($null -ne $Name)
+ {
+ foreach ($string in $Name)
+ {
+ $filterHash[$string] = $true
+ }
+ }
+ }
+
+ end
+ {
+ # List of preference variables taken from the about_Preference_Variables help file in PowerShell version 4.0
+
+ $vars = @{
+ 'ErrorView' = $null
+ 'FormatEnumerationLimit' = $null
+ 'LogCommandHealthEvent' = $null
+ 'LogCommandLifecycleEvent' = $null
+ 'LogEngineHealthEvent' = $null
+ 'LogEngineLifecycleEvent' = $null
+ 'LogProviderHealthEvent' = $null
+ 'LogProviderLifecycleEvent' = $null
+ 'MaximumAliasCount' = $null
+ 'MaximumDriveCount' = $null
+ 'MaximumErrorCount' = $null
+ 'MaximumFunctionCount' = $null
+ 'MaximumHistoryCount' = $null
+ 'MaximumVariableCount' = $null
+ 'OFS' = $null
+ 'OutputEncoding' = $null
+ 'ProgressPreference' = $null
+ 'PSDefaultParameterValues' = $null
+ 'PSEmailServer' = $null
+ 'PSModuleAutoLoadingPreference' = $null
+ 'PSSessionApplicationName' = $null
+ 'PSSessionConfigurationName' = $null
+ 'PSSessionOption' = $null
+
+ 'ErrorActionPreference' = 'ErrorAction'
+ 'DebugPreference' = 'Debug'
+ 'ConfirmPreference' = 'Confirm'
+ 'WhatIfPreference' = 'WhatIf'
+ 'VerbosePreference' = 'Verbose'
+ 'WarningPreference' = 'WarningAction'
+ }
+
+
+ foreach ($entry in $vars.GetEnumerator())
+ {
+ if (([string]::IsNullOrEmpty($entry.Value) -or -not $Cmdlet.MyInvocation.BoundParameters.ContainsKey($entry.Value)) -and
+ ($PSCmdlet.ParameterSetName -eq 'AllVariables' -or $filterHash.ContainsKey($entry.Name)))
+ {
+ $variable = $Cmdlet.SessionState.PSVariable.Get($entry.Key)
+
+ if ($null -ne $variable)
+ {
+ if ($SessionState -eq $ExecutionContext.SessionState)
+ {
+ Set-Variable -Scope 1 -Name $variable.Name -Value $variable.Value -Force
+ }
+ else
+ {
+ $SessionState.PSVariable.Set($variable.Name, $variable.Value)
+ }
+ }
+ }
+ }
+
+ if ($PSCmdlet.ParameterSetName -eq 'Filtered')
+ {
+ foreach ($varName in $filterHash.Keys)
+ {
+ if (-not $vars.ContainsKey($varName))
+ {
+ $variable = $Cmdlet.SessionState.PSVariable.Get($varName)
+
+ if ($null -ne $variable)
+ {
+ if ($SessionState -eq $ExecutionContext.SessionState)
+ {
+ Set-Variable -Scope 1 -Name $variable.Name -Value $variable.Value -Force
+ }
+ else
+ {
+ $SessionState.PSVariable.Set($variable.Name, $variable.Value)
+ }
+ }
+ }
+ }
+ }
+
+ } # end
+
+}
View
126 functions/resources/Get-HelpMessage.ps1
@@ -0,0 +1,126 @@
+
+function Get-HelpMessage {
+
+ <#
+
+ .SYNOPSIS
+ Return the help message from the help resource
+
+ .DESCRIPTION
+ Attempt to get a message from the resource and return a message object
+ This will contain any formatting information and colours that need to be applied
+ These extra details are for the providers and not the message itself
+ #>
+
+ [CmdletBinding()]
+ param (
+
+ [System.String]
+ # EventId to look for
+ $EventId,
+
+ # Resource containing the help message
+ $resource,
+
+ [System.String]
+ # Message to be put into the object if an eventid has not been set
+ $message,
+
+ [int]
+ # Any indent that should be applied to the message
+ $indent = 0,
+
+ [string]
+ # the string to use for the indent
+ $indent_string = " ",
+
+ # any string that needs to be prenpdened to the output
+ $prepend,
+
+ # An extra infromation that needs to be taken into account
+ $extra,
+
+ # Ensure that the colours from the calling function are pulled in
+ $ForegroundColor,
+ $BackgroundColor
+
+ )
+
+ # build up the message object to return
+ $object = @{
+ text = $message
+ }
+
+ $formatting = @{
+ indent = $indent
+ indent_string = $indent_string
+ foregroundcolor = $ForegroundColor
+ backgroundcolor = $BackgroundColor
+ prepend = $prepend
+ }
+
+ if ($resource -ne $false) {
+
+ # Build up the xpath to find the message
+ $xpath = "//resource[@code='{0}']" -f $eventid
+ Write-Debug -Message ("Message Xpath: '{0}'" -f $xpath)
+
+ # Get the item from the resources file
+ $item = $resource.SelectSingleNode($xpath)
+
+ # now if item is not null then set properties of the object
+ if (![String]::IsNullOrEmpty($resource)) {
+
+ # set the various parts of the object
+ if (![String]::IsNullOrEmpty($item.message)) {
+ $object.text = $ExecutionContext.InvokeCommand.ExpandString($item.message)
+ }
+
+ # check for indent
+ if (![String]::IsNullOrEmpty($item.indent)) {
+ $formatting.indent = $item.indent
+ }
+
+ # Set the foreground and background colours
+ if (![String]::IsNullOrEmpty($item.colours.foreground)) {
+ $formatting.foregroundcolor = $item.colours.foreground
+ }
+
+ if (![String]::IsNullOrEmpty($item.colours.foreground)) {
+ $formatting.foregroundcolor = $item.colours.foreground
+ }
+
+ } elseif ([String]::IsNullOrEmpty($message)) {
+
+ # if no message has been passed then set a default one
+ $object.text = "No message can be found for EventId '{0}'" -f $eventid
+ }
+ }
+
+ # Now that the object has been configured, check the message to see if any placeholders are to be replaced
+ if ($extra -ne $false) {
+
+ # turn the extra information into an array, if not already one
+ # this is so that is can be easily substituted inline or all the elements output on new lines beneath the main message
+ if ($extra -is [String]) {
+ $extra = @($extra)
+ } elseif ($extra -is [Hashtable]) {
+ $extra = $extra.Keys | Sort-Object $_ | ForEach-Object {"{0}: {1}" -f $_, ($extra.$_)}
+ }
+
+ # determine if the message text has any placeholders
+ $groups = [Regex]::Matches($object.text, "({[0-9]+})")
+ if ($groups.count -gt 0) {
+
+ # placeholders have been found so replace them with the extra array
+ $object.text = $object.text -f $extra
+ } else {
+
+ # there are no placeholders so add the extra to the object
+ $object.extra = $extra
+ }
+ }
+
+ # pass the message object and the formatting that needs to be applied
+ $object, $formatting
+}
View
65 functions/resources/Load-Help.ps1
@@ -0,0 +1,65 @@
+
+function Load-Help {
+
+ <#
+
+ .SYSNOPSIS
+ Attempt to load the specified help resources file
+
+ .DESCRIPTION
+ Function to load the resources file that has been specified to Write-Log or in the Set-LogParameters
+
+ #>
+
+ [CmdletBinding()]
+ param (
+
+ [System.String]
+ # Path to the resource file
+ $Path,
+
+ [switch]
+ # Denote if the recursing should stop
+ $norecurse
+ )
+
+ # Set a default value for the resource
+ $resource = $false
+
+ # Is the path empty?
+ # Is it valid?
+ # If either of these are not true then use the one in the Logging parameters
+
+ if (![String]::IsNullOrEmpty($path) -and (Test-Path -Path $Path)) {
+
+ # the path exists so attempt to load it
+ [xml] $resource = Get-Content -Path $Path -Raw
+
+ } else {
+
+ # only proceed if not norecurse
+ if (!$norecurse) {
+
+ # either the path is invalid or it is empty so attempt to get from the session
+ if (![String]::IsNullOrEmpty($script:Logging.resource)) {
+
+ # Check to see if the resource in the logging object is a string
+ if ($script:Logging.resource -is [System.String]) {
+
+ # call this function again
+ $resource = Load-Help -Path $script:Logging.resource -Norecurse
+ }
+
+ # if the logging object is an xmldocument then set that
+ if ($script:Logging.resource -is [XML.XMLDocument]) {
+ $resource = $script:Logging.resource
+ }
+ }
+ }
+ }
+
+ # return the resource
+ $resource
+
+
+}
View
76 functions/structure/Format-Message.ps1
@@ -0,0 +1,76 @@
+
+function Format-Message {
+
+ <#
+
+ .SYNOPSIS
+ Put the message into a object
+
+ .DESCRIPTION
+ Each message that is logged with the Logging module will be added to a structure
+ so that it can be passed to the providers with all the information attached
+
+ #>
+
+ [CmdletBinding()]
+ param (
+
+ [System.String]
+ # The level at which the message should be set
+ $level,
+
+ [System.String]
+ # Event ID associated with the message
+ $eventid,
+
+ [int]
+ # Severity of the message
+ $severity,
+
+ # Message to be added to the object
+ $message,
+
+ [System.String]
+ # Extra information to be assigned to the message
+ # If the message contains subsitution placeholders then this will be injected
+ $extra
+ )
+
+
+ # Create the object
+ $structure = @{
+
+ # Set the hostname of the machine
+ hostname = [System.Net.DNS]::GetHostByName(($env:COMPUTERNAME)).Hostname
+
+ # Set the eventid on the object which relates to the message
+ eventid = $eventid
+
+ # set the level for the message
+ level = $level
+
+ # add the severity
+ severity = $severity
+
+ # set the date stamp
+ timestamp = (Get-Date -Format "yyyy-MM-dd HH:mm:ss")
+
+ # add the message information
+ message = @{
+
+ # text of the message
+ text = $message.text
+
+ }
+
+ }
+
+ # If any extra information has been passed in the messahe object add it
+ if ($message.containskey("extra")) {
+ $structure.message.extra = $message.extra
+ }
+
+ # Return the structure
+ $structure
+
+}
View
51 providers/LogFile.ps1
@@ -0,0 +1,51 @@
+
+function Set-Message {
+
+ <#
+
+ .SYNOPSIS
+ Output the message to a log file
+
+ #>
+
+ [CmdletBinding()]
+ param (
+
+ [Hashtable]
+ [Parameter(Mandatory=$true)]
+ # Structure of the message
+ $structure,
+
+ [Hashtable]
+ # Hashtable containing any formatting, in this case it will be the prepend that is of interest
+ $formatting,
+
+ [System.String]
+ # path to log file to append data
+ $logfile
+
+ )
+
+ # iterate around each item in the message text
+ # as this could be an object
+ foreach ($line in ($structure.message.text | Out-String -Stream)) {
+
+ # if there is a prepend item then use that
+ if (![String]::IsNullOrEmpty($formatting.prepend)) {
+
+ # invoke the prepend script block
+ $results = $formatting.prepend.invoke($line)
+
+ # if there are results add the information tot heline
+ if ($results.count -gt 0 -and ($prependString = $results[0]) -is [System.String])
+ {
+ $line = "{0} - {1} - {2} - {3}" -f $prependString, $structure.severity, $structure.eventid, $line
+ }
+ }
+
+ # Add the line to the log file
+ Add-Content -Path $logfile -Value $line
+ }
+
+
+}

0 comments on commit 26c4fbd

Please sign in to comment.
Something went wrong with that request. Please try again.