<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Andrea Grillo</title>
    <description>The latest articles on Forem by Andrea Grillo (@krusty93).</description>
    <link>https://forem.com/krusty93</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F550281%2F4deafc8b-438c-4d6c-8bbb-b0fd33d06447.jpeg</url>
      <title>Forem: Andrea Grillo</title>
      <link>https://forem.com/krusty93</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/krusty93"/>
    <language>en</language>
    <item>
      <title>Sending an alert in Microsoft Teams from Azure DevOps Pipelines</title>
      <dc:creator>Andrea Grillo</dc:creator>
      <pubDate>Wed, 01 Feb 2023 22:54:52 +0000</pubDate>
      <link>https://forem.com/krusty93/sending-an-alert-in-microsoft-teams-from-azure-devops-pipelines-419k</link>
      <guid>https://forem.com/krusty93/sending-an-alert-in-microsoft-teams-from-azure-devops-pipelines-419k</guid>
      <description>&lt;p&gt;Sometimes there is the need to warn your team about an on going deployment to the cloud. And if you are using Microsoft Teams as primary communication platform, you are likely to have a private channel with your team. This is my case, where anyone is going to run a release pipeline warns the rest of the team about it.&lt;br&gt;
Since manually sending messages is annoying, we automated this operation by using Teams webhooks - without the help of Graph APIs 🐱‍👤.&lt;/p&gt;
&lt;h2&gt;
  
  
  Setup a channel webhook
&lt;/h2&gt;

&lt;p&gt;In order to start, you need to set up a webhook for the channel of where you would like to send messages. So select your channel and then click the 3 dots in the upper right corner; open the &lt;code&gt;Connectors&lt;/code&gt; menu and search for &lt;code&gt;Incoming Webhook&lt;/code&gt;. Once found, click on &lt;code&gt;Configure&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftyinbah96btx872ht8ug.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftyinbah96btx872ht8ug.png" alt="Connectors list on Teams"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, set the name, optionally upload an image (it will be the avatar next to the channel posts) and finally click on &lt;code&gt;Create&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;An URL should be generated: copy and note it, you will need it later on.&lt;/p&gt;
&lt;h2&gt;
  
  
  Setup the pipeline
&lt;/h2&gt;

&lt;p&gt;Select your pipeline and a task to run a bash script. Here write down the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-x&lt;/span&gt;
&lt;span class="nb"&gt;umask &lt;/span&gt;0002

&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ./post.json &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;endmsg&lt;/span&gt;&lt;span class="sh"&gt;
      {
        "summary": "&lt;/span&gt;&lt;span class="nv"&gt;$System&lt;/span&gt;&lt;span class="sh"&gt;.DefinitionName is being deployed in &lt;/span&gt;&lt;span class="nv"&gt;$Release&lt;/span&gt;&lt;span class="sh"&gt;.EnvironmentName",
        "text": "[&lt;/span&gt;&lt;span class="nv"&gt;$System&lt;/span&gt;&lt;span class="sh"&gt;.DefinitionName](&lt;/span&gt;&lt;span class="nv"&gt;$Release&lt;/span&gt;&lt;span class="sh"&gt;.ReleaseWebUrl) (&lt;/span&gt;&lt;span class="nv"&gt;$BRANCH_NAME&lt;/span&gt;&lt;span class="sh"&gt;) is being deployed in &lt;/span&gt;&lt;span class="nv"&gt;$Build&lt;/span&gt;&lt;span class="sh"&gt;.SourceBranchName by &lt;/span&gt;&lt;span class="nv"&gt;$Release&lt;/span&gt;&lt;span class="sh"&gt;.RequestedFor"
      }
&lt;/span&gt;&lt;span class="no"&gt;endmsg

&lt;/span&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; @post.json &amp;lt;url-you-copied-from-teams&amp;gt; &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script is simply creating a json file called &lt;code&gt;post.json&lt;/code&gt; with some &lt;a href="https://learn.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/connectors-using?tabs=cURL" rel="noopener noreferrer"&gt;content supported by Teams&lt;/a&gt;. Then, a POST request is sent to the endpoint of the webhook we previously configured. You should now see the message on Teams:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi6zzgeuft7r3bz5smlj2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi6zzgeuft7r3bz5smlj2.png" alt="The sent message on Teams channel"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the example above, the variables &lt;code&gt;System.DefinitionName&lt;/code&gt;, &lt;code&gt;Release.EnvironmentName&lt;/code&gt;, &lt;code&gt;Release.ReleaseWebUrl&lt;/code&gt;, &lt;code&gt;Build.SourceBranchName&lt;/code&gt; and &lt;code&gt;Release.RequestedFor&lt;/code&gt; are automatically provided by Azure DevOps in a release pipeline and they stand for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the pipeline name&lt;/li&gt;
&lt;li&gt;the pipeline stage name&lt;/li&gt;
&lt;li&gt;a permanent link to the current pipeline run&lt;/li&gt;
&lt;li&gt;the artifact branch name&lt;/li&gt;
&lt;li&gt;the user who triggered the pipeline&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;As shown, sending a message to a Teams channel is really easy and you don't need to integrate Graph APIs unless you care about some advanced features. However, implementing Graph APIs in a pipeline is not a 5 minute job like this one, so find your tradeoff between utility and effort.&lt;/p&gt;

&lt;p&gt;This tutorial sends a simple message as an example, but messages are customizable and also interactive as described in &lt;a href="https://learn.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/connectors-using?tabs=cURL" rel="noopener noreferrer"&gt;Microsoft documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Even if this procedure is quite straightforward, for me it took few hours since at the time, it wasn't documented at all.&lt;/p&gt;

&lt;p&gt;For this reason, I hope this has been helpful.&lt;/p&gt;

&lt;p&gt;Happy coding and happy releases! 🐱‍👤&lt;/p&gt;

</description>
      <category>devops</category>
      <category>productivity</category>
      <category>cloud</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Implementing health checks PT.2 - Azure Application Insights configuration</title>
      <dc:creator>Andrea Grillo</dc:creator>
      <pubDate>Sun, 03 Jul 2022 20:27:28 +0000</pubDate>
      <link>https://forem.com/krusty93/implementing-health-checks-pt2-azure-application-insights-configuration-51b9</link>
      <guid>https://forem.com/krusty93/implementing-health-checks-pt2-azure-application-insights-configuration-51b9</guid>
      <description>&lt;p&gt;This is the second post in a series dedicated to health checks.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://dev.to/krusty93/implementing-health-checks-pt1-aspnet-core-6-configuration-6gp"&gt;Implementing health checks PT.1 - Asp.Net Core 6 configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Implementing health checks PT.2 - Azure Application Insights configuration (this post)&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;Once the application is deployied, we may want to use some tool that periodically ping these endpoints and send us a notification when something is not properly working.&lt;/p&gt;

&lt;p&gt;The tool I use most is Azure Application Insights.&lt;/p&gt;

&lt;h2&gt;
  
  
  Application Insights
&lt;/h2&gt;

&lt;p&gt;Azure Application Insights is a wonderful service provided by Azure to monitor live apps. It provides tons of features, such as logs search and health checks ping as shown later, and it supports lot of languages - not just dotnet.&lt;/p&gt;

&lt;p&gt;From &lt;a href="https://docs.microsoft.com/en-us/azure/azure-monitor/app/app-insights-overview"&gt;official documentation&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Application Insights is a feature of Azure Monitor that provides extensible application performance management (APM) and monitoring for live web apps. Developers and DevOps professionals can use Application Insights to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automatically detect performance anomalies.&lt;/li&gt;
&lt;li&gt;Help diagnose issues by using powerful analytics tools.&lt;/li&gt;
&lt;li&gt;See what users actually do with apps.&lt;/li&gt;
&lt;li&gt;Help continuously improve app performance and usability.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Application Insights:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Supports a wide variety of platforms, including .NET, Node.js, Java, and Python.&lt;/li&gt;
&lt;li&gt;Works for apps hosted on-premises, hybrid, or on any public cloud.&lt;/li&gt;
&lt;li&gt;Integrates with DevOps processes.&lt;/li&gt;
&lt;li&gt;Has connection points to many development tools.&lt;/li&gt;
&lt;li&gt;Can monitor and analyze telemetry from mobile apps by integrating with Visual Studio App Center.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Configuring availability checks in Application Insights (AI)
&lt;/h2&gt;

&lt;p&gt;To get started, create a new AI instance in your Azure subscription and navigate to the overview page:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NMMhfc8V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/da1u4rf02bzyjiqn11vl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NMMhfc8V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/da1u4rf02bzyjiqn11vl.png" alt="Azure Portal on AI overview page" width="800" height="242"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In particular, there are three charts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Failed requests: shows the amount of requests that the application failed to process (4xx/5xx status codes)&lt;/li&gt;
&lt;li&gt;Server response time: average server response time&lt;/li&gt;
&lt;li&gt;Server requests: amount of requests per minute&lt;/li&gt;
&lt;li&gt;Availability: the server availability over time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Of course, to have some data from the first three charts you need to link this AI resource to an another Azure resource such as Azure WebApp, Azure Functions or Azure Kubernetes Service.&lt;/p&gt;

&lt;p&gt;Instead, the availability data is obtained from the ping requests that AI does to an endpoint of your choice.&lt;/p&gt;

&lt;p&gt;To setup them up, navigate to Availability section and click "Add Standard (preview) test".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vGDnzBSY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8wadmsn2hni88rgdqer6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vGDnzBSY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8wadmsn2hni88rgdqer6.png" alt="AI availability section" width="800" height="246"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;NB: the Standard test is still in preview but since it is only a ping service, I think it is safe enough to be used in production environments. It is not that different than the classic test but provides some additional options&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;Setup a name for the test, such as &lt;code&gt;MyWebAppPingTest&lt;/code&gt; and the url to ping (in the previous post I did set health checks on routes like &lt;code&gt;/alive&lt;/code&gt; and &lt;code&gt;dbcontext&lt;/code&gt;, so I put &lt;code&gt;&amp;lt;hostname&amp;gt;/alive&lt;/code&gt; or &lt;code&gt;&amp;lt;hostname&amp;gt;/dbcontext&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Then there are several options that can be checked to enhance the accuracy of the test. Each of them is properly documentated by going hover the &lt;code&gt;i&lt;/code&gt; icon with the mouse, so I don't go deep for each of them. However, I want to focus on some area:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you can set the HTTP Rest verb and eventually custom headers: it is useful when your resource is behind a gateway such as Azure API Management or you want to add some protection to your APIs.&lt;/li&gt;
&lt;li&gt;it is possible to set some custom successfull criteria by checking response status code, response body and other details that give you enough flexibility to play with different scenarios of your app&lt;/li&gt;
&lt;li&gt;the last section is about alerts, that can be configured only after the ping test creation. Enable it, we will use it in the next chapters&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click create and wait for the deploy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Looking at availability results
&lt;/h2&gt;

&lt;p&gt;After a while, data will start to be populated and a line in the chart is drawn:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--n_3UDO8G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/do1psncyyxef2rcv2k12.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--n_3UDO8G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/do1psncyyxef2rcv2k12.png" alt="AI availability results" width="800" height="241"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Clicking on a category result on the right hand side (such as Successful or Failed), a new pane opens showing the list of the ping requests in the selected category. By clicking on one of them, the trace of that request is shown:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yOC9EUmV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5t84iopr1tij7w5v3a73.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yOC9EUmV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5t84iopr1tij7w5v3a73.png" alt="Ping web trace" width="800" height="399"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Going back to the Availability section, you can set alerting by selecting your ping test and clicking on the three dots menu &lt;code&gt;Open Rules (Alerts) page&lt;/code&gt;:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iE1r-ax4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/heqy0gglyc3nrhxfkxn6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iE1r-ax4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/heqy0gglyc3nrhxfkxn6.png" alt="Alert rules page menu" width="800" height="272"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here you are basically in Azure Monitor and you see the list of alert rules in your subscriptions. Selecting the previously created alert, a new page where you can customize alerting rules opens:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Xt4PfcWm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/954r8qlto4fc2zx0lhee.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Xt4PfcWm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/954r8qlto4fc2zx0lhee.png" alt="Alert rules page" width="800" height="665"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;These two posts explain how to write health checks in your dotnet application using the dedicated SDK provided by Microsoft. Then, the second post shows the Azure Application Insights setup to ping and consume these endpoints in a real-like environment, with alerting support.&lt;/p&gt;




&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@nikkotations?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;nikko macaspac&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/health?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>azure</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Implementing health checks PT.1 - Asp.Net Core 6 configuration</title>
      <dc:creator>Andrea Grillo</dc:creator>
      <pubDate>Mon, 30 May 2022 21:58:51 +0000</pubDate>
      <link>https://forem.com/krusty93/implementing-health-checks-pt1-aspnet-core-6-configuration-6gp</link>
      <guid>https://forem.com/krusty93/implementing-health-checks-pt1-aspnet-core-6-configuration-6gp</guid>
      <description>&lt;ol&gt;
&lt;li&gt;Implementing health checks PT.1 - Asp.Net Core 6 configuration (this post)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/krusty93/implementing-health-checks-pt2-azure-application-insights-configuration-51b9"&gt;Implementing health checks PT.2 - Azure Application Insights configuration&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;When a production running application is not available to the customers due to technical issues, we are not only losing money but also losing trust and faith in our customers. In fact, they may spread a bad word about our product on social media, friends, store reviews or - even worst - move to competitors.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;Basically, when an application is not available to the stakeholders, it is as if it didn't exist.&lt;/u&gt;&lt;/p&gt;

&lt;p&gt;Of course it may happen. It might because of a human mistake, an erroneous configuration or a bug. Or maybe because our cloud provider has some connectivity issues.&lt;/p&gt;

&lt;p&gt;Monitoring the application availability it also allows you to elaborate some metrics to define SLA of your service.&lt;/p&gt;

&lt;p&gt;For these reasons, we must know immediately when application goes offline.&lt;/p&gt;

&lt;p&gt;One of the most used and simplest techniques are health checks endpoint.&lt;br&gt;
An health check endpoint, is an API exposed by an application that has no logic except for replying with a positive status code; if the caller doesn't receive any response or the status code is not the one expected, it means there is an error and an alert must be sent using the technology of your choice (email, push notification.. SMS and so on).&lt;/p&gt;

&lt;p&gt;Of course, Asp.Net Core provides a built-in mechanism to implement an health check endpoint.&lt;/p&gt;
&lt;h2&gt;
  
  
  Implementing a simple health check endpoint
&lt;/h2&gt;

&lt;p&gt;Create an Asp.Net Core project. For this demo (you can find the source code at the end of this post) I am going to use dotnet 6 with the new minimal host startup template.&lt;/p&gt;

&lt;p&gt;Install the NuGet package &lt;code&gt;Microsoft.Extensions.Diagnostics.HealthChecks&lt;/code&gt; and configurate it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddHealthChecks&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapHealthChecks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/alive"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the application and navigate to &lt;code&gt;localhost:&amp;lt;port&amp;gt;/alive&lt;/code&gt; and you should see something like this:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fffacj3u3fq62xrjek6dq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fffacj3u3fq62xrjek6dq.png" alt="Healthy endpoint"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You if you try to call the same endpoint with Postman or a similar tool, you see the status code is &lt;code&gt;200 OK&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And that's it - the health check endpoint is ready to be regularly consumed by a watch dog application 🎉&lt;/p&gt;
&lt;h3&gt;
  
  
  Advanced scenario
&lt;/h3&gt;

&lt;p&gt;It might be a scenario where the application is online but it can't communicate with the database for some reason. Potentially it is a big disservice to our customers and it is likely that users cannot perform almost all of the actions.&lt;/p&gt;
&lt;h4&gt;
  
  
  Entity Framework Health Checks
&lt;/h4&gt;

&lt;p&gt;Also Entity Framework provides a built-in mechanism to monitor the connectivity between an application and the SQL instance database.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;NB: to go ahead, of course you must have Entity Framework configured and pointing to a running SQL database&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Install the package &lt;code&gt;Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore&lt;/code&gt; from NuGet and configure it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;services.
AddHealthChecks()
.AddDbContextCheck&amp;lt;MyDbContext&amp;gt;("/dbcontext");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running the application and pointing again to &lt;code&gt;localhost:&amp;lt;port&amp;gt;/alive&lt;/code&gt; you should have the same result:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuyovhhnz510jw575l133.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuyovhhnz510jw575l133.png" alt="Health dbcontext endpoint"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By the default, under the hood it checks for:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The DbContextHealthCheck calls EF Core's CanConnectAsync method. You can customize what operation is run when checking health using AddDbContextCheck method overloads.&lt;br&gt;
The name of the health check is the name of the TContext type.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It now works but there is still a problem: no matter what health check endpoint we hit (&lt;code&gt;/alive&lt;/code&gt; or &lt;code&gt;/dbcontext&lt;/code&gt;), both checks are performed so exposing two different APIs is useless.&lt;/p&gt;

&lt;p&gt;How is it possible to set different endpoints for different checks? The answer is, through health checks names.&lt;/p&gt;

&lt;p&gt;Change the endpoints registration as follow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const string ALIVE = "alive";
const string ALIVE_DBCONTEXT = "dbcontext";

...
builder.Services.AddHealthChecks().AddDbContextCheck&amp;lt;MyDbContext&amp;gt;(name: ALIVE_DBCONTEXT);

app.MapHealthChecks(
    "/alive",
    new HealthCheckOptions { Predicate = (c) =&amp;gt; c.Name == ALIVE });

app.MapHealthChecks(
    "/dbcontext",
    new HealthCheckOptions { Predicate = (c) =&amp;gt; c.Name == ALIVE_DBCONTEXT });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, we are giving a name to a specific health check endpoint. When the name is matched, only the specific health check is executed.&lt;/p&gt;

&lt;p&gt;To have a confirmation, we can do a test:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;run the application and the database in a local Docker container&lt;/li&gt;
&lt;li&gt;navigate to &lt;code&gt;localhost:&amp;lt;port&amp;gt;/alive&lt;/code&gt;: you should get &lt;code&gt;Healthy&lt;/code&gt; as result&lt;/li&gt;
&lt;li&gt;stop the database container and refresh the page: you should still get &lt;code&gt;Healthy&lt;/code&gt; as result&lt;/li&gt;
&lt;li&gt;navigate to &lt;code&gt;localhost:&amp;lt;port&amp;gt;/dbcontext&lt;/code&gt;: you should get &lt;code&gt;Unhealthy&lt;/code&gt; as result&lt;/li&gt;
&lt;li&gt;restart the database container and refresh the page: you should get &lt;code&gt;Healthy&lt;/code&gt; as result&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Custom checks: Azure Cosmos DB
&lt;/h4&gt;

&lt;p&gt;Asp.Net allows to define some custom code while performing an health check. This is the case where we can test our connection to an Azure Cosmos DB instance, because the .NET SDK doesn't provide any mechanism to check the connection between the application and the database service.&lt;/p&gt;

&lt;p&gt;The following example tests the connection using &lt;code&gt;container.ReadContainerAsync()&lt;/code&gt;, a method exposed by the Cosmos SDK that needs database access to be executed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Cosmos initialization&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddHealthChecks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddDbContextCheck&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MyDbContext&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ALIVE_DBCONTEXT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddCheck&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CosmosHealthChecker&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ALIVE_COSMOS&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapHealthChecks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"/cosmos"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;HealthCheckOptions&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Predicate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ALIVE_COSMOS&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CosmosHealthChecker&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IHealthCheck&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IServiceScopeFactory&lt;/span&gt; &lt;span class="n"&gt;_serviceScopeFactory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IConfiguration&lt;/span&gt; &lt;span class="n"&gt;_configuration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CosmosHealthChecker&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;CosmosHealthChecker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;IServiceScopeFactory&lt;/span&gt; &lt;span class="n"&gt;scopeFactory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;IConfiguration&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CosmosHealthChecker&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_serviceScopeFactory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scopeFactory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_configuration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_logger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HealthCheckResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;CheckHealthAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;HealthCheckContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;collectionName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_configuration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Cosmos:Collection"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

            &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;IServiceScope&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_serviceScopeFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateScope&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;Database&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ServiceProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
            &lt;span class="n"&gt;Container&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetContainer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;collectionName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadContainerAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HealthCheckResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HealthStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Healthy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Error pinging Cosmos db"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HealthCheckResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Registration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FailureStatus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"An unhealthy result."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;This post explains how we can provide some health check endpoints in an Asp.Net project with just few lines of code 😎&lt;/p&gt;

&lt;p&gt;As usual the source code is available in my &lt;a href="https://github.com/Krusty93/HealthChecks" rel="noopener noreferrer"&gt;GitHub profile&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@bruno_nascimento?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Bruno Nascimento&lt;/a&gt; on &lt;a href="https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Public Service Announcement: I've joined the SRE team</title>
      <dc:creator>Andrea Grillo</dc:creator>
      <pubDate>Sun, 13 Feb 2022 11:53:12 +0000</pubDate>
      <link>https://forem.com/krusty93/public-service-announcement-ive-joined-the-sre-team-2h89</link>
      <guid>https://forem.com/krusty93/public-service-announcement-ive-joined-the-sre-team-2h89</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Cover image by &lt;a href="https://unsplash.com/@brianna_santellan?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Brianna Santellan&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/career-promotion?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since I joined my current company 9 months ago, I have always looked at the SRE team with interest. They look to be experts in everything: technology stack, application domain knowledge, software architecture, the ability to handle hot and risky tasks, DevOps techniques, cloud services and infrastructure. They literally looked so brilliant.&lt;/p&gt;

&lt;p&gt;Since then, I simply wanted to become one of them. So I thought that I had to work hard to fill the knowledge gap between me and them and then ask to join the team one day. I never explicity asked for a team change but few - and apparently right - people knew I would like it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you don't known what an SRE is, it stands for Site Reliability Engineer. It is a software engineering approach invented by Google in the 00s but became public knowledge only in the last years. Lot of companies like Amazon and Netflix adopted this technique. It is like a DevOps subset approach, but teams members have a software development background and they can fill the gap between devs and operation teams. SREs have to make sure the system runs smoothly. Resources provisioning, infrastructure, bugs, check and fix of production data, automation of ripetitive tasks in order to remove the toil is what they do everyday.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In an afternoon about a month ago, my boss called me on Teams and I thought the reason was a task I was working on. Instead, he asked me if it was true that I was interested in joining the SRE team, because they probably needed more people. The question was unofficial because everything needed to be sorted out yet but obviously I gave the green light on my candidature. At that moment I didn't realize immediatly what was going on and I felt completely normal. After the call I started to understand what happened and I was so excited about the news! A couple of week later I received another call from my boss and he confirmed the move. Deal was done!&lt;/p&gt;

&lt;p&gt;For the first time in my work career, I can finally say that I am not a software developer anymore, but an SRE! 🎉&lt;/p&gt;

</description>
      <category>sre</category>
      <category>devops</category>
      <category>career</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>Enforcing a consistent code style and quality in your team-wide .NET projects</title>
      <dc:creator>Andrea Grillo</dc:creator>
      <pubDate>Thu, 06 Jan 2022 14:18:54 +0000</pubDate>
      <link>https://forem.com/krusty93/enforcing-a-consistent-code-quality-and-style-in-your-team-wide-net-projects-4m62</link>
      <guid>https://forem.com/krusty93/enforcing-a-consistent-code-quality-and-style-in-your-team-wide-net-projects-4m62</guid>
      <description>&lt;p&gt;Each developer has his/her own coding style, usually inherited from personal preferences, habits and years of experience. Just think of naming conventions, indentation, spaces, curly braces, blank lines, etc.: there are many elements that can be typed in various ways, and none of them are wrong but just different.&lt;/p&gt;

&lt;p&gt;Especially in big teams, mixing different styles has some consequences:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;codebase is not coherent due to lot of discrepancies in code style&lt;/li&gt;
&lt;li&gt;hundreds of compiler warnings/suggestions may hide the important ones&lt;/li&gt;
&lt;li&gt;new developers don't know what style they shall use to write code in early days&lt;/li&gt;
&lt;li&gt;PR reviews may become a fight between people with different point of views&lt;/li&gt;
&lt;li&gt;reading the code could be difficult - just think at the variable naming convention that should help the reader to understand the variables scope&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then, the goal is creating a &lt;em&gt;team style&lt;/em&gt; rather than a &lt;em&gt;personal style&lt;/em&gt;. Moreover, keeping the solution free of warnings is definitely &lt;em&gt;a good move&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  I understand that enforcing code style rules in a team is important. But how can we achieve that?
&lt;/h2&gt;

&lt;p&gt;Just few years ago, Microsoft added the &lt;code&gt;.editorconfig&lt;/code&gt; file support to Visual Studio. This kind of file defines a set rules that override the local Visual Studio settings. So you can work on multiple projects owned by different teams with their own rules without having to change every time your local Visual Studio settings.&lt;/p&gt;

&lt;p&gt;And above all, &lt;strong&gt;the whole team has the same settings&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Last but not least, it is possible to define rules about code quality as well. To give you an idea, it is possible to throw a compiler error if an &lt;code&gt;IDisposable&lt;/code&gt; object is not properly disposed, or when an &lt;code&gt;async&lt;/code&gt; method is not &lt;code&gt;await&lt;/code&gt;ed.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;NOTE: Visual Studio Code needs &lt;a href="https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig" rel="noopener noreferrer"&gt;this extension&lt;/a&gt; to work with &lt;code&gt;.editorconfig&lt;/code&gt; files.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Starting with &lt;code&gt;.editorconfig&lt;/code&gt; files is quite simple: create a file named &lt;code&gt;.editorconfig&lt;/code&gt;. and put it in the solution folder; then add it to the Visual Studio solution by right clicking on the solution file -&amp;gt; Add -&amp;gt; Existing Item:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3a0b3b6uaa2q4xhyokfx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3a0b3b6uaa2q4xhyokfx.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F830ktof2gd9oec7k67in.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F830ktof2gd9oec7k67in.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Visual Studio displays an UI when you try to open the &lt;code&gt;.editorconfig&lt;/code&gt; file, but personally I don't like it. I think it is way quicker to manually edit it using VS Code.&lt;/p&gt;

&lt;p&gt;As covered later in this post, &lt;code&gt;.editorconfig&lt;/code&gt; files support inheritance, so the first thing to do is to set the nearly created file as the top most using the &lt;code&gt;root&lt;/code&gt; property:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;root&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, you can set any rule you would like to set. Personally, I start with the indentation rules for any file in the solution, and then I go deeper with more specific rules for each file extension and namespace. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="err"&gt;[*]&lt;/span&gt; &lt;span class="c"&gt;# Any file uses space rather than tabs
&lt;/span&gt;&lt;span class="py"&gt;indent_style&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;space&lt;/span&gt;

&lt;span class="c"&gt;# Code files
&lt;/span&gt;&lt;span class="err"&gt;[*.{cs,csx,vb,vbx}]&lt;/span&gt; &lt;span class="c"&gt;# these files use 4 spaces to indent the code
&lt;/span&gt;&lt;span class="py"&gt;indent_size&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;4&lt;/span&gt;
&lt;span class="py"&gt;insert_final_newline&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;charset&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;utf-8-bom&lt;/span&gt;

&lt;span class="c"&gt;# XML project files
&lt;/span&gt;&lt;span class="err"&gt;[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]&lt;/span&gt;
&lt;span class="py"&gt;indent_size&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;2&lt;/span&gt;

&lt;span class="c"&gt;# XML config files
&lt;/span&gt;&lt;span class="err"&gt;[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]&lt;/span&gt;
&lt;span class="py"&gt;indent_size&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;2&lt;/span&gt;

&lt;span class="c"&gt;# JSON files
&lt;/span&gt;&lt;span class="err"&gt;[*.json]&lt;/span&gt;
&lt;span class="py"&gt;indent_size&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;2&lt;/span&gt;

&lt;span class="c"&gt;# Powershell files
&lt;/span&gt;&lt;span class="err"&gt;[*.ps1]&lt;/span&gt;
&lt;span class="py"&gt;indent_size&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;2&lt;/span&gt;

&lt;span class="c"&gt;# Shell script files
&lt;/span&gt;&lt;span class="err"&gt;[*.sh]&lt;/span&gt;
&lt;span class="py"&gt;end_of_line&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;lf&lt;/span&gt;
&lt;span class="py"&gt;indent_size&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;2&lt;/span&gt;

&lt;span class="c"&gt;# Dotnet code style settings:
&lt;/span&gt;&lt;span class="err"&gt;[*.{cs,vb}]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can set hundreds of rules, of any type. On &lt;a href="https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/categories" rel="noopener noreferrer"&gt;Microsoft Docs&lt;/a&gt; there is an (almost) complete list of all the available rules that you can try locally and find the balance that suits best for your needs.&lt;br&gt;
Each rule can have a specific severity: you can treat warnings as errors or demote them to suggestions, or even disable them. Be careful, the most bottom rule has the precedence over the rest.&lt;/p&gt;

&lt;p&gt;By now, Visual Studio is reading the &lt;code&gt;.editorconfig&lt;/code&gt; file and changing the local settings but as you may noticed, if you set some warning-default rule to &lt;code&gt;error&lt;/code&gt; and try to build the project, you realized that the compiler works even if Visual Studio is showing the rule broke. This happens because you need to setup MSBuild by simply adding these two properties to each of your &lt;code&gt;.csproj&lt;/code&gt; (or the &lt;code&gt;Directory.Build.props&lt;/code&gt; parent file):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
    **&lt;span class="nt"&gt;&amp;lt;EnableNETAnalyzers&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/EnableNETAnalyzers&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;EnforceCodeStyleInBuild&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/EnforceCodeStyleInBuild&amp;gt;&lt;/span&gt;**
&lt;span class="nt"&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;_NOTE: By default, Visual Studio analyses open documents only. If you wish to scan the entire solution go in &lt;code&gt;Tools&lt;/code&gt; -&amp;gt; &lt;code&gt;Options&lt;/code&gt; -&amp;gt; &lt;code&gt;Text Editor&lt;/code&gt; -&amp;gt; &lt;code&gt;C#&lt;/code&gt; (or your favorite language) -&amp;gt; &lt;code&gt;Advanced&lt;/code&gt; -&amp;gt; Set &lt;code&gt;Background Analysis Scope&lt;/code&gt; to &lt;code&gt;Entire Solution&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Namespace rules
&lt;/h2&gt;

&lt;p&gt;Sometimes you want to define some rule (or exception) for a specific project or namespace. For example, I want to enforce the suffix &lt;code&gt;Async&lt;/code&gt; to any asynchronous method in the solution but for unit tests and controllers.&lt;br&gt;
Luckily &lt;code&gt;.editorconfig&lt;/code&gt; files allow you to achieve a solution quite straightforward. Basically, you have two ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;defining exceptions in the root &lt;code&gt;.editorconfig&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;use inheritance writing others files&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Define exceptions in the root file
&lt;/h3&gt;

&lt;p&gt;At the bottom of the &lt;code&gt;.editorconfig&lt;/code&gt;, define the namespace of where you would like to apply the exception. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="err"&gt;[src/**{.UnitTests,/Controllers}/**.cs]&lt;/span&gt;
&lt;span class="c"&gt;# Async methods should have "Async" suffix
&lt;/span&gt;&lt;span class="py"&gt;dotnet_naming_rule.async_methods_end_in_async.severity&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;none&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Using inheritance
&lt;/h3&gt;

&lt;p&gt;Inheritance is a good alternative. Do you remember the &lt;code&gt;root&lt;/code&gt; property we set at the beginning? That means that the given &lt;code&gt;.editorconfig&lt;/code&gt; is applied to the whole solution. But however it is possible to create others &lt;code&gt;.editorconfig&lt;/code&gt; files in subfolders that will inherit all the rules from the &lt;code&gt;root&lt;/code&gt; file. &lt;strong&gt;Rules defined here takes the precedence&lt;/strong&gt;. Getting back the previous example, you can navigate to the &lt;code&gt;Controllers&lt;/code&gt;/&lt;code&gt;UnitTests&lt;/code&gt; folders and create a new file. Do NOT define the &lt;code&gt;root=true&lt;/code&gt; property and add the rule:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="c"&gt;# Async methods should have "Async" suffix
&lt;/span&gt;&lt;span class="py"&gt;dotnet_naming_rule.async_methods_end_in_async.severity&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;none&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Shall I manually change any code formatting error?
&lt;/h3&gt;

&lt;p&gt;This is a tricky question with several solutions.&lt;/p&gt;

&lt;p&gt;The first solution is to use the built-in Visual Studio feature - &lt;code&gt;Code clean up&lt;/code&gt;. Personally I don't like it for the following reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it has a specific shortcut (CTRL+K, CTRL+E) that developers may forget to use every time they edit a file (we work under pressure sometimes!)&lt;/li&gt;
&lt;li&gt;it's annoying&lt;/li&gt;
&lt;li&gt;it's a &lt;strong&gt;local&lt;/strong&gt; setting that must be configured and &lt;em&gt;there is no way to share it with your team&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For these reasons, I had been using &lt;code&gt;Productivity Power Tools&lt;/code&gt;, an (historical) extensions developed by Microsoft. It provides a feature to format the entire file when it is saved (through UI or the shortcut CTRL+S). It makes simply impossible to forget to format your file before committing it in source control.&lt;/p&gt;

&lt;p&gt;But, unfortunately Microsoft decided to remove this feature from the PPT 2022 version - apparently without any reason, there are &lt;em&gt;plenty&lt;/em&gt; of negative feedbacks for this.&lt;/p&gt;

&lt;p&gt;Luckily, a developer named &lt;code&gt;Elders&lt;/code&gt; has published an extension called &lt;a href="https://github.com/Elders/VSE-FormatDocumentOnSave" rel="noopener noreferrer"&gt;Format document on Save&lt;/a&gt; and it does simply what its name says: it formats the document when the user save it!&lt;/p&gt;

&lt;h4&gt;
  
  
  I want to sort and remove unnecessary using statement in my project, but I have to do it manually even if I set the rule. Any way to automate it?
&lt;/h4&gt;

&lt;p&gt;Yes, you can do it!&lt;br&gt;
Going in &lt;code&gt;Tools&lt;/code&gt; -&amp;gt; &lt;code&gt;Options&lt;/code&gt; -&amp;gt; &lt;code&gt;Format Document on Save&lt;/code&gt; you can set the commands to be executed when user saves the file.&lt;br&gt;
In order to format the file and then sort and removing using statements, set it as follow:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1qy1zio4o3jvnfocd79j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1qy1zio4o3jvnfocd79j.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h5&gt;
  
  
  Good, but any team member shall remember to set this feature on?
&lt;/h5&gt;

&lt;p&gt;Nope! This extension is able to read a file named &lt;code&gt;.formatconfig&lt;/code&gt; that you can check in your source control and share with your team. It doesn't need to be added to the Visual Studio solution, but just to stay in the same level of the solution file. Local user preferences are ignored.&lt;/p&gt;

&lt;p&gt;Here's an example of definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;root&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;true&lt;/span&gt;

&lt;span class="err"&gt;[*.*]&lt;/span&gt;
&lt;span class="py"&gt;enable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;enable_in_debug&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;command&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;Edit.FormatDocument Edit.RemoveAndSort&lt;/span&gt;
&lt;span class="py"&gt;allowed_extensions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;.*&lt;/span&gt;
&lt;span class="py"&gt;denied_extensions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Anything familiar? 🐱‍💻&lt;/p&gt;

&lt;h2&gt;
  
  
  Working example
&lt;/h2&gt;

&lt;p&gt;Last thing to do is leaving you a link to a repository where you can find a fully working solution and especially an &lt;code&gt;.editorconfig&lt;/code&gt; file with lot of rules defined. You can get the remaining ones on &lt;a href="https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/overview" rel="noopener noreferrer"&gt;Microsoft Docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Krusty93/CodeStyle" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enjoy! 🎉&lt;/p&gt;

&lt;p&gt;UPDATE: Looking at the &lt;a href="https://github.com/dotnet/roslyn/issues/40163" rel="noopener noreferrer"&gt;Visual Studio 2022 development roadmap&lt;/a&gt;, the next preview (17.2) includes a synchronization between the &lt;code&gt;.editorconfig&lt;/code&gt; file and the code clean up profile. This is really exciting!&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>visualstudio</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Hide sensitive data in Azure Application Insights logs of our ASP.NET (Core) APIs</title>
      <dc:creator>Andrea Grillo</dc:creator>
      <pubDate>Fri, 24 Dec 2021 08:36:11 +0000</pubDate>
      <link>https://forem.com/krusty93/hide-sensitive-data-in-azure-application-insights-logs-of-our-aspnet-core-apis-4ji7</link>
      <guid>https://forem.com/krusty93/hide-sensitive-data-in-azure-application-insights-logs-of-our-aspnet-core-apis-4ji7</guid>
      <description>&lt;p&gt;Azure Application Insights is a wonderful tool to log data and errors in your (not only) .NET applications.&lt;/p&gt;

&lt;p&gt;But the Microsoft SDKs don't solve an issue I faced in one of the project I worked on: would it be possible to hide sensitive data from logs? Nobody should be able to read user sensitive data, especially from logs.&lt;/p&gt;

&lt;h2&gt;
  
  
  How we used to log APIs payloads
&lt;/h2&gt;

&lt;p&gt;We have various log sources in our distributed system and one of them is the APIM (Azure Api Management): with a simple checkbox you can log APIs requests and responses bodies. But since it is a &lt;em&gt;simple checkbox&lt;/em&gt;, you can't do anything more than that.&lt;br&gt;
So, we moved our logging system out of it.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4eukiiei5yk9vof93g1n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4eukiiei5yk9vof93g1n.png" alt="APIM log checkbox"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Where we log APIs payloads now
&lt;/h2&gt;

&lt;p&gt;The easiest way to do log in front of all the APIs in an ASP.NET Core project, is to add a &lt;code&gt;FilterAttribute&lt;/code&gt; class to the ASP.NET action invocation pipeline:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 csharp
// Startup.cs
protected override void ConfigureMvc(MvcOptions options)
{    
    options.Filters.Add&amp;lt;ApplicationInsightsActionFilterAttribute&amp;gt;();    
}

// ApplicationInsightsActionFilterAttribute.cs
public class ApplicationInsightsActionFilterAttribute : ActionFilterAttribute
{
    internal const string REQUEST_TELEMETRY_KEY = "Request-Body";
    internal const string RESPONSE_TELEMETRY_KEY = "Response-Body";

    private readonly ILogger&amp;lt;ApplicationInsightsActionFilterAttribute&amp;gt; _logger;

    public ApplicationInsightsActionFilterAttribute(
        ILogger&amp;lt;ApplicationInsightsActionFilterAttribute&amp;gt; logger)
    {
        _logger = logger;
    }

    public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        if (context.HttpContext is null)
        {
            await next();
            return;
        }

        try
        {
            string fromBodyParameter = context.ActionDescriptor.Parameters
                .Where(item =&amp;gt; item.BindingInfo.BindingSource == BindingSource.Body)
                .FirstOrDefault()
                ?.Name.ToUpperInvariant();

            if (string.IsNullOrWhiteSpace(fromBodyParameter))
                return;

            KeyValuePair&amp;lt;string, object&amp;gt; arguments = context.ActionArguments
                .First(x =&amp;gt; x.Key.ToUpperInvariant() == fromBodyParameter);

            string argument = SerializeDataUsingObfuscator(arguments.Value);

            RequestTelemetry request = context.HttpContext.Features.Get&amp;lt;RequestTelemetry&amp;gt;();
            request.Properties.Add(REQUEST_TELEMETRY_KEY, argument);
        }
        catch (Exception ex)
        {
            _logger.LogCritical(ex, "Error executing the response logger task");
        }
        finally
        {
            await next();
        }
    }

    public override async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
    {
        if (context?.HttpContext is null)
        {
            await next();
            return;
        }

        try
        {
            if (context.Result is ObjectResult result &amp;amp;&amp;amp;
                result.Value != null)
            {
                string serializedObj = SerializeDataUsingObfuscator(result.Value);
                RequestTelemetry request = context.HttpContext.Features.Get&amp;lt;RequestTelemetry&amp;gt;();
                request.Properties.Add(RESPONSE_TELEMETRY_KEY, serializedObj);
            };
        }
        catch (Exception ex)
        {
            _logger.LogCritical(ex, "Error executing the response logger task");
        }
        finally
        {
            await next();
        }
    }

    private static string SerializeDataUsingObfuscator(object value) =&amp;gt;
        JsonConvert.SerializeObject(value,
            new JsonSerializerSettings
            {
                ContractResolver = new ObfuscatorContractResolver()
            });
}



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The code above is defining two methods: &lt;code&gt;OnActionExecutionAsync&lt;/code&gt; and &lt;code&gt;OnResultExecutionAsync&lt;/code&gt;. The first one is fired before the incoming request triggers the controller action; the latter is fired when the controller action returns. Let's focus on &lt;code&gt;OnActionExecutionAsync&lt;/code&gt; for a moment: firstly it gets the controller action parameters that have a body, if any - you can change that code to get the query strings, paths, or whatever you'd like - and then calls the method &lt;code&gt;SerializeDataUsingObfuscator&lt;/code&gt; passing the CLR object that represents the request body. To better explain this concept, consider the following API:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 csharp
public async Task&amp;lt;ActionResult&amp;gt; MyWonderfulApi([FromBody] RequestDto dto)
{
    // code
    return Ok();
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The object consumed by the &lt;code&gt;SerializeDataUsingObfuscator&lt;/code&gt; method is of type &lt;code&gt;RequestDto&lt;/code&gt; and it contains the incoming data.&lt;/p&gt;

&lt;p&gt;As you can see, we are using a custom JSON.NET &lt;code&gt;ContractResolver&lt;/code&gt; implementation, called &lt;code&gt;ObfuscatorContractResolver&lt;/code&gt;. This class serializes the request body following our obfuscation rules - that we are going to define in a minute.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Sensitive attribute
&lt;/h3&gt;

&lt;p&gt;In order to define which properties must be obfuscated in ApplicationInsights logs, we defined a custom attribute named &lt;code&gt;Sensitive&lt;/code&gt;. Moreover, this attribute allows some customization to obfuscate properties using different patterns - ie. truncate the string, show the first/last N chars, etc. Here's the definition and an usage example:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="c1"&gt;// WonderfulDto.cs&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WonderfulDto&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Sensitive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ObfuscationType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MaskChar&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;JsonProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pin"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Pin&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// SensitiveAttribute.cs using the Factory pattern to separate responsibilities&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;AttributeUsage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;AttributeTargets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Property&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AllowMultiple&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SensitiveAttribute&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Attribute&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;SensitiveBase&lt;/span&gt; &lt;span class="n"&gt;Sensitive&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;TruncateAfter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;SensitiveAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;ObfuscationType&lt;/span&gt; &lt;span class="n"&gt;obfuscationType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;truncateAfter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;TruncateAfter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;truncateAfter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;Sensitive&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GetSensitive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obfuscationType&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;SensitiveAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;ObfuscationType&lt;/span&gt; &lt;span class="n"&gt;obfuscationType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;TruncateAfter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;Sensitive&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GetSensitive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obfuscationType&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;SensitiveBase&lt;/span&gt; &lt;span class="nf"&gt;GetSensitive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ObfuscationType&lt;/span&gt; &lt;span class="n"&gt;obfuscationType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;obfuscationType&lt;/span&gt; &lt;span class="k"&gt;switch&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;ObfuscationType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HeadVisible&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SensitiveHeadVisible&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TruncateAfter&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;ObfuscationType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TailVisible&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SensitiveTailVisible&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;ObfuscationType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MaskChar&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SensitiveMaskChar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TruncateAfter&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;NotImplementedException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"None type &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obfuscationType&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;obfuscationType&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;Obfuscate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;strValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Sensitive&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Obfuscate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strValue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;Obfuscate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Guid&lt;/span&gt; &lt;span class="n"&gt;guid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Sensitive&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Obfuscate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;guid&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;Obfuscate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;dateTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Sensitive&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Obfuscate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dateTime&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;Obfuscate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Enum&lt;/span&gt; &lt;span class="n"&gt;@enum&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Sensitive&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Obfuscate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;@enum&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;ObfuscateDefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Sensitive&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ObfuscateDefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now that obfuscation rules are defined, I can show you how to use them in &lt;code&gt;ObfuscatorContractResolver&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="c1"&gt;// ObfuscatorContractResolver.cs&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ObfuscatorContractResolver&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DefaultContractResolver&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="n"&gt;IList&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;JsonProperty&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;CreateProperties&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MemberSerialization&lt;/span&gt; &lt;span class="n"&gt;memberSerialization&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;IList&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;JsonProperty&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;baseProperties&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateProperties&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;memberSerialization&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;sensitiveData&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SensitiveAttribute&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PropertyInfo&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetProperties&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;customAttributes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetCustomAttributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;jsonPropertyAttribute&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;customAttributes&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OfType&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;JsonPropertyAttribute&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FirstOrDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jsonPropertyAttribute&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;sensitiveAttribute&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;customAttributes&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OfType&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SensitiveAttribute&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FirstOrDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sensitiveAttribute&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;propertyName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jsonPropertyAttribute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PropertyName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToUpperInvariant&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="n"&gt;sensitiveData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;propertyName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sensitiveAttribute&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;sensitiveData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;baseProperties&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;processedProperties&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;JsonProperty&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JsonProperty&lt;/span&gt; &lt;span class="n"&gt;baseProperty&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;baseProperties&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sensitiveData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryGetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;baseProperty&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PropertyName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToUpperInvariant&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="n"&gt;SensitiveAttribute&lt;/span&gt; &lt;span class="n"&gt;sensitiveAttribute&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;baseProperty&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PropertyType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="n"&gt;baseProperty&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ValueProvider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ObfuscatorValueProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;baseProperty&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ValueProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sensitiveAttribute&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="n"&gt;processedProperties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;baseProperty&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;processedProperties&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ObfuscatorValueProvider.cs&lt;/span&gt;
    &lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ObfuscatorValueProvider&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IValueProvider&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IValueProvider&lt;/span&gt; &lt;span class="n"&gt;_valueProvider&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;SensitiveAttribute&lt;/span&gt; &lt;span class="n"&gt;_sensitiveAttribute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;ObfuscatorValueProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;IValueProvider&lt;/span&gt; &lt;span class="n"&gt;valueProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;SensitiveAttribute&lt;/span&gt; &lt;span class="n"&gt;sensitiveAttribute&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_valueProvider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;valueProvider&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_sensitiveAttribute&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sensitiveAttribute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="nf"&gt;GetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;originalValue&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_valueProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;originalValue&lt;/span&gt; &lt;span class="k"&gt;switch&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;strValue&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_sensitiveAttribute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Obfuscate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strValue&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;Guid&lt;/span&gt; &lt;span class="n"&gt;guid&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_sensitiveAttribute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Obfuscate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;guid&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;Enum&lt;/span&gt; &lt;span class="n"&gt;@enum&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_sensitiveAttribute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Obfuscate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;@enum&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;dateTime&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_sensitiveAttribute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Obfuscate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dateTime&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_sensitiveAttribute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ObfuscateDefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;originalValue&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;SetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// we don't care&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And that's it: based on the &lt;code&gt;Sensitive&lt;/code&gt; attribute definition over DTOs properties, JSON.NET understands if and how a given property should be serialized. The result will be sent to ApplicationInsights.&lt;/p&gt;

&lt;h3&gt;
  
  
  And what about my API response body?
&lt;/h3&gt;

&lt;p&gt;The solution is pretty straightforward. The code above shows the &lt;code&gt;OnResultExecutionAsync&lt;/code&gt; implementation: if code runs smooth and no exception is thrown, the response body serialized using the obfuscation rules is attached to the &lt;code&gt;RequestTelemetry&lt;/code&gt; object.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exception handling
&lt;/h3&gt;

&lt;p&gt;If an exception occurs, you will realize that the &lt;code&gt;OnResultExecutionAsync&lt;/code&gt; will not be fired. This happens because the flow runs out the normal ASP.NET action invocation pipeline.&lt;br&gt;
But adding the exception handling middleware is enough to handle this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IApplicationBuilder&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseExceptionHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;RequestTelemetry&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Features&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RequestTelemetry&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;serializedObj&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JsonConvert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SerializeObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;JsonSerializerSettings&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;ContractResolver&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ObfuscatorContractResolver&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ApplicationInsightsActionFilterAttribute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RESPONSE_TELEMETRY_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;serializedObj&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here you can see references to &lt;code&gt;SensitiveHeadVisible&lt;/code&gt;, &lt;code&gt;SensitiveMaskChar&lt;/code&gt; and &lt;code&gt;SensitiveTailVisible&lt;/code&gt; classes that implement some code to apply different obfuscation rules - such as hiding the value with a specific char, showing the string tail, and so on. You are free to create your preferred ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance
&lt;/h2&gt;

&lt;p&gt;After the implementation described in this post, we spent some time doing some benchmark to understand the performance impact over our APIs. So we searched the APIs with the biggest incoming and outgoing DTOs and then we stressed these APIs running 1000 requests each through Postman. We suddenly realized that we were going to lose 60-90ms for each request on each APIs! This performance drop was caused by the string serialization performed by JSON.NET, because the rest of the code needed a couple of milliseconds to run. Our code is fine then, how can we cut down the serialization time then?&lt;br&gt;
After some research, we found a simple way to minimize the performance impact, keeping our APIs safe. Basically, we understood that we didn't need to wait that the serialization process to be finished before invoking the invocation pipeline!&lt;/p&gt;

&lt;p&gt;Coming back at &lt;code&gt;ApplicationInsightsActionFilterAttribute&lt;/code&gt; class, we did the following changes:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="c1"&gt;// OnActionExecutionAsync&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;loggerTask&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;fromBodyParameter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ActionDescriptor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Parameters&lt;/span&gt;

    &lt;span class="c1"&gt;// same code as before&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loggerTask&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;loggerTask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogCritical&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Error executing the response logger task"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// OnResultExecutionAsync&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;loggerTask&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Result&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;ObjectResult&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt;

    &lt;span class="c1"&gt;// same code as before&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loggerTask&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;loggerTask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogCritical&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Error executing the response logger task"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This implementation doesn't wait for the serialization to be completed, but instead it runs a task that will be awaited AFTER the invocation pipeline is completed. Most of the times, the &lt;code&gt;loggerTask&lt;/code&gt; is already completed when awaited, so the impact is almost nil.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We saw how to hide sensitive information from Application Insights logs, keeping our data (and users) safe.&lt;br&gt;
Since all the logic is wrapped in a custom &lt;code&gt;ContractResolver&lt;/code&gt;, you can obfuscate properties everywhere in your code! For example, we used the same approach for the log generated by our &lt;code&gt;HttpClient&lt;/code&gt; instances.&lt;/p&gt;

&lt;p&gt;I hope this tutorial was helpful! 🎉&lt;/p&gt;

</description>
      <category>azure</category>
      <category>csharp</category>
      <category>aspnet</category>
      <category>logs</category>
    </item>
    <item>
      <title>.NET 6.0 console app - Configuration, tricks and tips</title>
      <dc:creator>Andrea Grillo</dc:creator>
      <pubDate>Wed, 17 Mar 2021 09:24:09 +0000</pubDate>
      <link>https://forem.com/krusty93/net-core-5-0-console-app-configuration-trick-and-tips-c1j</link>
      <guid>https://forem.com/krusty93/net-core-5-0-console-app-configuration-trick-and-tips-c1j</guid>
      <description>&lt;p&gt;One of the biggest .NET benefit is the flexibility and the portability of its features.&lt;br&gt;
As ASP.NET Core relies on the &lt;code&gt;IHostBuilder&lt;/code&gt;, we can do the same with a classic console application.&lt;br&gt;
This approach will help us to have a basic infrastructure in our application, including the support for logging, dependency injection, app settings and so on.&lt;/p&gt;

&lt;p&gt;To start with this, add the &lt;code&gt;Microsoft.Extensions.Hosting&lt;/code&gt; NuGet package to your project and replace the "Hello World template" with this code:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Program&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;CreateHostBuilder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RunConsoleAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExitCode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;IHostBuilder&lt;/span&gt; &lt;span class="nf"&gt;CreateHostBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateDefaultBuilder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Doing this, we are asking for an &lt;code&gt;HostBuilder&lt;/code&gt; instance with the default infrastructure implementation.&lt;br&gt;
The &lt;code&gt;RunConsoleAsync&lt;/code&gt; starts the host builder with the "classic console" capabilities, such as the listener for the CTRL+C shortcut. Finally, it returns the application exit code (default is 0).&lt;br&gt;
This is a starting point; now we can extend our capabilities.&lt;/p&gt;



&lt;h2&gt;
  
  
  IHostedService
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;Main&lt;/code&gt; method is the application entry point, but we now need to continue the execution of our app. To achieve this, add a class that implement the &lt;code&gt;IHostedService&lt;/code&gt; interface and register it to the IoC container using the extension method &lt;code&gt;AddHostedService&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;IHostBuilder&lt;/span&gt; &lt;span class="nf"&gt;CreateHostBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateDefaultBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConfigureServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;services&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddHostedService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Worker&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IHostedService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;StartAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;NotImplementedException&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;StopAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;NotImplementedException&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now at the startup, the application runs the &lt;code&gt;StartAsync&lt;/code&gt; method; &lt;br&gt;
Instead &lt;code&gt;StopAsync&lt;/code&gt; is executed when the is going to be terminated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use the Dependency Injection
&lt;/h2&gt;

&lt;p&gt;We can now use the dependency injection system provided by .NET.&lt;br&gt;
In the &lt;code&gt;ConfigureServices&lt;/code&gt; method, add the services you need to run your application:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyService&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IMyService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;PerformLongTaskAsync&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IMyService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;PerformLongTaskAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Worker&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IHostedService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IMyService&lt;/span&gt; &lt;span class="n"&gt;_myService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IMyService&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_myService&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ArgumentNullException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;StartAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_myService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PerformLongTaskAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;IHostBuilder&lt;/span&gt; &lt;span class="nf"&gt;CreateHostBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateDefaultBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConfigureServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;services&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddHostedService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

            &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddTransient&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IMyService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MyService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Logging and configuration
&lt;/h2&gt;

&lt;p&gt;Using the &lt;code&gt;HostBuilder&lt;/code&gt;'s &lt;code&gt;ConfigureLogging&lt;/code&gt; extension method we have a full access to the logging configuration. In this case, we want to replace the default .NET implementation with one of the most used logging library, Serilog.&lt;br&gt;
First of all, install Serilog NuGet packages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Serilog.Extensions.Hosting&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Serilog.Settings.Configuration&lt;/code&gt;
In this example, we are going to use the Console and the File sinks:&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Serilog.Sinks.Console&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Serilog.Sinks.File&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our goal is to log events in a log file when running in a &lt;code&gt;Production&lt;/code&gt; environment; we stick instead to the console when debugging the app.&lt;br&gt;
To start with this, edit the &lt;code&gt;CreateHostBuilder&lt;/code&gt; method as follow:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateDefaultBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConfigureLogging&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ClearProviders&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseSerilog&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;hostContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;loggerConfiguration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;loggerConfiguration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadFrom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hostContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConfigureServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;services&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddHostedService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

        &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddTransient&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IMyService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MyService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;logging.ClearProviders()&lt;/code&gt; is used to clear all the default &lt;code&gt;HostBuilder&lt;/code&gt; logging providers. These are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Console&lt;/li&gt;
&lt;li&gt;Debug&lt;/li&gt;
&lt;li&gt;Event Source&lt;/li&gt;
&lt;li&gt;EventLog (Windows only)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then we read from the &lt;code&gt;appsettings.json&lt;/code&gt; (added in the next paragraph) the Serilog configuration using &lt;code&gt;loggerConfiguration.ReadFrom.Configuration(hostContext.Configuration);&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now it's time to give a configuration to our app. Add the &lt;code&gt;appsettings.json&lt;/code&gt; file to your project (ensure to set the &lt;code&gt;BuildAction&lt;/code&gt; to &lt;code&gt;Content&lt;/code&gt; and the &lt;code&gt;Copy to Output Directory&lt;/code&gt; to &lt;code&gt;Copy if newer&lt;/code&gt;). Then, paste the Serilog's configuration:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nl"&gt;"Serilog"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"MinimumLevel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Default"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Information"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Override"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Microsoft"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Warning"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"System"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Warning"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"WriteTo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"File"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"log.txt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"rollingInterval"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Day"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"outputTemplate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"shared"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Enrich"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FromLogContext"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The snippet above is setting the Serilog default level to &lt;code&gt;Information&lt;/code&gt; (with some override for the &lt;code&gt;Microsoft&lt;/code&gt; and &lt;code&gt;System&lt;/code&gt; namespaces) and configure the &lt;code&gt;File&lt;/code&gt; sink. In particular, it rolls out a different log file each day.&lt;br&gt;
But as stated above, we want to redirect all the application output to the console when debugging the app. Then we need to setup the &lt;code&gt;appsetting.Development.json&lt;/code&gt; file to the project and override the Serilog configuration, changing the default minimum level and setting the console as output provider:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nl"&gt;"Serilog"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"MinimumLevel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Default"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Debug"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Override"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Microsoft"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Information"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"System"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Information"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"WriteTo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Console"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"theme"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Code, Serilog.Sinks.Console"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"outputTemplate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} &amp;lt;s:{SourceContext}&amp;gt;{NewLine}{Exception}"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Enrich"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FromLogContext"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We don't need to do anything else to support different environemnts than defining them in the &lt;code&gt;launchSettings&lt;/code&gt; file. Add a folder called &lt;code&gt;Properties&lt;/code&gt; and a file named &lt;code&gt;launchSettings.json&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"profiles"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Development"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"commandName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Project"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"environmentVariables"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"DOTNET_ENVIRONMENT"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Development"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Production"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"commandName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Project"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"environmentVariables"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"DOTNET_ENVIRONMENT"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Production"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;DOTNET_ENVIRONMENT&lt;/code&gt; variable is automatically read from the &lt;code&gt;HostBuilder&lt;/code&gt;. It is possible to customize them, more info are available in the &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-6.0" rel="noopener noreferrer"&gt;official doc&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
We are now ready to use the logger and eventually the values coming from the app settings files. Inject the &lt;code&gt;ILogger&lt;/code&gt; and the &lt;code&gt;IConfiguration&lt;/code&gt; instances in your classes:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Worker&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IHostedService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IMyService&lt;/span&gt; &lt;span class="n"&gt;_myService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;_configKey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IMyService&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IConfiguration&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_myService&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ArgumentNullException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="n"&gt;_logger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ArgumentNullException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="n"&gt;_configKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"ConfigKey"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;StartAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Read {key} from settings"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_configKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_myService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PerformLongTaskAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;em&gt;NOTE: In order to make this code working, you need to add a &lt;code&gt;ConfigKey&lt;/code&gt; property in your app settings. If you do the same in the &lt;code&gt;appsettings.Development.json&lt;/code&gt; but with a differenva lue, you could notice that value changes depending on the current environment&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We could also add the user secrets and environment variables support using:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConfigureLogging&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseSerilog&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;hostContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;loggerConfiguration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConfigureAppConfiguration&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;hostContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddEnvironmentVariables&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hostContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HostingEnvironment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsDevelopment&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddUserSecrets&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Program&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Terminates the app properly using Exit Code
&lt;/h2&gt;

&lt;p&gt;Injecting the &lt;code&gt;IHostApplicationLifetime&lt;/code&gt; instance in the &lt;code&gt;Worker&lt;/code&gt; class, we can terminate properly the console process when the job is done. Calling &lt;code&gt;StopApplication&lt;/code&gt; is enough and it will redirect the running path to the &lt;code&gt;StopAsync&lt;/code&gt; method. Moreover, we can use Exit Code to tell the user why the app stopped:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IHostApplicationLifetime&lt;/span&gt; &lt;span class="n"&gt;_hostLifetime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;_exitCode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IMyService&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IConfiguration&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IHostApplicationLifetime&lt;/span&gt; &lt;span class="n"&gt;hostLifetime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="n"&gt;_hostLifetime&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hostLifetime&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ArgumentNullException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hostLifetime&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;StartAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Read {key} from settings"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_configKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_myService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PerformLongTaskAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;_exitCode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OperationCanceledException&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The job has been killed with CTRL+C"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;_exitCode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;LogError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"An error occurred"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;_exitCode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;finally&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_hostLifetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;StopApplication&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;StopAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExitCode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_exitCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetValueOrDefault&lt;/span&gt;&lt;span class="p"&gt;(-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Shutting down the service with code {exitCode}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExitCode&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CompletedTask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Use command line arguments efficiently
&lt;/h2&gt;

&lt;p&gt;One of the better way to handle the command line arguments in your app, is through the &lt;code&gt;McMaster.Extensions.CommandLineUtils&lt;/code&gt; NuGet package.&lt;br&gt;
It is a fork of the deprecated Microsoft library and allow you to easily manage the input commands. I show you a brief example of use. You need to modify the &lt;code&gt;Program.cs&lt;/code&gt; as follow:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"MyApp"&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"My app is very cool 😎"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;HelpOption&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"-h"&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LongName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"help"&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Get info"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;VersionOptionFromMember&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"-v"&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MemberName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GetVersion&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Program&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"-o"&lt;/span&gt;
        &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Some option"&lt;/span&gt;
        &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CommandOptionType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SingleValue&lt;/span&gt;
        &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LongName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"option"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;Option&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;CommandLineApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExecuteAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Program&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;OnExecuteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;CreateHostBuilder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RunConsoleAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExitCode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;IHostBuilder&lt;/span&gt; &lt;span class="nf"&gt;CreateHostBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{...}&lt;/span&gt;

&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;GetVersion&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Program&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;Assembly&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetCustomAttribute&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AssemblyInformationalVersionAttribute&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()?.&lt;/span&gt;&lt;span class="n"&gt;InformationalVersion&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here's a description:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Command&lt;/code&gt;: gives a name and a description to you app&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HelpOption&lt;/code&gt;: enables the &lt;code&gt;-h&lt;/code&gt; (or &lt;code&gt;--help&lt;/code&gt;) option&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;VersionOption&lt;/code&gt;: returns the app version number&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Option&lt;/code&gt;: represents the value passed as input argument to your app&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then, the Main method needs some refactoring. Instead, the &lt;code&gt;OnExecuteAsync&lt;/code&gt; is called automatically by the framework (it becomes the old &lt;code&gt;Main&lt;/code&gt; method basically). Not that &lt;code&gt;OnExecuteAsync&lt;/code&gt; is &lt;strong&gt;not&lt;/strong&gt; static, so it can access to the class properties such has &lt;code&gt;Option&lt;/code&gt; - that you can register in the IoC container using the &lt;code&gt;IOptions&lt;/code&gt; pattern:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyOptions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Program&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"-o"&lt;/span&gt;
        &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Some option"&lt;/span&gt;
        &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CommandOptionType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SingleValue&lt;/span&gt;
        &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LongName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"option"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;Option&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConfigureServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;services&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddHostedService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

        &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configure&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MyOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Option&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddTransient&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IMyService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MyService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Worker&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IHostedService&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;MyOptions&lt;/span&gt; &lt;span class="n"&gt;_options&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IMyService&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IConfiguration&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IHostApplicationLifetime&lt;/span&gt; &lt;span class="n"&gt;hostLifetime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MyOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;
        &lt;span class="n"&gt;_options&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ArgumentNullException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here the &lt;code&gt;CommandLineUtils&lt;/code&gt; in action:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgqt98q7ddrdk45zzisey.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgqt98q7ddrdk45zzisey.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Unfortunately my console doesn't support emoji yet 😒&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;I hope this tutorial gave you the right hint to start your next project based on a .NET console application! These are just an introduction of all the features you can add, basically with no limits! Yeah, it is true: you can use everything you are already using in your ASP.NET Core projects through the &lt;code&gt;IHostBuilder&lt;/code&gt; interface. Sounds good, isn't it? 🐱‍👤&lt;/p&gt;

&lt;p&gt;As usual the source code is available in my &lt;a href="https://github.com/Krusty93/NetCoreConsoleAppTips" rel="noopener noreferrer"&gt;GitHub profile&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>beginners</category>
      <category>csharp</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Xamarin Android and iOS .resx localization with MvvmCross</title>
      <dc:creator>Andrea Grillo</dc:creator>
      <pubDate>Sat, 23 Jan 2021 00:10:21 +0000</pubDate>
      <link>https://forem.com/krusty93/xamarin-android-and-ios-resx-localization-with-mvvmcross-n02</link>
      <guid>https://forem.com/krusty93/xamarin-android-and-ios-resx-localization-with-mvvmcross-n02</guid>
      <description>&lt;p&gt;Almost any mobile app published to the PlayStore or AppStore should be localizated in different languages in order to get more clients as possible from anywhere in the world.&lt;/p&gt;

&lt;p&gt;MvvmCross is a 'MVVM framework' written exclusively to work with Xamarin and provides an easy way to share the localized resources among the different platforms using the same &lt;code&gt;.resx&lt;/code&gt; files. In this post I will show you how to share localized resources between an Android and an iOS apps.&lt;/p&gt;

&lt;h1&gt;
  
  
  Let's localize it!
&lt;/h1&gt;

&lt;p&gt;Our starting point is the standard MvvmCross solution structure, with a separate project for each platform and the &lt;code&gt;Core&lt;/code&gt; project with the common stuff. I created a single view called &lt;code&gt;HomeView&lt;/code&gt; with its view model &lt;code&gt;HomeViewModel&lt;/code&gt; on both platforms:&lt;/p&gt;

&lt;p&gt;Solution structure:&lt;br&gt;
&lt;br&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---M0hzQBA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/bfe4lpctefrjjpkj6sdu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---M0hzQBA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/bfe4lpctefrjjpkj6sdu.png" alt="Solution" width="415" height="624"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;HomeView&lt;/code&gt;s contain only a label in the middle of the screen with the text "Hello". Below the screenshots and the UI definition:&lt;/p&gt;

&lt;p&gt;Android:&lt;br&gt;
&lt;br&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yVFfB5IL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/q1kie991qtipn62yhcdf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yVFfB5IL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/q1kie991qtipn62yhcdf.png" alt="Android" width="800" height="1644"&gt;&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;androidx.constraintlayout.widget.ConstraintLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  android:orientation="vertical"
  android:layout_width="match_parent"
  android:layout_height="match_parent"&amp;gt;

  &amp;lt;androidx.appcompat.widget.AppCompatTextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Hello!"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintBottom_toBottomOf="parent"
      app:layout_constraintTop_toTopOf="parent"/&amp;gt;

&amp;lt;/androidx.constraintlayout.widget.ConstraintLayout&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;p&gt;iOS:&lt;br&gt;
&lt;br&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JOTIBf_G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/4e92xkv2r0hpc7jfhkfd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JOTIBf_G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/4e92xkv2r0hpc7jfhkfd.png" alt="Alt Text" width="562" height="1218"&gt;&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public override void ViewDidLoad()
{
    View.BackgroundColor = UIColor.White;

    base.ViewDidLoad();

    var label = new UILabel { Text = "Hello!" };

    Add(label);

    View.SubviewsDoNotTranslateAutoresizingMaskIntoConstraints();
    View.AddConstraints(
        label.WithSameCenterX(View),
        label.WithSameCenterY(View));

    var set = CreateBindingSet();
    set.Bind(label).ToLocalizationId("Hello");
    set.Apply();
 }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;p&gt;&lt;em&gt;NOTE: For more information on how to setup MvvmCross, refer to the &lt;a href="https://www.mvvmcross.com/documentation/getting-started/getting-started"&gt;official documentation&lt;/a&gt;.&lt;/em&gt;&lt;br&gt;
&lt;br&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;p&gt;At this point, we must install the NuGet package &lt;code&gt;MvvmCross.Plugin.ResxLocalization&lt;/code&gt; to the &lt;code&gt;Core&lt;/code&gt; project.&lt;br&gt;
&lt;br&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--w4p11S4a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/ehh2swkluau27onl8pqr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--w4p11S4a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/ehh2swkluau27onl8pqr.png" alt="Alt Text" width="800" height="359"&gt;&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;p&gt;Then creates a folder &lt;code&gt;Localization&lt;/code&gt; and add a resource file, let's call it &lt;code&gt;AppResources.resx&lt;/code&gt;. This file will contain the main language of our app.&lt;br&gt;
&lt;br&gt;&lt;br&gt;
Open it and be sure to set the &lt;code&gt;Access Modifier&lt;/code&gt; to &lt;code&gt;Public&lt;/code&gt;:&lt;br&gt;
&lt;br&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8w8GiiN_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/10yin1hd5fape4873uju.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8w8GiiN_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/10yin1hd5fape4873uju.png" alt="Alt Text" width="591" height="67"&gt;&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;br&gt;
Let's add our first localized resource, as per in the screenshot below:&lt;br&gt;
&lt;br&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QPXkMQIW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/fsfftxc2b6bi62iv7w6x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QPXkMQIW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/fsfftxc2b6bi62iv7w6x.png" alt="Alt Text" width="202" height="56"&gt;&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;br&gt;
&lt;br&gt;&lt;br&gt;
&lt;em&gt;NOTE: the MvvmCross convention uses the string &lt;code&gt;{ViewModelName}.{ResourceName}&lt;/code&gt; as key. In our case it is &lt;code&gt;HomeViewModel.Hello&lt;/code&gt;.&lt;/em&gt;&lt;br&gt;
&lt;br&gt;&lt;br&gt;
&lt;br&gt;&lt;br&gt;
Now we can proceed adding another resource file containing the translations for another language of our choices - Italian in my case. To do this, add a file called &lt;code&gt;AppResources.it.resx&lt;/code&gt; still under &lt;code&gt;Localization&lt;/code&gt; folder:&lt;br&gt;
&lt;br&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KHRsMhY---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/7kq7q2vsvgdw5awpla39.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KHRsMhY---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/7kq7q2vsvgdw5awpla39.png" alt="Alt Text" width="580" height="127"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Please, note that now the &lt;code&gt;Access Modifer&lt;/code&gt; has been set to &lt;code&gt;No code generation&lt;/code&gt;. In fact, it's only the main resx file that is going to generate the necessary code.&lt;/p&gt;

&lt;p&gt;Close the resx files and go in &lt;code&gt;HomeViewModel&lt;/code&gt; and:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implement the interface &lt;code&gt;IMvxLocalizedTextSourceOwner&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Define the following property:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public IMvxLanguageBinder LocalizedTextSource =&amp;gt;
            new MvxLanguageBinder("", GetType().Name);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;p&gt;&lt;em&gt;NOTE: For demostration purposes I am doing this in the (only) page's view model, but in a real environment it would be better to add this property to a single base view model since almost every page in our application would need to localize resources.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
&lt;br&gt;&lt;br&gt;
Register the plugin in the Mvx IoC container at the startup (&lt;code&gt;App.cs&lt;/code&gt; in my example):&lt;br&gt;
&lt;br&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Mvx.IoCProvider.RegisterSingleton&amp;lt;IMvxTextProvider&amp;gt;(new MvxResxTextProvider(AppResources.ResourceManager));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;p&gt;Now it's time to apply the translation via the binding set exposed by MvvmCross. Even you can do this by UI too, I prefer to set all the resources by code: in case of refactoring or changes, it's definitely easier to keep track of all the changes. Let's see how to do it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Android
&lt;/h2&gt;

&lt;p&gt;From the definition above, replace the &lt;code&gt;android:text&lt;/code&gt; definition with &lt;code&gt;android:id&lt;/code&gt;. You can see mine below:&lt;br&gt;
&lt;br&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;androidx.appcompat.widget.AppCompatTextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/txtHello"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintTop_toTopOf="parent"/&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need the control's &lt;code&gt;id&lt;/code&gt; (&lt;code&gt;txtHello&lt;/code&gt;) in the code behind in the next step.&lt;/p&gt;

&lt;p&gt;Go in &lt;code&gt;HomeView.cs&lt;/code&gt;. Write code to get the reference of the UI control (&lt;code&gt;txtHello&lt;/code&gt;) and to bind it to the &lt;code&gt;MvxLanguageBinder&lt;/code&gt; instance we defined into the view model through a built-in converter:&lt;br&gt;
&lt;br&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;protected override void OnCreate(Bundle bundle)
{
    base.OnCreate(bundle);
    SetContentView(Resource.Layout.home_view);

    var textView = FindViewById&amp;lt;AppCompatTextView&amp;gt;(Resource.Id.txtHello);

    var set = CreateBindingSet();

    set.Bind(textView)**.ToLocalizationId("Hello");**

    set.Apply();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;br&gt;&lt;br&gt;
Now your app is ready to rock! This is what you see if you press F5:&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LyWjuL6k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/d0n7cfenaectw67wm54k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LyWjuL6k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/d0n7cfenaectw67wm54k.png" alt="Alt Text" width="800" height="1644"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;Then go to Android's settings and change the language to Italian. Then open the app again:&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--B-qU7q_1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/0j02z0p5ifya2cwhws6s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--B-qU7q_1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/0j02z0p5ifya2cwhws6s.png" alt="Alt Text" width="800" height="1644"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;Urrah! 🎉🎉&lt;br&gt;
&lt;br&gt;&lt;br&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  iOS
&lt;/h2&gt;

&lt;p&gt;In iOS everything works just as what we did on the Android side.&lt;/p&gt;

&lt;p&gt;Creates the Mvx's binding set and apply the binding to the localized resource:&lt;br&gt;
&lt;br&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var set = CreateBindingSet();
set.Bind(label).ToLocalizationId("Hello");
set.Apply();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;p&gt;Change the iPhone language to Italian.. and here we go again 🎉&lt;br&gt;
&lt;br&gt;&lt;br&gt;
&lt;br&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9uRqzXCj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/o7eu18ygbs54u7stvuuj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9uRqzXCj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/o7eu18ygbs54u7stvuuj.png" alt="Alt Text" width="562" height="1218"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;The source code is available on &lt;a href="https://github.com/Krusty93/MvvmCrossLocalization/tree/main"&gt;my GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>xamarin</category>
      <category>mvvmcross</category>
      <category>localization</category>
    </item>
  </channel>
</rss>
