Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
</ItemGroup>
<ItemGroup>
<PackageVersion Include="Microsoft.AspNetCore.TestHost" Version="10.0.1" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="10.0.1" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="10.0.1" />
<PackageVersion Include="Microsoft.Testing.Extensions.CodeCoverage" Version="18.1.0" />
<PackageVersion Include="NetEvolve.Extensions.TUnit" Version="3.3.2" />
<PackageVersion Include="System.CommandLine" Version="2.0.1" />
Expand Down
1 change: 1 addition & 0 deletions ForgingBlazor.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<Folder Name="/src/">
<Project Path="src/NetEvolve.ForgingBlazor.Core/NetEvolve.ForgingBlazor.Core.csproj" Id="45d886e1-2940-4146-a1ab-56d3b4ac1225" />
<Project Path="src/NetEvolve.ForgingBlazor.Extensibility/NetEvolve.ForgingBlazor.Extensibility.csproj" Id="5462e69f-74ab-40a7-afed-f06eec6bc202" />
<Project Path="src/NetEvolve.ForgingBlazor.Logging/NetEvolve.ForgingBlazor.Logging.csproj" Id="e3af7dbf-e2ae-4369-aed2-2192645b01cf" />
<Project Path="src/NetEvolve.ForgingBlazor/NetEvolve.ForgingBlazor.csproj" Id="fc1da919-3fd1-4c03-812b-74c77ab30682" />
</Folder>
<Folder Name="/tests/">
Expand Down
115 changes: 115 additions & 0 deletions src/NetEvolve.ForgingBlazor.Logging/ApplicationBuilderExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
namespace NetEvolve.ForgingBlazor.Logging;

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NetEvolve.ForgingBlazor.Extensibility.Abstractions;

/// <summary>
/// Provides extension methods for <see cref="IApplicationBuilder"/> to configure and integrate logging services
/// into the Forging Blazor application pipeline.
/// </summary>
/// <remarks>
/// This class offers a fluent API for adding logging capabilities to applications built with the Forging Blazor framework.
/// It provides both pre-configured default logging setups and flexible custom configurations to meet various logging requirements.
/// </remarks>
public static class ApplicationBuilderExtensions
{
/// <summary>
/// Configures the application with default logging providers, including console and debug output.
/// </summary>
/// <param name="builder">
/// The <see cref="IApplicationBuilder"/> instance that represents the application being configured.
/// This parameter serves as the entry point for adding logging services to the application's service collection.
/// </param>
/// <returns>
/// The same <see cref="IApplicationBuilder"/> instance that was passed in, enabling method chaining
/// and fluent configuration of additional application features.
/// </returns>
/// <exception cref="ArgumentNullException">
/// Thrown when <paramref name="builder"/> is <see langword="null"/>.
/// </exception>
/// <remarks>
/// <para>
/// This method provides a convenient way to quickly set up logging with sensible defaults.
/// It automatically configures both console and debug logging providers, which are suitable
/// for most development and debugging scenarios.
/// </para>
/// <para>
/// The console provider outputs log messages to the standard console output, while the debug provider
/// writes to the debug output window in development environments and attached debuggers.
/// </para>
/// <para>
/// For production scenarios or when specific logging providers are required, consider using the
/// overload that accepts an <see cref="Action{ILoggingBuilder}"/> delegate for custom configuration.
/// </para>
/// </remarks>
/// <example>
/// <code>
/// var builder = ApplicationBuilder.Create();
/// builder.WithLogging();
/// </code>
/// </example>
/// <seealso cref="WithLogging(IApplicationBuilder, Action{ILoggingBuilder})"/>
public static IApplicationBuilder WithLogging(this IApplicationBuilder builder) =>
builder.WithLogging(configure => configure.AddConsole().AddDebug());

/// <summary>
/// Configures the application with custom logging providers using a flexible configuration delegate.
/// </summary>
/// <param name="builder">
/// The <see cref="IApplicationBuilder"/> instance that represents the application being configured.
/// This parameter serves as the entry point for adding logging services to the application's service collection.
/// </param>
/// <param name="configure">
/// An <see cref="Action{ILoggingBuilder}"/> delegate that provides fine-grained control over the logging configuration.
/// This delegate receives an <see cref="ILoggingBuilder"/> instance that can be used to add logging providers,
/// set minimum log levels, add filters, and perform other logging-related configurations.
/// </param>
/// <returns>
/// The same <see cref="IApplicationBuilder"/> instance that was passed in, enabling method chaining
/// and fluent configuration of additional application features.
/// </returns>
/// <exception cref="ArgumentNullException">
/// Thrown when <paramref name="builder"/> is <see langword="null"/>.
/// </exception>
/// <remarks>
/// <para>
/// This method offers maximum flexibility for configuring logging in the Forging Blazor application.
/// It allows developers to choose specific logging providers, configure log levels, add custom filters,
/// and integrate third-party logging frameworks according to their application's requirements.
/// </para>
/// <para>
/// The configuration delegate is executed immediately during the application builder's configuration phase,
/// ensuring that logging services are properly registered before any application components attempt to use them.
/// </para>
/// <para>
/// Common use cases include integrating structured logging providers (such as Serilog or NLog),
/// configuring cloud-based logging services (like Application Insights or AWS CloudWatch),
/// or setting up custom logging filters for performance optimization.
/// </para>
/// </remarks>
/// <example>
/// <para>Example with custom log levels and multiple providers:</para>
/// <code>
/// var builder = ApplicationBuilder.Create();
/// builder.WithLogging(logging =>
/// {
/// logging.SetMinimumLevel(LogLevel.Information);
/// logging.AddConsole();
/// logging.AddEventLog();
/// logging.AddFilter("Microsoft", LogLevel.Warning);
/// });
/// </code>
/// </example>
/// <seealso cref="WithLogging(IApplicationBuilder)"/>
/// <seealso cref="ILoggingBuilder"/>
/// <seealso cref="LoggingServiceCollectionExtensions.AddLogging(IServiceCollection, Action{ILoggingBuilder})"/>
public static IApplicationBuilder WithLogging(this IApplicationBuilder builder, Action<ILoggingBuilder> configure)
{
ArgumentNullException.ThrowIfNull(builder);

_ = builder.Services.AddLogging(configure);

return builder;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(_ProjectTargetFrameworks)</TargetFrameworks>
<PackageTags>$(PackageTags);logging</PackageTags>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" />
<PackageReference Include="YamlDotNet" />
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="NetEvolve.ForgingBlazor" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\NetEvolve.ForgingBlazor.Extensibility\NetEvolve.ForgingBlazor.Extensibility.csproj" />
</ItemGroup>
</Project>
11 changes: 11 additions & 0 deletions src/NetEvolve.ForgingBlazor/ForgingBlazorApplicationBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
namespace NetEvolve.ForgingBlazor;

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using NetEvolve.ForgingBlazor.Extensibility.Abstractions;

/// <summary>
Expand Down Expand Up @@ -98,6 +100,15 @@ public ForgingBlazorApplicationBuilder(string[] args)
/// <seealso cref="IApplication.RunAsync"/>
public IApplication Build()
{
// Check if logging is already registered
if (!Services.IsServiceTypeRegistered<ILoggerFactory>())
{
// Register NullLoggerFactory and NullLogger<T> as defaults
_ = Services
.AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance)
.AddSingleton(typeof(ILogger<>), typeof(NullLogger<>));
}

var serviceProvider = Services.BuildServiceProvider();

return new ForgingBlazorApplication(_args, serviceProvider);
Expand Down
12 changes: 12 additions & 0 deletions src/NetEvolve.ForgingBlazor/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace NetEvolve.ForgingBlazor;

using System.CommandLine;
using Microsoft.Extensions.DependencyInjection;
using NetEvolve.ForgingBlazor.Commands;
using NetEvolve.ForgingBlazor.Extensibility.Abstractions;

internal static class ServiceCollectionExtensions
{
internal static bool IsServiceTypeRegistered<T>(this IServiceCollection builder)
where T : class => builder.Any(x => x.ServiceType == typeof(T));
}
9 changes: 8 additions & 1 deletion tests/NetEvolve.ForgingBlazor.Tests.Console/Program.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
using NetEvolve.ForgingBlazor;

var builder = ForgingBlazorApplicationBuilder.CreateDefaultBuilder(args);
var arguments = args;

if (arguments.Length == 0)
{
arguments = ["build"];
}

var builder = ForgingBlazorApplicationBuilder.CreateDefaultBuilder(arguments);

var app = builder.Build();

Expand Down