2011-04-22 59 views
3

嘿有人,這裏有一個小問題,我試圖包裹我的頭。nHibernate與Log4Net日誌記錄,線程會話問題

我現在開始與nHibernate,這樣我必須由於工作要求,並且有點卡住nHibernate的會話和多個線程。那麼我想在這裏完成的任務是讓Log4Net將所有東西都記錄到數據庫中,包括nHibernate的調試/錯誤等。

所以我做的是創建一個非常簡單的Log4Net:AppenderSkeleton類,當我需要它時會完全啓動。我碰到的一個初始問題是,當我使用GetCurrentSession時,很明顯,因爲Log4Net運行在一個單獨的線程上,所以它在初始線程的會話中出錯。所以我想我必須爲Log4Net AppenderSkeleton類創建一個新的nHiberante會話。代碼如下:

public class Custom : AppenderSkeleton 
{ 
    protected override void Append(LoggingEvent loggingEvent) 
    { 
     if (loggingEvent != null) 
     { 
      using (ISession session = NHibernateHelper.OpenSession()) 
      { 
       using (ITransaction tran = session.BeginTransaction()) 
       { 
        Log data = new Log 
        { 
         Date = loggingEvent.TimeStamp, 
         Level = loggingEvent.Level.ToString(), 
         Logger = loggingEvent.LoggerName, 
         Thread = loggingEvent.ThreadName, 
         Message = loggingEvent.MessageObject.ToString() 
        }; 

        if (loggingEvent.ExceptionObject != null) 
        { 
         data.Exception = loggingEvent.ExceptionObject.ToString(); 
        } 

        session.Save(data); 
        tran.Commit(); 
       } 
      } 
     } 
    } 

足夠簡單的想法真的,而這是在它的基本形式,現在,我將有更多的錯誤檢查信息等,但現在的問題是,雖然這工作完全是創建多個會話。也就是說,它創建一個新的會話記錄每個錯誤,因爲我不能使用GetCurrentSession,因爲這會得到調用會話(主程序流)。我確信有一種方法可以爲Log4Net的線程創建全局會話,但我不確定。請記住,我已經使用了下面的Global.asax綁定一個Session到INTIAL線程(的Application_BeginRequest):

ISession session = NHibernateHelper.OpenSession(); 

CurrentSessionContext.Bind(session); 

而對於那些會問,我的助手的內容是以下(這是在DLL):

public static class NHibernateHelper 
{ 
    private static Configuration _nHibernateConfig; 
    private static ISessionFactory _nHibernateSessionFactory; 

    private static ISessionFactory BuildNHibernateSessionFactory 
    { 
     get 
     { 
      if (_nHibernateSessionFactory == null) 
      { 
       if (_nHibernateConfig == null) 
       { 
        BuildSessionFactory(); 
       } 

       _nHibernateSessionFactory = _nHibernateConfig.BuildSessionFactory(); 
      } 

      return _nHibernateSessionFactory; 
     } 
    } 

    private static Configuration BuildNHibernateConfig 
    { 
     get 
     { 
      if (_nHibernateConfig == null) 
      { 
       _nHibernateConfig = new ConfigurationBuilder().Build(); 
      } 

      return _nHibernateConfig; 
     } 
    } 

    public static Configuration nHibernateConfig 
    { 
     get 
     { 
      return _nHibernateConfig; 
     } 
    } 

    public static ISessionFactory nHibernateSessionFactory 
    { 
     get 
     { 
      return _nHibernateSessionFactory; 
     } 
    } 

    public static Configuration BuildConfiguration() 
    { 
     return BuildNHibernateConfig; 
    } 

    public static ISessionFactory BuildSessionFactory() 
    { 
     return BuildNHibernateSessionFactory; 
    } 

    public static ISession OpenSession() 
    { 
     return _nHibernateSessionFactory.OpenSession(); 
    } 

    public static ISession GetCurrentSession() 
    { 
     try 
     { 
      return _nHibernateSessionFactory.GetCurrentSession(); 
     } 
     catch (HibernateException ex) 
     { 
      if(ex.Message == "No session bound to the current context") 
      { 
       // See if we can bind a session before complete failure 
       return _nHibernateSessionFactory.OpenSession(); 
      } 
      else 
      { 
       throw; 
      } 
     } 
     catch (Exception ex) 
     { 
      throw; 
     } 
    } 
} 

我知道我可以在log4net的使用ADO附加目的地,但我希望使用NHibernate的直接將數據添加到數據庫中。原因是當nHibernate已經在工作時,我不希望混淆連接字符串等。

一如既往,任何幫助總是讚賞。

- 編輯: -

因此,基於我一直initialy說,我修改了我的自定義log4net的記錄代碼。下面有兩個版本。我的問題,最好還是有更好的辦法?

  • 首先,根據NHibernate的教授,僅創建兩會 - 該INTIAL會議是主程序流程爲目的,第二個我log4net的記錄器的代碼。然而,這在第二次會議上有數百個入口,並且抱怨太多入口和對數據庫的許多呼叫。

  • 第二,nHibernate prof會顯示許多會話,與主程序流的記錄器+1調用一樣多的會話。然而在nHprof的任何地方都沒有投訴。雖然我有這樣的感覺,那麼多次會議會讓人皺眉頭,或者任務太多。

反正代碼:

碼1 -

protected override void Append(LoggingEvent loggingEvent) 
    { 
     if (!System.Web.HttpContext.Current.Items.Contains("Log4Net nHibernate Session")) 
     { 
      System.Web.HttpContext.Current.Items.Add("Log4Net nHibernate Session", NHibernateHelper.OpenStatelessSession()); 
     } 

     IStatelessSession statelessSession = System.Web.HttpContext.Current.Items["Log4Net nHibernate Session"] as IStatelessSession; 

     if (statelessSession != null && loggingEvent != null) 
     { 
      using (ITransaction tran = statelessSession.BeginTransaction()) 
      { 
       Log data = new Log 
        { 
         Id = Guid.NewGuid(), 
         Date = loggingEvent.TimeStamp, 
         Level = loggingEvent.Level.ToString(), 
         Logger = loggingEvent.LoggerName, 
         Thread = loggingEvent.ThreadName, 
         Message = loggingEvent.MessageObject.ToString() 
        }; 

       if (loggingEvent.ExceptionObject != null) 
       { 
        data.Exception = loggingEvent.ExceptionObject.ToString(); 
       } 

       statelessSession.Insert(data); 
       tran.Commit(); 
      } 
     } 
    } 

代碼2 -

protected override void Append(LoggingEvent loggingEvent) 
    { 
     if (loggingEvent != null) 
     { 
      using (IStatelessSession statelessSession = NHibernateHelper.OpenStatelessSession()) 
      using (ITransaction tran = statelessSession.BeginTransaction()) 
      { 
       Log data = new Log 
       { 
        Id = Guid.NewGuid(), 
        Date = loggingEvent.TimeStamp, 
        Level = loggingEvent.Level.ToString(), 
        Logger = loggingEvent.LoggerName, 
        Thread = loggingEvent.ThreadName, 
        Message = loggingEvent.MessageObject.ToString() 
       }; 

       if (loggingEvent.ExceptionObject != null) 
       { 
        data.Exception = loggingEvent.ExceptionObject.ToString(); 
       } 

       statelessSession.Insert(data); 
       tran.Commit(); 
      } 
     } 
    } 
+0

如果您使用NHibernate來記錄NHibernate調試消息不會進入無限循環並融化您的計算機? – dotjoe 2011-04-22 17:27:41

+0

我想你會檢查重複或重播。在任何情況下,到目前爲止,我的應用程序的啓動,這僅僅是德基礎,記錄從NHibernate的大約300調試消息並沒有循環:) – Anthony 2011-04-22 18:08:34

+0

StatelessSession是要走的路。你不會有任何問題。 – 2011-04-23 01:19:39

回答

1

你是對有關創建一個新的會話。你絕對不希望跨線程共享同一個會話。在你的日誌記錄實例中,我甚至會說使用IStatelessSession。另外會話應該相當輕量級,所以我不會擔心每次記錄語句時都會創建新的會話。

+0

感謝您的回答,但我很好奇。如果我爲每個「記錄項目」創建一個新的會話,這是不是每次都創建一個新的數據庫連接。因此,如果我在進程結束時記錄了100個項目,那麼我將至少有100個到數據庫的單獨連接? – Anthony 2011-04-22 16:12:10

+2

ADO.NET使用連接池。 http://msdn.microsoft.com/en-us/library/8xx3tyca.aspx – rbellamy 2011-04-22 16:52:53

+0

所以經過很多很多,不同的測試ADO池是肯定的工作。雖然我仍然不確定哪個是進行此測井工作的最佳方法。我注意到,如果一個新線程(例如Log4Net)觸發nHibernate,它會創建一個新的連接。因此,在IIS Express的本地測試中,運行到啓動程序流程結束時有兩個連接到數據庫。我可以得到這個只有一個連接? – Anthony 2011-04-23 00:42:13

1

NHibernate的已經使用log4net的內部,所以你只需要啓用記錄器,並使用AdoNetAppender的日誌發送到您的數據庫。

<log4net> 
    <appender name="AdoNetAppender" type="log4net.Appender.AdoNetAppender"> 
     ... 
    </appender> 
    <logger name="NHibernate"> 
     <level value="WARN"/> 
     <appender-ref ref="AdoNetAppender"/> 
    </logger> 
</log4net> 
+0

感謝邁克爾,但正如我指出的,我不希望使用AdoNetAppender爲需要外部信息傳遞給它。我的方法使用已經創建的nHibernate配置。 – Anthony 2011-04-22 18:07:23