PoshCode Logo PowerShell Code Repository

Logger.psm1 0.2 by Joel Bennett 22 months ago (modification of post by Joel Bennett view diff)
View followups from Joel Bennett and Joel Bennett | diff | embed code: <script type="text/javascript" src="http://PoshCode.org/embed/1745"></script>download | new post

The simplest logger. In your script just import-module Logger and debug, verbose, warnings and errors are logged to file.

This is a simple wrapper around Log4Net which (by default) causes Write-Verbose, Write-Warning, Write-Debug, and Write-Error to log their output when called from (any) script.

By default it logs to file, but it can log to the event log, console, .net trace, etc by simply using the Get-Logger function to configure new loggers (help is next)

There are a few extra helper methods in here, including Push-Context/Pop-Context (which you can call in your functions to enable stack tracing), and still more to come.

  1. <#
  2.   Name     : Universal Log4Net Logging Module (Logger.psm1)
  3.   Version  : 0.2
  4.   Author   : Joel Bennett (MVP)
  5.   Site     : http://www.HuddledMasses.org/
  6.  
  7.   Version History:
  8.   0.2 - Configless release.
  9.         Now configures with inline XML, and supports switches to create "reasonable" default loggers
  10.         Changed all the functions to Write-*Log so they don't overwrite the cmdlets
  11.         Added -Logger parameter to take the name of the logger to use (it must be created beforehand via Get-Logger)
  12.         Created aliases for Write-* to override the cmdlets -- these are easy for users to remove without breaking the module
  13.         ** NEED to write some docs, but basically, this is stupid simple to use, just:
  14.            Import-Module Logger
  15.            Write-Verbose "This message will be saved in your profile folder in a file named PowerShellLogger.log (by default)"
  16.         To change the defaults for your system, edit the last line in the module!!
  17.   0.1 - Initial release. http://poshcode.org/1744 (Required config: http://poshcode.org/1743)
  18.   Uses Log4Net : http`://logging.apache.org/log4net/download.html
  19.   Documentation: http`://logging.apache.org/log4net/release/sdk/
  20.  
  21.   NOTES:
  22.   By default, this overwrites the Write-* cmdlets for Error, Warning, Debug, Verbose, and even Host.
  23.   This means that you may end up logging a lot of stuff you didn't intend to log (ie: verbose output from other scripts)
  24.  
  25.   To avoid this behavior, remove the aliases after importing it
  26.   Import-Module Logger; Remove-Item Alias:Write-*
  27.   Write-Warning "This is your warning"
  28.   Write-Debug   "You should know that..."
  29.   Write-Verbose "Everything would be logged, otherwise"
  30.  
  31.   ***** NOTE: IT ONLY OVERRIDES THE DEFAULTS FOR SCRIPTS *****
  32.   It currently has no effect on error/verbose/warning that is logged from cmdlets.
  33.  
  34. #>
  35.  
  36.  
  37. Add-Type -Path $PSScriptRoot\log4net.dll
  38.  
  39. function Get-Logger {
  40. param(
  41.    [Parameter(Mandatory=$false)]
  42.    [string]$LoggerName = "*"
  43. ,
  44.    [Parameter(Mandatory=$false)]
  45.    [ValidateSet("DEBUG","INFO","WARN","ERROR","FATAL","VERBOSE","HOST","WARNING")]
  46.    [string]$LogLevel = "DEBUG"
  47. ,
  48.    [Parameter(Mandatory=$false)]
  49.    [string]$LogFolder = $PSScriptRoot
  50. ,  [Switch]$Force
  51. ,  [Switch]$Console
  52. ,  [Switch]$EventLog
  53. ,  [Switch]$Trace
  54. ,  [Switch]$File
  55. ,  [Switch]$RollingFile
  56. )
  57.  
  58.    $Script:Logger = [log4net.LogManager]::GetCurrentLoggers() | Where-Object { $_.Logger.Name -Like $LoggerName }
  59.    if(!$script:Logger -or $Force -and $LoggerName -ne "*") {
  60.  
  61.       if($LogLevel -eq "VERBOSE") { $LogLevel = "INFO" }
  62.       if($LogLevel -eq "HOST") { $LogLevel = "INFO" }
  63.       if($LogLevel -eq "WARNING") { $LogLevel = "WARN" }
  64.  
  65.       $AppenderRefs = ''
  66.       if($EventLog) { $AppenderRefs += "<appender-ref ref=""EventLogAppender"" />`n" }
  67.       if($Trace) { $AppenderRefs += "<appender-ref ref=""TraceAppender"" />`n" }
  68.       if($File) { $AppenderRefs +=  "<appender-ref ref=""FileAppender"" />`n" }
  69.       if($RollingFile) { $AppenderRefs +=  "<appender-ref ref=""RollingFileAppender"" />`n" }
  70.       if($Console -or ($AppenderRefs.Length -eq 0)) { $AppenderRefs += "<appender-ref ref=""ColoredConsoleAppender"" />`n" }
  71.  
  72.       $Script:Logger = [log4net.LogManager]::GetLogger($LoggerName)
  73.      
  74.       [xml]$config = @"
  75. <log4net>
  76.    <appender name="ColoredConsoleAppender" type="log4net.Appender.ColoredConsoleAppender">
  77.        <mapping>
  78.            <level value="FATAL" />
  79.            <foreColor value="Red, HighIntensity" />
  80.            <backColor value="White, HighIntensity" />
  81.        </mapping>
  82.        <mapping>
  83.            <level value="ERROR" />
  84.            <foreColor value="Red, HighIntensity" />
  85.        </mapping>
  86.        <mapping>
  87.            <level value="DEBUG" />
  88.            <foreColor value="Green, HighIntensity" />
  89.        </mapping>
  90.        <mapping>
  91.            <level value="INFO" />
  92.            <foreColor value="Cyan, HighIntensity" />
  93.        </mapping>
  94.        <mapping>
  95.            <level value="WARN" />
  96.            <foreColor value="Yellow, HighIntensity" />
  97.        </mapping>
  98.            <layout type="log4net.Layout.PatternLayout">
  99.            <conversionPattern value="%date %-5level %logger [%property{NDC}] - %message%newline" />
  100.        </layout>
  101.    </appender>
  102.    <appender name="EventLogAppender" type="log4net.Appender.EventLogAppender" >
  103.        <applicationName value="$LoggerName" />
  104.        <layout type="log4net.Layout.PatternLayout">
  105.            <conversionPattern value="%date %-5level %logger [%property{NDC}] - %message%newline" />
  106.        </layout>
  107.    </appender>
  108.    <appender name="TraceAppender" type="log4net.Appender.TraceAppender">
  109.        <layout type="log4net.Layout.PatternLayout">
  110.            <conversionPattern value="%date %-5level %logger [%property{NDC}] - %message%newline" />
  111.        </layout>
  112.    </appender>
  113.    <appender name="FileAppender" type="log4net.Appender.FileAppender">
  114.        <file value="$LogFolder\$LoggerName.Log" />
  115.        <appendToFile value="true" />
  116.        <layout type="log4net.Layout.PatternLayout">
  117.            <conversionPattern value="%date %-5level %logger [%property{NDC}] - %message%newline" />
  118.        </layout>
  119.    </appender>
  120.    <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
  121.        <file value="$LogFolder\$LoggerName.Log" />
  122.        <appendToFile value="true" />
  123.        <maximumFileSize value="100KB" />
  124.        <maxSizeRollBackups value="2" />
  125.  
  126.        <layout type="log4net.Layout.PatternLayout">
  127.            <conversionPattern value="%date %-5level %logger [%property{NDC}] - %message%newline" />
  128.        </layout>
  129.    </appender>
  130.    <root>
  131.       <level value="DEBUG" />
  132.    </root>
  133.    <logger name="$LoggerName">
  134.       <level value="$LogLevel" />
  135.       $AppenderRefs
  136.    </logger>
  137. </log4net>
  138. "@
  139.       [log4net.Config.XmlConfigurator]::Configure( $config.log4net )
  140.    }
  141.    return $Script:Logger
  142. }
  143.  
  144. function Set-Logger {
  145. param(
  146.    [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
  147.    [log4net.Core.LogImpl]$Logger
  148. )
  149.    $script:Logger = $Logger
  150. }
  151. function Push-LogContext {
  152. param(
  153.    [Parameter(Mandatory=$true)]
  154.    [string]$Name
  155. )
  156.    [log4net.NDC]::Push($Name)
  157. }
  158. function Pop-LogContext {
  159.    [log4net.NDC]::Pop()
  160. }
  161.  
  162. function Write-DebugLog {
  163. [CmdletBinding()]
  164. param(
  165.     [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)]
  166.     [Alias('Msg')]
  167.     [AllowEmptyString()]
  168.     [System.String]
  169.     ${Message}
  170. ,
  171.    [Parameter(Mandatory=$false, Position=99)]
  172.    ${Logger})
  173.  
  174. begin
  175. {
  176.     try {
  177.         if($PSBoundParameters.ContainsKey("Logger")) {
  178.             if($Logger -is [log4net.Core.LogImpl]) { Set-Logger $Logger } else { $script:Logger = Get-Logger "$Logger" }
  179.             $null = $PSBoundParameters.Remove("Logger")
  180.         }
  181.        
  182.         $outBuffer = $null
  183.         if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
  184.         {
  185.             $PSBoundParameters['OutBuffer'] = 1
  186.         }
  187.         $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Write-Debug', [System.Management.Automation.CommandTypes]::Cmdlet)
  188.         $scriptCmd = {& $wrappedCmd @PSBoundParameters }
  189.        $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
  190.        $steppablePipeline.Begin($PSCmdlet)
  191.    } catch {
  192.        throw
  193.    }
  194. }
  195.  
  196. process
  197. {
  198.    try {
  199.        $script:logger.debug($Message) #Write-Debug
  200.        $steppablePipeline.Process($_)
  201.    } catch {
  202.        throw
  203.    }
  204. }
  205.  
  206. end
  207. {
  208.    try {
  209.        $steppablePipeline.End()
  210.    } catch {
  211.        throw
  212.    }
  213. }
  214. <#
  215.  
  216. .ForwardHelpTargetName Write-Debug
  217. .ForwardHelpCategory Cmdlet
  218.  
  219. #>
  220. }
  221. function Write-VerboseLog {
  222.  
  223. [CmdletBinding()]
  224. param(
  225.    [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)]
  226.    [Alias('Msg')]
  227.    [AllowEmptyString()]
  228.    [System.String]
  229.    ${Message}
  230. ,
  231.   [Parameter(Mandatory=$false, Position=99)]
  232.   ${Logger})
  233.  
  234. begin
  235. {
  236.    try {
  237.        if($PSBoundParameters.ContainsKey("Logger")) {
  238.            if($Logger -is [log4net.Core.LogImpl]) { Set-Logger $Logger } else { $script:Logger = Get-Logger "$Logger" }
  239.            $null = $PSBoundParameters.Remove("Logger")
  240.        }
  241.  
  242.        $outBuffer = $null
  243.        if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
  244.        {
  245.            $PSBoundParameters['OutBuffer'] = 1
  246.        }
  247.        $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Write-Verbose', [System.Management.Automation.CommandTypes]::Cmdlet)
  248.        $scriptCmd = {& $wrappedCmd @PSBoundParameters }
  249.        $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
  250.        $steppablePipeline.Begin($PSCmdlet)
  251.    } catch {
  252.        throw
  253.    }
  254. }
  255.  
  256. process
  257. {
  258.    try {
  259.        $script:logger.info($Message)
  260.        $steppablePipeline.Process($_)
  261.    } catch {
  262.        throw
  263.    }
  264. }
  265.  
  266. end
  267. {
  268.    try {
  269.        $steppablePipeline.End()
  270.    } catch {
  271.        throw
  272.    }
  273. }
  274. <#
  275.  
  276. .ForwardHelpTargetName Write-Verbose
  277. .ForwardHelpCategory Cmdlet
  278.  
  279. #>
  280. }
  281. function Write-WarningLog {
  282. [CmdletBinding()]
  283. param(
  284.    [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)]
  285.    [Alias('Msg')]
  286.    [AllowEmptyString()]
  287.    [System.String]
  288.    ${Message}
  289. ,
  290.   [Parameter(Mandatory=$false, Position=99)]
  291.   ${Logger})
  292.  
  293. begin
  294. {
  295.    try {
  296.        if($PSBoundParameters.ContainsKey("Logger")) {
  297.            if($Logger -is [log4net.Core.LogImpl]) { Set-Logger $Logger } else { $script:Logger = Get-Logger "$Logger" }
  298.            $null = $PSBoundParameters.Remove("Logger")
  299.        }
  300.  
  301.        $outBuffer = $null
  302.        if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
  303.        {
  304.            $PSBoundParameters['OutBuffer'] = 1
  305.        }
  306.        $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Write-Warning', [System.Management.Automation.CommandTypes]::Cmdlet)
  307.        $scriptCmd = {& $wrappedCmd @PSBoundParameters }
  308.        $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
  309.        $steppablePipeline.Begin($PSCmdlet)
  310.    } catch {
  311.        throw
  312.    }
  313. }
  314.  
  315. process
  316. {
  317.    try {
  318.        $script:logger.warn($Message)  #Write-Warning
  319.        $steppablePipeline.Process($_)
  320.    } catch {
  321.        throw
  322.    }
  323. }
  324.  
  325. end
  326. {
  327.    try {
  328.        $steppablePipeline.End()
  329.    } catch {
  330.        throw
  331.    }
  332. }
  333. <#
  334.  
  335. .ForwardHelpTargetName Write-Warning
  336. .ForwardHelpCategory Cmdlet
  337.  
  338. #>
  339. }
  340. function Write-ErrorLog {
  341. [CmdletBinding(DefaultParameterSetName='NoException')]
  342. param(
  343.    [Parameter(ParameterSetName='WithException', Mandatory=$true)]
  344.    [System.Exception]
  345.    ${Exception},
  346.  
  347.    [Parameter(ParameterSetName='NoException', Mandatory=$true, Position=0, ValueFromPipeline=$true)]
  348.    [Parameter(ParameterSetName='WithException')]
  349.    [Alias('Msg')]
  350.    [AllowNull()]
  351.    [AllowEmptyString()]
  352.    [System.String]
  353.    ${Message},
  354.  
  355.    [Parameter(ParameterSetName='ErrorRecord', Mandatory=$true)]
  356.    [System.Management.Automation.ErrorRecord]
  357.    ${ErrorRecord},
  358.  
  359.    [Parameter(ParameterSetName='NoException')]
  360.    [Parameter(ParameterSetName='WithException')]
  361.    [System.Management.Automation.ErrorCategory]
  362.    ${Category},
  363.  
  364.    [Parameter(ParameterSetName='WithException')]
  365.    [Parameter(ParameterSetName='NoException')]
  366.    [System.String]
  367.    ${ErrorId},
  368.  
  369.    [Parameter(ParameterSetName='NoException')]
  370.    [Parameter(ParameterSetName='WithException')]
  371.    [System.Object]
  372.    ${TargetObject},
  373.  
  374.    [System.String]
  375.    ${RecommendedAction},
  376.  
  377.    [Alias('Activity')]
  378.    [System.String]
  379.    ${CategoryActivity},
  380.  
  381.    [Alias('Reason')]
  382.    [System.String]
  383.    ${CategoryReason},
  384.  
  385.    [Alias('TargetName')]
  386.    [System.String]
  387.    ${CategoryTargetName},
  388.  
  389.    [Alias('TargetType')]
  390.    [System.String]
  391.    ${CategoryTargetType}
  392. ,
  393.   [Parameter(Mandatory=$false, Position=99)]
  394.   ${Logger})
  395.  
  396. begin
  397. {
  398.    try {
  399.        if($PSBoundParameters.ContainsKey("Logger")) {
  400.            if($Logger -is [log4net.Core.LogImpl]) { Set-Logger $Logger } else { $script:Logger = Get-Logger "$Logger" }
  401.            $null = $PSBoundParameters.Remove("Logger")
  402.        }
  403.  
  404.        $outBuffer = $null
  405.        if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
  406.        {
  407.            $PSBoundParameters['OutBuffer'] = 1
  408.        }
  409.        $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Write-Error', [System.Management.Automation.CommandTypes]::Cmdlet)
  410.        $scriptCmd = {& $wrappedCmd @PSBoundParameters }
  411.        $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
  412.        $steppablePipeline.Begin($PSCmdlet)
  413.    } catch {
  414.        throw
  415.    }
  416. }
  417.  
  418. process
  419. {
  420.    try {
  421.        $script:logger.error($Message,$Exception) #Write-Error
  422.        $steppablePipeline.Process($_)
  423.    } catch {
  424.        throw
  425.    }
  426. }
  427.  
  428. end
  429. {
  430.    try {
  431.        $steppablePipeline.End()
  432.    } catch {
  433.        throw
  434.    }
  435. }
  436. <#
  437.  
  438. .ForwardHelpTargetName Write-Error
  439. .ForwardHelpCategory Cmdlet
  440.  
  441. #>
  442. }
  443. function Write-HostLog {
  444. [CmdletBinding()]
  445. param(
  446.    [Parameter(Position=0, ValueFromPipeline=$true, ValueFromRemainingArguments=$true)]
  447.    [System.Object]
  448.    ${Object},
  449.  
  450.    [Switch]
  451.    ${NoNewline},
  452.  
  453.    [System.Object]
  454.    ${Separator} = $OFS,
  455.  
  456.    [System.ConsoleColor]
  457.    ${ForegroundColor},
  458.  
  459.    [System.ConsoleColor]
  460.    ${BackgroundColor}
  461. ,
  462.   [Parameter(Mandatory=$false, Position=99)]
  463.   ${Logger})
  464.  
  465. begin
  466. {
  467.    try {
  468.        if($PSBoundParameters.ContainsKey("Logger")) {
  469.            if($Logger -is [log4net.Core.LogImpl]) { Set-Logger $Logger } else { $script:Logger = Get-Logger "$Logger" }
  470.            $null = $PSBoundParameters.Remove("Logger")
  471.        }
  472.  
  473.        $outBuffer = $null
  474.        if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
  475.        {
  476.            $PSBoundParameters['OutBuffer'] = 1
  477.        }
  478.        $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Write-Host', [System.Management.Automation.CommandTypes]::Cmdlet)
  479.        $scriptCmd = {& $wrappedCmd @PSBoundParameters }
  480.        $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
  481.        $steppablePipeline.Begin($PSCmdlet)
  482.    } catch {
  483.        throw
  484.    }
  485. }
  486.  
  487. process
  488. {
  489.    try {
  490.        $script:logger.info(($Object -join $Separator))  #Write-Verbose
  491.        $steppablePipeline.Process($_)
  492.    } catch {
  493.        throw
  494.    }
  495. }
  496.  
  497. end
  498. {
  499.    try {
  500.        $steppablePipeline.End()
  501.    } catch {
  502.        throw
  503.    }
  504. }
  505. <#
  506.  
  507. .ForwardHelpTargetName Write-Host
  508. .ForwardHelpCategory Cmdlet
  509.  
  510. #>
  511. }
  512.  
  513. Set-Alias Write-Debug Write-DebugLog
  514. Set-Alias Write-Verbose Write-VerboseLog
  515. Set-Alias Write-Warning Write-WarningLog
  516. Set-Alias Write-Error Write-ErrorLog
  517. #Set-Alias Write-Host Write-HostLog
  518.  
  519. Export-ModuleMember -Function * -Alias *
  520.  
  521. $script:Logger = Get-Logger "PowerShellLogger" -RollingFile -LogFolder (Split-Path $Profile.CurrentUserAllHosts)
  522.  
  523. ## THIS IS THE DEFAULT LOGGER. CONFIGURE AS YOU SEE FIT

Submit a correction or amendment below (
click here to make a fresh posting)
After submitting an amendment, you'll be able to view the differences between the old and new posts easily.

Syntax highlighting:


Remember me