I have this rule in my nlog.config. (There are other rules but this is the only one which ends up sending email, and therefore the only one to be debugged):
<rules>
<logger name="*cxml*" minlevel="Info" writeto="customer">
<filters>
<when condition="not equals('${aspnet-request:serverVariable=HTTP_HOST},'interestinghost.com')" action="ignore" />
</filters>
</logger>
</rules>
This should ensure that Info level messages, from the loggers with cxml in the logger name, should be written to the customer target; but when the HTTP_HOST server variable does not equal interestinghost.com, the Info message should be ignored. Thus only messages regarding interestinghost.com should end up being emailed.
The customer target sends an email, and has this definition. Notice that the layout includes the HTTP_HOST variable:
<target name="customer" xsi:type="Mail"
smtpServer="..." smtpPort="587" smtpAuthentication="Basic"
smtpUserName="..." smtpPassword="..." enableSsl="true"
from="..." to="..." cc="..."
subject="log from ${logger} ${longdate}"
layout="===================${aspnet-request:serverVariable=REQUEST_METHOD} ${aspnet-request:serverVariable=SERVER_PORT} ${aspnet-request:serverVariable=HTTP_HOST}${aspnet-request:serverVariable=URL} ${aspnet-request:serverVariable=QUERY_STRING} ${newline}${level} | ${longdate} | ${logger} ${stacktrace:topframes=5}| ${message} | ${exception:format=Message,ShortType,StackTrace}"
encoding="iso-8859-2" />
Nevertheless, my actual customer reports he is getting messages he shouldn't -- in particular, messages that are directed to another HTTP_HOST:
===================POST 80 boring.noninteresting.host/cxml/setup
Warn | 2016-06-15 13:33:38.4817 | application.page.logger => Page.ProcessRequest => Page.ProcessRequestMain => Control.LoadRecursive => cxmlSetup.Page_Load => LoggerImpl.Write| Sending cXML PunchOutSetupResponse message:
<?xml version="1.0" encoding="utf-16"?>
<!DOCTYPE cXML SYSTEM "http://xml.cxml.org/schemas/cXML/1.2.014/cXML.dtd">
<cXML xml:lang="en-US" payloadID....
How could this possibly be happening?
There's a missing single quote on your function, between the right brace and the comma.
↓↓
not equals('${aspnet-request:serverVariable=HTTP_HOST},'interestinghost.com')
Related
I'm using the following structure:
<target name="MailOnError" xsi:type="AutoFlushWrapper" condition="level >= LogLevel.Error" flushOnConditionOnly="true">
<target name="MailBuffer" xsi:type="BufferingWrapper" bufferSize="250" overflowAction="Discard">
<-- note: mail config has been removed -->
<target xsi:type="Mail"
name="email"
header="Error in ${processname}:${newline}
Machine: ${machinename}${newline}
Proces: ${processname}${newline}
User: ${environment-user}${newline}
${newline}
${newline}"
layout="${date:format=HH\:mm\:ss}: ${message} (in ${callsite})${newline}"
/>
</target>
This works almost fine to achieve my goal: receiving an e-mail on error, with the most recent log-entries embedded to have some context about the error.
It would be nice if I get the error on the top of my e-mail-message (or, even better, receive the log-entries in descending order).
How can this be done (if possible without writing my own custom target)?
You need something like this:
[Target("ReverseOrderWrapper", IsWrapper = true)]
public class ReverseOrderWrapper : WrapperTargetBase
{
protected override void Write(IList<AsyncLogEventInfo> logEvents)
{
// Some re oder logic
var reorderEvents = logEvents.Reverse().ToArray();
WrappedTarget.WriteAsyncLogEvents(reorderEvents);
}
}
Register (as soon as possible), NLog 4.7+ syntax
NLog.LogManager.Setup().SetupExtensions(s =>
s.RegisterTarget<ReverseOrderWrapper>("ReverseOrderWrapper")
);
Usage:
<target name="MailOnError" xsi:type="AutoFlushWrapper" condition="level >= LogLevel.Error" flushOnConditionOnly="true">
<target name="MailBuffer" xsi:type="BufferingWrapper" bufferSize="250"
overflowAction="Discard">
<target name="Reoder" xsi:type="ReverseOrderWrapper">
<-- note: mail config has been removed -->
<target xsi:type="Mail"
name="email"
header="Error in ${processname}:${newline}
Machine: ${machinename}${newline}
Proces: ${processname}${newline}
User: ${environment-user}${newline}
${newline}
${newline}"
layout="${date:format=HH\:mm\:ss}: ${message} (in ${callsite})${newline}"
/>
</target>
</target>
</target>
Basically I have this config for log4net:
<?xml version="1.0" encoding="utf-8"?>
<log4net>
<root>
<level value="INFO" />
<appender-ref ref="RollingFileAppenderWithDeletion" />
</root>
<appender name="RollingFileAppenderWithDeletion" type="Namespace.RollingFileAppenderWithDeletion">
<file type="log4net.Util.PatternString" value="Logs/%property{LogName}/log.%property{ServiceName}-PID-%processid_%date{yyyyMMdd}.log" />
<appendToFile value="true" />
<rollingStyle value="Date" />
<datePattern value="yyyyMMMdd" />
<maxSizeRollBackups value="10" />
<maximumFileSize value="50MB" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%-5level %date{dd MMM yyyy HH:mm:ss,ffff} [%thread] %exception - %message%newline"/>
</layout>
</appender>
</log4net>
Now I have a problem. Everytime log4net rolls log file, it doesn't change the date part of log file. For example if today's log file is - log.MyServiceName-PID-1234_20131208.log, tomorrow after rolling the file, name of the file won't change, so I will end up having a rolled back log file and active log file like this
log.QAService-PID-17584_20131208.log
log.QAService-PID-17584_20131208.log2013Dec08
What I would like to have is
log.QAService-PID-17584_20131209.log - active log file
log.QAService-PID-17584_20131208.log2013Dec08 - rolled back
Now I came across this post, but it didn't help me. In particular, if I remove date pattern from file, and set preserveLogFileNameExtension to true, I can't see date part on active log file anymore. If I go further and set staticLogFileName to false, I don't have active log files anymore, instead active log files are having the rolled back log file name pattern.
What am I missing here? How can I have right log file names generated after roll back.
I'm using log4net version 1.2.10.0. Unfortunately I can't upgrade it to newer version.
Update
This is custom implementation of RollingFileAppenderWithDeletion. It just cleans up old rolled back files after log file rolling took place.
public class RollingFileAppenderWithDeletion :RollingFileAppender
{
private IFileBurner m_fileBurner;
private const int checkMinutes = 1664;
public RollingFileAppenderWithDeletion()
{
m_fileBurner = FileBurner.Instance;
}
protected override void AdjustFileBeforeAppend()
{
base.AdjustFileBeforeAppend();
string path = base.File;
string directoryPath = Path.GetDirectoryName(path);
IDeletionRequirements requirements = new DeletionRequirements();
requirements.CheckEveryMinutes = checkMinutes;
requirements.DayLimit = MaxSizeRollBackups;
requirements.Directories = new List<string> { directoryPath };
m_fileBurner.ClearLogFiles(requirements);
}
}
You are using the type Namespace.RollingAppenderWithDeletion, which is definately not a standard appender. Try use the type log4net.Appender.RollingFileAppender and see if that works. When you use your custom appender you need to post some code, or find why the filename code is not called.
How could I use NLog to create a user-specific logfile the file name equivalent to the username? I understand that you can use variables in layouts, but the file name attribute is set at the level of the target. I would like to be able to do something like filename="C:\pathtologfiles\${myApp:user}.txt" and in the calling class, ClassLogger.Debug("user did something", thisUser.Username).
Here's how I ended up doing it (in VB, unfortunately.) You would of course need to implement your own _UsernameIsMeaningfulInThisContext and _GetUsername.
In nlog config
<variable name="AppLogDir" value="C:\inetpub\ApplicationLogging\MyApplication" />
...
<targets>
...
<target name="UserSpecificLogfile"
xsi:type="File"
fileName="${AppLogDir}\Users\AppUser_${event-context:item=AppUsername}.txt"
createDirs="true" />
</targets>
<rules>
...
<logger name="*" minLevel="Trace" maxLevel="Fatal" writeTo="UserSpecificLogfile" />
</rules>
In calling class
Private Property _ClassLogger As Logger = LogManager.GetCurrentClassLogger()
...
Private Sub _LogMaybeUser(ByVal nLogLevel As NLog.LogLevel, ByVal nMsg As String)
{
Dim logfileUser As String = "Unknown"
If _UsernameIsMeaningfulInThisContext() { logfileUser = _GetUsername() }
Dim logInfo As New LogEventInfo(nLogLevel, "", nMsg)
logInfo.Properties("AppUsername") = logfileUser
ClassLogger.Log(logInfo)
}
I want to write a stand alone string (like a horizontal line or delimiter) to the log file.
If I use
string delimiter = "------------------------------------------------";
_logger.Info(delimiter);
then in the log file I get:
2013-01-08 15:58:54.4008 INFO ------------------------------------------------
I do not want the extra information at the beginning of the line.
Is there a way to write a separator like this with NLog? I checked the NLog wiki, but didn't find anything.
See my answer to a similar question here:
Nlog - Generating Header Section for a log file
To summarize, I propose defining another logging target. If you are logging to a file, define a second file target, pointing to the same file, but with a different layout. Define the layout so that it has the format that you want. You could define the layout to be hardcoded to the header value that you want ("---------------" in your case), or your could define the layout to only log the message and then you could pass the layout to it.
Here is the shortest thing that might work. Note, I cut and pasted from the answer linked above and modified slightly for your case. I did not test it.
Define the layouts:
<variable name="HeaderLayout" value="${message}"/>
<variable name="FileLayout" value="${longdate} | ${logger} | ${level} | ${message}" />
Define the targets using the layouts:
<target name="fileHeader" xsi:type="File" fileName="xxx.log" layout="${HeaderLayout}" />
<target name="file" xsi:type="File" fileName="xxx.log" layout="${InfoLayout}" />
Define the rules/loggers:
<rules>
<logger name="headerlogger" minlevel="Trace" writeTo="fileHeader" final="true" />
<logger name="*" minlevel="Trace" writeTo="file" />
</rules>
Use the loggers:
public class MyClass
{
private static Logger logger = LogManager.GetCurrentClassLogger();
private static Logger headerLogger = LogManager.GetLogger("headerlogger");
public void DoSomething()
{
headerLogger.Info("---------------------");
logger.Info("Inside DoSomething");
headerLogger.Info("---------------------");
}
}
Alternatively, you could define the layout such that it has the header definition in it:
<variable name="HeaderLayout" value="-----------------------------"/>
<variable name="FileLayout" value="${longdate} | ${logger} | ${level} | ${message}" />
Then you would use it like this:
Logger headerLogger = LogManager.GetLogger("headerlogger"); //Assuming same rules/loggers definition as above.
headerLogger.Info("It doesn't matter what you put here because the layout has the header message hardcoded");
You could write a helper function so that you don't have to deal explicitly with the header logger:
public void WriteHeader()
{
LogManager.GetLogger("headerlogger").Info("This string does not matter");
}
I think that this should give you some good insight into how you can accomplish what you are trying to do.
Hope this helps!
Good luck!
Here's my appender configuration from my app.config. This just prints out the literal string instead of translating it to the date (i.e., it literally prints "[START: %date{MM/dd/yy HH:mm} ]").
<appender name="RollingLogFileAppender"
type="log4net.Appender.RollingFileAppender">
<file value="C:\somelog" />
<appendToFile value="true" />
<rollingStyle value="Date" />
<datePattern value="-yyyy-MM-dd'.txt'" />
<layout type="log4net.Layout.PatternLayout">
<header value="[START: %date{MM/dd/yy HH:mm} ]
" />
<conversionPattern value="%date{yyyy-MM-dd HH:mm:ss} - %message" />
<footer value="[END]
" />
</layout>
</appender>
How can I get this to print the date/time in the header?
Answer from here.
<layout type="log4net.Layout.DynamicPatternLayout">
...
<header value="[BEGIN LOGGING AT %date]%newline"/>
<footer value="[END LOGGING AT %date]%newline"/>
...
</layout>
Works for me like a charm. No need to write a piece of code.
Also in projects we usually use:
<header type="log4net.Util.PatternString" value="Our Application Name version %property{Assembly.Version}, .NET version %property{Runtime.Version}, %date ***%newline"/>
Take a look at PatternString doc also.
Also I've noticed that log file won't appear until you write first log statement.
An easy way to do this is to subclass log4net.Layout.PatternLayout and override Header and Footer. Then you can add whatever you want to your Header: date stamp, machine name, user name, assembly version, or whatever your heart desires:
using System;
using log4net.Layout;
namespace MyAssembly
{
class MyPatternLayout : PatternLayout
{
public override string Header
{
get
{
var dateString = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
return string.Format("[START: {0} ]\r\n", dateString);
}
}
}
}
Include this new class in your assembly, and use the new type in your file, like this:
<layout type="MyAssembly.MyPatternLayout">
<conversionPattern value="%date{yyyy-MM-dd HH:mm:ss} - %message" />
</layout>
Since you overrode Header and Footer, you don't even need to add it here. The Header will be added every time your application starts, and every time the file rolls over.
Building on #Wizou's comment. See the DynamicPatternLayout Class documentation.
I'm actually using this difference in behaviour to have a start and end time
One important difference between PatternLayout and DynamicPatternLayout is the treatment of the Header and Footer parameters in the configuration. The Header and Footer parameters for DynamicPatternLayout must be syntactically in the form of a PatternString, but should not be marked as type log4net.Util.PatternString. Doing so causes the pattern to be statically converted at configuration time and causes DynamicPatternLayout to perform the same as PatternLayout.
Setting the type on Header to PatternString but leaving Footer as dynamic
<layout type="log4net.Layout.DynamicPatternLayout">
<param name="Header" value="%newline**** Trace Opened Local: %date{yyyy-MM-dd HH:mm:ss.fff} UTC: %utcdate{yyyy-MM-dd HH:mm:ss.fff} ****%newline" type="log4net.Util.PatternString" />
<param name="Footer" value="**** Trace Closed %date{yyyy-MM-dd HH:mm:ss.fff} ****%newline" />
</layout>
I encountered this problem and a quick workaround I used is to just use a fixed header and footer. Then do this as soon as you have the ILog object:
log.Info(string.Format("[START: {0} ]\r\n", dateString))
So just under the header you will get the information you desire.
E.g
===Start===
[START: 2012-02-23 12:12:12 ]
logs...