# encoding: ascii
# api: powershell
# title: Logger.psm1
# description: A trivial Log4Net implementation which (optionally) causes Write-Host, Write-Verbose, Write-Warning, Write-Debug, and Write-Error to log their output when called from (any) script.  
# version: 0.1
# type: module
# author: Joel Bennett
# license: CC0
# function: Get-Logger
# x-poshcode-id: 1744
# x-archived: 2016-03-06T14:22:19
# x-published: 2011-04-06T22:20:00
#
# The logger output can be configured via a configuration file placed in the module’s folder.
# 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 more to come.
#
<#
   Name     : Universal Log4Net Logging Module (Logger.psm1)
   Version  : 0.1
   Author   : Joel Bennett (MVP)
   Site     : http://www.HuddledMasses.org/

   See Also : http://poshcode.org/1743
   You should download this config and put it in your Modules\Logger folder with this Logger.psm1
   
   Uses Log4Net : http://logging.apache.org/log4net/download.html
   Documentation: http://logging.apache.org/log4net/release/sdk/
   
   NOTES:
   By default, this overwrites the Write-* cmdlets for Error, Warning, Debug, Verbose, and even Host.
   This means that you may end up logging a lot of stuff you didn't intend to log (ie: verbose output from other scripts)
   ***** BUT IT ONLY WORKS FOR SCRIPTS *****
   It currently has no effect on error/verbose/warning that is logged from cmdlets.
   
   To avoid this behavior, Import it with a -Prefix, and call it's methods directly. I.E.:
   Import-Module Logger -Prefix Logged
   Write-LoggedWarning "This is your warning"
   Write-LoggedHost    "You should know that..."
   Write-LoggedVerbose "Everything would be logged, otherwise"
#>


Add-Type -Path $PSScriptRoot\log4net.dll
[Log4Net.Config.BasicConfigurator]::Configure()

function Get-Logger {
param( 
   [Parameter(Mandatory=$true)]
   [string]$Name
,
   [Parameter(Mandatory=$false)]
   [string]$Configuration = "default.config"
)
   [log4net.LogManager]::GetLogger($Name) | Tee-Object -Variable script:logger
   
   if(Test-Path $Configuration) {
      [log4net.Config.XmlConfigurator]::Configure( ([IO.FileInfo]$Configuration) )
   } else { 
      $Configuration = Join-Path $PSScriptRoot $Configuration
      if(Test-Path $Configuration) {
         [log4net.Config.XmlConfigurator]::Configure( ([IO.FileInfo]$Configuration) )
      }
   }
}

function Set-Logger {
param(
   [Parameter(Mandatory=$true)]
   [log4net.Core.LogImpl]$Logger
)
   $script:Logger = $Logger
}

function Push-Context {
param( 
   [Parameter(Mandatory=$true)]
   [string]$Name
)
   [log4net.NDC]::Push($Name)
}

function Pop-Context {
   [log4net.NDC]::Pop()
}


function Write-Debug {
[CmdletBinding()]
param(
    [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)]
    [Alias('Msg')]
    [AllowEmptyString()]
    [System.String]
    ${Message})

begin
{
    try {
        $outBuffer = $null
        if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
        {
            $PSBoundParameters['OutBuffer'] = 1
        }
        $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Write-Debug', [System.Management.Automation.CommandTypes]::Cmdlet)
        $scriptCmd = {& $wrappedCmd @PSBoundParameters }
        $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
        $steppablePipeline.Begin($PSCmdlet)
    } catch {
        throw
    }
}

process
{
    try {
        $logger.debug($Message) #Write-Debug
        $steppablePipeline.Process($_)
    } catch {
        throw
    }
}

end
{
    try {
        $steppablePipeline.End()
    } catch {
        throw
    }
}
<#

.ForwardHelpTargetName Write-Debug
.ForwardHelpCategory Cmdlet

#>
}
function Write-Error {
[CmdletBinding(DefaultParameterSetName='NoException')]
param(
    [Parameter(ParameterSetName='WithException', Mandatory=$true)]
    [System.Exception]
    ${Exception},

    [Parameter(ParameterSetName='NoException', Mandatory=$true, Position=0, ValueFromPipeline=$true)]
    [Parameter(ParameterSetName='WithException')]
    [Alias('Msg')]
    [AllowNull()]
    [AllowEmptyString()]
    [System.String]
    ${Message},

    [Parameter(ParameterSetName='ErrorRecord', Mandatory=$true)]
    [System.Management.Automation.ErrorRecord]
    ${ErrorRecord},

    [Parameter(ParameterSetName='NoException')]
    [Parameter(ParameterSetName='WithException')]
    [System.Management.Automation.ErrorCategory]
    ${Category},

    [Parameter(ParameterSetName='WithException')]
    [Parameter(ParameterSetName='NoException')]
    [System.String]
    ${ErrorId},

    [Parameter(ParameterSetName='NoException')]
    [Parameter(ParameterSetName='WithException')]
    [System.Object]
    ${TargetObject},

    [System.String]
    ${RecommendedAction},

    [Alias('Activity')]
    [System.String]
    ${CategoryActivity},

    [Alias('Reason')]
    [System.String]
    ${CategoryReason},

    [Alias('TargetName')]
    [System.String]
    ${CategoryTargetName},

    [Alias('TargetType')]
    [System.String]
    ${CategoryTargetType})

begin
{
    try {
        $outBuffer = $null
        if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
        {
            $PSBoundParameters['OutBuffer'] = 1
        }
        $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Write-Error', [System.Management.Automation.CommandTypes]::Cmdlet)
        $scriptCmd = {& $wrappedCmd @PSBoundParameters }
        $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
        $steppablePipeline.Begin($PSCmdlet)
    } catch {
        throw
    }
}

process
{
    try {
        $logger.error($Message,$Exception) #Write-Error
        $steppablePipeline.Process($_)
    } catch {
        throw
    }
}

end
{
    try {
        $steppablePipeline.End()
    } catch {
        throw
    }
}
<#

.ForwardHelpTargetName Write-Error
.ForwardHelpCategory Cmdlet

#>
}
function Write-Host {
[CmdletBinding()]
param(
    [Parameter(Position=0, ValueFromPipeline=$true, ValueFromRemainingArguments=$true)]
    [System.Object]
    ${Object},

    [Switch]
    ${NoNewline},

    [System.Object]
    ${Separator} = $OFS,

    [System.ConsoleColor]
    ${ForegroundColor},

    [System.ConsoleColor]
    ${BackgroundColor})

begin
{
    try {
        $outBuffer = $null
        if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
        {
            $PSBoundParameters['OutBuffer'] = 1
        }
        $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Write-Host', [System.Management.Automation.CommandTypes]::Cmdlet)
        $scriptCmd = {& $wrappedCmd @PSBoundParameters }
        $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
        $steppablePipeline.Begin($PSCmdlet)
    } catch {
        throw
    }
}

process
{
    try {
        $logger.info(($Object -join $Separator))  #Write-Verbose
        $steppablePipeline.Process($_)
    } catch {
        throw
    }
}

end
{
    try {
        $steppablePipeline.End()
    } catch {
        throw
    }
}
<#

.ForwardHelpTargetName Write-Host
.ForwardHelpCategory Cmdlet

#>
}


function Write-Verbose {

[CmdletBinding()]
param(
    [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)]
    [Alias('Msg')]
    [AllowEmptyString()]
    [System.String]
    ${Message})

begin
{
    try {
        $outBuffer = $null
        if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
        {
            $PSBoundParameters['OutBuffer'] = 1
        }
        $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Write-Verbose', [System.Management.Automation.CommandTypes]::Cmdlet)
        $scriptCmd = {& $wrappedCmd @PSBoundParameters }
        $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
        $steppablePipeline.Begin($PSCmdlet)
    } catch {
        throw
    }
}

process
{
    try {
        $logger.info($Message)
        $steppablePipeline.Process($_)
    } catch {
        throw
    }
}

end
{
    try {
        $steppablePipeline.End()
    } catch {
        throw
    }
}
<#

.ForwardHelpTargetName Write-Verbose
.ForwardHelpCategory Cmdlet

#>
}
function Write-Warning {
[CmdletBinding()]
param(
    [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)]
    [Alias('Msg')]
    [AllowEmptyString()]
    [System.String]
    ${Message})

begin
{
    try {
        $outBuffer = $null
        if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
        {
            $PSBoundParameters['OutBuffer'] = 1
        }
        $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Write-Warning', [System.Management.Automation.CommandTypes]::Cmdlet)
        $scriptCmd = {& $wrappedCmd @PSBoundParameters }
        $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
        $steppablePipeline.Begin($PSCmdlet)
    } catch {
        throw
    }
}

process
{
    try {
        $logger.warn($Message)  #Write-Warning
        $steppablePipeline.Process($_)
    } catch {
        throw
    }
}

end
{
    try {
        $steppablePipeline.End()
    } catch {
        throw
    }
}
<#

.ForwardHelpTargetName Write-Warning
.ForwardHelpCategory Cmdlet

#>
}





$script:defaultLogger = Get-Logger "Default"
