Реализовать log4net в ядре webpi asp.net - PullRequest
0 голосов
/ 18 октября 2019

У меня есть основной веб-интерфейс asp.net. На данный момент я использую ILogger для регистрации сообщений. Но в ILogger нет логического уровня Fatal. Существует критический уровень, но нашей команде требуется слово Fatal вместо критического слова. Есть ли способ настроить работу, которая будет напечатана в журналах?

Если нет, я хочу заменить ILogger на log4Net с фатальным уровнем. Так вот, что я сделал, но почему-то это не работает. У меня есть многоуровневая архитектура: WebApplication1 , WebApplication1.Helper . Все это разные проекты с решением.

В WebApplication1 : Я добавил ссылку на Microsoft.Extensions.Logging.Log4Net.AspNetCore.
В startup.cs

public void ConfigureServices(IServiceCollection apiServices)
    {
        var provider = apiServices.BuildServiceProvider();

        var factory = new LoggerFactory()
               .AddConsole().AddLog4Net().AddApplicationInsights(provider, LogLevel.Information);

        apiServices.AddSingleton(factory);
        apiServices.AddLogging();
        apiServices.AddMvc();
        apiServices.AddOptions();
    }

HomeController.cs

[Route("api/[controller]")]
    [ApiController]
    public class HomeController : Controller
    {
        private readonly ILog4NetHelper _logHelper = new Log4NetHelper();
        [HttpGet]
        public virtual IActionResult GetData()
        {
            try
            {
                _logHelper.Log4NetMessage("Info", "Start GetData");
                return new OkObjectResult("Your in Home Controller");
            }
            catch (Exception ex)
            {
                _logHelper.Log4NetMessage("Error", "Exception in GetData" + ex.Message);
                throw;
            }
        }
    }

Проект WebApplication1.Helper
А в проекте WebApplication1.Helper я добавил интерфейс ILog4NetHelper и класс, которыйреализует этот интерфейс Log4NetHelper. Также я добавил конфигурационный файл log4Net.

  public class Log4NetHelper : ILog4NetHelper
    {
        readonly ILog _log =log4net.LogManager.GetLogger(typeof(Log4NetHelper));
        public void Log4NetMessage(string type,string message)
        {
            string logMessage = message;    
            switch (type)
            {
                case "Info":
                    _log.Info(logMessage);
                    break;
                case "Error":
                    _log.Error(logMessage);
                    break;
                case "Fatal":
                    _log.Fatal(logMessage);
                    break;
                default:
                    _log.Info(logMessage);
                    break;
            }
        }
    }

Когда я размещаю это приложение и запускаю его, оно дает мне 500 внутренних ошибок сервера. Это сообщение об ошибке:

InvalidOperationException: невозможно разрешить службу для типа «WebApplication1.Helper.Log4NetHelper» при попытке активировать «WebApplication1.Helper.Log4NetHelper». Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateArgumentCallSites (Тип serviceType, Тип instanceType типа, CallSiteChain, параметры CallSiteChain, ParameterInfo [], bool throwIfCallSiteNotFound)

* *1035* *1035* * 1036

Ответы [ 4 ]

1 голос
/ 20 октября 2019

Встроенное ведение журналов ASP.Net Core было попыткой Microsoft вести журналы Microsoft, используя инъекцию зависимостей. Он следует основным принципам и принципам подхода Log4Net (который был стандартизирован среди .Net, Java и Javascript, среди прочих). Таким образом, эти два подхода не полностью противоречат друг другу.

Однако в данном конкретном случае реализация, по-видимому, фактически вступает в противоречие с намерением обоих подходов к ведению журнала.

Log4Net разделяет два акта запись и запись журнала . Первый выполняется через интерфейс ILog . Вторая выполняется через один из Appenders .

Аналогично, в ASP.net Core API используется ILogger и один или несколько провайдеров отправлять сообщения журнала.

Поскольку мне удобнее работать с log4net, а также я не вижу особого смысла в добавлении регистраторов посредством внедрения зависимостей в КАЖДЫЙ КЛАСС, я использовал вместо log4net подход LogManager.GetLogger(typeof(MyClass)), а неделать это через Microsoft DI. Мои appenders также работают через log4net. Таким образом, моя реализация была сосредоточена на переводе результатов регистрации Microsoft в формат log4net, который, по-видимому, является тем, чего хочет ваша команда, но противоположным тому, что вы здесь делаете. Мой подход был основан на этой статье . Код, который я использовал ниже:

Замечания по реализации:

Я настроил пользовательский appender через log4net, который записывает мои журналы в базу данных журналов (часто используемые базы данных). для этого есть локи и / или эластичный поиск).

В методе Configure() на startup.cs вам понадобится следующая строка (обратите внимание, что я создаю экземпляр customAppender в ConfigureServices, а затем добавляю его в DI, но вы несделать это нужно следующим образом):

loggerFactory.AddLog4Net(_serverConfig.LoggingSettings, customAppender);

Также необходимо иметь следующее в ConfigureServices() (не уверен почему, но, похоже, он гарантирует, что включится обычное ведение журнала ядра .net).

services.AddLogging(config => {
    config.AddDebug();
    config.AddConsole();
});

Log4NetLogger.cs

/// <summary>
/// Writes ASP.net core logs out to the log4net system.
/// </summary>
public class Log4NetLogger : ILogger
{
    private readonly ILog _logger;
    public Log4NetLogger(string name)
    {
        _logger = LogManager.GetLogger(typeof(Log4NetProvider).Assembly, name);
    }

    public IDisposable BeginScope<TState>(TState state)
    {
        return null;
    }

    public bool IsEnabled(LogLevel logLevel)
    {
        switch (logLevel) {
            case LogLevel.Critical:
                return _logger.IsFatalEnabled;
            case LogLevel.Debug:
            case LogLevel.Trace:
                return _logger.IsDebugEnabled;
            case LogLevel.Error:
                return _logger.IsErrorEnabled;
            case LogLevel.Information:
                return _logger.IsInfoEnabled;
            case LogLevel.Warning:
                return _logger.IsWarnEnabled;
            default:
                throw new ArgumentOutOfRangeException(nameof(logLevel));
        }
    }

    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state,
        Exception exception, Func<TState, Exception, string> formatter)
    {
        if (!this.IsEnabled(logLevel)) {
            return;
        }

        if (formatter == null) {
            throw new ArgumentNullException(nameof(formatter));
        }
        string message = null;
        if (null != formatter) {
            message = formatter(state, exception);
        }
        if (!string.IsNullOrEmpty(message) || exception != null) {
            switch (logLevel) {
                case LogLevel.Critical:
                    _logger.Fatal(message);
                    break;
                case LogLevel.Debug:
                case LogLevel.Trace:
                    _logger.Debug(message);
                    break;
                case LogLevel.Error:
                    _logger.Error(message);
                    break;
                case LogLevel.Information:
                    _logger.Info(message);
                    break;
                case LogLevel.Warning:
                    _logger.Warn(message);
                    break;
                default:
                    _logger.Warn($"Encountered unknown log level {logLevel}, writing out as Info.");
                    _logger.Info(message, exception);
                    break;
            }
        }
    }

Log4NetProvider.cs

/// <summary>
/// Returns new log4net loggers when called by the ASP.net core logging framework
/// </summary>
public class Log4NetProvider : ILoggerProvider
{
    private readonly LoggingConfig _config;
    private readonly ConcurrentDictionary<string, Log4NetLogger> _loggers =
        new ConcurrentDictionary<string, Log4NetLogger>();
    private readonly ILoggerRepository _repository =
        log4net.LogManager.CreateRepository(typeof(Log4NetProvider).Assembly, typeof(log4net.Repository.Hierarchy.Hierarchy));

    public Log4NetProvider(LoggingConfig config, MyCustomAppender otherAppender)
    {
        _config = config;
        BasicConfigurator.Configure(_repository, new ConsoleAppender(), otherAppender);

        LogManager.GetLogger(this.GetType()).Info("Logging initialized.");
    }

    public ILogger CreateLogger(string categoryName)
    {
        return _loggers.GetOrAdd(categoryName, this.CreateLoggerImplementation(categoryName));
    }

    public void Dispose()
    {
        _loggers.Clear();
    }

    private Log4NetLogger CreateLoggerImplementation(string name)
    {
        return new Log4NetLogger(name);
    }
}

Log4NetExtensions.cs

/// <summary>
/// A helper class for initializing Log4Net in the .NET core project.
/// </summary>
public static class Log4netExtensions
{
    public static ILoggerFactory AddLog4Net(this ILoggerFactory factory, LoggingConfig config, MyCustomAppender appender)
    {
        factory.AddProvider(new Log4NetProvider(config, appender));
        return factory;
    }
}
0 голосов
/ 12 ноября 2019

Система Microsoft.Extensions.Logging не содержит Фатальный уровень в качестве принятого LogLevel .

Но, если вас интересует обработка критических сообщений как фатального уровня log4net, начиная св v.2.2.5 имеется свойство в пакете nuget Microsoft.Extensions.Logging.Log4Net.AspNetCore , которое позволяет вам решить, является ли критический уровень управляемым как фатальный или нет.

Пожалуйста, просмотрите оригинальный выпуск , чтобы проверить, почему реализация была сделана такой, какая была.

0 голосов
/ 25 октября 2019

Я столкнулся с несколькими проблемами с Log4Net несколько лет назад - я точно не помню, что, с тех пор, как я переключился на свою собственную реализацию для журнала, написать свою собственную несложно. моя реализация скопирована ниже.

Когда мне пришлось изменить класс журнала, это было кошмаром, поскольку мне пришлось заменить все его использование новой реализацией.

Есть два преимущества пользовательского класса.

A. Это будет соответствовать требованию вашего приложения. B. Он действует как оболочка для любой базовой структуры ведения журналов, которую вы можете использовать. Так что теперь, даже если мне придется изменить ведение журнала в будущем, мне просто нужно изменить внутреннюю реализацию этого пользовательского класса.

С момента написания этой оболочки я перешел с log4net на расширения журнала Microsoft и теперь на 100% мойсобственная реализация.

using System;
using System.Text;
using System.IO;

namespace BF
{
    //This is a custom publisher class use to publish the exception details 
    //into log file
    public class LogPublisher 
    {
        private static object _lock;

        static LogPublisher()
        {
            _lock = new object();
        }

        //Constructor
        public LogPublisher()
        {
        }

        //Method to publish the exception details into log file
        public static void Debug(string message)
        {
            if (ClientConfigHandler.Config.IsDebugMode())
            {
                Exception eMsg = new Exception(message);

                Publish(eMsg, "#DEBUG");
            }
        }

        public static void DebugBackgroundAction(string message)
        {
            if (ClientConfigHandler.Config.IsDebugMode())
            {
                Exception eMsg = new Exception(message);

                Publish(eMsg, "#DEBUG #BG");
            }
        }

        public static void BackgroundAction(string message)
        {
            Exception eMsg = new Exception(message);

            Publish(eMsg, "#BG");
        }

        public static void Publish(string message)
        {
            Exception eMsg = new Exception(message);

            Publish(eMsg, "");
        }

        public static void Publish(Exception fException)
        {
            Publish(fException, "");
        }


        public static void Publish(Exception fException, string prefix)
        {
            if (fException == null) return;

            // Load Config values if they are provided.
            string m_LogName = ResourceConfig.LogFileName;

            // Create StringBuilder to maintain publishing information.
            StringBuilder strInfo = new StringBuilder();

            // Record required content of the AdditionalInfo collection.
            strInfo.AppendFormat("{0}**T {1} {2} ", Environment.NewLine, CommonConversions.CurrentTime.ToString(CommonConversions.DATE_TIME_FORMAT_LOG), prefix);

            // Append the exception message and stack trace
            strInfo.Append(BuildExceptionLog(fException, false));

            try
            {
                lock (_lock)
                {
                    FileStream fs = File.Open(m_LogName, FileMode.Append, FileAccess.Write);
                    StreamWriter sw = new StreamWriter(fs);
                    sw.Write(strInfo.ToString());
                    sw.Close();
                    fs.Close();
                }
            }
            catch
            {
                //ignore log error
            }
        }

        private static string BuildExceptionLog(Exception fException, bool isInnerExp)
        {
            StringBuilder strInfo = new StringBuilder();

            if (fException != null)
            {
                string msgType;

                if (isInnerExp)
                {
                    msgType = "#IN-ERR";
                }
                else if (fException.StackTrace == null)
                {
                    msgType = "#INF";
                }
                else
                {
                    msgType = "#ERR";
                }

                strInfo.AppendFormat("{0}: {1}", msgType, fException.Message.ToString());

                if (fException.StackTrace != null)
                {
                    strInfo.AppendFormat("{0}#TRACE: {1}", Environment.NewLine, fException.StackTrace);
                }

                if (fException.InnerException != null)
                {
                    strInfo.AppendFormat("{0}{1}", Environment.NewLine, BuildExceptionLog(fException.InnerException, true));
                }
            }

            return strInfo.ToString();
        }
    }
}
0 голосов
/ 19 октября 2019

Я вижу одну проблему в вашем коде. Ваш класс Log4NetHelper требует в конструкторе экземпляр Log4NetHelper . Поэтому он не может создать Log4NetHelper. Почему вы передаете Log4NetHelper в конструктор, это пахнет. Вы должны предоставить конструктору по умолчанию или конструктору параметры, которые зарегистрированы в службе DI.

Пожалуйста, попробуйте добавить конструктор без параметров и проверьте, работает ли он, если нет, проверьте исключение и / или сообщение об ошибке.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...