Search code examples
c#azure.net-corevisual-studio-code

How to check Debug.Writeline() output in VS code?


I want to know the exact way to check the output of Debug.Writeline() in VS Code using C# extension.

I was using .net core 2.2 as a framework to run a sample test from this website https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/keyvault/Azure.Security.KeyVault.Keys/samples/Sample1_HelloWorld.cs.

The test passed but I couldn't see any output from the Debug.Writeline() in the terminal. So I googled and found that common solution is to include the following code in my .cs file:

/* Create a listener that outputs to the console screen, and 
   add it to the debug listeners. */
   TextWriterTraceListener myWriter = new TextWriterTraceListener(System.Console.Out);
   Debug.Listeners.Add(myWriter);

However, after I put the code into the file, it can't find the property 'listeners' under the class 'Debug'. So I googled again and realised that it is because this property is only included in .Net Framework 4.8. Therefore, I downloaded .Net Framework v4.8 and changed 'TargetFramework as follows:

<TargetFramework>net48</TargetFramework>

After the modifications, I ran the test again:

dotnet test 

But the result is still like this:

Microsoft (R) Test Execution Command Line Tool Version 16.2.0-preview-20190606-02
Copyright (c) Microsoft Corporation.  All rights reserved.

Starting test execution, please wait...


Test Run Successful.
Total tests: 1
     Passed: 1

This is my .csproj file:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net48</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Azure.Identity" Version="1.0.0-preview.3"/>
    <PackageReference Include="Azure.Security.KeyVault.Keys" Version="4.0.0-preview.2"/>
    <PackageReference Include="NUnit" Version="3.12.0"/>
    <PackageReference Include="NUnit3TestAdapter" Version="3.14.0"/>
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.3.0-preview-20190808-03"/>
  </ItemGroup>
</Project>

This is the sample test I ran without modification:

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for
// license information.

using Azure.Identity;
using NUnit.Framework;
using System;
using System.Diagnostics;
using System.Security.Cryptography;
using System.Threading;

namespace Azure.Security.KeyVault.Keys.Samples
{
    /// <summary>
    /// Sample demonstrates how to set, get, update and delete a key using the synchronous methods of the KeyClient.
    /// </summary>
    [Category("Live")]
    public partial class HelloWorld
    {
        [Test]
        public void HelloWorldSync()
        {
            // Environment variable with the Key Vault endpoint.
            string keyVaultUrl = Environment.GetEnvironmentVariable("AZURE_KEYVAULT_URL");

            // Instantiate a key client that will be used to call the service. Notice that the client is using default Azure
            // credentials. To make default credentials work, ensure that environment variables 'AZURE_CLIENT_ID',
            // 'AZURE_CLIENT_KEY' and 'AZURE_TENANT_ID' are set with the service principal credentials.
            var client = new KeyClient(new Uri(keyVaultUrl), new DefaultAzureCredential());

            // Let's create a RSA key valid for 1 year. If the key
            // already exists in the Key Vault, then a new version of the key is created.
            string rsaKeyName = $"CloudRsaKey-{Guid.NewGuid()}";
            var rsaKey = new RsaKeyCreateOptions(rsaKeyName, hsm: false, keySize: 2048)
            {
                Expires = DateTimeOffset.Now.AddYears(1)
            };

            client.CreateRsaKey(rsaKey);

            // Let's Get the Cloud RSA Key from the Key Vault.
            Key cloudRsaKey = client.GetKey(rsaKeyName);
            Debug.WriteLine($"Key is returned with name {cloudRsaKey.Name} and type {cloudRsaKey.KeyMaterial.KeyType}");

            // After one year, the Cloud RSA Key is still required, we need to update the expiry time of the key.
            // The update method can be used to update the expiry attribute of the key.
            cloudRsaKey.Expires.Value.AddYears(1);
            KeyBase updatedKey = client.UpdateKey(cloudRsaKey, cloudRsaKey.KeyMaterial.KeyOps);
            Debug.WriteLine($"Key's updated expiry time is {updatedKey.Expires}");

            // We need the Cloud RSA key with bigger key size, so you want to update the key in Key Vault to ensure
            // it has the required size.
            // Calling CreateRsaKey on an existing key creates a new version of the key in the Key Vault 
            // with the new specified size.
            var newRsaKey = new RsaKeyCreateOptions(rsaKeyName, hsm: false, keySize: 4096)
            {
                Expires = DateTimeOffset.Now.AddYears(1)
            };

            client.CreateRsaKey(newRsaKey);

            // The Cloud RSA Key is no longer needed, need to delete it from the Key Vault.
            client.DeleteKey(rsaKeyName);

            // To ensure key is deleted on server side.
            Assert.IsTrue(WaitForDeletedKey(client, rsaKeyName));

            // If the keyvault is soft-delete enabled, then for permanent deletion, deleted key needs to be purged.
            client.PurgeDeletedKey(rsaKeyName);

        }

        private bool WaitForDeletedKey(KeyClient client, string keyName)
        {
            int maxIterations = 20;
            for (int i = 0; i < maxIterations; i++)
            {
                try
                {
                    client.GetDeletedKey(keyName);
                    return true;
                }
                catch
                {
                    Thread.Sleep(5000);
                }
            }
            return false;
        }
    }
}

This is the test after modification:

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for
// license information.

using Azure.Identity;
using NUnit.Framework;
using System;
using System.Diagnostics;
using System.Security.Cryptography;
using System.Threading;

namespace Azure.Security.KeyVault.Keys.Samples
{
    /// <summary>
    /// Sample demonstrates how to set, get, update and delete a key using the synchronous methods of the KeyClient.
    /// </summary>
    [Category("Live")]
    public partial class HelloWorld
    {
        [Test]
        public static void Main()
        {    
            /* Create a listener that outputs to the console screen, and 
             add it to the debug listeners. */
           TextWriterTraceListener myWriter = new TextWriterTraceListener(System.Console.Out);
           Debug.Listeners.Add(myWriter);

            // Environment variable with the Key Vault endpoint.
            string keyVaultUrl = Environment.GetEnvironmentVariable("AZURE_KEYVAULT_URL");

            // Instantiate a key client that will be used to call the service. Notice that the client is using default Azure
            // credentials. To make default credentials work, ensure that environment variables 'AZURE_CLIENT_ID',
            // 'AZURE_CLIENT_KEY' and 'AZURE_TENANT_ID' are set with the service principal credentials.
            var client = new KeyClient(new Uri(keyVaultUrl), new DefaultAzureCredential());

            // Let's create a RSA key valid for 1 year. If the key
            // already exists in the Key Vault, then a new version of the key is created.
            string rsaKeyName = $"CloudRsaKey-{Guid.NewGuid()}";
            var rsaKey = new RsaKeyCreateOptions(rsaKeyName, hsm: false, keySize: 2048)
            {
                Expires = DateTimeOffset.Now.AddYears(1)
            };

            client.CreateRsaKey(rsaKey);
            // Let's Get the Cloud RSA Key from the Key Vault.
            Key cloudRsaKey = client.GetKey(rsaKeyName);
            Debug.WriteLine($"Key is returned with name {cloudRsaKey.Name} and type {cloudRsaKey.KeyMaterial.KeyType}");

            // After one year, the Cloud RSA Key is still required, we need to update the expiry time of the key.
            // The update method can be used to update the expiry attribute of the key.
            cloudRsaKey.Expires.Value.AddYears(1);
            KeyBase updatedKey = client.UpdateKey(cloudRsaKey, cloudRsaKey.KeyMaterial.KeyOps);
            Debug.WriteLine($"Key's updated expiry time is {updatedKey.Expires}");

            // We need the Cloud RSA key with bigger key size, so you want to update the key in Key Vault to ensure
            // it has the required size.
            // Calling CreateRsaKey on an existing key creates a new version of the key in the Key Vault 
            // with the new specified size.
            var newRsaKey = new RsaKeyCreateOptions(rsaKeyName, hsm: false, keySize: 4096)
            {
                Expires = DateTimeOffset.Now.AddYears(1)
            };

            client.CreateRsaKey(newRsaKey);

            // The Cloud RSA Key is no longer needed, need to delete it from the Key Vault.
            client.DeleteKey(rsaKeyName);

            // To ensure key is deleted on server side.
            // Assert.IsTrue(WaitForDeletedKey(client, rsaKeyName));

            // If the keyvault is soft-delete enabled, then for permanent deletion, deleted key needs to be purged.
            // client.PurgeDeletedKey(rsaKeyName);

        }

        private bool WaitForDeletedKey(KeyClient client, string keyName)
        {
            int maxIterations = 20;
            for (int i = 0; i < maxIterations; i++)
            {
                try
                {
                    client.GetDeletedKey(keyName);
                    return true;
                }
                catch
                {
                    Thread.Sleep(5000);
                }
            }
            return false;
        }
    }
}

In the end I tried to type the following in the terminal:

dotnet run

I finally got what I expected from the terminal in a format like this:

Key is returned with name CloudRsaKey-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and type xxx
Key's updated expiry time is x/xx/20xx x:xx:xx AM +00:00

But I want to know if it is the only way to see the output from Debug.Witeline(). Since I did not type the dotnet test command in the terminal, I don't think I was actually running the test. I am quite confused.


Solution

  • First off, you'll have to set an environment variable that will enable debugging tests hosted on the dotnet test process to use the configuration for attaching a debugger in VS Code. On Windows with Powershell, this is done as: $env:VSTEST_HOST_DEBUG=1

    Running dotnet test without this setting would simply pass. And even breakpoints would be ignored if not executed in Debug mode.

    Once you attach a debugger to the test process, your breakpoints will be caught and you'll be able to step through code as usual, now seeing the output of all the Debug.WriteLine() statements in the Debug Console of VS Code.

    Check this article for a quick walk through.

    Here is a short demo:

    Debugging in VS Code

    Hope this helps!