Search code examples
c#azure.net-coreazure-application-insightsilogger

Application Insights - ILogger arguments rendered as name of the object in custom dimensions


Objects are rendered as strings, (name of the object), in Application Insights custom dimensions when passed as arguments to ilogger. The actual values are not shown.

Register Application Insights

services.AddApplicationInsightsTelemetry();

New log

public class HealthController : ControllerBase
{
    private readonly ILogger<HealthController> _logger;

    public HealthController(ILogger<HealthController> logger)
    {
        _logger = logger;
    }

    [HttpGet]
    public IActionResult Get()
    {
        var health = new HealthViewModel()
        {
             ok = false
        };

        _logger.LogInformation("Hlep me pls {health}", health);

        return Ok(health);
    }
}

Result

enter image description here

I do not want to this this for every log:

var health = new HealthViewModel()
{
     ok = false
};

_logger.LogInformation("Hlep me pls {health}", JsonConvert.SerializeObject(health));

I tried creating a middleware for application insights but the value is still the name of the object..

enter image description here

Why are arguments not rendered as json?

Edit

It seems like

var health = new
{
     ok = false
};

_logger.LogInformation("HEJ2 {health}", health);

works but not

var health = new HealthViewModel
{
     ok = false
};

_logger.LogInformation("HEJ2 {health}", health);

Solution

  • Not supported

    Quote from https://github.com/microsoft/ApplicationInsights-dotnet/issues/1722

    I think you're expecting too much of the logger. It doesn't know about JSON format, it just calls Convert.ToString on properties

    Convert.ToString typically calls ToString() and the default ToString implementation for new classes is simply to return the type name

    What you can do

    Use ToJson() on objects logged to ILogger and create a middleware for application insights and modify the name of the log and the custom dimensions.

    Middleware

    public class ProcessApiTraceFilter : ITelemetryProcessor
    {
        private ITelemetryProcessor Next { get; set; }
        private readonly IIdentity _identity;
        private readonly IHostEnvironment _hostEnvironment;
    
        public ProcessApiTraceFilter(ITelemetryProcessor next, IHostEnvironment hostEnvironment, IIdentity identity)
        {
            Next = next;
            _identity = identity;
            _hostEnvironment = hostEnvironment;
        }
    
        public void Process(ITelemetry item)
        {
            item.Process(_hostEnvironment, _identity);
    
            Next.Process(item);
        }
    }
    

    Implementation

    public static class ApplicationInsightsExtensions
    {
        public static void Process(this ITelemetry item, IHostEnvironment hostEnvironment, IIdentity identity)
        {
            if (item is TraceTelemetry)
            {
                var traceTelemetry = item as TraceTelemetry;
                var originalMessage = traceTelemetry.Properties.FirstOrDefault(x => x.Key == "{OriginalFormat}");
    
                if (!string.IsNullOrEmpty(originalMessage.Key))
                {
                    var reg = new Regex("{([A-z]*)*}", RegexOptions.Compiled);
                    var match = reg.Matches(originalMessage.Value);
                    var formattedMessage = originalMessage.Value;
                    foreach (Match arg in match)
                    {
                        var parameterName = arg.Value.Replace("{", "").Replace("}", "");
                        var parameterValue = traceTelemetry.Properties.FirstOrDefault(x => x.Key == parameterName);
                        formattedMessage = formattedMessage.Replace(arg.Value, "");
                    }
    
                    traceTelemetry.Message = formattedMessage.Trim();
                }
    
                if (identity != null)
                {
                    var isAuthenticated = identity.IsAuthenticated();
                    const string customerKey = "customer";
    
                    if (isAuthenticated && !traceTelemetry.Properties.ContainsKey(customerKey))
                    {
                        var customer = identity.Customer();
    
                        if (customer != null)
                        {
                            traceTelemetry.Properties.Add(customerKey, customer.ToJson());
                        }
                    }
    
                    var request = identity.Request();
                    const string requestKey = "request";
    
                    if (request != null && !traceTelemetry.Properties.ContainsKey(requestKey))
                    {
                        traceTelemetry.Properties.Add(requestKey, request.ToJson());
                    }
                }
    
                var applicationNameKey = "applicationName";
    
                if (hostEnvironment != null && !string.IsNullOrEmpty(hostEnvironment.ApplicationName) && !traceTelemetry.Properties.ContainsKey(applicationNameKey))
                {
                    traceTelemetry.Properties.Add(applicationNameKey, hostEnvironment.ApplicationName);
                }
            }
        }
    }
    

    Register application insights and middleware in startup

    services.AddApplicationInsightsTelemetry();
    services.AddApplicationInsightsTelemetryProcessor<ProcessApiTraceFilter>();
    

    ToJson

    public static class ObjectExtensions
    {
        private static readonly string Null = "null";
        private static readonly string Exception = "Could not serialize object to json";
    
        public static string ToJson(this object value, Formatting formatting = Formatting.None)
        {
            if (value == null) return Null;
    
            try
            {
                string json = JsonConvert.SerializeObject(value, formatting);
    
                return json;
            }
            catch (Exception ex)
            {
                return $"{Exception} - {ex?.Message}";
            }
        }
    }
    

    Log

    //Log object? _smtpAppSettings.ToJson()
    
    _logger.LogInformation("Email sent {to} {from} {subject}", to, _smtpAppSettings.From, subject)
    

    Result

    enter image description here