2012-07-13 3 views
6

Ho una registrazione del database in atto utilizzando AdoNetAppender. Quello che mi piacerebbe fare è registrare l'identità dell'utente su ogni istruzione di registro. Tuttavia, non voglio utilizzare il parametro di identità standard log4net% per due motivi:log4net registrazione del database con parametri personalizzati

  1. log4net avvisare che è estremamente lento in quanto deve cercare l'identità del contesto.
  2. In alcuni componenti di servizio l'identità standard è un account di servizio ma abbiamo già acquisito l'identità dell'utente in una variabile e mi piacerebbe usarlo.

ho visto codice in cui alcune persone usano il log4net.ThreadContext per aggiungere ulteriori proprietà ma capisco che questo è 'sicuro' a causa di filo interleaving (ed è anche uno scarico di performance).

Il mio approccio è stato quello di estendere la classe AdoNetAppenderParameter così:

public class UserAdoNetAppenderParameter : AdoNetAppenderParameter 
{ 

    public UserAdoNetAppenderParameter() 
    { 
     DbType = DbType.String; 
     PatternLayout layout = new PatternLayout(); 
     Layout2RawLayoutAdapter converter = new Layout2RawLayoutAdapter(layout); 
     Layout = converter; 
     ParameterName = "@username"; 
     Size = 255; 
    } 


    public override void Prepare(IDbCommand command) 
    {    
     command.Parameters.Add(this); 
    } 


    public override void FormatValue(IDbCommand command, LoggingEvent loggingEvent) 
    {    
     string[] data = loggingEvent.RenderedMessage.Split('~'); 
     string username = data[0]; 
     command.Parameters["@username"] = username; 
    } 

} 

e quindi a livello di codice aggiungere questo al appender corrente in questo modo:

ILog myLog = LogManager.GetLogger("ConnectionService"); 
IAppender[] appenders = myLog.Logger.Repository.GetAppenders(); 
AdoNetAppender appender = (AdoNetAppender)appenders[0];      

appender.AddParameter(new UserAdoNetAppenderParameter()); 

myLog.InfoFormat("{0}~{1}~{2}~{3}", userName, "ClassName", "Class Method", "Message"); 

L'intento è quello di utilizzare un formato standard per i messaggi e analizzare la prima parte della stringa che dovrebbe sempre essere il nome utente. Il metodo FormatValue() del parametro appender personalizzato deve quindi utilizzare solo quella parte della stringa in modo che possa essere scritta in un campo separato nel database dei registri.

Il mio problema è che nessuna istruzione di registro viene scritta nel database. Stranamente, durante il debug, un punto di interruzione nel metodo FormatValue() viene colpito solo quando interrompo il servizio.

Ho trascinato un sacco di cose relative a questo ma non ho ancora trovato alcuna risposta. Qualcuno è riuscito a farlo, o sono sulla strada sbagliata. P.S. Ho anche provato ad estendere l'AdoNetAppender ma non ti dà accesso per impostare i valori dei parametri.

risposta

4

Dopo alcuni esperimenti, ho finalmente funzionato. Garantire che la registrazione interna di log4net abbia aiutato a identificare gli errori e scaricare il codice sorgente di log4net e rivedere la classe AdoNetAppenderParameter ha mostrato come dovrebbe essere utilizzato il metodo FormatValue().Quindi, ecco il parametro appender personalizzato modificato:

public class UserAdoNetAppenderParameter : AdoNetAppenderParameter 
{   

    public override void FormatValue(IDbCommand command, LoggingEvent loggingEvent) 
    {    
     string[] data = loggingEvent.RenderedMessage.Split('~'); 
     string username = string.Empty; 
     if (data != null && data.Length >= 1) 
      username = data[0]; 

     // Lookup the parameter 
     IDbDataParameter param = (IDbDataParameter)command.Parameters[ParameterName]; 

     // Format the value 
     object formattedValue = username; 

     // If the value is null then convert to a DBNull 
     if (formattedValue == null) 
     { 
      formattedValue = DBNull.Value; 
     } 

     param.Value = formattedValue; 
    } 

} 

E per utilizzare questo, aggiungo che nel file di configurazione log4net in questo modo:

<parameter type="MyAssembly.Logging.UserAdoNetAppenderParameter, MyAssembly"> 
<parameterName value="@username" /> 
<dbType value="String" /> 
<size value="255" /> 
<layout type="log4net.Layout.PatternLayout" value="%message" /> 
</parameter> 

E per convenzione, le mie dichiarazioni di registro sarà qualcosa di simile questo:

if (log.IsDebugEnabled) 
    log.DebugFormat("{0}~{1}~{2}", username, someOtherParameter, message); 

Se si guarda alla classe, utilizza i dati [0] come nome utente, quindi dipende seguendo la convenzione. Tuttavia, recupera il nome utente nel proprio parametro e in un campo separato nella tabella del database dei registri, senza ricorrere all'inserimento temporaneo nel ThreadContext non sicuro.

2

Sì, agilità sui thread significa che potresti non recuperare i dati corretti. Per log4net, dovrai inserirlo nel tuo HttpContext's Items collection.

Il problema è che devi fare un po 'di lavoro per tornare indietro quando è il momento di scrivere quei valori nel database, per questo ho sempre usato il Marek's Adaptive Property Provider class per fare il lavoro per me. E 'super semplice da usare come tutto quello che dovete fare è la seguente:

log4net.ThreadContext.Properties["UserName"] = AdaptivePropertyProvider.Create("UserName", Thread.CurrentPrincipal.Identity.Name); 

La proprietà adattiva conoscerà il luogo appropriato per recuperare il valore quando log4net richiede.

Opzione Alternativa

Se non sei bloccato con log4net, NLog rende la registrazione per siti Web ASP.NET modo più semplice perché supportano in modo nativo le applicazioni ASP.NET. L'utilizzo e persino la configurazione sono quasi identici a log4net!

+1

Sono tipo bloccato con log4net e ho Window s componenti di servizio nella soluzione che non hanno accesso a HttpContext, sebbene passiamo il nome utente come stringa. Non voglio davvero impostare la proprietà ThreadContext, accedere e quindi annullarla ogni volta che ho bisogno di accedere a qualcosa. Speravo che l'esteso AdoNetAppenderParameter si sarebbe preso cura di esso. Grazie per il tuo suggerimento –

5

Ho anche bisogno di registrare i dati strutturati e non piaceva usare l'interfaccia di registrazione in questo modo:

log.Debug(new { 
    SomeProperty: "some value", 
    OtherProperty: 123 
}) 

Così ho scritto anche classe AdoNetAppenderParameter personalizzato per fare il lavoro:

public class CustomAdoNetAppenderParameter : AdoNetAppenderParameter 
{ 
    public override void FormatValue(IDbCommand command, LoggingEvent loggingEvent) 
    { 
     // Try to get property value 
     object propertyValue = null; 
     var propertyName = ParameterName.Replace("@", ""); 

     var messageObject = loggingEvent.MessageObject; 
     if (messageObject != null) 
     { 
      var property = messageObject.GetType().GetProperty(propertyName); 
      if (property != null) 
      { 
       propertyValue = property.GetValue(messageObject, null); 
      } 
     } 

     // Insert property value (or db null) into parameter 
     var dataParameter = (IDbDataParameter)command.Parameters[ParameterName]; 
     dataParameter.Value = propertyValue ?? DBNull.Value; 
    } 
} 

configurazione Ora log4net può essere utilizzato per registrare qualsiasi proprietà dell'oggetto dato:

<?xml version="1.0" encoding="utf-8"?> 
<log4net> 
    <appender name="MyAdoNetAppender" type="log4net.Appender.AdoNetAppender"> 
     <connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> 
     <connectionString value="... your connection string ..." /> 
     <commandText value="INSERT INTO mylog ([level],[someProperty]) VALUES (@log_level,@SomeProperty)" /> 

     <parameter> 
      <parameterName value="@log_level" /> 
      <dbType value="String" /> 
      <size value="50" /> 
      <layout type="log4net.Layout.PatternLayout"> 
       <conversionPattern value="%level" /> 
      </layout> 
     </parameter> 

     <parameter type="yourNamespace.CustomAdoNetAppenderParameter, yourAssemblyName"> 
      <parameterName value="@SomeProperty" /> 
      <dbType value="String" /> 
      <size value="255" /> 
     </parameter> 
    </appender> 

    <root> 
     <level value="DEBUG" /> 
     <appender-ref ref="MyAdoNetAppender" /> 
    </root> 
</log4net>