-
Notifications
You must be signed in to change notification settings - Fork 26
Basics
Once you added some of the tracer logger adapters to your project it will do the weaving on every build using Fody. It will add trace enter and leave logs and it will rewrite static log calls.
Trace enter and leave are trace level log calls at the beginning and at the end of the method. Enter logs the location (class name, method name) and all arguments. Trace leave will log the return value (if any) and the time spent in the method. Both enter and leave handle ByRef and Out arguments properly. Trace leave also logs if the method exits with an unhandled exception.
The default configuration will trace public methods of public classes. If you wish to trace non public methods and/or classes you can change the behaviour by either changing the configuration in FodyWeavers.xml or applying attributes to classes and methods.
The FodyWeavers.xml can contain TraceOn
and NoTrace
elements which control the way tracing is used in the assembly. For example an xml element <TraceOn class="public" method="public" />
means that it will turn on trace logging for public (or more visible) methods in public (or more visible) classes. The possible attribute values are Public, Internal, Protected, Private
where a value means same or higher visibility. TraceOn
element also has an optional namespace attribute where you can define the scope on which the rule is applied. So for example <TraceOn namespace="Rootnamespace.Other" class="public" method="public" />
will trace all public method in public classes in the Rootnamespace.Other namespace.
NoTrace
elements specifies that methods in the given namespace are not traced at all. So an <NoTrace namespace="Root" />
will exclude all methods in all classes in Root namespace (but not in Root.Inner
namespace).
When defining the namespace there are 3 options to choose from:
- specify an exact namespace (e.g. Mynamespace.Inner)
- specify that all child namespaces of a namespace using .* at the end (e.g. Mynamespace.*)
- specify self and all child namespaces of a namespace using +* at the end (e.g. Mynamespace+*)
When multiple elements are defined more specific namespace scope definition trumps the less specific. So Mynamespace.Inner
is stronger than Mynamespace.*
An other way to influence trace logging is to use the trace attributes. We have two of them:
- TraceOn
- NoTrace
If you put [NoTrace]
on a method that method will not be traced. If you put it on a class none of its methods will be traced.
[NoTrace]
public void DontTraceMe()
{
// ... your code ...
}
If the TraceOn
attribute is put on a method it means that the method should be traced.
If you put TraceOn
a class you should specify on which visibility level you want tracing to take place. The possible values are Public, Internal, Protected, Private
where a value means same or higher visibility. So
[TraceOn(Protected)]
public class MyClass
{
// ... your code ...
}
will trace everything except private methods.
You might wonder what happens if for a given method there are multiple trace configurations are specified. Tracer processes them taking into account the hierarchy. So an attribute on a method always overrides attributes on classes or the global config in FodyWeavers.xml. Class attribute will override the global config. For nested classes the attribute on an inner class overrides the attribute on the outer class. Take this example:
[TraceOn(TraceTarget.Private)]
public class LogAllPrivateMethods
{
[NoTrace]
private void VeryFrequentlyCalledUnitrestingMethod()
{ }
}
The global is set to public/public so TraceOn
overrides to have all methods traced. But we don't want to trace that VeryFrequentlyCalledUnitrestingMethod
method so we use [NoTrace]
.
[NoTrace]
and [TraceOn]
attributes can also be applied on properties in which case they influence the tracing of getters and setters.
[NoTrace]
can be applied to method parameters to avoid logging its value. To skip return value logging use [NoReturnTrace]
on the method.
[NoReturnTrace]
private int DontLogReturnAndSecondParameter(string firstParam, [NoTrace] string secondParam)
{ }
}
By default trace rewrite exclude constructors and include properties. If you wish to change this behaviour there are two global flags on Tracer element (in FodyWeavers.xml) which can be used.
-
traceConstructors
true/false -
traceProperties
true/false
Another feature of the Tracer is to let you use a single static log class all over your project without the need to create the actual loggers. It will create the loggers for each class and change the static call to a proper instance call. It also changes static property getter calls to instance getter calls. The static methods that will be rewritten are defined in the adapter so it can vary from one adapter to the other. The configuration contains the name of the class which contains the static log methods and also a reference to the adapter (see next section for config details). The fact that the static log methods are not fixed gives you versatility when you build your own adapter.
The Tracer.Log4Net.Fody adapter supports the general log4net log methods on a class called Log so you can use any of these
public static void Debug(object message) { }
public static void Debug(object message, Exception exception) { }
public static void DebugFormat(string format, params object[] args) { }
public static void DebugFormat(string format, object arg0) { }
public static void DebugFormat(string format, object arg0, object arg1) { }
public static void DebugFormat(string format, object arg0, object arg1, object arg2) { }
public static void DebugFormat(IFormatProvider provider, string format, params object[] args) { }
public static void Info(object message) { }
public static void Info(object message, Exception exception){ }
public static void InfoFormat(string format, params object[] args){ }
public static void InfoFormat(string format, object arg0){ }
public static void InfoFormat(string format, object arg0, object arg1){ }
public static void InfoFormat(string format, object arg0, object arg1, object arg2){ }
public static void InfoFormat(IFormatProvider provider, string format, params object[] args){ }
public static void Warn(object message){ }
public static void Warn(object message, Exception exception){ }
public static void WarnFormat(string format, params object[] args){ }
public static void WarnFormat(string format, object arg0){ }
public static void WarnFormat(string format, object arg0, object arg1){ }
public static void WarnFormat(string format, object arg0, object arg1, object arg2){ }
public static void WarnFormat(IFormatProvider provider, string format, params object[] args){ }
public static void Error(object message){ }
public static void Error(object message, Exception exception){ }
public static void ErrorFormat(string format, params object[] args){ }
public static void ErrorFormat(string format, object arg0){ }
public static void ErrorFormat(string format, object arg0, object arg1){ }
public static void ErrorFormat(string format, object arg0, object arg1, object arg2){ }
public static void ErrorFormat(IFormatProvider provider, string format, params object[] args){ }
public static void Fatal(object message){ }
public static void Fatal(object message, Exception exception){ }
public static void FatalFormat(string format, params object[] args){ }
public static void FatalFormat(string format, object arg0){ }
public static void FatalFormat(string format, object arg0, object arg1){ }
public static void FatalFormat(string format, object arg0, object arg1, object arg2){ }
public static void FatalFormat(IFormatProvider provider, string format, params object[] args){ }
public static bool IsDebugEnabled { get; }
public static bool IsInfoEnabled { get; }
public static bool IsWarnEnabled { get; }
public static bool IsErrorEnabled { get; }
public static bool IsFatalEnabled { get; }
Static property rewrites let you use conditional logging which will be rewritten properly to instance calls.
if (Log.IsDebugEnabled)
{
Log.Debug(CostlyMethod(...));
}
will be rewritten to
if (_log.IsDebugEnabled)
{
_log.Debug("methodInfo", CostlyMethod(...));
}
This is the section the tracer adapter adds to FodyWeaver.xml.
<Tracer adapterAssembly="Tracer.Log4Net"
logManager="Tracer.Log4Net.Adapters.LogManagerAdapter"
logger="Tracer.Log4Net.Adapters.LoggerAdapter"
staticLogger="Tracer.Log4Net.Log"
traceConstructors="true"
traceProperties="false">
<TraceOn namespace="Root+*" class="public" method="public" />
<NoTrace namespace="Root.Generated" />
</Tracer>
Child elements of the Tracer
element can be TraceOn
and NoTrace
. TraceOn
element lets you specify the global trace enter/leave logging targets. The possible values are: public, internal, protected, private or none. Except for 'none' the value indicates that same or higher visibility is traced. Namespace attribute defines the scope of the rule. It can be either a specific namespace, child namespaces (using .) or self and children (using +). NoTrace
excludes the specified namespace from logging. For TraceOn
namespace is optional defaulting to every namespace in the assembly.
The attributes on the Tracer
element specify all required information about the actual adapter. You don't need to edit or modify these except if you build your own adapter. Nevertheless, just for the sake of information: The adapterAssembly
should contain the adapter's assembly name (can be full name with version). The logManager
and logger
contains the actual adapters and staticLogger
is the class that contains the static methods that will be rewritten. An optional traceConstructors
attribute can be used to turn on trace enter and leave logging for constructors as well. The tracing of property getters and setters can be influenced with the traceProperties
attribute.
The rewrite gets the location information and builds into the calls at weaving time. So the logger doesn't need to do costly stack analysis to find out the location as it is embedded into the call. This speeds up the logging pretty much. Compared to bare log4net logging it is 3-4 times faster.