Plan: Intro TelemetryClient logs in App Insights ILogger logs in App Insights ILogger improvements Benchmark Intro: When your start building a system that consists of more than one application you can get such problems with logging: not structuring logging (logging as a string, no data to query through) in different classes and apps you have different names for values you are logging difficulties to see all logs from one request through all classes and services in one query to logging provider no way to create robust analytical diagrams, charts and alerts To solve the issues above let's try to answer the next questions: find the best way to log data corresponding to the action across all call stack tree provide a way to log important data (like ids and exception types) to separate columns to make a comfortable logs querying experience Logs using Microsoft.ApplicationInsights.TelemetryClient Let's check this code example below: a = TelemetryClient( TelemetryConfiguration( )); a.TrackTrace( , Dictionary< , >() { { , } }); var new new "the key" "TelemetryClient test 1" new string string "someKey" "some value" Code above will create a log entry that will be displayed as following: This data in custom dimensions will allow you to make queries without using regular expressions. TelemetryClient doing pretty well its job but let's see pros and cons: pros: name of the key appears in the app insights without any changes custom dimensions contain only wanted data cons: usage of TelemetryClient requires to stay with the Microsoft App Insights Logs with Microsoft.Extensions.Logging.ILogger Let's check a code example , ILogger ) { calloutId = ; testData = ; = WeatherForecast{ = DateTimeOffset.Now, Summary = , TemperatureCelsius = }; jsonString = JsonConvert.SerializeObject( ); .LogInformation( , calloutId, testData, , jsonString); . ... log var 1234 var "test data" var data new Date "test" 15 var data log "ILogger log22 {calloutId}, {testData}, {data}, {jsonString}" data ... This code will be displayed in the Application Insights as following: ILogger also doing pretty well bat also has some disadvantages: pros: it is possible to log data as a separate column ILogger is a generic logger that may be used for any output destination cons: name of the property appears in the app insights with the prefix “prop__“ custom dimensions contain other data that was not manually provided is used for each param passed to the ILogger, so for objects passed will be displayed only their fully qualified name (requires to use before passing objects, except anonymous ones) .ToString() .ToJson() is a good choice for those who going to stay with AppInsights forever, on the other hand going with the default will add prefixes to your logged data but provide flexibility for the destination of the logs. Intermediate result 1: TelemetryClient ILogger Improving developer experience for logging with ILogger to Application insights: To encapsulate that weird way to provide additional properties to logging templates you can create a set of extension methods. Also, you can include there additional useful data like row number, file path and method name. So usage of prepared extensions may look like this: ..., ILogger log) { var calloutId = ; var companyId = ; log. ; log. ; { throw ; } catch (Exception ex) { log. ; } .... 1234 1234 LogInfo( , , ) "Something happened" calloutId companyId LogWarn( , , ) "Something happened" calloutId companyId try new ArgumentOutOfRangeException( ) "Test exception" LogErr( , , , ) ex "Something happened" calloutId companyId Here is a code example for extension methods used above: { { logger.LogInformation( , reason, calloutId, companyId, customerId, method, srcFilePath, srcLineNumber); } { logger.LogWarning( , reason, calloutId, companyId, customerId, method, srcFilePath, srcLineNumber); } { logger.LogError( , ex.GetType().Name, reason, calloutId, companyId, customerId, method, srcFilePath, srcLineNumber, ex.ToString()); } } public static class LoggerExtensions ( ) public static void LogInfo ILogger logger, reason, calloutId, ? companyId = , ? customerId = , [CallerMemberName] method = , [CallerFilePath] srcFilePath = , [CallerLineNumber] srcLineNumber = this string int int null int null string "" string "" int 0 "{reason}, {calloutId}, {companyId}, {customerId}, {method}, {srcFilePath}, {srcLineNumber}" ( ) public static void LogWarn ILogger logger, reason, calloutId, ? companyId = , ? customerId = , [CallerMemberName] method = , [CallerFilePath] srcFilePath = , [CallerLineNumber] srcLineNumber = this string int int null int null string "" string "" int 0 "{reason}, {calloutId}, {companyId}, {customerId}, {method}, {srcFilePath}, {srcLineNumber}" ( ) public static void LogErr ILogger logger, Exception ex, reason, calloutId, ? companyId = , ? customerId = , [CallerMemberName] method = , [CallerFilePath] srcFilePath = , [CallerLineNumber] srcLineNumber = this string int int null int null string "" string "" int 0 "{exType}, {reason}, {calloutId}, {companyId}, {customerId}, {method}, {srcFilePath}, {srcLineNumber}, {exDetails}" With such a logging approach you can group all your logs by method name or source file as well as by any data you provide as an additional column. Intermediate result 2: for vs vs Benchmark custom extensions for ILogger ILogger LoggerMessage Results for 1000 logs per logger candidate running on Azure Function. iLoggerTimer: 00:00:05.0994452 enhancedLoggerTimer: 00:00:05.4228425 loggerMsgTimer: 00:00:05.1233116 I see good results for including the fact it is also logging line number, file path and method name and have the ability to log even more useful data. custom extensions for ILogger SpecificLogger _log; Func<ILogger, , , IDisposable> scope = LoggerMessage.DefineScope< , >( ); Action<ILogger, , Exception> testLog = LoggerMessage.Define< >(LogLevel.Information, EventId( , ), ); { _log = log; } [ ] { calloutId = ; companyId = ; customerId = ; times = ; iLoggerTimer = Stopwatch.StartNew(); (log.BeginScope( , calloutId, companyId)) { ( i = ; i < times; i++) { log.LogInformation( , customerId); } } iLoggerTimer.Stop(); enhancedLoggerTimer = Stopwatch.StartNew(); _log.SetCalloutId(calloutId); _log.SetCompanyId(companyId); ( i = ; i < times; i++) { _log.LogWarn( , customerId: customerId); } enhancedLoggerTimer.Stop(); loggerMsgTimer = Stopwatch.StartNew(); (scope(log, calloutId, companyId)) { ( i = ; i < times; i++) { testLog(log, customerId, ); } } loggerMsgTimer.Stop(); result = ; OkObjectResult(result); } private readonly private static int int int int "Scope calloutId: {calloutId}, companyId: {companyId}" private static readonly int int new 4 "TestEventName" "loggerMsg: Something happened {customerId}" ( ) public Function2 SpecificLogger log FunctionName( ) "Function2" Task<IActionResult> ( )] HttpRequest req, ILogger log) public async Run [HttpTrigger(AuthorizationLevel.Function, , , Route = "get" "post" null var 1234 var 2345 var 3456 var 1000 var using "Scope calloutId: {calloutId}, companyId: {companyId}" for int 0 "iLogger: Something happened {customerId}" var for int 0 "enhancedLogger: Something happened 4" var using for int 0 null var $"iLoggerTimer: ; enhancedLoggerTimer: , loggerMsgTimer: " {iLoggerTimer.Elapsed} {enhancedLoggerTimer.Elapsed} {loggerMsgTimer.Elapsed} return new Thanks for reading this article, hope it helps you!