This is a code review for a set of loggers following [this](https://stackoverflow.com/questions/41175864/how-to-assure-contract-with-multiple-classes-that-are-similar-in-behavior-but-di/41230131?noredirect=1#comment69660624_41230131) question.
The project is a universal windows application targeting Windows Phone.

There are concerns over the tight coupling between the loggers and App. There is way too much code in the main program that is responsible for setting up the logger just right.

There are also concerns on how to deal with the fact that a ReleaseLogger requires special behavior (establishing a session) that the DebugLogger class does not, and how to deal with this.

App.xaml.cs

```
using Log.Common;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.Foundation.Diagnostics;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Animation;
using Windows.UI.Xaml.Navigation;

// The Blank Application template is documented at http://go.microsoft.com/fwlink/?LinkId=234227

namespace Log
{
    /// <summary>
    /// Provides application-specific behavior to supplement the default Application class.
    /// </summary>
    public sealed partial class App : Application
    {
#if WINDOWS_PHONE_APP
        private TransitionCollection transitions;
#endif

        public const string TelemetryUrl = "http://try.count.ly";
        public const string TelemetryAppKey = "";

        private ILogger _logger;
        private ILogger Logger
        {
            get
            {
                if(null == _logger)
                {
//#if PROD
                    _logger = new ReleaseLogger();
//#else
                    //_logger = new DebugLogger();
//#endif
                }
                return _logger;
            }
        }

        /// <summary>
        /// Initializes the singleton application object.  This is the first line of authored code
        /// executed, and as such is the logical equivalent of main() or WinMain().
        /// </summary>
        public App()
        {
            this.InitializeComponent();
            this.Suspending += this.OnSuspending;
            this.Resuming += this.OnResuming;
            this.UnhandledException += OnUnhandledException;
        }

        private async Task TryStartTelemetry()
        {
            var logger = Logger as ReleaseLogger;
            if (null != logger)
            {
                await logger.StartSessionAsync(this, TelemetryUrl, TelemetryAppKey);
            }
        }

        private async Task TryEndTelemetry()
        {
            var logger = Logger as ReleaseLogger;
            if (null != logger)
            {
                await logger.EndSessionAsync();
            }
        }

        /// <summary>
        /// Invoked when the application is launched normally by the end user.  Other entry points
        /// will be used when the application is launched to open a specific file, to display
        /// search results, and so forth.
        /// </summary>
        /// <param name="e">Details about the launch request and process.</param>
        protected override async void OnLaunched(LaunchActivatedEventArgs e)
        {
#if DEBUG
            if (System.Diagnostics.Debugger.IsAttached)
            {
                this.DebugSettings.EnableFrameRateCounter = true;
            }
#endif

            Frame rootFrame = Window.Current.Content as Frame;

            // Do not repeat app initialization when the Window already has content,
            // just ensure that the window is active
            if (rootFrame == null)
            {
                // Create a Frame to act as the navigation context and navigate to the first page
                rootFrame = new Frame();

                // TODO: change this value to a cache size that is appropriate for your application
                rootFrame.CacheSize = 1;

                if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
                {
                    // TODO: Load state from previously suspended application
                }

                // Place the frame in the current Window
                Window.Current.Content = rootFrame;
            }

            if (rootFrame.Content == null)
            {
#if WINDOWS_PHONE_APP
                // Removes the turnstile navigation for startup.
                if (rootFrame.ContentTransitions != null)
                {
                    this.transitions = new TransitionCollection();
                    foreach (var c in rootFrame.ContentTransitions)
                    {
                        this.transitions.Add(c);
                    }
                }

                rootFrame.ContentTransitions = null;
                rootFrame.Navigated += this.RootFrame_FirstNavigated;
#endif

                // When the navigation stack isn't restored navigate to the first page,
                // configuring the new page by passing required information as a navigation
                // parameter
                if (!rootFrame.Navigate(typeof(MainPage), e.Arguments))
                {
                    throw new Exception("Failed to create initial page");
                }
            }

            await TryStartTelemetry();

            Logger.Log("Application Launched", LoggingLevel.Information, null);

            // Ensure the current window is active
            Window.Current.Activate();
        }

        protected override async void OnActivated(IActivatedEventArgs args)
        {
            base.OnActivated(args);

            await TryStartTelemetry();
        }

#if WINDOWS_PHONE_APP
        /// <summary>
        /// Restores the content transitions after the app has launched.
        /// </summary>
        /// <param name="sender">The object where the handler is attached.</param>
        /// <param name="e">Details about the navigation event.</param>
        private void RootFrame_FirstNavigated(object sender, NavigationEventArgs e)
        {
            var rootFrame = sender as Frame;
            rootFrame.ContentTransitions = this.transitions ?? new TransitionCollection() { new NavigationThemeTransition() };
            rootFrame.Navigated -= this.RootFrame_FirstNavigated;
        }
#endif

        /// <summary>
        /// Invoked when application execution is being suspended.  Application state is saved
        /// without knowing whether the application will be terminated or resumed with the contents
        /// of memory still intact.
        /// </summary>
        /// <param name="sender">The source of the suspend request.</param>
        /// <param name="e">Details about the suspend request.</param>
        private async void OnSuspending(object sender, SuspendingEventArgs e)
        {
            var deferral = e.SuspendingOperation.GetDeferral();

            // TODO: Save application state and stop any background activity
            await TryEndTelemetry();

            deferral.Complete();
        }

        private async void OnResuming(object sender, object e)
        {
            await TryStartTelemetry();
        }

        private async void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            await TryEndTelemetry();
        }
    }
}

```

ILogger.cs

```
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Windows.Foundation.Diagnostics;

namespace Log.Common
{
    public interface ILogger
    {
        void LogException(Exception ex);

        void Log(string message, LoggingLevel loggingLevel, params object[] args);
    }
}

```

DebugLogger.cs

```
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Foundation.Diagnostics;

namespace Log.Common
{
    public class DebugLogger : ILogger
    {
        public void LogException(Exception ex)
        {
            if (ex == null)
                throw new ArgumentNullException("exception");
            Debug.WriteLine("Exception: " + ex.Message);
            // Log to UI here.
        }

        public void Log(string message, LoggingLevel loggingLevel, params object[] args)
        {
            if (string.IsNullOrWhiteSpace(message))
                throw new ArgumentException("message");

            if (args != null && args.Length > 0)
            {
                message = string.Format(message, args);
            }
            //TODO: let's define when and where log 
            switch (loggingLevel)
            {
                case LoggingLevel.Critical:
                    Debug.WriteLine("Critical: " + message, args);
                    // Log to UI here.
                    break;
                case LoggingLevel.Error:
                    Debug.WriteLine("Error: " + message, args);
                    // Log to UI here.
                    break;
                case LoggingLevel.Warning:
                    Debug.WriteLine("Warning: " + message, args);
                    // Log to UI here.
                    break;
                case LoggingLevel.Information:
                    Debug.WriteLine("Info: " + message, args);
                    // Log to UI here.
                    break;
                case LoggingLevel.Verbose:
                    Debug.WriteLine("Verbose: " + message, args);
                    break;
                default:
                    throw new ArgumentOutOfRangeException(nameof(loggingLevel), loggingLevel, null);
            }
        }
    }
}

```

ReleaseLogger.cs

```
using CountlySDK;
using Log;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Foundation.Diagnostics;
using Windows.UI.Xaml;

namespace Log.Common
{
    public class ReleaseLogger : ILogger
    {
        private bool _sessionStarted;
        // A Queue of exceptions to log.
        private Queue<Exception> _exceptionQueue;

        public ReleaseLogger()
        {
            _exceptionQueue = new Queue<Exception>();
            _sessionStarted = false;
            Countly.IsLoggingEnabled = true;
        }

        public async Task StartSessionAsync(Application app, string telemetryUrl, string telemetryAppKey)
        {
            if (telemetryUrl == null)
                throw new ArgumentNullException("telemetryUrl");
            if (telemetryAppKey == null)
                throw new ArgumentNullException("telemetryAppKey");

            try
            {
                await Countly.StartSession(telemetryUrl, telemetryAppKey, app);
                while (_exceptionQueue.Count > 0)
                {
                    await LogExceptionAsync(_exceptionQueue.Dequeue());
                }
                _sessionStarted = true;
            }
            catch (Exception countlyEx)
            {
                // Log to UI here.
            }
        }

        public async Task EndSessionAsync()
        {
            try
            {
                await Countly.EndSession();
            }
            catch (Exception countlyEx)
            {
                // Log to UI here.
            }
            finally
            {
                _sessionStarted = false;
                // Log to UI here.
            }
        }

        public async Task LogExceptionAsync(Exception ex)
        {
            if (ex == null)
                throw new ArgumentNullException("exception");

            if (_sessionStarted)
            {
                try
                {
                    await Countly.RecordException(ex.Message, ex.StackTrace);
                }
                catch (Exception countlyEx)
                {
                    // Log to UI here.
                }
            }
            else
            {
                _exceptionQueue.Enqueue(ex);
            }
        }

        public void LogException(Exception ex)
        {
            var result = LogExceptionAsync(ex);
        }

        public async Task LogAsync(string message, LoggingLevel loggingLevel, params object[] args)
        {
            if (string.IsNullOrWhiteSpace(message))
                throw new ArgumentException("message");

            if (args != null && args.Length > 0)
            {
                message = string.Format(message, args);
            }
            switch (loggingLevel)
            {
                case LoggingLevel.Critical:
                case LoggingLevel.Error:
                    // Log to UI here.
                    await Countly.RecordException(message);
                    break;
                case LoggingLevel.Warning:
                    // Log to UI here.
                    break;
                case LoggingLevel.Information:
                    await Countly.RecordEvent(message);
                    // Log to UI here.
                    break;
                case LoggingLevel.Verbose:
                    Debug.WriteLine(message, args);
                    break;
                default:
                    throw new ArgumentOutOfRangeException(nameof(loggingLevel), loggingLevel, null);
            }
        }

        public void Log(string message, LoggingLevel loggingLevel, params object[] args)
        {
            var result = LogAsync(message, loggingLevel, args);
        }
    }
}

```
['https://codereview.stackexchange.com/questions/150528', 'https://codereview.stackexchange.com', 'https://codereview.stackexchange.com/users/42250/']
Interesting question,

* I noticed that 9-3\*3 does not return 0, but 27, you are not following PEMDAS
* Personally, I would make sure that the input only contains numbers, dots, and math operators, and then use `eval`. `eval("9-3*3")` does return correctly 0.
* 0.0022 \* 2 returns zero.
* I would assign html elements like the one found from `document.getElementById(numberScreen)` to a higher scope variable for readability and performance
* You do not check for existing periods in the current number, allowing for numbers like 12.24.2016
* Using a pure UI function (`innerHTML`) to drive the logic is wrong. It's okay for a beginner class, but anything more advanced should drive of (`id`). You dont want to replace 'x' with '\*' and also having to change the JS code
* Your indentation is not perfect, use <http://jsbeautifier.org/>
* You have zero warnings on jshint.com, which is amazing
Just a few side notes:

To ordinary people, `%` means *percent*. Only programmers think of `%` being the modulus (or remainder, depending on the language) operator. Since your calculator is intended for ordinary people, the `%` means exactly what it should.

You should add a function to display a result:

```
function display(result) {
    document.getElementById(numberScreen).innerHTML = result;
}

```

With this function you don't need to mention `numberScreen` anywhere else in the code, which is good since the calculator code should talk about numbers and operators, not about manipulating HTML.