2016-07-27 8 views
2

私はASP.NET Coreを勉強中です。ファイルシステムでのロギングを成功させましたが、データベースソリューションでロギング機能を実装する方法はどうですか?どのように私の 'LoggerDatabaseProvider'にEFのコンテキストを渡すには、まだそれらの2つのデカップル?以下のコードは、いくつかの先行質問をクリアする必要があります。ASP.NET Core ILoggerProvider for database

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(); 
    } 

は、テキストファイルと私の実装に成功ロガーです:非常によく似た実装について

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() 
      { 
      } 
     } 
    } 
} 

しかし、どのように、しかし、データベースソリューション?私のWorldContextは、EFによってデータベースとの通信に利用されていますが、もしそれが簡単なら私のリポジトリを利用することができます。コンテキストとリポジトリのデカップルを「LoggerDatabaseProvider」から取り除きたいので、他のプロジェクトで利用することができます。

+0

そのほぼすべてのロギングフレームワークDBのログプロバイダーを使用しないのはなぜありますか? – Brad

答えて

3

仕様ごとに、誰かが同様のソリューションを探す場合に備えて、実装した方法をここに示します。

'スタートアップ。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(); 
} 

カスタム '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() 
      { 
      } 
     } 
    } 
} 

カスタム '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() 
      { 
      } 
     } 
    } 
} 
+0

うわー、私はあなたが 'Configure'に別のパラメータを追加するだけで、DIがあなたに値を提供するかもしれないということを知りませんでした。 – t3chb0t

+0

Dependency Injectionを使用すると、Configureに任意の数のInterfaceパラメータを指定することができ、それらはConfigureServicesで設定された対応するクラスに置き換えられます – lucas

1

ログを表示するためのデータベースとウェブUIを実装しました。

私の現在の動作例ではEFを使用できますが、NoDb(ファイルシステムストレージ)を使用できます。他のDBプラットフォーム用のgithubリポジトリには、ILogRepositoryの他の実装がありますが、それらは現在最新ではなく、動作していないので、EFとNoDbのものだけが現在動作しています。

あなたが見ることができる私のDBLoggerProviderが、私はそれのコンストラクタに私ILogRepositoryの実装に渡す必要が

ILogRepositoryの私のEFの実装はhere

ロギング用のEFを使用するためのいくつかの重要なポイントは、通常、DbContextがあります要求ごとに有効範囲が設定されていますが、要求ごとに複数回ロギングが発生する可能性があります。また、リクエスト中にコンテキストで複数回SaveChangesを呼び出すとエラーが発生する可能性があります。私は私がログに追加するたびに

using (var context = new LoggingDbContext(dbContextOptions)) 
{ 
    context.Add(logItem); 
    context.SaveChanges(); 
} 

もEFは、多くの過渡DbContextまで新しいログオンすることができますので、私がログに追加するために注入DbContextを使用していないこの問題を解決するには、代わりに私がDbContextOptionsを注入しますログに追加することでより多くのログ項目が生成されないように、フィルタリングする必要があります。これはログの無限ループになります。これは、あなたが他の場所からEFによって記録されたものを見ることに興味があって、複数のロガーを使用する以外の解決策を見つけられなかった場合、つまりコンソールログを使用してEFによって記録されたものを見るが、 EFログにEF関連のものを記録させない。

このロガーを使用するアプリケーションを表示する場合は、特にstartup codeが表示され、ILogRepositoryの実装がConfigureServicesに組み込まれている必要がありますので、Configureのメソッドシグネチャに追加する必要があります。その方法に注入してDbLoggerProviderに渡すことができます

私のプロジェクトでは、DbLogProviderからリポジトリを切り離していますので、独自のログ実装を見たり、それはオープンソースであり、使用の準備ができています。

+0

ILogRepositoryの私のEF実装がここにあります - 壊れたリンク –