<?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>DEV Community: Wallism</title>
    <description>The latest articles on DEV Community by Wallism (@wallism).</description>
    <link>https://dev.to/wallism</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%2F743590%2F6e816b63-5dc2-46f2-ac17-534684671562.png</url>
      <title>DEV Community: Wallism</title>
      <link>https://dev.to/wallism</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wallism"/>
    <language>en</language>
    <item>
      <title>polling after CreateOrUpdate: polling failed</title>
      <dc:creator>Wallism</dc:creator>
      <pubDate>Mon, 22 Jul 2024 22:48:07 +0000</pubDate>
      <link>https://dev.to/wallism/polling-after-createorupdate-polling-failed-31p0</link>
      <guid>https://dev.to/wallism/polling-after-createorupdate-polling-failed-31p0</guid>
      <description>&lt;p&gt;When updating an Azure APIM API using Pulumi (pulumi up) we started seeing this obscure error.&lt;/p&gt;

&lt;p&gt;First thing to do is get more info via debug:&lt;br&gt;
&lt;code&gt;pulumi up --debug&lt;/code&gt;&lt;br&gt;
(if you're running this in a shell, make sure you've increased the buffer for the window (in options) because there's alot of extra logs!)&lt;/p&gt;

&lt;p&gt;Searching through that output we can see the PUT that tries to update the Open API (swagger), followed by a few GET polls to the same API. The value of note is the provisioningState, the first couple of calls were:&lt;/p&gt;

&lt;p&gt;"provisioningState": "InProgress"&lt;/p&gt;

&lt;p&gt;followed by a final:&lt;/p&gt;

&lt;p&gt;"provisioningState": "Failed"&lt;/p&gt;

&lt;p&gt;But no real info on what the issue actually is. To get that info we need to go into the APIM and manually try to update the Open API.&lt;/p&gt;

&lt;p&gt;In the Azure portal, navigate to APIs in the APIM, each API name has an ellipse next to it, click the ellipse for the API that is having the issue, then select "Import", then OpenAPI and select the json file that has your OpenAPI from your machine:&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%2Ftzx8n02y4zgd5gzga7ls.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%2Ftzx8n02y4zgd5gzga7ls.png" alt="apim openapi upload file"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click Import, then after a short amount of time you should see an error, ours came up like this:&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%2Fahhyyvdwlze5zn4hi253.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%2Fahhyyvdwlze5zn4hi253.png" alt="polling failed actual error"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From there should have enough info to resolve whatever the error is!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Github action to deploy to Azure container app fails with LinkedAuthorizationFailed</title>
      <dc:creator>Wallism</dc:creator>
      <pubDate>Wed, 07 Feb 2024 20:59:30 +0000</pubDate>
      <link>https://dev.to/wallism/github-action-to-deploy-to-azure-container-app-fails-with-linkedauthorizationfailed-4nd8</link>
      <guid>https://dev.to/wallism/github-action-to-deploy-to-azure-container-app-fails-with-linkedauthorizationfailed-4nd8</guid>
      <description>&lt;p&gt;I created my github action from the Azure portal, in the container app (capp) "Continuous deployment" (CD) section.&lt;br&gt;
The first capp I did this with worked fine, but the second one generates the error.&lt;br&gt;
One important detail, I put the second capp into the same container environment as the first (a very normal thing to do).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;This error should not occur any more as Contributor access is given on the resource group now, not on the container app and environment.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is the error:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The client 'guid' with object id 'guid' has permission to perform action 'Microsoft.App/containerApps/write' on scope '/subscriptions/guid/resourceGroups/MyRG/providers/Microsoft.App/containerApps/capp-02'; however, it does not have permission to perform action(s) 'Microsoft.App/managedEnvironments/join/action' on the linked scope(s) '/subscriptions/guid/resourceGroups/MyRG/providers/Microsoft.App/managedEnvironments/capp-environment' (respectively) or the linked scope(s) are invalid.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's actually a really clear error. The "client" (or in this case, Service Principal) does not have permission to do stuff on the capp environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why?
&lt;/h3&gt;

&lt;p&gt;When we setup CD and the Github action was created, a part of that process is the creation of a service principal (aka client). &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The client is what allows the action to securely update our resource(s) in Azure, you can find the client in Microsoft Entra ID -&amp;gt; App registrations. You can also see the permissions granted to the client on the capp and capp environment, under "Access Control (IAM)".&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When we create the first CD, the client is granted permission (Contributor) on both the capp and the capp environment. But when we create the second CD on the second capp, a second client is created but this second client is only granted access to the capp, &lt;strong&gt;not&lt;/strong&gt; the capp environment! I don't know why, I guess it's a bug.&lt;/p&gt;

&lt;h3&gt;
  
  
  The solution
&lt;/h3&gt;

&lt;p&gt;To fix the error, just go to the capp enviroment -&amp;gt; Access Control (IAM), then "Add role assignment" and grant the client &lt;code&gt;Contributor&lt;/code&gt; access.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Azure Az.Storage command returns "A task was canceled."</title>
      <dc:creator>Wallism</dc:creator>
      <pubDate>Wed, 10 Jan 2024 23:52:30 +0000</pubDate>
      <link>https://dev.to/wallism/azure-azstorage-command-returns-a-task-was-canceled-4j73</link>
      <guid>https://dev.to/wallism/azure-azstorage-command-returns-a-task-was-canceled-4j73</guid>
      <description>&lt;p&gt;Using az.storage version 6.1.0 with this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$context = New-AzStorageContext -StorageAccountName accountname
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(run after successfully connecting - Connect-AzAccount - and selecting the correct subscription - Set-AzAccount)&lt;/p&gt;

&lt;p&gt;I kept getting this error:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;New-AzStorageContext: A task was canceled.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not the most helpful error message! Turns out the fix is very simple, we now &lt;strong&gt;need to provide the key&lt;/strong&gt; as well (the previous version I was running did not require the key)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$context = New-AzStorageContext -StorageAccountName accountname -StorageAccountKey accountkey
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Easy...made difficult by the useless error message.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Connection creation correlation with slow response times - in Azure WebApp</title>
      <dc:creator>Wallism</dc:creator>
      <pubDate>Mon, 25 Sep 2023 21:27:15 +0000</pubDate>
      <link>https://dev.to/wallism/connection-creation-correlation-with-slow-response-times-15ep</link>
      <guid>https://dev.to/wallism/connection-creation-correlation-with-slow-response-times-15ep</guid>
      <description>&lt;p&gt;We were experiencing a strange performance issue with one of our API's running in an Azure Web App. None of the usual metrics were spiking in correlation with the response time spikes, CPU, memory, even request count didn't really correlate.&lt;/p&gt;

&lt;p&gt;Then I noticed a distinct correlation with, Connections.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AdJpD9a1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/auyzbdzptfzcoixpcl4i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AdJpD9a1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/auyzbdzptfzcoixpcl4i.png" alt="azure webapp metrics" width="800" height="487"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Red line = Response Time, blue line = Connections&lt;/p&gt;

&lt;p&gt;I had never seen this link before, so first I had to figure out exactly what "Connections" meant. Essentially it means Connections out, in our case a connection to an SQL Server.&lt;/p&gt;

&lt;p&gt;This leads to the question, how are we creating the connections...and are we doing something inefficient? Looking in the code, this is how we get a new connection:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;new System.Data.SqlClient.SqlConnection("valid-sql-connection-string")&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Nothing tricky there, at first I thought maybe we were creating a new connection every time and we could help the issue by re-using connections. That connection re-use actually happens automagically under the hood in the SqlClient library via &lt;a href="https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/sql-server-connection-pooling"&gt;pooling&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One key point from the docs for me was: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The connection pooler removes a connection from the pool after it has been idle for approximately 4-8 minutes&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Our spikes were happening every 15 min (it's an api, there must be an automated process running every 15 min), so (as you can see in the metrics graph) after a short period of time all of those connections that were created, get cleaned up and the pool drops to the minimum.&lt;/p&gt;

&lt;p&gt;Which leads to the next question, "Can I increase that minimum connection count?" &lt;/p&gt;

&lt;p&gt;Turns out that's really easy by adding &lt;a href="https://learn.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlconnection.connectionstring?view=dotnet-plat-ext-7.0"&gt;Min Pool Size&lt;/a&gt; to the connection string.&lt;/p&gt;

&lt;p&gt;So set Min Pool Size=70 in the connection string. Result, it still needs to create more connections sometimes but those spikes were significantly flattened. In fact, looking in App Insights, our average response times for one endpoint went from &amp;gt;10s (yes, seconds) to &amp;lt;800ms!&lt;/p&gt;

&lt;p&gt;Creating connections is expensive (I found this &lt;a href="https://stackoverflow.com/questions/19221687/why-do-we-need-to-set-min-pool-size-in-connectionstring"&gt;SO&lt;/a&gt; useful), but having never seen this correlation before, why did it impact this service so significantly and not others? I believe it is due to the fact that the DB is in a different region to the service (most of our other services are in the same region). That slight increase in latency (guessing ~100ms) blows out the connection creation time on a service as busy as this one.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Azure APIM - GET total call count for subscription</title>
      <dc:creator>Wallism</dc:creator>
      <pubDate>Thu, 21 Sep 2023 01:13:09 +0000</pubDate>
      <link>https://dev.to/wallism/azure-apim-get-total-call-count-for-subscription-52ih</link>
      <guid>https://dev.to/wallism/azure-apim-get-total-call-count-for-subscription-52ih</guid>
      <description>&lt;p&gt;How do you get the total call count for a given subscriptionId from Azure APIM?&lt;br&gt;
The easy but not overly useful way is in the Azure Portal, go to your APIM, then select the Analytics blade (under Monitoring), select the Subscriptions tab, pick your time range and there they are.&lt;/p&gt;

&lt;h2&gt;
  
  
  Programatically
&lt;/h2&gt;

&lt;p&gt;How to get this data programatically is far more useful and is also relatively easy, the only tricky part is getting the token.&lt;br&gt;
First, check out the &lt;a href="https://learn.microsoft.com/en-us/rest/api/apimanagement/current-preview/reports/list-by-subscription?tabs=HTTP"&gt;docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Your call will look like this:&lt;br&gt;
&lt;code&gt;GET https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ApiManagement/service/{serviceName}/reports/bySubscription?$filter={$filter}&amp;amp;api-version=2023-03-01-preview&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You can run that in Postman or similar tool (e.g. open source &lt;a href="https://hoppscotch.io/"&gt;Hoppscotch&lt;/a&gt;), however you will need an access token.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to get an access token
&lt;/h2&gt;

&lt;p&gt;The easiest way is, hit that "Try It" button on the docs page, after you are logged in, you should see a Bearer token in the Request Preview section. You can use that. I dropped mine into &lt;a href="https://jwt.io/#debugger-io"&gt;jwt debugger&lt;/a&gt; to check the expiry, it lasts one hour.&lt;/p&gt;

&lt;p&gt;OR - generate your own...&lt;/p&gt;

&lt;p&gt;I followed these &lt;a href="https://dzone.com/articles/getting-access-token-for-microsoft-graph-using-oau"&gt;instructions&lt;/a&gt; :)&lt;/p&gt;

&lt;p&gt;Essentially (super brief summary), call this &lt;code&gt;POST https://login.microsoftonline.com/{{AAD_name}}/oauth2/token&lt;/code&gt; with client_id and client_secret specified in the body (can't recall why but in my Postman my body is "x-www-form-encoded").&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Azure App Configuration task - An unexpected error occurred</title>
      <dc:creator>Wallism</dc:creator>
      <pubDate>Wed, 03 May 2023 23:37:58 +0000</pubDate>
      <link>https://dev.to/wallism/azure-app-configuration-task-an-unexpected-error-occurred-4pc5</link>
      <guid>https://dev.to/wallism/azure-app-configuration-task-an-unexpected-error-occurred-4pc5</guid>
      <description>&lt;p&gt;In Azure DevOps, using the task, "Azure App Configuration Push", I hit the rather obscure error, "An unexpected error occurred".&lt;/p&gt;

&lt;p&gt;First step to dig into this is to edit the release, adding the following debug variable -&amp;gt; &lt;code&gt;system.debug&lt;/code&gt; set the value to &lt;code&gt;true&lt;/code&gt;. Then re-run and you do get more detail about what actually went wrong. Looking through the log I can now see: &lt;/p&gt;

&lt;blockquote&gt;
&lt;h2&gt;
  
  
  [debug]{"name":"RestError","statusCode":403
&lt;/h2&gt;
&lt;/blockquote&gt;

&lt;p&gt;There is much more detail but that is all that was enough for me, following &lt;a href="https://learn.microsoft.com/en-au/azure/azure-app-configuration/push-kv-devops-pipeline"&gt;these instructions&lt;/a&gt; I knew I hadn't given my service principal the "App Configuration Data Owner" role in my Azure App Configuration instance. Once the role was added, the task ran successfully.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>pulumi stack - error: getting selected stack: failed to decrypt</title>
      <dc:creator>Wallism</dc:creator>
      <pubDate>Thu, 06 Apr 2023 03:48:38 +0000</pubDate>
      <link>https://dev.to/wallism/pulumi-stack-error-getting-selected-stack-failed-to-decrypt-1h0l</link>
      <guid>https://dev.to/wallism/pulumi-stack-error-getting-selected-stack-failed-to-decrypt-1h0l</guid>
      <description>&lt;p&gt;If you do &lt;code&gt;pulumi stack&lt;/code&gt; and get an error like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;error: getting selected stack: failed to decrypt: incorrect passphrase, please set PULUMI_CONFIG_PASSPHRASE to the correct passphrase or set PULUMI_CONFIG_PASSPHRASE_FILE to a file containing the passphrase&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Check what value is being used for the passphrase, in powershell, &lt;code&gt;$env:PULUMI_CONFIG_PASSPHRASE&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Ultimately you simply need to ensure the passphrase is correct. The issue I had was it looked correct but the project I was working on at the time had a different passphrase to our other projects. Turns out the passphrase for this project was "", an empty string.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Best practice suggestion - keep the passphrase consistent across all dev projects and stacks, then different for all prod stacks.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To change the password for a stack you can use &lt;a href="https://www.pulumi.com/docs/reference/cli/pulumi_stack_change-secrets-provider/"&gt;this&lt;/a&gt;: &lt;code&gt;pulumi stack change-secrets-provider passphrase&lt;/code&gt;. Apply this to ALL stacks. Also do it for the PROD stack but you want to use a different passphrase for that stack.&lt;/p&gt;

&lt;p&gt;Why make the passphrase consistent? When you start working with multiple Pulumi projects, when you change projects, you have to remember to log into the correct Pulumi backend, plus ensure you are logged into the correct Azure tenant and have the correct subscription selected. Having different passphrases is one more thing to trip up you and your developers and waste time. Some projects require more security, obviously in those cases you want to prefer being more secure vs being more convenient.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Failed to negotiate protocol, waiting for response timed out</title>
      <dc:creator>Wallism</dc:creator>
      <pubDate>Thu, 06 Apr 2023 00:44:13 +0000</pubDate>
      <link>https://dev.to/wallism/failed-to-negotiate-protocol-waiting-for-response-timed-out-jpk</link>
      <guid>https://dev.to/wallism/failed-to-negotiate-protocol-waiting-for-response-timed-out-jpk</guid>
      <description>&lt;p&gt;After upgrading to .Net 6 running &lt;code&gt;dotnet test myproj.csproj&lt;/code&gt; failed with this error:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Failed to negotiate protocol, waiting for response timed out after 90 seconds. This may occur due to machine slowness, please set environment variable VSTEST_CONNECTION_TIMEOUT to increase timeout.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I found the solution &lt;a href="https://www.c-sharpcorner.com/article/how-to-resolve-issue-of-test-project-not-running-the-unit-tests-after-upgrade-to/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And in case that link disappears, basically you need to add to following to the test project:&lt;br&gt;
&lt;code&gt;&amp;lt;PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" /&amp;gt;&lt;/code&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Pulumi with Serilog</title>
      <dc:creator>Wallism</dc:creator>
      <pubDate>Fri, 24 Mar 2023 05:27:27 +0000</pubDate>
      <link>https://dev.to/wallism/pulumi-with-serilog-3g61</link>
      <guid>https://dev.to/wallism/pulumi-with-serilog-3g61</guid>
      <description>&lt;p&gt;I use Serilog everywhere and I want to do the same in Pulumi projects. It provides all the flexibility I want/need for logging, so I try and bake it into any new project (&lt;em&gt;unless it's going to be a shared library, in which case I want minimal dependencies&lt;/em&gt;.)&lt;/p&gt;

&lt;p&gt;When doing this, we want to ensure we don't lose any of the console logging that Pulumi does.&lt;/p&gt;

&lt;p&gt;First thing, add the Serilog &lt;a href="https://www.nuget.org/packages/serilog/"&gt;nuget&lt;/a&gt; package. Then what I do is create a method somewhere to configure it. (this can usually go into the config file however with the way Pulumi runs, that doesn't work out so well, didn't for me anyway).&lt;/p&gt;

&lt;p&gt;Config method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    public static void ConfigureLogger()
    {

        Log.Logger = new LoggerConfiguration()
            .Enrich.FromLogContext()
            .Enrich.WithExceptionDetails(new DestructuringOptionsBuilder()
                .WithDefaultDestructurers()
                .WithDestructuringDepth(8)) // essential to limit the depth
            .WriteTo.PulumiLogSink()
            .WriteTo.File(path: $"{Environment.CurrentDirectory}\\log\\log.txt")
            .CreateLogger();

        Log.Information("Logging configured");

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

&lt;/div&gt;



&lt;p&gt;Either remove "WithExceptionDetails" or add that &lt;a href="https://github.com/RehanSaeed/Serilog.Exceptions"&gt;package&lt;/a&gt;. Same with WriteTo.File, I recommend adding, then you have a history.&lt;/p&gt;

&lt;p&gt;Then what we need to do, is implement PulumiLogSink...so we don't lose any of the normal Pulumi output.&lt;/p&gt;

&lt;p&gt;Info on building a custom sink is &lt;a href="https://github.com/serilog/serilog/wiki/Developing-a-sink"&gt;here&lt;/a&gt;. Following those instructions our Pulumi sink looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    public class PulumiLogSink : ILogEventSink
    {
        private readonly IFormatProvider _formatProvider;

        public PulumiLogSink(IFormatProvider formatProvider)
        {
            _formatProvider = formatProvider;
        }

        public void Emit(LogEvent logEvent)
        {
            var message = logEvent.RenderMessage(_formatProvider);
            if (logEvent.Level == LogEventLevel.Debug || logEvent.Level == LogEventLevel.Verbose)
                Pulumi.Log.Debug(message);
            if (logEvent.Level == LogEventLevel.Information)
                Pulumi.Log.Info(message);
            if (logEvent.Level == LogEventLevel.Warning)
                Pulumi.Log.Warn(message);
            if (logEvent.Level == LogEventLevel.Error || logEvent.Level == LogEventLevel.Fatal)
                Pulumi.Log.Error(message);
        }
    }

    public static class MySinkExtensions
    {
        public static LoggerConfiguration PulumiLogSink(
            this LoggerSinkConfiguration loggerConfiguration,
            IFormatProvider formatProvider = null)
        {
            return loggerConfiguration.Sink(new PulumiLogSink(formatProvider));
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, the first line of our Pulumi should call &lt;code&gt;ConfigureLogger()&lt;/code&gt; and voila, serilog logging hooked up so you can add whatever sinks you like. &lt;/p&gt;

&lt;p&gt;Tip, you probably want to add this using: &lt;code&gt;using Log = Serilog.Log;&lt;/code&gt; to ensure 'Log.' uses serilog and not the Pulumi logger.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>generate swagger file from dll</title>
      <dc:creator>Wallism</dc:creator>
      <pubDate>Fri, 26 Aug 2022 05:28:13 +0000</pubDate>
      <link>https://dev.to/wallism/generate-swagger-file-from-dll-3o0p</link>
      <guid>https://dev.to/wallism/generate-swagger-file-from-dll-3o0p</guid>
      <description>&lt;p&gt;To generate a swagger file from a dll you need to run a command like this:&lt;br&gt;
&lt;code&gt;dotnet swagger tofile --output "valid-file-path" "valid-dll-path" v1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To make that work there's a few steps to follow and few issues you might hit.&lt;/p&gt;

&lt;p&gt;First, you need &lt;a href="https://github.com/domaindrivendev/Swashbuckle.AspNetCore#retrieve-swagger-directly-from-a-startup-assembly"&gt;Swashbuckle.AspNetCore&lt;/a&gt;. Instructions to install are in the link, install the latest version globally, you may need a different, lower version depending on your project (I did).&lt;/p&gt;

&lt;p&gt;To install a specific version for a project, go to the root of that project in a ps or cmd window. Do the following:&lt;/p&gt;

&lt;p&gt;First, make sure you're running the correct version of dotnet sdk, see this &lt;a href="https://dev.to/wallism/dotnet-swagger-tofile-could-not-load-file-or-assembly-systemruntime-1po3"&gt;post&lt;/a&gt; and follow through to the SO article for how to change the sdk dotnet cli uses.&lt;br&gt;
Then install the correct version of Swashbuckle:&lt;br&gt;
&lt;em&gt;(ensure you are in your project root folder)&lt;/em&gt;&lt;br&gt;
&lt;code&gt;dotnet new tool-manifest&lt;/code&gt;&lt;br&gt;
&lt;code&gt;dotnet tool install --version 6.2.3 Swashbuckle.AspNetCore.Cli&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You should see in the output of the the install command where the dotnet-tools.json files is, that was updated by the command. If you run &lt;code&gt;dotnet swagger&lt;/code&gt; in any subfolder, it will use this version, not the globally installed version.&lt;/p&gt;

&lt;h2&gt;
  
  
  Note on version numbers
&lt;/h2&gt;

&lt;p&gt;when you run&lt;br&gt;
&lt;code&gt;dotnet tool install --version 6.5.0 Swashbuckle.AspNetCore.Cli&lt;/code&gt;&lt;br&gt;
ensure the version is the same as the nuget package version that is installed in your api project.&lt;br&gt;
So be careful when choosing your version or you will see an error like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Could not load file or assembly 'Swashbuckle.AspNetCore.Swagger, Version=6.4.0.0, Culture=neutral, PublicKeyToken=62657d7474907593'. The located assembly's manifest definition does not match the assembly reference.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
    </item>
    <item>
      <title>dotnet swagger tofile : Could not load file or assembly System.Runtime</title>
      <dc:creator>Wallism</dc:creator>
      <pubDate>Fri, 26 Aug 2022 05:00:05 +0000</pubDate>
      <link>https://dev.to/wallism/dotnet-swagger-tofile-could-not-load-file-or-assembly-systemruntime-1po3</link>
      <guid>https://dev.to/wallism/dotnet-swagger-tofile-could-not-load-file-or-assembly-systemruntime-1po3</guid>
      <description>&lt;p&gt;When running this command:&lt;br&gt;
&lt;code&gt;dotnet swagger tofile --output "valid-path" "valid-dll" v1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I was getting this error:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Unhandled exception. System.IO.FileLoadException: Could not load file or assembly 'System.Runtime, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The located assembly's manifest definition does not match the assembly reference. (0x80131040)&lt;br&gt;
File name: 'System.Runtime, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The target dll was .net 5.0 but because I have .net 6.0 installed, my system was using that sdk by default. To use the correct sdk when running dotnet cli, this &lt;a href="https://stackoverflow.com/questions/42077229/switch-between-dotnet-core-sdk-versions"&gt;SO answer&lt;/a&gt; has a clear explanation. Basically you add a new global.json file at a folder level that makes sense (e.g. project or solution root folder).&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Logging Headers with Serilog into Sumo</title>
      <dc:creator>Wallism</dc:creator>
      <pubDate>Mon, 25 Jul 2022 10:48:42 +0000</pubDate>
      <link>https://dev.to/wallism/logging-headers-with-serilog-into-sumo-53c0</link>
      <guid>https://dev.to/wallism/logging-headers-with-serilog-into-sumo-53c0</guid>
      <description>&lt;p&gt;A quick post showing subtle changes to how request headers were being logged and the significant improvement on the end result.&lt;/p&gt;

&lt;h2&gt;
  
  
  Version 1
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Log.Logger.Information("Request {headers}");&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;manifests like this in Sumo:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FuGUYgjO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g4yvga07hvgori192i8d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FuGUYgjO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g4yvga07hvgori192i8d.png" alt="Image description" width="432" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ugly and larger than required.&lt;/p&gt;

&lt;h2&gt;
  
  
  Version 2
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Log.Logger.Information("Request {@headers}");&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Simply using the destructuring operator produces this:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--J3Ku4A7A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tnvmdb4u4mu8apnlljij.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--J3Ku4A7A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tnvmdb4u4mu8apnlljij.png" alt="Image description" width="424" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Overall probably better but there's alot of expanding required to see all of the header values. Particularly annoying is the need to expand Value.&lt;/p&gt;

&lt;h2&gt;
  
  
  Version 3
&lt;/h2&gt;

&lt;p&gt;Create a simple class&lt;br&gt;
&lt;code&gt;internal class Header&lt;br&gt;
    {&lt;br&gt;
        public string Key { get; set; }&lt;br&gt;
        public string Value { get; set; }&lt;br&gt;
    }&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Extract the keyvalue pairs into a list of Headers:&lt;br&gt;
&lt;code&gt;var headerList = httpRequest.Headers.Select(kvp =&amp;gt; new Header {Key = kvp.Key , Value = kvp.Value.FirstOrDefault()}).ToList();&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then log that list:&lt;br&gt;
&lt;code&gt;Log.Logger.Information("Request {@headersList}");&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;produces:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SnZWL3ub--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oxohcodj6byuo80f0m2u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SnZWL3ub--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oxohcodj6byuo80f0m2u.png" alt="Image description" width="443" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Marginally better, at least we don't need to expand Value.&lt;/p&gt;

&lt;h2&gt;
  
  
  Version 4
&lt;/h2&gt;

&lt;p&gt;Still use the Header class and headerList from Version 3, but change the way we log it to:&lt;br&gt;
&lt;code&gt;Log.Logger.ForContext("Headers", headerList).Information("request")&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;The key difference being the use of ForContext, produces:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--s__cvW6S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a28lr6pdhehd47bmwyo5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--s__cvW6S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a28lr6pdhehd47bmwyo5.png" alt="Image description" width="438" height="307"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Which to my eye is perfect, all the headers, tightly grouped, one click to see them all. &lt;/p&gt;

</description>
      <category>blog</category>
    </item>
  </channel>
</rss>
