2011-03-02 18 views
5

NLog mi consente di utilizzare SplitGroup per registrare i miei messaggi su più destinazioni. Mi piacerebbe utilizzare questa funzione per registro ogni messaggio a un comune, specifica dall'utente e specifici di data log in una sola volta:Come applicare diversi layout alla stessa destinazione in NLog?

<variable name="commonLog" value="${logDir}\Common.log" /> 
<variable name="username" value="${identity:fSNormalize=true:authType=false:isAuthenticated=false}" /> 
<variable name="userLog" value="${logDir}\ByUser\${username}.log" /> 
<variable name="dateLog" value="${logDir}\ByDate\${shortdate}.log" /> 

<target name="logFiles" xsi:type="SplitGroup"> 
    <target xsi:type="File" fileName="${commonLog}" layout="${myLayout}" /> 
    <target xsi:type="File" fileName="${userLog}" layout="${myLayout}" /> 
    <target xsi:type="File" fileName="${dateLog}" layout="${myLayout}" /> 
</target> 

Questo è grande, ma voglio anche utilizzare diversi layout per diversi livelli di gravità. Ad esempio, errorLayout dovrebbe includere informazioni sulle eccezioni e inserire [!] marcatore così ho potuto poi evidenziare errori di spettatori di registro come BareTail:

<variable name="stamp" value="${date} ${username} ${logger}" /> 

<variable name="debugLayout" value="${stamp} ... ${message}" /> 
<variable name="infoLayout" value="${stamp} [i] ${message}" /> 
<variable name="warnLayout" value="${stamp} [!] ${message}" /> 
<variable name="errorLayout" 
    value="${warnLayout}${newline}${pad:padding=10:inner=${exception:format=ToString}}" /> 

<!-- logFiles target --> 

<rules> 
    <logger name="*" level="Debug" writeTo="logFiles" layout="debugLayout" /> 
    <logger name="*" level="Info" writeTo="logFiles" layout="infoLayout" /> 
    <logger name="*" level="Warn" writeTo="logFiles" layout="warnLayout" /> 
    <logger name="*" level="Error" writeTo="logFiles" layout="errorLayout" /> 
</rules> 

Questo codice assume Error s vengono sempre con eccezioni e Warning s no, ma non è questo il punto.

Il problema è questa configurazione è errata. Non funzionerà perché logger non ha l'attributo layout. È definito solo per target.

Il layout che viene utilizzato deve essere dichiarato dagli obiettivi stessi, ma non vedo alcun modo per specificare layout diversi per livelli di gravità diversi.

Per ora, ho dovuto copiare e incollare lo stesso codice di configurazione quattro volte solo per avere quattro diversi layout s per lo stesso insieme di file:

<targets> 
    <target name="logFilesDebug" xsi:type="SplitGroup"> 
    <target xsi:type="File" fileName="${commonLog}" layout="${debugLayout}" /> 
    <target xsi:type="File" fileName="${userLog}" layout="${debugLayout}" /> 
    <target xsi:type="File" fileName="${dateLog}" layout="${debugLayout}" /> 
    </target> 

    <target name="logFilesInfo" xsi:type="SplitGroup"> 
    <target xsi:type="File" fileName="${commonLog}" layout="${infoLayout}" /> 
    <target xsi:type="File" fileName="${userLog}" layout="${infoLayout}" /> 
    <target xsi:type="File" fileName="${dateLog}" layout="${infoLayout}" /> 
    </target> 

    <target name="logFilesWarn" xsi:type="SplitGroup"> 
    <target xsi:type="File" fileName="${commonLog}" layout="${warnLayout}" /> 
    <target xsi:type="File" fileName="${userLog}" layout="${warnLayout}" /> 
    <target xsi:type="File" fileName="${dateLog}" layout="${warnLayout}" /> 
    </target> 

    <target name="logFilesError" xsi:type="SplitGroup"> 
    <target xsi:type="File" fileName="${commonLog}" layout="${errorLayout}" /> 
    <target xsi:type="File" fileName="${userLog}" layout="${errorLayout}" /> 
    <target xsi:type="File" fileName="${dateLog}" layout="${errorLayout}" /> 
    </target> 
</targets> 

<rules> 
    <logger name="*" level="Debug" writeTo="logFilesDebug" /> 
    <logger name="*" level="Info" writeTo="logFilesInfo" /> 
    <logger name="*" level="Warn" writeTo="logFilesWarn" /> 
    <logger name="*" level="Error" writeTo="logFilesError" /> 
</rules> 

Questo fa male solo i miei occhi.
C'è un modo migliore per farlo ed evitare la duplicazione?

risposta

1

Non sono sicuro, ma penso che probabilmente sei bloccato con la duplicazione. Vuoi 4 layout diversi da utilizzare sullo stesso file e vuoi 3 file diversi. Un obiettivo richiede un layout. Pertanto, se si desidera accedere solo a un file, è necessario definire 4 destinazioni, ciascuna delle quali punta allo stesso file e ognuna con il proprio layout. Non penso che NLog abbia un modo più conveniente per associare più layout a una destinazione e quindi scegliere un layout in base al contenuto del messaggio di registrazione.

A seconda esattamente ciò che si desidera ottenere con i formati, è possibile ridurre leggermente la duplicazione scrivendo un LayoutRenderer personalizzato. Nel tuo esempio, mostri che il layout di Debug ha "..." in esso, Info ha [i], Warn ha [!], E Error ha Warn + exception. Potresti scrivere un Layoutender che aggiunge il marcatore speciale, a seconda del livello del messaggio. In questo modo, eseguirai il debug, le informazioni e l'avviso in un unico layout e l'errore manterrà il proprio layout.

Ad esempio:

Qualcosa di simile a questo per un LayoutRenderer personalizzato (in base a NLog 1.0 di aggiornamento, non 2.0):

[LayoutRenderer("LevelMarkerLayoutRenderer")] 
    class LevelMarkerLayoutRenderer : LayoutRenderer 
    {  
    int estimatedSize = 3;  
    protected override void Append(StringBuilder builder, LogEventInfo logEvent) 
    {  
     string marker; 
     switch (logEvent.Level) 
     { 
     case Debug: 
      marker = "..."; 
      break; 
     case Info: 
      marker = "[i]"; 
      break; 
     case Warn: 
      marker = "[!]"; 
      break; 
     case Error: 
      marker = "[!]"; 
      break; 
     case Fatal: 
      marker = "[!]"; 
      break; 
     default: 
      marker = "?"; 
     } 

     builder.Append(marker);  
    }  

    protected override int GetEstimatedBufferSize(LogEventInfo logEvent)  
    {  
     return estimatedSize;  
    } 
    } 

Ora è possibile configurare due layout: "normale", e "l'errore ".

Qualcosa di simile:

<variable name="stamp" value="${date} ${username} ${logger}" /> 

<variable name="normal" value="${stamp} ${LevelMarkerLayoutRenderer} ${message}" /> 
<variable name="error" 
    value="${warnLayout}${newline}${pad:padding=10:inner=${exception:format=ToString}}" /> 

si potrebbe forse anche creare una LayoutRenderer personalizzato per gestire le eccezioni. Se nessuna eccezione, non emettere nulla. Se eccezione, newline concatenato, riempimento e la stringa di eccezione.

Se tu avessi una "riserva" di layout eccezione renderer, allora si potrebbe avere solo un layout che potrebbe essere simile a questo:

<variable name="normal" value="${stamp} ${LevelMarkerLayoutRenderer} ${message} ${ConditionalExceptionLayoutRenderer}" /> 

maggior parte del tempo, ConditionalExceptionLayoutRenderer produrrebbe nulla perché non ci sarebbe un eccezione.

Spero che questo aiuti.

+0

Grazie per il tuo commento. Per ora voglio attenermi a dipendenze minime ma quando re-engineer il sistema, forse lo implementerò. –

8

Una soluzione alternativa consiste nell'utilizzare la condizione when nel layout.

target.Layout = "${longdate}|[${level}]|${logger}|${message}${onexception:inner=|${exception}${when:when=(level > LogLevel.Warn):inner=|[!] ${exception:format=ToString:innerFormat=Message:maxInnerExceptionLevel=5} }}" 

Volevo solo fornire il messaggio di eccezione quando qualcosa di meno di errore. Quando c'era un errore volevo traccia di stack completo.

+0

Questa dovrebbe essere la risposta accettata. Se capisco correttamente l'OP, la domanda dovrebbe essere riformulata per rispondere in modo specifico a questo: "Voglio anche usare layout diversi per diversi livelli di gravità", e quindi questa sarebbe la risposta corretta per questo. – MattM