powershell-logging.psm1

Enum LogTargetType{
  File
  Console
  Stream
  AzureTableStorage
  None
}
 Class Severity {
  $Name
  $Color
  Severity($name) {
    switch ($name) {
      "DEBUG" {
        $this.Color = [System.ConsoleColor]::DarkBlue
        $this.Name = "DEBUG"
        break
      }
      "VERBOSE" {
        $this.Color = [System.ConsoleColor]::DarkYellow
        $this.Name = "VERBOSE"
        break
      }
      "INFO" {
        $this.Color = [System.ConsoleColor]::White
        $this.Name = "INFO"
        break
      }
      "WARNING" {
        $this.Color = [System.ConsoleColor]::Yellow
        $this.Name = "WARNING"
        break
      }
      "SUCCESS" {
        $this.Color = [System.ConsoleColor]::Green
        $this.Name = "SUCCESS"
        break
      }
      "ERROR" {
        $this.Color = [System.ConsoleColor]::Red
        $this.Name = "ERROR"
        break
      }
      default {
        break
      }
    }
  }
  [string] ToString() {
    return $this.Name
  }
}
 Class LogLine {
  [DateTime]$DateTime
  [string]$User
  [string]$Domain
  [Severity]$Severity
  [String]$Message
  [bool]$Saved = $false

  LogLine($severity, $message, $name) {
    $this.DateTime = Get-Date
    if([string]::isnullorempty($name)){
      if (-not[string]::IsNullOrEmpty($env:USERNAME)) { $this.User = $env:USERNAME.ToLower() }
      elseif (-not[string]::IsNullOrEmpty($env:USER)) { $this.User = $env:USER.ToLower() }
      if (-not[string]::IsNullOrEmpty($env:USERDNSDOMAIN)) { $this.Domain = $env:USERDNSDOMAIN.ToLower() }
      elseif (-not[string]::IsNullOrEmpty($env:COMPUTERNAME)) { $this.Domain = $env:COMPUTERNAME.ToLower() }
      elseif (-not[string]::IsNullOrEmpty($env:NAME)) { $this.Domain = $env:NAME.ToLower() }
      else { $this.Domain = $(hostname).ToLower() }
    }
    else{
      $this.User = $name
      $this.Domain = $null
    }
    $this.Severity = [Severity]::new($severity)
    $this.Message = $message.trim()
  }
  LogLine([string]$line) {
    $lineRegex = [regex]::new("(\d{4}\-\d{2}\-\d{2}\s\d{2}:\d{2}:\d{2}\.\d{4})\s\-\s(.+?)@(.*?)\s\-\s+([A-Z]{1,7})\s\-\s(.+)$")
    $match = $lineRegex.Match($line)
    if ($match.Success) {
      $this.DateTime = Get-Date $match.Groups[1].Value
      $this.User = $match.Groups[2].Value
      $this.Domain = $match.Groups[3].Value
      $this.Severity = [Severity]::new($match.Groups[4].Value)
      $this.Message = $match.Groups[5].Value.trim()
    }
    else {
      Write-Warning "Skipping Line (Format not recognized)$([Environment]::NewLine)`t`"$line`""
    }
  }
  [string] ToString() {
    return "{0} - {1}@{2} - {3} - {4}" -f @(
      (Get-Date $this.DateTime -Format "yyyy-MM-dd HH:mm:ss.ffff")
      $this.User
      $this.Domain
      $this.Severity.Name.padLeft(7, " ")
      $this.Message
    )
  }
}
 Class LogTarget {
  [string]$GUID
  [LogTargetType]$Type
  [bool]$Active = $true
  LogTarget([LogTargetType]$type) {
    $this.GUID = [GUID]::NewGuid().GUID.ToUpper()
    $this.Type = $type
  }

  Disable(){
    $this.Active = $false
  }

  Enable(){
    $this.Active = $true
  }

  hidden checkState(){
    if(!$this.Active){
      throw "the target `"$($this.GUID)`" is inactive, please enable it to use it again!"
    }
  }

  Set([LogLine[]]$logLines) {
    throw "not implemented"
  }

  [LogLine[]] Get() {
    throw "not implemented"
  }

  Rename() {
    throw "not implemented"
  }
  
  Move() {
    throw "not implemented"
  }

  Clear() {
    throw "not implemented"
  }

  [String] ToString() {
    return "LogTarget.$($this.Type)"
  }
}
 Class LogTargetConsole : LogTarget {
  [Severity[]]$severitiesToDisplay
  LogTargetConsole($severitiesToDisplay) : base([LogTargetType]::Console) {
    $this.severitiesToDisplay = $severitiesToDisplay
  }

  Set([LogLine[]]$logLines) {
    $this.checkState()
    $logLines | ForEach-Object -Process {
      $_logLine = $_
      if ($this.severitiesToDisplay.Name -contains $_logLine.Severity.Name) {
        Write-Host -Object $logline.ToString() -ForegroundColor $logline.Severity.Color
      }
    }
  }

  [LogLine[]] Get() {
    $this.checkState()
    return @()
  }

  Rename() {
    throw "cannot rename the console target"
  }
  
  Move() {
    throw "cannot move the console target"
  }

  Clear() {
    Clear-Host
  }
}
 Class LogTargetFile : LogTarget {
  [System.IO.FileSystemInfo]$fileInformation
  LogTargetFile($filePath) : base([LogTargetType]::File) {
    try {
      $this.fileInformation = Get-Item -Path $filePath -ErrorAction Stop
    }
    catch {
      $this.fileInformation = New-Item -Path $filePath -ItemType File -Force
    }
  }

  Set([LogLine[]]$logLines) {
    $this.checkState()
    $newContent = $logLines | ForEach-Object -Process { $_.ToString() }
    $joinedContent = $newContent -join [Environment]::NewLine
    try {
      Out-File -InputObject $joinedContent -Append -FilePath $this.fileInformation.FullName -Encoding utf8
    }
    catch {
      throw "Error Saving File"
    }
  }

  [LogLine[]] Get() {
    $this.checkState()
    $availableContent = (Get-Content -Encoding UTF8 -Path $this.fileInformation.FullName) -split [Environment]::NewLine
    $returnedLines = @()
    foreach ($line in $availableContent) {
      $newline = [LogLine]::new($line)
      $newline.Saved = $true
      $returnedLines += $newline
    }
    return $returnedLines
  }

  Rename($newName) {
    $this.checkState()
    $dest = Rename-Item -Path $this.fileInformation.FullName -NewName $newName -PassThru
    $this.fileInformation = $dest
  }

  Move($newLocation) {
    $this.checkState()
    if (Test-Path -Path $newLocation) {
      $newLocationItem = Get-Item -Path $newLocation
      if ($newLocationItem -is [System.IO.DirectoryInfo]) {
        $dest = Move-Item -Path $this.fileInformation.FullName -Destination $newLocation -PassThru
        $this.fileInformation = $dest
      }
      else {
        throw "destination needs to be a folder"
      }
    }
    else {
      throw "folder '$newLocation' not existant"
    }
  }

  Clear() {
    New-Item -Path $this.fileInformation.FullName -ItemType File -Force
  }
}
 Class LogTargetStream : LogTarget {
  [Severity[]]$severitiesToDisplay
  LogTargetStream($severitiesToDisplay) : base([LogTargetType]::Console) {
    $this.severitiesToDisplay = $severitiesToDisplay
  }

  Set([LogLine[]]$logLines) {
    $this.checkState()
    $logLines | ForEach-Object -Process {
      $_logLine = $_
      if ($this.severitiesToDisplay.Name -contains $_logLine.Severity.Name) {
        switch ($_logLine.Severity.Name) {
          "DEBUG" {
            Write-Debug -Message $_logLine.Message
            break
          }
          "VERBOSE" {
            Write-Verbose -Message $_logLine.Message
            break
          }
          "INFO" {
            Write-Information -MessageData $_logLine.Message
            break
          }
          "WARNING" {
            Write-Warning -Message $_logLine.Message
            break
          }
          "SUCCESS" {
            Write-Host -Object $_logLine.Message
            break
          }
          "ERROR" {
            Write-Error -Message $_logLine.Message
            break
          }
        }
      }
    }
  }

  [LogLine[]] Get() {
    $this.checkState()
    return @()
  }

  Rename() {
    throw "cannot rename the stream target"
  }
  
  Move() {
    throw "cannot move the stream target"
  }

  Clear() {
    Clear-Host
  }
}
 Class LogFile {
  [string]$Name
  [array]$LogLines
  [bool]$Active
  [bool]$ShowName
  [LogTarget[]]$Targets

  LogFile($name) {
    $this.Name = $name
    $this.Active = $true
  }

  [LogTarget] AddTarget([LogTargetType]$targetType = [LogTargetType]::File, $targetArguments) {
    $newTaget = New-Object -TypeName "LogTarget$targetType" -ArgumentList @($targetArguments.Values)
    $this.Targets += $newTaget
    if ($newTaget | Get-Member -MemberType Method -Name Get) {
      $this.LogLines += $newTaget.Get()
      $this.LogLines = $this.LogLines | Sort-Object -Property DateTime -Unique
    }
    return $newTaget
  }

  RemoveTarget([GUID]$GUID) {
    $newTargets = $this.Targets | Where-Object -Property GUID -NE $GUID.Guid
    $this.Targets = $newTargets
  }

  AddLine($severity, $message) {
    $logName = $null
    if (!$this.Active) { throw "Log '$($this.Name)' is inactive! Open it again to use it." }
    if($this.ShowName){
      $logName = $this.Name
    }
    $logline = [LogLine]::new($severity, $message, $logName)
    $this.LogLines += $logline
    $this.Targets |Where-Object -Property Active -EQ -Value $true | ForEach-Object -Process {
      if ($null -eq $_) { continue }
      $_.Set($logline);
    }
  }

  Close() {
    if (!$this.active) { throw "Log '$($this.Name)' is inactive! Open it again to use it." }
    $this.active = $false
  }
}

function Add-LogTarget() {
  [CMDLetBinding(PositionalBinding = $false, DefaultParameterSetName = "file")]
  param(
    [Parameter(Mandatory = $true, Position = 0, ParameterSetName = "file", ValueFromPipelineByPropertyName = $true)]
    [string]
    $FullName,

    [Parameter(Mandatory = $false)]
    [ValidateSet("Host", "Stream", "None")]
    [string]
    $ConsoleType = "Host",

    [Parameter(Mandatory = $true, ParameterSetName = "console")]
    [Switch]
    $Console,
    
    [Parameter(Mandatory = $false, ParameterSetName = "console")]
    [switch]
    $ShowDebug,

    [Parameter(Mandatory = $false, ParameterSetName = "console")]
    [switch]
    $ShowVerbose,

    [Parameter(Mandatory = $false, ParameterSetName = "console")]
    [switch]
    $ShowInfo,
    
    [Parameter(Mandatory = $false, ParameterSetName = "console")]
    [switch]
    $ShowWarning,
    
    [Parameter(Mandatory = $false, ParameterSetName = "console")]
    [switch]
    $ShowSuccess,
    
    [Parameter(Mandatory = $false, ParameterSetName = "console")]
    [switch]
    $ShowError,

    [parameter()]
    [LogFile]
    $LogConnection = $Script:LogConnection
  )
  begin {
    if ([string]::isnullorempty($PSBoundParameters.ShowInfo)) { $ShowInfo = $true }
    if ([string]::isnullorempty($PSBoundParameters.ShowWarning)) { $ShowWarning = $true }
    if ([string]::isnullorempty($PSBoundParameters.ShowSuccess)) { $ShowSuccess = $true }
    if ([string]::isnullorempty($PSBoundParameters.ShowError)) { $ShowError = $true }
  }
  process {
    if ($null -eq $LogConnection) {
      throw "Use `"Open-Log`" first, to connect to a logfile!"
      return
    }
    switch ($PsCmdlet.ParameterSetName) {
      "file" {
        $fileTarget = $LogConnection.AddTarget([LogTargetType]::File, [ordered]@{
            filePath = $FullName
          })
        break;
      }
      "console" {
        if ($LogConnection.Targets.Type -Contains "Console") {
          throw "there can only be one console target!"
        }
        $LogLevel = @()
        if ($ShowDebug) { $LogLevel += "DEBUG" }
        if ($ShowVerbose) { $LogLevel += "VERBOSE" }
        if ($ShowInfo) { $LogLevel += "INFO" }
        if ($ShowWarning) { $LogLevel += "WARNING" }
        if ($ShowSuccess) { $LogLevel += "SUCCESS" }
        if ($ShowError) { $LogLevel += "ERROR" }
        switch ($ConsoleType) {
          "Host" {
            $consoleTarget = $LogConnection.AddTarget([LogTargetType]::Console, [ordered]@{
                severitiesToDisplay = [Severity[]]$LogLevel
              })
            break
          }
          "Stream" {
            $consoleTarget = $LogConnection.AddTarget([LogTargetType]::Stream, [ordered]@{
                severitiesToDisplay = [Severity[]]$LogLevel
              })
            break
          }
          default {
            # add no console target
          }
        }
        break;
      }
    }

    return $LogConnection
  }
  end {}
}
 function Clear-LogTarget(){
    [CmdletBinding(PositionalBinding=$false, DefaultParameterSetName="GUID")]
    param(
        [Parameter(ParameterSetName="GUID",ValueFromPipelineByPropertyName = $true, Mandatory = $true)]
        [GUID]
        $GUID,
        
        [Parameter(ParameterSetName="pipeline",ValueFromPipelineByPropertyName = $true, Mandatory = $true)]
        [LogTarget[]]
        $Targets,
        
        [parameter(ValueFromPipeline = $false)]
        [LogFile]
        $LogConnection = $Script:LogConnection
    )
    begin{
    }
    process {
        if($null -eq $LogConnection){
            throw "Use `"Open-Log`" first, to connect to a logfile!"
            return
        }
        
        if($PsCmdlet.ParameterSetName -eq "GUID"){
            $Targets = $LogConnection.Targets |Where-Object -Property GUID -EQ $GUID
        }
        $Targets |ForEach-Object -Process {$_.Clear()}
        
    }
    end{}
}
 function Close-Log(){
    [CmdletBinding()]
    param(
        [parameter()]
        [LogFile]
        $LogConnection = $Script:LogConnection
    )
    begin{
    }
    process{
        if($null -eq $Script:LogConnection){
            throw "Use `"Open-Log`" first, to connect to a logfile!"
            return
        }
        $LogConnection.close()
        Remove-Variable "LogConnection" -Scope "Script"
    }
    end{}
}
 function Disable-LogTarget() {
  [CmdletBinding(PositionalBinding=$false, DefaultParameterSetName="GUID")]
  param(
    [Parameter(ParameterSetName = "GUID", ValueFromPipelineByPropertyName = $true, Mandatory = $true)]
    [GUID]
    $GUID,

    [Parameter(ParameterSetName = "pipeline", ValueFromPipelineByPropertyName = $true)]
    [LogTarget[]]
    $Targets,

    [parameter()]
    [LogFile]
    $LogConnection = $Script:LogConnection
  )
  begin {
  }
  process {
    if ($null -eq $LogConnection) {
      throw "Use `"Open-Log`" first, to connect to a logfile!"
      return
    }

    if($PsCmdlet.ParameterSetName -eq "GUID"){
      $Targets = $LogConnection.Targets | Where-Object -Property GUID -EQ $GUID
    }
    $Targets |ForEach-Object -Process {$_.Disable()}
  }
  end {}
}
 function Enable-LogTarget() {
  [CmdletBinding(PositionalBinding=$false, DefaultParameterSetName="GUID")]
  param(
    [Parameter(ParameterSetName = "GUID", ValueFromPipelineByPropertyName = $true, Mandatory = $true)]
    [GUID]
    $GUID,

    [Parameter(ParameterSetName = "pipeline", ValueFromPipelineByPropertyName = $true)]
    [LogTarget[]]
    $Targets,

    [parameter()]
    [LogFile]
    $LogConnection = $Script:LogConnection
  )
  begin {
  }
  process {
    if ($null -eq $LogConnection) {
      throw "Use `"Open-Log`" first, to connect to a logfile!"
      return
    }

    if($PsCmdlet.ParameterSetName -eq "GUID"){
      $Targets = $LogConnection.Targets | Where-Object -Property GUID -EQ $GUID
    }
    $Targets |ForEach-Object -Process {$_.Enable()}
  }
  end {}
}
 function Get-Log(){
    [CmdletBinding()]
    param(
        [parameter()]
        [LogFile]
        $LogConnection = $Script:LogConnection
    )
    begin{
    }
    process{
        if($null -eq $LogConnection){
            throw "Use `"Open-Log`" first, to connect to a logfile!"
            return
        }
        return $LogConnection
    }
    end{}
}
 function Get-LogContent(){
    [CmdletBinding(DefaultParameterSetName="__default")]
    param(
        [Parameter(ValueFromRemainingArguments=$true)]
        [string]
        $Filter,

        [int32]
        [Parameter(ParameterSetName="First")]
        $First,

        [int32]
        [Parameter(ParameterSetName="Last")]
        $Last,

        [switch]
        $IncludeDebug,

        [switch]
        $IncludeVerbose,

        [switch]
        $IncludeInfo,

        [switch]
        $IncludeWarning,

        [switch]
        $IncludeSuccess,

        [switch]
        $IncludeError,

        [parameter()]
        [LogFile]
        $LogConnection = $Script:LogConnection
    )
    begin{
    }
    process{
        if($null -eq $LogConnection){
            throw "Use `"Open-Log`" first, to connect to a logfile!"
            return
        }
        if($LogConnection.isEncrypted){
            throw "Use Unprotect-Log first, to edit this logfile!"
            return
        }
        $Lines = $LogConnection.LogLines

        if(![string]::IsNullOrEmpty($PSBoundParameters.Filter)){
            $Lines = $Lines |Where-Object -FilterScript {
                $_LogLine = $_
                $_LogLine.Domain -like $Filter -or
                    $_LogLine.User -like $Filter -or
                    $_LogLine.Message -like $Filter
            }
        }

        if($PSBoundParameters.IncludeDebug -or 
            $PSBoundParameters.IncludeVerbose -or 
            $PSBoundParameters.IncludeInfo -or 
            $PSBoundParameters.IncludeWarning -or 
            $PSBoundParameters.IncludeSuccess -or 
            $PSBoundParameters.IncludeError
        ){
            $selectedSeverityLevels = @()
            if($PSBoundParameters.IncludeDebug){ $selectedSeverityLevels += "DEBUG" }
            if($PSBoundParameters.IncludeVerbose){ $selectedSeverityLevels += "VERBOSE" }
            if($PSBoundParameters.IncludeInfo){ $selectedSeverityLevels += "INFO" }
            if($PSBoundParameters.IncludeWarning){ $selectedSeverityLevels += "WARNING" }
            if($PSBoundParameters.IncludeSuccess){ $selectedSeverityLevels += "SUCCESS" }
            if($PSBoundParameters.IncludeError){ $selectedSeverityLevels += "ERROR" }

            $Lines = $Lines |Where-Object -FilterScript {
                $_LogLine = $_
                $_LogLine.Severity.Name -in $selectedSeverityLevels
            }
        }

        if(![string]::IsNullOrEmpty($PSBoundParameters.First)){ $Lines = $Lines |Select-Object -First $First }
        elseif(![string]::IsNullOrEmpty($PSBoundParameters.Last)){ $Lines = $Lines |Select-Object -Last $Last }

        return $Lines
    }
    end{}
}
 function Move-LogTarget() {
  [CmdletBinding(PositionalBinding=$false, DefaultParameterSetName="GUID")]
  param(
    [Parameter(ParameterSetName = "GUID", ValueFromPipelineByPropertyName = $true, Mandatory = $true)]
    [GUID]
    $GUID,

    [Parameter(ParameterSetName = "pipeline", ValueFromPipelineByPropertyName = $true)]
    [LogTarget]
    $Targets,
        
    [Parameter(Mandatory = $true, Position = 0)]
    [string]
    $Path,

    [parameter()]
    [LogFile]
    $LogConnection = $Script:LogConnection
  )
  begin {
  }
  process {
    if ($null -eq $LogConnection) {
      throw "Use `"Open-Log`" first, to connect to a logfile!"
      return
    }
    
    if($PsCmdlet.ParameterSetName -eq "GUID"){
      $Targets = $LogConnection.Targets | Where-Object -Property GUID -EQ $GUID
    }
    $Targets |ForEach-Object -Process {$_.Move($Path)}
  }
  end {}
}
 function Open-Log(){
    [CmdletBinding()]
    [Alias("Connect-Log")]
    param(
        [parameter(Mandatory=$true,Position=0,ValueFromPipelineByPropertyName=$true)]
        [string]
        $Name,
    
        [Parameter(ParameterSetName="file",ValueFromPipelineByPropertyName=$true)]
        [Alias("DirectoryName")]
        [String]
        $LogPath = [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::Desktop),

        [Parameter(Mandatory=$false)]
        [ValidateSet("Host", "Stream", "None")]
        [string]
        $ConsoleType = "Host",
    
        [switch]
        $ShowDebug,

        [switch]
        $ShowVerbose,

        [switch]
        $ShowInfo,
        
        [switch]
        $ShowWarning,
        
        [switch]
        $ShowSuccess,
        
        [switch]
        $ShowError,

        [switch]
        $ShowName
    )
    begin{
        if([string]::isnullorempty($PSBoundParameters.ShowInfo)){ $ShowInfo = $true }
        if([string]::isnullorempty($PSBoundParameters.ShowWarning)){ $ShowWarning = $true }
        if([string]::isnullorempty($PSBoundParameters.ShowSuccess)){ $ShowSuccess = $true }
        if([string]::isnullorempty($PSBoundParameters.ShowError)){ $ShowError = $true }
    }
    process{
        # try{Close-Log}catch{}
        $LogLevel = @()
        if($ShowDebug){$LogLevel += "DEBUG"}
        if($ShowVerbose){$LogLevel += "VERBOSE"}
        if($ShowInfo){$LogLevel += "INFO"}
        if($ShowWarning){$LogLevel += "WARNING"}
        if($ShowSuccess){$LogLevel += "SUCCESS"}
        if($ShowError){$LogLevel += "ERROR"}
        $Script:LogConnection = [LogFile]::new($Name)
        $Script:LogConnection.ShowName = $ShowName

        switch($ConsoleType){
            "Host" {
                $consoleTarget = $Script:LogConnection.AddTarget([LogTargetType]::Console, [ordered]@{
                    severitiesToDisplay = [Severity[]]$LogLevel
                })
                break
            }
            "Stream" {
                $consoleTarget = $Script:LogConnection.AddTarget([LogTargetType]::Stream, [ordered]@{
                    severitiesToDisplay = [Severity[]]$LogLevel
                })
                break
            }
            default {
                # add no console target
            }
        }


        if($PsCmdlet.ParameterSetName -eq "file"){
            if(-not($Name -like "*.log")){$Name += ".log"}
            $fileTarget = $Script:LogConnection.AddTarget([LogTargetType]::File, [ordered]@{
                filePath = (Join-Path -Path $LogPath -ChildPath $Name)
            })
        }

        return $Script:LogConnection
    }
    end{}
}
 function Remove-LogTarget() {
  [CmdletBinding(PositionalBinding=$false)]
  param(
    [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)]
    [GUID]
    $GUID,

    [parameter()]
    [LogFile]
    $LogConnection = $Script:LogConnection
  )
  begin {
  }
  process {
    if ($null -eq $LogConnection) {
      throw "Use `"Open-Log`" first, to connect to a logfile!"
      return
    }
    $Target = $LogConnection.Targets | Where-Object -Property GUID -EQ $GUID
    $LogConnection.RemoveTarget($Target.GUID)
  }
  end {}
}
 function Rename-LogTarget() {
  [CmdletBinding(PositionalBinding=$false, DefaultParameterSetName="GUID")]
  param(
    [Parameter(ParameterSetName = "GUID", ValueFromPipelineByPropertyName = $true, Mandatory = $true)]
    [GUID]$GUID,

    [Parameter(ParameterSetName = "pipeline", ValueFromPipelineByPropertyName = $true)]
    [LogTarget]
    $Targets,

    [Parameter(Mandatory = $true, Position = 1)]
    [string]
    $NewName,

    [parameter()]
    [LogFile]
    $LogConnection = $Script:LogConnection
  )
  begin {
  }
  process {
    if ($null -eq $LogConnection) {
      throw "Use `"Open-Log`" first, to connect to a logfile!"
      return
    }
    if($PsCmdlet.ParameterSetName -eq "GUID"){
      $Targets = $LogConnection.Targets | Where-Object -Property GUID -EQ $GUID
    }
    $Targets |ForEach-Object -Process {$_.Rename($NewName)}
  }
  end {}
}
 function Switch-Log(){
  param(
    [parameter(Mandatory=$true)]
    [LogFile]$LogConnection
  )
  begin{}
  process{
    $Script:LogConnection = $LogConnection
    return $Script:LogConnection
  }
  end{}
}
 function Write-Log(){
    [CMDLetBinding(PositionalBinding=$false)]
    [Alias("ulog")]
    param(
        # severity of logline
        [Parameter(Mandatory=$true, Position=0)]
        [ValidateSet("DEBUG", "VERBOSE", "INFO", "WARNING", "SUCCESS", "ERROR")]
        [string]
        $Severity,

        # actual error text
        [Parameter(Mandatory=$true, Position=1, ValueFromRemainingArguments=$true)]
        [string]
        $LogLine,
        
        [parameter()]
        [LogFile]
        $LogConnection = $Script:LogConnection
    )
    begin{
    }
    process {
        if($null -eq $LogConnection){
            throw "Use `"Open-Log`" first, to connect to a logfile!"
            return
        }
        $LogConnection.AddLine($Severity, $LogLine)
    }
    end{}
}