2011-04-22 5 views
3

안녕하세요 여러분, 여기 내 머리를 감싸는 데 약간의 문제가 있습니다.Log4Net을 통한 nHibernate 로깅, 세션 세션 문제

저는 현재 nHibernate로 시작하고 있습니다. 작업 요구 사항에 따라야하고, nHibernate의 세션과 다중 스레드에 조금 붙어 있습니다. Log4Net에서 nHibernate의 디버그/오류 등을 포함한 모든 것을 Logging 할 필요가있다.

그래서 내가 한 것은 매우 간단한 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에의 아래 사용 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가 이미 작동하고있을 때 connectionstrings 등으로 엉망이되고 싶지 않기 때문이다.

항상 그렇듯이 모든 도움은 언제나 감사하겠습니다.

- 편집 : - 나는 initialy 말되었다 무엇을, 나는 내 사용자 지정 Log4Net 로거 코드 수정에

는 그래서 기초. 아래 두 가지 버전이 있습니다. 제 질문이 가장 좋으며 더 좋은 방법이 있습니까?

  • 첫째, nHibernate 수 교수에 따르면, 단지 두 개의 세션을 생성 - intial 세션 의도 메인 프로그램 흐름 내 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(); 
      } 
     } 
    } 
+0

NHibernate를 사용하여 NHibernate 디버그 메시지를 기록하면 무한 루프가되어 컴퓨터가 녹지 않을까요? – dotjoe

+0

그런 다음 중복 또는 재실행을 확인해야합니다. 어쨌든, 지금까지, 내 ​​애플 리케이션의 시작, 그냥 기본적인 것은, 약 300 디버그 메시지를 nHibernate 및 루프에서 로그 : – Anthony

+0

StatelessSession 갈 방법입니다. 당신은 그것에 어떤 문제도 가지지 않을 것입니다. –

답변

1

새 세션을 만드는 방법에 대한 권리입니다. 확실히 같은 세션을 여러 스레드에서 공유하고 싶지는 않습니다. 로깅 인스턴스에서 IStatelessSession을 사용한다고 말합니다. 또한 세션은 상당히 가볍기 때문에 성명을 기록 할 때마다 새로운 세션을 만드는 것에 대해 걱정하지 않아도됩니다.

+0

답변을 주셔서 감사합니다, 나는 호기심입니다. "기록 된 항목"당 새 세션을 만들면 매번 새 데이터베이스 연결이 생성되지 않습니다. 따라서 프로세스가 끝날 때까지 100 개의 항목이 기록 된 경우 데이터베이스에 최소 100 개의 별도 연결이 필요합니까? – Anthony

+2

ADO.NET은 연결 풀링을 사용합니다. http://msdn.microsoft.com/en-us/library/8xx3tyca.aspx – rbellamy

+0

많은, 많은 다른 테스트 후에 ADO 풀링은 확실히 작동합니다. 비록 내가이 로깅 작업을하는 가장 좋은 방법인지 확실하지 않다. Log4Net의 새로운 스레드가 nHibernate로 실행되면 새로운 연결이 만들어집니다. 따라서 IIS Express의 로컬 테스트에서는 시작 프로그램 흐름이 끝날 때까지 데이터베이스에 두 개의 연결이있었습니다. 연결을 하나만 만들 수 있습니까? – Anthony

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

감사합니다. Michael입니다.하지만 AdoNetAppender를 사용하고 싶지는 않습니다. 외부 정보를 전달해야하므로 AdoNetAppender를 사용하고 싶지는 않습니다. 내 메소드는 이미 생성 된 nHibernate의 설정을 사용한다. – Anthony