Журналирование nHibernate с Log4Net, проблема сеанса потока - PullRequest
3 голосов
/ 22 апреля 2011

Привет, ребята, у меня есть небольшая проблема, которую я пытаюсь обернуть.

В настоящее время я начинаю с nHibernate, так что мне это нужно из-за рабочих требований, и я немного застреваю с сессиями nHibernate и несколькими потоками. Итак, задача, которую я хочу выполнить, состоит в том, чтобы Log4Net регистрировал все в базе данных, включая отладку / ошибки nHibernate и т. Д.

Итак, я создал очень простой класс Log4Net: AppenderSkeleton, который срабатывает идеально, когда мне это нужно. Основная проблема, с которой я столкнулся, заключалась в том, что, когда я использовал GetCurrentSession, очевидно, что, поскольку Log4Net работает в отдельном потоке (ах), он завершился ошибкой во время сеанса исходного потока. Поэтому я решил, что мне нужно создать новую сессию nHiberante для класса Log4Net AppenderSkeleton. Код ниже:

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, но я не уверен, что да. Имея в виду, что я уже связываю Session с начальным потоком, используя нижеприведенное ниже в 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;
        }
    } 
}

Я понимаю, что могу использовать приложение ADO в log4net, но я хочу использовать nHibernate напрямую, чтобы добавить данные в базу данных. Причина в том, что я не хочу возиться со строками соединений и т. Д., Когда nHibernate уже работает.

Как всегда, любая помощь всегда ценится.

- Изменить: -

Итак, исходя из того, что мне сказали вначале, я изменил свой код регистратора Log4Net. Ниже представлены две версии. Мой вопрос, что лучше или есть лучший способ?

  • Первый, согласно nHibernate Prof, создает только два сеанса. Первый сеанс предназначен для основного потока программы, как и предполагалось, а второй - для кода моего регистратора 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();
            }
        }
    }

Ответы [ 2 ]

1 голос
/ 22 апреля 2011

NHibernate уже использует Log4Net для внутреннего использования, поэтому вам просто нужно включить регистратор и использовать AdoNetAppender для отправки журналов в вашу базу данных.

<log4net>
    <appender name="AdoNetAppender" type="log4net.Appender.AdoNetAppender">
        ...
    </appender>
    <logger name="NHibernate">
        <level value="WARN"/>
        <appender-ref ref="AdoNetAppender"/>
    </logger>
</log4net>
1 голос
/ 22 апреля 2011

Вы правы в создании нового сеанса.Вы определенно не хотите делить один и тот же сеанс между потоками.В вашем случае регистрации я бы даже сказал использовать IStatelessSession.Кроме того, сессии должны быть достаточно легкими, чтобы я не беспокоился о создании новых сессий каждый раз, когда вы регистрируете оператор.

...