Add BizTalk Checkmk Pulse local check
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<appSettings>
|
||||
<!-- Empty or "." means local BizTalk machine. -->
|
||||
<add key="Server" value="." />
|
||||
<add key="ServicePrefix" value="BizTalk" />
|
||||
<add key="EnvironmentName" value="" />
|
||||
|
||||
<add key="QueryTimeoutSeconds" value="25" />
|
||||
<add key="WarnResumableThreshold" value="1" />
|
||||
<add key="CritNonResumableThreshold" value="1" />
|
||||
<add key="MaxSummaryItems" value="12" />
|
||||
|
||||
<!-- In many BizTalk landscapes stopped artifacts can be intentional. -->
|
||||
<add key="AlertOnArtifactRuntimeIssues" value="false" />
|
||||
<add key="EmitPerApplicationSuspensionServices" value="false" />
|
||||
|
||||
<add key="ProbeEventLog" value="true" />
|
||||
<add key="EventLogLookbackMinutes" value="60" />
|
||||
<add key="EventLogWarnThreshold" value="1" />
|
||||
<add key="EventLogCritThreshold" value="10" />
|
||||
<add key="EventLogSources" value="BizTalk Server|XLANG/s|ENTSSO|BizTalk Server Application|BizTalk Server EDI" />
|
||||
</appSettings>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
|
||||
</startup>
|
||||
</configuration>
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" Condition="false" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{A4D4D050-9EA7-4A71-B510-7D9D699B9F38}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>BizTalkCheckmkPulse</RootNamespace>
|
||||
<AssemblyName>BizTalkCheckmkPulse</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<Deterministic>true</Deterministic>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Management" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="CheckmkLocalFormatter.cs" />
|
||||
<Compile Include="EventLogProbe.cs" />
|
||||
<Compile Include="MonitoringOptions.cs" />
|
||||
<Compile Include="Models.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="WmiBizTalkProbe.cs" />
|
||||
<Compile Include="WmiHelpers.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,355 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizTalkCheckmkPulse
|
||||
{
|
||||
internal sealed class CheckmkLocalFormatter
|
||||
{
|
||||
private const int HostStopped = 1;
|
||||
private const int HostStartPending = 2;
|
||||
private const int HostStopPending = 3;
|
||||
private const int HostStarted = 4;
|
||||
private const int HostContinuePending = 5;
|
||||
private const int HostPausePending = 6;
|
||||
private const int HostPaused = 7;
|
||||
private const int HostUnknown = 8;
|
||||
private readonly MonitoringOptions _options;
|
||||
|
||||
public CheckmkLocalFormatter(MonitoringOptions options)
|
||||
{
|
||||
_options = options;
|
||||
}
|
||||
|
||||
public IEnumerable<string> Format(ProbeResult result)
|
||||
{
|
||||
yield return FormatPlatform(result);
|
||||
yield return FormatSuspendedInstances(result);
|
||||
yield return FormatHostInstances(result);
|
||||
yield return FormatRuntimeArtifacts(result);
|
||||
yield return FormatEventLog(result);
|
||||
|
||||
if (_options.EmitPerApplicationSuspensionServices)
|
||||
{
|
||||
foreach (var line in FormatApplicationSuspensions(result))
|
||||
{
|
||||
yield return line;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<string> FormatSelfTest()
|
||||
{
|
||||
yield return BuildLine(CheckState.Ok, _options.ServiceName("Platform"), "-", "Self test OK. Checkmk local check output is valid.");
|
||||
yield return BuildLine(CheckState.Ok, _options.ServiceName("Suspended Instances"), "biztalk_suspended_total=0;;;0", "Self test OK.");
|
||||
}
|
||||
|
||||
public IEnumerable<string> FormatFatal(string message)
|
||||
{
|
||||
yield return BuildLine(CheckState.Unknown, _options.ServiceName("Platform"), "-", message);
|
||||
yield return BuildLine(CheckState.Unknown, _options.ServiceName("Suspended Instances"), "-", message);
|
||||
yield return BuildLine(CheckState.Unknown, _options.ServiceName("Host Instances"), "-", message);
|
||||
yield return BuildLine(CheckState.Unknown, _options.ServiceName("Runtime Artifacts"), "-", message);
|
||||
yield return BuildLine(CheckState.Unknown, _options.ServiceName("Event Log"), "-", message);
|
||||
}
|
||||
|
||||
private string FormatPlatform(ProbeResult result)
|
||||
{
|
||||
var state = result.Platform.WmiConnected ? CheckState.Ok : CheckState.Unknown;
|
||||
var detail = new StringBuilder();
|
||||
detail.Append(result.Platform.WmiConnected ? "BizTalk WMI reachable" : "BizTalk WMI not reachable");
|
||||
detail.Append(", server=").Append(EmptyAsUnknown(result.Platform.ServerName));
|
||||
AppendOptional(detail, "group", result.Platform.GroupName);
|
||||
AppendOptional(detail, "mgmt_db", JoinDb(result.Platform.ManagementDbServer, result.Platform.ManagementDbName));
|
||||
AppendOptional(detail, "msgbox_db", JoinDb(result.Platform.MessageBoxDbServer, result.Platform.MessageBoxDbName));
|
||||
|
||||
return BuildLine(state, _options.ServiceName("Platform"), "-", AppendDiagnostics(detail.ToString(), result.Diagnostics));
|
||||
}
|
||||
|
||||
private string FormatSuspendedInstances(ProbeResult result)
|
||||
{
|
||||
var total = result.SuspendedInstances.Count;
|
||||
var resumable = result.SuspendedInstances.Count(x => x.Kind == SuspendedKind.Resumable);
|
||||
var nonresumable = result.SuspendedInstances.Count(x => x.Kind == SuspendedKind.NonResumable);
|
||||
var state = DetermineSuspensionState(resumable, nonresumable);
|
||||
var metrics = string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"biztalk_suspended_total={0};;;0|biztalk_suspended_resumable={1};{2};;0|biztalk_suspended_nonresumable={3};;{4};0",
|
||||
total,
|
||||
resumable,
|
||||
_options.WarnResumableThreshold,
|
||||
nonresumable,
|
||||
_options.CritNonResumableThreshold);
|
||||
|
||||
var detail = total == 0
|
||||
? "No suspended BizTalk service instances found."
|
||||
: BuildSuspensionDetail(result.SuspendedInstances, total, resumable, nonresumable);
|
||||
|
||||
return BuildLine(state, _options.ServiceName("Suspended Instances"), metrics, detail);
|
||||
}
|
||||
|
||||
private string FormatHostInstances(ProbeResult result)
|
||||
{
|
||||
var total = result.HostInstances.Count;
|
||||
var started = result.HostInstances.Count(x => x.ServiceState == HostStarted);
|
||||
var stopped = result.HostInstances.Count(x => x.ServiceState == HostStopped || x.ServiceState == HostPaused);
|
||||
var pending = result.HostInstances.Count(x => x.ServiceState == HostStartPending || x.ServiceState == HostStopPending || x.ServiceState == HostContinuePending || x.ServiceState == HostPausePending);
|
||||
var unknown = total - started - stopped - pending;
|
||||
var state = total == 0 ? CheckState.Unknown : stopped > 0 || unknown > 0 ? CheckState.Critical : pending > 0 ? CheckState.Warning : CheckState.Ok;
|
||||
var metrics = string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"biztalk_host_instances_total={0};;;0|biztalk_host_instances_started={1};;;0|biztalk_host_instances_stopped={2};;1;0|biztalk_host_instances_pending={3};1;;0|biztalk_host_instances_unknown={4};;1;0",
|
||||
total,
|
||||
started,
|
||||
stopped,
|
||||
pending,
|
||||
unknown);
|
||||
|
||||
var detail = new StringBuilder();
|
||||
detail.Append("Host instances total=").Append(total)
|
||||
.Append(", started=").Append(started)
|
||||
.Append(", stopped=").Append(stopped)
|
||||
.Append(", pending=").Append(pending)
|
||||
.Append(", unknown=").Append(unknown);
|
||||
|
||||
var affected = result.HostInstances
|
||||
.Where(x => x.ServiceState != HostStarted)
|
||||
.Take(_options.MaxSummaryItems)
|
||||
.Select(x => EmptyAsUnknown(x.HostName) + "@" + EmptyAsUnknown(x.RunningServer) + "=" + HostStateName(x.ServiceState));
|
||||
AppendList(detail, "affected", affected);
|
||||
|
||||
return BuildLine(state, _options.ServiceName("Host Instances"), metrics, detail.ToString());
|
||||
}
|
||||
|
||||
private string FormatRuntimeArtifacts(ProbeResult result)
|
||||
{
|
||||
var receiveLocations = result.Applications.Sum(x => x.ReceiveLocationTotal);
|
||||
var receiveLocationsDisabled = result.Applications.Sum(x => x.ReceiveLocationDisabled);
|
||||
var sendPorts = result.Applications.Sum(x => x.SendPortTotal);
|
||||
var sendPortsStarted = result.Applications.Sum(x => x.SendPortStarted);
|
||||
var sendPortsInactive = result.Applications.Sum(x => x.SendPortStopped + x.SendPortBound);
|
||||
var sendPortsUnknown = result.Applications.Sum(x => x.SendPortUnknown);
|
||||
var orchestrations = result.Applications.Sum(x => x.OrchestrationTotal);
|
||||
var orchestrationsStarted = result.Applications.Sum(x => x.OrchestrationStarted);
|
||||
var orchestrationsInactive = result.Applications.Sum(x => x.OrchestrationStopped + x.OrchestrationBound + x.OrchestrationUnbound);
|
||||
var orchestrationsUnknown = result.Applications.Sum(x => x.OrchestrationUnknown);
|
||||
var state = sendPortsUnknown > 0 || orchestrationsUnknown > 0
|
||||
? CheckState.Critical
|
||||
: _options.AlertOnArtifactRuntimeIssues && (receiveLocationsDisabled > 0 || sendPortsInactive > 0 || orchestrationsInactive > 0)
|
||||
? CheckState.Warning
|
||||
: CheckState.Ok;
|
||||
var metrics = string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"biztalk_applications={0};;;0|biztalk_receive_locations={1};;;0|biztalk_receive_locations_disabled={2};1;;0|biztalk_send_ports={3};;;0|biztalk_send_ports_started={4};;;0|biztalk_send_ports_inactive={5};1;;0|biztalk_send_ports_unknown={6};;1;0|biztalk_orchestrations={7};;;0|biztalk_orchestrations_started={8};;;0|biztalk_orchestrations_inactive={9};1;;0|biztalk_orchestrations_unknown={10};;1;0",
|
||||
result.Applications.Count,
|
||||
receiveLocations,
|
||||
receiveLocationsDisabled,
|
||||
sendPorts,
|
||||
sendPortsStarted,
|
||||
sendPortsInactive,
|
||||
sendPortsUnknown,
|
||||
orchestrations,
|
||||
orchestrationsStarted,
|
||||
orchestrationsInactive,
|
||||
orchestrationsUnknown);
|
||||
|
||||
var detail = new StringBuilder();
|
||||
detail.Append("Applications=").Append(result.Applications.Count)
|
||||
.Append("; receive_locations total=").Append(receiveLocations).Append(", disabled=").Append(receiveLocationsDisabled)
|
||||
.Append("; send_ports total=").Append(sendPorts).Append(", started=").Append(sendPortsStarted).Append(", inactive=").Append(sendPortsInactive).Append(", unknown=").Append(sendPortsUnknown)
|
||||
.Append("; orchestrations total=").Append(orchestrations).Append(", started=").Append(orchestrationsStarted).Append(", inactive=").Append(orchestrationsInactive).Append(", unknown=").Append(orchestrationsUnknown);
|
||||
|
||||
var affected = result.Applications
|
||||
.Where(x => x.ReceiveLocationDisabled > 0 || x.SendPortStopped + x.SendPortBound + x.SendPortUnknown > 0 || x.OrchestrationStopped + x.OrchestrationBound + x.OrchestrationUnbound + x.OrchestrationUnknown > 0)
|
||||
.Take(_options.MaxSummaryItems)
|
||||
.Select(x => x.ApplicationName + "(rl_disabled=" + x.ReceiveLocationDisabled + ", sp_inactive=" + (x.SendPortStopped + x.SendPortBound) + ", orch_inactive=" + (x.OrchestrationStopped + x.OrchestrationBound + x.OrchestrationUnbound) + ")");
|
||||
AppendList(detail, "notable_apps", affected);
|
||||
|
||||
return BuildLine(state, _options.ServiceName("Runtime Artifacts"), metrics, detail.ToString());
|
||||
}
|
||||
|
||||
private string FormatEventLog(ProbeResult result)
|
||||
{
|
||||
if (!_options.ProbeEventLog)
|
||||
{
|
||||
return BuildLine(CheckState.Ok, _options.ServiceName("Event Log"), "-", "Event log probe disabled.");
|
||||
}
|
||||
|
||||
if (!result.EventLog.Available)
|
||||
{
|
||||
return BuildLine(CheckState.Unknown, _options.ServiceName("Event Log"), "-", "Application event log could not be read: " + result.EventLog.Failure);
|
||||
}
|
||||
|
||||
var state = _options.EventLogCritThreshold > 0 && result.EventLog.Errors >= _options.EventLogCritThreshold
|
||||
? CheckState.Critical
|
||||
: _options.EventLogWarnThreshold > 0 && (result.EventLog.Errors >= _options.EventLogWarnThreshold || result.EventLog.Warnings >= _options.EventLogWarnThreshold)
|
||||
? CheckState.Warning
|
||||
: CheckState.Ok;
|
||||
var metrics = string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"biztalk_eventlog_errors={0};{1};{2};0|biztalk_eventlog_warnings={3};{1};;0",
|
||||
result.EventLog.Errors,
|
||||
_options.EventLogWarnThreshold,
|
||||
_options.EventLogCritThreshold,
|
||||
result.EventLog.Warnings);
|
||||
var detail = string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"BizTalk-related Application log entries in last {0} minutes: errors={1}, warnings={2}",
|
||||
_options.EventLogLookbackMinutes,
|
||||
result.EventLog.Errors,
|
||||
result.EventLog.Warnings);
|
||||
|
||||
return BuildLine(state, _options.ServiceName("Event Log"), metrics, detail);
|
||||
}
|
||||
|
||||
private IEnumerable<string> FormatApplicationSuspensions(ProbeResult result)
|
||||
{
|
||||
foreach (var group in result.SuspendedInstances.GroupBy(x => x.ApplicationName).OrderBy(x => x.Key, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
var resumable = group.Count(x => x.Kind == SuspendedKind.Resumable);
|
||||
var nonresumable = group.Count(x => x.Kind == SuspendedKind.NonResumable);
|
||||
var state = DetermineSuspensionState(resumable, nonresumable);
|
||||
var metrics = string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"biztalk_suspended_total={0};;;0|biztalk_suspended_resumable={1};{2};;0|biztalk_suspended_nonresumable={3};;{4};0",
|
||||
group.Count(),
|
||||
resumable,
|
||||
_options.WarnResumableThreshold,
|
||||
nonresumable,
|
||||
_options.CritNonResumableThreshold);
|
||||
|
||||
yield return BuildLine(state, _options.ServiceName("Suspended " + group.Key), metrics, "Suspended instances: resumable=" + resumable + ", nonresumable=" + nonresumable);
|
||||
}
|
||||
}
|
||||
|
||||
private CheckState DetermineSuspensionState(int resumable, int nonresumable)
|
||||
{
|
||||
if (_options.CritNonResumableThreshold > 0 && nonresumable >= _options.CritNonResumableThreshold)
|
||||
{
|
||||
return CheckState.Critical;
|
||||
}
|
||||
|
||||
if (_options.WarnResumableThreshold > 0 && resumable >= _options.WarnResumableThreshold)
|
||||
{
|
||||
return CheckState.Warning;
|
||||
}
|
||||
|
||||
return CheckState.Ok;
|
||||
}
|
||||
|
||||
private string BuildSuspensionDetail(IEnumerable<SuspendedInstance> instances, int total, int resumable, int nonresumable)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
builder.Append(total).Append(" suspended BizTalk service instance(s): resumable=").Append(resumable)
|
||||
.Append(", nonresumable=").Append(nonresumable);
|
||||
|
||||
var apps = instances
|
||||
.GroupBy(x => x.ApplicationName)
|
||||
.OrderByDescending(x => x.Count())
|
||||
.Take(_options.MaxSummaryItems)
|
||||
.Select(x => x.Key + "(R=" + x.Count(y => y.Kind == SuspendedKind.Resumable) + ", NR=" + x.Count(y => y.Kind == SuspendedKind.NonResumable) + ")");
|
||||
AppendList(builder, "applications", apps);
|
||||
|
||||
var examples = instances
|
||||
.OrderByDescending(x => x.SuspendTime ?? DateTime.MinValue)
|
||||
.Take(Math.Min(_options.MaxSummaryItems, 5))
|
||||
.Select(x => EmptyAsUnknown(x.ServiceName) + " on " + EmptyAsUnknown(x.HostName));
|
||||
AppendList(builder, "examples", examples);
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private static string BuildLine(CheckState state, string serviceName, string metrics, string detail)
|
||||
{
|
||||
return ((int)state).ToString(CultureInfo.InvariantCulture)
|
||||
+ " \"" + SanitizeService(serviceName) + "\" "
|
||||
+ (string.IsNullOrWhiteSpace(metrics) ? "-" : metrics)
|
||||
+ " "
|
||||
+ SanitizeDetail(detail);
|
||||
}
|
||||
|
||||
private static string AppendDiagnostics(string detail, IEnumerable<string> diagnostics)
|
||||
{
|
||||
var visible = diagnostics == null ? new string[0] : diagnostics.Take(3).ToArray();
|
||||
if (visible.Length == 0)
|
||||
{
|
||||
return detail;
|
||||
}
|
||||
|
||||
return detail + "; diagnostics: " + string.Join("; ", visible);
|
||||
}
|
||||
|
||||
private static void AppendList(StringBuilder builder, string label, IEnumerable<string> values)
|
||||
{
|
||||
var list = values.Where(x => !string.IsNullOrWhiteSpace(x)).ToArray();
|
||||
if (list.Length > 0)
|
||||
{
|
||||
builder.Append("; ").Append(label).Append("=").Append(string.Join(", ", list));
|
||||
}
|
||||
}
|
||||
|
||||
private static void AppendOptional(StringBuilder builder, string label, string value)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
builder.Append(", ").Append(label).Append("=").Append(value);
|
||||
}
|
||||
}
|
||||
|
||||
private static string JoinDb(string server, string database)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(server) && string.IsNullOrWhiteSpace(database))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return EmptyAsUnknown(server) + "\\" + EmptyAsUnknown(database);
|
||||
}
|
||||
|
||||
private static string HostStateName(int state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case HostStopped:
|
||||
return "Stopped";
|
||||
case HostStartPending:
|
||||
return "StartPending";
|
||||
case HostStopPending:
|
||||
return "StopPending";
|
||||
case HostStarted:
|
||||
return "Started";
|
||||
case HostContinuePending:
|
||||
return "ContinuePending";
|
||||
case HostPausePending:
|
||||
return "PausePending";
|
||||
case HostPaused:
|
||||
return "Paused";
|
||||
case HostUnknown:
|
||||
return "Unknown";
|
||||
default:
|
||||
return "Unknown(" + state.ToString(CultureInfo.InvariantCulture) + ")";
|
||||
}
|
||||
}
|
||||
|
||||
private static string EmptyAsUnknown(string value)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(value) ? "unknown" : value.Trim();
|
||||
}
|
||||
|
||||
private static string SanitizeService(string value)
|
||||
{
|
||||
return SanitizeDetail(value).Replace("\"", "'");
|
||||
}
|
||||
|
||||
private static string SanitizeDetail(string value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
return "No details.";
|
||||
}
|
||||
|
||||
return value.Replace('\r', ' ').Replace('\n', ' ').Replace('\t', ' ').Trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace BizTalkCheckmkPulse
|
||||
{
|
||||
internal sealed class EventLogProbe
|
||||
{
|
||||
private readonly MonitoringOptions _options;
|
||||
|
||||
public EventLogProbe(MonitoringOptions options)
|
||||
{
|
||||
_options = options;
|
||||
}
|
||||
|
||||
public void Query(ProbeResult result)
|
||||
{
|
||||
result.EventLog.Since = DateTime.Now.AddMinutes(-_options.EventLogLookbackMinutes);
|
||||
|
||||
try
|
||||
{
|
||||
using (var log = new EventLog("Application", "."))
|
||||
{
|
||||
var entries = log.Entries;
|
||||
for (var i = entries.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var entry = entries[i];
|
||||
if (entry.TimeGenerated < result.EventLog.Since)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (!IsRelevantSource(entry.Source))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entry.EntryType == EventLogEntryType.Error)
|
||||
{
|
||||
result.EventLog.Errors++;
|
||||
}
|
||||
else if (entry.EntryType == EventLogEntryType.Warning)
|
||||
{
|
||||
result.EventLog.Warnings++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.EventLog.Available = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result.EventLog.Available = false;
|
||||
result.EventLog.Failure = ex.GetType().Name + ": " + ex.Message;
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsRelevantSource(string source)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(source))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return _options.EventLogSources.Any(x => source.IndexOf(x, StringComparison.OrdinalIgnoreCase) >= 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BizTalkCheckmkPulse
|
||||
{
|
||||
internal enum CheckState
|
||||
{
|
||||
Ok = 0,
|
||||
Warning = 1,
|
||||
Critical = 2,
|
||||
Unknown = 3
|
||||
}
|
||||
|
||||
internal enum SuspendedKind
|
||||
{
|
||||
Resumable,
|
||||
NonResumable
|
||||
}
|
||||
|
||||
internal sealed class ProbeResult
|
||||
{
|
||||
public ProbeResult()
|
||||
{
|
||||
Diagnostics = new List<string>();
|
||||
Platform = new PlatformState();
|
||||
HostInstances = new List<HostInstanceState>();
|
||||
SuspendedInstances = new List<SuspendedInstance>();
|
||||
Applications = new List<ApplicationRuntimeState>();
|
||||
EventLog = new EventLogState();
|
||||
}
|
||||
|
||||
public List<string> Diagnostics { get; private set; }
|
||||
public PlatformState Platform { get; private set; }
|
||||
public List<HostInstanceState> HostInstances { get; private set; }
|
||||
public List<SuspendedInstance> SuspendedInstances { get; private set; }
|
||||
public List<ApplicationRuntimeState> Applications { get; private set; }
|
||||
public EventLogState EventLog { get; private set; }
|
||||
}
|
||||
|
||||
internal sealed class PlatformState
|
||||
{
|
||||
public bool WmiConnected { get; set; }
|
||||
public string ServerName { get; set; }
|
||||
public string GroupName { get; set; }
|
||||
public string ManagementDbServer { get; set; }
|
||||
public string ManagementDbName { get; set; }
|
||||
public string MessageBoxDbServer { get; set; }
|
||||
public string MessageBoxDbName { get; set; }
|
||||
}
|
||||
|
||||
internal sealed class HostInstanceState
|
||||
{
|
||||
public string InstanceName { get; set; }
|
||||
public string HostName { get; set; }
|
||||
public string RunningServer { get; set; }
|
||||
public int ServiceState { get; set; }
|
||||
}
|
||||
|
||||
internal sealed class SuspendedInstance
|
||||
{
|
||||
public string ApplicationName { get; set; }
|
||||
public string ServiceName { get; set; }
|
||||
public string HostName { get; set; }
|
||||
public string ServiceClass { get; set; }
|
||||
public string InstanceId { get; set; }
|
||||
public string ErrorDescription { get; set; }
|
||||
public DateTime? SuspendTime { get; set; }
|
||||
public SuspendedKind Kind { get; set; }
|
||||
}
|
||||
|
||||
internal sealed class ApplicationRuntimeState
|
||||
{
|
||||
public string ApplicationName { get; set; }
|
||||
public int ReceiveLocationTotal { get; set; }
|
||||
public int ReceiveLocationDisabled { get; set; }
|
||||
public int SendPortTotal { get; set; }
|
||||
public int SendPortStarted { get; set; }
|
||||
public int SendPortStopped { get; set; }
|
||||
public int SendPortBound { get; set; }
|
||||
public int SendPortUnknown { get; set; }
|
||||
public int OrchestrationTotal { get; set; }
|
||||
public int OrchestrationStarted { get; set; }
|
||||
public int OrchestrationStopped { get; set; }
|
||||
public int OrchestrationBound { get; set; }
|
||||
public int OrchestrationUnbound { get; set; }
|
||||
public int OrchestrationUnknown { get; set; }
|
||||
}
|
||||
|
||||
internal sealed class EventLogState
|
||||
{
|
||||
public bool Available { get; set; }
|
||||
public int Errors { get; set; }
|
||||
public int Warnings { get; set; }
|
||||
public DateTime Since { get; set; }
|
||||
public string Failure { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
namespace BizTalkCheckmkPulse
|
||||
{
|
||||
internal sealed class MonitoringOptions
|
||||
{
|
||||
public string Server { get; set; }
|
||||
public string ServicePrefix { get; set; }
|
||||
public string EnvironmentName { get; set; }
|
||||
public int QueryTimeoutSeconds { get; set; }
|
||||
public int WarnResumableThreshold { get; set; }
|
||||
public int CritNonResumableThreshold { get; set; }
|
||||
public int MaxSummaryItems { get; set; }
|
||||
public bool AlertOnArtifactRuntimeIssues { get; set; }
|
||||
public bool EmitPerApplicationSuspensionServices { get; set; }
|
||||
public bool ProbeEventLog { get; set; }
|
||||
public int EventLogLookbackMinutes { get; set; }
|
||||
public int EventLogWarnThreshold { get; set; }
|
||||
public int EventLogCritThreshold { get; set; }
|
||||
public IReadOnlyList<string> EventLogSources { get; set; }
|
||||
public bool SelfTest { get; set; }
|
||||
|
||||
public MonitoringOptions()
|
||||
{
|
||||
Server = ".";
|
||||
ServicePrefix = "BizTalk";
|
||||
EnvironmentName = string.Empty;
|
||||
QueryTimeoutSeconds = 25;
|
||||
WarnResumableThreshold = 1;
|
||||
CritNonResumableThreshold = 1;
|
||||
MaxSummaryItems = 12;
|
||||
AlertOnArtifactRuntimeIssues = false;
|
||||
EmitPerApplicationSuspensionServices = false;
|
||||
ProbeEventLog = true;
|
||||
EventLogLookbackMinutes = 60;
|
||||
EventLogWarnThreshold = 1;
|
||||
EventLogCritThreshold = 10;
|
||||
EventLogSources = new[] { "BizTalk Server", "XLANG/s", "ENTSSO", "BizTalk Server Application", "BizTalk Server EDI" };
|
||||
}
|
||||
|
||||
public string ServiceName(string suffix)
|
||||
{
|
||||
var prefix = string.IsNullOrWhiteSpace(EnvironmentName)
|
||||
? ServicePrefix
|
||||
: ServicePrefix + " " + EnvironmentName.Trim();
|
||||
return prefix.Trim() + " " + suffix;
|
||||
}
|
||||
|
||||
public static MonitoringOptions Load(string[] args)
|
||||
{
|
||||
var options = new MonitoringOptions();
|
||||
var settings = ConfigurationManager.AppSettings;
|
||||
|
||||
options.Server = ReadString(settings, "Server", options.Server);
|
||||
options.ServicePrefix = ReadString(settings, "ServicePrefix", options.ServicePrefix);
|
||||
options.EnvironmentName = ReadString(settings, "EnvironmentName", options.EnvironmentName);
|
||||
options.QueryTimeoutSeconds = ReadInt(settings, "QueryTimeoutSeconds", options.QueryTimeoutSeconds, 5, 120);
|
||||
options.WarnResumableThreshold = ReadInt(settings, "WarnResumableThreshold", options.WarnResumableThreshold, 0, 1000000);
|
||||
options.CritNonResumableThreshold = ReadInt(settings, "CritNonResumableThreshold", options.CritNonResumableThreshold, 0, 1000000);
|
||||
options.MaxSummaryItems = ReadInt(settings, "MaxSummaryItems", options.MaxSummaryItems, 1, 100);
|
||||
options.AlertOnArtifactRuntimeIssues = ReadBool(settings, "AlertOnArtifactRuntimeIssues", options.AlertOnArtifactRuntimeIssues);
|
||||
options.EmitPerApplicationSuspensionServices = ReadBool(settings, "EmitPerApplicationSuspensionServices", options.EmitPerApplicationSuspensionServices);
|
||||
options.ProbeEventLog = ReadBool(settings, "ProbeEventLog", options.ProbeEventLog);
|
||||
options.EventLogLookbackMinutes = ReadInt(settings, "EventLogLookbackMinutes", options.EventLogLookbackMinutes, 1, 10080);
|
||||
options.EventLogWarnThreshold = ReadInt(settings, "EventLogWarnThreshold", options.EventLogWarnThreshold, 0, 1000000);
|
||||
options.EventLogCritThreshold = ReadInt(settings, "EventLogCritThreshold", options.EventLogCritThreshold, 0, 1000000);
|
||||
options.EventLogSources = ReadSources(ReadString(settings, "EventLogSources", string.Join("|", options.EventLogSources)));
|
||||
|
||||
ApplyArguments(options, args ?? new string[0]);
|
||||
return options;
|
||||
}
|
||||
|
||||
private static void ApplyArguments(MonitoringOptions options, string[] args)
|
||||
{
|
||||
for (var i = 0; i < args.Length; i++)
|
||||
{
|
||||
var arg = args[i];
|
||||
if (EqualsAny(arg, "--self-test", "/self-test"))
|
||||
{
|
||||
options.SelfTest = true;
|
||||
}
|
||||
else if (EqualsAny(arg, "--server", "/server") && i + 1 < args.Length)
|
||||
{
|
||||
options.Server = args[++i];
|
||||
}
|
||||
else if (EqualsAny(arg, "--environment", "/environment") && i + 1 < args.Length)
|
||||
{
|
||||
options.EnvironmentName = args[++i];
|
||||
}
|
||||
else if (EqualsAny(arg, "--emit-app-services", "/emit-app-services"))
|
||||
{
|
||||
options.EmitPerApplicationSuspensionServices = true;
|
||||
}
|
||||
else if (EqualsAny(arg, "--alert-artifacts", "/alert-artifacts"))
|
||||
{
|
||||
options.AlertOnArtifactRuntimeIssues = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string ReadString(System.Collections.Specialized.NameValueCollection settings, string key, string fallback)
|
||||
{
|
||||
var value = settings[key];
|
||||
return value == null ? fallback : value.Trim();
|
||||
}
|
||||
|
||||
private static int ReadInt(System.Collections.Specialized.NameValueCollection settings, string key, int fallback, int min, int max)
|
||||
{
|
||||
int parsed;
|
||||
if (!int.TryParse(settings[key], NumberStyles.Integer, CultureInfo.InvariantCulture, out parsed))
|
||||
{
|
||||
return fallback;
|
||||
}
|
||||
|
||||
if (parsed < min)
|
||||
{
|
||||
return min;
|
||||
}
|
||||
|
||||
return parsed > max ? max : parsed;
|
||||
}
|
||||
|
||||
private static bool ReadBool(System.Collections.Specialized.NameValueCollection settings, string key, bool fallback)
|
||||
{
|
||||
bool parsed;
|
||||
return bool.TryParse(settings[key], out parsed) ? parsed : fallback;
|
||||
}
|
||||
|
||||
private static IReadOnlyList<string> ReadSources(string value)
|
||||
{
|
||||
return (value ?? string.Empty)
|
||||
.Split(new[] { '|', ';', ',' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(x => x.Trim())
|
||||
.Where(x => x.Length > 0)
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private static bool EqualsAny(string value, params string[] candidates)
|
||||
{
|
||||
return candidates.Any(x => string.Equals(value, x, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BizTalkCheckmkPulse
|
||||
{
|
||||
internal static class Program
|
||||
{
|
||||
private static int Main(string[] args)
|
||||
{
|
||||
MonitoringOptions options = null;
|
||||
|
||||
try
|
||||
{
|
||||
Console.OutputEncoding = new UTF8Encoding(false);
|
||||
options = MonitoringOptions.Load(args);
|
||||
var formatter = new CheckmkLocalFormatter(options);
|
||||
|
||||
if (options.SelfTest)
|
||||
{
|
||||
foreach (var line in formatter.FormatSelfTest())
|
||||
{
|
||||
Console.WriteLine(line);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
var result = new ProbeResult();
|
||||
var wmiProbe = new WmiBizTalkProbe(options);
|
||||
wmiProbe.Query(result);
|
||||
|
||||
if (options.ProbeEventLog)
|
||||
{
|
||||
var eventLogProbe = new EventLogProbe(options);
|
||||
eventLogProbe.Query(result);
|
||||
}
|
||||
|
||||
foreach (var line in formatter.Format(result))
|
||||
{
|
||||
Console.WriteLine(line);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var fallbackOptions = options ?? new MonitoringOptions();
|
||||
var formatter = new CheckmkLocalFormatter(fallbackOptions);
|
||||
foreach (var line in formatter.FormatFatal("BizTalk Checkmk Pulse failed: " + ex.GetType().Name + ": " + ex.Message))
|
||||
{
|
||||
Console.WriteLine(line);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,288 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Management;
|
||||
|
||||
namespace BizTalkCheckmkPulse
|
||||
{
|
||||
internal sealed class WmiBizTalkProbe
|
||||
{
|
||||
private const string NamespacePath = "root\\MicrosoftBizTalkServer";
|
||||
private const string UnknownApplication = "(unknown)";
|
||||
private const int SuspendedResumable = 4;
|
||||
private const int SuspendedNonResumable = 32;
|
||||
private const int SendPortBound = 1;
|
||||
private const int SendPortStopped = 2;
|
||||
private const int SendPortStarted = 3;
|
||||
private const int OrchestrationUnbound = 1;
|
||||
private const int OrchestrationBound = 2;
|
||||
private const int OrchestrationStopped = 3;
|
||||
private const int OrchestrationStarted = 4;
|
||||
private readonly MonitoringOptions _options;
|
||||
|
||||
public WmiBizTalkProbe(MonitoringOptions options)
|
||||
{
|
||||
_options = options;
|
||||
}
|
||||
|
||||
public void Query(ProbeResult result)
|
||||
{
|
||||
var server = string.IsNullOrWhiteSpace(_options.Server) || _options.Server == "."
|
||||
? Environment.MachineName
|
||||
: _options.Server.Trim();
|
||||
|
||||
result.Platform.ServerName = server;
|
||||
|
||||
var scope = new ManagementScope("\\\\" + server + "\\" + NamespacePath);
|
||||
scope.Connect();
|
||||
result.Platform.WmiConnected = true;
|
||||
|
||||
QueryPlatform(scope, result);
|
||||
QueryHostInstances(scope, result);
|
||||
var applicationIndex = BuildApplicationIndex(scope, result);
|
||||
QueryApplicationRuntime(scope, result);
|
||||
QuerySuspendedInstances(scope, result, applicationIndex);
|
||||
}
|
||||
|
||||
private void QueryPlatform(ManagementScope scope, ProbeResult result)
|
||||
{
|
||||
TryQuery(scope, result, "MSBTS_GroupSetting", "SELECT * FROM MSBTS_GroupSetting", item =>
|
||||
{
|
||||
result.Platform.GroupName = FirstNonEmpty(WmiHelpers.GetString(item, "Name"), WmiHelpers.GetString(item, "MgmtDbName"));
|
||||
result.Platform.ManagementDbServer = FirstNonEmpty(WmiHelpers.GetString(item, "MgmtDbServerName"), WmiHelpers.GetString(item, "DBServerName"));
|
||||
result.Platform.ManagementDbName = FirstNonEmpty(WmiHelpers.GetString(item, "MgmtDbName"), WmiHelpers.GetString(item, "DatabaseName"));
|
||||
}, true);
|
||||
|
||||
TryQuery(scope, result, "MSBTS_MessageBoxSetting", "SELECT * FROM MSBTS_MessageBoxSetting", item =>
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(result.Platform.MessageBoxDbServer))
|
||||
{
|
||||
result.Platform.MessageBoxDbServer = FirstNonEmpty(WmiHelpers.GetString(item, "DBServerName"), WmiHelpers.GetString(item, "ServerName"));
|
||||
result.Platform.MessageBoxDbName = FirstNonEmpty(WmiHelpers.GetString(item, "DBName"), WmiHelpers.GetString(item, "DatabaseName"));
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
private Dictionary<string, string> BuildApplicationIndex(ManagementScope scope, ProbeResult result)
|
||||
{
|
||||
var index = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
AddArtifactIndex(scope, result, index, "MSBTS_SendPort");
|
||||
AddArtifactIndex(scope, result, index, "MSBTS_ReceivePort");
|
||||
AddArtifactIndex(scope, result, index, "MSBTS_ReceiveLocation");
|
||||
AddArtifactIndex(scope, result, index, "MSBTS_Orchestration");
|
||||
return index;
|
||||
}
|
||||
|
||||
private void AddArtifactIndex(ManagementScope scope, ProbeResult result, Dictionary<string, string> index, string className)
|
||||
{
|
||||
TryQuery(scope, result, className, "SELECT * FROM " + className, item =>
|
||||
{
|
||||
var app = GetApplicationName(item);
|
||||
foreach (var key in CandidateKeys(item))
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(key) && !index.ContainsKey(key))
|
||||
{
|
||||
index.Add(key, app);
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
|
||||
private void QueryHostInstances(ManagementScope scope, ProbeResult result)
|
||||
{
|
||||
TryQuery(scope, result, "MSBTS_HostInstance", "SELECT * FROM MSBTS_HostInstance", item =>
|
||||
{
|
||||
result.HostInstances.Add(new HostInstanceState
|
||||
{
|
||||
InstanceName = FirstNonEmpty(WmiHelpers.GetString(item, "InstanceName"), WmiHelpers.GetString(item, "Name")),
|
||||
HostName = WmiHelpers.GetString(item, "HostName"),
|
||||
RunningServer = WmiHelpers.GetString(item, "RunningServer"),
|
||||
ServiceState = WmiHelpers.GetInt32(item, "ServiceState", 0)
|
||||
});
|
||||
}, true);
|
||||
}
|
||||
|
||||
private void QueryApplicationRuntime(ManagementScope scope, ProbeResult result)
|
||||
{
|
||||
var apps = new Dictionary<string, ApplicationRuntimeState>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
TryQuery(scope, result, "MSBTS_ReceiveLocation", "SELECT * FROM MSBTS_ReceiveLocation", item =>
|
||||
{
|
||||
var app = GetApplication(apps, GetApplicationName(item));
|
||||
app.ReceiveLocationTotal++;
|
||||
if (WmiHelpers.GetBoolean(item, "IsDisabled", false))
|
||||
{
|
||||
app.ReceiveLocationDisabled++;
|
||||
}
|
||||
}, true);
|
||||
|
||||
TryQuery(scope, result, "MSBTS_SendPort", "SELECT * FROM MSBTS_SendPort", item =>
|
||||
{
|
||||
var app = GetApplication(apps, GetApplicationName(item));
|
||||
app.SendPortTotal++;
|
||||
switch (WmiHelpers.GetInt32(item, "Status", 0))
|
||||
{
|
||||
case SendPortStarted:
|
||||
app.SendPortStarted++;
|
||||
break;
|
||||
case SendPortStopped:
|
||||
app.SendPortStopped++;
|
||||
break;
|
||||
case SendPortBound:
|
||||
app.SendPortBound++;
|
||||
break;
|
||||
default:
|
||||
app.SendPortUnknown++;
|
||||
break;
|
||||
}
|
||||
}, true);
|
||||
|
||||
TryQuery(scope, result, "MSBTS_Orchestration", "SELECT * FROM MSBTS_Orchestration", item =>
|
||||
{
|
||||
var app = GetApplication(apps, GetApplicationName(item));
|
||||
app.OrchestrationTotal++;
|
||||
switch (WmiHelpers.GetInt32(item, "OrchestrationStatus", 0))
|
||||
{
|
||||
case OrchestrationStarted:
|
||||
app.OrchestrationStarted++;
|
||||
break;
|
||||
case OrchestrationStopped:
|
||||
app.OrchestrationStopped++;
|
||||
break;
|
||||
case OrchestrationBound:
|
||||
app.OrchestrationBound++;
|
||||
break;
|
||||
case OrchestrationUnbound:
|
||||
app.OrchestrationUnbound++;
|
||||
break;
|
||||
default:
|
||||
app.OrchestrationUnknown++;
|
||||
break;
|
||||
}
|
||||
}, true);
|
||||
|
||||
result.Applications.AddRange(apps.Values.OrderBy(x => x.ApplicationName, StringComparer.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
private void QuerySuspendedInstances(ManagementScope scope, ProbeResult result, Dictionary<string, string> applicationIndex)
|
||||
{
|
||||
TryQuery(scope, result, "MSBTS_ServiceInstance", "SELECT * FROM MSBTS_ServiceInstance WHERE ServiceStatus = 4 OR ServiceStatus = 32", item =>
|
||||
{
|
||||
var serviceStatus = WmiHelpers.GetInt32(item, "ServiceStatus", 0);
|
||||
result.SuspendedInstances.Add(new SuspendedInstance
|
||||
{
|
||||
ApplicationName = ResolveApplication(item, applicationIndex),
|
||||
ServiceName = FirstNonEmpty(WmiHelpers.GetString(item, "ServiceName"), WmiHelpers.GetString(item, "Name")),
|
||||
HostName = WmiHelpers.GetString(item, "HostName"),
|
||||
ServiceClass = WmiHelpers.GetString(item, "ServiceClass"),
|
||||
InstanceId = WmiHelpers.GetString(item, "InstanceID"),
|
||||
ErrorDescription = WmiHelpers.GetString(item, "ErrorDescription"),
|
||||
SuspendTime = WmiHelpers.GetDmtfDateTime(item, "SuspendTime"),
|
||||
Kind = serviceStatus == SuspendedNonResumable ? SuspendedKind.NonResumable : SuspendedKind.Resumable
|
||||
});
|
||||
}, true);
|
||||
}
|
||||
|
||||
private void TryQuery(ManagementScope scope, ProbeResult result, string label, string queryText, Action<ManagementObject> action, bool required)
|
||||
{
|
||||
try
|
||||
{
|
||||
ForEachObject(scope, queryText, action);
|
||||
}
|
||||
catch (ManagementException ex)
|
||||
{
|
||||
AddDiagnostic(result, label, ex.Message, required);
|
||||
}
|
||||
catch (UnauthorizedAccessException ex)
|
||||
{
|
||||
AddDiagnostic(result, label, ex.Message, required);
|
||||
}
|
||||
catch (System.Runtime.InteropServices.COMException ex)
|
||||
{
|
||||
AddDiagnostic(result, label, ex.Message, required);
|
||||
}
|
||||
}
|
||||
|
||||
private void ForEachObject(ManagementScope scope, string queryText, Action<ManagementObject> action)
|
||||
{
|
||||
var queryOptions = new EnumerationOptions
|
||||
{
|
||||
ReturnImmediately = true,
|
||||
Rewindable = false,
|
||||
Timeout = TimeSpan.FromSeconds(_options.QueryTimeoutSeconds)
|
||||
};
|
||||
|
||||
using (var searcher = new ManagementObjectSearcher(scope, new ObjectQuery(queryText), queryOptions))
|
||||
using (var collection = searcher.Get())
|
||||
{
|
||||
foreach (ManagementObject item in collection)
|
||||
{
|
||||
using (item)
|
||||
{
|
||||
action(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static ApplicationRuntimeState GetApplication(Dictionary<string, ApplicationRuntimeState> apps, string applicationName)
|
||||
{
|
||||
var name = string.IsNullOrWhiteSpace(applicationName) ? UnknownApplication : applicationName.Trim();
|
||||
ApplicationRuntimeState app;
|
||||
if (!apps.TryGetValue(name, out app))
|
||||
{
|
||||
app = new ApplicationRuntimeState { ApplicationName = name };
|
||||
apps.Add(name, app);
|
||||
}
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
private static string ResolveApplication(ManagementBaseObject item, Dictionary<string, string> applicationIndex)
|
||||
{
|
||||
var direct = GetApplicationName(item);
|
||||
if (!string.Equals(direct, UnknownApplication, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return direct;
|
||||
}
|
||||
|
||||
foreach (var key in CandidateKeys(item))
|
||||
{
|
||||
string app;
|
||||
if (!string.IsNullOrWhiteSpace(key) && applicationIndex.TryGetValue(key, out app))
|
||||
{
|
||||
return app;
|
||||
}
|
||||
}
|
||||
|
||||
return UnknownApplication;
|
||||
}
|
||||
|
||||
private static IEnumerable<string> CandidateKeys(ManagementBaseObject item)
|
||||
{
|
||||
yield return WmiHelpers.GetString(item, "ApplicationName");
|
||||
yield return WmiHelpers.GetString(item, "ServiceName");
|
||||
yield return WmiHelpers.GetString(item, "Name");
|
||||
yield return WmiHelpers.GetString(item, "HostName");
|
||||
}
|
||||
|
||||
private static string GetApplicationName(ManagementBaseObject item)
|
||||
{
|
||||
return FirstNonEmpty(
|
||||
WmiHelpers.GetString(item, "ApplicationName"),
|
||||
WmiHelpers.GetString(item, "Application"),
|
||||
WmiHelpers.GetString(item, "BizTalkApplication")) ?? UnknownApplication;
|
||||
}
|
||||
|
||||
private static string FirstNonEmpty(params string[] values)
|
||||
{
|
||||
return values == null ? null : values.FirstOrDefault(x => !string.IsNullOrWhiteSpace(x));
|
||||
}
|
||||
|
||||
private static void AddDiagnostic(ProbeResult result, string label, string message, bool required)
|
||||
{
|
||||
result.Diagnostics.Add((required ? "Required" : "Optional") + " WMI query " + label + " failed: " + message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Management;
|
||||
|
||||
namespace BizTalkCheckmkPulse
|
||||
{
|
||||
internal static class WmiHelpers
|
||||
{
|
||||
public static string GetString(ManagementBaseObject item, string propertyName)
|
||||
{
|
||||
if (item == null || string.IsNullOrWhiteSpace(propertyName))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var value = item.Properties[propertyName] == null ? null : item.Properties[propertyName].Value;
|
||||
return value == null ? string.Empty : Convert.ToString(value, CultureInfo.InvariantCulture).Trim();
|
||||
}
|
||||
catch (ManagementException)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public static int GetInt32(ManagementBaseObject item, string propertyName, int fallback)
|
||||
{
|
||||
var value = GetString(item, propertyName);
|
||||
int parsed;
|
||||
return int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out parsed) ? parsed : fallback;
|
||||
}
|
||||
|
||||
public static bool GetBoolean(ManagementBaseObject item, string propertyName, bool fallback)
|
||||
{
|
||||
var value = GetString(item, propertyName);
|
||||
bool parsed;
|
||||
return bool.TryParse(value, out parsed) ? parsed : fallback;
|
||||
}
|
||||
|
||||
public static DateTime? GetDmtfDateTime(ManagementBaseObject item, string propertyName)
|
||||
{
|
||||
var value = GetString(item, propertyName);
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return ManagementDateTimeConverter.ToDateTime(value);
|
||||
}
|
||||
catch (ArgumentOutOfRangeException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user