本文于 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 中:
- 帮助标题 - 和带有几个示例的
Main 函数模板
- 一个包含一些脚本和日志定义的
My 对象
- 四个函数来控制日志记录:
-
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%\<ScriptName>.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)