使用 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
的输出那么上面的内容可能对你有帮助...
我正在为 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
的输出那么上面的内容可能对你有帮助...