Skip to content

Commit c247dbb

Browse files
committed
Add exclusion logging
1 parent 8352a82 commit c247dbb

File tree

4 files changed

+73
-27
lines changed

4 files changed

+73
-27
lines changed

src/Foundatio.Mediator/EndpointGenerator.cs

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,11 @@ public static void Execute(
137137
// Filter handlers that should generate endpoints based on discovery mode
138138
var endpointHandlers = GetEndpointHandlers(handlers, endpointDefaults);
139139

140+
// Collect handlers that were skipped from endpoint generation (with reasons)
141+
var skippedHandlers = handlers
142+
.Where(h => h.Endpoint is { GenerateEndpoint: false, ExcludeReason: not null } && !h.MessageType.IsInterface && !h.IsGenericHandlerClass)
143+
.ToList();
144+
140145
if (endpointHandlers.Count == 0 && !compilationInfo.IsApplication)
141146
return;
142147

@@ -146,7 +151,7 @@ public static void Execute(
146151
ValidateEndpoints(context, endpointHandlers, endpointDefaults);
147152

148153
// Generate the per-module endpoint registration code
149-
var source = GenerateEndpointCode(endpointHandlers, endpointDefaults, configuration, compilationInfo);
154+
var source = GenerateEndpointCode(endpointHandlers, skippedHandlers, endpointDefaults, configuration, compilationInfo);
150155
context.AddSource("_MediatorEndpoints.g.cs", source);
151156
}
152157

@@ -247,7 +252,7 @@ private static void ValidateEndpoints(SourceProductionContext context, List<Hand
247252
/// <summary>
248253
/// Generates the complete endpoint registration source code.
249254
/// </summary>
250-
private static string GenerateEndpointCode(List<HandlerInfo> handlers, EndpointDefaultsInfo endpointDefaults, GeneratorConfiguration configuration, CompilationInfo compilationInfo)
255+
private static string GenerateEndpointCode(List<HandlerInfo> handlers, List<HandlerInfo> skippedHandlers, EndpointDefaultsInfo endpointDefaults, GeneratorConfiguration configuration, CompilationInfo compilationInfo)
251256
{
252257
var source = new IndentedStringBuilder();
253258

@@ -297,7 +302,7 @@ private static string GenerateEndpointCode(List<HandlerInfo> handlers, EndpointD
297302

298303
// Generate the core implementation as a static partial void method
299304
// that fills in the stub declared in _MediatorEndpoints.Api.g.cs
300-
GenerateMapMediatorEndpointsCoreMethod(source, handlers, endpointDefaults, configuration, hasAsParametersAttribute, hasFromBodyAttribute, hasWithOpenApi, assemblySuffix, compilationInfo.HasLoggerFactory);
305+
GenerateMapMediatorEndpointsCoreMethod(source, handlers, skippedHandlers, endpointDefaults, configuration, hasAsParametersAttribute, hasFromBodyAttribute, hasWithOpenApi, assemblySuffix, compilationInfo.HasLoggerFactory);
301306

302307
source.DecrementIndent();
303308
source.AppendLine("}");
@@ -316,6 +321,7 @@ private static string GenerateEndpointCode(List<HandlerInfo> handlers, EndpointD
316321
private static void GenerateMapMediatorEndpointsCoreMethod(
317322
IndentedStringBuilder source,
318323
List<HandlerInfo> handlers,
324+
List<HandlerInfo> skippedHandlers,
319325
EndpointDefaultsInfo endpointDefaults,
320326
GeneratorConfiguration configuration,
321327
bool hasAsParametersAttribute,
@@ -409,7 +415,7 @@ private static void GenerateMapMediatorEndpointsCoreMethod(
409415
.ToList();
410416

411417
// Collect endpoint info for startup logging
412-
var endpointLogEntries = new List<(string HttpMethod, string FullRoute, string HandlerInfo)>();
418+
var endpointLogEntries = new List<(string HttpMethod, string FullRoute, string HandlerInfo, bool IsExplicitRoute)>();
413419

414420
foreach (var categoryGroup in handlersByCategory)
415421
{
@@ -493,12 +499,22 @@ private static void GenerateMapMediatorEndpointsCoreMethod(
493499
endpointLogEntries.Add((
494500
handler.Endpoint!.Value.HttpMethod,
495501
fullRoute,
496-
$"{handler.Identifier}.{handler.MethodName}({handler.MessageType.Identifier})"));
502+
$"{handler.Identifier}.{handler.MethodName}({handler.MessageType.Identifier})",
503+
handler.Endpoint!.Value.HasExplicitRoute));
497504
}
498505
}
499506

507+
// Collect skipped handler info for logging
508+
var skippedLogEntries = new List<(string HandlerInfo, string Reason)>();
509+
foreach (var handler in skippedHandlers)
510+
{
511+
skippedLogEntries.Add((
512+
$"{handler.Identifier}.{handler.MethodName}({handler.MessageType.Identifier})",
513+
handler.Endpoint!.Value.ExcludeReason!));
514+
}
515+
500516
// Emit endpoint logging block
501-
EmitEndpointLogging(source, endpointLogEntries, hasLoggerFactory);
517+
EmitEndpointLogging(source, endpointLogEntries, skippedLogEntries, hasLoggerFactory);
502518

503519
source.DecrementIndent();
504520
source.AppendLine("}");
@@ -509,14 +525,15 @@ private static void GenerateMapMediatorEndpointsCoreMethod(
509525
/// </summary>
510526
private static void EmitEndpointLogging(
511527
IndentedStringBuilder source,
512-
List<(string HttpMethod, string FullRoute, string HandlerInfo)> entries,
528+
List<(string HttpMethod, string FullRoute, string HandlerInfo, bool IsExplicitRoute)> entries,
529+
List<(string HandlerInfo, string Reason)> skippedEntries,
513530
bool hasLoggerFactory)
514531
{
515-
if (entries.Count == 0)
532+
if (entries.Count == 0 && skippedEntries.Count == 0)
516533
return;
517534

518-
var maxMethodLen = entries.Max(e => e.HttpMethod.Length);
519-
var maxRouteLen = entries.Max(e => e.FullRoute.Length);
535+
var maxMethodLen = entries.Count > 0 ? entries.Max(e => e.HttpMethod.Length) : 0;
536+
var maxRouteLen = entries.Count > 0 ? entries.Max(e => e.FullRoute.Length) : 0;
520537

521538
source.AppendLine();
522539
source.AppendLine("// Log mapped endpoints when requested");
@@ -540,11 +557,21 @@ private static void EmitEndpointLogging(
540557

541558
source.AppendLine($"writeLog(\"Foundatio.Mediator mapped {entries.Count} endpoint(s):\");");
542559

543-
foreach (var (httpMethod, fullRoute, handlerInfo) in entries)
560+
foreach (var (httpMethod, fullRoute, handlerInfo, isExplicitRoute) in entries)
544561
{
545562
var paddedMethod = httpMethod.PadRight(maxMethodLen);
546563
var paddedRoute = fullRoute.PadRight(maxRouteLen);
547-
source.AppendLine($"writeLog(\" {paddedMethod} {paddedRoute} \u2192 {handlerInfo}\");");
564+
var routeSource = isExplicitRoute ? "explicit" : "convention";
565+
source.AppendLine($"writeLog(\" {paddedMethod} {paddedRoute} \u2192 {handlerInfo} ({routeSource})\");");
566+
}
567+
568+
if (skippedEntries.Count > 0)
569+
{
570+
source.AppendLine($"writeLog(\"Foundatio.Mediator skipped {skippedEntries.Count} handler(s) from endpoint generation:\");");
571+
foreach (var (handlerInfo, reason) in skippedEntries)
572+
{
573+
source.AppendLine($"writeLog(\" SKIP {handlerInfo} ({reason})\");");
574+
}
548575
}
549576

550577
source.DecrementIndent();

src/Foundatio.Mediator/HandlerAnalyzer.cs

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -403,13 +403,14 @@ private static string[] ExtractTypeArrayArgument(AttributeData? attr, string arg
403403

404404
if (isExcluded)
405405
{
406-
return new EndpointInfo { GenerateEndpoint = false };
406+
return new EndpointInfo { GenerateEndpoint = false, ExcludeReason = "excluded by attribute" };
407407
}
408408

409409
// Auto-exclude events/notifications from endpoint generation
410-
if (ShouldExcludeAsEvent(classSymbol, messageType))
410+
var eventExcludeReason = GetEventExcludeReason(classSymbol, messageType);
411+
if (eventExcludeReason != null)
411412
{
412-
return new EndpointInfo { GenerateEndpoint = false };
413+
return new EndpointInfo { GenerateEndpoint = false, ExcludeReason = eventExcludeReason };
413414
}
414415

415416
// Extract category info
@@ -934,30 +935,42 @@ private static string RemoveVerbPrefix(string name)
934935
/// Determines if a handler/message should be excluded from endpoint generation because it's an event.
935936
/// </summary>
936937
private static bool ShouldExcludeAsEvent(INamedTypeSymbol classSymbol, INamedTypeSymbol messageType)
938+
=> GetEventExcludeReason(classSymbol, messageType) != null;
939+
940+
/// <summary>
941+
/// Returns a human-readable reason string if the handler/message should be excluded from endpoint generation
942+
/// because it's an event, or null if it should not be excluded.
943+
/// </summary>
944+
private static string? GetEventExcludeReason(INamedTypeSymbol classSymbol, INamedTypeSymbol messageType)
937945
{
938946
// 1. Check if message implements INotification (MediatR-style)
939-
if (messageType.AllInterfaces.Any(i =>
947+
var notificationInterface = messageType.AllInterfaces.FirstOrDefault(i =>
940948
i.Name == "INotification" ||
941949
i.ToDisplayString() == "Foundatio.Mediator.INotification" ||
942-
i.ToDisplayString() == "MediatR.INotification"))
950+
i.ToDisplayString() == "MediatR.INotification");
951+
if (notificationInterface != null)
943952
{
944-
return true;
953+
return $"implements {notificationInterface.Name}";
945954
}
946955

947956
// 2. Check if message implements common event marker interfaces
948-
if (messageType.AllInterfaces.Any(i =>
957+
var eventInterface = messageType.AllInterfaces.FirstOrDefault(i =>
949958
i.Name == "IEvent" ||
950959
i.Name == "IDomainEvent" ||
951-
i.Name == "IIntegrationEvent"))
960+
i.Name == "IIntegrationEvent");
961+
if (eventInterface != null)
952962
{
953-
return true;
963+
return $"implements {eventInterface.Name}";
954964
}
955965

956966
// 3. Check if handler class name ends with "EventHandler" or "NotificationHandler"
957-
if (classSymbol.Name.EndsWith("EventHandler") ||
958-
classSymbol.Name.EndsWith("NotificationHandler"))
967+
if (classSymbol.Name.EndsWith("EventHandler"))
959968
{
960-
return true;
969+
return "handler name ends with 'EventHandler'";
970+
}
971+
if (classSymbol.Name.EndsWith("NotificationHandler"))
972+
{
973+
return "handler name ends with 'NotificationHandler'";
961974
}
962975

963976
// 4. Check if message type name has common event suffixes
@@ -966,11 +979,11 @@ private static bool ShouldExcludeAsEvent(INamedTypeSymbol classSymbol, INamedTyp
966979
{
967980
if (messageName.EndsWith(suffix, StringComparison.Ordinal))
968981
{
969-
return true;
982+
return $"message name ends with '{suffix}'";
970983
}
971984
}
972985

973-
return false;
986+
return null;
974987
}
975988

976989
#endregion

src/Foundatio.Mediator/Models/EndpointInfo.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,12 @@ internal readonly record struct EndpointInfo
8484
/// </summary>
8585
public bool GenerateEndpoint { get; init; }
8686

87+
/// <summary>
88+
/// Reason this handler was excluded from endpoint generation, if applicable.
89+
/// Null when the handler is not excluded.
90+
/// </summary>
91+
public string? ExcludeReason { get; init; }
92+
8793
/// <summary>
8894
/// Whether this handler has an explicit [HandlerEndpoint] attribute on the method or class.
8995
/// Used by "Explicit" discovery mode to distinguish explicitly marked handlers.

tests/Foundatio.Mediator.Tests/BasicHandlerGenerationTests.EndpointGeneration.verified.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ public static partial class Tests_MediatorEndpoints
184184
{
185185
System.Action<string> writeLog = System.Console.WriteLine;
186186
writeLog("Foundatio.Mediator mapped 1 endpoint(s):");
187-
writeLog(" GET /api/widget/{id} → WidgetHandler.Handle(GetWidget)");
187+
writeLog(" GET /api/widget/{id} → WidgetHandler.Handle(GetWidget) (convention)");
188188
}
189189
else
190190
{

0 commit comments

Comments
 (0)