【问题标题】:Powershell with exchange-- How do I append all verbose output to a filePowershell with exchange——如何将所有详细输出附加到文件中
【发布时间】:2017-06-11 04:24:14
【问题描述】:

我正在尝试在我的脚本中添加一些日志记录。任何建议将不胜感激。首先,我想在出现问题时添加错误日志。

例如,当找不到用户时,会抛出以下错误:

无法执行操作,因为在“HQ-DC-6.domain.com”上找不到对象“asdfa”。 + CategoryInfo : NotSpecified: (0:Int32) [Set-RemoteMailbox], ManagementObjectNotFoundException + FullyQualifiedErrorId:47285FC7,Microsoft.Exchange.Management.RecipientTasks.SetRemoteMailbox + PSComputerName : hq-cas2.domain.com

================================

$users = ForEach ($user in $(Get-Content 'C:\Users\test\Documents\Powershell Scripts\OffboardUsers.txt')) {

$tmpname = Get-RemoteMailbox -Identity $user | Select-Object -ExpandProperty Name
$tmpDisplayName = Get-RemoteMailbox -Identity $user | Select-Object -ExpandProperty DisplayName

Set-RemoteMailbox -Identity $user -Name ("_" + "$tmpname") >> error.log
Set-RemoteMailbox -Identity $user -DisplayName ("_" + "$tmpDisplayName") >> error.log
Set-RemoteMailbox -Identity $user -HiddenFromAddressListsEnabled $true >> error.log

}

【问题讨论】:

  • 我可以大胆猜测,但您没有解释您的脚本有什么问题。您尝试的日志记录级别与问题的标题并不匹配。您是要记录所有内容还是只记录一些特定的行?
  • 嗨@Matt,感谢您的快速回复。如果可能的话,我现在只想记录错误。例如,当用户不存在时会引发此错误:无法执行操作,因为在“HQ-DC-6.domain.com”上找不到对象“asdfa”。 + CategoryInfo:未指定:(0:Int32)[Set-RemoteMailbox],ManagementObjectNotFoundException + FullyQualifiedErrorId:47285FC7,Microsoft.Exchange.Management.RecipientTasks.SetRemoteMailbox + PSComputerName:hq-cas2.domain.com
  • windows 事件日志是不可能的吗?您可以创建自己的应用程序特定日志。

标签: powershell office365 exchange-server


【解决方案1】:

您可以使用写入日志功能。我相信这对你来说是最好的方法。

此函数由 Jason Wasser 编写:

<#
.Synopsis
   Write-Log writes a message to a specified log file with the current time stamp.
.DESCRIPTION
   The Write-Log function is designed to add logging capability to other scripts.
   In addition to writing output and/or verbose you can write to a log file for
   later debugging.
.NOTES
   Created by: Jason Wasser @wasserja
   Modified: 11/24/2015 09:30:19 AM  

   Changelog:
    * Code simplification and clarification - thanks to @juneb_get_help
    * Added documentation.
    * Renamed LogPath parameter to Path to keep it standard - thanks to @JeffHicks
    * Revised the Force switch to work as it should - thanks to @JeffHicks

   To Do:
    * Add error handling if trying to create a log file in a inaccessible location.
    * Add ability to write $Message to $Verbose or $Error pipelines to eliminate
      duplicates.
.PARAMETER Message
   Message is the content that you wish to add to the log file. 
.PARAMETER Path
   The path to the log file to which you would like to write. By default the function will 
   create the path and file if it does not exist. 
.PARAMETER Level
   Specify the criticality of the log information being written to the log (i.e. Error, Warning, Informational)
.PARAMETER NoClobber
   Use NoClobber if you do not wish to overwrite an existing file.
.EXAMPLE
   Write-Log -Message 'Log message' 
   Writes the message to c:\Logs\PowerShellLog.log.
.EXAMPLE
   Write-Log -Message 'Restarting Server.' -Path c:\Logs\Scriptoutput.log
   Writes the content to the specified log file and creates the path and file specified. 
.EXAMPLE
   Write-Log -Message 'Folder does not exist.' -Path c:\Logs\Script.log -Level Error
   Writes the message to the specified log file as an error message, and writes the message to the error pipeline.
.LINK
   https://gallery.technet.microsoft.com/scriptcenter/Write-Log-PowerShell-999c32d0
#>
function Write-Log
{
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true)]
        [ValidateNotNullOrEmpty()]
        [Alias("LogContent")]
        [string]$Message,

        [Parameter(Mandatory=$false)]
        [Alias('LogPath')]
        [string]$Path='C:\Logs\PowerShellLog.log',

        [Parameter(Mandatory=$false)]
        [ValidateSet("Error","Warn","Info")]
        [string]$Level="Info",

        [Parameter(Mandatory=$false)]
        [switch]$NoClobber
    )

    Begin
    {
        # Set VerbosePreference to Continue so that verbose messages are displayed.
        $VerbosePreference = 'Continue'
    }
    Process
    {

        # If the file already exists and NoClobber was specified, do not write to the log.
        if ((Test-Path $Path) -AND $NoClobber) {
            Write-Error "Log file $Path already exists, and you specified NoClobber. Either delete the file or specify a different name."
            Return
            }

        # If attempting to write to a log file in a folder/path that doesn't exist create the file including the path.
        elseif (!(Test-Path $Path)) {
            Write-Verbose "Creating $Path."
            $NewLogFile = New-Item $Path -Force -ItemType File
            }

        else {
            # Nothing to see here yet.
            }

        # Format Date for our Log File
        $FormattedDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"

        # Write message to error, warning, or verbose pipeline and specify $LevelText
        switch ($Level) {
            'Error' {
                Write-Error $Message
                $LevelText = 'ERROR:'
                }
            'Warn' {
                Write-Warning $Message
                $LevelText = 'WARNING:'
                }
            'Info' {
                Write-Verbose $Message
                $LevelText = 'INFO:'
                }
            }

        # Write log entry to $Path
        "$FormattedDate $LevelText $Message" | Out-File -FilePath $Path -Append
    }
    End
    {
    }
}

用法:

 Write-Log -Message 'Folder does not exist.' -Path c:\Logs\Script.log -Level Error

Write-Log -Message 'Restarting Server.' -Path c:\Logs\Scriptoutput.log

注意:您始终可以为此功能使用获取帮助来获取所有详细信息。

希望对你有帮助。

【讨论】:

    【解决方案2】:

    本文于 2017 年 7 月 18 日全面修订,因为新的 Log-Entry 解决方案取代了以前的 Write-Log 解决方案,不会进一步更新。另请参阅:Migrating from Write-Log。


    总的来说,我发现 Microsoft 脚本语言的日志记录被低估了。不仅在脚本(或 cmdlet)的设计时日志会派上用场,而且当脚本被部署并且出现问题时,您通常希望您有更好的日志记录。
    这就是为什么我认为像 PowerShell(以及它的前身 VBScript)这样的脚本语言实际上应该具有比现在可用的更复杂的本机日志记录功能。

    最佳实践

    甚至在 PowerShell 出现之前,我就需要在 VBScript 中提供足够的日志记录功能。事实上,我在 VBScript 中使用的一些概念,我仍在 PowerShell 中使用。同时,我已经扩展了我的日志记录解决方案,其中包含一整套改进和要求,因为我期望日志功能是:

    • 强大且不会导致实际 cmdlet 意外失败(甚至
      例如,当由于某种原因拒绝访问日志文件)

    • 调用简单,可用作
      Write-Host 命令替换

    • 解析所有数据类型并显示内容

    • 捕获意外的本机脚本错误

    • 能够通过对象进行内联日志记录,以尽量减少额外的代码行

    • 每个条目都有一个准确的 (10ms) 时间戳,以解决性能问题
      拍摄

    • 标准捕获故障排除信息,例如:

      • 脚本版本

      • PowerShell 版本

      • 运行时间(进程开始时间)

      • 运行方式(参数)和从何处(位置)运行

    • 将附加信息附加到不会无限增长的可配置日志文件中

    • 向下兼容 PowerShell 版本 2

    稳健

    如果您想要一个强大的日志记录解决方案,您可能想要使用本机 Start-Transcript cmdlet,但您可能会发现 Start-Transcript 缺少您可能期望从适当的日志记录 cmdlet。您可以寻求第 3 方解决方案,但这通常意味着额外的安装程序和依赖项。
    因此,您决定自己编写它,但即使是仅将信息写入文件的最简单解决方案也可能已经在该领域引起问题:该文件可能无法访问。它甚至可能存在,但是您的脚本被触发了两次,并且多个实例同时运行,日志文件可能被其中一个实例打开并且来自另一个实例的访问被拒绝(参见例如:Powershell Scheduled Tasks conflicts?)。就在这一点上,日志记录实际上应该可以帮助您解决正在发生的事情,因为重复触发也可能导致脚本本身出现意外行为。对于这个特定示例,我在此处提供的解决方案会缓冲输出,直到它能够写入。但是在编写日志记录 cmdlet 和正确格式化输出时还有很多陷阱。

    日志条目

    我已将整个解决方案放在由几个主要部分组成的 Log-Entry.ps1 framework 中:

    1. 帮助标题 - 和带有几个示例的 Main 函数模板
    2. 一个包含一些脚本和日志定义的My 对象
    3. 四个函数来控制日志记录:
      • Log-Entry(别名Log)记录信息和对象
      • Set-LogFile(别名LogFile)设置日志文件的位置
      • End-Script(别名End)可以用来很好地关闭会话
      • ConvertTo-Text(别名CText)解析对象

    如需最新的Log-Entry.ps1 版本,请参阅:https://github.com/iRon7/Log-Entry。

    用法

    下载上面的Log-Entry.ps1 framwork,用自己的脚本替换Main {}函数中的例子。在您想要显示和记录信息的任何地方,使用Log 命令(类似于Write-Host 命令语法)。
    运行脚本并检查日志文件:%Temp%\&lt;ScriptName&gt;.Log

    语法

    有关语法的详细信息,请参阅:readme.mdhttps://github.com/iRon7/Log-Entry

    示例

    以下是一些显示Log-Entry 框架的一些功能的命令:

    LogFile .\Test.log                  # Redirect the log file location (Optional)
    Log -Color Yellow "Examples:"
    Log "Several examples that usually aren't displayed by Write-Host:" $NotSet @() @(@()) @(@(), @()) @($Null)
    Log -Indent 1 "Note 1: An empty string:" "" "isn't displayed by Log-Entry either (as you usually do not want every comment quoted)."
    Log -Indent 2 "In case you want to reveal a (possible) empty string, use -QuoteString:" -NoNewline; Log -QuoteString ""
    Log -Indent 1 "Note 2: An empty array embedded in another array:" @(@()) "is flattened by PowerShell (and not Write-Log)."
    Log -Indent 2 "To prevent this use a comma in front of the embbed array: " @(,@())
    Log "A hashtable:" @{one = 1; two = 2; three = 3}
    Log "A recursive hashtable:" @{one = @{one = @{one = 1; two = 2; three = 3}; two = 2; three = 3}; two = 2; three = 3} -Expand -Depth:9
    Log "Character array:" "Hallo World".ToCharArray()
    Log-Verbose "The following line produces a error which is captured in the log file:"
    $File = Log "File:" (Get-ChildItem "C:\NoSuchFile.txt" -ErrorAction SilentlyContinue)
    Log-Verbose "The switch -FlushErrors prevents the error being logged:"
    $File = Log "File:" (Get-ChildItem "C:\NoSuchFile.txt" -ErrorAction SilentlyContinue) -FlushErrors
    Log "Below are two inline log examples (the object preceding the ""?"" is returned):"
    $Height = Log "Height:" 3 ? "Inch"
    $Width  = Log "Width:"  4 ? "Inch"
    Log-Verbose "Or one display/log line spread over multiple code lines:"
    Log "Periphery:" -NoNewline
    $Periphery = Log (2 * $Height + 2 * $Width) ? -Color Green -NoNewline
    Log "Inch"
    Log-Debug "Password:" $Password "(This will not be shown and captured unless the common -Debug argument is supplied)"
    

    显示

    示例命令以下列格式显示:

    日志文件

    示例命令在日志文件中记录以下信息:

    2017-07-13  PowerShell version: 5.1.15063.483, process start: 2017-07-13 15:39:44
    15:39:46.75 Log-Entry version: 02.00.01, command line: C:\Users\User\Scripts\Log-Entry\Log-Entry.ps1 
    15:39:46.80 Examples:
    15:39:46.94 Several examples that usually aren't displayed by Write-Host: $Null @() @() @(@(), @()) @($Null)
    15:39:46.95         Note 1: An empty string: isn't displayed by Log-Entry either (as you do not want every comment quoted).
    15:39:46.98                 In case you want to reveal a (possible) empty string, use -QuoteString: ""
    15:39:47.00         Note 2: An empty array embedded in another array: @() is flattened by PowerShell (and not Write-Log).
    15:39:47.01                 To prevent this use a comma in front of the embbed array:  @(@())
    15:39:47.05 A hashtable: @{one = 1, three = 3, two = 2}
    15:39:47.06 A recursive hashtable: @{
                    one = @{
                        one = @{
                            one = 1, 
                            three = 3, 
                            two = 2
                        }, 
                        three = 3, 
                        two = 2
                    }, 
                    three = 3, 
                    two = 2
                }
    15:39:47.10 Character array: @(H, a, l, l, o,  , W, o, r, l, d)
    15:39:47.11 The following line produces a error which is captured in the log file:
    Error at 51,23: Cannot find path 'C:\NoSuchFile.txt' because it does not exist.
    15:39:47.15 File: $Null
    15:39:47.16 The switch -FlushErrors prevents the error being logged:
    15:39:47.17 File: $Null
    15:39:47.17 Below are two inline log examples (the object preceding the "?" is returned):
    15:39:47.18 Height: 3 Inch
    15:39:47.19 Width: 4 Inch
    15:39:47.19 Or one display/log line spread over multiple code lines:
    15:39:47.20 Periphery: 14 Inch
    15:39:47.27 End (Execution time: 00:00:00.5781145, Process time: 00:00:03.1067112)
    

    【讨论】:

    • 哇。在被 python 的日志记录宠坏后进入 powershell,提供的功能令人沮丧。谷歌搜索更好的东西并偶然发现了这篇文章。看到您对此的详细考虑和想法真是太棒了。这也太新奇了!希望这会像疯了一样流行起来,最终微软会与您讨论将其整合到适当的 powershell 中。关闭下载并尝试一下。谢谢!
    猜你喜欢
    • 2011-05-28
    • 2016-06-07
    • 2019-06-21
    • 2017-12-24
    • 1970-01-01
    • 2023-02-08
    • 2021-08-15
    • 1970-01-01
    相关资源
    最近更新 更多