Plux.NET
Logger Tutorial

Click here for a screencast of this quick tutorial.

Downloads

Please note that this is an alpha version of Plux. Modifications of features and programming interfaces may happen. The Plux executables are distributed "as is" and without any warranty or license. The sources are not (yet) published, and a license model for them is yet to be defined.

Table of Contents

Introduction

Let's look at a simple example. Assume that we want to write an application that performs some actions and logs them. Since the logging should be kept flexible, we do not implement it as part of the application but rather as an extension. For every action to be logged, the application will pass to the extension a log message with a time stamp.

Step 1: Define a slot »Logger«

First we define a slot into which extensions can be plugged:

using Plux;
[SlotDefinition("Logger")]
[ParamDefinition("TimeFormat", typeof(string))]
public interface ILogger {
  void Print(string msg);
}

A slot is an interface tagged with a »SlotDefinition« attribute specifying the name of the slot ("Logger"). A slot can have parameters defined by »ParamDefinition« attributes. In our case we have just a single parameter »TimeFormat« of type »string«, which is to be filled by the extension. We compile this interface to an assembly »ILogger.dll«.

csc /t:library /out:ILogger.dll /r:Plux.dll ILogger.cs

Step 2: Write an extension for the »Logger« slot

Now we write an extension that fits into the »Logger« slot:

using System;
using Plux;
[Extension("ConsoleLogger")]
[Plug("Logger")]
[Param("TimeFormat", "hh:mm:ss")]
public class ConsoleLogger: ILogger {
  public void Print(string msg) {
    Console.WriteLine(msg);
  }
}

An extension is a class tagged with an »Extension« attribute and implementing the interface of the corresponding slot. In our example the extension attribute defines an extension name "ConsoleLogger". The »Plug« attribute defines a plug that fits into the logger slot. The »Param« attribute assigns the value "hh:mm:ss" to the parameter »TimeFormat«. We compile this class to an assembly »ConsoleLogger.dll«.

csc /t:library /out:ConsoleLogger.dll /r:Plux.dll,ILogger.dll ConsoleLogger.cs

Step 3: Open the »Logger« slot in the application

Our application runs under Plux.NET, so it has to be implemented as a plug-in itself extending the core. The core has a slot »Application« into which our application should plug. So we need something like this:

using System;
using Plux;
[Extension]
[Plug("Application")]
public class MyApp : IApplication {...}

The application has to open a logger slot. This is done with an »Slot« attribute as shown below:

using System;
using System.Threading;
using Plux;
[Extension]
[Plug("Startup")]
[Slot("Logger", OnPlugged="AddLogger")]
public class MyApp : IApplication {

  ILogger logger = null; // the logger extension
  string  timeFormat;    // parameter of the logger extension

  public void Start() {
    string msg;
    while (true) {
      DoSomeAction(out msg);
      if (logger != null) {
        string time = DateTime.Now.ToString(timeFormat);
        logger.Print(time + ": " + msg);
      }
      Thread.Sleep(1500);
    }
  }

  public void AddLogger(object s, CompositionEvent args) {
    logger = (ILogger) args.Plug.Extension.Object;
    timeFormat = (string) args.Plug.Params["TimeFormat"].Value;
  }

  void DoSomeAction(out string msg) {
    msg = "Hello";
  }
}

The »Slot« attribute specifies that »MyApp« opens a logger slot. Whenever the runtime finds an extension that fits into this slot it loads it and throws an »Plugged« event, which causes »AddLogger« to be invoked. »AddLogger« stores a reference to the plugged extension in the field »logger«. It also retrieves the value of the »TimeFormat« parameter and stores it in the field »timeFormat«. In that way the extension is integrated with the host. The »Start« method is called by the runtime core, because it is part of the »Application« slot contract. It repeatedly performs some action and calls »logger.Print« (if a logger extension has been plugged in).

We compile this class to an assembly »MyApp.dll«.

csc /t:library /out:MyApp.dll /r:Plux.dll,ILogger.dll MyApp.cs

Step 4: Run the plug-in application

We have two plug-ins now (»MyApp.dll« and »ConsoleLogger.dll«) and one contract (»ILogger.dll«). All three assemblies are moved into a common directory together with the runtime core »Plux.dll« and the runtime starter »Plux.exe«. By default plug-ins and contracts that reside in the same directory as »Plux.exe« are discovered at startup. If your plug-ins reside in directories separate from »Plux.exe« use the command line arguments /discovery and /base (enter plux /? for help).

If we start »Plux« (enter plux.exe) it searches the directory for an extension that fits into the »Application« slot. It finds »MyApp.dll« and installs it. Since »MyApp« opens a »Logger« slot the runtime again searches the directory for a matching extension. It finds »ConsoleLogger.dll« and plugs it in.

Hot Plugging

Adding plug-ins. Plux supports hot plugging, i.e. extensions can not only be added to an application at startup but also at any later time without disrupting its execution. Hot plugging requires a plug-in such as »FileSystemDetector.dll«, which is a discovery component that monitors the application directory to detect any additions or removals of plug-ins. Although the discovery can replaced by a custom discovery component later, »FileSystemDetector.dll« has to be there in the beginning.

In order to demonstrate hot plugging, let us start »Plux.exe« from a directory containing only »FileSystemDetector.dll«. The discovery plug-in »FileSystemDetector.dll« is plugged into a dedicated »Discovery« slot of the runtime. We don't see any effects yet, but the discovery plug-in now monitors the directory for additions. If we now move »MyApp.dll« into the directory it will be detected as a valid extension and plugged into the »Application« slot of the core. »MyApp« is running now performing its actions but without logging them. If we move also »ConsoleLogger.dll« into the directory it will be plugged into the »Logger« slot of »MyApp«, causing logging to take effect.

Removing plug-ins. Plux allows you not only to add extensions but also to remove them at run time. In order to do so we have to provide a handler for the »Unplugged« event:

[Extension]
[Plug("Application")]
[Slot("Logger", OnPlugged="AddLogger", OnUnplugged="RemoveLogger")]
public class MyApp : IApplication {
  ...
  public void RemoveLogger(object source, CompositionEventArgs args) {
    logger = null;
    timeFormat = null;
  }
}

If we now remove »ConsoleLogger.dll« from the directory this will throw an »Unplugged« event and »RemoveLogger« will be invoked. »MyApp« continues to run but it will not log its actions any more.