<commit_before>﻿/*
 * Author: <NAME> (<EMAIL>)
 * Date: 10/5/2014
 * License: MIT (http://opensource.org/licenses/MIT)
 * GitHub: https://github.com/ivanpointer/NuLog
 */
using NuLog.Configuration;
using NuLog.Dispatch;
using NuLog.Loggers;
using NuLog.MetaData;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;

namespace NuLog
{
    /// <summary>
    /// The factory for building and providing all of the pieces for the NuLogging framework.
    /// The factory ties the four other major pieces of the framework together:
    ///   configuration, the dispatcher, the targets and loggers
    /// </summary>
    public class LoggerFactory
    {
        #region Constants

        // Messages
        private const string FailedConfigBuilderMessage = "Failed to configure using the provided config builder {0}";
        private const string ConfigurationFailedUsingDefaultsMessage = "Configuration failed, using default configuration";
        private const string ConfigLoadFailureMessage = "Failure loading config: {0}:\r\n{1}";
        private const string ShutdownDispatcherFailureMessage = "Failed to shutdown the existing dispatcher due to error {0}";
        private const string ConfigExtendersLoadFailedMessage = "Failure loading configuration extenders: {1}:\r\n{2}";

        // Functional Values
        private const string TraceConfigCategory = "config";
        private const string NullString = "NULL";

        #endregion

        #region Members and Constructors

        // Used for general locking on the factory level
        private static readonly object _factoryLock = new object();
        // Setting up the lazy singleton pattern
        private static readonly Lazy<LoggerFactory> Instance = new Lazy<LoggerFactory>(() =>
        { return new LoggerFactory(); });

        /// <summary>
        /// The framework can be extended with "ConfigExtenders".  These are used to,
        /// well, extend the configuration.
        /// </summary>
        [ImportMany(typeof(ILoggingConfigExtender))]
        private IEnumerable<ILoggingConfigExtender> ConfigExtenders { get; set; }

        /// <summary>
        /// The single primary configuration used for all of the components
        /// of the framework
        /// </summary>
        private LoggingConfig LoggingConfig { get; set; }

        /// <summary>
        /// The log event dispatcher, or "dispatcher".
        /// The dispatcher is responsible for managing the targets,
        /// and for dispatching log events from the loggers to
        /// the various targets.
        /// </summary>
        private LogEventDispatcher LogEventDispatcher { get; set; }

        /// <summary>
        /// Whether or not the factory is initialized.  This is used
        /// to prevent loading the configuration multiple times,
        /// unless it is strictly intentional (I.E. a force tag on
        /// the Initialize function)
        /// </summary>
        private bool Initialized { get; set; }

        /// <summary>
        /// A cache of named loggers.  Loggers by name are only
        /// initialized once, allowing for developers to call the
        /// factory for a logger instance multiple times without
        /// provisioning more than a single logger multiple times
        /// </summary>
        private IDictionary<string, LoggerBase> NamedLoggers { get; set; }

        /// <summary>
        /// Used for debugging purposes, an exception handler is used
        /// to intercept exceptions occuring inside of the logging framework.
        /// If not supplied, exceptions within the framework are sent
        /// to Trace.
        /// </summary>
        private Action<Exception, string> ExceptionHandler { get; set; }

        /// <summary>
        /// The default constructor for the factory.  Is marked private
        /// for the singleton pattern.
        /// </summary>
        private LoggerFactory()
        {
            NamedLoggers = new Dictionary<string, LoggerBase>();
        }

        #endregion

        #region Initialization Methods

        /// <summary>
        /// Initializes the logging framework using a provided ILoggingConfigBuilder
        /// </summary>
        /// <param name="configBuilder">The config builder to use to initialize the logging framework</param>
        /// <param name="dispatcher">An alternate dispatcher to use for the logging framework</param>
        /// <param name="exceptionHandler">An exception handler to use for debugging purposes</param>
        /// <param name="force">Whether or not to force the re-configuration if the framework has alredy been intiaizlied</param>
        public static void Initialize(ILoggingConfigBuilder configBuilder, LogEventDispatcher dispatcher = null, Action<Exception, string> exceptionHandler = null, bool force = true)
        {
            try
            {
                var config = configBuilder.Build();
                Initialize(config, dispatcher, exceptionHandler, force);
            }
            catch
            {
                // Failed to configure using the provided configuration builder
                Trace.WriteLine(String.Format(FailedConfigBuilderMessage, configBuilder == null ? NullString : configBuilder.GetType().FullName));
                Initialize(config: null, dispatcher: dispatcher, force: force);
            }
        }

        /// <summary>
        /// Initializes the logging framework using a file
        /// </summary>
        /// <param name="configFile">The path to the JSON configuration file to use</param>
        /// <param name="dispatcher">An alternate dispatcher to use for the logging framework</param>
        /// <param name="exceptionHandler">An exception handler to use for debugging purposes</param>
        /// <param name="force">Whether or not to force the re-configuration if the framework has alredy been intiaizlied</param>
        public static void Initialize(string configFile, LogEventDispatcher dispatcher = null, Action<Exception, string> exceptionHandler = null, bool force = true)
        {
            lock (_factoryLock)
            {
                if (force || !Instance.Value.Initialized)
                {
                    if (Instance.Value.Initialized)
                        Instance.Value.LoggingConfig.Shutdown();

                    try
                    {
                        Instance.Value.LoggingConfig = new LoggingConfig(configFile);
                    }
                    catch (Exception e)
                    {
                        // Report the exception
                        if (exceptionHandler != null)
                        {
                            exceptionHandler(e, ConfigurationFailedUsingDefaultsMessage);
                        }
                        else
                        {
                            Trace.WriteLine(e, TraceConfigCategory);                        
                        }

                        // Load the default config to get us off the ground!!
                        Instance.Value.LoggingConfig = new LoggingConfig();
                    }

                    if (Instance.Value.LogEventDispatcher == null)
                    {
                        Instance.Value.LogEventDispatcher = new LogEventDispatcher(Instance.Value.LoggingConfig, Instance.Value.ExceptionHandler);
                    }
                    else
                    {
                        Instance.Value.LogEventDispatcher.NewConfig(Instance.Value.LoggingConfig);
                    }

                    Instance.Value.LoggingConfig.RegisterObserver(Instance.Value.LogEventDispatcher);

                    Instance.Value.Initialized = true;
                }
            }
        }

        /// <summary><commit_msg>Bugfix to cause the configuration extenders to fire every time the factory is configured, or reconfigured.<commit_after>﻿/*
 * Author: <NAME> (<EMAIL>)
 * Date: 10/5/2014
 * License: MIT (http://opensource.org/licenses/MIT)
 * GitHub: https://github.com/ivanpointer/NuLog
 */
using NuLog.Configuration;
using NuLog.Dispatch;
using NuLog.Loggers;
using NuLog.MetaData;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;

namespace NuLog
{
    /// <summary>
    /// The factory for building and providing all of the pieces for the NuLogging framework.
    /// The factory ties the four other major pieces of the framework together:
    ///   configuration, the dispatcher, the targets and loggers
    /// </summary>
    public class LoggerFactory
    {
        #region Constants

        // Messages
        private const string FailedConfigBuilderMessage = "Failed to configure using the provided config builder {0}";
        private const string ConfigurationFailedUsingDefaultsMessage = "Configuration failed, using default configuration";
        private const string ConfigLoadFailureMessage = "Failure loading config: {0}:\r\n{1}";
        private const string ShutdownDispatcherFailureMessage = "Failed to shutdown the existing dispatcher due to error {0}";
        private const string ConfigExtendersLoadFailedMessage = "Failure loading configuration extenders: {1}:\r\n{2}";

        // Functional Values
        private const string TraceConfigCategory = "config";
        private const string NullString = "NULL";

        #endregion

        #region Members and Constructors

        // Used for general locking on the factory level
        private static readonly object _factoryLock = new object();
        // Setting up the lazy singleton pattern
        private static readonly Lazy<LoggerFactory> Instance = new Lazy<LoggerFactory>(() =>
        { return new LoggerFactory(); });

        /// <summary>
        /// The framework can be extended with "ConfigExtenders".  These are used to,
        /// well, extend the configuration.
        /// </summary>
        [ImportMany(typeof(ILoggingConfigExtender))]
        private IEnumerable<ILoggingConfigExtender> ConfigExtenders { get; set; }

        /// <summary>
        /// The single primary configuration used for all of the components
        /// of the framework
        /// </summary>
        private LoggingConfig LoggingConfig { get; set; }

        /// <summary>
        /// The log event dispatcher, or "dispatcher".
        /// The dispatcher is responsible for managing the targets,
        /// and for dispatching log events from the loggers to
        /// the various targets.
        /// </summary>
        private LogEventDispatcher LogEventDispatcher { get; set; }

        /// <summary>
        /// Whether or not the factory is initialized.  This is used
        /// to prevent loading the configuration multiple times,
        /// unless it is strictly intentional (I.E. a force tag on
        /// the Initialize function)
        /// </summary>
        private bool Initialized { get; set; }

        /// <summary>
        /// A cache of named loggers.  Loggers by name are only
        /// initialized once, allowing for developers to call the
        /// factory for a logger instance multiple times without
        /// provisioning more than a single logger multiple times
        /// </summary>
        private IDictionary<string, LoggerBase> NamedLoggers { get; set; }

        /// <summary>
        /// Used for debugging purposes, an exception handler is used
        /// to intercept exceptions occuring inside of the logging framework.
        /// If not supplied, exceptions within the framework are sent
        /// to Trace.
        /// </summary>
        private Action<Exception, string> ExceptionHandler { get; set; }

        /// <summary>
        /// The default constructor for the factory.  Is marked private
        /// for the singleton pattern.
        /// </summary>
        private LoggerFactory()
        {
            NamedLoggers = new Dictionary<string, LoggerBase>();
        }

        #endregion

        #region Initialization Methods

        /// <summary>
        /// Initializes the logging framework using a provided ILoggingConfigBuilder
        /// </summary>
        /// <param name="configBuilder">The config builder to use to initialize the logging framework</param>
        /// <param name="dispatcher">An alternate dispatcher to use for the logging framework</param>
        /// <param name="exceptionHandler">An exception handler to use for debugging purposes</param>
        /// <param name="force">Whether or not to force the re-configuration if the framework has alredy been intiaizlied</param>
        public static void Initialize(ILoggingConfigBuilder configBuilder, LogEventDispatcher dispatcher = null, Action<Exception, string> exceptionHandler = null, bool force = true)
        {
            try
            {
                var config = configBuilder.Build();
                Initialize(config, dispatcher, exceptionHandler, force);
            }
            catch
            {
                // Failed to configure using the provided configuration builder
                Trace.WriteLine(String.Format(FailedConfigBuilderMessage, configBuilder == null ? NullString : configBuilder.GetType().FullName));
                Initialize(config: null, dispatcher: dispatcher, force: force);
            }
        }

        /// <summary>
        /// Initializes the logging framework using a file
        /// </summary>
        /// <param name="configFile">The path to the JSON configuration file to use</param>
        /// <param name="dispatcher">An alternate dispatcher to use for the logging framework</param>
        /// <param name="exceptionHandler">An exception handler to use for debugging purposes</param>
        /// <param name="force">Whether or not to force the re-configuration if the framework has alredy been intiaizlied</param>
        public static void Initialize(string configFile, LogEventDispatcher dispatcher = null, Action<Exception, string> exceptionHandler = null, bool force = true)
        {
            lock (_factoryLock)
            {
                if (force || !Instance.Value.Initialized)
                {
                    if (Instance.Value.Initialized)
                        Instance.Value.LoggingConfig.Shutdown();

                    try
                    {
                        Instance.Value.LoggingConfig = new LoggingConfig(configFile);
                    }
                    catch (Exception e)
                    {
                        // Report the exception
                        if (exceptionHandler != null)
                        {
                            exceptionHandler(e, ConfigurationFailedUsingDefaultsMessage);
                        }
                        else
                        {
                            Trace.WriteLine(e, TraceConfigCategory);                        
                        }

                        // Load the default config to get us off the ground!!
                        Instance.Value.LoggingConfig = new LoggingConfig();
                    }

                    Initialize(Instance.Value.LoggingConfig, dispatcher, exceptionHandler, force);
                }
            }
        }

        /// <summary>
{'fraction_non_alphanumeric': 0.08668430904279352, 'fraction_numerical': 0.0016781772413347964, 'mean_word_length': 1.9534883720930232, 'pattern_counts': {'":': 0, '<': 97, '<?xml version=': 0, '>': 99, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3075769', 'n_tokens_mistral': 3530, 'n_tokens_neox': 3377, 'n_words': 1234}