Category Archives: C#

Websockets with Fleck

I’m going to create a .NET core console application to demonstrate using Fleck.

So create yourself a project and add the Fleck nuget package or simply add the Fleck nuget package to an existing project.

Creating a websocket server

To begin with, we simply create a websocket server, supplying the “location”, i.e.

var websocketServer = new WebSocketServer("ws://0.0.0.0:8181");

Don’t forget the using Fleck; line at the start of your code

In this example we’re using 0.0.0.0 (the non-routable meta-address) along with the port 8181 and ofcourse we prefix this with the ws protocol.

Interacting with clients

Next up we’ll want to start the server and intercept various events/messages. Fleck uses a callback function/delegate style, so we simply supply our functions for each of the connection methods that we wish to intercept, for example

websocketServer.Start(connection =>
{
  connection.OnOpen = () => 
    Console.WriteLine("OnOpen");
  connection.OnClose = () => 
    Console.WriteLine("OnClose");
  connection.OnMessage = message => 
    Console.WriteLine($"OnMessage {message}");
  connection.OnBinary = bytes => 
    Console.WriteLine($"OnBinary {Encoding.UTF8.GetString(bytes)}");
  connection.OnError = exception => 
    Console.WriteLine($"OnError {exception.Message}");
  connection.OnPing = bytes => 
    Console.WriteLine("OnPing");
  connection.OnPong = bytes => 
    Console.WriteLine("OnPong");
});

Note: if we’re handling different state for different connections to the same url, it’s our responsibility to create our own form “session state”.

In this example, we’ve listed all the OnXXX actions that we can intercept.

Obviously OnOpen occurs when a new client connects to the server (on ws://0.0.0.0:8181) and OnClose occurs if the client closes the connection.

OnMessage is called when string messages are sent over the websocket, whereas OnBinary is, ofcourse, the binary equivalent (in the example above we’re assuming the bytes represent a string, obviously change this if you’re sending raw byte data).

OnError is called with an Exception for instances where exceptions occur (as you’ll have surmised).

OnPing is used when being pinged and like wise OnPong is used when receiving a pong – ping and pong are used as ways to, in essence, check if the client or server are still running. If supported, a server might send a ping to the connected clients then mark the clients as stopped (and hence dispose of any connections) if the server does not receive a pong within a specified timeout period. Obviously one of the biggest problems for any server that is maintaining some form of state is at what point they can assume the client is no longer around. Obviously if the client closes the connection the server can handle this, but what about if they just disconnect – this is where ping and pong come into play.

Obviously we also need to be able to send data to the client, hence we use the connection’s Send method. For example let’s change the OnMessage delegate to send an “Echo” of the message back to the client

connection.OnMessage = message =>
{
  Console.WriteLine($"OnMessage {message}");
  connection.Send($"Echo: {message}");
};

Writing a client to test our server

Let’s now create a simple console app to test our server code. This will use the System.Net.WebSockets ClientWebSocket class.

We will need to actually specify a network address for the client, so we’ll use the loopback 127.0.0.1.

Below are the contents of our client console application’s Main method

var websocketClient = new ClientWebSocket();
var cancellationToken = new CancellationTokenSource();

var connection = websocketClient.ConnectAsync(
  new Uri("ws://127.0.0.1:8181"), 
  cancellationToken.Token);

connection.ContinueWith(async tsk =>
{
  // sends a string/text message causes OnMessage to be called
  await websocketClient.SendAsync(
    new ArraySegment<byte>(Encoding.UTF8.GetBytes("Hello World")),
    WebSocketMessageType.Text,
    true,
    cancellationToken.Token);

  // receives a string/text from the server
  var buffer = new byte[128];
  await websocketClient.ReceiveAsync(
    new ArraySegment<byte>(buffer), cancellationToken.Token);
  Console.WriteLine(Encoding.UTF8.GetString(buffer));

  // sends a string/text message causes OnBinary to be called
  await websocketClient.SendAsync(
    new ArraySegment<byte>(Encoding.UTF8.GetBytes("Hello World")),
    WebSocketMessageType.Binary,
    true,
    cancellationToken.Token);
  });

  Console.WriteLine("Press <enter> to exit");
  Console.Read();

  // close the connection nicely
  websocketClient.CloseAsync(
     WebSocketCloseStatus.NormalClosure, 
     String.Empty, 
     cancellationToken.Token);

  // this will cause OnError on the server if not closed first
  cancellationToken.Cancel();

Hopefully it’s fairly self explanatory what’s going on – we create a websocket client and a cancellation token (as the methods all required one). We connect to the server and when a connection is established we send and receive data (strings and then binary). Eventually we close the connection.

What and ping and pong?

At this time I don’t have any examples to implement these.

In the case of the ClientWebSocket code, if you leave the client and server running you will periodically see OnPing being called.

I almost forgot…

We can also interact with the connection’s ConnectionInfo property which gives us access to headers, cookies and whether path was specified, i.e. the client url ws://127.0.0.1:8181/somepath will result in ConnectionInfo.Path having the value /somepath.

Here’s an example of the server changes for OnOpen

connection.OnOpen = () =>
{
  Console.WriteLine("OnOpen");
  Console.WriteLine(connection.ConnectionInfo.Path);
};

From what I can tell, each connection is assigned a GUID (found in ConnectionInfo.Id), so when handling multiple different clients with different state requirements we should be able to use this Id.

References

Fleck
Writing WebSocket servers

So _ are called discards

I’ve used the _ (underscore) previously in code – particularly with F# tuples but also C#. It is used to denote a value that is intentionally unused, ignored, discarded.

It seems that the underscore used in such situations has a name (maybe it has the same name in other languages, I’ve not yet checked). It’s called a discard.

Note: With regard to its use in C#, it appeared with C# 7.0.

Let’s have a look at a very simple example

public bool IsNumber => Double.TryParse("123", out _);

This slightly contrived example (the best I could think of quickly) shows how this might be used. In this example we’re not interested in the out value so just use a discard to basically say “this is unused, ignore it”.

Note: The discard doesn’t need a var or type.

Obviously the discard comes in very useful with tuples. I guess, for C#, this became most useful when tuples became a first class language feature.

So for example we might have this code

public (string, string, int) GetPerson()
{
   return ("Scooby", "Doo", 13);
}

// in use we only want the age
var (_, _, age) = GetPerson();

The “UNAVAILABLE: Trying to connect an http1.x server” gRPC error

I’m working with on C# client library using gRpc. This is not a web based library where there’s a need for envoy or other application to help work with HTTP2. Instead this was on a C# client using tcp/ip.

Everything was working fine on my machine until the machine was relocated (not that this specifically was the problem as I later found out others were having similar problems, some times intermittently) at which point I found that the client would exception with the message “UNAVAILABLE: Trying to connect an http1.x server”.

Note: if you turn on all exceptions within Visual Studio, you’ll firstly see an exception where grpc tries to get a Value from a Nullable when it’s null (this was the call to Native.grpcsharp_batch_context_recv_message_length). This is a red herring, it’s simple that the nullable is null which seems to be expected behaviour, maybe should be handled in the gRPC .NET library code.

Testing the client on another machine demonstrated the problem was seemingly network related, turns out it was HTTP proxy related to be precise.

This doesn’t seem to be too well documented from what I could tell, but setting the ChannelOption grpc.enable_http_proxy to 0 (see https://grpc.github.io/grpc/core/group__grpc__arg__keys.html) fixed the problem.

var channel = new ManagedChannel(
   host, 
   port, 
   ChannelCredentials.Insecure,
   new []{ new ChannelOption("grpc.enable_http_proxy", 0)} );

Razor templates in your Xamarin application

I had a situation where I wanted to have a template file made up of HTML along with data supplied by my Xamarin application. There are several options to do this but a Razor template file would be a perfect fit, but how to embed one in my (non-Web) application?

Note: Whilst this post talks about a Xamarin application this should be usable in other types of application.

  • I created a Templates folder (not required for this to work) in my solution.
  • Within the Templates folder I add a new text file but saved with a .cshtml extension, for example EmployeeTemplate.cshtml. Visual Studio 2019 doesn’t have a specific template for Xamarin application, but just naming it with the correct extension works.
  • Select the file in the solution explorer and open it’s properties, set the Custom Tool to RazorTemplatePreprocessor.

Here’s a snippet of the contents within the Razor template file

<div class="row">
  <div>Date: @Model.DateEntered</div>
    <div>
      <div>Employee Number:</div>
      <div>@Model.OrderDetails.EmployeeNumber</div>
    </div>
</div>

Visual Studio will (due the use of the RazorTemplatePreprocessor Custom Tool) generate a .cs file that is a class that we can pass our model into and will process the template file and output a string representing the combination of the template with our model.

To generate our output string we simply use the following code

var template = new EmployeeTemplate() 
{ 
  Model = viewModel 
};

var result = template.GenerateString();

Text to Speech on mobile with Xamarin

Previously I looked at Speech synthesis in Windows with C# but what about speech synthesis on mobile.

Xamarin.Essentials has this covered with the TextToSpeechTextToSpeech class and is included by default when creating a Xamarin Forms mobile application.

It’s really simple to add text to speech capabilities, we simply write code such as

await TextToSpeech.SpeakAsync("Hello World");

As you can see by the use of the await keyword (and the standard Async suffix), the SpeakAsync method returns a task. It also accepts a cancellation token if you need to cancel mid utterance. There’s also an overload which accepts SpeechOptions which allows us to set the volume, pitch and locale.

The second TextToSpeech method is GetLocalesAsync which allows is to get a list of supported locales from the device that can then be used within the SpeakAsync method’s SpeechOptions.

await TextToSpeech.GetLocalesAsync();

Note: It’s fun listening to the attempts at different accents depending upon the locale.

Requesting permissions in Android with a Target SDK Version >= 23

Various Android permissions (classed as “dangerous”), within applications targeting Android SDK >= 23 now require “requesting”.

So for example, on older Android versions we simply set the permissions, such as those for accessing Bluetooth, within the AndroidManifest.xml (or via the editor within Visual Studio – I’m using Xamarin Forms for my application, hence Visual Studio). These look like this

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

All works fine until you try to run your application (which requires these permissions) on an Android device with a >= 23 version. Instead, within the debug output you’ll see Security Exceptions around such permissions.

So along with our manifest, we now need to request these permissions within the MainActivity.cs we can write something like

private void CheckPermissions()
{
   ActivityCompat.RequestPermissions(this, new []
   {
      Manifest.Permission.AccessCoarseLocation,
      Manifest.Permission.AccessFineLocation,
      Manifest.Permission.Bluetooth,
      Manifest.Permission.BluetoothAdmin
   }, 1);
}

We might wish to first check whether a permission has already been granted, using code such as

if (ContextCompat.CheckSelfPermission(
   this, Manifest.Permission.AccessCoarseLocation) 
   != Permission.Granted)
{
   // request permission
}

On top of this, we probably want to either inform the user or handle failures to get the requested permissions (for example disabling such functionality), in such cases we override the OnRequestPermissionsResult method within the MainActivity.cs file (i.e. within the FormsAppCompatActivity class)

public override void OnRequestPermissionsResult(
   int requestCode, 
   string[] permissions, 
   Permission[] grantResults)
{
   // we may want to handle failure to get the permissions we require
   // we should handle here, otherwise call the base method
   base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}

See Permissions In Xamarin.Android, also see the Android documentation Permissions Overview.

Anatomy of an Azure Function

In a previous post we looked at creating Azure Functions, hopefully from this we’re able to quickly and easily get things up and running. We also looked (in another post) and various triggers and hooks which help run our functions.

For this post I want to look into functions a little deeper.

Template code

As we’ve seen, from the Azure Portal or from Visual Studio (with the relevant extensions) we can easily generate our function code. Different function types have slightly different code patterns.

This post will concentrate on C# code, but we can generated code in JavaScript, F# and more.

Azure Portal Generated Code

The Azure Portal generates code for us, which for a C# developer is actually generating C# scripting code. Hence, unlike via Visual Studio, to reference external assemblies which are not included by default. In this case we can use #r (requires) and the assembly name, for example

#r "System.Data"

It’s fairly obvious that an Azure function is literally that, a function/method. Not a class with multiple methods where you create an instance of the class and interact with it. Hence Azure functions are static and may be async, or not, as required.

Depending upon the type of function we’re creating we may return a response or void or another object type. Again, depending on the type of function, we may have a TimerInfo, or an HttpRequestMessage and there may be other arguments, whether they’re parameters for a query of connections to BlobStorage (via a Stream), Queue names or whatever.

With the Azure Portal functions we also have generated for us, the function.json file (and in some cases a README.md file).

function.json

This file acts as a binding (using JSON as the extension suggests) which defines the inputs and outputs as well as some configuration details, such as whether it’s disabled or not.

Visual Studio Generated Code

Code from Visual Studio differs from the Portal code in that we’re creating assemblies here, we’re not uploading scripts to the Azure Portal. Hence we add references in the standard way to the project. We don’t have a function.json file, but instead have a host.json file as well as a local.settings.json file (which we’re look into later).

Our function name (i.e. the name we see in the Portal etc.) is defined using the FunctioNameAttribute on the method. The configuration that exists in the function.json file becomes attributes on the Run method (generated by Visual Studio). So for example, what type of trigger is often defined as an attribute on the first arguments, for example

[FunctionName("TimerTest")]
public static void Run([TimerTrigger("0 */5 * * * *")]TimerInfo myTimer, TraceWriter log)
{
   log.Info($"C# Timer trigger function executed at: {DateTime.Now}");
}

and this attribute then will tend to have the configuration for the trigger, for example this TimerTriggerAttribute has the CRON format string, an HttpTriggerAttribute might have the AuthorizationLevel used, the HTTP methods uses, the Route etc. The BlobTriggerAttribute has connection information etc. Obviously, just like the Azure generated code, other parameters exist depending upon the type of trigger and/or params passed to the method. Also included is the TraceWriter for us to log information to.

For anything other than an HttpTrigger function, you’ll need to supply AzureWebJobStorage details etc. in the local.settings.json file, to do this, from Visual Studio Command Prompt and from your project’s folder (where it’s host.json and local.settings.json files reside) run

func azure functionapp fetch-app-settings (your-app-name)

Replacing your-app-name with the app you will need to have created in the Azure Portal. If you have multiple projects then you have to do this in each one’s project folder. This command will fill in connections strings, account details and encrypted keys etc.

Note: if the func exe is not in your path etc. you can run

npm i -g azure-functions-core-tools

to install the tools.

Running/Debugging locally

Obviously, for as much development as possible, it would be best if, whilst testing, we ran our functions locally. As mentioned, to integrate your Azure Portal app settings we can run

func azure functionapp fetch-app-settings (your-app-name)

and you may have noticed when we run via the debugger the func.exe is executed. So it also makes sense that we can run our binary representations of our functions locally outside of the debugger.

Within your project’s output folder, i.e. bin\Debug\net461 (for example) and obviously after you’ve actually built you project, you can run (via the command prompt or Visual Studio command prompt (where func.exe is in your path) you can run

func host start

This will fire up an Azure emulator, read host.json, generate the host for your functions and then run a service on localhost:7071 (in my case) to host your functions.

C# 8.0 enhancements with pattern matching

C# 8.0 language features include pattern matching similar to that within F#.

Tuples have become a first class citizen of C# 8.0, so we can write something like this

public (string, int) CreateTuple(string s)
{
   return (s, s.Length);
}

This results in a ValueType being created, which can still be accessed using properties Item1 and Item2, however we can also use tuples within pattern matching, for example

var t = CreateTuple("Ten");
switch (t)
{
   case (string s, 1):
      return $"Single Character {s}";
   case (string s, int l):
      return $"{s}:{l}");
}      

In the above we’re matching firstly against a tuple where the first argument is a string and the second an int with value 1, the second case statements is basically going to catch everything else. Obviously the “catch all” must go after the more specific pattern match.

This can be tidied further by using expressions. So the above becomes

return t switch
{
   (string s, 1) => $"Single Character {s}",
   (string s, int l) => $"{s}:{l}"
};

JSON within subclasses, across multiple programming languages

I was developing an expression tree on a project, i.e. made up of subclasses of the class Expression, such as AndExpression, MemberExpression, LiteralExpression etc. The main code is TypeScript/JavaScript but this needs to pass JSON to TypeScript/JavaScript, C# or Java code (and possibly other languages).

Now when JavaScript JSON.stringify does it’s thing we’re left with no type information making it problematic converting each type back to it’s actual type, i.e. to an AndExpression not just an Expression.

A relatively easy way to solve this, whilst not as elegant as one might hope is to store a string representing the type within the object, for example

export class LiteralExpression extends Expression {
  public readonly $type: string = "LiteralExpression"
}

When we run the expression through JSON.stringify we get JSON with “$type”:”AndExpression” for example. In JavaScript we still need to do some work to convert this back to JavaScript classes, it’s easy enough to use JSON.parse(json) then iterate over our expression objects converting to subclasses and revive our objects from JSON in this way.

Note the use of the variable name $type. It’s important that it’s named $type if you want it to easily be translated into C# objects with Json.NET as this name is hard coded in this library, whereas Java’s jackson JAR allows us to easily change the name/key used.

Json.NET (Newtonsoft.Json)

Sadly we don’t quite get everything for free using Json.NET because it’s expecting C# style naming for classes, i.e. assembly/namespace etc. The easiest way to deal with this it to serialize/deserialize using our own SerializationBinder, for example

public static class Serialization
{
  public class KnownTypesBinder : ISerializationBinder
  {
    public IList<Type> KnownTypes { get; set; }

    public Type BindToType(string assemblyName, string typeName)
    {
      return KnownTypes.SingleOrDefault(t => t.Name == typeName);
    }

    public void BindToName(Type serializedType, out string assemblyName, out string typeName)
    {
      assemblyName = null;
      typeName = serializedType.Name;
    }
  }

  private static KnownTypesBinder knownTypesBinder = new KnownTypesBinder
  {
    KnownTypes = new List<Type>
    {
      typeof(AndExpression),
      typeof(BinaryExpression),
      typeof(LiteralExpression),
      typeof(LogicalExpression),
      typeof(MemberExpression),
      typeof(NotExpression),
      typeof(OperatorExpression),
      typeof(OrExpression)
    }
  };

  public static string Serialize(Expression expression)
  {
    var json = JsonConvert.SerializeObject(
      expression, 
      Formatting.None, 
      new JsonSerializerSettings
    {
      TypeNameHandling = TypeNameHandling.Objects,
      SerializationBinder = knownTypesBinder
    });
    return json;
  }

  public static Expression Deserialize(string json)
  {
    return JsonConvert.DeserializeObject<Expression>(
      json, 
      new JsonSerializerSettings
    {
      TypeNameHandling = TypeNameHandling.Objects,
      SerializationBinder = knownTypesBinder
    });
  }
}

Don’t forget you’ll also need to mark your properties and/or constructor parameters with [JsonProperty(“left”)] especially if you have situations where the names are keywords.

com.fasterxml.jackson.core

In Java we can add the following dependency to our pom.xml

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.10.0</version>
</dependency>

Now in our Expression base class we write the following

import com.fasterxml.jackson.annotation.JsonTypeInfo;

@JsonTypeInfo(
  use = JsonTypeInfo.Id.NAME, 
  include = JsonTypeInfo.As.PROPERTY, 
  property = "$type")
public class Expression {
}

This tells jackson to include the type info using the keyword $type.

We also need to add the @JsonCreator annotation to each of our classes and each constructor parameter requires the following annotation @JsonProperty(“left”). Finally to serialize/deserialize we create an ObjectMapper to allow us to map our types to real objects using

package com.rbs.expressions;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.NamedType;

public class Serialization {

  private static ObjectMapper createMapper() {
    ObjectMapper mapper = new ObjectMapper();

    mapper.registerSubtypes(
      new NamedType(AndExpression.class, "AndExpression"),
      new NamedType(BinaryExpression.class, "BinaryExpression"),
      new NamedType(OrExpression.class, "OrExpression"),
      new NamedType(LiteralExpression.class, "LiteralExpression"),
      new NamedType(LogicalExpression.class, "LogicalExpression"),
      new NamedType(MemberExpression.class, "MemberExpression"),
      new NamedType(NotExpression.class, "NotExpression"),
      new NamedType(OperatorExpression.class, "OperatorExpression")
    );

    return mapper;
  }

  public static String serialize(Expression expression) throws JsonProcessingException {
    return createMapper().writeValueAsString(expression);
  }

  public static Expression deserialize(String json) throws JsonProcessingException {
    return createMapper().readValue(json, Expression.class);
  }
}

Using Microsoft.Extensions.Logging

The Microsoft.Extensions.Logging namespace includes interfaces and implementations for a common logging interface. It’s a little like common-logging and whilst it’s not restricted to ASP.NET Core it’s got things entry points to work with ASP.NET Core.

What’s common logging all about?

In the past we’ve been hit with the problem of multiple logging frameworks/libraries which have slightly different interfaces. On top of this we might have other libraries which require those specific interfaces.

So for example, popular .NET logging frameworks such as log4net, NLog, Serilog along with the likes of the Microsoft Enterprise Block Logging might be getting used/expected in different parts of our application and libraries. Each ultimately offers similar functionality but we really don’t want multiple logging frameworks if we can help it.

The common-logging was introduced a fair few years back to allow us to write code with a standarised interface, but allow us to use whatever logging framework we wanted behind the scenes. Microsoft’s Microsoft.Extensions.Logging offers a similar abstraction.

Out of the box, the Microsoft.Extensions.Logging comes with some standard logging capabilities, Microsoft.Extensions.Logging.Console, Microsoft.Extensions.Logging.Debug, Microsoft.Extensions.Logging.EventLog, Microsoft.Extensions.Logging.TraceSource and Microsoft.Extensions.Logging.AzureAppServices. As the names suggest, these give us logging to the console, to debug, to the event log, to trace source and to Azure’s diagnostic logging.

Microsoft.Extensions.Logging offers is a relatively simple mechanism for adding further logging “providers” and third part logging frameworks such as NLog, log4net and SeriLog.

How to use Microsoft.Extensions.Logging

Let’s start by simply seeing how we can create a logger using this framework.

Add the Microsoft.Extensions.Logging and Microsoft.Extensions.Logging.Debug NuGet packages to your application.

The first gives us the interfaces and code for the LoggerFactory etc. whilst the second gives us the debug extensions for the logging factory.

Note: The following code has been deprecated and replaced with ILoggerBuilder extensions.

var factory = new LoggerFactory()
   .AddDebug(LogLevel.Debug);

Once the logger factory has been created we can create an ILogger using

ILogger logger = factory.CreateLogger<MyService>();

The MyService may be a type that you want to create logs for, alternatively you can pass the CreateLogger a category name.

Finally, using a standard interface we can log something using the following code

logger.Log(LogLevel.Debug, "Some message to log");

Using other logging frameworks

I’ll just look at a couple of other frameworks, NLog and Serilog.

For NLog add the NuGet package NLog.Extensions.Logging, for Serilog add Serilog.Extensions.Logging and in my case I’ve added Serilog.Sinks.RollingFile to create logs to a rolling file and Serilog.Sinks.Debug for debug output.

Using NLog

Create a file named nlog.config and set it’s properties within Visual Studio as Copy always. Here’s a sample

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" 
   xsi:schemaLocation="NLog NLog.xsd"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   autoReload="true"
   internalLogFile="c:\temp\mylog.log"
   internalLogLevel="Info" >


<targets>
   <target xsi:type="File" name="fileTarget" 
      fileName="c:\temp\mylog.log"      
      layout="${date}|${level:uppercase=true}|${message}
         ${exception}|${logger}|${all-event-properties}" />
   <target xsi:type="Console" name="consoleTarget"
      layout="${date}|${level:uppercase=true}|${message} 
         ${exception}|${logger}|${all-event-properties}" />
</targets>

<rules>
   <logger name="*" minlevel="Trace" writeTo="fileTarget,consoleTarget" />
</rules>
</nlog>

Now in code we can load this configuration file using

NLog.LogManager.LoadConfiguration("nlog.config");

and now the only difference from the previous example of using the LoggerFactory is change the creation of the factory to

var factory = new LoggerFactory()
   .AddNLog();

Everything else remains the same. Now you should be seeing a file named mylog.log in c:\temp along with debug output.

Using serilog

In Serilog’s case we’ll create the logger configuration in code, hence here’s the code to create both a file and debug log

Note: See Serilog documentation for creating the configuration via a config or settings file.

Log.Logger = new LoggerConfiguration()
   .MinimumLevel.Debug()
   .WriteTo.RollingFile("c:\\temp\\log-{Date}.txt")
   .WriteTo.Debug()
   .CreateLogger();

The will create a log file in c:\temp named log-{Date}.txt, where {Date} is replaced with today’s date. Obviously the include of WriteTo.Debug also gives us debug output.

We simply create the logger using familiar looking code

var factory = new LoggerFactory()
   .AddSerilog();

Everything else works the same but now we’ll see Serilog output.

Creating our own LoggerProvider

As you’ve seen in all examples, extension methods are used to AddDebug, AddNLog, AddSerilog. Basically each of these adds an ILoggerProvider to the factory. We can easily add our own provider by implementing the ILoggerProvider interface, here’s a simple example of a DebugLoggerProvider

public class DebugLoggerProvider : ILoggerProvider
{
   private readonly ConcurrentDictionary<string, ILogger> _loggers;

   public DebugLoggerProvider()
   {
      _loggers = new ConcurrentDictionary<string, ILogger>();
   }

   public void Dispose()
   {
   }

   public ILogger CreateLogger(string categoryName)
   {
      return _loggers.GetOrAdd(categoryName, new DebugLogger());
   }
}

The provider needs to keep track of any ILogger’s created based upon the category name. Next we’ll create a DebugLogger which implements the ILogger interface

public class DebugLogger : ILogger
{
   public void Log<TState>(
      LogLevel logLevel, EventId eventId, 
      TState state, Exception exception, 
      Func<TState, Exception, string> formatter)
   {
      if (formatter != null)
      {
         Debug.WriteLine(formatter(state, exception));
      }
   }

   public bool IsEnabled(LogLevel logLevel)
   {
      return true;
   }

   public IDisposable BeginScope<TState>(TState state)
   {
      return null;
   }
}

In this sample logger, we’re going to handle all LogLevel’s and are not supporting BeginScope. So all the work is done in the Log method and even that is pretty simple as we use the supplied formatter to create our message then output it to our log sink, in this case Debug. If no formatter is passed, we’ll currently not output anything, but obviously we could create our own formatter to be used instead.

Finally, sticking with the extension method pattern to add a provider, we’ll create the following

public static class DebugLoggerFactoryExtensions
{
   public static ILoggerFactory AddDebugLogger(
      this ILoggerFactory factory)
   {
      factory.AddProvider(new DebugLoggerProvider());
      return factory;
   }
}

That’s all there is to this very simple example, we create the factory in the standard way, i.e.

var factory = new LoggerFactory()
   .AddDebugLogger();

and everything else works without any changes.