使用 PowerShell 解析 IBM MQ 报告以格式化 Prometheus 可以使用

Use PowerShell to parse IBM MQ report to format Prometheus can use

我正在为 Prometheus 做 POC,我们希望从中获取指标的其中一件事是 W2012R2 上的 IBM MQ 服务器 运行。有一个适用于 IBM MQ 的 Prometheus 导出器,但它适用于 Linux,无法在 Windows.

上运行

我们确实有一个选项可以安排报告将数据写入文件,但是该文件的输出对 Prometheus 没有用,因此必须重新格式化文档。

我一直在思考如何使用 PowerShell 实现这一点,但由于我缺乏使用 PowerShell 编写脚本的经验,所以我决定在这里寻求帮助。

你们知道如何更改此布局中的报告:

5724-H72 (C) Copyright IBM Corp. 1994, 2015.
Starting MQSC for queue manager QMGR.


     1 : DISPLAY CHSTATUS(*) 

AMQ8450: Display Channel Status details.
   CHANNEL(QMGR.HOSTNAME.R1)               CHLTYPE(RCVR)
   CONNAME(10.10.10.10)                    CURRENT
   RQMNAME(QMGR)                           STATUS(RUNNING)
   SUBSTATE(RECEIVE)                    
AMQ8450: Display Channel Status details.
   CHANNEL(QMGR.HOSTNAME.R3)               CHLTYPE(RCVR)
   CONNAME(10.10.10.10)                    CURRENT
   STATUS(RUNNING)                         SUBSTATE(RECEIVE)

看起来像这样的东西:

status{channel="QMGR.HOSTNAME.R1", chltype="rcvr", conname="10.10.10.10", rqmname="QMGR"} running
substate{channel="QMGR.HOSTNAME.R1", chltype="rcvr", conname="10.10.10.10", rqmname="QMGR"} receive
status{channel="QMGR.HOSTNAME.R3", chltype="rcvr", conname="10.10.10.10"} running
substate{channel="QMGR.HOSTNAME.R3", chltype="rcvr", conname="10.10.10.10"} receive

这是我到目前为止的想法,我猜不是很漂亮,但它似乎是朝着正确方向迈出的一步...

$Contents = Get-Content ".\CHSTATUS.txt" | select -Skip 5 
foreach($Line in $Contents) {
    foreach($channel in [Regex]::Matches($Line, '(?<=CHANNEL\()(.*?)(?=\))')) {}
    foreach($chltype in [Regex]::Matches($Line, '(?<=CHLTYPE\()(.*?)(?=\))')) {}
    foreach($conname in [Regex]::Matches($Line, '(?<=CONNAME\()(.*?)(?=\))')) {}
    foreach($status in [Regex]::Matches($Line, '(?<=STATUS\()(.*?)(?=\))')) {}
    foreach($substate in [Regex]::Matches($Line, '(?<=SUBSTATE\()(.*?)(?=\))')) {
    Write-Host "status{channel=""$channel"", chltype=""$chltype"", conname=""$conname""}" $status 
    Write-Host "substate{channel=""$channel"", chltype=""$chltype"", conname=""$conname""}" $substate
    }
}

希望有人能在正确的方向上推动我解决这个问题。

我认为这不是一个好方法。使用 Java/PCF 并以您想要的格式输出数据会简单得多。

我发布了一个简单的(功能齐全的)MQ/PCF/Java 程序,名为:MQListChannelStatus01

只需将参数更新为:

request.addParameter(CMQCFC.MQIACH_CHANNEL_INSTANCE_ATTRS,
                     new int []
                     {
                        CMQCFC.MQCACH_CHANNEL_NAME,
                        CMQCFC.MQCACH_CONNECTION_NAME,
                        CMQCFC.MQIACH_CHANNEL_STATUS,
                        CMQCFC.MQIACH_CHANNEL_SUBSTATE,
                        CMQC.MQCA_REMOTE_Q_MGR_NAME
                     } );

您需要注意的是,您将获得有关 运行 正在尝试 运行 等所有频道的信息。因此,"MQCA_REMOTE_Q_MGR_NAME" 是并非对所有渠道类型都有效。

您需要在 "for loop" 中执行以下操作:

int chlType = responses[i].getIntParameterValue(CMQCFC.MQIACH_CHANNEL_TYPE);

String remoteQMName ="";
if (chlType == CMQXC.MQCHT_RECEIVER)
{
   remoteQMName = responses[i].getStringParameterValue(CMQC.MQCA_REMOTE_Q_MGR_NAME);
   if (remoteQMName != null)
      remoteQMName = remoteQMName.trim();
}

要转换该输入并解析出字段,您可以这样做:

$inputFile = 'D:\Report.txt'

# read the file, split into usable blocks and collect in variable $report
$report = ((Get-Content -Path $inputFile -Raw) -split '.*Display Channel Status details\.' | Select-Object -Skip 1 | ForEach-Object {
    $channel  = ([regex] 'CHANNEL\(([^)]+)\)').Match($_).Groups[1].Value
    $chltype  = ([regex] 'CHLTYPE\(([^)]+)\)').Match($_).Groups[1].Value.ToLower()
    $conname  = ([regex] 'CONNAME\(([^)]+)\)').Match($_).Groups[1].Value
    $rqmname  = ([regex] 'RQMNAME\(([^)]+)\)').Match($_).Groups[1].Value
    $status   = ([regex] 'STATUS\(([^)]+)\)').Match($_).Groups[1].Value.ToLower()
    $substate = ([regex] 'SUBSTATE\(([^)]+)\)').Match($_).Groups[1].Value.ToLower()

    if ($rqmname) {
        $out = 'status{{channel="{0}", chltype="{1}", conname="{2}", rqmname="{3}"}} {4}' + "`r`n" +
               'substate{{channel="{0}", chltype="{1}", conname="{2}", rqmname="{3}"}} {5}'
        # output the two lines including rqmname
        $out -f $channel, $chltype, $conname, $rqmname, $status, $substate

    }
    else {
        $out = 'status{{channel="{0}", chltype="{1}", conname="{2}"}} {3}' + "`r`n" +
               'substate{{channel="{0}", chltype="{1}", conname="{2}"}} {4}'
        # output the two lines excluding rqmname
        $out -f $channel, $chltype, $conname, $status, $substate
    }
}) -join "`r`n"

# output on screen
$report

# or save to new file
$report | Set-Content -Path 'D:\ConvertedReport.txt'

结果:

status{channel="QMGR.HOSTNAME.R1", chltype="rcvr", conname="10.10.10.10", rqmname="QMGR"} running
substate{channel="QMGR.HOSTNAME.R1", chltype="rcvr", conname="10.10.10.10", rqmname="QMGR"} receive
status{channel="QMGR.HOSTNAME.R3", chltype="rcvr", conname="10.10.10.10"} running
substate{channel="QMGR.HOSTNAME.R3", chltype="rcvr", conname="10.10.10.10"} receive

我过去不得不为 MQ 通道状态的 Nagios 警报做类似的事情。这是将输出解析为结构化哈希表的通用函数:

function Invoke-ParseMqscOutput
{
    param( $output )

    # strip off the main header and split the AMQ#### records into a $parts array
    $recordHeaderPattern = "^(?<AmqCode>AMQ\d+): (?<AmqDescription>.*)$";
    $parts = [regex]::Split($stdout, $recordHeaderPattern, "Multiline");

    # patterns for splitting attribute strings into AttributeName and AttributeValues
    #     + e.g. CHANNEL(QMGR.HOSTNAME.R1)
    # as well as some pathological cases like:
    #     + special characters        - e.g. ATTRXNAME(attrx.ATTRX_12345[W X-Y\Z])
    #     + comma-separated lists     - e.g. ATTRYNAME(ATTRY1, ATTRY2)
    #     + "flags" with no "( ... )" - e.g. CURRENT
    $attributeNamePattern = "(?<AttributeName>[a-z|A-Z]+)";
    $attributeValuesPattern = "(?<AttributeValues>[a-z|A-Z|0-9|,|_|\-|\.|\[|\]|\|\s]*)";
    $attributePattern = $attributeNamePattern + "(\(" + $attributeValuesPattern + "\))?";

    # convert the parts array into an array of hashtables
    # (one hashtable per record in the output)
    $records = @();
    $index = 1;
    while( $index -lt $parts.Length )
    {

        # split the attributes block into individual attributes and parse them
        $attributes = [ordered] @{};
        $attributeMatches = [regex]::Matches($parts[$index + 2], $attributePattern, "Multiline");
        $attributeMatches | % {
            $attributeName = $_.Groups["AttributeName"].Value;
            $attributeValues = $_.Groups["AttributeValues"].Value;
            $attributeValues = $attributeValues.Split(",") | % { $_.Trim(); };
            $attributes.Add($attributeName, $attributeValues);
        }

        # collect the next 3 "$parts" values into a single record
        $records += @{
            "AmqCode" = $parts[$index]
            "Message" = $parts[$index + 1]
            "Attributes" = $attributes
        };

        # move on to the start of the next record
        $index = $index + 3;

    }

    return @(, $records );

}

例子 用法:

$output = @"
5724-H72 (C) Copyright IBM Corp. 1994, 2015.
Starting MQSC for queue manager QMGR.


     1 : DISPLAY CHSTATUS(*)

AMQ8450: Display Channel Status details.
   CHANNEL(QMGR.HOSTNAME.R1)               CHLTYPE(RCVR)
   CONNAME(10.10.10.10)                    CURRENT
   RQMNAME(QMGR)                           STATUS(RUNNING)
   SUBSTATE(RECEIVE)
   ATTRXNAME(attrx.ATTRX_12345[W X-Y\Z])
   ATTRYNAME(ATTRY1, ATTRY2)
AMQ8450: Display Channel Status details.
   CHANNEL(QMGR.HOSTNAME.R3)               CHLTYPE(RCVR)
   CONNAME(10.10.10.10)                    CURRENT
   STATUS(RUNNING)                         SUBSTATE(RECEIVE)
"@

$records = Invoke-ParseMqscOutput $output;

# examples of how to traverse the $records:
write-host $records[0].Attributes.CHANNEL      # QMGR.HOSTNAME.R1
write-host $records[0].Attributes.ATTRXNAME    # attrx.ATTRX_12345[W X-Y\Z]
write-host $records[0].Attributes.ATTRYNAME[0] # ATTRY1

然后要生成输出,您可以这样做:

$metrics = @();
foreach( $record in $records )
{
    switch( $record.AmqCode )
    {
        "AMQ8450" {
            $attributes = $record.Attributes;
            $metrics += "status{channel=`"$($attributes.CHANNEL)`", chltype=`"$($attributes.CHLTYPE)`", conname=`"$($attributes.CONNAME)`", rqmname=`"$($attributes.RQMNAME)`"} $($attributes.STATE)";
            $metrics += "substate{channel=`"$($attributes.CHANNEL)`", chltype=`"$($attributes.CHLTYPE)`", conname=`"$($attributes.CONNAME)`", rqmname=`"$($attributes.RQMNAME)`"} $($attributes.SUBSTATE)";
        }
    }
}

write-host ($metrics | fl * | out-string);

输出:

status{channel="QMGR.HOSTNAME.R1", chltype="RCVR", conname="10.10.10.10", rqmname="QMGR"}
substate{channel="QMGR.HOSTNAME.R1", chltype="RCVR", conname="10.10.10.10", rqmname="QMGR"} RECEIVE
status{channel="QMGR.HOSTNAME.R3", chltype="RCVR", conname="10.10.10.10", rqmname=""}
substate{channel="QMGR.HOSTNAME.R3", chltype="RCVR", conname="10.10.10.10", rqmname=""} RECEIVE

请注意,如果您 能够 使用 Websphere API 调用命令,您将更容易处理结果,但如果您绝对 来解析 runmqsc.exe 的输出那么上面的内容可能对你有帮助...