用于数据库的ASP.NET Core ILoggerProvider

luc*_*cas 5 c# asp.net-core

我正在研究ASP.NET Core,我已经成功地使用文件系统实现了日志记录,但是如何使用数据库解决方案实现日志记录功能.如何将EF上下文传递给我的' LoggerDatabaseProvider',仍然将这两个解耦?下面的代码应该清除一些前期问题:

Startup.cs:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();
        services.AddEntityFramework()
            .AddEntityFrameworkSqlServer()
            .AddDbContext<WorldContext>();

        services.AddTransient<WorldContextSeedData>();


        services.AddScoped<IWorldRepository, WorldRepository>();
        //this one adds service with Dependency Injection that calls 'MyLogger' constructor with two parameters, instead the default one parametereless constructor. 
        services.AddScoped<ILogger, LoggerFileProvider.Logger>(provider => new LoggerFileProvider.Logger("CustomErrors", Startup.Configuration["Data:LogFilePath"]));
        //services.AddScoped<ILogger, LoggerDatabaseProvider.Logger>(provider => new LoggerDatabaseProvider.Logger("CustomErrors"));
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, WorldContextSeedData seeder)
    {
        string basePath = Startup.Configuration["Data:LogFilePath"];

        loggerFactory.AddProvider(new LoggerFileProvider(basePath));
        loggerFactory.AddProvider(new LoggerDatabaseProvider());

        app.UseStaticFiles();

        app.UseMvc(config =>
        {
            config.MapRoute(
                name: "Default",
                template: "{controller}/{action}/{id?}",
                defaults: new { controller = "App", action = "index" }
                );
        });

        seeder.EnsureSeedData();
    }
Run Code Online (Sandbox Code Playgroud)

这是我成功实现的带有文本文件的记录器:

public class LoggerFileProvider : ILoggerProvider
{
    private readonly string _logFilePath;

    public LoggerFileProvider(string logFilePath)
    {
        _logFilePath = logFilePath;
    }

    public ILogger CreateLogger(string categoryName)
    {
        return new Logger(categoryName, _logFilePath);
    }

    public void Dispose()
    {
    }

    public class Logger : ILogger
    {
        private readonly string _categoryName;
        private readonly string _path;

        public Logger(string categoryName, string logFilePath)
        {
            _path = logFilePath;
            _categoryName = categoryName;
        }

        public bool IsEnabled(LogLevel logLevel)
        {
            return true;
        }

        public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
        {
            try
            {
                RecordMsg(logLevel, eventId, state, exception, formatter);
            }
            catch (Exception ex)
            {
                //this is being used in case of error 'the process cannot access the file because it is being used by another process', could not find a better way to resolve the issue
                RecordMsg(logLevel, eventId, state, exception, formatter);
            }
        }

        private void RecordMsg<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
        {
            string msg = $"{logLevel} :: {_categoryName} :: {formatter(state, exception)} :: username :: {DateTime.Now}";

            using (var writer = File.AppendText(_path))
            {
                writer.WriteLine(msg);
            }
        }

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

        private class NoopDisposable : IDisposable
        {
            public void Dispose()
            {
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

但是如何实现非常相似,但是使用数据库解决方案呢?WorldContextEF利用我与数据库进行通信,但如果更容易,我可以使用我的存储库.我想保持上下文/存储库与LoggerDatabaseProvider我即将实现的' ' 分离,所以我可以在其他项目中使用它.

luc*_*cas 7

根据规范,这是我设法实现它的方式,以防万一有人会寻找类似的解决方案.

'Startup.cs':

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.AddEntityFramework()
        .AddEntityFrameworkSqlServer()
        .AddDbContext<WorldContext>();

    services.AddLogging();

    services.AddTransient<WorldContextSeedData>();

    services.AddScoped<IMailService, MailServiceDebug>();
    services.AddScoped<IWorldRepository, WorldRepository>();
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, WorldContextSeedData seeder, IWorldRepository worldRepo)
{
    string basePath = Startup.Configuration["Data:LogFilePath"];

    loggerFactory.AddProvider(new LoggerFileProvider(basePath));
    loggerFactory.AddProvider(new LoggerDatabaseProvider(worldRepo));

    app.UseStaticFiles();

    app.UseMvc(config =>
    {
        config.MapRoute(
            name: "Default",
            template: "{controller}/{action}/{id?}",
            defaults: new { controller = "App", action = "index" }
            );
    });

    seeder.EnsureSeedData();
}
Run Code Online (Sandbox Code Playgroud)

自定义'LoggerFileProvider.cs':

public class LoggerFileProvider : ILoggerProvider
{
    private readonly string _logFilePath;

    public LoggerFileProvider(string logFilePath)
    {
        _logFilePath = logFilePath;
    }

    public ILogger CreateLogger(string categoryName)
    {
        return new Logger(categoryName, _logFilePath);
    }

    public void Dispose()
    {
    }

    public class Logger : ILogger
    {
        private readonly string _categoryName;
        private readonly string _path;

        public Logger(string categoryName, string logFilePath)
        {
            _path = logFilePath;
            _categoryName = categoryName;
        }

        public bool IsEnabled(LogLevel logLevel)
        {
            return true;
        }

        public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
        {
            try
            {
                RecordMsg(logLevel, eventId, state, exception, formatter);
            }
            catch (Exception ex)
            {
                //this is being used in case of error 'the process cannot access the file because it is being used by another process', could not find a better way to resolve the issue
                RecordMsg(logLevel, eventId, state, exception, formatter);
            }
        }

        private void RecordMsg<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
        {
            string msg = $"{logLevel} :: {_categoryName} :: {formatter(state, exception)} :: username :: {DateTime.Now}";

            using (var writer = File.AppendText(_path))
            {
                writer.WriteLine(msg);
            }
        }

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

        private class NoopDisposable : IDisposable
        {
            public void Dispose()
            {
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

自定义'LoggerDatabaseProvider.cs':

public class LoggerDatabaseProvider : ILoggerProvider
{
    private IWorldRepository _repo;

    public LoggerDatabaseProvider(IWorldRepository repo)
    {
        _repo = repo;
    }

    public ILogger CreateLogger(string categoryName)
    {
        return new Logger(categoryName, _repo);
    }

    public void Dispose()
    {
    }

    public class Logger : ILogger
    {
        private readonly string _categoryName;
        private readonly IWorldRepository _repo;

        public Logger(string categoryName, IWorldRepository repo)
        {
            _repo = repo;
            _categoryName = categoryName;
        }

        public bool IsEnabled(LogLevel logLevel)
        {
            return true;
        }

        public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
        {
            if (logLevel == LogLevel.Critical || logLevel == LogLevel.Error || logLevel == LogLevel.Warning)
                RecordMsg(logLevel, eventId, state, exception, formatter);
        }

        private void RecordMsg<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
        {
            _repo.Log(new Log
            {
                LogLevel = logLevel.ToString(),
                CategoryName = _categoryName,
                Msg = formatter(state, exception),
                User = "username",
                Timestamp = DateTime.Now
            });
        }

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

        private class NoopDisposable : IDisposable
        {
            public void Dispose()
            {
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 哇,我不知道您只需向“Configure”添加另一个参数,DI 就会为您提供该值。 (2认同)