Jump to content
  • Creating Custom Logging Functionality for Spotfire® Extensions


    This article provides information about how to create custom extensions that use log4net without interfering with the log4net usage already performed by the Spotfire software.

    Introduction

    When adding logging capabilities to custom functionality extending the Spotfire® client it is important to do so without interfering with the log4net usage already performed by the Spotfire software. This can be achieved by instantiating and suppliying the logger settings of the custom component programmatically in order to avoid overwriting the Spotfire client's logger settings, in combination with either avoiding to specify and provide a specific version of log4net, or avoid linking to log4net at all at compile time of the custom logging functionality.

    Avoiding specific version dependency in Visual Studio and Package Builder

    Avoiding version dependencies may be accomplished by the following:

    1. Compile the custom extension referencing the log4net DLL as "Specific Version False" in the reference properties for the log4net DLL.
    2. In the Spotfire SDK Package Builder (more information here), choose not to include the the log4net.dll in the .spk file when building

      no4netdll.png.45783e6e1bffdc44032b5c87a7c1fd90.png

       

    3. Instantiate the logger in the extension code without interfering with the existing configuration as well as making sure that the custom logging and Spotfire client log messages are written to separate log files. The root of the log4net configuration must not be replaced. One way to do this is to programmatically add the appender with the needed parameters, e.g.
      public static IAppender CreateFileAppender(string name, string fileName)
      {
      	var layout = new log4net.Layout.PatternLayout
      	{
      		ConversionPattern = "%d{yyyy-MM-dd hh:mm:ss} - [%thread] %-5level %logger - %message%newline"
      	};
      	layout.ActivateOptions();
      
      	var appender = new RollingFileAppender
      	{
      		Name = name,
      		AppendToFile = true,
      		File = fileName,
      		Layout = layout,
      		MaxSizeRollBackups = 5,
      		MaximumFileSize = "5MB",
      		RollingStyle = RollingFileAppender.RollingMode.Size,
      		StaticLogFileName = true
      	};
      
      	appender.ActivateOptions();
      
      	return appender;
      }

    This should make sure that Spotfire is able to load the correct log4net version and the custom functionality spk as  well as that the log entries is are written in a file separate from the Spotfire clients own log file, and that the Spotfire clients log messages does not accidentally get written into the custom components log file.

    Runtime access to log4net through reflection

    If the above method does not work in a specific case, there is the choice not link to log4net at all, but use reflection to access the log4net assembly already loaded by the Spotfire application and run it through the .NET reflection mechanism. This will allow you to do without any kind of compile-time linking between the custom code contained in the .spk and the log4net dll. 

    // Example of how to get the needed log4net types from the Spotfire appdomain
    foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies())
    {
    	if (a.GetName().Name.Contains("log4net"))
    	{
    		foreach (Type t in a.GetTypes())
    		{
    			if (t.Namespace != null && (t.Namespace.Equals("log4net.Appender") && t.Name.Equals("IAppender")))
    			{
    				_iAppenderType = t;
    			}
    			if (t.Namespace != null && (t.Namespace.Equals("log4net") && t.Name.Equals("LogManager")))
    			{
    				_logManagerType = t;
    			}
    			if (t.Namespace != null && (t.Namespace.Equals("log4net.Layout") && t.Name.Equals("PatternLayout")))
    			{
    				_patternLayoutType = t;
    			}
    			if (t.Namespace != null && (t.Namespace.Equals("log4net.Appender") && t.Name.Equals("RollingFileAppender")))
    			{
    				_rollingAppenderType = t;
    			}
    			if (t.Namespace != null && (t.Namespace.Equals("log4net.Repository.Hierarchy") && t.Name.Equals("Logger")))
    			{
    				_loggerType = t;
    			}
    			if (t.Namespace != null && (t.Namespace.Equals("log4net.Core") && t.Name.Equals("LogImpl")))
    			{
    				_logImplType = t;
    				_debugMethodInfo = _logImplType.GetMethod("Debug", new[] {typeof(object) });
    				_infoMethodInfo = _logImplType.GetMethod("Info", new[] { typeof(object) });
    				_warnMethodInfo = _logImplType.GetMethod("Warn", new[] { typeof(object) });
    				_errorMethodInfo = _logImplType.GetMethod("Error", new[] { typeof(object) });
    
    				_debugMethodExpInfo = _logImplType.GetMethod("Debug", new[] { typeof(object), typeof(Exception) });
    				_infoMethodExpInfo = _logImplType.GetMethod("Info", new[] { typeof(object), typeof(Exception) });
    				_warnMethodExpInfo = _logImplType.GetMethod("Warn", new[] { typeof(object), typeof(Exception) });
    				_errorMethodExpInfo = _logImplType.GetMethod("Error", new[] { typeof(object), typeof(Exception) });
    			}
    		}
    	}
    }
    
    // Example on creating the appender logger object
    MethodInfo getLoggerInfo = _logManagerType.GetMethod("GetLogger", new[] {typeof(string)});
                        _oLog = getLoggerInfo.Invoke(null, new object []{AppenderName });
    
    // Example on how to call the logging function
    public static void Debug(object message)
    {
    	_debugMethodInfo.Invoke(_oLog, new[] {message});
    }
     

    User Feedback

    Recommended Comments

    There are no comments to display.


×
×
  • Create New...