Care sunt cele mai bune sau cele mai utile configurații pentru logare cu NLog? (Acestea pot fi simple sau complexe, atâta timp cât acestea're util.)
Am'm gândesc de exemple, cum ar fi automat de rulare peste fișiere jurnal la o anumită dimensiune, schimbarea layout (log message) dacă există sau nu este o excepție, crescând nivelul de jurnal o dată a apărut o eroare, etc.
Aici sunt cateva link-uri:
Unele dintre acestea se încadrează în categoria de general NLog (sau logare) sfaturi, mai degrabă decât strict de configurare sugestii.
Aici sunt unele generale de logare link-uri de aici de la ASA (s-ar putea să fi văzut unele sau toate aceste deja):
https://stackoverflow.com/questions/710863/log4net-vs-nlog
https://stackoverflow.com/questions/576185/logging-best-practices
https://stackoverflow.com/questions/3825276/whats-the-point-of-a-logging-facade
https://stackoverflow.com/questions/3143929/why-do-loggers-recommend-using-a-logger-per-class
Utilizarea în comun model de numirea logger bazat pe clasa Logger înregistrator = LogManager.GetCurrentClassLogger()`. Acest lucru vă oferă un grad ridicat de granularitate în tăietorii și vă oferă o mare flexibilitate în configurare de la furnizori de bustean (control la nivel global, de nume, de anumite logger nume, etc).
Utilizarea non-code pe bază de furnizori de bustean, dacă este cazul. Poate ai o funcție pentru care vrei într-adevăr să controleze exploatarea separat. Poate aveti unele transversale logare preocupări (de performanță logare).
Dacă tu nu't folosi classname pe bază de logare, ia în considerare numirea furnizori de bustean într-un fel de structură ierarhică (poate de zonă funcțională), astfel încât să puteți menține o mai mare flexibilitate în configurare. De exemplu, ai putea avea o "de date" zona funcțională, un "analiza" FA si o "ui" FA. Fiecare dintre acestea ar putea fi sub-zone. Deci, s-ar putea solicita furnizori de bustean astfel:
Logger logger = LogManager.GetLogger("Database.Connect");
Logger logger = LogManager.GetLogger("Database.Query");
Logger logger = LogManager.GetLogger("Database.SQL");
Logger logger = LogManager.GetLogger("Analysis.Financial");
Logger logger = LogManager.GetLogger("Analysis.Personnel");
Logger logger = LogManager.GetLogger("Analysis.Inventory");
Și așa mai departe. Cu ierarhice furnizori de bustean, puteți configura logare la nivel global ("*" sau rădăcină logger), de FA (baze de Date, Analiza, UI), sau de subzona (baza de Date.Connect, etc).
Oamenii au mai multe opțiuni de configurare:
<logger name="Name.Space.Class1" minlevel="Debug" writeTo="f1" />
<logger name="Name.Space.Class1" levels="Debug,Error" writeTo="f1" />
<logger name="Name.Space.*" writeTo="f3,f4" />
<logger name="Name.Space.*" minlevel="Debug" maxlevel="Error" final="true" />
Vezi NLog ajuta pentru mai multe informații cu privire la exact ceea ce fiecare dintre opțiunile de mijloace. Probabil cele mai importante elemente aici sunt capacitatea de a wildcard logger reguli, conceptul care mai multe logger reguli pot "execute" pentru o singură logare declarație, și că un logger de regulă pot fi marcate ca "final" astfel încât ulterioare reguli nu vor executa pentru un anumit logare declarație.
Utilizați GlobalDiagnosticContext, MappedDiagnosticContext, și NestedDiagnosticContext pentru a adăuga un context suplimentar la ieșire.
Utilizați "variabil" în fișierul de configurare pentru a simplifica. De exemplu, s-ar putea defini variabile pentru machete și apoi de referință variabilă în configurare țintă, mai degrabă decât specifica layout-ul direct.
<variable name="brief" value="${longdate} | ${level} | ${logger} | ${message}"/>
<variable name="verbose" value="${longdate} | ${machinename} | ${processid} | ${processname} | ${level} | ${logger} | ${message}"/>
<targets>
<target name="file" xsi:type="File" layout="${verbose}" fileName="${basedir}/${shortdate}.log" />
<target name="console" xsi:type="ColoredConsole" layout="${brief}" />
</targets>
Sau, ai putea crea un "personalizate" set de proprietăți pentru a adăuga la un aspect.
<variable name="mycontext" value="${gdc:item=appname} , ${mdc:item=threadprop}"/>
<variable name="fmt1withcontext" value="${longdate} | ${level} | ${logger} | [${mycontext}] |${message}"/>
<variable name="fmt2withcontext" value="${shortdate} | ${level} | ${logger} | [${mycontext}] |${message}"/>
Sau, puteți face lucruri cum ar fi crearea de "ziua" sau "luna" aspect renderers strict prin configurare:
<variable name="day" value="${date:format=dddd}"/>
<variable name="month" value="${date:format=MMMM}"/>
<variable name="fmt" value="${longdate} | ${level} | ${logger} | ${day} | ${month} | ${message}"/>
<targets>
<target name="console" xsi:type="ColoredConsole" layout="${fmt}" />
</targets>
Puteți folosi, de asemenea, aspect face pentru a defini numele fisierului:
<variable name="day" value="${date:format=dddd}"/>
<targets>
<target name="file" xsi:type="File" layout="${verbose}" fileName="${basedir}/${day}.log" />
</targets>
Dacă rola fișierul dvs. de zi cu zi, fiecare fișier ar putea fi numit "de luni.jurnal", "marți.jurnal", etc.
Don't fie frică să-ți scrie propriul aspect renderer. Este ușor și vă permite să adăugați propriile dvs. informații de context, pentru fișierul jurnal prin configurare. De exemplu, aici este un aspect renderer (bazat pe NLog 1.x, nu 2.0), care poate adăuga la Urmă.CorrelationManager.ActivityId la jurnal:
[LayoutRenderer("ActivityId")]
class ActivityIdLayoutRenderer : LayoutRenderer
{
int estimatedSize = Guid.Empty.ToString().Length;
protected override void Append(StringBuilder builder, LogEventInfo logEvent)
{
builder.Append(Trace.CorrelationManager.ActivityId);
}
protected override int GetEstimatedBufferSize(LogEventInfo logEvent)
{
return estimatedSize;
}
}
Spune-NLog unde NLog extensii (ce de asamblare) astfel:
<extensions>
<add assembly="MyNLogExtensions"/>
</extensions>
Utilizați layout personalizat renderer astfel:
<variable name="fmt" value="${longdate} | ${ActivityId} | ${message}"/>
Utilizarea asincron obiective:
<nlog>
<targets async="true">
<!-- all targets in this section will automatically be asynchronous -->
</targets>
</nlog>
Și implicit țintă ambalaje:
<nlog>
<targets>
<default-wrapper xsi:type="BufferingWrapper" bufferSize="100"/>
<target name="f1" xsi:type="File" fileName="f1.txt"/>
<target name="f2" xsi:type="File" fileName="f2.txt"/>
</targets>
<targets>
<default-wrapper xsi:type="AsyncWrapper">
<wrapper xsi:type="RetryingWrapper"/>
</default-wrapper>
<target name="n1" xsi:type="Network" address="tcp://localhost:4001"/>
<target name="n2" xsi:type="Network" address="tcp://localhost:4002"/>
<target name="n3" xsi:type="Network" address="tcp://localhost:4003"/>
</targets>
</nlog>
acolo unde este cazul. Vezi NLog docs pentru mai multe informații cu privire la aceste.
Spune-NLog pentru a viziona și de a reîncărca automat configurația dacă se schimbă:
<nlog autoReload="true" />
Există mai multe opțiuni de configurare pentru a ajuta cu depanare NLog
<nlog throwExceptions="true" />
<nlog internalLogFile="file.txt" />
<nlog internalLogLevel="Trace|Debug|Info|Warn|Error|Fatal" />
<nlog internalLogToConsole="false|true" />
<nlog internalLogToConsoleError="false|true" />
Vezi NLog Ajutor pentru mai multe informații.
NLog 2.0 adaugă LayoutRenderer ambalaje care permit suplimentare de prelucrare să fie efectuate la ieșirea de un aspect renderer (cum ar fi tunderea spațiu, uppercasing, lowercasing, etc).
Don't fie frică să-și încheie logger dacă doriți izola codul de pe un hard dependența NLog, dar înfășurați corect. Există exemple de cum să-și încheie în NLog's github. Un alt motiv pentru a încheia, ar putea fi pe care doriți să adăugați automat anumite informații de context, pentru fiecare conectat mesaj (prin punerea în LogEventInfo.Context).
Există argumente pro și contra pentru ambalaj (sau abstractizare) NLog (sau orice alte logare cadru pentru care contează). Cu un pic de efort, puteți găsi o mulțime de informații aici, pe ATÂT de prezentarea ambele părți.
Dacă aveți în vedere ambalaj, luați în considerare utilizarea Comun.Logare. Acesta funcționează destul de bine și vă permite să comutați cu ușurință de la un alt cadru de lucru de înregistrare, dacă aveți dorința de a face așa. De asemenea, dacă aveți în vedere ambalaj, gândiți-vă cum vă veți descurca contextul obiecte (GDC, MDC, NDC). Comune.Logare nu acceptă în prezent o abstracție pentru ei, dar se presupune că este în coadă de capacități pentru a adăuga.
Noi de multe ori doriți să obțineți mai multe informații atunci când există o excepție. Următoarea configurație are două obiective, un fișier și consola, care să filtreze dacă există sau nu există nici o excepție de informatii. (EDIT: Jarek a postat despre o nouă metodă de a face acest lucru în vNext.)
Cheia este de a avea un înveliș țintă cu xsi:type="FilteringWrapper" stare="lungime('${excepție}')>0"`
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.mono2.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"
internalLogLevel="Warn"
internalLogFile="nlog log.log"
>
<variable name="VerboseLayout"
value="${longdate} ${level:upperCase=true} ${message}
(${callsite:includSourcePath=true})" />
<variable name="ExceptionVerboseLayout"
value="${VerboseLayout} (${stacktrace:topFrames=10})
${exception:format=ToString}" />
<targets async="true">
<target name="file" xsi:type="File" fileName="log.log"
layout="${VerboseLayout}">
</target>
<target name="fileAsException"
xsi:type="FilteringWrapper"
condition="length('${exception}')>0">
<target xsi:type="File"
fileName="log.log"
layout="${ExceptionVerboseLayout}" />
</target>
<target xsi:type="ColoredConsole"
name="console"
layout="${NormalLayout}"/>
<target xsi:type="FilteringWrapper"
condition="length('${exception}')>0"
name="consoleException">
<target xsi:type="ColoredConsole"
layout="${ExceptionVerboseLayout}" />
</target>
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="console,consoleException" />
<logger name="*" minlevel="Warn" writeTo="file,fileAsException" />
</rules>
</nlog>
Se pare că, acum puteți utiliza NLog cu Growl pentru Windows.
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<extensions>
<add assembly="NLog.Targets.GrowlNotify" />
</extensions>
<targets>
<target name="growl" type="GrowlNotify" password="" host="" port="" />
</targets>
<rules>
<logger name="*" minLevel="Trace" appendTo="growl"/>
</rules>
</nlog>
Configura NLog prin intermediul XML, dar Programatic
Ce? Știați că puteți specifica NLog XML direct la NLog din aplicație, spre deosebire de a avea NLog citit din fișierul de configurare? Ei bine, poți. Las's spun că ai o distribuite aplicație și doriți să utilizați aceeași configurație peste tot. Ai putea ține un fișier de configurare în fiecare locație și să-l mențină separat, ai putea menține într-o locație centrală și împingeți-l la locații prin satelit, sau ai putea face, probabil, o mulțime de alte lucruri. Sau, ai putea stoca XML într-o bază de date, se ajunge la pornirea app, și de a configura NLog direct cu XML (poate revii periodic pentru a vedea dacă acesta s-a schimbat).
string xml = @"<nlog>
<targets>
<target name='console' type='Console' layout='${message}' />
</targets>
<rules>
<logger name='*' minlevel='Error' writeTo='console' />
</rules>
</nlog>";
StringReader sr = new StringReader(xml);
XmlReader xr = XmlReader.Create(sr);
XmlLoggingConfiguration config = new XmlLoggingConfiguration(xr, null);
LogManager.Configuration = config;
//NLog is now configured just as if the XML above had been in NLog.config or app.config
logger.Trace("Hello - Trace"); //Won't log
logger.Debug("Hello - Debug"); //Won't log
logger.Info("Hello - Info"); //Won't log
logger.Warn("Hello - Warn"); //Won't log
logger.Error("Hello - Error"); //Will log
logger.Fatal("Hello - Fatal"); //Will log
//Now let's change the config (the root logging level) ...
string xml2 = @"<nlog>
<targets>
<target name='console' type='Console' layout='${message}' />
</targets>
<rules>
<logger name='*' minlevel='Trace' writeTo='console' />
</rules>
</nlog>";
StringReader sr2 = new StringReader(xml2);
XmlReader xr2 = XmlReader.Create(sr2);
XmlLoggingConfiguration config2 = new XmlLoggingConfiguration(xr2, null);
LogManager.Configuration = config2;
logger.Trace("Hello - Trace"); //Will log
logger.Debug("Hello - Debug"); //Will log
logger.Info("Hello - Info"); //Will log
logger.Warn("Hello - Warn"); //Will log
logger.Error("Hello - Error"); //Will log
logger.Fatal("Hello - Fatal"); //Will log
Am'm nu sunt sigur cât de robust este, dar acest exemplu oferă un punct de plecare util pentru persoanele care ar putea să doriți să încercați configurarea de genul asta.
I-am oferit-o pereche de rezonabil interesante răspunsuri la această întrebare:
https://stackoverflow.com/questions/4196663/nlog-generating-header-section-for-a-log-file
Adăugarea unui Antet:
Întrebarea vrut să știți cum să adăugați un antet pentru fișierul jurnal. Folosind config intrări, cum ar fi acest lucru vă permite să definiți antet format separat de format din restul de intrări jurnal. Utilizați o singură logger, poate numit "headerlogger" pentru a vă conecta un singur mesaj la început de aplicație și veți obține antet:
Defini antet și un fișier aspecte:
<variable name="HeaderLayout" value="This is the header. Start time = ${longdate} Machine = ${machinename} Product version = ${gdc:item=version}"/>
<variable name="FileLayout" value="${longdate} | ${logger} | ${level} | ${message}" />
A defini obiectivele, folosind machete:
<target name="fileHeader" xsi:type="File" fileName="xxx.log" layout="${HeaderLayout}" />
<target name="file" xsi:type="File" fileName="xxx.log" layout="${InfoLayout}" />
Defini furnizori de bustean:
<rules>
<logger name="headerlogger" minlevel="Trace" writeTo="fileHeader" final="true" />
<logger name="*" minlevel="Trace" writeTo="file" />
</rules>
Scrie antet, probabil, mai devreme în program:
GlobalDiagnosticsContext.Set("version", "01.00.00.25");
LogManager.GetLogger("headerlogger").Info("It doesn't matter what this is because the header format does not include the message, although it could");
Acest lucru este în mare măsură doar o altă versiune a "Tratarea excepțiilor în mod diferit" ideea.
Log Log fiecare nivel cu un aspect diferit
În mod similar, poster vrut să știe cum de a schimba formatul pe exploatarea nivel. Nu a fost't de clar pentru mine ceea ce obiectivul final a fost (și dacă acesta ar putea fi realizat într-o "bine" mod), dar am fost în măsură să ofere o configurație care a făcut ceea ce a cerut:
<variable name="TraceLayout" value="This is a TRACE - ${longdate} | ${logger} | ${level} | ${message}"/>
<variable name="DebugLayout" value="This is a DEBUG - ${longdate} | ${logger} | ${level} | ${message}"/>
<variable name="InfoLayout" value="This is an INFO - ${longdate} | ${logger} | ${level} | ${message}"/>
<variable name="WarnLayout" value="This is a WARN - ${longdate} | ${logger} | ${level} | ${message}"/>
<variable name="ErrorLayout" value="This is an ERROR - ${longdate} | ${logger} | ${level} | ${message}"/>
<variable name="FatalLayout" value="This is a FATAL - ${longdate} | ${logger} | ${level} | ${message}"/>
<targets>
<target name="fileAsTrace" xsi:type="FilteringWrapper" condition="level==LogLevel.Trace">
<target xsi:type="File" fileName="xxx.log" layout="${TraceLayout}" />
</target>
<target name="fileAsDebug" xsi:type="FilteringWrapper" condition="level==LogLevel.Debug">
<target xsi:type="File" fileName="xxx.log" layout="${DebugLayout}" />
</target>
<target name="fileAsInfo" xsi:type="FilteringWrapper" condition="level==LogLevel.Info">
<target xsi:type="File" fileName="xxx.log" layout="${InfoLayout}" />
</target>
<target name="fileAsWarn" xsi:type="FilteringWrapper" condition="level==LogLevel.Warn">
<target xsi:type="File" fileName="xxx.log" layout="${WarnLayout}" />
</target>
<target name="fileAsError" xsi:type="FilteringWrapper" condition="level==LogLevel.Error">
<target xsi:type="File" fileName="xxx.log" layout="${ErrorLayout}" />
</target>
<target name="fileAsFatal" xsi:type="FilteringWrapper" condition="level==LogLevel.Fatal">
<target xsi:type="File" fileName="xxx.log" layout="${FatalLayout}" />
</target>
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="fileAsTrace,fileAsDebug,fileAsInfo,fileAsWarn,fileAsError,fileAsFatal" />
<logger name="*" minlevel="Info" writeTo="dbg" />
</rules>
Din nou, foarte asemănătoare cu Tratarea excepțiilor în mod diferit.
Acest exemplu vă permite să obțineți mai multe informații atunci când există o eroare în cod. Practic, tampoane mesaje și ieșiri doar cele de la un anumit nivel jurnal (de exemplu, Avertiza) dacă o anumită condiție este îndeplinită (de exemplu, nu a fost o eroare, astfel încât nivelul de jurnal este >= Eroare), atunci acesta va afișa mai multe informații (de exemplu, toate mesajele de log niveluri >= Urme). Deoarece mesajele sunt tamponate, acest lucru vă permite să adune informații de urmărire despre ce s-a întâmplat înainte o Eroare sau ErrorException a fost conectat - foarte util!
Am adaptat unul de la un exemplu în codul sursă. Am fost aruncat în primul rând pentru că am plecat din AspNetBufferingWrapper(din a mea e't un ASP app) - se pare că [PostFilteringWrapper][2] necesită unele tamponat țintă. Rețineți că țintă-ref
element folosit în cele de mai sus legate de exemplu nu poate fi utilizat în NLog 1.0 (eu sunt, folosind 1.0 Refresh pentru un .NET 4.0 app); este necesar pentru a pune tinta în interiorul ambalajului bloc. De asemenea, rețineți că logica sintaxa (de exemplu, mai mare sau mai mică decât simboluri, < și >) a de a utiliza simbolurile, nu XML scapă de aceste simboluri (de exemplu, > " și " <
) sau altceva NLog vor eroare.
app.config:
<?xml version="1.0"?>
<configuration>
<configSections>
<section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog"/>
</configSections>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
throwExceptions="true" internalLogToConsole="true" internalLogLevel="Warn" internalLogFile="nlog.log">
<variable name="appTitle" value="My app"/>
<variable name="csvPath" value="${specialfolder:folder=Desktop:file=${appTitle} log.csv}"/>
<targets async="true">
<!--The following will keep the default number of log messages in a buffer and write out certain levels if there is an error and other levels if there is not. Messages that appeared before the error (in code) will be included, since they are buffered.-->
<wrapper-target xsi:type="BufferingWrapper" name="smartLog">
<wrapper-target xsi:type="PostFilteringWrapper">
<!--<target-ref name="fileAsCsv"/>-->
<target xsi:type="File" fileName="${csvPath}"
archiveAboveSize="4194304" concurrentWrites="false" maxArchiveFiles="1" archiveNumbering="Sequence"
>
<layout xsi:type="CsvLayout" delimiter="Tab" withHeader="false">
<column name="time" layout="${longdate}" />
<column name="level" layout="${level:upperCase=true}"/>
<column name="message" layout="${message}" />
<column name="callsite" layout="${callsite:includeSourcePath=true}" />
<column name="stacktrace" layout="${stacktrace:topFrames=10}" />
<column name="exception" layout="${exception:format=ToString}"/>
<!--<column name="logger" layout="${logger}"/>-->
</layout>
</target>
<!--during normal execution only log certain messages-->
<defaultFilter>level >= LogLevel.Warn</defaultFilter>
<!--if there is at least one error, log everything from trace level-->
<when exists="level >= LogLevel.Error" filter="level >= LogLevel.Trace" />
</wrapper-target>
</wrapper-target>
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="smartLog"/>
</rules>
</nlog>
</configuration>
Conectați-vă la Twitter
Bazat pe acest post despre un log4net Twitter Appender, m-am gândit să încerc mâna mea la scris o NLog Twitter Țintă (folosind NLog 1.0 refresh, nu 2.0). Din păcate, până acum nu am fost în stare pentru a obține un Tweet la posta cu succes. Eu nu't știu dacă este ceva greșit în codul meu, Twitter, compania noastra's conexiune la internet/firewall, sau ce. Postez aici codul în cazul în care cineva este interesat în a încerca-l. Rețineți că există trei diferite "Post" metode. Prima pe care am incercat este PostMessageToTwitter. PostMessageToTwitter este în esență același ca PostLoggingEvent în orignal post. Dacă am folosi că am obține o 401 excepție. PostMessageBasic devine aceeași excepție. PostMessage ruleaza fara erori, dar mesajul tot nu se face până la Twitter. PostMessage și PostMessageBasic sunt bazate pe exemple care am găsit-o aici pe ATÂT.
*FYI - am găsit acum un comentariu de @Jason Diller la un răspuns în acest mesaj care spune că twitter este de gând să dezactivați autentificarea de bază "luna viitoare". Acest lucru a fost în luna Mai 2010 și acum este decembrie 2010, deci cred că asta ar putea fi motivul pentru acest lucru nu este de lucru.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Web;
using System.IO;
using NLog;
using NLog.Targets;
using NLog.Config;
namespace NLogExtensions
{
[Target("TwitterTarget")]
public class TwitterTarget : TargetWithLayout
{
private const string REQUEST_CONTENT_TYPE = "application/x-www-form-urlencoded";
private const string REQUEST_METHOD = "POST";
// The source attribute has been removed from the Twitter API,
// unless you're using OAuth.
// Even if you are using OAuth, there's still an approval process.
// Not worth it; "API" will work for now!
// private const string TWITTER_SOURCE_NAME = "Log4Net";
private const string TWITTER_UPDATE_URL_FORMAT = "http://twitter.com/statuses/update.xml?status={0}";
[RequiredParameter]
public string TwitterUserName { get; set; }
[RequiredParameter]
public string TwitterPassword { get; set; }
protected override void Write(LogEventInfo logEvent)
{
if (string.IsNullOrWhiteSpace(TwitterUserName) || string.IsNullOrWhiteSpace(TwitterPassword)) return;
string msg = this.CompiledLayout.GetFormattedMessage(logEvent);
if (string.IsNullOrWhiteSpace(msg)) return;
try
{
//PostMessageToTwitter(msg);
PostMessageBasic(msg);
}
catch (Exception ex)
{
//Should probably do something here ...
}
}
private void PostMessageBasic(string msg)
{
// Create a webclient with the twitter account credentials, which will be used to set the HTTP header for basic authentication
WebClient client = new WebClient { Credentials = new NetworkCredential { UserName = TwitterUserName, Password = TwitterPassword } };
// Don't wait to receive a 100 Continue HTTP response from the server before sending out the message body
ServicePointManager.Expect100Continue = false;
// Construct the message body
byte[] messageBody = Encoding.ASCII.GetBytes("status=" + msg);
// Send the HTTP headers and message body (a.k.a. Post the data)
client.UploadData(@"http://twitter.com/statuses/update.xml", messageBody);
}
private void PostMessage(string msg)
{
string user = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(TwitterUserName + ":" + TwitterPassword));
byte [] bytes = System.Text.Encoding.UTF8.GetBytes("status=" + msg.ToTweet());
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://twitter.com/statuses/update.xml");
request.Method = "POST";
request.ServicePoint.Expect100Continue = false;
request.Headers.Add("Authorization", "Basic " + user);
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = bytes.Length;
Stream reqStream = request.GetRequestStream();
reqStream.Write(bytes, 0, bytes.Length);
reqStream.Close();
}
private void PostMessageToTwitter(string msg)
{
var updateRequest = HttpWebRequest.Create(string.Format(TWITTER_UPDATE_URL_FORMAT,
HttpUtility.UrlEncode(msg.ToTweet()))) as HttpWebRequest;
updateRequest.ContentLength = 0;
updateRequest.ContentType = REQUEST_CONTENT_TYPE;
updateRequest.Credentials = new NetworkCredential(TwitterUserName, TwitterPassword);
updateRequest.Method = REQUEST_METHOD;
updateRequest.ServicePoint.Expect100Continue = false;
var updateResponse = updateRequest.GetResponse() as HttpWebResponse;
if (updateResponse.StatusCode != HttpStatusCode.OK && updateResponse.StatusCode != HttpStatusCode.Continue)
{
throw new Exception(string.Format("An error occurred while invoking the Twitter REST API [Response Code: {0}]", updateResponse.StatusCode));
}
}
}
public static class Extensions
{
public static string ToTweet(this string s)
{
if (string.IsNullOrEmpty(s) || s.Length < 140)
{
return s;
}
return s.Substring(0, 137) + "...";
}
}
}
Configurați-l astfel:
Spune-NLog ansamblul conține țintă:
<extensions>
<add assembly="NLogExtensions"/>
</extensions>
Configurați ținta:
<targets>
<target name="twitter" type="TwitterTarget" TwitterUserName="yourtwittername" TwitterPassword="yourtwitterpassword" layout="${longdate} ${logger} ${level} ${message}" />
</targets>
Dacă cineva încearcă acest lucru și are succes, post înapoi cu concluziile tale.
Am vrut o modalitate de a pur și simplu și în mod automat erorile de raport (din utilizatorii de multe ori nu't) de la aplicațiile noastre. Cea mai simplă soluție ar putea veni cu un URL public - o pagină web care ar putea dura de intrare și magazin acesta la o baza de date care este trimis datele la eroare de aplicație. (Baza de date ar putea fi apoi verificat de un dev sau un script sa stiu daca sunt noi erori.)
Am scris o pagină web în PHP și a creat o bază de date mysql, utilizator, și de masă pentru a stoca datele. M-am decis asupra a patru variabile utilizator, un id și o amprentă de timp. Posibilele variabile (fie incluse în URL-ul sau ca POST de date) sunt:
msg
(mesaj - de exemplu Excepție a avut loc ...)dev
(producător - de exemplu, Pat)src
(sursa - acest lucru ar veni de la o variabilă referitoare la masina pe care aplicația a fost de funcționare, de exemplu, de Mediu.MachineName` sau asa ceva)log
(un fișier jurnal sau verbose mesaj)(Toate variabilele sunt opționale, dar nimic nu este raportat dacă nici unul dintre ele sunt set - deci, dacă tocmai ați vizitat site-ul URL-ul nimic nu este trimis la db.)
Pentru a trimite datele la URL-ul, am folosit NLog's `WebService țintă. (Notă, am avut câteva probleme cu această țintă la început. Nu a fost't până când m-am uitat la sursa care mi-am dat seama că mi url
ar putea sa nu se termine cu un /
.)
Toate în toate, l's nu este un sistem prost pentru păstrarea file pe aplicații externe. (Desigur, cel mai politicos lucru pe care să faceți este să: * informarea utilizatorilor** pe care le va fi de raportare, eventual date sensibile și să le dea o modalitate de a opta in/out.)
(Db utilizatorul are doar pentru a INTRODUCE privilegiilor pe aceasta un tabel în baza de date proprie.)
CREATE TABLE `reports` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`ts` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`applicationName` text,
`message` text,
`developer` text,
`source` text,
`logData` longtext,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='storage place for reports from external applications'
(PHP 5.3 sau 5.2 cu DOP activat, fisier este index.php
în /report
folder)
<?php
$app = $_REQUEST['app'];
$msg = $_REQUEST['msg'];
$dev = $_REQUEST['dev'];
$src = $_REQUEST['src'];
$log = $_REQUEST['log'];
$dbData =
array( ':app' => $app,
':msg' => $msg,
':dev' => $dev,
':src' => $src,
':log' => $log
);
//print_r($dbData); // For debugging only! This could allow XSS attacks.
if(isEmpty($dbData)) die("No data provided");
try {
$db = new PDO("mysql:host=$host;dbname=reporting", "reporter", $pass, array(
PDO::ATTR_PERSISTENT => true
));
$s = $db->prepare("INSERT INTO reporting.reports
(
applicationName,
message,
developer,
source,
logData
)
VALUES
(
:app,
:msg,
:dev,
:src,
:log
);"
);
$s->execute($dbData);
print "Added report to database";
} catch (PDOException $e) {
// Sensitive information can be displayed if this exception isn't handled
//print "Error!: " . $e->getMessage() . "<br/>";
die("PDO error");
}
function isEmpty($array = array()) {
foreach ($array as $element) {
if (!empty($element)) {
return false;
}
}
return true;
}
?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
throwExceptions="true" internalLogToConsole="true" internalLogLevel="Warn" internalLogFile="nlog.log">
<variable name="appTitle" value="My External App"/>
<variable name="csvPath" value="${specialfolder:folder=Desktop:file=${appTitle} log.csv}"/>
<variable name="developer" value="Pat"/>
<targets async="true">
<!--The following will keep the default number of log messages in a buffer and write out certain levels if there is an error and other levels if there is not. Messages that appeared before the error (in code) will be included, since they are buffered.-->
<wrapper-target xsi:type="BufferingWrapper" name="smartLog">
<wrapper-target xsi:type="PostFilteringWrapper">
<target xsi:type="File" fileName="${csvPath}"
archiveAboveSize="4194304" concurrentWrites="false" maxArchiveFiles="1" archiveNumbering="Sequence"
>
<layout xsi:type="CsvLayout" delimiter="Comma" withHeader="false">
<column name="time" layout="${longdate}" />
<column name="level" layout="${level:upperCase=true}"/>
<column name="message" layout="${message}" />
<column name="callsite" layout="${callsite:includeSourcePath=true}" />
<column name="stacktrace" layout="${stacktrace:topFrames=10}" />
<column name="exception" layout="${exception:format=ToString}"/>
<!--<column name="logger" layout="${logger}"/>-->
</layout>
</target>
<!--during normal execution only log certain messages-->
<defaultFilter>level >= LogLevel.Warn</defaultFilter>
<!--if there is at least one error, log everything from trace level-->
<when exists="level >= LogLevel.Error" filter="level >= LogLevel.Trace" />
</wrapper-target>
</wrapper-target>
<target xsi:type="WebService" name="web"
url="http://example.com/report"
methodName=""
namespace=""
protocol="HttpPost"
>
<parameter name="app" layout="${appTitle}"/>
<parameter name="msg" layout="${message}"/>
<parameter name="dev" layout="${developer}"/>
<parameter name="src" layout="${environment:variable=UserName} (${windows-identity}) on ${machinename} running os ${environment:variable=OSVersion} with CLR v${environment:variable=Version}"/>
<parameter name="log" layout="${file-contents:fileName=${csvPath}}"/>
</target>
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="smartLog"/>
<logger name="*" minlevel="Error" writeTo="web"/>
</rules>
</nlog>
Notă: pot exista unele probleme cu dimensiunea de fișier jurnal, dar nu am't dat seama de un mod simplu de a trunchia (de exemplu, o la *nix's coada
de comanda).
Mai ușor Mod de A vă Conecta fiecare nivel jurnal cu un aspect diferit, folosind Condiționată Machete
<variable name="VerboseLayout" value="${level:uppercase=true}: ${longdate} | ${logger} :
${when:when=level == LogLevel.Trace:inner=MONITOR_TRACE ${message}}
${when:when=level == LogLevel.Debug:inner=MONITOR_DEBUG ${message}}
${when:when=level == LogLevel.Info:inner=MONITOR_INFO ${message}}
${when:when=level == LogLevel.Warn:inner=MONITOR_WARN ${message}}
${when:when=level == LogLevel.Error:inner=MONITOR_ERROR ${message}}
${when:when=level == LogLevel.Fatal:inner=MONITOR_CRITICAL ${message}} |
${exception:format=tostring} | ${newline} ${newline}" />
Vezi https://github.com/NLog/NLog/wiki/When-Filter pentru sintaxa
Jurnal de Silverlight
Atunci când se utilizează NLog cu Silverlight puteți trimite urmă la partea de server prin intermediul prevăzută servicii web. Puteți scrie, de asemenea, de la un fișier local în Depozit Izolat, care vin la îndemână în cazul în care serverul web este indisponibil. A se vedea aici pentru detalii, de exemplu, folosesc ceva de genul asta pentru a te face o țintă:
namespace NLogTargets
{
[Target("IsolatedStorageTarget")]
public sealed class IsolatedStorageTarget : TargetWithLayout
{
IsolatedStorageFile _storageFile = null;
string _fileName = "Nlog.log"; // Default. Configurable through the 'filename' attribute in nlog.config
public IsolatedStorageTarget()
{
}
~IsolatedStorageTarget()
{
if (_storageFile != null)
{
_storageFile.Dispose();
_storageFile = null;
}
}
public string filename
{
set
{
_fileName = value;
}
get
{
return _fileName;
}
}
protected override void Write(LogEventInfo logEvent)
{
try
{
writeToIsolatedStorage(this.Layout.Render(logEvent));
}
catch (Exception e)
{
// Not much to do about his....
}
}
public void writeToIsolatedStorage(string msg)
{
if (_storageFile == null)
_storageFile = IsolatedStorageFile.GetUserStoreForApplication();
using (IsolatedStorageFile isolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
// The isolated storage is limited in size. So, when approaching the limit
// simply purge the log file. (Yeah yeah, the file should be circular, I know...)
if (_storageFile.AvailableFreeSpace < msg.Length * 100)
{
using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(_fileName, FileMode.Truncate, FileAccess.Write, isolatedStorage))
{ }
}
// Write to isolated storage
using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(_fileName, FileMode.Append, FileAccess.Write, isolatedStorage))
{
using (TextWriter writer = new StreamWriter(stream))
{
writer.WriteLine(msg);
}
}
}
}
}
}