Thursday, June 24, 2010

Silverlight Browser Console logger

Presenting another just very draft and simple concept here. This time it is about ability to log into browser console from silverlight, like this:

image

BTW, as it stands, in the current April 2010 release of a Silverlight 4 toolkit we can’t write into “Output” field of a test. Believe me it is just not passed through and bound anyhow on the screen. Hopefully it will be fixed in the next release.

Still, during tests and in many other places we can benefit from writing directly to the browser console.

So we can start with adding few javascript methods to the test/host page:

    <script type="text/javascript">
        function LogInfo(message) { if (((typeof (console) !== "undefined") && (typeof (console.log) !== "undefined"))) console.log("info:" + message); }
        function LogError(message) { if (IsConsoleDefined()) console.error(message); }
        function LogFatal(message) { if (IsConsoleDefined()) console.error(message); }
        function LogWarning(message) { if (IsConsoleDefined()) console.warn(message); }
        function LogDebug(message) { if (IsConsoleDefined()) console.log("debug:" + message); }
        function IsConsoleDefined() { return ((typeof (console) !== "undefined") && (typeof (console.log) !== "undefined")); }
    </script>

What we want as well is being able to control log level from the page somehow, e.g.:

http://localhost:59277/BrowserLoggerTestTestPage.aspx?log=debug

Which can by done with a help of the HtmlPage.Document.QueryString:

        private static string ResolveLogLevel()
        {
            string requestedLogLevel = null;
            HtmlPage.Document.QueryString.TryGetValue("log", out requestedLogLevel);
            return requestedLogLevel;
        }

And finally it is just about calling the javascript method from Silverlight:

        public void Debug(string message)
        {
            string requestedLogLevel = ResolveLogLevel();
 
            if (requestedLogLevel != null && requestedLogLevel.ToLower() == "debug")
                HtmlPage.Window.Invoke(LOG_DEBUG_FUNCTION_NAME, message);
        }

The usage would be like:

    [TestClass]
    public class Tests
    {
        [TestMethod]
        public void Log()
        {
            ILogger logger = new BrowserConsoleLogger();
            logger.Debug("Debug message.");
            logger.Info("Info message.");
            logger.Warning("Warning message.");
            logger.Error("Error message.", new Exception("Test exception"));
            logger.Fatal("Fatal error message", new Exception("Fatal exception"));
        }
    }

And finally, the complete logger code, please note this is just a draft concept implementation and can/should be subject to many improvements:

using System;
using System.Collections.Generic;
using System.Windows.Browser;
 
 
namespace BrowserLoggerTest
{
    interface ILogger
    {
        void Debug(string message);
        void Error(string message, Exception ex);
        void Fatal(string message, Exception ex);
        void Info(string message);
        void Warning(string message);
    }
 
    public class BrowserConsoleLogger : ILogger
    {
        public const string LOG_DEBUG_FUNCTION_NAME = "LogDebug";
        public const string LOG_ERROR_FUNCTION_NAME = "LogError";
        public const string LOG_FATAL_FUNCTION_NAME = "LogFatal";
        public const string LOG_INFO_FUNCTION_NAME = "LogInfo";
        public const string LOG_WARNING_FUNCTION_NAME = "LogWarning";
 
        private readonly List<string> _levelsForError = new List<string> { "debug", "info", "error", "warn" };
        private readonly List<string> _levelsForFatal = new List<string> { "debug", "info", "error", "warn", "fatal" };
        private readonly List<string> _levelsForInfo = new List<string> { "debug", "info" };
        private readonly List<string> _levelsForWarning = new List<string> { "debug", "info", "warn" };
 
        public void Debug(string message)
        {
            string requestedLogLevel = ResolveLogLevel();
 
            if (requestedLogLevel != null && requestedLogLevel.ToLower() == "debug")
                HtmlPage.Window.Invoke(LOG_DEBUG_FUNCTION_NAME, message);
        }
 
        public void Error(string message, Exception ex)
        {
            string requestedLogLevel = ResolveLogLevel();
 
            if (requestedLogLevel != null && _levelsForError.Contains(requestedLogLevel.ToLower()))
                HtmlPage.Window.Invoke(LOG_ERROR_FUNCTION_NAME, String.Format("{0}. Exception text: {1}", message, ex));
        }
 
 
        public void Fatal(string message, Exception ex)
        {
            string requestedLogLevel = ResolveLogLevel();
 
            if (requestedLogLevel != null && _levelsForFatal.Contains(requestedLogLevel.ToLower()))
                HtmlPage.Window.Invoke(LOG_FATAL_FUNCTION_NAME, String.Format("{0}. Exception text: {1}", message, ex));
        }
 
        public void Info(string message)
        {
            string requestedLogLevel = ResolveLogLevel();
 
            if (requestedLogLevel != null && _levelsForInfo.Contains(requestedLogLevel.ToLower()))
                HtmlPage.Window.Invoke(LOG_INFO_FUNCTION_NAME, message);
        }
 
        public void Warning(string message)
        {
            string requestedLogLevel = ResolveLogLevel();
 
            if (requestedLogLevel != null && _levelsForWarning.Contains(requestedLogLevel.ToLower()))
                HtmlPage.Window.Invoke(LOG_WARNING_FUNCTION_NAME, message);
        }
 
        private static string ResolveLogLevel()
        {
            string requestedLogLevel = null;
            HtmlPage.Document.QueryString.TryGetValue("log", out requestedLogLevel);
            return requestedLogLevel;
        }
    }
}

No comments: