Sunday, 27 February 2022

Write to Physical File with Serilog in .NET Core

It is common for a need to log application errors or including trace log for ease of troubleshooting issues that may arise. By default, the .NET itself already provided a few classes (For example: FileStream, TextWriter, etc) that allows you to write content into file. But what if you want to include logs that are written by the .NET itself, or customize the .NET log content, or write the same content to many different destination (eg: File, ElasticSearch, BlogStorage, etc). Then Serilog can help you with it.

For the prerequisites, it is required to NuGet the following 2 packages

  • Serilog.AspNetCore
  • Serilog.Sinks.File

Next, head to Program.cs file. You will need to call to the extension method UseSerilog() to start using Serilog.

Method 1: Configure Serilog in code.

[.NET 6] - Minimal hosting model
builder.Host.UseSerilog((hostBuilderContext, loggerConfiguration) =>
    loggerConfiguration
        .WriteTo.Console()
        .WriteTo.File("logs/log.txt", rollingInterval: RollingInterval.Day));

[.NET 5 and earlier]
Host.CreateDefaultBuilder(args)
    .UseSerilog((hostBuilderContext, loggerConfiguration) =>
        loggerConfiguration
            .WriteTo.Console()
            .WriteTo.File("logs/log.txt", rollingInterval: RollingInterval.Day))

Method 2: Configure Serilog from appSettings file.

[appSettings.json]
"Serilog": {
  "WriteTo": [
    {
      "Name": "Console"
    },
    {
      "Name": "File",
      "Args": {
        "path": "logs/log.txt",
        "rollingInterval": "Day"
      }
    }
  ]
}

[.NET 6] - Minimal hosting model
builder.Host.UseSerilog((hostBuilderContext, loggerConfiguration) =>
    loggerConfiguration
        .ReadFrom.Configuration(hostBuilderContext.Configuration));

[.NET 5 and earlier]
Host.CreateDefaultBuilder(args)
    .UseSerilog((hostBuilderContext, loggerConfiguration) =>
        loggerConfiguration
            .ReadFrom.Configuration(hostBuilderContext.Configuration))

What the above code snippet does is to write the same log content into console and log file. 

Write to console

Write to file

Based on the specified path value, the file format will be log{yyyyMMdd}.txt (example: log20220227.txt). And since the configuration mentioned the rolling interval is Day, everyday a new file will be created whenever there are new content to be logged. For more details on what can be further customized for the file, you can head over to https://github.com/serilog/serilog-sinks-file.

You may be wondering what is the difference of the above code snippet compared to creating a new instance of logger with CreateLogger(). First would be to include .NET logs into the file. Second, you will be able to rely on the Microsoft logging library (Microsoft.Extensions.Logging) and inject the instance of ILogger to any of your class through the constructor. Any logs written through the ILogger will be written into your file as well as the console output.

Sample Code: https://1drv.ms/u/s!Aj2AA7hoIWHm1G4BIALQnYew9KFY?e=bIa0tq





Saturday, 26 February 2022

Multi Target Framework for .NET Core Application

It is possible to target multiple framework for .NET Core application. But it have some limitations. For example, if the project target netcoreapp3.1 and net6.0, features that are available on net6.0 will not longer be supported. Due to this, it is rarely to do so unless you got hit with situation where you want to upgrade to newest .NET version but due to certain servers yet to installed the latest .NET core runtime.

First and foremost, make sure you have the targeted framework installed. Otherwise you get hit with an error while compiling your code. For example, if you are targeting netcoreapp3.1 and net6.0, make sure both of them is installed on your machine.

To get started, open up your project by double clicking on it and look for TargetFramework. Rename it to TargetFrameworks and specify net6.0;netcoreapp3.1

<TargetFrameworks>net6.0;netcoreapp3.1</TargetFrameworks>

Next item to look out for is the NuGet packages. You can specify different NuGet package version to be installed for different framework. As an example, I only have 1 NuGet package, which is Swashbuckle.AspNetCore as shown below. 

<ItemGroup>
  <PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
</ItemGroup>

You are required to remove it and then create 2 separate ItemGroup as shown below. By doing so, Swashbuckle.AspNetCore version 6.2.3 will be installed for net6.0, whereas version 5.6.3 will be installed for netcoreapp3.1

<ItemGroup Condition="$(TargetFramework)=='net6.0'">
  <PackageReference Include="Swashbuckle.AspNetCore">
    <Version>6.2.3</Version>
  </PackageReference>
</ItemGroup>
<ItemGroup Condition="$(TargetFramework)=='netcoreapp3.1'">
  <PackageReference Include="Swashbuckle.AspNetCore">
    <Version>5.6.3</Version>
  </PackageReference>
</ItemGroup>

Once you saved your changes, you may notice the Background Task which locate at the bottom left of your visual studio is in progress of grabbing the NuGet packages for your project. Make sure to wait it to complete before compiling your code. Also, you will be able to see 2 different dependencies in your project under the Solution Explorer.

Multiple dependencies

Sunday, 13 September 2020

Upload and Retrieve File from Azure Blob Storage in C# & VB for .NET Framework

Azure Blob Storage is one of the Microsoft Azure Resource to store any types of file. Files that are placed in the blob storage can be accessible through API, Microsoft Azure Storage Explorer, directly from the azure portal or from PowerShell. So if you have the need to archive files or share the access to the file to different environment or application that is located in different locations, then Blob Storage is a good candidate for you. For more detail, you may further check it out from this link https://docs.microsoft.com/en-us/azure/storage/common/storage-introduction#blob-storage.

So to get started on how to access Blob Storage from your code, firstly you need to retrieve the following packages from NuGet.

Microsoft.Azure.ConfigurationManager
Microsoft.Azure.KeyVault.Core
Microsoft.Azure.Storage.Blob
Microsoft.Azure.Storage.Common
Newtonsoft.Json

Once you have gotten the necessary packages, you need to define where is your blob storage located in your configuration file. But if you currently do not have an azure account, you may setup and run the Microsoft Azure Storage Emulator on your machine. Refer to this link https://docs.microsoft.com/en-us/azure/storage/common/storage-use-emulator for more information on how to set it up.

If you are using the Microsoft Azure Storage Emulator, define the following setting in the configuration file.

<add key="StorageConnectionString" value="UseDevelopmentStorage=true" />

Otherwise, you need to retrieve the connection string from the Azure Portal. The format of the connection string is as follows.

<add key="StorageConnectionString" value="DefaultEndpointsProtocol=https;AccountName=YourAzureBlobStorageName;AccountKey=YourAzureBlobStorageKey" />

To start using the packages retrieved from NuGet. Define the CloudStorageAccount instance based on the configuration defined above. This is to specify the location of the Azure Blob Storage.

[C#]
CloudStorageAccount cloudStorageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("StorageConnectionString"));

[VB]
Dim cloudStorageAccount As CloudStorageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("StorageConnectionString"))

With the CloudStorageAccount, use it to create the container. This is required before file can be placed into the Azure Blob Storage. Do take note that the container name must be lower case. For more detail about the container, you may check the following link https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blobs-introduction#blob-storage-resources.

[C#]
CloudBlobContainer cloudBlobContainer = cloudStorageAccount.CreateCloudBlobClient().GetContainerReference("containername");

cloudBlobContainer.CreateIfNotExists();

[VB]
Dim cloudBlobContainer As CloudBlobContainer = cloudStorageAccount.CreateCloudBlobClient().GetContainerReference("containername")

cloudBlobContainer.CreateIfNotExists()

After the creation of the container, it is now possible to upload file to the Azure Blob Storage. In the following demonstration, the file in the local machine will be used to upload.

[C#]
CloudBlockBlob cloudBlockBlob = cloudBlobContainer.GetBlockBlobReference("SampleFileInBlob.txt");

cloudBlockBlob.UploadFromFile(Path.Combine(Environment.CurrentDirectory, "SampleFile.txt"));

[VB]
Dim cloudBlockBlob As CloudBlockBlob = cloudBlobContainer.GetBlockBlobReference("SampleFileInBlob.txt")

cloudBlockBlob.UploadFromFile(Path.Combine(Environment.CurrentDirectory, "SampleFile.txt"))

Alternatively, the following demonstrate on how to retrieve the file from Azure Blob Storage and save it on local machine. 

[C#]
CloudBlockBlob cloudBlockBlob = cloudBlobContainer.GetBlockBlobReference("SampleFileInBlob.txt");

cloudBlockBlob.DownloadToFile(Path.Combine(Environment.CurrentDirectory, "SavedSampleFile.txt"), FileMode.Create);

[VB]
Dim cloudBlockBlob As CloudBlockBlob = cloudBlobContainer.GetBlockBlobReference("SampleFileInBlob.txt")

cloudBlockBlob.DownloadToFile(Path.Combine(Environment.CurrentDirectory, "SavedSampleFile.txt"), FileMode.Create)

Check out the sample code here https://1drv.ms/u/s!Aj2AA7hoIWHm03MBsAUCRtvATCYe.

Sunday, 6 September 2020

How to Solve mutex in Namespace std does not Name a Type

Setting up the environment for Mingw-w64 may look easy and straightforward, but choosing the wrong setting during the setup may lead to failure to build or compile the source code. For example, if the source code contains the usage of mutex. With the wrong installation settings for Mingw-w64, you may stumble upon similar errors that are shown below.

error: 'mutex' in namespace 'std' does not name a type

     std::mutex m_mutex;

 

note: 'std::mutex' is defined in header '<mutex>'; did you forget to '#include <mutex>'?

 

error: 'mutex' is not a member of 'std'

         std::lock_guard<std::mutex> lock(m_mutex);

 

error: template argument 1 is invalid

         std::lock_guard<std::mutex> lock(m_mutex);

 

error: 'm_mutex' was not declared in this scope

         std::lock_guard<std::mutex> lock(m_mutex);

 

note: suggested alternative: 'uv_mutex_t'

         std::lock_guard<std::mutex> lock(m_mutex);

                                          ^~~~~~~

                                          uv_mutex_t

 

 warning: unused variable 'lock' [-Wunused-variable]

         std::lock_guard<std::mutex> lock(m_mutex);


This is due to the settings that you specified during the installation doesn't contain the mutex implementation. So to fix this, you need to reinstall with the correct setting, where the mutex implementation is in the posix. Rerun the Mingw-w64 installer and make sure the posix is selected for the threads and continue with the installation.


Do take note to update the %PATH% Environment Variables to the newly installed path of Mingw-w64 with posix and restart your command prompt session to ensure the command is picking up the correct settings.