【问题标题】:Using Powershell to search for specific strings in the last x lines of very large log files使用 Powershell 在非常大的日志文件的最后 x 行中搜索特定字符串
【发布时间】:2012-11-17 10:41:03
【问题描述】:

我有一个要求,到目前为止我还没有在论坛上找到解决方案或帮助...

问题:我监控实时系统,这些会生成许多非常大的日志文件,这些文件每天都会滚动,1GB 的情况并不少见,我所做的是查看(尾端)日志我想要监控的已知错误条件或其他字符串,我可能需要对其采取措施。

由于此类工作既费时又乏味,而且人为错误很容易遗漏日志中的问题,因此我一直在自动执行此日志文件监控。我利用一个名为 Servers Alive 的产品来执行计划检查,并编写脚本来监视日志文件中是否出现我感兴趣的日志条目,这些条目可能表明服务存在问题,并且我可以在必要时调用其他脚本来重新启动服务解决遇到的问题。

我以前使用 Perl 为这些日志监控任务编写过脚本,这些脚本非常快,但不一定是最干净的方法,我是管理员而不是程序员,所以我没有开发人员方法或可以依赖的经验。

下面的 Perl 代码 sn-p 显示我“打开”了一个日志文件 $logfile,然后从文件末尾向后“寻找”给定数量的数据,然后我从这里搜索数据指向我有兴趣监控的日志条目的文件末尾,在此示例中,日志条目为“No packet received from EISEC Client

在此示例中,我们正在监视的日志条目表明 EISEC 服务存在问题,并且简单地重新启动服务通常可以解决问题,所有这些我都会自动使用 Servers Alive 作为计划检查和警报机制。

Perl 脚本函数

sub checkEisecSrvloggedon {

print "$logfile\n";

if (open (EISECSRV_LOGGEDON, $logfile)) {

    seek (EISECSRV_LOGGEDON, -40000, 2);

    $line = <EISECSRV_LOGGEDON>;

    $eisecsrvloggedon_ok = 0;

    while ($line = <EISECSRV_LOGGEDON>) {   

            if ($line =~/No packet received from EISEC Client/) {

                #increment counter
                ++$eisecsrvloggedon_ok; 

                }

            }
        }
    }

既然我们已经转移到 Windows Server 2008 R2Windows 7 客户端,我想尽可能使用 PowerShell 来实现这个解决方案,但我找不到详细说明我如何能够快速高效地完成此任务,并且不会占用大量内存。

我尝试过基于 Get-Content 的解决方案,但由于需要读取整个日志文件,因此此类解决方案无法使用,因为查询日志文件需要很长时间。我需要能够定期检查这些大型日志文件中的一些,在某些情况下每隔几分钟检查一次。我已经看到非常适合尾随日志文件末尾的尾部类型解决方案,并且这些脚本使用System.IO.File 方法。这确实获得了我希望在我的脚本中实现的性能/速度,但我对 PowerShell 不够熟悉,不知道如何使用这种方法快速到达大型日志文件的末尾,并且然后能够向后读取给定数量的数据,然后在此日志部分中搜索相关字符串。

有人有什么想法吗?

【问题讨论】:

    标签: perl powershell


    【解决方案1】:

    如果您使用 PowerShell 3.0 或更高版本,则可以结合使用 get-contentselect-string 命令行开关来获得相同的功能。从 3.0 版开始,get-content 支持 -tail 选项,该选项仅以有效的方式返回文件的最后 n 行。使用它,您可以使用以下内容重新实现上面的 Perl 脚本(在最后 1000 行中搜索):

    # Returns the number of occurrences
    (get-content logfile.txt -Tail 1000 | select-string -pattern "No packet received from EISEC Client").Length
    

    【讨论】:

    • 这听起来像是可以满足我要求的选项。我目前在我的系统上安装了 PowerShell 2.0,我是否可以在我的站点中仅选定数量的机器上使用 PowerShell 3.0,以便在使用 PowerShell V2.0 再次运行机器的脚本中使用这些 PowerShell V3.0 功能?
    • 我猜想运行脚本的机器必须绝对安装了 PowerShell 3.0。话虽如此,更新到 3.0 的风险可能非常低;众所周知,微软在向后兼容性方面非常迂腐。
    • 我会尝试在我的管理客户端上安装 PowerShell 3.0 并尝试一下,如果没有必要,我不想安装在 20 多个服务器和 150 多个客户端上。当我尝试上述方法后,我会回复。
    • 这似乎是一个合理的解决方案,我已经测试并且可以在大约 400MB+ 的日志文件中读取搜索最后 50000 行的特定字符串。 14 秒。 - 我将需要尝试转换 perl 脚本或 2 以查看它们在生产环境中是否正常运行。
    • 这看起来很符合我的要求——还有一个小问题。如果我想从日志中获取最后几分钟的数据,有没有一种简单的方法可以实现这一点?日志条目并不总是一致的,但大多数行都有给出时间的实体,例如这里的示例 - (11:38:15 04-06612- SendOnSocket - Data sent to MDTSVR) - 是否有可能从如果我使用 Get-Date 函数获取当前时间并从该点减去一分钟(或 2 分钟),而不是指定我想要尾随的确切行数,则给定点?
    【解决方案2】:

    向后搜索,this contribution to the Poshcode repository 似乎很适合您的要求:

    # Name: Get-Tail.ps1
    # Author: William Stacey
    # Created: 02/22/2007
    # Description: Gets the last N lines of a file. Does scan from end-of-file so works on large files. Also has a loop flag that prompts for refresh.
    
    function Get-Tail([string]$path = $(throw "Path name must be specified."), [int]$count = 10, [bool]$loop = $false)
    {
        if ( $count -lt 1 ) {$(throw "Count must be greater than 1.")}
        function get-last
        {
            $lineCount = 0
            $reader = new-object -typename System.IO.StreamReader -argumentlist $path, $true
            [long]$pos = $reader.BaseStream.Length - 1
    
            while($pos -gt 0)
            {
                $reader.BaseStream.Position = $pos
                if ($reader.BaseStream.ReadByte() -eq 10)
                {
                    if($pos -eq $reader.BaseStream.Length - 1)
                    {
                        $count++
                    }
                    $lineCount++
                    if ($lineCount -ge $count) { break }
                }
                $pos--
            } 
    
            if ($lineCount -lt $count)
            {
                $reader.BaseStream.Position = 0
            }
    
            while($line = $reader.ReadLine())
            {
                $lines += ,$line
            }
    
            $reader.Close()
            $lines
        }
    
        while(1)
        {
            get-last
            if ( ! $loop ) { break }
            $in = read-host -prompt "Hit [Enter] to tail again or Ctrl-C to exit"
        }
    }
    

    【讨论】:

      【解决方案3】:

      PSCX 有一个名为 Get-FileTail 的 cmdlet,它可以有效地获取文件的最后几行,从而避免事先读取完整的文件。

      【讨论】:

      • 从 3.0 版开始,PowerShell 原生内置获取文件尾部;看我的回答。
      猜你喜欢
      • 2022-11-07
      • 1970-01-01
      • 2016-10-08
      • 1970-01-01
      • 2013-02-11
      • 1970-01-01
      • 2015-04-10
      • 2023-03-15
      • 2020-01-02
      相关资源
      最近更新 更多