Custom Logging Extensions
A custom logger is a standard C# class thats exposes a static factory method and an implementation.
As an example we discuss an imaginary logger that sends logs to some kind of external service.
Firstly we will need the builder method. This is the method that is called as part of the runtime build process and creates a new instance of your logging controller. This controller is responsible for creating new ILogger instances and managing the interactions any other logging framework you may be using.
// This exposes the installed method for installing the logging framework when
// creating a runtime
public static class MyCustomLoggingInstaller
{
public static IRuntimeFactory UseMyCustomLogging(this IRuntimeFactory runtimeFactory)
{
runtimeFactory.SetupLogging(new MyCustomLoggingImpl());
return runtimeFactory;
}
}
The logging controller will look something like the following. This version creates a connection to an imaginary remote logging service.
internal class MyCustomLoggingImpl: ILoggingImplementation
{
private MyLoggerLogger rootLogger;
public OpenFin.Net.Adapter.Logging.ILogger GetLogger(Type context = null)
{
if (context != null)
{
return new MyLoggerLogger(myServiceConnection, context);
}
return rootLogger;
}
public void Initialize(LogConfig loggerConfig, string targetFilePath)
{
// TODO: Setup some remote logger service connection
myServiceConnection = ...create connection;
rootLogger = new MyLoggerLogger(myServiceConnection);
}
}
The Initialize method is responsible for setting up any logging service you may be using or creating files etc.
The GetLogger method is called whenever a new logger for a context is required.
The following example creates a new logger with a passed in context of MainWindow.
var loggerMgr = runtime.GetService<ILogManager>();
var logger = loggerMgr.GetLogger(typeof(MainWindow));
A logger implementation simply handles each of the log levels and outputs any message to your required destination.
An ILogger may look something like
public class MyLoggerLogger : ILogger
{
private LogConfig logConfig;
public MyLoggerLogger(ISomeConnection connection, Type context)
{
Connection = connection;
Context = context;
}
public ISomeConnection Connection { get; }
public Type Context{ get; }
/// Invoked after creation and whenever the logger configuration changes
public void OnLogConfigUpdate(LogConfig logConfig)
{
this.logConfig = logConfig;
}
// Output a message to our logging service
public void Debug(string message)
{
Connection.SendDebug(message);
}
// Output a message and any passed exception to our loggin service
public void Debug(string message, Exception exception)
{
Connection.SendDebug(exception, message);
}
// Output a message resulting from a Func evaluation to our logging service
// Note: The func is only evaluated if the logging level matches, in this case, debug. This allows for more
// output but minimal impact on performance if the log level is not enabled
public void Debug(Func<string> message)
{
if (LogLevel.Debug >= logConfig.MinLevel)
{
Debug(message());
}
}
public void Error(string message)
{
Connection.SendError(message);
}
public void Error(string message, Exception exception)
{
Connection.SendError(exception, message);
}
public void Error(Func<string> message)
{
if (LogLevel.Error >= logConfig.MinLevel)
{
Error(message());
}
}
public void Fatal(string message)
{
Connection.SendFatal(message);
}
public void Fatal(string message, Exception exception)
{
Connection.SendFatal(exception, message);
}
public void Fatal(Func<string> message)
{
if (LogLevel.Fatal >= logConfig.MinLevel)
{
Fatal(message());
}
}
public void Information(string message)
{
Connection.SendInformation(message);
}
public void Information(string message, Exception exception)
{
Connection.Information(exception, message);
}
public void Information(Func<string> message)
{
if (LogLevel.Information >= logConfig.MinLevel)
{
Information(message());
}
}
public void Verbose(string message)
{
Connection.SendSendVerbose(message);
}
public void Verbose(string message, Exception exception)
{
Connection.SendVerbose(exception, message);
}
public void Verbose(Func<string> message)
{
if (LogLevel.Verbose >= logConfig.MinLevel)
{
Verbose(message());
}
}
public void Warning(string message)
{
Connection.SendWarning(message);
}
public void Warning(string message, Exception exception)
{
Connection.SendWarning(exception, message);
}
public void Warning(Func<string> message)
{
if (LogLevel.Warning >= logConfig.MinLevel)
{
Warning(message());
}
}
}
Once you have your logger written and deployed as an assembly you can enabled within your applications using the runtime builder.
For example:
runtime = new RuntimeFactory()
.UseMyCustomLogging()
.GetRuntimeInstance(new RuntimeOptions
{
Version = "stable",
UUID = "dotnet-adapter-sample-wpf-channels",
LicenseKey = "your-license-key"
});
var loggerMgr = runtime.GetService<ILogManager>();
var logger = loggerMgr.GetLogger(typeof(MainWindow));
logger.Debug("This will be router via my custom logger");