In the previous examples, we applied the logging aspect manually using the [Log]
custom attribute. But when we must
add logging to all methods of a namespace, doing it manually becomes tedious.
We can programmatically filter the code model of our project and add aspects to desired methods by adding a ProjectFabric to our code. Fabrics are compile-time classes executed by Metalama during compilation or from our IDE. They can add aspects to any eligible target declaration. For details, see Adding many aspects simultaneously.
The following code adds the logging aspect to all public
methods of public
types:
1using Metalama.Framework.Fabrics;
2using Metalama.Framework.Code;
3
4internal class Fabric : ProjectFabric
5{
6 public override void AmendProject( IProjectAmender amender )
7 => amender
8 .SelectTypes()
9 .Where( type => type.Accessibility == Accessibility.Public )
10 .SelectMany( type => type.Methods )
11 .Where(
12 method =>
13 method.Accessibility == Accessibility.Public && method.Name != "ToString" )
14 .AddAspectIfEligible<LogAttribute>();
15}
Warning
It is important to exclude the ToString
method from logging; otherwise, infinite recursion could occur.
We can see that the Calculator
class has been transformed, even though there is no longer any custom attribute:
1public class Calculator
2{
3 public double Add( double a, double b ) => a + b;
4}
1using System;
2using Microsoft.Extensions.Logging;
3
4public class Calculator
5{
6 public double Add( double a, double b ) { var isTracingEnabled = _logger.IsEnabled(LogLevel.Trace);
7 if (isTracingEnabled)
8 {
9 _logger.LogTrace($"Calculator.Add(a = {{{a}}}, b = {{{b}}}) started.");
10 }
11
12 try
13 {
14 double result;
15 result = a + b;
16 if (isTracingEnabled)
17 {
18 _logger.LogTrace($"Calculator.Add(a = {{{a}}}, b = {{{b}}}) returned {result}.");
19 }
20
21 return (double)result;
22 }
23 catch (Exception e) when (_logger.IsEnabled(LogLevel.Warning))
24 {
25 _logger.LogWarning($"Calculator.Add(a = {{{a}}}, b = {{{b}}}) failed: {e.Message}");
26 throw;
27 }
28 }
29private ILogger _logger;
30
31 public Calculator(ILogger<Calculator> logger = null)
32 {
33 this._logger = logger ?? throw new System.ArgumentNullException(nameof(logger));
34 }
35}
Warning
Including sensitive information (such as user credentials or personal data) in logs can pose a security risk. You should be cautious when adding parameter values to logs and avoid exposing sensitive data. To remove sensitive information from the logs, see Logging example, step 7: Removing sensitive data.