Updated logging plus more resilient coding to allow better run experience

This commit is contained in:
2026-06-29 13:34:05 +02:00
parent 105f5ac7d5
commit 2dab6f970a
19 changed files with 2052 additions and 60 deletions
@@ -9,12 +9,32 @@ using Microsoft.BizTalk.ExplorerOM;
namespace BizTalkMonitorConfigGenerator.BizTalk
{
/// <summary>
/// Discovers monitorable endpoints from the BizTalk management catalog.
/// </summary>
public sealed class BizTalkCatalogDiscoveryService
{
/// <summary>
/// BizTalk discovery options.
/// </summary>
private readonly BizTalkOptions _options;
/// <summary>
/// Output defaults used while normalizing endpoints.
/// </summary>
private readonly OutputOptions _outputOptions;
/// <summary>
/// Logger used for discovery decisions.
/// </summary>
private readonly GeneratorFileLogger _logger;
/// <summary>
/// Initializes a new BizTalk catalog discovery service.
/// </summary>
/// <param name="options">BizTalk discovery options.</param>
/// <param name="outputOptions">Output defaults for generated targets.</param>
/// <param name="logger">Discovery logger.</param>
public BizTalkCatalogDiscoveryService(BizTalkOptions options, OutputOptions outputOptions, GeneratorFileLogger logger)
{
_options = options ?? new BizTalkOptions();
@@ -22,10 +42,17 @@ namespace BizTalkMonitorConfigGenerator.BizTalk
_logger = logger;
}
/// <summary>
/// Discovers all configured BizTalk artifact endpoints and returns monitor-compatible endpoints.
/// </summary>
/// <returns>The discovered endpoints sorted by application, target name and endpoint.</returns>
public IReadOnlyCollection<DiscoveredEndpoint> Discover()
{
var discovered = new List<DiscoveredEndpoint>();
var inspectedApplications = 0;
var skippedApplications = 0;
_logger.Info("BizTalk-Katalog wird geöffnet. IncludeSendPorts=" + _options.IncludeSendPorts + ", IncludeReceiveLocations=" + _options.IncludeReceiveLocations + ".");
var catalog = new BtsCatalogExplorer();
catalog.ConnectionString = _options.ConnectionString;
@@ -38,10 +65,14 @@ namespace BizTalkMonitorConfigGenerator.BizTalk
if (ShouldSkipApplication(application))
{
skippedApplications++;
_logger.Info("BizTalk-Anwendung übersprungen: " + application.Name);
continue;
}
inspectedApplications++;
_logger.Info("BizTalk-Anwendung wird analysiert: " + application.Name);
if (_options.IncludeSendPorts)
{
DiscoverSendPorts(application, discovered);
@@ -53,6 +84,8 @@ namespace BizTalkMonitorConfigGenerator.BizTalk
}
}
_logger.Info("BizTalk-Discovery beendet. AnalysierteAnwendungen=" + inspectedApplications + ", UebersprungeneAnwendungen=" + skippedApplications + ", UnterstuetzteTargets=" + discovered.Count + ".");
return discovered
.OrderBy(x => x.MappedApplicationName, StringComparer.OrdinalIgnoreCase)
.ThenBy(x => x.TargetName, StringComparer.OrdinalIgnoreCase)
@@ -60,6 +93,11 @@ namespace BizTalkMonitorConfigGenerator.BizTalk
.ToList();
}
/// <summary>
/// Discovers send port transports from one BizTalk application.
/// </summary>
/// <param name="application">The BizTalk application to inspect.</param>
/// <param name="discovered">The target collection receiving supported endpoints.</param>
private void DiscoverSendPorts(Application application, List<DiscoveredEndpoint> discovered)
{
foreach (SendPort sendPort in application.SendPorts)
@@ -90,6 +128,11 @@ namespace BizTalkMonitorConfigGenerator.BizTalk
}
}
/// <summary>
/// Discovers receive locations from one BizTalk application.
/// </summary>
/// <param name="application">The BizTalk application to inspect.</param>
/// <param name="discovered">The target collection receiving supported endpoints.</param>
private void DiscoverReceiveLocations(Application application, List<DiscoveredEndpoint> discovered)
{
foreach (ReceivePort receivePort in application.ReceivePorts)
@@ -131,6 +174,16 @@ namespace BizTalkMonitorConfigGenerator.BizTalk
}
}
/// <summary>
/// Adds a send port transport when it exists and can be normalized.
/// </summary>
/// <param name="discovered">The target collection receiving supported endpoints.</param>
/// <param name="bizTalkApplicationName">The source BizTalk application name.</param>
/// <param name="artifactType">The artifact type label.</param>
/// <param name="targetNameBase">The base name for the generated target.</param>
/// <param name="artifactName">The source artifact name.</param>
/// <param name="transport">The BizTalk transport information.</param>
/// <param name="isSecondary">True when the transport is a secondary send port transport.</param>
private void AddTransportIfSupported(List<DiscoveredEndpoint> discovered, string bizTalkApplicationName, string artifactType, string targetNameBase, string artifactName, TransportInfo transport, bool isSecondary)
{
if (transport == null)
@@ -150,6 +203,17 @@ namespace BizTalkMonitorConfigGenerator.BizTalk
isSecondary);
}
/// <summary>
/// Adds a discovered endpoint when artifact filters and endpoint normalization allow it.
/// </summary>
/// <param name="discovered">The target collection receiving supported endpoints.</param>
/// <param name="bizTalkApplicationName">The source BizTalk application name.</param>
/// <param name="artifactType">The artifact type label.</param>
/// <param name="targetNameBase">The base name for the generated target.</param>
/// <param name="artifactName">The source artifact name.</param>
/// <param name="adapterName">The BizTalk adapter name.</param>
/// <param name="sourceAddress">The raw address read from BizTalk.</param>
/// <param name="isSecondary">True when the endpoint is from a secondary transport.</param>
private void AddEndpointIfSupported(List<DiscoveredEndpoint> discovered, string bizTalkApplicationName, string artifactType, string targetNameBase, string artifactName, string adapterName, string sourceAddress, bool isSecondary)
{
if (ShouldSkipArtifact(artifactName))
@@ -190,6 +254,11 @@ namespace BizTalkMonitorConfigGenerator.BizTalk
_logger.Discovery("ADD", artifactType, bizTalkApplicationName, artifactName, adapterName, sourceAddress, normalizedEndpoint, "Zuordnung zu ApplicationName='" + mappedApplication + "'.");
}
/// <summary>
/// Determines whether a BizTalk application should be skipped.
/// </summary>
/// <param name="application">The BizTalk application to inspect.</param>
/// <returns>True when the application should be skipped.</returns>
private bool ShouldSkipApplication(Application application)
{
if (application == null)
@@ -205,6 +274,11 @@ namespace BizTalkMonitorConfigGenerator.BizTalk
return _options.IgnoreApplications.Any(pattern => WildcardMatch(application.Name, pattern));
}
/// <summary>
/// Determines whether a BizTalk artifact name matches a configured ignore pattern.
/// </summary>
/// <param name="artifactName">The artifact name to inspect.</param>
/// <returns>True when the artifact should be skipped.</returns>
private bool ShouldSkipArtifact(string artifactName)
{
if (string.IsNullOrWhiteSpace(artifactName))
@@ -215,6 +289,13 @@ namespace BizTalkMonitorConfigGenerator.BizTalk
return _options.IgnoreArtifactNamePatterns.Any(pattern => WildcardMatch(artifactName, pattern));
}
/// <summary>
/// Resolves the generated monitor application name for a BizTalk artifact.
/// </summary>
/// <param name="bizTalkApplicationName">The source BizTalk application name.</param>
/// <param name="artifactType">The source artifact type.</param>
/// <param name="artifactName">The source artifact name.</param>
/// <returns>The generated monitor application name.</returns>
private string ResolveMappedApplicationName(string bizTalkApplicationName, string artifactType, string artifactName)
{
foreach (var rule in _options.ArtifactMappings.Where(r => r != null && !string.IsNullOrWhiteSpace(r.ApplicationName)))
@@ -241,6 +322,14 @@ namespace BizTalkMonitorConfigGenerator.BizTalk
return string.IsNullOrWhiteSpace(bizTalkApplicationName) ? "Unassigned Application" : bizTalkApplicationName.Trim();
}
/// <summary>
/// Builds a generated monitor target name from BizTalk artifact metadata.
/// </summary>
/// <param name="artifactType">The source artifact type.</param>
/// <param name="baseName">The base artifact display name.</param>
/// <param name="adapterName">The BizTalk adapter name.</param>
/// <param name="isSecondary">True when the endpoint is from a secondary transport.</param>
/// <returns>The generated target name.</returns>
private string BuildTargetName(string artifactType, string baseName, string adapterName, bool isSecondary)
{
var prefix = string.Equals(artifactType, "ReceiveLocation", StringComparison.OrdinalIgnoreCase)
@@ -261,6 +350,16 @@ namespace BizTalkMonitorConfigGenerator.BizTalk
return name;
}
/// <summary>
/// Normalizes a BizTalk adapter address into a monitor-compatible endpoint.
/// </summary>
/// <param name="sourceAddress">The raw address read from BizTalk.</param>
/// <param name="adapterName">The BizTalk adapter name.</param>
/// <param name="normalizedEndpoint">The normalized monitor endpoint when successful.</param>
/// <param name="port">The resolved explicit port when available.</param>
/// <param name="httpMethod">The generated HTTP method when applicable.</param>
/// <param name="reason">The skip reason when normalization fails.</param>
/// <returns>True when the address can be monitored; otherwise false.</returns>
private bool TryNormalizeEndpoint(string sourceAddress, string adapterName, out string normalizedEndpoint, out int? port, out string httpMethod, out string reason)
{
normalizedEndpoint = string.Empty;
@@ -386,6 +485,11 @@ namespace BizTalkMonitorConfigGenerator.BizTalk
return true;
}
/// <summary>
/// Safely resolves the adapter name from a BizTalk transport.
/// </summary>
/// <param name="transport">The BizTalk transport information.</param>
/// <returns>The adapter name or an empty string.</returns>
private static string ResolveAdapterName(TransportInfo transport)
{
try
@@ -398,6 +502,11 @@ namespace BizTalkMonitorConfigGenerator.BizTalk
}
}
/// <summary>
/// Safely resolves the adapter name from a BizTalk protocol type.
/// </summary>
/// <param name="transportType">The BizTalk protocol type.</param>
/// <returns>The adapter name or an empty string.</returns>
private static string ResolveAdapterName(ProtocolType transportType)
{
try
@@ -410,6 +519,11 @@ namespace BizTalkMonitorConfigGenerator.BizTalk
}
}
/// <summary>
/// Safely reads the address from a BizTalk transport.
/// </summary>
/// <param name="transport">The BizTalk transport information.</param>
/// <returns>The transport address or an empty string.</returns>
private static string SafeAddress(TransportInfo transport)
{
try
@@ -422,12 +536,23 @@ namespace BizTalkMonitorConfigGenerator.BizTalk
}
}
/// <summary>
/// Checks whether an adapter name contains a fragment.
/// </summary>
/// <param name="adapterName">The adapter name to inspect.</param>
/// <param name="fragment">The fragment to search for.</param>
/// <returns>True when the adapter name contains the fragment.</returns>
private static bool AdapterContains(string adapterName, string fragment)
{
return !string.IsNullOrWhiteSpace(adapterName)
&& adapterName.IndexOf(fragment, StringComparison.OrdinalIgnoreCase) >= 0;
}
/// <summary>
/// Detects semicolon-separated email recipient lists that are not monitorable endpoints.
/// </summary>
/// <param name="value">The address value to inspect.</param>
/// <returns>True when the value looks like an email address list.</returns>
private static bool LooksLikeEmailAddressList(string value)
{
if (string.IsNullOrWhiteSpace(value))
@@ -475,6 +600,12 @@ namespace BizTalkMonitorConfigGenerator.BizTalk
return true;
}
/// <summary>
/// Applies simple wildcard matching with asterisks and question marks.
/// </summary>
/// <param name="input">The input value.</param>
/// <param name="pattern">The wildcard pattern.</param>
/// <returns>True when the input matches the pattern.</returns>
private static bool WildcardMatch(string input, string pattern)
{
if (string.IsNullOrWhiteSpace(pattern))
@@ -487,6 +618,11 @@ namespace BizTalkMonitorConfigGenerator.BizTalk
return Regex.IsMatch(input, regex, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
}
/// <summary>
/// Determines whether a value looks like a host:port endpoint.
/// </summary>
/// <param name="value">The value to inspect.</param>
/// <returns>True when the value contains a host and numeric port.</returns>
private static bool LooksLikeHostPort(string value)
{
if (string.IsNullOrWhiteSpace(value))
@@ -497,6 +633,11 @@ namespace BizTalkMonitorConfigGenerator.BizTalk
return Regex.IsMatch(value.Trim(), @"^[a-zA-Z0-9._-]+:\d{1,5}($|/.*$)");
}
/// <summary>
/// Parses a port from a host:port value.
/// </summary>
/// <param name="value">The value containing a port.</param>
/// <returns>The parsed port, or null when no port is found.</returns>
private static int? ParsePort(string value)
{
if (string.IsNullOrWhiteSpace(value))
@@ -1,18 +1,68 @@
namespace BizTalkMonitorConfigGenerator.BizTalk
{
/// <summary>
/// Represents one BizTalk artifact endpoint that can be written as a monitor target.
/// </summary>
public sealed class DiscoveredEndpoint
{
/// <summary>
/// Gets or sets the BizTalk artifact type.
/// </summary>
public string ArtifactType { get; set; }
/// <summary>
/// Gets or sets the source BizTalk application name.
/// </summary>
public string BizTalkApplicationName { get; set; }
/// <summary>
/// Gets or sets the generated monitor application name.
/// </summary>
public string MappedApplicationName { get; set; }
/// <summary>
/// Gets or sets the source BizTalk artifact name.
/// </summary>
public string ArtifactName { get; set; }
/// <summary>
/// Gets or sets the BizTalk adapter name.
/// </summary>
public string AdapterName { get; set; }
/// <summary>
/// Gets or sets the raw address read from BizTalk.
/// </summary>
public string SourceAddress { get; set; }
/// <summary>
/// Gets or sets the monitor-compatible endpoint.
/// </summary>
public string NormalizedEndpoint { get; set; }
/// <summary>
/// Gets or sets the generated monitor target name.
/// </summary>
public string TargetName { get; set; }
/// <summary>
/// Gets or sets the HTTP method for generated HTTP targets.
/// </summary>
public string HttpMethod { get; set; }
/// <summary>
/// Gets or sets the explicit target port, when resolved.
/// </summary>
public int? Port { get; set; }
/// <summary>
/// Gets or sets an optional UNC path validation override.
/// </summary>
public bool? ValidateUncPathAccess { get; set; }
/// <summary>
/// Gets or sets discovery detail text for diagnostics.
/// </summary>
public string Detail { get; set; }
}
}