├── .github
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ ├── build-and-publish.yml
│ ├── build-and-test.yml
│ └── snyk-test.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Examples
├── Example.Logging.Appsettings
│ ├── Controllers
│ │ └── WeatherForecastController.cs
│ ├── Example.Logging.Appsettings.csproj
│ ├── Program.cs
│ ├── Startup.cs
│ ├── WeatherForecast.cs
│ ├── appsettings.Development.json
│ └── appsettings.json
├── Example.Logging.AspNetCore
│ ├── Controllers
│ │ └── WeatherForecastController.cs
│ ├── Example.Logging.AspNetCore.csproj
│ ├── Program.cs
│ ├── Startup.cs
│ ├── WeatherForecast.cs
│ ├── appsettings.Development.json
│ └── appsettings.json
├── Example.Logging.Microsoft.Extensions
│ ├── Controllers
│ │ └── WeatherForecastController.cs
│ ├── Example.Logging.Microsoft.Extensions.csproj
│ ├── Program.cs
│ ├── Startup.cs
│ ├── WeatherForecast.cs
│ ├── appsettings.Development.json
│ └── appsettings.json
└── Example.Logging
│ ├── Controllers
│ └── WeatherForecastController.cs
│ ├── Example.Logging.csproj
│ ├── Program.cs
│ ├── Startup.cs
│ ├── WeatherForecast.cs
│ ├── appsettings.Development.json
│ └── appsettings.json
├── LICENSE.md
├── README.md
├── RockLib.Logging.All.sln
├── RockLib.Logging.AspNetCore
├── .editorconfig
├── CHANGELOG.md
├── CorrelationIdContextProvider.cs
├── CorrelationIdContextProviderOptions.cs
├── Directory.Build.props
├── ForwardedForContextProvider.cs
├── HeaderNames.cs
├── HttpContextExtensions.cs
├── HttpContextProvider.cs
├── InfoLogAttribute.cs
├── LogEntryExtensions.cs
├── LoggerBuilderExtensions.cs
├── LoggingActionFilterAttribute.cs
├── PathContextProvider.cs
├── ReferrerContextProvider.cs
├── RemoteIpAddressContextProvider.cs
├── RequestMethodContextProvider.cs
├── RockLib.Logging.AspNetCore.csproj
├── RockLib.Logging.AspNetCore.sln
├── RouteNotFoundMiddleware.cs
├── RouteNotFoundMiddlewareExtensions.cs
├── RouteNotFoundMiddlewareOptions.cs
└── UserAgentContextProvider.cs
├── RockLib.Logging.Microsoft.Extensions
├── .editorconfig
├── CHANGELOG.md
├── Directory.Build.props
├── RockLib.Logging.Microsoft.Extensions.csproj
├── RockLib.Logging.Microsoft.Extensions.sln
├── RockLibLogger.cs
├── RockLibLoggerOptions.cs
├── RockLibLoggerProvider.cs
├── RockLibLoggerProviderExtensions.cs
└── Shims.cs
├── RockLib.Logging.Moq
├── .editorconfig
├── CHANGELOG.md
├── Directory.Build.props
├── MockLogger.cs
├── MockLoggerExtensions.VerifyLog.cs
├── MockLoggerExtensions.cs
├── RockLib.Logging.Moq.csproj
└── RockLib.Logging.Moq.sln
├── RockLib.Logging
├── .editorconfig
├── AssemblyAttributes.cs
├── CHANGELOG.md
├── Cached.cs
├── DependencyInjection
│ ├── ILoggerBuilder.cs
│ ├── LoggerBuilder.cs
│ ├── LoggerBuilderExtensions.cs
│ ├── LoggerLookup.cs
│ ├── Options
│ │ ├── ConsoleLogProviderOptions.cs
│ │ ├── DebugLogProviderOptions.cs
│ │ ├── FileLogProviderOptions.cs
│ │ ├── FormattableLogProviderOptions.cs
│ │ ├── ILoggerOptions.cs
│ │ ├── LogProviderOptions.cs
│ │ ├── LoggerOptions.cs
│ │ └── RollingFileLogProviderOptions.cs
│ ├── ReloadingLogProvider.cs
│ ├── ReloadingLogger.cs
│ └── ServiceCollectionExtensions.cs
├── Diagnostics
│ └── LoggingTraceListener.cs
├── Directory.Build.props
├── Error.cs
├── Extensions
│ ├── EnabledExtensions.cs
│ ├── ErrorHandlerExtensions.cs
│ └── LoggingExtensions.cs
├── FormatToString
│ ├── BlockIndentExtension.cs
│ └── FormatToStringExtension.cs
├── IContextProvider.cs
├── IErrorHandler.cs
├── ILogger.cs
├── LogEntry.cs
├── LogLevel.cs
├── LogProcessing
│ ├── BackgroundLogProcessor.cs
│ ├── FireAndForgetLogProcessor.cs
│ ├── ILogProcessor.cs
│ ├── LogProcessor.cs
│ └── SynchronousLogProcessor.cs
├── LogProviders
│ ├── ConsoleLogProvider.cs
│ ├── DebugLogProvider.cs
│ ├── FileLogProvider.cs
│ ├── ILogFormatter.cs
│ ├── ILogLevelResolver.cs
│ ├── ILogProvider.cs
│ ├── RollingFileLogProvider.cs
│ ├── RolloverPeriod.cs
│ └── TemplateLogFormatter.cs
├── Logger.cs
├── LoggerFactory.cs
├── LoggerFactoryExtensions.cs
├── RockLib.Logging.csproj
├── RockLib.Logging.sln
└── SafeLogging
│ ├── NotSafeToLogAttribute.cs
│ ├── SafeLoggingExtensions.cs
│ ├── SafeToLogAttribute.cs
│ └── SanitizeEngine.cs
├── Tests
├── RockLib.Logging.AspNetCore.Tests
│ ├── .editorconfig
│ ├── ContextProviderTests.cs
│ ├── Directory.Build.props
│ ├── InfoLogAttributeTests.cs
│ ├── LogBuilderExtensionTests.cs
│ ├── LogEntryExtensionsTests.cs
│ ├── LoggingActionFilterAttributeTests.cs
│ ├── RockLib.Logging.AspNetCore.Tests.csproj
│ ├── RouteNotFoundMiddlewareExtensionsTests.cs
│ └── RouteNotFoundMiddlewareTests.cs
├── RockLib.Logging.Microsoft.Extensions.Tests
│ ├── .editorconfig
│ ├── Directory.Build.props
│ ├── RockLib.Logging.Microsoft.Extensions.Tests.csproj
│ ├── RockLibLoggerProviderExtensionsTests.cs
│ ├── RockLibLoggerProviderTests.cs
│ └── RockLibLoggerTests.cs
├── RockLib.Logging.Moq.Tests
│ ├── .editorconfig
│ ├── AssemblyInformation.cs
│ ├── Directory.Build.props
│ ├── MockLoggerExtensionsTests.cs
│ ├── MockLoggerTests.cs
│ └── RockLib.Logging.Moq.Tests.csproj
└── RockLib.Logging.Tests
│ ├── .editorconfig
│ ├── DelegateErrorHandler.cs
│ ├── DependencyInjection
│ ├── LoggerBuilderExtensionsTests.cs
│ ├── LoggerBuilderTests.cs
│ ├── Options
│ │ ├── FileLogProviderOptionsTests.cs
│ │ ├── FormattableLogProviderOptionsTests.cs
│ │ └── RollingFileLogProviderOptionsTests.cs
│ ├── ReloadingLogProviderTests.cs
│ ├── ReloadingLoggerTests.cs
│ ├── ServiceCollectionExtensionsTests.cs
│ ├── TestConfigurationProvider.cs
│ └── TestConfigurationSource.cs
│ ├── Diagnostics
│ └── LoggingTraceListenerTests.cs
│ ├── Directory.Build.props
│ ├── LogEntryExceptionTests.cs
│ ├── LogEntryTests.cs
│ ├── LogProcessingTests
│ ├── BackgroundLogProcessorTests.cs
│ ├── FakeLogProvider.cs
│ ├── FireAndForgetLogProcessorTests.cs
│ ├── LogProcessorTests.cs
│ └── SynchronousLogProcessorTests.cs
│ ├── LogProviderTests
│ ├── ConsoleLogProviderTests.cs
│ ├── DebugLogProviderTests.cs
│ ├── FileLogProviderTests.cs
│ └── RollingFileLogProviderTests.cs
│ ├── LoggerFactoryTests.cs
│ ├── LoggerTests.cs
│ ├── NullErrorHandler.cs
│ ├── RockLib.Logging.Tests.csproj
│ ├── SafeLogging
│ └── SanitizeEngineTests.cs
│ └── appsettings.json
├── docs
├── AspNetCore.md
├── ConsoleLogProvider.md
├── ContextProviders.md
├── DI.md
├── FileLogProvider.md
├── Formatting.md
├── GettingStarted.md
├── LogLevelResolver.md
├── LogProcessors.md
├── LogProviderErrors.md
├── LogProviders.md
├── Logger.md
├── LoggerFactory.md
├── LoggingStart.md
├── LoggingTraceListener.md
├── Microsoft.md
├── Moq.md
├── Reloading.md
├── RollingFileLogProvider.md
├── SafeLogging.md
└── Tracing.md
└── icon.png
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## Description
2 |
3 |
12 |
13 | ## Type of change:
14 |
15 | 1. Non-functional change (e.g. documentation changes, removing unused `using` directives, renaming local variables, etc)
16 | 2. Bug fix (non-breaking change that fixes an issue)
17 | 3. New feature (non-breaking change that adds functionality)
18 | 4. Breaking change (fix or feature that could cause existing functionality to not work as expected)
19 |
20 | ## Checklist:
21 |
22 | - Have you reviewed your own code? Do you understand every change?
23 | - Are you following the [contributing guidelines](../blob/main/CONTRIBUTING.md)?
24 | - Have you added tests that prove your fix is effective or that this feature works?
25 | - New and existing unit tests pass locally with these changes?
26 | - Have you made corresponding changes to the documentation?
27 | - Will this change require an update to an example project? (if so, create an issue and link to it)
28 |
29 | ---
30 |
31 | _[Reviewer guidelines](../blob/main/CONTRIBUTING.md#reviewing-changes)_
32 |
--------------------------------------------------------------------------------
/.github/workflows/build-and-publish.yml:
--------------------------------------------------------------------------------
1 | name: Build and Publish
2 |
3 | ####################################################################################################
4 | ## WORKFLOW TRIGGER
5 | ####################################################################################################
6 | on:
7 | # Workflow will run when a release is published.
8 | release:
9 | types: [ released, prereleased ]
10 |
11 | ####################################################################################################
12 | ## WORKFLOW JOBS
13 | ####################################################################################################
14 | jobs:
15 | # Calls the shared build-and-publish workflow.
16 | call_build_and_publish:
17 | name: Call build-and-publish workflow
18 | uses: RockLib/RockLib.Workflows/.github/workflows/build-and-publish.yml@main
19 | secrets: inherit
20 |
--------------------------------------------------------------------------------
/.github/workflows/build-and-test.yml:
--------------------------------------------------------------------------------
1 | name: Run Unit Test
2 |
3 | ####################################################################################################
4 | ## WORKFLOW TRIGGER
5 | ####################################################################################################
6 | on:
7 | # Workflow will run on pull requests to the main branch.
8 | pull_request:
9 | branches: [ main ]
10 |
11 | ####################################################################################################
12 | ## WORKFLOW JOBS
13 | ####################################################################################################
14 | jobs:
15 | # Calls the shared unit-test workflow.
16 | call_unit_test:
17 | name: Call unit-test workflow
18 | uses: RockLib/RockLib.Workflows/.github/workflows/unit-test.yml@main
19 | secrets: inherit
20 |
--------------------------------------------------------------------------------
/.github/workflows/snyk-test.yml:
--------------------------------------------------------------------------------
1 | name: Run Snyk Test
2 |
3 | ####################################################################################################
4 | ## WORKFLOW TRIGGER
5 | ####################################################################################################
6 | on:
7 | # Workflow will run after unit-test is completed.
8 | workflow_run:
9 | workflows: [ Run Unit Test ]
10 | types: [ completed ]
11 |
12 | ####################################################################################################
13 | ## WORKFLOW JOBS
14 | ####################################################################################################
15 | jobs:
16 | # Calls the shared snyk-test workflow.
17 | call_snyk_test:
18 | name: Call snyk-test workflow
19 | uses: RockLib/RockLib.Workflows/.github/workflows/snyk-test.yml@main
20 | secrets: inherit
21 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at RockLibSupport@quickenloans.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/Examples/Example.Logging.Appsettings/Controllers/WeatherForecastController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Microsoft.AspNetCore.Mvc;
5 | using RockLib.Logging;
6 | using RockLib.Logging.SafeLogging;
7 |
8 | namespace Example.Logging.Controllers
9 | {
10 | [ApiController]
11 | [Route("[controller]")]
12 | public class WeatherForecastController : ControllerBase
13 | {
14 | private static readonly string[] Summaries = new[]
15 | {
16 | "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
17 | };
18 |
19 | private readonly ILogger _logger;
20 |
21 | public WeatherForecastController(ILogger logger)
22 | {
23 | _logger = logger;
24 | }
25 |
26 | [HttpGet]
27 | public IEnumerable Get()
28 | {
29 | var rng = new Random();
30 | var forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast
31 | {
32 | Date = DateTime.Now.AddDays(index),
33 | TemperatureC = rng.Next(-20, 55),
34 | Summary = Summaries[rng.Next(Summaries.Length)]
35 | })
36 | .ToArray();
37 |
38 | _logger.InfoSanitized("GET /WeatherForecast", new { Result = forecasts });
39 |
40 | return forecasts;
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Examples/Example.Logging.Appsettings/Example.Logging.Appsettings.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net8.0
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Examples/Example.Logging.Appsettings/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Hosting;
2 | using Microsoft.Extensions.Hosting;
3 |
4 | namespace Example.Logging
5 | {
6 | public class Program
7 | {
8 | public static void Main(string[] args)
9 | {
10 | CreateHostBuilder(args).Build().Run();
11 | }
12 |
13 | public static IHostBuilder CreateHostBuilder(string[] args) =>
14 | Host.CreateDefaultBuilder(args)
15 | .ConfigureWebHostDefaults(webBuilder =>
16 | {
17 | webBuilder.UseStartup();
18 | });
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Examples/Example.Logging.Appsettings/Startup.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Builder;
2 | using Microsoft.AspNetCore.Hosting;
3 | using Microsoft.Extensions.Configuration;
4 | using Microsoft.Extensions.DependencyInjection;
5 | using Microsoft.Extensions.Hosting;
6 | using RockLib.Logging.DependencyInjection;
7 |
8 | namespace Example.Logging
9 | {
10 | public class Startup
11 | {
12 | public Startup(IConfiguration configuration)
13 | {
14 | Configuration = configuration;
15 | }
16 |
17 | public IConfiguration Configuration { get; }
18 |
19 | // This method gets called by the runtime. Use this method to add services to the container.
20 | public void ConfigureServices(IServiceCollection services)
21 | {
22 | services.AddControllers();
23 |
24 | services.AddLogger();
25 | }
26 |
27 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
28 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
29 | {
30 | if (env.IsDevelopment())
31 | {
32 | app.UseDeveloperExceptionPage();
33 | }
34 |
35 | app.UseHttpsRedirection();
36 |
37 | app.UseRouting();
38 |
39 | app.UseAuthorization();
40 |
41 | app.UseEndpoints(endpoints =>
42 | {
43 | endpoints.MapControllers();
44 | });
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Examples/Example.Logging.Appsettings/WeatherForecast.cs:
--------------------------------------------------------------------------------
1 | using RockLib.Logging.SafeLogging;
2 | using System;
3 |
4 | namespace Example.Logging
5 | {
6 | [SafeToLog]
7 | public class WeatherForecast
8 | {
9 | public DateTime Date { get; set; }
10 |
11 | public int TemperatureC { get; set; }
12 |
13 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
14 |
15 | public string Summary { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Examples/Example.Logging.Appsettings/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Warning",
5 | "Microsoft.Hosting.Lifetime": "Information"
6 | }
7 | },
8 | "Rocklib.Logging": {
9 | "Level": "Info"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Examples/Example.Logging.Appsettings/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "AllowedHosts": "*",
3 | "Rocklib.Logging": {
4 | "Level": "Warn",
5 | "LogProviders": { "Type": "RockLib.Logging.ConsoleLogProvider, RockLib.Logging" }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/Examples/Example.Logging.AspNetCore/Controllers/WeatherForecastController.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Mvc;
2 | using Microsoft.Extensions.Logging;
3 | using RockLib.Logging.AspNetCore;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 |
8 | namespace Example.Logging.AspNetCore.Controllers
9 | {
10 | [ApiController]
11 | [Route("[controller]")]
12 | public class WeatherForecastController : ControllerBase
13 | {
14 | private static readonly string[] Summaries = new[]
15 | {
16 | "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
17 | };
18 |
19 | private readonly ILogger _logger;
20 |
21 | public WeatherForecastController(ILogger logger)
22 | {
23 | _logger = logger;
24 | }
25 |
26 | [HttpGet]
27 | [InfoLog]
28 | public IEnumerable Get()
29 | {
30 | var rng = new Random();
31 | return Enumerable.Range(1, 5).Select(index => new WeatherForecast
32 | {
33 | Date = DateTime.Now.AddDays(index),
34 | TemperatureC = rng.Next(-20, 55),
35 | Summary = Summaries[rng.Next(Summaries.Length)]
36 | })
37 | .ToArray();
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Examples/Example.Logging.AspNetCore/Example.Logging.AspNetCore.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | NU1605
4 | net8.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Examples/Example.Logging.AspNetCore/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Hosting;
2 | using Microsoft.Extensions.Hosting;
3 |
4 | namespace Example.Logging.AspNetCore
5 | {
6 | public class Program
7 | {
8 | public static void Main(string[] args)
9 | {
10 | CreateHostBuilder(args).Build().Run();
11 | }
12 |
13 | public static IHostBuilder CreateHostBuilder(string[] args) =>
14 | Host.CreateDefaultBuilder(args)
15 | .ConfigureWebHostDefaults(webBuilder =>
16 | {
17 | webBuilder.UseStartup();
18 | });
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Examples/Example.Logging.AspNetCore/Startup.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Builder;
2 | using Microsoft.AspNetCore.Hosting;
3 | using Microsoft.Extensions.Configuration;
4 | using Microsoft.Extensions.DependencyInjection;
5 | using Microsoft.Extensions.Hosting;
6 | using RockLib.Logging.AspNetCore;
7 | using RockLib.Logging.DependencyInjection;
8 |
9 | namespace Example.Logging.AspNetCore
10 | {
11 | public class Startup
12 | {
13 | public Startup(IConfiguration configuration)
14 | {
15 | Configuration = configuration;
16 | }
17 |
18 | public IConfiguration Configuration { get; }
19 |
20 | // This method gets called by the runtime. Use this method to add services to the container.
21 | public void ConfigureServices(IServiceCollection services)
22 | {
23 | services.AddControllers();
24 |
25 | services.AddHttpContextAccessor();
26 |
27 | services.Configure(Configuration.GetSection("Logger"));
28 |
29 | services.AddLogger()
30 | .AddConsoleLogProvider()
31 | .AddHttpContextProvider();
32 | }
33 |
34 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
35 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
36 | {
37 | app.UseRouteNotFoundLogging();
38 |
39 | if (env.IsDevelopment())
40 | {
41 | app.UseDeveloperExceptionPage();
42 | }
43 |
44 | app.UseRouting();
45 |
46 | app.UseAuthorization();
47 |
48 | app.UseEndpoints(endpoints =>
49 | {
50 | endpoints.MapControllers();
51 | });
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/Examples/Example.Logging.AspNetCore/WeatherForecast.cs:
--------------------------------------------------------------------------------
1 | using RockLib.Logging.SafeLogging;
2 | using System;
3 |
4 | namespace Example.Logging.AspNetCore
5 | {
6 | [SafeToLog]
7 | public class WeatherForecast
8 | {
9 | public DateTime Date { get; set; }
10 |
11 | public int TemperatureC { get; set; }
12 |
13 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
14 |
15 | public string Summary { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Examples/Example.Logging.AspNetCore/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "Logger": {
10 | "Level": "Info"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Examples/Example.Logging.AspNetCore/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "AllowedHosts": "*",
10 | "Logger": {
11 | "Level": "Warn"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Examples/Example.Logging.Microsoft.Extensions/Controllers/WeatherForecastController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Microsoft.AspNetCore.Mvc;
5 | using Microsoft.Extensions.Logging;
6 | using Newtonsoft.Json;
7 |
8 | namespace Example.Logging.Microsoft.Extensions.Controllers
9 | {
10 | [ApiController]
11 | [Route("[controller]")]
12 | public class WeatherForecastController : ControllerBase
13 | {
14 | private static readonly string[] Summaries = new[]
15 | {
16 | "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
17 | };
18 |
19 | private readonly ILogger _logger;
20 |
21 | public WeatherForecastController(ILogger logger)
22 | {
23 | _logger = logger;
24 | }
25 |
26 | [HttpGet]
27 | public IEnumerable Get()
28 | {
29 | var rng = new Random();
30 | var forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast
31 | {
32 | Date = DateTime.Now.AddDays(index),
33 | TemperatureC = rng.Next(-20, 55),
34 | Summary = Summaries[rng.Next(Summaries.Length)]
35 | })
36 | .ToArray();
37 |
38 | _logger.LogInformation("Forecasts: {forecasts}", JsonConvert.SerializeObject(forecasts));
39 |
40 | return forecasts;
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Examples/Example.Logging.Microsoft.Extensions/Example.Logging.Microsoft.Extensions.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net8.0
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Examples/Example.Logging.Microsoft.Extensions/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Hosting;
2 | using Microsoft.Extensions.Hosting;
3 | using Microsoft.Extensions.Logging;
4 | using RockLib.Logging;
5 |
6 | namespace Example.Logging.Microsoft.Extensions
7 | {
8 | public class Program
9 | {
10 | public static void Main(string[] args)
11 | {
12 | CreateHostBuilder(args).Build().Run();
13 | }
14 |
15 | public static IHostBuilder CreateHostBuilder(string[] args) =>
16 | Host.CreateDefaultBuilder(args)
17 | .ConfigureWebHostDefaults(webBuilder =>
18 | {
19 | webBuilder.UseStartup();
20 | })
21 | .ConfigureLogging(logging =>
22 | {
23 | logging.ClearProviders();
24 | logging.AddRockLibLoggerProvider(options => options.IncludeScopes = true);
25 | });
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Examples/Example.Logging.Microsoft.Extensions/Startup.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Builder;
2 | using Microsoft.AspNetCore.Hosting;
3 | using Microsoft.Extensions.Configuration;
4 | using Microsoft.Extensions.DependencyInjection;
5 | using Microsoft.Extensions.Hosting;
6 | using RockLib.Logging;
7 | using RockLib.Logging.DependencyInjection;
8 |
9 | namespace Example.Logging.Microsoft.Extensions
10 | {
11 | public class Startup
12 | {
13 | public Startup(IConfiguration configuration)
14 | {
15 | Configuration = configuration;
16 | }
17 |
18 | public IConfiguration Configuration { get; }
19 |
20 | // This method gets called by the runtime. Use this method to add services to the container.
21 | public void ConfigureServices(IServiceCollection services)
22 | {
23 | services.AddControllers();
24 |
25 | services.Configure(Configuration.GetSection("Logger"));
26 |
27 | services.AddLogger(processingMode: Logger.ProcessingMode.Synchronous)
28 | .AddConsoleLogProvider();
29 | }
30 |
31 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
32 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
33 | {
34 | if (env.IsDevelopment())
35 | {
36 | app.UseDeveloperExceptionPage();
37 | }
38 |
39 | app.UseHttpsRedirection();
40 |
41 | app.UseRouting();
42 |
43 | app.UseAuthorization();
44 |
45 | app.UseEndpoints(endpoints =>
46 | {
47 | endpoints.MapControllers();
48 | });
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/Examples/Example.Logging.Microsoft.Extensions/WeatherForecast.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Example.Logging.Microsoft.Extensions
4 | {
5 | public class WeatherForecast
6 | {
7 | public DateTime Date { get; set; }
8 |
9 | public int TemperatureC { get; set; }
10 |
11 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
12 |
13 | public string Summary { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Examples/Example.Logging.Microsoft.Extensions/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "Logger": {
10 | "Level": "Info"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Examples/Example.Logging.Microsoft.Extensions/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "AllowedHosts": "*",
3 | "Logger": {
4 | "Level": "Warn"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Examples/Example.Logging/Controllers/WeatherForecastController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Microsoft.AspNetCore.Mvc;
5 | using RockLib.Logging;
6 | using RockLib.Logging.SafeLogging;
7 |
8 | namespace Example.Logging.Controllers
9 | {
10 | [ApiController]
11 | [Route("[controller]")]
12 | public class WeatherForecastController : ControllerBase
13 | {
14 | private static readonly string[] Summaries = new[]
15 | {
16 | "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
17 | };
18 |
19 | private readonly ILogger _logger;
20 |
21 | public WeatherForecastController(ILogger logger)
22 | {
23 | _logger = logger;
24 | }
25 |
26 | [HttpGet]
27 | public IEnumerable Get()
28 | {
29 | var rng = new Random();
30 | var forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast
31 | {
32 | Date = DateTime.Now.AddDays(index),
33 | TemperatureC = rng.Next(-20, 55),
34 | Summary = Summaries[rng.Next(Summaries.Length)]
35 | })
36 | .ToArray();
37 |
38 | _logger.InfoSanitized("GET /WeatherForecast", new { Result = forecasts });
39 |
40 | return forecasts;
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Examples/Example.Logging/Example.Logging.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net8.0
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Examples/Example.Logging/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Hosting;
2 | using Microsoft.Extensions.Hosting;
3 |
4 | namespace Example.Logging
5 | {
6 | public class Program
7 | {
8 | public static void Main(string[] args)
9 | {
10 | CreateHostBuilder(args).Build().Run();
11 | }
12 |
13 | public static IHostBuilder CreateHostBuilder(string[] args) =>
14 | Host.CreateDefaultBuilder(args)
15 | .ConfigureWebHostDefaults(webBuilder =>
16 | {
17 | webBuilder.UseStartup();
18 | });
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Examples/Example.Logging/Startup.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Builder;
2 | using Microsoft.AspNetCore.Hosting;
3 | using Microsoft.Extensions.Configuration;
4 | using Microsoft.Extensions.DependencyInjection;
5 | using Microsoft.Extensions.Hosting;
6 | using RockLib.Logging.DependencyInjection;
7 |
8 | namespace Example.Logging
9 | {
10 | public class Startup
11 | {
12 | public Startup(IConfiguration configuration)
13 | {
14 | Configuration = configuration;
15 | }
16 |
17 | public IConfiguration Configuration { get; }
18 |
19 | // This method gets called by the runtime. Use this method to add services to the container.
20 | public void ConfigureServices(IServiceCollection services)
21 | {
22 | services.AddControllers();
23 |
24 | services.Configure(Configuration.GetSection("Logger"));
25 |
26 | services.AddLogger()
27 | .AddConsoleLogProvider();
28 | }
29 |
30 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
31 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
32 | {
33 | if (env.IsDevelopment())
34 | {
35 | app.UseDeveloperExceptionPage();
36 | }
37 |
38 | app.UseHttpsRedirection();
39 |
40 | app.UseRouting();
41 |
42 | app.UseAuthorization();
43 |
44 | app.UseEndpoints(endpoints =>
45 | {
46 | endpoints.MapControllers();
47 | });
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Examples/Example.Logging/WeatherForecast.cs:
--------------------------------------------------------------------------------
1 | using RockLib.Logging.SafeLogging;
2 | using System;
3 |
4 | namespace Example.Logging
5 | {
6 | [SafeToLog]
7 | public class WeatherForecast
8 | {
9 | public DateTime Date { get; set; }
10 |
11 | public int TemperatureC { get; set; }
12 |
13 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
14 |
15 | public string Summary { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Examples/Example.Logging/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Warning",
5 | "Microsoft.Hosting.Lifetime": "Information"
6 | }
7 | },
8 | "Logger": {
9 | "Level": "Info"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Examples/Example.Logging/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "AllowedHosts": "*",
3 | "Logger": {
4 | "Level": "Warn"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright (C) 2018-2021 Rocket Mortgage
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # RockLib.Logging
2 |
3 | *A simple logging library.*
4 |
5 | ## Packages
6 |
7 | ### RockLib.Logging
8 | The main library.
9 |
10 | ### RockLib.Logging.AspNetCore
11 | RockLib.Logging for AspNetCore. Includes context providers and a logging action filter.
12 |
13 | ### RockLib.Logging.Microsoft.Extensions
14 | An implementation of Microsoft.Extensions.Logging that uses RockLib.Logging.
15 |
16 | ### RockLib.Logging.Moq
17 | Extensions for verifying logging operations with Moq.
18 |
19 | ---
20 |
21 | - [Getting started](docs/GettingStarted.md)
22 | - How to:
23 | - [Perform basic logging operations](docs/LoggingStart.md)
24 | - [Instantiate and configure a logger](docs/Logger.md)
25 | - [Add log extended properties "safely" (i.e. automatically remove PII)](docs/SafeLogging.md)
26 | - [Configure and use LoggerFactory](docs/LoggerFactory.md)
27 | - [Add RockLib logging to the Microsoft dependency injection system](docs/DI.md)
28 | - [Add RockLib logging to the Microsoft logging system](docs/Microsoft.md)
29 | - [Use log providers](docs/LogProviders.md)
30 | - [ConsoleLogProvider](docs/ConsoleLogProvider.md)
31 | - [FileLogProvider](docs/FileLogProvider.md)
32 | - [RollingFileLogProvider](docs/RollingFileLogProvider.md)
33 | - [Handle log provider errors](docs/LogProviderErrors.md)
34 | - [Use context providers](docs/ContextProviders.md)
35 | - [Use log processors / processing mode](docs/LogProcessors.md)
36 | - [Use log level resolver](docs/LogLevelResolver.md)
37 | - [Format logs](docs/Formatting.md)
38 | - [Enable tracing for troubleshooting](docs/Tracing.md)
39 | - [Use LoggingTraceListener to log trace messages](docs/LoggingTraceListener.md)
40 | - [Automatically capture HttpContext information in AspNetCore apps](docs/AspNetCore.md#context-providers)
41 | - [Automatically log AspNetCore controller actions](docs/AspNetCore.md#logging-action-filters)
42 | - [Automatically log 404 http responses](docs/AspNetCore.md#route-not-found-middleware)
43 | - [Change a logger's settings "on the fly" (in a running application)](docs/Reloading.md)
44 | - [Test logging in an application using RockLib.Logging.Moq](docs/Moq.md)
45 | - API Reference:
46 | - [RockLib.Logging](https://www.nuget.org/packages/RockLib.Logging)
47 | - [RockLib.Logging.AspNetCore](https://www.nuget.org/packages/RockLib.Logging.AspNetCore)
48 | - [RockLib.Logging.Microsoft.Extensions](https://www.nuget.org/packages/RockLib.Logging.Microsoft.Extensions)
49 | - [RockLib.Logging.Moq](https://www.nuget.org/packages/RockLib.Logging.Moq)
50 |
--------------------------------------------------------------------------------
/RockLib.Logging.AspNetCore/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 |
6 | [*.cs]
7 | # Styling
8 | indent_style = space
9 | indent_size = 4
10 | csharp_indent_case_contents = true
11 | csharp_indent_switch_labels = true
12 | csharp_new_line_before_catch = true
13 | csharp_new_line_before_else = true
14 | csharp_new_line_before_finally = true
15 | csharp_new_line_before_members_in_anonymous_types = false
16 | csharp_new_line_before_members_in_object_initializers = false
17 | csharp_new_line_before_open_brace = methods, control_blocks, types, properties, lambdas, accessors, object_collection_array_initializers
18 | csharp_new_line_between_query_expression_clauses = true
19 | csharp_prefer_braces = false:suggestion
20 | csharp_prefer_simple_default_expression = true:suggestion
21 | csharp_preferred_modifier_order = public,private,internal,protected,static,readonly,async,override,sealed:suggestion
22 | csharp_preserve_single_line_blocks = true
23 | csharp_preserve_single_line_statements = true
24 | csharp_space_after_cast = false
25 | csharp_space_after_colon_in_inheritance_clause = true
26 | csharp_space_after_keywords_in_control_flow_statements = true
27 | csharp_space_before_colon_in_inheritance_clause = true
28 | csharp_space_between_method_call_empty_parameter_list_parentheses = false
29 | csharp_space_between_method_call_name_and_opening_parenthesis = false
30 | csharp_space_between_method_call_parameter_list_parentheses = false
31 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
32 | csharp_space_between_method_declaration_parameter_list_parentheses = false
33 | csharp_style_pattern_matching_over_as_with_null_check = true:error
34 | csharp_style_expression_bodied_accessors = true:error
35 | csharp_style_expression_bodied_constructors = true:error
36 | csharp_style_expression_bodied_methods = true:error
37 | csharp_style_expression_bodied_properties = true:error
38 | csharp_style_namespace_declarations = file_scoped:error
39 | csharp_style_inlined_variable_declaration = true:error
40 | csharp_style_var_elsewhere = true:error
41 | csharp_style_var_for_built_in_types = true:error
42 | csharp_style_var_when_type_is_apparent = true:error
43 | dotnet_sort_system_directives_first = false
44 | dotnet_style_explicit_tuple_names = true:suggestion
45 | dotnet_style_object_initializer = true:suggestion
46 | csharp_style_pattern_local_over_anonymous_function = false:error
47 | dotnet_style_predefined_type_for_member_access = true:error
48 | dotnet_style_prefer_inferred_anonymous_type_member_names = false:error
49 | dotnet_style_prefer_inferred_tuple_names = true:error
50 | dotnet_style_predefined_type_for_locals_parameters_members = true:error
51 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:error
52 | dotnet_style_qualification_for_field = false:error
53 | dotnet_style_qualification_for_method = false:error
54 | dotnet_style_qualification_for_property = false:error
55 |
56 | # Analyzer Configuration
57 | # These are rules we want to either ignore or have set as suggestion or info
58 |
59 | # CA1014: Mark assemblies with CLSCompliant
60 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1014
61 | dotnet_diagnostic.CA1014.severity = none
62 |
63 | # CA1725: Parameter names should match base declaration
64 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1725
65 | dotnet_diagnostic.CA1725.severity = suggestion
66 |
67 | # CA2227: Collection properties should be read only
68 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2227
69 | dotnet_diagnostic.CA2227.severity = suggestion
70 |
--------------------------------------------------------------------------------
/RockLib.Logging.AspNetCore/CorrelationIdContextProvider.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Http;
2 | using Microsoft.Extensions.Options;
3 | using RockLib.DistributedTracing;
4 | using RockLib.DistributedTracing.AspNetCore;
5 | using System;
6 | using static RockLib.DistributedTracing.AspNetCore.HeaderNames;
7 |
8 | namespace RockLib.Logging.AspNetCore;
9 |
10 | ///
11 | /// An implementation of used to add a correlation id to a .
12 | ///
13 | public class CorrelationIdContextProvider : IContextProvider
14 | {
15 | ///
16 | /// Initializes a new instance of the class.
17 | ///
18 | /// The http context accessor used to retreive the correlation id.
19 | /// Options containing the name of the correlation id header.
20 | public CorrelationIdContextProvider(IHttpContextAccessor httpContextAccessor,
21 | IOptionsMonitor? options = null)
22 | : this(httpContextAccessor?.HttpContext?.GetCorrelationIdAccessor(options?.CurrentValue.CorrelationIdHeader ?? CorrelationId))
23 | {
24 | }
25 |
26 | ///
27 | /// Initializes a new instance of the class.
28 | ///
29 | /// The accessor used to retreive the correlation id.
30 | public CorrelationIdContextProvider(ICorrelationIdAccessor? accessor) => Accessor = accessor;
31 |
32 | ///
33 | /// Gets the accessor used to retreive the correlation id.
34 | ///
35 | public ICorrelationIdAccessor? Accessor { get; }
36 |
37 | ///
38 | /// Add custom context to the object.
39 | ///
40 | /// The log entry to add custom context to.
41 | public void AddContext(LogEntry logEntry)
42 | {
43 | #if NET6_0_OR_GREATER
44 | ArgumentNullException.ThrowIfNull(logEntry);
45 | #else
46 | if (logEntry is null) { throw new ArgumentNullException(nameof(logEntry)); }
47 | #endif
48 | logEntry.CorrelationId ??= Accessor?.CorrelationId;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/RockLib.Logging.AspNetCore/CorrelationIdContextProviderOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | using static RockLib.DistributedTracing.AspNetCore.HeaderNames;
4 |
5 | namespace RockLib.Logging.AspNetCore;
6 |
7 | ///
8 | /// Defines options for .
9 | ///
10 | public class CorrelationIdContextProviderOptions
11 | {
12 | private string _correlationIdHeader = CorrelationId;
13 |
14 | ///
15 | /// The name of the correlation id header.
16 | ///
17 | public string CorrelationIdHeader
18 | {
19 | get => _correlationIdHeader;
20 | set
21 | {
22 | if (string.IsNullOrWhiteSpace(value))
23 | {
24 | throw new ArgumentNullException(nameof(value));
25 | }
26 |
27 | _correlationIdHeader = value;
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/RockLib.Logging.AspNetCore/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 | all
7 | Rocket Mortgage
8 | Copyright 2025 (c) Rocket Mortgage. All rights reserved.
9 | latest
10 | enable
11 | net48;net8.0
12 | NU1603,NU1701
13 | true
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
--------------------------------------------------------------------------------
/RockLib.Logging.AspNetCore/ForwardedForContextProvider.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Http;
2 | using Microsoft.Extensions.Primitives;
3 |
4 | namespace RockLib.Logging.AspNetCore;
5 |
6 | ///
7 | /// An implementation of used to add the ForwardedFor value to a .
8 | ///
9 | public class ForwardedForContextProvider : IContextProvider
10 | {
11 | ///
12 | /// Initializes a new instance of the class.
13 | ///
14 | /// The http context accessor used to retreive the ForwardedFor value.
15 | public ForwardedForContextProvider(IHttpContextAccessor httpContextAccessor)
16 | : this(httpContextAccessor?.HttpContext?.GetForwardedFor() ?? default)
17 | {
18 | }
19 |
20 | ///
21 | /// Initializes a new instance of the class.
22 | ///
23 | /// The FowardedFor value.
24 | public ForwardedForContextProvider(StringValues forwardedFor) => ForwardedFor = forwardedFor;
25 |
26 | ///
27 | /// Gets the ForwardedFor value.
28 | ///
29 | public StringValues ForwardedFor { get; }
30 |
31 | ///
32 | /// Add custom context to the object.
33 | ///
34 | /// The log entry to add custom context to.
35 | public void AddContext(LogEntry logEntry) => logEntry.SetForwardedFor(ForwardedFor);
36 | }
37 |
--------------------------------------------------------------------------------
/RockLib.Logging.AspNetCore/HeaderNames.cs:
--------------------------------------------------------------------------------
1 | namespace RockLib.Logging.AspNetCore;
2 |
3 | ///
4 | /// A class for http header names.
5 | ///
6 | public static class HeaderNames
7 | {
8 | ///
9 | /// The forwarded for http header name.
10 | ///
11 | public const string ForwardedFor = "X-Forwarded-For";
12 | }
13 |
--------------------------------------------------------------------------------
/RockLib.Logging.AspNetCore/HttpContextExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Http;
2 | using Microsoft.AspNetCore.Http.Features;
3 | using Microsoft.AspNetCore.Http.Headers;
4 | using Microsoft.AspNetCore.Routing;
5 | using Microsoft.Extensions.Primitives;
6 | using System;
7 | using System.Net;
8 |
9 | using static Microsoft.Net.Http.Headers.HeaderNames;
10 |
11 | namespace RockLib.Logging.AspNetCore;
12 |
13 | using static HeaderNames;
14 |
15 | ///
16 | /// Extensions for .
17 | ///
18 | public static class HttpContextExtensions
19 | {
20 | ///
21 | /// Gets the http request method from an .
22 | ///
23 | /// The http context.
24 | /// The http request method.
25 | public static string? GetMethod(this HttpContext httpContext) =>
26 | httpContext?.Request?.Method;
27 |
28 | ///
29 | /// Gets the http request path from an .
30 | ///
31 | /// The http context.
32 | /// The http request path.
33 | public static string? GetPath(this HttpContext httpContext)
34 | {
35 | if (httpContext?.Features[typeof(IEndpointFeature)] is IEndpointFeature endpointFeature
36 | && endpointFeature.Endpoint is RouteEndpoint endpoint)
37 | {
38 | return endpoint.RoutePattern.RawText;
39 | }
40 |
41 | return httpContext?.Request?.Path;
42 | }
43 |
44 | ///
45 | /// Gets the http user agent header from an .
46 | ///
47 | /// The http context.
48 | /// The http user agent header.
49 | public static StringValues GetUserAgent(this HttpContext httpContext) =>
50 | httpContext.GetHeaderValue(UserAgent);
51 |
52 | ///
53 | /// Gets the http referrer header from an .
54 | ///
55 | /// The http context.
56 | /// The http referrer header.
57 | public static Uri? GetReferrer(this HttpContext httpContext) =>
58 | httpContext?.Request?.GetTypedHeaders() is RequestHeaders headers
59 | ? headers.Referer
60 | : null;
61 |
62 | ///
63 | /// Gets the connection remote ip address from an .
64 | ///
65 | /// The http context.
66 | /// The remote ip address.
67 | public static IPAddress? GetRemoteIpAddress(this HttpContext httpContext) =>
68 | httpContext?.Connection?.RemoteIpAddress;
69 |
70 | ///
71 | /// Gets the forwarded for header from an .
72 | ///
73 | /// The http context.
74 | /// The forwarded for header.
75 | public static StringValues GetForwardedFor(this HttpContext httpContext) =>
76 | httpContext.GetHeaderValue(ForwardedFor);
77 |
78 | private static StringValues GetHeaderValue(this HttpContext httpContext, string headerName) =>
79 | httpContext?.Request?.Headers is IHeaderDictionary headers
80 | && headers.TryGetValue(headerName, out var headerValue)
81 | ? headerValue
82 | : default;
83 | }
84 |
--------------------------------------------------------------------------------
/RockLib.Logging.AspNetCore/HttpContextProvider.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Http;
2 | using Microsoft.Extensions.Options;
3 | using System.Collections.Generic;
4 |
5 | namespace RockLib.Logging.AspNetCore;
6 |
7 | ///
8 | /// An implementation of used to add various http context values to a .
9 | ///
10 | public class HttpContextProvider : IContextProvider
11 | {
12 | ///
13 | /// Initializes a new instance of the class.
14 | ///
15 | /// The http context accessor used to retreive the various context values.
16 | /// Options for the .
17 | public HttpContextProvider(IHttpContextAccessor httpContextAccessor,
18 | IOptionsMonitor? correlationIdOptions = null)
19 | : this(new IContextProvider[]
20 | {
21 | new RequestMethodContextProvider(httpContextAccessor),
22 | new PathContextProvider(httpContextAccessor),
23 | new UserAgentContextProvider(httpContextAccessor),
24 | new ReferrerContextProvider(httpContextAccessor),
25 | new RemoteIpAddressContextProvider(httpContextAccessor),
26 | new ForwardedForContextProvider(httpContextAccessor),
27 | new CorrelationIdContextProvider(httpContextAccessor, correlationIdOptions)
28 | })
29 | {
30 | }
31 |
32 | ///
33 | /// Initializes a new instance of the class.
34 | ///
35 | /// The set of providers used to retreive the various context values.
36 | protected HttpContextProvider(IReadOnlyCollection contextProviders) => ContextProviders = contextProviders;
37 |
38 | ///
39 | /// Gets the set of providers used to retrieve the context values.
40 | ///
41 | public IReadOnlyCollection ContextProviders { get; }
42 |
43 | ///
44 | /// Add custom context to the object.
45 | ///
46 | /// The log entry to add custom context to.
47 | public void AddContext(LogEntry logEntry)
48 | {
49 | foreach (var contextProvider in ContextProviders)
50 | {
51 | contextProvider.AddContext(logEntry);
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/RockLib.Logging.AspNetCore/InfoLogAttribute.cs:
--------------------------------------------------------------------------------
1 | namespace RockLib.Logging.AspNetCore;
2 |
3 | ///
4 | /// An action filter that records an info log each time an action is executed.
5 | ///
6 | public sealed class InfoLogAttribute : LoggingActionFilterAttribute
7 | {
8 | ///
9 | /// Initializes a new instance of the class.
10 | ///
11 | ///
12 | /// The message format string. The action name is used as the {0} placeholder when
13 | /// formatting the message.
14 | ///
15 | /// The name of the logger.
16 | public InfoLogAttribute(string? messageFormat = DefaultMessageFormat, string? loggerName = Logger.DefaultName)
17 | : base(messageFormat, loggerName, LogLevel.Info)
18 | {
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/RockLib.Logging.AspNetCore/LoggerBuilderExtensions.cs:
--------------------------------------------------------------------------------
1 | using RockLib.Logging.DependencyInjection;
2 |
3 | namespace RockLib.Logging.AspNetCore;
4 |
5 | ///
6 | /// Extensions for .
7 | ///
8 | public static class LoggerBuilderExtensions
9 | {
10 | ///
11 | /// Adds the to the logger builder.
12 | ///
13 | /// The logger builder.
14 | /// The same logger builder.
15 | public static ILoggerBuilder AddHttpContextProvider(this ILoggerBuilder builder) =>
16 | builder.AddContextProvider();
17 | }
18 |
--------------------------------------------------------------------------------
/RockLib.Logging.AspNetCore/PathContextProvider.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Http;
2 |
3 | namespace RockLib.Logging.AspNetCore;
4 |
5 | ///
6 | /// An implementation of used to add the path value to a .
7 | ///
8 | public class PathContextProvider : IContextProvider
9 | {
10 | ///
11 | /// Initializes a new instance of the class.
12 | ///
13 | /// The http context accessor used to retreive the path value.
14 | public PathContextProvider(IHttpContextAccessor httpContextAccessor)
15 | : this(httpContextAccessor?.HttpContext?.GetPath())
16 | {
17 | }
18 |
19 | ///
20 | /// Initializes a new instance of the class.
21 | ///
22 | /// The path value.
23 | public PathContextProvider(string? path) => Path = path;
24 |
25 | ///
26 | /// Gets the path value.
27 | ///
28 | public string? Path { get; }
29 |
30 | ///
31 | /// Add custom context to the object.
32 | ///
33 | /// The log entry to add custom context to.
34 | public void AddContext(LogEntry logEntry) => logEntry.SetPath(Path);
35 | }
36 |
--------------------------------------------------------------------------------
/RockLib.Logging.AspNetCore/ReferrerContextProvider.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Http;
2 | using System;
3 |
4 | namespace RockLib.Logging.AspNetCore;
5 |
6 | ///
7 | /// An implementation of used to add the referrer value to a .
8 | ///
9 | public class ReferrerContextProvider : IContextProvider
10 | {
11 | ///
12 | /// Initializes a new instance of the class.
13 | ///
14 | /// The http context accessor used to retreive the referrer value.
15 | public ReferrerContextProvider(IHttpContextAccessor httpContextAccessor)
16 | : this(httpContextAccessor?.HttpContext?.GetReferrer())
17 | {
18 | }
19 |
20 | ///
21 | /// Initializes a new instance of the class.
22 | ///
23 | /// The referrer value.
24 | public ReferrerContextProvider(string? referrer)
25 | : this(referrer is null ? null : new Uri(referrer, UriKind.RelativeOrAbsolute))
26 | {
27 | }
28 |
29 | ///
30 | /// Initializes a new instance of the class.
31 | ///
32 | /// The referrer value.
33 | public ReferrerContextProvider(Uri? referrer) => Referrer = referrer;
34 |
35 | ///
36 | /// Gets the referrer value.
37 | ///
38 | public Uri? Referrer { get; }
39 |
40 | ///
41 | /// Add custom context to the object.
42 | ///
43 | /// The log entry to add custom context to.
44 | public void AddContext(LogEntry logEntry) => logEntry.SetReferrer(Referrer);
45 | }
46 |
--------------------------------------------------------------------------------
/RockLib.Logging.AspNetCore/RemoteIpAddressContextProvider.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Http;
2 | using System.Net;
3 |
4 | namespace RockLib.Logging.AspNetCore;
5 |
6 | ///
7 | /// An implementation of used to add the remote ip address value to a .
8 | ///
9 | public class RemoteIpAddressContextProvider : IContextProvider
10 | {
11 | ///
12 | /// Initializes a new instance of the class.
13 | ///
14 | /// The http context accessor used to retreive the remote ip address value.
15 | public RemoteIpAddressContextProvider(IHttpContextAccessor httpContextAccessor)
16 | : this(httpContextAccessor?.HttpContext?.GetRemoteIpAddress())
17 | {
18 | }
19 |
20 | ///
21 | /// Initializes a new instance of the class.
22 | ///
23 | /// The remote ip address value.
24 | public RemoteIpAddressContextProvider(string remoteIpAddress)
25 | : this(remoteIpAddress is null ? null : IPAddress.Parse(remoteIpAddress))
26 | {
27 | }
28 |
29 | ///
30 | /// Initializes a new instance of the class.
31 | ///
32 | /// The remote ip address value.
33 | public RemoteIpAddressContextProvider(IPAddress? remoteIpAddress) => RemoteIpAddress = remoteIpAddress;
34 |
35 | ///
36 | /// Gets the remote ip address value.
37 | ///
38 | public IPAddress? RemoteIpAddress { get; }
39 |
40 | ///
41 | /// Add custom context to the object.
42 | ///
43 | /// The log entry to add custom context to.
44 | public void AddContext(LogEntry logEntry) => logEntry.SetRemoteIpAddress(RemoteIpAddress);
45 | }
46 |
--------------------------------------------------------------------------------
/RockLib.Logging.AspNetCore/RequestMethodContextProvider.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Http;
2 |
3 | namespace RockLib.Logging.AspNetCore;
4 |
5 | ///
6 | /// An implementation of used to add the request method value to a .
7 | ///
8 | public class RequestMethodContextProvider : IContextProvider
9 | {
10 | ///
11 | /// Initializes a new instance of the class.
12 | ///
13 | /// The http context accessor used to retreive the request method value.
14 | public RequestMethodContextProvider(IHttpContextAccessor httpContextAccessor)
15 | : this(httpContextAccessor?.HttpContext?.GetMethod())
16 | {
17 | }
18 |
19 | ///
20 | /// Initializes a new instance of the class.
21 | ///
22 | /// The request method value.
23 | public RequestMethodContextProvider(string? requestMethod) => RequestMethod = requestMethod;
24 |
25 | ///
26 | /// Gets the request method value.
27 | ///
28 | public string? RequestMethod { get; }
29 |
30 | ///
31 | /// Add custom context to the object.
32 | ///
33 | /// The log entry to add custom context to.
34 | public void AddContext(LogEntry logEntry) => logEntry.SetRequestMethod(RequestMethod);
35 | }
36 |
--------------------------------------------------------------------------------
/RockLib.Logging.AspNetCore/RockLib.Logging.AspNetCore.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Embedded
4 | RockLib.Logging for AspNetCore. Includes context providers and a logging action filter.
5 | True
6 | True
7 | NU1605,NU1608
8 | icon.png
9 | RockLib.Logging.AspNetCore
10 | LICENSE.md
11 | https://github.com/RockLib/RockLib.Logging
12 | false
13 | A changelog is available at https://github.com/RockLib/RockLib.Logging/blob/main/RockLib.Logging.AspNetCore/CHANGELOG.md.
14 | RockLib Logging Logger AspNetCore HttpContext ActionFilter
15 | 6.0.0
16 | True
17 | 6.0.0
18 |
19 |
20 | bin\$(Configuration)\$(TargetFramework)\$(PackageId).xml
21 |
22 |
23 | true
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/RockLib.Logging.AspNetCore/RockLib.Logging.AspNetCore.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27428.2037
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RockLib.Logging.AspNetCore", "RockLib.Logging.AspNetCore.csproj", "{A07A8BEF-6786-44A5-8E2D-60A14FFBE772}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RockLib.Logging.AspNetCore.Tests", "..\Tests\RockLib.Logging.AspNetCore.Tests\RockLib.Logging.AspNetCore.Tests.csproj", "{1E143C4D-C527-4038-8CD3-5F26EC7E644B}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {A07A8BEF-6786-44A5-8E2D-60A14FFBE772}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {A07A8BEF-6786-44A5-8E2D-60A14FFBE772}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {A07A8BEF-6786-44A5-8E2D-60A14FFBE772}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {A07A8BEF-6786-44A5-8E2D-60A14FFBE772}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {1E143C4D-C527-4038-8CD3-5F26EC7E644B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {1E143C4D-C527-4038-8CD3-5F26EC7E644B}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {1E143C4D-C527-4038-8CD3-5F26EC7E644B}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {1E143C4D-C527-4038-8CD3-5F26EC7E644B}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {806D89B7-78D1-4038-944B-AFFE3C7A0F00}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/RockLib.Logging.AspNetCore/RouteNotFoundMiddlewareExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Builder;
2 |
3 | namespace RockLib.Logging.AspNetCore;
4 |
5 | ///
6 | /// Provides extension methods for the class.
7 | ///
8 | public static class RouteNotFoundMiddlewareExtensions
9 | {
10 | ///
11 | /// Adds to the application's request pipeline.
12 | ///
13 | /// The .
14 | /// The .
15 | public static IApplicationBuilder UseRouteNotFoundLogging(this IApplicationBuilder builder) =>
16 | builder.UseMiddleware();
17 | }
18 |
--------------------------------------------------------------------------------
/RockLib.Logging.AspNetCore/RouteNotFoundMiddlewareOptions.cs:
--------------------------------------------------------------------------------
1 | namespace RockLib.Logging.AspNetCore;
2 |
3 | ///
4 | /// Defines the options for the class.
5 | ///
6 | public class RouteNotFoundMiddlewareOptions
7 | {
8 | ///
9 | /// Gets or sets the name of the logger.
10 | ///
11 | public string? LoggerName { get; set; }
12 |
13 | ///
14 | /// Gets or sets the level used when sending logs.
15 | ///
16 | public LogLevel LogLevel { get; set; }
17 |
18 | ///
19 | /// Gets or sets the message used when sending logs.
20 | ///
21 | public string? LogMessage { get; set; }
22 | }
--------------------------------------------------------------------------------
/RockLib.Logging.AspNetCore/UserAgentContextProvider.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Http;
2 |
3 | namespace RockLib.Logging.AspNetCore;
4 |
5 | ///
6 | /// An implementation of used to add the UserAgent value to a .
7 | ///
8 | public class UserAgentContextProvider : IContextProvider
9 | {
10 | ///
11 | /// Initializes a new instance of the class.
12 | ///
13 | /// The http context accessor used to retreive the UserAgent value.
14 | public UserAgentContextProvider(IHttpContextAccessor httpContextAccessor)
15 | : this(httpContextAccessor?.HttpContext?.GetUserAgent())
16 | {
17 | }
18 |
19 | ///
20 | /// Initializes a new instance of the class.
21 | ///
22 | /// The UserAgent value.
23 | public UserAgentContextProvider(string? userAgent) => UserAgent = userAgent;
24 |
25 | ///
26 | /// Gets the UserAgent value.
27 | ///
28 | public string? UserAgent { get; }
29 |
30 | ///
31 | /// Add custom context to the object.
32 | ///
33 | /// The log entry to add custom context to.
34 | public void AddContext(LogEntry logEntry) => logEntry.SetUserAgent(UserAgent);
35 | }
36 |
--------------------------------------------------------------------------------
/RockLib.Logging.Microsoft.Extensions/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 |
6 | [*.cs]
7 | # Styling
8 | indent_style = space
9 | indent_size = 4
10 | csharp_indent_case_contents = true
11 | csharp_indent_switch_labels = true
12 | csharp_new_line_before_catch = true
13 | csharp_new_line_before_else = true
14 | csharp_new_line_before_finally = true
15 | csharp_new_line_before_members_in_anonymous_types = false
16 | csharp_new_line_before_members_in_object_initializers = false
17 | csharp_new_line_before_open_brace = methods, control_blocks, types, properties, lambdas, accessors, object_collection_array_initializers
18 | csharp_new_line_between_query_expression_clauses = true
19 | csharp_prefer_braces = false:suggestion
20 | csharp_prefer_simple_default_expression = true:suggestion
21 | csharp_preferred_modifier_order = public,private,internal,protected,static,readonly,async,override,sealed:suggestion
22 | csharp_preserve_single_line_blocks = true
23 | csharp_preserve_single_line_statements = true
24 | csharp_space_after_cast = false
25 | csharp_space_after_colon_in_inheritance_clause = true
26 | csharp_space_after_keywords_in_control_flow_statements = true
27 | csharp_space_before_colon_in_inheritance_clause = true
28 | csharp_space_between_method_call_empty_parameter_list_parentheses = false
29 | csharp_space_between_method_call_name_and_opening_parenthesis = false
30 | csharp_space_between_method_call_parameter_list_parentheses = false
31 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
32 | csharp_space_between_method_declaration_parameter_list_parentheses = false
33 | csharp_style_pattern_matching_over_as_with_null_check = true:error
34 | csharp_style_expression_bodied_accessors = true:error
35 | csharp_style_expression_bodied_constructors = true:error
36 | csharp_style_expression_bodied_methods = true:error
37 | csharp_style_expression_bodied_properties = true:error
38 | csharp_style_namespace_declarations = file_scoped:error
39 | csharp_style_inlined_variable_declaration = true:error
40 | csharp_style_var_elsewhere = true:error
41 | csharp_style_var_for_built_in_types = true:error
42 | csharp_style_var_when_type_is_apparent = true:error
43 | dotnet_sort_system_directives_first = false
44 | dotnet_style_explicit_tuple_names = true:suggestion
45 | dotnet_style_object_initializer = true:suggestion
46 | csharp_style_pattern_local_over_anonymous_function = false:error
47 | dotnet_style_predefined_type_for_member_access = true:error
48 | dotnet_style_prefer_inferred_anonymous_type_member_names = false:error
49 | dotnet_style_prefer_inferred_tuple_names = true:error
50 | dotnet_style_predefined_type_for_locals_parameters_members = true:error
51 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:error
52 | dotnet_style_qualification_for_field = false:error
53 | dotnet_style_qualification_for_method = false:error
54 | dotnet_style_qualification_for_property = false:error
55 |
56 | # Analyzer Configuration
57 | # These are rules we want to either ignore or have set as suggestion or info
58 |
59 | # CA1014: Mark assemblies with CLSCompliant
60 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1014
61 | dotnet_diagnostic.CA1014.severity = none
62 |
63 | # CA1725: Parameter names should match base declaration
64 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1725
65 | dotnet_diagnostic.CA1725.severity = suggestion
66 |
67 | # CA2227: Collection properties should be read only
68 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2227
69 | dotnet_diagnostic.CA2227.severity = suggestion
--------------------------------------------------------------------------------
/RockLib.Logging.Microsoft.Extensions/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # RockLib.Logging.Microsoft.Extensions Changelog
2 |
3 | All notable changes to this project will be documented in this file.
4 |
5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 |
8 | ## 4.0.0 - 2025-01-31
9 |
10 | #### Changed
11 | - Finalized 4.0.0 release.
12 |
13 | ## 4.0.0-alpha.1 - 2025-01-30
14 |
15 | #### Changed
16 | - Removed .NET 6 as a target framework
17 | - Updated the following package references
18 | - RockLib.Logging - 6.0.0-alpha.1
19 |
20 | ## 3.1.2 - 2024-10-22
21 |
22 | #### Changed
23 | - RockLib.Logging 5.1.1 -> RockLib.Logging 5.1.2.
24 | - Microsoft.Extensions.Logging 8.0.0 -> Microsoft.Extensions.Logging 8.0.1.
25 |
26 | ## 3.1.1 - 2024-07-16
27 |
28 | #### Changed
29 | - RockLib.Logging.5.1.0 -> RockLib.Logging.5.1.1.
30 |
31 | ## 3.1.0 - 2024-02-26
32 |
33 | #### Changed
34 | - Finalized 3.1.0 release.
35 |
36 | ## 3.0.0 - 2024-02-15
37 |
38 | #### Changed
39 | - Finalized 3.0.0 release.
40 |
41 | ## 3.0.0-alpha.1 - 2024-01-31
42 |
43 | #### Changed
44 | - Supported targets: net6.0, net8.0, and net48 - netcoreapp3.1 was removed.
45 | - Updated package references.
46 |
47 | ## 2.0.1 - 2023-02-21
48 |
49 | #### Changed
50 | - Updated RockLib.Logging package reference to `4.0.2`
51 |
52 | ## 2.0.0 - 2022-11-15
53 |
54 | #### Added
55 | - Added `.editorconfig` and `Directory.Build.props` files to ensure consistency.
56 |
57 | #### Changed
58 | - Supported targets: net6.0, netcoreapp3.1, and net48.
59 | - As the package now uses nullable reference types, some method parameters now specify if they can accept nullable values.
60 |
61 | ## 2.0.0-alpha.1 - 2022-11-10
62 |
63 | #### Added
64 | - Added `.editorconfig` and `Directory.Build.props` files to ensure consistency.
65 |
66 | #### Changed
67 | - Supported targets: net6.0, netcoreapp3.1, and net48.
68 | - As the package now uses nullable reference types, some method parameters now specify if they can accept nullable values.
69 |
70 | ## 1.0.5 - 2021-08-11
71 |
72 | #### Changed
73 |
74 | - Changes "Quicken Loans" to "Rocket Mortgage".
75 | - Updates RockLib.Logging to latest version, [3.0.8](https://github.com/RockLib/RockLib.Logging/blob/main/RockLib.Logging/CHANGELOG.md#308---2021-08-11).
76 |
77 | ## 1.0.4 - 2021-07-30
78 |
79 | #### Changed
80 |
81 | - Updates [RockLib.Logging](https://github.com/RockLib/RockLib.Logging/blob/main/RockLib.Logging/CHANGELOG.md#307---2021-07-30) package to latest version.
82 |
83 | ## 1.0.3 - 2021-07-27
84 |
85 | #### Changed
86 |
87 | - Updates [RockLib.Logging](https://github.com/RockLib/RockLib.Logging/blob/main/RockLib.Logging/CHANGELOG.md#306---2021-07-22) package to latest version.
88 |
89 | #### Added
90 |
91 | - Adds [RockLib.Logging.Microsoft.Extensions.Analyzers](https://github.com/RockLib/RockLib.Analyzers/blob/main/Logging.Microsoft.Extensions/CHANGELOG.md#100---2021-07-21) package reference.
92 |
93 | ## 1.0.2 - 2021-05-06
94 |
95 | #### Added
96 |
97 | - Adds SourceLink to nuget package.
98 |
99 | #### Changed
100 |
101 | - Updates RockLib.Logging package to latest version, which includes SourceLink.
102 |
103 | ----
104 |
105 | **Note:** Release notes in the above format are not available for earlier versions of
106 | RockLib.Logging.Microsoft.Extensions. What follows below are the original release notes.
107 |
108 | ----
109 |
110 | ## 1.0.1 - 2021-02-19
111 |
112 | Adds net5.0 target.
113 |
114 | ## 1.0.0 - 2020-11-16
115 |
116 | Initial release.
117 |
118 | ## 1.0.0-alpha02 - 2020-08-26
119 |
120 | Updates RockLib.Logging package to the latest version, 2.1.4.
121 |
122 | ## 1.0.0-alpha01 - 2020-08-24
123 |
124 | Initial prerelease.
125 |
--------------------------------------------------------------------------------
/RockLib.Logging.Microsoft.Extensions/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 | all
7 | Rocket Mortgage
8 | Copyright 2025 (c) Rocket Mortgage. All rights reserved.
9 | latest
10 | enable
11 | net48;net8.0
12 | NU1603,NU1701
13 | true
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
--------------------------------------------------------------------------------
/RockLib.Logging.Microsoft.Extensions/RockLib.Logging.Microsoft.Extensions.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Embedded
4 | An implementation of Microsoft.Extensions.Logging that uses RockLib.Logging.
5 | True
6 | True
7 | icon.png
8 | RockLib.Logging.Microsoft.Extensions
9 | LICENSE.md
10 | https://github.com/RockLib/RockLib.Logging
11 | A changelog is available at https://github.com/RockLib/RockLib.Logging/blob/main/RockLib.Logging.Microsoft.Extensions/CHANGELOG.md.
12 | false
13 | RockLib Logging Logger Microsoft Extensions
14 | 4.0.0
15 | True
16 | RockLib.Logging
17 | 4.0.0
18 |
19 |
20 | bin\$(Configuration)\$(TargetFramework)\$(PackageId).xml
21 |
22 |
23 | true
24 |
25 |
26 |
27 | runtime; build; native; contentfiles; analyzers; buildtransitive
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/RockLib.Logging.Microsoft.Extensions/RockLib.Logging.Microsoft.Extensions.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.3.32825.248
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RockLib.Logging.Microsoft.Extensions", "RockLib.Logging.Microsoft.Extensions.csproj", "{16C1DBC6-BE39-4109-932F-EE33A601ADB6}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RockLib.Logging.Microsoft.Extensions.Tests", "..\Tests\RockLib.Logging.Microsoft.Extensions.Tests\RockLib.Logging.Microsoft.Extensions.Tests.csproj", "{88065170-E5E2-4E4B-8E04-B6B68F862076}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{DA85831C-723F-4E0F-AF50-8D6BDCEC6A61}"
11 | EndProject
12 | Global
13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
14 | Debug|Any CPU = Debug|Any CPU
15 | Release|Any CPU = Release|Any CPU
16 | EndGlobalSection
17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
18 | {16C1DBC6-BE39-4109-932F-EE33A601ADB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19 | {16C1DBC6-BE39-4109-932F-EE33A601ADB6}.Debug|Any CPU.Build.0 = Debug|Any CPU
20 | {16C1DBC6-BE39-4109-932F-EE33A601ADB6}.Release|Any CPU.ActiveCfg = Release|Any CPU
21 | {16C1DBC6-BE39-4109-932F-EE33A601ADB6}.Release|Any CPU.Build.0 = Release|Any CPU
22 | {88065170-E5E2-4E4B-8E04-B6B68F862076}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {88065170-E5E2-4E4B-8E04-B6B68F862076}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {88065170-E5E2-4E4B-8E04-B6B68F862076}.Release|Any CPU.ActiveCfg = Release|Any CPU
25 | {88065170-E5E2-4E4B-8E04-B6B68F862076}.Release|Any CPU.Build.0 = Release|Any CPU
26 | EndGlobalSection
27 | GlobalSection(SolutionProperties) = preSolution
28 | HideSolutionNode = FALSE
29 | EndGlobalSection
30 | GlobalSection(ExtensibilityGlobals) = postSolution
31 | SolutionGuid = {806D89B7-78D1-4038-944B-AFFE3C7A0F00}
32 | EndGlobalSection
33 | EndGlobal
34 |
--------------------------------------------------------------------------------
/RockLib.Logging.Microsoft.Extensions/RockLibLoggerOptions.cs:
--------------------------------------------------------------------------------
1 | namespace RockLib.Logging;
2 |
3 | ///
4 | /// Options for a .
5 | ///
6 | public class RockLibLoggerOptions
7 | {
8 | ///
9 | /// Gets or sets a value indicating whether to include scopes when logging.
10 | ///
11 | public bool IncludeScopes { get; set; }
12 | }
13 |
--------------------------------------------------------------------------------
/RockLib.Logging.Microsoft.Extensions/Shims.cs:
--------------------------------------------------------------------------------
1 | #if NET48
2 | namespace System.Diagnostics.CodeAnalysis;
3 |
4 | /// Specifies that null is disallowed as an input even if the corresponding type allows it.
5 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)]
6 | public sealed class DisallowNullAttribute : Attribute { }
7 | #endif
--------------------------------------------------------------------------------
/RockLib.Logging.Moq/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 |
6 | [*.cs]
7 | # Styling
8 | indent_style = space
9 | indent_size = 4
10 | csharp_indent_case_contents = true
11 | csharp_indent_switch_labels = true
12 | csharp_new_line_before_catch = true
13 | csharp_new_line_before_else = true
14 | csharp_new_line_before_finally = true
15 | csharp_new_line_before_members_in_anonymous_types = false
16 | csharp_new_line_before_members_in_object_initializers = false
17 | csharp_new_line_before_open_brace = methods, control_blocks, types, properties, lambdas, accessors, object_collection_array_initializers
18 | csharp_new_line_between_query_expression_clauses = true
19 | csharp_prefer_braces = false:suggestion
20 | csharp_prefer_simple_default_expression = true:suggestion
21 | csharp_preferred_modifier_order = public,private,internal,protected,static,readonly,async,override,sealed:suggestion
22 | csharp_preserve_single_line_blocks = true
23 | csharp_preserve_single_line_statements = true
24 | csharp_space_after_cast = false
25 | csharp_space_after_colon_in_inheritance_clause = true
26 | csharp_space_after_keywords_in_control_flow_statements = true
27 | csharp_space_before_colon_in_inheritance_clause = true
28 | csharp_space_between_method_call_empty_parameter_list_parentheses = false
29 | csharp_space_between_method_call_name_and_opening_parenthesis = false
30 | csharp_space_between_method_call_parameter_list_parentheses = false
31 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
32 | csharp_space_between_method_declaration_parameter_list_parentheses = false
33 | csharp_style_pattern_matching_over_as_with_null_check = true:error
34 | csharp_style_expression_bodied_accessors = true:error
35 | csharp_style_expression_bodied_constructors = true:error
36 | csharp_style_expression_bodied_methods = true:error
37 | csharp_style_expression_bodied_properties = true:error
38 | csharp_style_namespace_declarations = file_scoped:error
39 | csharp_style_inlined_variable_declaration = true:error
40 | csharp_style_var_elsewhere = true:error
41 | csharp_style_var_for_built_in_types = true:error
42 | csharp_style_var_when_type_is_apparent = true:error
43 | dotnet_sort_system_directives_first = false
44 | dotnet_style_explicit_tuple_names = true:suggestion
45 | dotnet_style_object_initializer = true:suggestion
46 | csharp_style_pattern_local_over_anonymous_function = false:error
47 | dotnet_style_predefined_type_for_member_access = true:error
48 | dotnet_style_prefer_inferred_anonymous_type_member_names = false:error
49 | dotnet_style_prefer_inferred_tuple_names = true:error
50 | dotnet_style_predefined_type_for_locals_parameters_members = true:error
51 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:error
52 | dotnet_style_qualification_for_field = false:error
53 | dotnet_style_qualification_for_method = false:error
54 | dotnet_style_qualification_for_property = false:error
55 |
56 | # Analyzer Configuration
57 | # These are rules we want to either ignore or have set as suggestion or info
58 |
59 | # CA1014: Mark assemblies with CLSCompliant
60 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1014
61 | dotnet_diagnostic.CA1014.severity = none
62 |
63 | # CA1725: Parameter names should match base declaration
64 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1725
65 | dotnet_diagnostic.CA1725.severity = suggestion
66 |
67 | # CA2227: Collection properties should be read only
68 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2227
69 | dotnet_diagnostic.CA2227.severity = suggestion
--------------------------------------------------------------------------------
/RockLib.Logging.Moq/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 | all
7 | Rocket Mortgage
8 | Copyright 2025 (c) Rocket Mortgage. All rights reserved.
9 | latest
10 | enable
11 | net48;net8.0
12 | NU1603,NU1701
13 | true
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
--------------------------------------------------------------------------------
/RockLib.Logging.Moq/MockLogger.cs:
--------------------------------------------------------------------------------
1 | using Moq;
2 |
3 | namespace RockLib.Logging.Moq;
4 |
5 | ///
6 | /// Provides a mock implementation of .
7 | ///
8 | public class MockLogger : Mock
9 | {
10 | ///
11 | /// Initializes a new instance of the class.
12 | ///
13 | /// The level of the mock logger.
14 | /// The name of the mock logger.
15 | /// The behavior of the mock.
16 | public MockLogger(LogLevel level = LogLevel.Debug, string name = Logger.DefaultName, MockBehavior behavior = MockBehavior.Default)
17 | : base(behavior) => this.SetupLogger(level, name);
18 | }
19 |
--------------------------------------------------------------------------------
/RockLib.Logging.Moq/RockLib.Logging.Moq.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Embedded
4 | Extensions for verifying logging operations with Moq.
5 | True
6 | True
7 | RockLib.Logging.Moq
8 | LICENSE.md
9 | https://github.com/RockLib/RockLib.Logging
10 | A changelog is available at https://github.com/RockLib/RockLib.Logging/blob/main/RockLib.Logging.Moq/CHANGELOG.md.
11 | false
12 | RockLib Logging Logger Extensions Moq
13 | 4.0.0
14 | icon.png
15 | True
16 | 4.0.0
17 |
18 |
19 | bin\$(Configuration)\$(TargetFramework)\$(PackageId).xml
20 |
21 |
22 | true
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/RockLib.Logging.Moq/RockLib.Logging.Moq.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.29230.47
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RockLib.Logging.Moq", "RockLib.Logging.Moq.csproj", "{1781C3A1-5CC5-4338-9D68-B86D7C4C7356}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RockLib.Logging.Moq.Tests", "..\Tests\RockLib.Logging.Moq.Tests\RockLib.Logging.Moq.Tests.csproj", "{86EFB7D5-CF52-476D-BA9F-1D94778517C9}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {1781C3A1-5CC5-4338-9D68-B86D7C4C7356}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {1781C3A1-5CC5-4338-9D68-B86D7C4C7356}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {1781C3A1-5CC5-4338-9D68-B86D7C4C7356}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {1781C3A1-5CC5-4338-9D68-B86D7C4C7356}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {86EFB7D5-CF52-476D-BA9F-1D94778517C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {86EFB7D5-CF52-476D-BA9F-1D94778517C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {86EFB7D5-CF52-476D-BA9F-1D94778517C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {86EFB7D5-CF52-476D-BA9F-1D94778517C9}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {806D89B7-78D1-4038-944B-AFFE3C7A0F00}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/RockLib.Logging/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 |
6 | [*.cs]
7 | # Styling
8 | indent_style = space
9 | indent_size = 4
10 | csharp_indent_case_contents = true
11 | csharp_indent_switch_labels = true
12 | csharp_new_line_before_catch = true
13 | csharp_new_line_before_else = true
14 | csharp_new_line_before_finally = true
15 | csharp_new_line_before_members_in_anonymous_types = false
16 | csharp_new_line_before_members_in_object_initializers = false
17 | csharp_new_line_before_open_brace = methods, control_blocks, types, properties, lambdas, accessors, object_collection_array_initializers
18 | csharp_new_line_between_query_expression_clauses = true
19 | csharp_prefer_braces = false:suggestion
20 | csharp_prefer_simple_default_expression = true:suggestion
21 | csharp_preferred_modifier_order = public,private,internal,protected,static,readonly,async,override,sealed:suggestion
22 | csharp_preserve_single_line_blocks = true
23 | csharp_preserve_single_line_statements = true
24 | csharp_space_after_cast = false
25 | csharp_space_after_colon_in_inheritance_clause = true
26 | csharp_space_after_keywords_in_control_flow_statements = true
27 | csharp_space_before_colon_in_inheritance_clause = true
28 | csharp_space_between_method_call_empty_parameter_list_parentheses = false
29 | csharp_space_between_method_call_name_and_opening_parenthesis = false
30 | csharp_space_between_method_call_parameter_list_parentheses = false
31 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
32 | csharp_space_between_method_declaration_parameter_list_parentheses = false
33 | csharp_style_pattern_matching_over_as_with_null_check = true:error
34 | csharp_style_expression_bodied_accessors = true:error
35 | csharp_style_expression_bodied_constructors = true:error
36 | csharp_style_expression_bodied_methods = true:error
37 | csharp_style_expression_bodied_properties = true:error
38 | csharp_style_namespace_declarations = file_scoped:error
39 | csharp_style_inlined_variable_declaration = true:error
40 | csharp_style_var_elsewhere = true:error
41 | csharp_style_var_for_built_in_types = true:error
42 | csharp_style_var_when_type_is_apparent = true:error
43 | dotnet_sort_system_directives_first = false
44 | dotnet_style_explicit_tuple_names = true:suggestion
45 | dotnet_style_object_initializer = true:suggestion
46 | csharp_style_pattern_local_over_anonymous_function = false:error
47 | dotnet_style_predefined_type_for_member_access = true:error
48 | dotnet_style_prefer_inferred_anonymous_type_member_names = false:error
49 | dotnet_style_prefer_inferred_tuple_names = true:error
50 | dotnet_style_predefined_type_for_locals_parameters_members = true:error
51 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:error
52 | dotnet_style_qualification_for_field = false:error
53 | dotnet_style_qualification_for_method = false:error
54 | dotnet_style_qualification_for_property = false:error
55 |
56 | # Analyzer Configuration
57 | # These are rules we want to either ignore or have set as suggestion or info
58 |
59 | # CA1014: Mark assemblies with CLSCompliant
60 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1014
61 | dotnet_diagnostic.CA1014.severity = none
62 |
63 | # CA1725: Parameter names should match base declaration
64 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1725
65 | dotnet_diagnostic.CA1725.severity = suggestion
66 |
67 | # CA2227: Collection properties should be read only
68 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2227
69 | dotnet_diagnostic.CA2227.severity = suggestion
70 |
--------------------------------------------------------------------------------
/RockLib.Logging/AssemblyAttributes.cs:
--------------------------------------------------------------------------------
1 | using RockLib.Configuration.ObjectFactory;
2 | using RockLib.Logging;
3 | using System.Collections.Generic;
4 |
5 | [assembly: ConfigSection("RockLib.Logging", typeof(List))]
6 |
7 | #if NET48
8 | namespace System.Diagnostics.CodeAnalysis;
9 |
10 | /// Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it.
11 | [AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
12 | public sealed class NotNullWhenAttribute : Attribute
13 | {
14 | /// Initializes the attribute with the specified return value condition.
15 | ///
16 | /// The return value condition. If the method returns this value, the associated parameter will not be null.
17 | ///
18 | public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;
19 |
20 | /// Gets the return value condition.
21 | public bool ReturnValue { get; }
22 | }
23 | #endif
--------------------------------------------------------------------------------
/RockLib.Logging/Cached.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Net.NetworkInformation;
4 |
5 | namespace RockLib.Logging;
6 |
7 | internal static class Cached
8 | {
9 | private static readonly Lazy _ipAddress = new(GetMachineIpAddress);
10 | private static readonly Lazy _machineName = new(() => Environment.MachineName);
11 | private static readonly Lazy _userName = new(() => Environment.UserName);
12 |
13 | public static string? IpAddress => _ipAddress.Value;
14 |
15 | public static string MachineName => _machineName.Value;
16 |
17 | public static string UserName => _userName.Value;
18 |
19 | private static string? GetMachineIpAddress()
20 | {
21 | try
22 | {
23 | var ipAddresses =
24 | from i in NetworkInterface.GetAllNetworkInterfaces()
25 | where IsValidNetworkInterface(i)
26 | let p = i.GetIPProperties()
27 | where p.GatewayAddresses.Count > 0
28 | from a in p.UnicastAddresses
29 | where IsDuplicateAddressDetectionStatePreferred(a) && IsDnsEligible(a)
30 | select a.Address.ToString();
31 | return string.Join("\n", ipAddresses);
32 | }
33 | #pragma warning disable CA1031 // Do not catch general exception types
34 | catch
35 | #pragma warning restore CA1031 // Do not catch general exception types
36 | {
37 | return null;
38 | }
39 | }
40 |
41 | private static bool IsValidNetworkInterface(NetworkInterface networkInterface) => networkInterface.OperationalStatus == OperationalStatus.Up
42 | && (networkInterface.NetworkInterfaceType == NetworkInterfaceType.Ethernet || networkInterface.NetworkInterfaceType == NetworkInterfaceType.Wireless80211);
43 |
44 | #pragma warning disable CA1416 // Validate platform compatibility
45 | private static bool IsDuplicateAddressDetectionStatePreferred(UnicastIPAddressInformation addressInfo) =>
46 | addressInfo.DuplicateAddressDetectionState == DuplicateAddressDetectionState.Preferred;
47 |
48 | private static bool IsDnsEligible(UnicastIPAddressInformation addressInfo) => addressInfo.IsDnsEligible;
49 | #pragma warning restore CA1416 // Validate platform compatibility
50 | }
51 |
--------------------------------------------------------------------------------
/RockLib.Logging/DependencyInjection/ILoggerBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace RockLib.Logging.DependencyInjection;
4 |
5 | ///
6 | /// A builder used to add log providers and context providers to a logger.
7 | ///
8 | public interface ILoggerBuilder
9 | {
10 | ///
11 | /// The name of the logger to build.
12 | ///
13 | string LoggerName { get; }
14 |
15 | ///
16 | /// Adds an to the logger.
17 | ///
18 | /// A method that creates the .
19 | /// The same .
20 | ILoggerBuilder AddLogProvider(Func logProviderRegistration);
21 |
22 | ///
23 | /// Adds an to the logger.
24 | ///
25 | /// A method that creates the .
26 | /// The same .
27 | ILoggerBuilder AddContextProvider(Func contextProviderRegistration);
28 | }
--------------------------------------------------------------------------------
/RockLib.Logging/DependencyInjection/LoggerLookup.cs:
--------------------------------------------------------------------------------
1 | namespace RockLib.Logging.DependencyInjection;
2 |
3 | ///
4 | /// Represents a method that retrieves an by its name.
5 | ///
6 | /// The name of the to retrieve.
7 | /// The matching .
8 | public delegate ILogger LoggerLookup(string name);
--------------------------------------------------------------------------------
/RockLib.Logging/DependencyInjection/Options/ConsoleLogProviderOptions.cs:
--------------------------------------------------------------------------------
1 | namespace RockLib.Logging.DependencyInjection;
2 |
3 | ///
4 | /// Defines an options class for creating a .
5 | ///
6 | public class ConsoleLogProviderOptions : FormattableLogProviderOptions
7 | {
8 | ///
9 | /// The type of console output stream to write to.
10 | ///
11 | public ConsoleLogProvider.Output Output { get; set; }
12 | }
--------------------------------------------------------------------------------
/RockLib.Logging/DependencyInjection/Options/DebugLogProviderOptions.cs:
--------------------------------------------------------------------------------
1 | namespace RockLib.Logging.DependencyInjection;
2 |
3 | ///
4 | /// Defines an options class for creating a .
5 | ///
6 | public class DebugLogProviderOptions : FormattableLogProviderOptions
7 | {
8 | }
--------------------------------------------------------------------------------
/RockLib.Logging/DependencyInjection/Options/FileLogProviderOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace RockLib.Logging.DependencyInjection;
4 |
5 | ///
6 | /// Defines an options class for creating a .
7 | ///
8 | public class FileLogProviderOptions : FormattableLogProviderOptions
9 | {
10 | private string _file = string.Empty;
11 |
12 | ///
13 | /// The file to write to.
14 | ///
15 | public string File
16 | {
17 | get => _file;
18 | set => _file = value ?? throw new ArgumentNullException(nameof(value));
19 | }
20 | }
--------------------------------------------------------------------------------
/RockLib.Logging/DependencyInjection/Options/FormattableLogProviderOptions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 | using System;
3 |
4 | namespace RockLib.Logging.DependencyInjection;
5 |
6 | ///
7 | /// Defines an options class for creating an that is
8 | /// formatted by an .
9 | ///
10 | public abstract class FormattableLogProviderOptions : LogProviderOptions
11 | {
12 | ///
13 | /// The method used to create the of the log provider.
14 | ///
15 | public Func? FormatterRegistration { get; set; }
16 |
17 | ///
18 | /// Sets to a method that creates a
19 | /// with the specified template.
20 | ///
21 | /// The template to use when formatting logs.
22 | public void SetTemplate(string template)
23 | {
24 | #if NET6_0_OR_GREATER
25 | ArgumentNullException.ThrowIfNull(template);
26 | #else
27 | if (template is null)
28 | {
29 | throw new ArgumentNullException(nameof(template));
30 | }
31 | #endif
32 |
33 | SetFormatter(template);
34 | }
35 |
36 | ///
37 | /// Sets to a method that returns the specified
38 | /// .
39 | ///
40 | /// The to use for formatting logs.
41 | public void SetFormatter(ILogFormatter formatter)
42 | {
43 | #if NET6_0_OR_GREATER
44 | ArgumentNullException.ThrowIfNull(formatter);
45 | #else
46 | if (formatter is null)
47 | {
48 | throw new ArgumentNullException(nameof(formatter));
49 | }
50 | #endif
51 |
52 | FormatterRegistration = serviceProvider => formatter;
53 | }
54 |
55 | ///
56 | /// Sets to a method that returns a new instance
57 | /// of type .
58 | ///
59 | ///
60 | /// The type of that the log provider uses for formatting logs.
61 | ///
62 | ///
63 | /// Constructor arguments for type that are not provided
64 | /// by the .
65 | ///
66 | public void SetFormatter(params object[] logFormatterParameters)
67 | where TLogFormatter : ILogFormatter => FormatterRegistration = serviceProvider => ActivatorUtilities.CreateInstance(serviceProvider, logFormatterParameters);
68 | }
--------------------------------------------------------------------------------
/RockLib.Logging/DependencyInjection/Options/ILoggerOptions.cs:
--------------------------------------------------------------------------------
1 | namespace RockLib.Logging.DependencyInjection;
2 |
3 | ///
4 | /// Defines an interface for the options for creating an .
5 | ///
6 | public interface ILoggerOptions
7 | {
8 | ///
9 | /// The logging level of the logger.
10 | ///
11 | LogLevel? Level { get; set; }
12 |
13 | ///
14 | /// Whether the logger should be disabled.
15 | ///
16 | bool? IsDisabled { get; set; }
17 |
18 | ///
19 | /// Whether to create a logger that automatically reloads itself when its configuration or options change.
20 | ///
21 | bool ReloadOnChange { get; set; }
22 | }
--------------------------------------------------------------------------------
/RockLib.Logging/DependencyInjection/Options/LogProviderOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace RockLib.Logging.DependencyInjection;
4 |
5 | ///
6 | /// Defines an options class for creating an .
7 | ///
8 | public abstract class LogProviderOptions
9 | {
10 | ///
11 | /// The logging level of the log provider.
12 | ///
13 | public LogLevel Level { get; set; }
14 |
15 | ///
16 | /// The timeout of the log provider.
17 | ///
18 | public TimeSpan? Timeout { get; set; }
19 |
20 | ///
21 | /// Whether to create a log provider that automatically reloads itself when its options change.
22 | ///
23 | public bool ReloadOnChange { get; set; }
24 | }
--------------------------------------------------------------------------------
/RockLib.Logging/DependencyInjection/Options/LoggerOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace RockLib.Logging.DependencyInjection;
5 |
6 | ///
7 | /// Defines the options for creating an .
8 | ///
9 | public class LoggerOptions : ILoggerOptions
10 | {
11 | ///
12 | /// The list of log provider registrations that create the log providers of the logger.
13 | ///
14 | public IList> LogProviderRegistrations { get; } = new List>();
15 |
16 | ///
17 | /// The list of context provider registrations that create the context providers of the logger.
18 | ///
19 | public IList> ContextProviderRegistrations { get; } = new List>();
20 |
21 | ///
22 | /// The logging level of the logger.
23 | ///
24 | public LogLevel? Level { get; set; }
25 |
26 | ///
27 | /// Whether the logger should be disabled.
28 | ///
29 | public bool? IsDisabled { get; set; }
30 |
31 | ///
32 | /// Whether to create a logger that automatically reloads itself when its configuration or options change.
33 | ///
34 | public bool ReloadOnChange { get; set; }
35 | }
--------------------------------------------------------------------------------
/RockLib.Logging/DependencyInjection/Options/RollingFileLogProviderOptions.cs:
--------------------------------------------------------------------------------
1 | namespace RockLib.Logging.DependencyInjection;
2 |
3 | ///
4 | /// Defines an options class for creating a .
5 | ///
6 | public class RollingFileLogProviderOptions : FileLogProviderOptions
7 | {
8 | ///
9 | /// The maximum file size, in bytes, of the file. If the file size is greater than this value,
10 | /// it is archived.
11 | ///
12 | public int MaxFileSizeKilobytes { get; set; } = RollingFileLogProvider.DefaultMaxFileSizeKilobytes;
13 |
14 | ///
15 | /// The maximum number of archive files that will be kept. If the number of archive files is
16 | /// greater than this value, then they are deleted, oldest first.
17 | ///
18 | public int MaxArchiveCount { get; set; } = RollingFileLogProvider.DefaultMaxArchiveCount;
19 |
20 | ///
21 | /// The rollover period, indicating if/how the file should archived on a periodic basis.
22 | ///
23 | public RolloverPeriod RolloverPeriod { get; set; } = RollingFileLogProvider.DefaultRolloverPeriod;
24 | }
--------------------------------------------------------------------------------
/RockLib.Logging/DependencyInjection/ReloadingLogProvider.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Options;
2 | using System;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 | using static RockLib.Logging.DependencyInjection.ServiceCollectionExtensions;
6 |
7 | namespace RockLib.Logging.DependencyInjection;
8 |
9 | internal sealed class ReloadingLogProvider : ILogProvider
10 | {
11 | private readonly Func _createLogProvider;
12 | private readonly string _name;
13 | private readonly Action _configureOptions;
14 | private ILogProvider _logProvider;
15 | private readonly IDisposable _changeListener;
16 |
17 | public ReloadingLogProvider(IOptionsMonitor optionsMonitor, TOptions options,
18 | Func createLogProvider, string name, Action configureOptions)
19 | {
20 | _createLogProvider = createLogProvider;
21 | _name = name;
22 | _configureOptions = configureOptions;
23 | _logProvider = _createLogProvider(options);
24 |
25 | _changeListener = optionsMonitor.OnChange(OptionsMonitorChanged)!;
26 | }
27 |
28 | public TimeSpan Timeout => _logProvider.Timeout;
29 |
30 | public LogLevel Level => _logProvider.Level;
31 |
32 | public Task WriteAsync(LogEntry logEntry, CancellationToken cancellationToken) =>
33 | _logProvider.WriteAsync(logEntry, cancellationToken);
34 |
35 | private void OptionsMonitorChanged(TOptions options, string? name)
36 | {
37 | if (NamesEqual(_name, name))
38 | {
39 | _configureOptions?.Invoke(options);
40 | _logProvider = _createLogProvider(options);
41 | }
42 | }
43 |
44 | ~ReloadingLogProvider()
45 | {
46 | _changeListener.Dispose();
47 | }
48 | }
--------------------------------------------------------------------------------
/RockLib.Logging/DependencyInjection/ReloadingLogger.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Options;
2 | using RockLib.Logging.LogProcessing;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Runtime.CompilerServices;
6 | using Microsoft.Extensions.DependencyInjection;
7 | using static RockLib.Logging.DependencyInjection.ServiceCollectionExtensions;
8 | using RockLib.Logging.LogProviders;
9 |
10 | namespace RockLib.Logging.DependencyInjection;
11 |
12 | internal sealed class ReloadingLogger : ILogger
13 | {
14 | private readonly IServiceProvider _serviceProvider;
15 | private readonly ILogProcessor _logProcessor;
16 | private readonly IReadOnlyCollection _logProviders;
17 | private readonly IReadOnlyCollection _contextProviders;
18 | private readonly Action _configureOptions;
19 | private Logger _logger;
20 | private readonly IDisposable _changeListener;
21 |
22 | public ReloadingLogger(IServiceProvider serviceProvider,
23 | ILogProcessor logProcessor,
24 | string name,
25 | IReadOnlyCollection logProviders,
26 | IReadOnlyCollection contextProviders,
27 | IOptionsMonitor optionsMonitor,
28 | LoggerOptions options,
29 | Action configureOptions)
30 | {
31 | Name = name;
32 | _serviceProvider = serviceProvider;
33 | _logProcessor = logProcessor;
34 | _logProviders = logProviders;
35 | _contextProviders = contextProviders;
36 | _configureOptions = configureOptions;
37 | _logger = CreateLogger(_serviceProvider, options);
38 | _changeListener = optionsMonitor.OnChange(OptionsMonitorChanged)!;
39 | }
40 |
41 | public bool IsDisabled => _logger.IsDisabled;
42 |
43 | public IErrorHandler? ErrorHandler { get => _logger.ErrorHandler; set => _logger.ErrorHandler = value; }
44 |
45 | public string Name { get; }
46 |
47 | public LogLevel Level => _logger.Level;
48 |
49 | public IReadOnlyCollection LogProviders => _logger.LogProviders;
50 |
51 | public IReadOnlyCollection ContextProviders => _logger.ContextProviders;
52 |
53 | public void Dispose()
54 | {
55 | _changeListener.Dispose();
56 | _logger.Dispose();
57 | }
58 |
59 | public void Log(LogEntry logEntry, [CallerMemberName] string? callerMemberName = null, [CallerFilePath] string? callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0) =>
60 | _logger.Log(logEntry, callerMemberName, callerFilePath, callerLineNumber);
61 |
62 | private void OptionsMonitorChanged(LoggerOptions options, string? name)
63 | {
64 | if (NamesEqual(Name, name))
65 | {
66 | _configureOptions?.Invoke(options);
67 |
68 | var oldLogger = _logger;
69 | var newLogger = CreateLogger(_serviceProvider, options);
70 |
71 | if (oldLogger.ErrorHandler is not null && newLogger.ErrorHandler is null)
72 | {
73 | newLogger.ErrorHandler = oldLogger.ErrorHandler;
74 | }
75 |
76 | _logger = newLogger;
77 | }
78 | }
79 |
80 | private Logger CreateLogger(IServiceProvider serviceProvider, LoggerOptions options) =>
81 | new(_logProcessor, Name, options.Level.GetValueOrDefault(), serviceProvider.GetService()!,
82 | _logProviders, options.IsDisabled.GetValueOrDefault(), _contextProviders);
83 | }
--------------------------------------------------------------------------------
/RockLib.Logging/Diagnostics/LoggingTraceListener.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 |
4 | namespace RockLib.Logging.Diagnostics;
5 |
6 | using static Logger;
7 |
8 | ///
9 | /// A trace listener that records trace messages with an .
10 | ///
11 | public class LoggingTraceListener : TraceListener
12 | {
13 | ///
14 | /// Initializes a new instance of the class.
15 | ///
16 | /// The logger that will record trace messages.
17 | /// The level that trace messages will be logged at.
18 | public LoggingTraceListener(ILogger logger, LogLevel? logLevel = null)
19 | : base(logger?.Name ?? DefaultName)
20 | {
21 | Logger = logger ?? throw new ArgumentNullException(nameof(logger));
22 | LogLevel = logLevel ?? logger.Level;
23 | }
24 |
25 | ///
26 | /// Initializes a new instance of the class.
27 | ///
28 | ///
29 | /// The name of the logger to create, using , that will record
30 | /// trace messages.
31 | ///
32 | /// The level that trace messages will be logged at.
33 | public LoggingTraceListener(string loggerName = DefaultName, LogLevel? logLevel = null)
34 | : this(LoggerFactory.Create(loggerName ?? DefaultName), logLevel)
35 | {
36 | }
37 |
38 | ///
39 | /// The logger that records trace messages.
40 | ///
41 | public ILogger Logger { get; }
42 |
43 | ///
44 | /// The level that trace messages are logged at.
45 | ///
46 | public LogLevel LogLevel { get; }
47 |
48 | ///
49 | /// Writes the specified message to .
50 | ///
51 | /// The message to write.
52 | public override void Write(string? message) => WriteLog(message);
53 |
54 | ///
55 | /// Writes the specified message to .
56 | ///
57 | /// The message to write.
58 | public override void WriteLine(string? message) => WriteLog(message);
59 |
60 | private void WriteLog(string? message)
61 | {
62 | if (Logger.IsEnabled(LogLevel))
63 | {
64 | Logger.Log(new LogEntry(message, LogLevel));
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/RockLib.Logging/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 | all
7 | Rocket Mortgage
8 | Copyright 2025 (c) Rocket Mortgage. All rights reserved.
9 | latest
10 | enable
11 | net48;net8.0
12 | NU1603,NU1701
13 | true
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
--------------------------------------------------------------------------------
/RockLib.Logging/Error.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace RockLib.Logging;
4 |
5 | ///
6 | /// Defines an error to be handled by the interface.
7 | ///
8 | #pragma warning disable CA1716 // Identifiers should not match keywords
9 | public sealed class Error
10 | #pragma warning restore CA1716 // Identifiers should not match keywords
11 | {
12 | ///
13 | /// Initializes a new instance of the class.
14 | ///
15 | /// A message the describes the error.
16 | /// The exception responsible for the error, or null to indicate a timeout.
17 | ///
18 | /// The log provider that failed to write the log entry.
19 | ///
20 | /// The log entry that failed to write.
21 | ///
22 | /// The number of times the log provider has failed to write the log entry.
23 | ///
24 | public Error(string message, Exception? exception, ILogProvider logProvider, LogEntry logEntry, int failureCount)
25 | {
26 | if (failureCount < 0)
27 | {
28 | throw new ArgumentException("Cannot be less than zero.", nameof(failureCount));
29 | }
30 |
31 | Message = message ?? throw new ArgumentNullException(nameof(message));
32 | Exception = exception;
33 | LogProvider = logProvider ?? throw new ArgumentNullException(nameof(logProvider));
34 | LogEntry = logEntry ?? throw new ArgumentNullException(nameof(logEntry));
35 | FailureCount = failureCount;
36 | }
37 |
38 | ///
39 | /// Gets the message that describes the error.
40 | ///
41 | public string Message { get; }
42 |
43 | ///
44 | /// Gets the exception responsible for the error.
45 | ///
46 | public Exception? Exception { get; }
47 |
48 | ///
49 | /// Gets the log provider that failed to write the log entry.
50 | ///
51 | public ILogProvider LogProvider { get; }
52 |
53 | ///
54 | /// Gets the log entry that failed to write.
55 | ///
56 | public LogEntry LogEntry { get; }
57 |
58 | ///
59 | /// Gets the number of times the log provider has failed to write the log entry.
60 | ///
61 | public int FailureCount { get; }
62 |
63 | ///
64 | /// Gets a value indicating whether the error was a result of timing out.
65 | ///
66 | public bool IsTimeout => Exception is null;
67 |
68 | ///
69 | /// Gets the time that the error event occurred.
70 | ///
71 | public DateTime Timestamp { get; } = DateTime.UtcNow;
72 |
73 | ///
74 | /// Gets or sets a value indicating whether the log provider should attempt to send
75 | /// the log entry again.
76 | ///
77 | public bool ShouldRetry { get; set; }
78 | }
--------------------------------------------------------------------------------
/RockLib.Logging/Extensions/EnabledExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace RockLib.Logging;
4 |
5 | ///
6 | /// Defines extension method that determine if a given logging level is enabled.
7 | ///
8 | public static class EnabledExtensions
9 | {
10 | ///
11 | /// Returns whether debug logging is enabled for the logger.
12 | ///
13 | /// The logger to check.
14 | /// True if debug logging is enabled, otherwise false.
15 | public static bool IsDebugEnabled(this ILogger logger) => logger.IsEnabled(LogLevel.Debug);
16 |
17 | ///
18 | /// Returns whether info logging is enabled for the logger.
19 | ///
20 | /// The logger to check.
21 | /// True if info logging is enabled, otherwise false.
22 | public static bool IsInfoEnabled(this ILogger logger) => logger.IsEnabled(LogLevel.Info);
23 |
24 | ///
25 | /// Returns whether warn logging is enabled for the logger.
26 | ///
27 | /// The logger to check.
28 | /// True if warn logging is enabled, otherwise false.
29 | public static bool IsWarnEnabled(this ILogger logger) => logger.IsEnabled(LogLevel.Warn);
30 |
31 | ///
32 | /// Returns whether error logging is enabled for the logger.
33 | ///
34 | /// The logger to check.
35 | /// True if error logging is enabled, otherwise false.
36 | public static bool IsErrorEnabled(this ILogger logger) => logger.IsEnabled(LogLevel.Error);
37 |
38 | ///
39 | /// Returns whether fatal logging is enabled for the logger.
40 | ///
41 | /// The logger to check.
42 | /// True if fatal logging is enabled, otherwise false.
43 | public static bool IsFatalEnabled(this ILogger logger) => logger.IsEnabled(LogLevel.Fatal);
44 |
45 | ///
46 | /// Returns whether audit logging is enabled for the logger.
47 | ///
48 | /// The logger to check.
49 | /// True if audit logging is enabled, otherwise false.
50 | public static bool IsAuditEnabled(this ILogger logger) => logger.IsEnabled(LogLevel.Audit);
51 |
52 | ///
53 | /// Returns whether logging is enabled for the given level for the logger.
54 | ///
55 | /// The logger to check.
56 | /// The level to check.
57 | /// True if logging is enabled for the given level, otherwise false.
58 | /// Thrown if is null
59 | public static bool IsEnabled(this ILogger logger, LogLevel level)
60 | {
61 | #if NET6_0_OR_GREATER
62 | ArgumentNullException.ThrowIfNull(logger);
63 | #else
64 | if(logger is null) { throw new ArgumentNullException(nameof(logger)); }
65 | #endif
66 |
67 | return !logger.IsDisabled && level >= logger.Level;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/RockLib.Logging/Extensions/ErrorHandlerExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace RockLib.Logging;
4 |
5 | ///
6 | /// Defines extension methods for the interface for setting
7 | /// its property.
8 | ///
9 | public static class ErrorHandlerExtensions
10 | {
11 | ///
12 | /// Sets the property to an implementation of
13 | /// the interface that invokes the
14 | /// delegate when its method is called.
15 | ///
16 | ///
17 | /// The logger whose property is to be set.
18 | ///
19 | ///
20 | /// The delegate to be invoked when the method
21 | /// of the logger's property is called.
22 | ///
23 | /// Thrown if is null
24 | public static void SetErrorHandler(this ILogger logger, Action errorHandler)
25 | {
26 | #if NET6_0_OR_GREATER
27 | ArgumentNullException.ThrowIfNull(logger);
28 | #else
29 | if (logger is null) { throw new ArgumentNullException(nameof(logger)); }
30 | #endif
31 | logger.ErrorHandler = new DelegateErrorHandler(errorHandler);
32 | }
33 |
34 | private sealed class DelegateErrorHandler : IErrorHandler
35 | {
36 | private readonly Action _handleError;
37 | public DelegateErrorHandler(Action handleError) => _handleError = handleError;
38 | public void HandleError(Error error) => _handleError(error);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/RockLib.Logging/FormatToString/BlockIndentExtension.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace RockLib.Logging;
4 |
5 | internal static class BlockIndentExtension
6 | {
7 | public static string BlockIndent(this string self, string indention) =>
8 | #if NETCOREAPP3_1_OR_GREATER
9 | $"{indention}{self.Replace("\n", $"\n{indention}", StringComparison.CurrentCulture)}";
10 | #else
11 | $"{indention}{self.Replace("\n", $"\n{indention}")}";
12 | #endif
13 | }
14 |
--------------------------------------------------------------------------------
/RockLib.Logging/IContextProvider.cs:
--------------------------------------------------------------------------------
1 | namespace RockLib.Logging;
2 |
3 | ///
4 | /// Defines an object that adds custom context to objects.
5 | ///
6 | public interface IContextProvider
7 | {
8 | ///
9 | /// Add custom context to the object.
10 | ///
11 | /// The log entry to add custom context to.
12 | void AddContext(LogEntry logEntry);
13 | }
14 |
--------------------------------------------------------------------------------
/RockLib.Logging/IErrorHandler.cs:
--------------------------------------------------------------------------------
1 | namespace RockLib.Logging;
2 |
3 | ///
4 | /// Defines an object that can handle errors that occur when processing log entries.
5 | ///
6 | public interface IErrorHandler
7 | {
8 | ///
9 | /// Handle the specified error. If an implementation sets the
10 | /// property to true, then the log provider should
11 | /// attempt to send the log entry again.
12 | ///
13 | /// An error that has occurred.
14 | #pragma warning disable CA1716 // Identifiers should not match keywords
15 | void HandleError(Error error);
16 | #pragma warning restore CA1716 // Identifiers should not match keywords
17 | }
--------------------------------------------------------------------------------
/RockLib.Logging/ILogger.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Runtime.CompilerServices;
4 |
5 | namespace RockLib.Logging;
6 |
7 | ///
8 | /// Defines an object used for logging.
9 | ///
10 | public interface ILogger : IDisposable
11 | {
12 | ///
13 | /// Gets the name of the logger.
14 | ///
15 | string Name { get; }
16 |
17 | ///
18 | /// Gets a value indicating whether the logger is disabled.
19 | ///
20 | bool IsDisabled { get; }
21 |
22 | ///
23 | /// Gets the logging level of the logger.
24 | ///
25 | ///
26 | /// Log entries with a level lower than the value of this property should
27 | /// not be logged by this logger.
28 | ///
29 | LogLevel Level { get; }
30 |
31 | ///
32 | /// Gets the collection of objects used by this logger.
33 | ///
34 | IReadOnlyCollection LogProviders { get; }
35 |
36 | ///
37 | /// Gets the collection of objects used by this logger.
38 | ///
39 | IReadOnlyCollection ContextProviders { get; }
40 |
41 | ///
42 | /// Gets or sets the object that handles errors that occur during log processing.
43 | ///
44 | IErrorHandler? ErrorHandler { get; set; }
45 |
46 | ///
47 | /// Logs the specified log entry.
48 | ///
49 | /// The log entry to log.
50 | /// The method or property name of the caller.
51 | /// The path of the source file that contains the caller.
52 | /// The line number in the source file at which this method is called.
53 | void Log(
54 | LogEntry logEntry,
55 | [CallerMemberName] string? callerMemberName = null,
56 | [CallerFilePath] string? callerFilePath = null,
57 | [CallerLineNumber] int callerLineNumber = 0);
58 | }
--------------------------------------------------------------------------------
/RockLib.Logging/LogLevel.cs:
--------------------------------------------------------------------------------
1 | namespace RockLib.Logging;
2 |
3 | ///
4 | /// Defines various logging levels.
5 | ///
6 | public enum LogLevel
7 | {
8 | ///
9 | /// The logging level has not been set.
10 | ///
11 | NotSet,
12 |
13 | ///
14 | /// The Debug logging level.
15 | ///
16 | Debug,
17 |
18 | ///
19 | /// The Info logging level.
20 | ///
21 | Info,
22 |
23 | ///
24 | /// The Warn logging level.
25 | ///
26 | Warn,
27 |
28 | ///
29 | /// The Error logging level.
30 | ///
31 | Error,
32 |
33 | ///
34 | /// The Fatal logging level.
35 | ///
36 | Fatal,
37 |
38 | ///
39 | /// The Audit logging level.
40 | ///
41 | Audit
42 | }
--------------------------------------------------------------------------------
/RockLib.Logging/LogProcessing/FireAndForgetLogProcessor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.Threading;
4 |
5 | namespace RockLib.Logging.LogProcessing;
6 |
7 | ///
8 | /// A log processor that processes logs asynchronously, but without any
9 | /// task tracking.
10 | ///
11 | [Obsolete("Please use the FireAndForgetProcessor instead.", false)]
12 | public sealed class FireAndForgetLogProcessor : LogProcessor
13 | {
14 | ///
15 | protected async override void SendToLogProvider(ILogProvider logProvider, LogEntry logEntry,
16 | IErrorHandler errorHandler, int failureCount)
17 | {
18 | try
19 | {
20 | await logProvider.WriteAsync(logEntry, CancellationToken.None).ConfigureAwait(false);
21 |
22 | TraceSource.TraceEvent(TraceEventType.Information, 0,
23 | "[{0:s}] - [" + nameof(FireAndForgetLogProcessor) + "] - Successfully processed log entry {1} from log provider {2}.",
24 | DateTime.UtcNow, logEntry.UniqueId, logProvider);
25 | }
26 | #pragma warning disable CA1031 // Do not catch general exception types
27 | catch (Exception ex)
28 | #pragma warning restore CA1031 // Do not catch general exception types
29 | {
30 | HandleError(ex, logProvider, logEntry, errorHandler, failureCount + 1,
31 | "Error while sending log entry {0} to log provider {1}.", logEntry.UniqueId, logProvider);
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/RockLib.Logging/LogProcessing/ILogProcessor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace RockLib.Logging.LogProcessing;
4 |
5 | ///
6 | /// Defines an object that processes log entries on behalf of a logger.
7 | ///
8 | public interface ILogProcessor : IDisposable
9 | {
10 | ///
11 | /// Gets a value indicating whether the log processor has been disposed.
12 | ///
13 | bool IsDisposed { get; }
14 |
15 | ///
16 | /// Processes the log entry on behalf of the logger.
17 | ///
18 | ///
19 | /// The logger that the log entry is processed on behalf of. Its log
20 | /// providers and context providers define how the log entry is processed.
21 | ///
22 | /// The log entry to process.
23 | ///
24 | /// Implementations should not call the method
25 | /// on the parameter.
26 | ///
27 | void ProcessLogEntry(ILogger logger, LogEntry logEntry);
28 | }
29 |
--------------------------------------------------------------------------------
/RockLib.Logging/LogProcessing/SynchronousLogProcessor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.Threading;
4 |
5 | namespace RockLib.Logging.LogProcessing;
6 |
7 | ///
8 | /// A log processor that processes logs on the same thread as the caller.
9 | ///
10 | [Obsolete("Please use the FireAndForgetProcessor instead.", false)]
11 | public sealed class SynchronousLogProcessor : LogProcessor
12 | {
13 | ///
14 | protected override void SendToLogProvider(ILogProvider logProvider, LogEntry logEntry,
15 | IErrorHandler errorHandler, int failureCount)
16 | {
17 | var old = SynchronizationContext.Current;
18 | try
19 | {
20 | SynchronizationContext.SetSynchronizationContext(null);
21 | logProvider.WriteAsync(logEntry, CancellationToken.None).GetAwaiter().GetResult();
22 |
23 | TraceSource.TraceEvent(TraceEventType.Information, 0,
24 | "[{0:s}] - [" + nameof(SynchronousLogProcessor) + "] - Successfully processed log entry {1} from log provider {2}.",
25 | DateTime.UtcNow, logEntry.UniqueId, logProvider);
26 | }
27 | finally
28 | {
29 | SynchronizationContext.SetSynchronizationContext(old);
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/RockLib.Logging/LogProviders/DebugLogProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 |
6 | namespace RockLib.Logging;
7 |
8 | ///
9 | /// An implementation of that writes log entries to debug.
10 | ///
11 | public class DebugLogProvider : ILogProvider
12 | {
13 | ///
14 | /// The default template.
15 | ///
16 | public const string DefaultTemplate = ConsoleLogProvider.DefaultTemplate;
17 |
18 | ///
19 | /// The default timeout.
20 | ///
21 | public static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(1);
22 |
23 | ///
24 | /// Initializes a new instance of the class.
25 | ///
26 | /// The template used to format log entries.
27 | /// The level of the log provider.
28 | /// The timeout of the log provider.
29 | public DebugLogProvider(
30 | string template = DefaultTemplate, LogLevel level = default, TimeSpan? timeout = null)
31 | : this(new TemplateLogFormatter(template ?? DefaultTemplate), level, timeout)
32 | {
33 | }
34 |
35 | ///
36 | /// Initializes a new instance of the class.
37 | ///
38 | /// An object that formats log entries prior to writing to standard out.
39 | /// The level of the log provider.
40 | /// The timeout of the log provider.
41 | public DebugLogProvider(
42 | ILogFormatter formatter, LogLevel level = default, TimeSpan? timeout = null)
43 | {
44 | if (!Enum.IsDefined(typeof(LogLevel), level))
45 | {
46 | throw new ArgumentException($"Log level is not defined: {level}.", nameof(level));
47 | }
48 |
49 | if (timeout.HasValue && timeout.Value < TimeSpan.Zero)
50 | {
51 | throw new ArgumentException("Timeout cannot be negative.", nameof(timeout));
52 | }
53 |
54 | Formatter = formatter ?? throw new ArgumentNullException(nameof(formatter));
55 | Level = level;
56 | Timeout = timeout ?? DefaultTimeout;
57 | }
58 |
59 | ///
60 | /// Gets an object that formats log entries.
61 | ///
62 | public ILogFormatter Formatter { get; }
63 |
64 | ///
65 | /// Gets the log level.
66 | ///
67 | public LogLevel Level { get; }
68 |
69 | ///
70 | /// Gets the timeout.
71 | ///
72 | public TimeSpan Timeout { get; }
73 |
74 | ///
75 | /// Formats the log entry using the property and writes it to standard out.
76 | ///
77 | /// The log entry to write.
78 | /// The to observe.
79 | /// A task that completes when the log entry has been written to standard out.
80 | public Task WriteAsync(LogEntry logEntry, CancellationToken cancellationToken = default)
81 | {
82 | var formattedLog = Formatter.Format(logEntry);
83 | WriteToDebug(formattedLog);
84 | return Task.CompletedTask;
85 | }
86 |
87 | ///
88 | /// Writes to the class
89 | ///
90 | /// The formatted log message.
91 | protected virtual void WriteToDebug(string formattedLog) => Debug.WriteLine(formattedLog);
92 | }
93 |
--------------------------------------------------------------------------------
/RockLib.Logging/LogProviders/ILogFormatter.cs:
--------------------------------------------------------------------------------
1 | namespace RockLib.Logging;
2 |
3 | ///
4 | /// Defines an object that formats objects to string
5 | /// representations.
6 | ///
7 | public interface ILogFormatter
8 | {
9 | ///
10 | /// Formats the specified log entry as a string.
11 | ///
12 | /// The log entry to format.
13 | /// The formatted log entry.
14 | string Format(LogEntry logEntry);
15 | }
16 |
--------------------------------------------------------------------------------
/RockLib.Logging/LogProviders/ILogLevelResolver.cs:
--------------------------------------------------------------------------------
1 | namespace RockLib.Logging.LogProviders;
2 |
3 | ///
4 | /// Used to resolve the when logging with an
5 | ///
6 | public interface ILogLevelResolver
7 | {
8 | ///
9 | /// Retrieves the currently active
10 | ///
11 | /// The current or null if it cannot be determined
12 | /// If the cannot be determined, and is returned as null, then the default
13 | /// of the will be used instead
14 | LogLevel? GetLogLevel();
15 | }
16 |
--------------------------------------------------------------------------------
/RockLib.Logging/LogProviders/ILogProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 |
5 | namespace RockLib.Logging;
6 |
7 | ///
8 | /// Defines a object that writes log entries.
9 | ///
10 | ///
11 | /// Implementations of this interface *must* ensure that their method
12 | /// is thread-safe. Multiple threads can invoke the method simultaneously.
13 | ///
14 | public interface ILogProvider
15 | {
16 | ///
17 | /// Gets the timeout of the .
18 | ///
19 | ///
20 | /// If a task returned by the method does not complete
21 | /// by the value of the property, the task will be cancelled.
22 | ///
23 | TimeSpan Timeout { get; }
24 |
25 | ///
26 | /// Gets the level of the .
27 | ///
28 | ///
29 | /// This value is used by the class to determine if it should
30 | /// call this instance's method for a given log entry. If
31 | /// the value of this property is higher than a log entry's level, then this log
32 | /// provider is skipped.
33 | ///
34 | LogLevel Level { get; }
35 |
36 | ///
37 | /// Writes the specified log entry.
38 | ///
39 | /// The log entry to write.
40 | /// The to observe.
41 | /// A task that completes when the log entry has been written.
42 | Task WriteAsync(LogEntry logEntry, CancellationToken cancellationToken);
43 | }
--------------------------------------------------------------------------------
/RockLib.Logging/LogProviders/RolloverPeriod.cs:
--------------------------------------------------------------------------------
1 | namespace RockLib.Logging;
2 |
3 | ///
4 | /// Defines the various rollover periods.
5 | ///
6 | public enum RolloverPeriod
7 | {
8 | ///
9 | /// The rolling file log provider should never archive logs on a periodic basis.
10 | ///
11 | Never,
12 |
13 | ///
14 | /// The rolling file provider should archive logs daily.
15 | ///
16 | Daily,
17 |
18 | ///
19 | /// The rolling file provider should archive logs hourly.
20 | ///
21 | Hourly
22 | }
--------------------------------------------------------------------------------
/RockLib.Logging/RockLib.Logging.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Embedded
4 | A simple logging library.
5 | True
6 | True
7 | icon.png
8 | RockLib.Logging
9 | LICENSE.md
10 | https://github.com/RockLib/RockLib.Logging
11 | 6.0.0
12 | A changelog is available at https://github.com/RockLib/RockLib.Logging/blob/main/RockLib.Logging/CHANGELOG.md.
13 | false
14 | RockLib Logging Logger
15 | True
16 | 6.0.0
17 |
18 |
19 | bin\$(Configuration)\$(TargetFramework)\$(PackageId).xml
20 |
21 |
22 | true
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/RockLib.Logging/RockLib.Logging.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.30907.101
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RockLib.Logging", "RockLib.Logging.csproj", "{0F6F91D3-E6A9-4694-9598-D0CAC6B115B3}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RockLib.Logging.Tests", "..\Tests\RockLib.Logging.Tests\RockLib.Logging.Tests.csproj", "{280C39C8-EB1D-4DE2-A24F-1462F77FF0C7}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {0F6F91D3-E6A9-4694-9598-D0CAC6B115B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {0F6F91D3-E6A9-4694-9598-D0CAC6B115B3}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {0F6F91D3-E6A9-4694-9598-D0CAC6B115B3}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {0F6F91D3-E6A9-4694-9598-D0CAC6B115B3}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {280C39C8-EB1D-4DE2-A24F-1462F77FF0C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {280C39C8-EB1D-4DE2-A24F-1462F77FF0C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {280C39C8-EB1D-4DE2-A24F-1462F77FF0C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {280C39C8-EB1D-4DE2-A24F-1462F77FF0C7}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {806D89B7-78D1-4038-944B-AFFE3C7A0F00}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/RockLib.Logging/SafeLogging/NotSafeToLogAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq.Expressions;
3 | using System.Reflection;
4 |
5 | namespace RockLib.Logging.SafeLogging;
6 |
7 | ///
8 | /// An attribute that signifies a property as not safe for logging. This should be used when a class is
9 | /// marked with the .
10 | ///
11 | [AttributeUsage(AttributeTargets.Property)]
12 | public sealed class NotSafeToLogAttribute : Attribute
13 | {
14 | ///
15 | /// Decorate the specified property with the .
16 | ///
17 | /// The property to decorate.
18 | public static void Decorate(PropertyInfo property)
19 | {
20 | #if NET6_0_OR_GREATER
21 | ArgumentNullException.ThrowIfNull(property);
22 | #else
23 | if (property is null)
24 | {
25 | throw new ArgumentNullException(nameof(property));
26 | }
27 | #endif
28 |
29 | SanitizeEngine.NotSafeProperties.Add(property);
30 | }
31 |
32 | ///
33 | /// Decorate the property specified by the expression with the .
34 | ///
35 | /// The type of the property to decorate.
36 | /// An expression that defines the property to decorate.
37 | public static void Decorate(Expression> expression)
38 | {
39 | #if NET6_0_OR_GREATER
40 | ArgumentNullException.ThrowIfNull(expression);
41 | #else
42 | if (expression is null)
43 | {
44 | throw new ArgumentNullException(nameof(expression));
45 | }
46 | #endif
47 | if (expression.Body is MemberExpression memberExpression
48 | && memberExpression.Expression == expression.Parameters[0]
49 | && memberExpression.Member is PropertyInfo property)
50 | {
51 | Decorate(property);
52 | }
53 | else
54 | {
55 | throw new ArgumentException($"Expression does not define a property of type {typeof(T).Name}.", nameof(expression));
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/RockLib.Logging/SafeLogging/SafeToLogAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq.Expressions;
3 | using System.Reflection;
4 |
5 | namespace RockLib.Logging.SafeLogging;
6 |
7 | ///
8 | /// An attribute that signifies a property or all the properties of a class are safe to add as extended properties
9 | /// in a log. When used with a class, use the to mark specific properites
10 | /// that should not be logged.
11 | ///
12 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property)]
13 | public sealed class SafeToLogAttribute : Attribute
14 | {
15 | ///
16 | /// Decorate the specified type with the .
17 | ///
18 | /// The type to decorate.
19 | public static void Decorate(Type type)
20 | {
21 | #if NET6_0_OR_GREATER
22 | ArgumentNullException.ThrowIfNull(type);
23 | #else
24 | if (type is null)
25 | {
26 | throw new ArgumentNullException(nameof(type));
27 | }
28 | #endif
29 | SanitizeEngine.SafeTypes.Add(type);
30 | }
31 |
32 | ///
33 | /// Decorate the type of with the .
34 | ///
35 | /// The type to decorate.
36 | public static void Decorate() => Decorate(typeof(T));
37 |
38 | ///
39 | /// Decorate the specified property with the .
40 | ///
41 | /// The property to decorate.
42 | public static void Decorate(PropertyInfo property)
43 | {
44 | #if NET6_0_OR_GREATER
45 | ArgumentNullException.ThrowIfNull(property);
46 | #else
47 | if (property is null)
48 | {
49 | throw new ArgumentNullException(nameof(property));
50 | }
51 | #endif
52 | SanitizeEngine.SafeProperties.Add(property);
53 | }
54 |
55 | ///
56 | /// Decorate the property specified by the expression with the .
57 | ///
58 | /// The type of the property to decorate.
59 | /// An expression that defines the property to decorate.
60 | public static void Decorate(Expression> expression)
61 | {
62 | #if NET6_0_OR_GREATER
63 | ArgumentNullException.ThrowIfNull(expression);
64 | #else
65 | if (expression is null)
66 | {
67 | throw new ArgumentNullException(nameof(expression));
68 | }
69 | #endif
70 |
71 | if (expression.Body is MemberExpression memberExpression
72 | && memberExpression.Expression == expression.Parameters[0]
73 | && memberExpression.Member is PropertyInfo property)
74 | {
75 | Decorate(property);
76 | }
77 | else
78 | {
79 | throw new ArgumentException($"Expression does not define a property of type {typeof(T).Name}.", nameof(expression));
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/Tests/RockLib.Logging.AspNetCore.Tests/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 |
6 | [*.cs]
7 | # Styling
8 | indent_style = space
9 | indent_size = 4
10 | csharp_indent_case_contents = true
11 | csharp_indent_switch_labels = true
12 | csharp_new_line_before_catch = true
13 | csharp_new_line_before_else = true
14 | csharp_new_line_before_finally = true
15 | csharp_new_line_before_members_in_anonymous_types = false
16 | csharp_new_line_before_members_in_object_initializers = false
17 | csharp_new_line_before_open_brace = methods, control_blocks, types, properties, lambdas, accessors, object_collection_array_initializers
18 | csharp_new_line_between_query_expression_clauses = true
19 | csharp_prefer_braces = false:suggestion
20 | csharp_prefer_simple_default_expression = true:suggestion
21 | csharp_preferred_modifier_order = public,private,internal,protected,static,readonly,async,override,sealed:suggestion
22 | csharp_preserve_single_line_blocks = true
23 | csharp_preserve_single_line_statements = true
24 | csharp_space_after_cast = false
25 | csharp_space_after_colon_in_inheritance_clause = true
26 | csharp_space_after_keywords_in_control_flow_statements = true
27 | csharp_space_before_colon_in_inheritance_clause = true
28 | csharp_space_between_method_call_empty_parameter_list_parentheses = false
29 | csharp_space_between_method_call_name_and_opening_parenthesis = false
30 | csharp_space_between_method_call_parameter_list_parentheses = false
31 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
32 | csharp_space_between_method_declaration_parameter_list_parentheses = false
33 | csharp_style_pattern_matching_over_as_with_null_check = true:error
34 | csharp_style_expression_bodied_accessors = true:error
35 | csharp_style_expression_bodied_constructors = true:error
36 | csharp_style_expression_bodied_methods = true:error
37 | csharp_style_expression_bodied_properties = true:error
38 | csharp_style_namespace_declarations = file_scoped:error
39 | csharp_style_inlined_variable_declaration = true:error
40 | csharp_style_var_elsewhere = true:error
41 | csharp_style_var_for_built_in_types = true:error
42 | csharp_style_var_when_type_is_apparent = true:error
43 | dotnet_sort_system_directives_first = false
44 | dotnet_style_explicit_tuple_names = true:suggestion
45 | dotnet_style_object_initializer = true:suggestion
46 | csharp_style_pattern_local_over_anonymous_function = false:error
47 | dotnet_style_predefined_type_for_member_access = true:error
48 | dotnet_style_prefer_inferred_anonymous_type_member_names = false:error
49 | dotnet_style_prefer_inferred_tuple_names = true:error
50 | dotnet_style_predefined_type_for_locals_parameters_members = true:error
51 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:error
52 | dotnet_style_qualification_for_field = false:error
53 | dotnet_style_qualification_for_method = false:error
54 | dotnet_style_qualification_for_property = false:error
55 |
56 | # Analyzer Configuration
57 | # These are rules we want to either ignore or have set as suggestion or info
58 |
59 | # CA1014: Mark assemblies with CLSCompliant
60 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1014
61 | dotnet_diagnostic.CA1014.severity = none
62 |
63 | # CA1725: Parameter names should match base declaration
64 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1725
65 | dotnet_diagnostic.CA1725.severity = suggestion
66 |
67 | # CA2227: Collection properties should be read only
68 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2227
69 | dotnet_diagnostic.CA2227.severity = suggestion
70 |
--------------------------------------------------------------------------------
/Tests/RockLib.Logging.AspNetCore.Tests/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 | all
7 | Rocket Mortgage
8 | Copyright 2025 (c) Rocket Mortgage. All rights reserved.
9 | latest
10 | enable
11 | net48;net8.0
12 | NU1603,NU1701
13 | true
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Tests/RockLib.Logging.AspNetCore.Tests/InfoLogAttributeTests.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using Xunit;
3 |
4 | namespace RockLib.Logging.AspNetCore.Tests;
5 |
6 | using static LoggingActionFilterAttribute;
7 | using static Logger;
8 |
9 | public class InfoLogAttributeTests
10 | {
11 | [Fact(DisplayName = "Constructor sets properties from non-null parameters")]
12 | public void ConstructorHappyPath1()
13 | {
14 | var messageFormat = "My message format: {0}.";
15 | var loggerName = "MyLogger";
16 |
17 | var infoLogAttribute = new InfoLogAttribute(messageFormat, loggerName);
18 |
19 | infoLogAttribute.MessageFormat.Should().Be(messageFormat);
20 | infoLogAttribute.LoggerName.Should().Be(loggerName);
21 | infoLogAttribute.LogLevel.Should().Be(LogLevel.Info);
22 | }
23 |
24 | [Fact(DisplayName = "Constructor sets properties from null parameters")]
25 | public void ConstructorHappyPath2()
26 | {
27 | var infoLogAttribute = new InfoLogAttribute(null, null);
28 |
29 | infoLogAttribute.MessageFormat.Should().Be(DefaultMessageFormat);
30 | infoLogAttribute.LoggerName.Should().Be(DefaultName);
31 | infoLogAttribute.LogLevel.Should().Be(LogLevel.Info);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Tests/RockLib.Logging.AspNetCore.Tests/LogBuilderExtensionTests.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using Microsoft.AspNetCore.Http;
3 | using Microsoft.Extensions.DependencyInjection;
4 | using Moq;
5 | using RockLib.Logging.DependencyInjection;
6 | using System;
7 | using System.Collections.Generic;
8 | using Xunit;
9 |
10 | namespace RockLib.Logging.AspNetCore.Tests;
11 |
12 | public class LogBuilderExtensionTests
13 | {
14 | [Fact(DisplayName = "AddHttpContextProvider adds HttpContextProvider")]
15 | public void AddHttpContextProviderExtension()
16 | {
17 | var contextMock = new Mock();
18 | var loggerBuilder = new TestLoggerBuilder();
19 |
20 | var serviceProvider = new ServiceCollection()
21 | .AddSingleton(contextMock.Object)
22 | .BuildServiceProvider();
23 |
24 | loggerBuilder.AddHttpContextProvider();
25 |
26 | var registration = loggerBuilder.ContextProviderRegistrations.Should().ContainSingle().Subject;
27 |
28 | var contextProvider = registration.Invoke(serviceProvider);
29 | contextProvider.Should().BeOfType();
30 | }
31 |
32 | private sealed class TestLoggerBuilder : ILoggerBuilder
33 | {
34 | public string LoggerName => Logger.DefaultName;
35 |
36 | public List> LogProviderRegistrations { get; } = new List>();
37 |
38 | public List> ContextProviderRegistrations { get; } = new List>();
39 |
40 | public ILoggerBuilder AddLogProvider(Func logProviderRegistration)
41 | {
42 | LogProviderRegistrations.Add(logProviderRegistration);
43 | return this;
44 | }
45 |
46 | public ILoggerBuilder AddContextProvider(Func contextProviderRegistration)
47 | {
48 | ContextProviderRegistrations.Add(contextProviderRegistration);
49 | return this;
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Tests/RockLib.Logging.AspNetCore.Tests/RockLib.Logging.AspNetCore.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | false
4 | NU1605,NU1608
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | all
15 | runtime; build; native; contentfiles; analyzers; buildtransitive
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Tests/RockLib.Logging.AspNetCore.Tests/RouteNotFoundMiddlewareExtensionsTests.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Threading.Tasks;
3 | using FluentAssertions;
4 | using Microsoft.AspNetCore.Builder;
5 | #if !NET5_0
6 | using Microsoft.AspNetCore.Builder.Internal;
7 | #endif
8 | using Microsoft.AspNetCore.Http;
9 | using Microsoft.Extensions.DependencyInjection;
10 | using Moq;
11 | using RockLib.Logging.DependencyInjection;
12 | using RockLib.Logging.Moq;
13 | using Xunit;
14 |
15 | namespace RockLib.Logging.AspNetCore.Tests;
16 |
17 | public class RouteNotFoundMiddlewareExtensionsTests
18 | {
19 | [Fact(DisplayName = "UseRouteNotFound adds the RouteNotFoundMiddleware to the pipline")]
20 | public async Task UseRouteNotFoundHappyPath()
21 | {
22 | var path = "/SomePathThing";
23 | var mockLogger = new MockLogger();
24 |
25 | var httpResponseMock = new Mock();
26 | httpResponseMock.Setup(hrm => hrm.StatusCode).Returns(404);
27 |
28 | var httpRequestMock = new Mock();
29 | httpRequestMock.Setup(requestMock => requestMock.Path).Returns(path);
30 |
31 | var httpContextMock = new Mock();
32 | httpContextMock.Setup(hcm => hcm.Response).Returns(httpResponseMock.Object);
33 | httpContextMock.Setup(hcm => hcm.Request).Returns(httpRequestMock.Object);
34 |
35 | var services = new ServiceCollection();
36 | services.AddSingleton(loggerName => mockLogger.Object);
37 |
38 | var applicationBuilder = new ApplicationBuilder(services.BuildServiceProvider());
39 |
40 | applicationBuilder.UseRouteNotFoundLogging();
41 | applicationBuilder.Use((c, f) => Task.CompletedTask);
42 |
43 | var pipeline = applicationBuilder.Build();
44 |
45 | await pipeline(httpContextMock.Object).ConfigureAwait(true);
46 |
47 | mockLogger.Invocations.Count.Should().Be(1);
48 |
49 | var extendedProperties = new Dictionary
50 | {
51 | [RouteNotFoundMiddleware.RouteExtendedPropertiesKey] = new PathString(path)
52 | };
53 |
54 | mockLogger.VerifyWarn(RouteNotFoundMiddleware.DefaultLogMessage, extendedProperties);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/Tests/RockLib.Logging.Microsoft.Extensions.Tests/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 |
6 | [*.cs]
7 | # Styling
8 | indent_style = space
9 | indent_size = 4
10 | csharp_indent_case_contents = true
11 | csharp_indent_switch_labels = true
12 | csharp_new_line_before_catch = true
13 | csharp_new_line_before_else = true
14 | csharp_new_line_before_finally = true
15 | csharp_new_line_before_members_in_anonymous_types = false
16 | csharp_new_line_before_members_in_object_initializers = false
17 | csharp_new_line_before_open_brace = methods, control_blocks, types, properties, lambdas, accessors, object_collection_array_initializers
18 | csharp_new_line_between_query_expression_clauses = true
19 | csharp_prefer_braces = false:suggestion
20 | csharp_prefer_simple_default_expression = true:suggestion
21 | csharp_preferred_modifier_order = public,private,internal,protected,static,readonly,async,override,sealed:suggestion
22 | csharp_preserve_single_line_blocks = true
23 | csharp_preserve_single_line_statements = true
24 | csharp_space_after_cast = false
25 | csharp_space_after_colon_in_inheritance_clause = true
26 | csharp_space_after_keywords_in_control_flow_statements = true
27 | csharp_space_before_colon_in_inheritance_clause = true
28 | csharp_space_between_method_call_empty_parameter_list_parentheses = false
29 | csharp_space_between_method_call_name_and_opening_parenthesis = false
30 | csharp_space_between_method_call_parameter_list_parentheses = false
31 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
32 | csharp_space_between_method_declaration_parameter_list_parentheses = false
33 | csharp_style_pattern_matching_over_as_with_null_check = true:error
34 | csharp_style_expression_bodied_accessors = true:error
35 | csharp_style_expression_bodied_constructors = true:error
36 | csharp_style_expression_bodied_methods = true:error
37 | csharp_style_expression_bodied_properties = true:error
38 | csharp_style_namespace_declarations = file_scoped:error
39 | csharp_style_inlined_variable_declaration = true:error
40 | csharp_style_var_elsewhere = true:error
41 | csharp_style_var_for_built_in_types = true:error
42 | csharp_style_var_when_type_is_apparent = true:error
43 | dotnet_sort_system_directives_first = false
44 | dotnet_style_explicit_tuple_names = true:suggestion
45 | dotnet_style_object_initializer = true:suggestion
46 | csharp_style_pattern_local_over_anonymous_function = false:error
47 | dotnet_style_predefined_type_for_member_access = true:error
48 | dotnet_style_prefer_inferred_anonymous_type_member_names = false:error
49 | dotnet_style_prefer_inferred_tuple_names = true:error
50 | dotnet_style_predefined_type_for_locals_parameters_members = true:error
51 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:error
52 | dotnet_style_qualification_for_field = false:error
53 | dotnet_style_qualification_for_method = false:error
54 | dotnet_style_qualification_for_property = false:error
55 |
56 | # Analyzer Configuration
57 | # These are rules we want to either ignore or have set as suggestion or info
58 |
59 | # CA1014: Mark assemblies with CLSCompliant
60 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1014
61 | dotnet_diagnostic.CA1014.severity = none
62 |
63 | # CA1725: Parameter names should match base declaration
64 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1725
65 | dotnet_diagnostic.CA1725.severity = suggestion
66 |
67 | # CA2227: Collection properties should be read only
68 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2227
69 | dotnet_diagnostic.CA2227.severity = suggestion
--------------------------------------------------------------------------------
/Tests/RockLib.Logging.Microsoft.Extensions.Tests/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 | all
7 | Rocket Mortgage
8 | Copyright 2025 (c) Rocket Mortgage. All rights reserved.
9 | latest
10 | enable
11 | net48;net8.0
12 | NU1603,NU1701
13 | true
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Tests/RockLib.Logging.Microsoft.Extensions.Tests/RockLib.Logging.Microsoft.Extensions.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | all
9 | runtime; build; native; contentfiles; analyzers; buildtransitive
10 |
11 |
12 | all
13 | runtime; build; native; contentfiles; analyzers; buildtransitive
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Tests/RockLib.Logging.Moq.Tests/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 |
6 | [*.cs]
7 | # Styling
8 | indent_style = space
9 | indent_size = 4
10 | csharp_indent_case_contents = true
11 | csharp_indent_switch_labels = true
12 | csharp_new_line_before_catch = true
13 | csharp_new_line_before_else = true
14 | csharp_new_line_before_finally = true
15 | csharp_new_line_before_members_in_anonymous_types = false
16 | csharp_new_line_before_members_in_object_initializers = false
17 | csharp_new_line_before_open_brace = methods, control_blocks, types, properties, lambdas, accessors, object_collection_array_initializers
18 | csharp_new_line_between_query_expression_clauses = true
19 | csharp_prefer_braces = false:suggestion
20 | csharp_prefer_simple_default_expression = true:suggestion
21 | csharp_preferred_modifier_order = public,private,internal,protected,static,readonly,async,override,sealed:suggestion
22 | csharp_preserve_single_line_blocks = true
23 | csharp_preserve_single_line_statements = true
24 | csharp_space_after_cast = false
25 | csharp_space_after_colon_in_inheritance_clause = true
26 | csharp_space_after_keywords_in_control_flow_statements = true
27 | csharp_space_before_colon_in_inheritance_clause = true
28 | csharp_space_between_method_call_empty_parameter_list_parentheses = false
29 | csharp_space_between_method_call_name_and_opening_parenthesis = false
30 | csharp_space_between_method_call_parameter_list_parentheses = false
31 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
32 | csharp_space_between_method_declaration_parameter_list_parentheses = false
33 | csharp_style_pattern_matching_over_as_with_null_check = true:error
34 | csharp_style_expression_bodied_accessors = true:error
35 | csharp_style_expression_bodied_constructors = true:error
36 | csharp_style_expression_bodied_methods = true:error
37 | csharp_style_expression_bodied_properties = true:error
38 | csharp_style_namespace_declarations = file_scoped:error
39 | csharp_style_inlined_variable_declaration = true:error
40 | csharp_style_var_elsewhere = true:error
41 | csharp_style_var_for_built_in_types = true:error
42 | csharp_style_var_when_type_is_apparent = true:error
43 | dotnet_sort_system_directives_first = false
44 | dotnet_style_explicit_tuple_names = true:suggestion
45 | dotnet_style_object_initializer = true:suggestion
46 | csharp_style_pattern_local_over_anonymous_function = false:error
47 | dotnet_style_predefined_type_for_member_access = true:error
48 | dotnet_style_prefer_inferred_anonymous_type_member_names = false:error
49 | dotnet_style_prefer_inferred_tuple_names = true:error
50 | dotnet_style_predefined_type_for_locals_parameters_members = true:error
51 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:error
52 | dotnet_style_qualification_for_field = false:error
53 | dotnet_style_qualification_for_method = false:error
54 | dotnet_style_qualification_for_property = false:error
55 |
56 | # Analyzer Configuration
57 | # These are rules we want to either ignore or have set as suggestion or info
58 |
59 | # CA1014: Mark assemblies with CLSCompliant
60 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1014
61 | dotnet_diagnostic.CA1014.severity = none
62 |
63 | # CA1725: Parameter names should match base declaration
64 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1725
65 | dotnet_diagnostic.CA1725.severity = suggestion
66 |
67 | # CA2227: Collection properties should be read only
68 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2227
69 | dotnet_diagnostic.CA2227.severity = suggestion
--------------------------------------------------------------------------------
/Tests/RockLib.Logging.Moq.Tests/AssemblyInformation.cs:
--------------------------------------------------------------------------------
1 | using Xunit;
2 |
3 | [assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly)]
--------------------------------------------------------------------------------
/Tests/RockLib.Logging.Moq.Tests/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 | all
7 | Rocket Mortgage
8 | Copyright 2025 (c) Rocket Mortgage. All rights reserved.
9 | latest
10 | enable
11 | net48;net8.0
12 | NU1603,NU1701
13 | true
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Tests/RockLib.Logging.Moq.Tests/MockLoggerTests.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using Moq;
3 | using Xunit;
4 |
5 | namespace RockLib.Logging.Moq.Tests;
6 |
7 | public class MockLoggerTests
8 | {
9 | [Fact(DisplayName = "Constructor adds setups for Level and Name properties")]
10 | public void ConstructorHappyPath()
11 | {
12 | var mockLogger = new MockLogger(LogLevel.Info, "TestLogger", MockBehavior.Strict);
13 |
14 | mockLogger.Setups.Should().HaveCount(2);
15 | mockLogger.Behavior.Should().Be(MockBehavior.Strict);
16 | mockLogger.Object.Level.Should().Be(LogLevel.Info);
17 | mockLogger.Object.Name.Should().Be("TestLogger");
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Tests/RockLib.Logging.Moq.Tests/RockLib.Logging.Moq.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | false
4 |
5 |
6 |
7 |
8 |
9 |
10 | all
11 | runtime; build; native; contentfiles; analyzers; buildtransitive
12 |
13 |
14 | all
15 | runtime; build; native; contentfiles; analyzers; buildtransitive
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/Tests/RockLib.Logging.Tests/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 |
6 | [*.cs]
7 | # Styling
8 | indent_style = space
9 | indent_size = 4
10 | csharp_indent_case_contents = true
11 | csharp_indent_switch_labels = true
12 | csharp_new_line_before_catch = true
13 | csharp_new_line_before_else = true
14 | csharp_new_line_before_finally = true
15 | csharp_new_line_before_members_in_anonymous_types = false
16 | csharp_new_line_before_members_in_object_initializers = false
17 | csharp_new_line_before_open_brace = methods, control_blocks, types, properties, lambdas, accessors, object_collection_array_initializers
18 | csharp_new_line_between_query_expression_clauses = true
19 | csharp_prefer_braces = false:suggestion
20 | csharp_prefer_simple_default_expression = true:suggestion
21 | csharp_preferred_modifier_order = public,private,internal,protected,static,readonly,async,override,sealed:suggestion
22 | csharp_preserve_single_line_blocks = true
23 | csharp_preserve_single_line_statements = true
24 | csharp_space_after_cast = false
25 | csharp_space_after_colon_in_inheritance_clause = true
26 | csharp_space_after_keywords_in_control_flow_statements = true
27 | csharp_space_before_colon_in_inheritance_clause = true
28 | csharp_space_between_method_call_empty_parameter_list_parentheses = false
29 | csharp_space_between_method_call_name_and_opening_parenthesis = false
30 | csharp_space_between_method_call_parameter_list_parentheses = false
31 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
32 | csharp_space_between_method_declaration_parameter_list_parentheses = false
33 | csharp_style_pattern_matching_over_as_with_null_check = true:error
34 | csharp_style_expression_bodied_accessors = true:error
35 | csharp_style_expression_bodied_constructors = true:error
36 | csharp_style_expression_bodied_methods = true:error
37 | csharp_style_expression_bodied_properties = true:error
38 | csharp_style_namespace_declarations = file_scoped:error
39 | csharp_style_inlined_variable_declaration = true:error
40 | csharp_style_var_elsewhere = true:error
41 | csharp_style_var_for_built_in_types = true:error
42 | csharp_style_var_when_type_is_apparent = true:error
43 | dotnet_sort_system_directives_first = false
44 | dotnet_style_explicit_tuple_names = true:suggestion
45 | dotnet_style_object_initializer = true:suggestion
46 | csharp_style_pattern_local_over_anonymous_function = false:error
47 | dotnet_style_predefined_type_for_member_access = true:error
48 | dotnet_style_prefer_inferred_anonymous_type_member_names = false:error
49 | dotnet_style_prefer_inferred_tuple_names = true:error
50 | dotnet_style_predefined_type_for_locals_parameters_members = true:error
51 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:error
52 | dotnet_style_qualification_for_field = false:error
53 | dotnet_style_qualification_for_method = false:error
54 | dotnet_style_qualification_for_property = false:error
55 |
56 | # Analyzer Configuration
57 | # These are rules we want to either ignore or have set as suggestion or info
58 |
59 | # CA1014: Mark assemblies with CLSCompliant
60 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1014
61 | dotnet_diagnostic.CA1014.severity = none
62 |
63 | # CA1725: Parameter names should match base declaration
64 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1725
65 | dotnet_diagnostic.CA1725.severity = suggestion
66 |
67 | # CA2227: Collection properties should be read only
68 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2227
69 | dotnet_diagnostic.CA2227.severity = suggestion
70 |
--------------------------------------------------------------------------------
/Tests/RockLib.Logging.Tests/DelegateErrorHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using RockLib.Dynamic;
3 |
4 | namespace RockLib.Logging.Tests;
5 |
6 | internal static class DelegateErrorHandler
7 | {
8 | private const string _delegateErrorHandlerTypeName = "RockLib.Logging.ErrorHandlerExtensions+DelegateErrorHandler, RockLib.Logging";
9 | private static readonly Type _delegateErrorHandlerType = Type.GetType(_delegateErrorHandlerTypeName, true)!;
10 |
11 | public static IErrorHandler New(Action handleError) => _delegateErrorHandlerType.New(handleError);
12 | }
13 |
--------------------------------------------------------------------------------
/Tests/RockLib.Logging.Tests/DependencyInjection/Options/FileLogProviderOptionsTests.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using RockLib.Logging.DependencyInjection;
3 | using System;
4 | using Xunit;
5 |
6 | namespace RockLib.Logging.Tests.DependencyInjection;
7 |
8 | public static class FileLogProviderOptionsTests
9 | {
10 | [Fact(DisplayName = "File property throws when set to null")]
11 | public static void FilePropertySetToNull()
12 | {
13 | Action act = () => new FileLogProviderOptions().File = null!;
14 |
15 | act.Should().ThrowExactly().WithMessage("*value*");
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Tests/RockLib.Logging.Tests/DependencyInjection/Options/FormattableLogProviderOptionsTests.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using Microsoft.Extensions.DependencyInjection;
3 | using Moq;
4 | using RockLib.Logging.DependencyInjection;
5 | using System;
6 | using Xunit;
7 |
8 | namespace RockLib.Logging.Tests.DependencyInjection;
9 |
10 | public static class FormattableLogProviderOptionsTests
11 | {
12 | private static readonly IServiceProvider _emptyServiceProvider = new ServiceCollection().BuildServiceProvider();
13 |
14 | [Fact(DisplayName = "SetTemplate method sets FormatterRegistration property to TemplateLogFormatter with specified template")]
15 | public static void SetTemplateMethod()
16 | {
17 | var options = new TestFormattableLogProviderOptions();
18 |
19 | options.SetTemplate("foo");
20 |
21 | var formatter = options.FormatterRegistration!.Invoke(_emptyServiceProvider);
22 |
23 | formatter.Should().BeOfType()
24 | .Which.Template.Should().Be("foo");
25 | }
26 |
27 | [Fact(DisplayName = "SetTemplate method throws when template parameter is null")]
28 | public static void SetTemplateMethodWhenTemplateParameterIsNull()
29 | {
30 | var options = new TestFormattableLogProviderOptions();
31 |
32 | Action act = () => options.SetTemplate(null!);
33 |
34 | act.Should().ThrowExactly().WithMessage("*template*");
35 | }
36 |
37 | [Fact(DisplayName = "SetFormatter method 1 sets FormatterRegistration property to formatter")]
38 | public static void SetFormatterMethod()
39 | {
40 | var formatter = new Mock().Object;
41 |
42 | var options = new TestFormattableLogProviderOptions();
43 |
44 | options.SetFormatter(formatter);
45 |
46 | var actualFormatter = options.FormatterRegistration!.Invoke(_emptyServiceProvider);
47 |
48 | actualFormatter.Should().BeSameAs(formatter);
49 | }
50 |
51 | [Fact(DisplayName = "SetFormatter method 1 throws when formatter parameter is null")]
52 | public static void SetFormatterMethodWhenFormatterIsNull()
53 | {
54 | var options = new TestFormattableLogProviderOptions();
55 |
56 | Action act = () => options.SetFormatter(null!);
57 |
58 | act.Should().ThrowExactly().WithMessage("*formatter*");
59 | }
60 |
61 | [Fact(DisplayName = "SetFormatter method 2 sets FormatterRegistration property to specified formatter")]
62 | public static void SetFormatterMethodToSpecificFormatter()
63 | {
64 | var options = new TestFormattableLogProviderOptions();
65 |
66 | options.SetFormatter(123);
67 |
68 | var formatter = options.FormatterRegistration!.Invoke(_emptyServiceProvider);
69 |
70 | formatter.Should().BeOfType()
71 | .Which.Foo.Should().Be(123);
72 | }
73 |
74 | private sealed class TestFormattableLogProviderOptions : FormattableLogProviderOptions
75 | {
76 | }
77 |
78 | #pragma warning disable CA1812
79 | private sealed class TestLogFormatter : ILogFormatter
80 | {
81 | public TestLogFormatter(int foo) => Foo = foo;
82 |
83 | public int Foo { get; }
84 |
85 | public string Format(LogEntry logEntry) => throw new NotImplementedException();
86 | }
87 | #pragma warning restore CA1812
88 | }
89 |
--------------------------------------------------------------------------------
/Tests/RockLib.Logging.Tests/DependencyInjection/Options/RollingFileLogProviderOptionsTests.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using RockLib.Logging.DependencyInjection;
3 | using Xunit;
4 |
5 | namespace RockLib.Logging.Tests.DependencyInjection;
6 |
7 | public static class RollingFileLogProviderOptionsTests
8 | {
9 | [Fact(DisplayName = "RollingFileLogProviderOptions has the correct default property values")]
10 | public static void DefaultValues()
11 | {
12 | var options = new RollingFileLogProviderOptions();
13 |
14 | options.MaxFileSizeKilobytes.Should().Be(RollingFileLogProvider.DefaultMaxFileSizeKilobytes);
15 | options.MaxArchiveCount.Should().Be(RollingFileLogProvider.DefaultMaxArchiveCount);
16 | options.RolloverPeriod.Should().Be(RollingFileLogProvider.DefaultRolloverPeriod);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Tests/RockLib.Logging.Tests/DependencyInjection/TestConfigurationProvider.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Configuration;
2 |
3 | namespace RockLib.Logging.Tests.DependencyInjection;
4 |
5 | public class TestConfigurationProvider : ConfigurationProvider
6 | {
7 | public void Reload() => OnReload();
8 | }
9 |
--------------------------------------------------------------------------------
/Tests/RockLib.Logging.Tests/DependencyInjection/TestConfigurationSource.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Configuration;
2 |
3 | namespace RockLib.Logging.Tests.DependencyInjection;
4 |
5 | public class TestConfigurationSource : IConfigurationSource
6 | {
7 | public TestConfigurationProvider Provider { get; } = new TestConfigurationProvider();
8 |
9 | public IConfigurationProvider Build(IConfigurationBuilder builder) => Provider;
10 | }
11 |
--------------------------------------------------------------------------------
/Tests/RockLib.Logging.Tests/Diagnostics/LoggingTraceListenerTests.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using Moq;
3 | using RockLib.Logging.Diagnostics;
4 | using RockLib.Logging.Moq;
5 | using System;
6 | using Xunit;
7 |
8 | namespace RockLib.Logging.Tests.Diagnostics;
9 |
10 | public static class LoggingTraceListenerTests
11 | {
12 | [Fact(DisplayName = "Constructor sets properties")]
13 | public static void Create()
14 | {
15 | var logger = new Mock().Object;
16 |
17 | using var listener = new LoggingTraceListener(logger, LogLevel.Info);
18 |
19 | listener.Logger.Should().BeSameAs(logger);
20 | listener.LogLevel.Should().Be(LogLevel.Info);
21 | }
22 |
23 | [Fact(DisplayName = "Constructor sets LogLevel to logger's level if not provided")]
24 | public static void CreateWithLevelSetFromLogger()
25 | {
26 | var logger = new MockLogger(LogLevel.Info).Object;
27 |
28 | using var listener = new LoggingTraceListener(logger);
29 |
30 | listener.Logger.Should().BeSameAs(logger);
31 | listener.LogLevel.Should().Be(LogLevel.Info);
32 | }
33 |
34 | [Fact(DisplayName = "Constructor sets LogLevel to logger's level if null")]
35 | public static void CreateWhenLevelIsNull()
36 | {
37 | var logger = new MockLogger(LogLevel.Info).Object;
38 |
39 | using var listener = new LoggingTraceListener(logger, null);
40 |
41 | listener.Logger.Should().BeSameAs(logger);
42 | listener.LogLevel.Should().Be(LogLevel.Info);
43 | }
44 |
45 | [Fact(DisplayName = "Constructor throws if logger is null")]
46 | public static void CreateWhenLoggerIsNull()
47 | {
48 | var act = () => new LoggingTraceListener((null as ILogger)!, LogLevel.Info);
49 |
50 | act.Should().ThrowExactly().WithMessage("*logger*");
51 | }
52 |
53 | [Fact(DisplayName = "Write logs at the correct level")]
54 | public static void Write()
55 | {
56 | var mockLogger = new MockLogger();
57 |
58 | using var listener = new LoggingTraceListener(mockLogger.Object, LogLevel.Info);
59 |
60 | listener.Write("Hello, world!");
61 |
62 | mockLogger.VerifyInfo("Hello, world!", Times.Once());
63 | }
64 |
65 | [Fact(DisplayName = "Write does not log if the level is not high enough for the logger")]
66 | public static void WriteWhenLogLevelIsNotHighEnough()
67 | {
68 | var mockLogger = new MockLogger(LogLevel.Warn);
69 |
70 | using var listener = new LoggingTraceListener(mockLogger.Object, LogLevel.Info);
71 |
72 | listener.Write("Hello, world!");
73 |
74 | mockLogger.VerifyInfo(Times.Never());
75 | }
76 |
77 | [Fact(DisplayName = "WriteLine logs at the correct level")]
78 | public static void WriteLineWithLogLevelEqual()
79 | {
80 | var mockLogger = new MockLogger();
81 |
82 | using var listener = new LoggingTraceListener(mockLogger.Object, LogLevel.Info);
83 |
84 | listener.WriteLine("Hello, world!");
85 |
86 | mockLogger.VerifyInfo("Hello, world!", Times.Once());
87 | }
88 |
89 | [Fact(DisplayName = "WriteLine does not log if the level is not high enough for the logger")]
90 | public static void WriteLineWhenLogLevelIsNotHighEnough()
91 | {
92 | var mockLogger = new MockLogger(LogLevel.Warn);
93 |
94 | using var listener = new LoggingTraceListener(mockLogger.Object, LogLevel.Info);
95 |
96 | listener.WriteLine("Hello, world!");
97 |
98 | mockLogger.VerifyInfo(Times.Never());
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/Tests/RockLib.Logging.Tests/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 | all
7 | Rocket Mortgage
8 | Copyright 2025 (c) Rocket Mortgage. All rights reserved.
9 | latest
10 | enable
11 | net48;net8.0
12 | NU1603,NU1701
13 | true
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Tests/RockLib.Logging.Tests/LogProcessingTests/FakeLogProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 |
5 | namespace RockLib.Logging.Tests.LogProcessingTests;
6 |
7 | public class FakeLogProvider : ILogProvider
8 | {
9 | public TimeSpan Timeout => TimeSpan.FromSeconds(5);
10 |
11 | public LogLevel Level => LogLevel.NotSet;
12 |
13 | #pragma warning disable 1998
14 | public async Task WriteAsync(LogEntry logEntry, CancellationToken cancellationToken) => throw new NotSupportedException("oh, no.");
15 | #pragma warning restore 1998
16 | }
17 |
--------------------------------------------------------------------------------
/Tests/RockLib.Logging.Tests/LogProcessingTests/FireAndForgetLogProcessorTests.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using Moq;
3 | using RockLib.Dynamic;
4 | using RockLib.Logging.LogProcessing;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 | using Xunit;
8 |
9 | namespace RockLib.Logging.Tests.LogProcessingTests;
10 |
11 | public static class FireAndForgetLogProcessorTests
12 | {
13 | [Fact]
14 | public static void ProcessLogEntryCallsWriteAsyncOnTheLogProvider()
15 | {
16 | #pragma warning disable CS0618 // Type or member is obsolete
17 | using var logProcessor = new FireAndForgetLogProcessor();
18 | #pragma warning restore CS0618 // Type or member is obsolete
19 |
20 | var mockLogProvider = new Mock();
21 | var logEntry = new LogEntry();
22 |
23 | mockLogProvider.Setup(m => m.WriteAsync(It.IsAny(), It.IsAny())).Returns(Task.FromResult(0));
24 |
25 | logProcessor.Unlock().SendToLogProvider(mockLogProvider.Object, logEntry, null, 1);
26 |
27 | mockLogProvider.Verify(m => m.WriteAsync(logEntry, It.IsAny()), Times.Once);
28 | }
29 |
30 | [Fact]
31 | public static void IfWriteAsyncThrowsWhileAwaitingHandleErrorIsCalled()
32 | {
33 | #pragma warning disable CS0618 // Type or member is obsolete
34 | using var logProcessor = new FireAndForgetLogProcessor();
35 | #pragma warning restore CS0618 // Type or member is obsolete
36 |
37 | var logProvider = new FakeLogProvider();
38 | var logEntry = new LogEntry();
39 |
40 | Error? capturedError = null;
41 | using var waitHandle = new AutoResetEvent(false);
42 |
43 | var errorHandler = DelegateErrorHandler.New(error =>
44 | {
45 | capturedError = error;
46 | waitHandle.Set();
47 | });
48 |
49 | logProcessor.Unlock().SendToLogProvider(logProvider, logEntry, errorHandler, 1);
50 |
51 | waitHandle.WaitOne(10000).Should().BeTrue();
52 |
53 | capturedError.Should().NotBeNull();
54 | capturedError!.Exception!.Message.Should().Be("oh, no.");
55 | capturedError.LogProvider.Should().BeSameAs(logProvider);
56 | capturedError.LogEntry.Should().BeSameAs(logEntry);
57 | capturedError.FailureCount.Should().Be(2);
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/Tests/RockLib.Logging.Tests/LogProcessingTests/SynchronousLogProcessorTests.cs:
--------------------------------------------------------------------------------
1 | using Moq;
2 | using RockLib.Dynamic;
3 | using RockLib.Logging.LogProcessing;
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 | using Xunit;
7 |
8 | namespace RockLib.Logging.Tests.LogProcessingTests;
9 |
10 | public static class SynchronousLogProcessorTests
11 | {
12 | [Fact]
13 | public static void ProcessLogEntryCallsWriteAsyncOnTheLogProvider()
14 | {
15 | #pragma warning disable CS0618 // Type or member is obsolete
16 | using var logProcessor = new SynchronousLogProcessor();
17 | #pragma warning restore CS0618 // Type or member is obsolete
18 |
19 | var mockLogProvider = new Mock();
20 | var logEntry = new LogEntry();
21 |
22 | mockLogProvider.Setup(m => m.WriteAsync(It.IsAny(), It.IsAny())).Returns(Task.FromResult(0));
23 |
24 | logProcessor.Unlock().SendToLogProvider(mockLogProvider.Object, logEntry, null, 1);
25 |
26 | mockLogProvider.Verify(m => m.WriteAsync(logEntry, It.IsAny()), Times.Once);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Tests/RockLib.Logging.Tests/NullErrorHandler.cs:
--------------------------------------------------------------------------------
1 | using RockLib.Dynamic;
2 | using System;
3 |
4 | namespace RockLib.Logging.Tests;
5 |
6 | internal static class NullErrorHandler
7 | {
8 | private const string _nullErrorHandlerTypeName = "RockLib.Logging.LogProcessing.LogProcessor+NullErrorHandler, RockLib.Logging";
9 |
10 | public static readonly IErrorHandler Instance =
11 | UniversalMemberAccessor.GetStatic(Type.GetType(_nullErrorHandlerTypeName, true)!).Instance;
12 | }
13 |
--------------------------------------------------------------------------------
/Tests/RockLib.Logging.Tests/RockLib.Logging.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | false
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | all
13 | runtime; build; native; contentfiles; analyzers; buildtransitive
14 |
15 |
16 | all
17 | runtime; build; native; contentfiles; analyzers; buildtransitive
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | PreserveNewest
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/Tests/RockLib.Logging.Tests/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "rocklib.logging": {
3 | "Name": "TestLogger",
4 | "Level": "Info",
5 | "LogProviders": {
6 | "type": "RockLib.Logging.ConsoleLogProvider, RockLib.Logging",
7 | "value": {
8 | "template": "foo bar",
9 | "level": "Warn"
10 | }
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/docs/ContextProviders.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 13
3 | ---
4 |
5 | # Context Providers
6 |
7 | By implementing the `RockLib.Logging.IContextProvider` interface, defining the `AddContext` method, and providing it to a `Logger`, you are able to automatically modify a `LogEntry` whenever it is written to its destination.
8 |
9 | The following implementation of `IContextProvider` will add the version of the operating system (OS) under which the application is running as an extended property to any `LogEntry` emitted by the logger:
10 |
11 | ```csharp
12 | public class OsContextProvider : IContextProvider
13 | {
14 | public void AddContext(LogEntry logEntry)
15 | {
16 | logEntry.SetExtendedProperties(new {OSVersion = System.Environment.OSVersion.VersionString});
17 | }
18 | }
19 | ```
20 |
21 | Context providers are passed to the `Logger` upon instantiation via an array of type `IContextProvider`. For example, the following code will provide a new instance of the above context provider to a new `Logger`:
22 |
23 | ```csharp
24 | var logger = new Logger(contextProviders: new [] { new OsContextProvider() });
25 | ```
26 |
27 | Alternatively, context providers can be assigned to a `Logger` by means of configuration. In the following example, we will assign our context provider to the default `Logger`:
28 |
29 | ```csharp
30 | {
31 | "RockLib.Logging": {
32 | "LogProviders": {
33 | "Type": "RockLib.Logging.ConsoleLogProvider, RockLib.Logging"
34 | },
35 | "ContextProviders": {
36 | "Type": "MyProject.OsContextProvider, MyProject"
37 | }
38 | }
39 | }
40 | ```
41 |
--------------------------------------------------------------------------------
/docs/Formatting.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 16
3 | ---
4 |
5 | # Formatting Logs
6 |
7 | By implementing the `RockLib.Logging.ILogFormatter` interface, defining the `Format` method, and providing it to a log provider (such as a `FileLogProvider`, a `ConsoleLogProvider` or a `RollingFileLogProvider`), you are able to transform a `LogEntry` object into a `string` emitted by the log provider.
8 |
9 | As an example, the following implementation of `ILogFormatter` will output `LogEntry.Message` followed by the Log `LogEntry.CreateTime`, with each separated by a hyphen.
10 |
11 | ```csharp
12 | public class SimpleMessageFormatter : ILogFormatter
13 | {
14 | public string Format(LogEntry logEntry)
15 | {
16 | // output Message - CreateTime (in ISO8601 format)
17 | return $"{logEntry.Message} - {logEntry.CreateTime.ToString("o")}";
18 | }
19 | }
20 | ```
21 |
22 | This would result in the following message being output by the consuming log provider when it is prompted to log:
23 | ```
24 | SAMPLE LOG MESSAGE - 2025-06-10T16:20:57.7876207Z
25 | ```
26 |
27 | Log formatters are passed to the Log provider via the constructor. For example, the following code will provide a new instance of the above message formatter to a new `FileLogProvider`:
28 |
29 | ```csharp
30 | var fileLogProvider = new FileLogProvider("log.txt", new SimpleMessageFormatter());
31 | ```
32 |
--------------------------------------------------------------------------------
/docs/GettingStarted.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 1
3 | ---
4 |
5 | # Getting Started
6 |
7 | In this tutorial, we will be building a console application with a hosted service that writes logs when started and stopped (the service doesn't actually do anything).
8 |
9 | ---
10 |
11 | Create a .NET Core console application named "LoggingTutorial".
12 |
13 | ---
14 |
15 | Add nuget references for "RockLib.Logging", "Microsoft.Extensions.DependencyInjection" and "Microsoft.Extensions.Hosting" to the project.
16 |
17 | ---
18 |
19 | ## Add a new class named 'ExampleService' to the project:
20 |
21 | ```csharp
22 | using Microsoft.Extensions.Hosting;
23 | using RockLib.Logging;
24 | using System.Threading;
25 | using System.Threading.Tasks;
26 |
27 | namespace LoggingTutorial
28 | {
29 | public class ExampleService : IHostedService
30 | {
31 | private readonly ILogger _logger;
32 |
33 | public ExampleService(ILogger logger)
34 | {
35 | _logger = logger;
36 | }
37 |
38 | public Task StartAsync(CancellationToken cancellationToken)
39 | {
40 | _logger.Info("Starting service...");
41 | return Task.CompletedTask;
42 | }
43 |
44 | public Task StopAsync(CancellationToken cancellationToken)
45 | {
46 | _logger.Info("Stopping service...");
47 | return Task.CompletedTask;
48 | }
49 | }
50 | }
51 | ```
52 |
53 | ---
54 |
55 | ## Edit the _Program.cs_ file as follows:
56 |
57 | ```csharp
58 | using Microsoft.Extensions.DependencyInjection;
59 | using Microsoft.Extensions.Hosting;
60 | using RockLib.Logging.DependencyInjection;
61 | using System.Threading.Tasks;
62 |
63 | namespace LoggingTutorial
64 | {
65 | class Program
66 | {
67 | static Task Main(string[] args)
68 | {
69 | return CreateHostBuilder(args)
70 | .RunConsoleAsync(options => options.SuppressStatusMessages = true);
71 | }
72 |
73 | static IHostBuilder CreateHostBuilder(string[] args)
74 | {
75 | return Host.CreateDefaultBuilder(args).ConfigureServices(services =>
76 | {
77 | services.AddLogger()
78 | .AddConsoleLogProvider(options => options.SetTemplate("[{createTime(O)}] {level} Log: {message}"));
79 |
80 | services.AddHostedService();
81 | });
82 | }
83 | }
84 | }
85 | ```
86 |
87 | ---
88 |
89 | ## Start the app
90 |
91 | It should output something similar to the following to console:
92 |
93 | ```
94 | [2020-07-17T15:33:58.9262051Z] Info Log: Starting service...
95 | ```
96 |
97 | ---
98 |
99 | ## Stop the app
100 |
101 | After hitting ` + c`, it should output this, then exit:
102 |
103 | ```
104 | [2020-07-17T15:35:38.5859970Z] Info Log: Stopping service...
105 | ```
106 |
--------------------------------------------------------------------------------
/docs/LogLevelResolver.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 15
3 | ---
4 |
5 | # Log Level Resolvers
6 |
7 | The `ILogLevelResolver` interface defines a custom method in which any given `ILogger` can use to retrieve it's `LogLevel` on-demand.
8 |
9 | > **Note:** `ILogLevelResolver` does not affect the logging level for other default loggers (such as `Microsoft.Extensions.Logging.ILogger`). It will only affect logs routed through `RockLib.Logging.ILogger`.
10 |
11 | ## Creating Your Own Resolver
12 |
13 | New log level resolvers can be created by implementing the `ILogLevelResolver` interface, and registering the implementation to the DI container. If no DI container is used, you may pass `ILogLevelResolver` directly into the constructor of a `Logger`.
14 |
15 | Only one `ILogLevelResolver` may be used per application when DI is used.
16 |
17 | ### Implementation
18 |
19 | The interface `ILogLevelResolver` contains one method - `GetLogLevel()`. This method can return a `LogLevel`, or `null`. When a `LogLevel` is returned, that `LogLevel` will be used for all messages. When `null` is returned, the `ILogger` will fall back to it's primary method of obtaining the logging level (e.g. via an `appsettings.json` configuration).
20 |
21 | ## Use Cases
22 |
23 | This interface can be useful if you would like your application to be able to switch logging levels at runtime. When adding a custom `ILogLevelResolver`, the implementation itself can be setup to pull log levels from wherever you would like.
24 |
--------------------------------------------------------------------------------
/docs/LogProcessors.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 14
3 | ---
4 |
5 | # Log Processors
6 |
7 | `LogProcessors` provide different ways of sending logs to `LogProviders`.
8 |
9 | `RockLib.Logging` comes with three `LogProcessors` available for use.
10 |
11 | ## BackgroundLogProcessor
12 |
13 | The `BackgroundLogProcessor` is the default `LogProcessor` used in `RockLib.Logging`.
14 |
15 | This `LogProcessor` processes and tracks logs on dedicated non-threadpool background threads. When this is disposed of, it will block until all in-flight logs have finished processing.
16 |
17 | This behavior ensures that, on a graceful shutdown of an application, logs will still be sent to their destination.
18 |
19 | ## FireAndForgetLogProcessor
20 |
21 | The `FireAndForgetLogProcessor` processes logs asynchronously, but without any task tracking.
22 |
23 | As its name implies, these logs are not tracked and, in the event that the application shuts down before the logs have been processed, they may be lost. This is best used when logs from this application are not of great importance.
24 |
25 | ## SynchronousLogProcessor
26 |
27 | The `SynchronousLogProcessor` processes logs on the same thread as the caller.
28 |
29 | This behavior ensures that logs will be processed before the application can continue work. This is best used when logging is a necessary component of the application's operation.
30 |
31 | ## Creating Your Own LogProcessor
32 |
33 | The above-mentioned implementations of `LogProcessor` should cover the majority of use cases for logging. However, if these do not meet the requirements for your application, you can implement a new log processor by having the new class implement the `ILogProcessor` interface or inherit the `LogProcessor` class.
34 |
35 | ## ILogProcessor
36 |
37 | The `ILogProcessor` interface is available for implementation if custom logic is needed for processing logs. The `IsDisposed` property and `ProcessLogEntry` method both need to be defined.
38 |
39 | **Note:** when defining the `ProcessLogEntry` method, `ILogger.Log` should not be called.
40 |
41 | ## LogProcessor
42 |
43 | The abstract `LogProcessor` class implements `ILogProcessor`. The `SendToLogProvider` abstract method must be defined.
44 |
45 | Additionally, the folllowing virtual members provide base functionality, but are available for override if necessary:
46 |
47 | * `Dispose`
48 | * `ProcessLogEntry`
49 | * `HandleError`
50 |
--------------------------------------------------------------------------------
/docs/LogProviderErrors.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 12
3 | ---
4 |
5 | # Handle Log Provider Errors
6 |
7 | By default, errors produced by log providers are only traced, see [Tracing](Tracing.md). In order to directly handle the errors, the `ILogger.ErrorHandler` can be set, either directly or through an extension method.
8 |
9 | ## Error Type
10 |
11 | **Message**
12 | - Type: string
13 | - Description: Gets the message that describes the error.
14 |
15 | **Exception**
16 | - Type: Exception
17 | - Description: Gets the exception responsible for the error.
18 |
19 | **LogProvider**
20 | - Type: ILogProvider
21 | - Description: Gets the log provider that failed to write the log entry.
22 |
23 | **LogEntry**
24 | - Type: LogEntry
25 | - Description: Gets the log entry that failed to write.
26 |
27 | **FailureCount**
28 | - Type: int
29 | - Description: Gets the number of times the log provider has failed to write the log entry.
30 |
31 | **IsTimeout**
32 | - Type: bool
33 | - Description: Gets a value indicating whether the error was a result of timing out.
34 |
35 | **Timestamp**
36 | - Type: DateTime
37 | - Description: Gets the time that the error event occurred.
38 |
39 | **ShouldRetry**
40 | - Type: bool
41 | - Description: Gets or sets a value indicating whether the log provider should attempt to send the log entry again.
42 |
43 | ## SetErrorHandler Extension
44 |
45 | The `SetErrorHandler` extension uses a `DelegateErrorHandler` behind the scenes. This will invoke whatever action is passed into it with the given `Error`.
46 |
47 | ```csharp
48 | logger.SetErrorHandler(e =>
49 | {
50 | // Do something with the error here.
51 | })
52 | ```
53 |
54 | ## Directly Setting ErrorHandler Property
55 |
56 | In order to directly set the `ErrorHandler` property, there needs to be a new implementation of the `IErrorHandler` interface. There are currently no public implementations of the interface.
57 |
58 | ```csharp
59 | logger.ErrorHandler = new ExampleErrorHandler();
60 |
61 | public class ExampleErrorHandler : IErrorHandler
62 | {
63 | public void HandleError(Error error)
64 | {
65 | // Do something with the error here.
66 | }
67 | }
68 | ```
69 |
70 | ## Adding Retry On Error
71 |
72 | One possibility for handling the errors is to retry sending to the provider. If the `ShouldRetry` property is set to `true` when `HandleError` is called, the provider will be retried. Each attempt will increment the `FailureCount` property.
73 |
74 | ### Simple example of adding retry:
75 |
76 | ```csharp
77 | logger.SetErrorHandler(e =>
78 | {
79 | if (e.FailureCount < 4)
80 | e.ShouldRetry = true;
81 | })
82 | ```
83 |
--------------------------------------------------------------------------------
/docs/LogProviders.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 8
3 | sidebar_label: 'Use log providers'
4 | ---
5 |
6 | # Log Providers
7 |
8 | The `ILogProvider` interface defines exactly how logs are written; a logger can have zero to many log providers. `RockLib.Logging` comes with three implementations of the `ILogProvider`.
9 |
10 | ## [ConsoleLogProvider](ConsoleLogProvider.md)
11 |
12 | Writes log entries to standard out or standard error.
13 |
14 | ## [FileLogProvider](FileLogProvider.md)
15 |
16 | Writes log entries to a file.
17 |
18 | ## [RollingFileLogProvider](RollingFileLogProvider.md)
19 |
20 | Writes log entries to a file. Log files will then be archived based on time and filesize configuration.
21 |
22 | ## Creating Your Own LogProvider
23 |
24 | New log providers can be created by implementing the `ILogProvider` interface.
25 |
26 | ## ILogProvider
27 |
28 | The `ILogProvider` interface has `Timeout` and `Level` properties, as well as a `WriteAsync` method.
29 |
30 | **Timeout Property**
31 | - Type: TimeSpan
32 | - Description: If a task returned by the `WriteAsync` method does not complete by the value of the `Timeout` property, the task will be cancelled.
33 |
34 | **Level Property**
35 | - Type: LogLevel enum (Debug, Info, Warn, Error, Fatal, Audit)
36 | - Description: This value is used by the `Logger` class to determine if it should call this instance's `WriteAsync` method for a given log entry. If the value of this property is higher than a log entry's level, then this log provider is skipped.
37 |
38 | **WriteAsync Method**
39 | - Description: Writes the specified log entry.
40 | - Parameters:
41 | - logEntry
42 | - Type: LogEntry
43 | - Description: The log entry to write.
44 | - cancellationToken
45 | - Type: CancellationToken.
46 | - Description: The `CancellationToken` to observe.
47 | - Returns: A task that completes when the log entry has been written.
48 |
--------------------------------------------------------------------------------
/docs/Logger.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 3
3 | sidebar_label: 'Instantiate and configure a logger'
4 | ---
5 |
6 | # How to instantiate and configure a logger
7 |
8 | The `Logger` class can be directly instantiated and has two public constructors. The only difference between the constructors is how its log processor is initialized (whether by enum or directly). It also has one read/write property.
9 |
10 | ## Constructor 1:
11 |
12 | Name | Type | Description | Required | Default Value
13 | ---- | ---- | ----------- | -------- | -------------
14 | name | `string` | The name of the logger. | No | `"default"`
15 | level | `enum LogLevel`: `NotSet`, `Debug`, `Info`, `Warn`, `Error`, `Fatal`, `Audit` | The logging level of the logger. Logs with a level lower than this are not processed. | No | `NotSet`
16 | logProviders | `IReadOnlyCollection` | A collection of `ILogProvider` objects used by this logger. | No | Empty list
17 | isDisabled | `bool` | Whether the logger should be disabled. | No | `false`
18 | processingMode | `enum ProcessingMode`: `Background`, `Synchronous`, `FireAndForget` | A value that indicates how the logger will process logs. | No | `Background`
19 | contextProviders | `IReadOnlyCollection` | A collection of `IContextProvider` objects that customize outgoing log entries. | No | Empty list
20 |
21 | ## Constructor 2:
22 |
23 | Name | Type | Description | Required | Default Value
24 | ---- | ---- | ----------- | -------- | -------------
25 | logProcessor | `ILogProcessor` | The object responsible for processing logs. | Yes | N/A
26 | name | `string` | The name of the logger. | No | `"default"`
27 | level | `enum LogLevel`: `NotSet`, `Debug`, `Info`, `Warn`, `Error`, `Fatal`, `Audit` | The logging level of the logger. Logs with a level lower than this are not processed. | No | `NotSet`
28 | logProviders | `IReadOnlyCollection` | A collection of `ILogProvider` objects used by this logger. | No | Empty list
29 | isDisabled | `bool` | Whether the logger should be disabled. | No | `false`
30 | contextProviders | `IReadOnlyCollection` | A collection of `IContextProvider` objects that customize outgoing log entries. | No | Empty list
31 |
32 | ## Read / write properties
33 |
34 | Name | Type | Description | Default Value
35 | ---- | ---- | ----------- | -------------
36 | ErrorHandler | `IErrorHandler` | An object that handles errors that occur during log processing. | `null`
37 |
--------------------------------------------------------------------------------
/docs/LoggingTraceListener.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 18
3 | ---
4 |
5 | # Use LoggingTraceListener to log trace messages
6 |
7 | The `RockLib.Logging.Diagnostics.LoggingTraceListener` is an inheritor of `System.Diagnostics.TraceListener` that sends trace messages to a `RockLib.Logging.ILogger`. It is for troubleshooting *other* libraries that output trace messages - such trace messages will be logged with the `Ilogger` that the application chooses.
8 |
9 | > **WARNING:** DO NOT use `LoggingTraceListener` for troubleshooting issues with RockLib.Logging itself!
10 |
11 | ## RockLib.Diagnostics Configuration
12 |
13 | The easiest way to add a `LoggingTraceListener` is with configuration. The following example assumes that the library we're troubleshooting uses a trace source named "my_library_trace_source_name".
14 |
15 | ```json
16 | {
17 | "Rocklib.Logging": {
18 | "Level": "Debug",
19 | "Providers": { "Type": "RockLib.Logging.ConsoleLogProvider, RockLib.Logging" }
20 | },
21 |
22 | "RockLib.Diagnostics": {
23 | "Sources": {
24 | "Name": "my_library_trace_source_name",
25 | "Switch": {
26 | "Name": "my_library_trace_source_name",
27 | "Level": "All"
28 | },
29 | "Listeners": { "Type": "RockLib.Logging.Diagnostics.LoggingTraceListener, RockLib.Logging" }
30 | }
31 | }
32 | }
33 | ```
34 |
35 | If the application defines more than one logger, the `LoggingTraceListener` needs to specify the logger name.
36 |
37 | ```json
38 | {
39 | "Rocklib.Logging": [
40 | {
41 | "Name": "MainLogger",
42 | "Level": "Warn",
43 | "Providers": { "Type": "RockLib.Logging.ConsoleLogProvider, RockLib.Logging" }
44 | },
45 | {
46 | "Name": "DiagnosticLogger",
47 | "Level": "Debug",
48 | "Providers": { "Type": "RockLib.Logging.ConsoleLogProvider, RockLib.Logging" }
49 | }
50 | ]
51 |
52 | "RockLib.Diagnostics": {
53 | "Sources": {
54 | "Name": "my_library_trace_source_name",
55 | "Switch": {
56 | "Name": "my_library_trace_source_name",
57 | "Level": "All"
58 | },
59 | "Listeners": {
60 | "Type": "RockLib.Logging.Diagnostics.LoggingTraceListener, RockLib.Logging",
61 | "Value": {
62 | "LoggerName": "DiagnosticLogger"
63 | }
64 | }
65 | }
66 | }
67 | }
68 | ```
69 |
--------------------------------------------------------------------------------
/docs/Reloading.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 20
3 | ---
4 |
5 | # Changing a logger's settings "on the fly"
6 |
7 | It is generally a bad idea to run an application in production while logging at the `Debug` (or arguably the `Info`) level. Especially when the logging comes at a price per log. Millions or billions of debug or info logs could be quite expensive. On the other hand, production issues can and do arise, and having those `Debug` or `Info` logs available for diagnostics is incredibly valuable. A solution to this problem is to normally log at `Warn`, but have the ability to change the level to `Info` or `Debug` on-the-fly, should the need arise.
8 |
9 | To accomplish this, RockLib.Logging binds to configuration. Microsoft.Extensions.Configuration supports automatically reloading itself when its settings change - this is the mechanism that RockLib hooks into to keep its loggers up-to-date. The Json congiruation provider supports this reloading capability, so it is easy to verify that your loggers have the most up-to-date settings on a developer machine: edit the appsettings.json file while the app is running and set a breakpoint to examine your logger - it should stay up-to-date.
10 |
11 | ### Up-to-date Loggers
12 |
13 | When registering and defining a logger with DI extension methods, in order to have up-to-date loggers, bind the logger's `LoggerOptions` to a configuration section:
14 |
15 | ```csharp
16 | services.AddLogger().AddConsoleLogProvider();
17 | services.Configure(Configuration.GetSection("MyLogger"));
18 | ```
19 |
20 | ```json
21 | {
22 | "MyLogger": {
23 | "Level": "Warn"
24 | }
25 | }
26 | ```
27 |
28 | ---
29 |
30 | It is also possible to register a logger with DI, but define it in configuration using a standard `LoggerFactory` configuration section. In this case, it is not necessary to bind options.
31 |
32 | ```csharp
33 | // An empty logger builder will create the logger defined in configuration using COF.
34 | services.AddLogger();
35 | ```
36 |
37 | ```json
38 | {
39 | "Rocklib.Logging": {
40 | "Level": "Warn",
41 | "Providers": { "Type": "RockLib.Logging.ConsoleLogProvider, RockLib.Logging" }
42 | }
43 | }
44 | ```
45 |
46 | ### Reloading Loggers
47 |
48 | For applications with short-lived, transient or scopped loggers (like ASP.NET Core), there's nothing else to do. Since loggers are created at the time they are used, and since they are created with the most recent configuration, they will always be up-to-date. However, for singleton or other long-lived loggers, we need reloading loggers - they should be notified of configuration changes so they can update themselves accordingly.
49 |
50 | ---
51 |
52 | When registering and defining a logger with DI, in order to have reloading loggers, make sure the `ReloadOnChange` setting of the options is true:
53 |
54 | ```csharp
55 | services.AddLogger().AddConsoleLogProvider();
56 | services.Configure(Configuration.GetSection("MyLogger"));
57 | ```
58 |
59 | ```json
60 | {
61 | "MyLogger": {
62 | "ReloadOnChange": true,
63 | "Level": "Warn"
64 | }
65 | }
66 | ```
67 |
68 | ---
69 |
70 | When registering with DI and defining in configuration with a `LoggerFactory` section, specifiy the `reloadOnChange` parameter like this:
71 |
72 | ```json
73 | {
74 | "Rocklib.Logging": {
75 | "ReloadOnChange": true,
76 | "Value": {
77 | "Level": "Warn",
78 | "Providers": { "Type": "RockLib.Logging.ConsoleLogProvider, RockLib.Logging" }
79 | }
80 | }
81 | }
82 | ```
83 |
84 | ---
85 |
86 | When using LoggerFactory or the extension methods defined by the LoggerFactoryExtensions class, there is a parameter, `reloadOnConfigChange`, which is true by default. Set the parameter to false to *disable* reloading.
87 |
88 | ```csharp
89 | ILogger logger = LoggerFactory.CreateLogger(reloadOnConfigChange: false);
90 | ```
91 |
--------------------------------------------------------------------------------
/docs/Tracing.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 17
3 | ---
4 |
5 | # Enable Tracing For Troubleshooting
6 |
7 | It is possible for an implementation of `ILogProvider` to throw an exception in its `WriteAsync` method (in fact implementations should throw any exception instead of catching and handling it). If an exception is thrown, it will caught by the log processor, passed to the logger's `ErrorHandler`, and if a `TraceSource` exists with the name "rocklib.logging", a trace event is sent to it.
8 |
9 | In addition, if the `TraceSource` is defined and is tracing is at `Information`, a trace event is sent for each successful log processing.
10 |
11 | ## To set a _TraceSource_, add this to your configuration:
12 |
13 | ```json
14 | "RockLib.Diagnostics": {
15 | "Sources": {
16 | "Name": "rocklib.logging",
17 | "Switch": {
18 | "Name": "rocklib.logging",
19 | "Level": "All"
20 | },
21 | "Listeners": {
22 | "Name": "rocklib.logging",
23 | "LogFileName": "C:\\my\\path\\rocklib_logging.log"
24 | }
25 | }
26 | }
27 | ```
28 |
29 | ## To set the _TraceSource_ programmatically:
30 |
31 | ```csharp
32 | Tracing.Settings = new DiagnosticsSettings(
33 | sources: new System.Diagnostics.TraceSource[]
34 | {
35 | new System.Diagnostics.TraceSource(name: "rocklib.logging")
36 | {
37 | Switch = new System.Diagnostics.SourceSwitch(name: "rocklib.logging")
38 | {
39 | Level = System.Diagnostics.SourceLevels.All
40 | },
41 | Listeners =
42 | {
43 | new System.Diagnostics.DefaultTraceListener
44 | {
45 | Name = "rocklib.logging",
46 | LogFileName = "C:\\my\\path\\rocklib_logging.log"
47 | }
48 | }
49 | }
50 | });
51 | ```
52 |
--------------------------------------------------------------------------------
/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RockLib/RockLib.Logging/f76d19604961b5395d7f3dd741b14ffe24f908f9/icon.png
--------------------------------------------------------------------------------