2011-04-22 62 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