[英]Implement log4net in asp.net core webapi
我有一个 asp.net 核心 web api。 截至目前,我正在使用 ILogger 记录消息。 但是 ILogger 中没有致命的日志级别。 有关键级别,但我们的团队需要致命词而不是关键词。有什么办法可以调整打印到日志的工作吗?
如果没有,我想用其中具有致命级别的 log4Net 替换 ILogger。所以这就是我所做的,但不知何故它不起作用。 我有多层架构: WebApplication1 , WebApplication1.Helper 。 所有这些都是解决方案中的不同项目。
在WebApplication1中:我添加了 Microsoft.Extensions.Logging.Log4Net.AspNetCore 参考。
在启动.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();
}
家庭控制器.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
和 class ,它实现了这个接口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,类型 implementationType,CallSiteChain callSiteChain,ParameterInfo[] 参数,bool throwIfCallSiteNotFound)
我该如何解决这个问题?
ASP.Net Core 内置日志记录是 Microsoft 在以注入依赖项的方式进行日志记录方面的尝试。 它遵循 Log4Net 方法的基本原则和原则(已在.Net、Java 和 Javascript 等标准化)。 因此,这两种方法并不完全相互矛盾。
但是,在这种特殊情况下,实现似乎实际上与两种日志记录方法的意图相冲突。
Log4Net 将记录和写入日志 output两个行为分开。 第一个是通过ILog接口完成的。 第二个是通过其中一个Appenders完成的。
同样,ASP.net Core API 使用ILogger和一个或多个Provider来发出日志消息。
由于我对 log4net 更满意,而且在每个 CLASS 中通过依赖注入添加记录器也没有多大意义,我使用 log4net 的方法LogManager.GetLogger(typeof(MyClass))
而不是通过 Microsoft DI . 我的附加程序也通过 log4net 运行。 因此,我的实施侧重于将 Microsoft 日志输出转换为 log4net 格式,这似乎是您的团队想要的,但与您在这里所做的相反。 我的方法是基于这篇文章。 我使用的代码如下。
实施说明:
我通过 log4net 设置了一个自定义 appender,它将我的日志写入日志数据库(常用的数据库是 loki 和/或 elasticsearch)。
在startup.cs
上的Configure()
方法中,您需要具有以下行(请注意,我在ConfigureServices
中实例化了 customAppender,然后将其添加到 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;
}
}
我没有完全理解你正在混合的东西。
尝试使用 Log4Helper class 的以下代码。 我不确定,但检查它是否有效。
public class Log4NetHelper : ILog4NetHelper
{
readonly ILog _log;
public Log4NetHelper()
{
_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;
}
}
}
几年前我在使用 Log4Net 时遇到了多个问题——我不记得具体是什么,从那以后我切换到我自己的 log 实现,编写自己的实现并不复杂。 我的实现复制如下。
当我不得不更改日志 class 时,这是一场噩梦,因为我不得不用新的实现替换它的所有用法。
定制 class 有 2 个好处。
A. 它将符合您的应用程序的要求。 B. 它充当您可能使用的任何底层日志框架的包装器。 所以现在即使我将来必须更改日志记录,我只需要更改此自定义 class 的内部实现。
自从编写了这个包装器,我已经从 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();
}
}
}
Microsoft.Extensions.Logging 系统不包含致命级别作为接受的 LogLevel 。
但是,如果您有兴趣将严重消息视为 log4net 的致命级别,从 v.2.2.5 开始, Microsoft.Extensions.Logging.Log4Net.AspNetCore nuget package 上有一个属性允许您决定是否为严重级别管理为致命与否。
请查看原始问题以检查为什么按原样完成实施。
我在您的代码中看到一个问题。 您的 class Log4NetHelper在构造函数中需要Log4NetHelper的实例。 所以它不能创建 Log4NetHelper。 为什么你在构造函数中传递Log4NetHelper ,它闻起来很臭。 您应该提供默认构造函数,或带有在 DI 服务中注册的参数的构造函数。
请尝试添加无参数构造函数并检查它是否工作,如果没有检查异常和/或错误消息。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.