使用Log4Net进行nHibernate日志记录,线程会话问题

时间:2011-04-22 14:40:40

标签: asp.net multithreading nhibernate thread-safety log4net

嘿,伙计们,我在这里遇到一些小问题,我正试着把头包起来。

我目前正在开始使用nHibernate,因此我必须满足工作要求,而且我对nHibernate的Sessions和多线程感到困惑。我想在这里完成的任务是让Log4Net将所有内容记录到数据库中,包括nHibernate的调试/错误等。

所以我所做的就是创建一个非常简单的Log4Net:AppenderSkeleton类,在需要它时可以完美地激活它。我遇到的最初的问题是,当我使用GetCurrentSession时,显然因为Log4Net在一个单独的线程上运行,所以它在初始线程的会话中出错了。所以我想我必须为Log4Net AppenderSkeleton类创建一个新的nHiberante Session。代码如下:

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,因为这将获得调用Session(主程序流)。我确信有一种方法可以为Log4Net的线程全局创建一个会话,但我不确定。请记住,我已经使用Global.asax(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 appender,但我希望直接使用nHibernate将数据添加到数据库中。原因是当nHibernate已经工作时我不想乱用连接线等。

与往常一样,任何帮助都会受到赞赏。

- 修改: -

因此,基于我最初告诉我的内容,我修改了自定义Log4Net记录器代码。下面有两个版本。我的问题,哪个最好还是有更好的方法?

  • 根据nHibernate教授的说法,第一个只创建两个会话 - 初始会话用于主程序流程,第二个用于我的Log4Net记录器代码。然而,在第二次会议中有数百个企业,抱怨有太多的企业家和许多人打电话到数据库。

  • 第二个,nHibernate教授显示了许多会话,与主程序流的记录器+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();
            }
        }
    }

2 个答案:

答案 0 :(得分:1)

你是关于创建一个新会话的。你绝对不希望跨线程共享同一个会话。在您的日志记录实例中,我甚至会说使用IStatelessSession。会话也应该相当轻量级,所以每次记录语句时我都不会担心创建新的会话。

答案 1 :(得分: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>