I was recently studying documentation on TraceSource. Microsift says that TraceSource is a new way and should be used instead of old Trace class.
// create single TraceSource instance to be used for logging
static TraceSource ts = new TraceSource("TraceTest");
// somewhere in the code
ts.TraceEvent(TraceEventType.Warning, 2, "File Test not found");
Now my question. You have large project with several assemblies where you have lots of classes. Say you wanna trace specific bit of functionality that is spread across classes. Obvious idea is that you need to create one specific TraceSource .
1) To work with Tracesource I need to create instance first. What is MS thinking about sharing this instance across various classes or assemblies? Should I create one dummy class with static singleton property? What are you doing in that case.
2) Why do I need TraceSource instance? Every propery is described in the configuration file. The old logic based on Trace class did not require some instance and provided the way to work with static methods only.
*1. Just define the TraceSource in each class where you want to use it. You can make the TraceSource static so that it shared among all instances of the class you define it in. No need to share the instance among all classes (types) that need the "same" TraceSource. Each time you decleare a new TraceSource (TraceSource ts = new TraceSource("somename"); instance, you get a new TraceSource object, but it references the same config information. So, if you create a new TraceSource in each of several classes and you use the same name for each one, you will get different instances of TraceSource, but they will all be configured the same. In short, there is no need to try to share the TraceSource instances among classes. There is also no need to create a dummy class with a static singleton. See my examples below. I have also included several more links from here on SO that describe how to work with TraceSources.
//
// In this example, tracing in classes A and B is controlled by the "TraceTest" TraceSource
// in the app.config file. Tracing in class C is controlled by the "TraceTestTwo"
// TraceSource in the app.config.
//
// In addition to using different TraceSource names, you can also use SourceSwitches
// (in the app.config). See some examples of app.config in the
// "turning-tracing-off-via-app-config" link below.
//
public class A
{
private static readonly TraceSource ts = new TraceSource("TraceTest");
public void DoSomething()
{
ts.TraceEvent(TraceEventType.Warning, 2, "File Test not found");
}
}
public class B
{
//
//Use the same config info for TraceTest in this class
//It's ok to use a different instance of TraceSource, but with the same name,
//in this class, the new instance will be configured based on the params in the
//app.config file.
//
private static readonly TraceSource ts = new TraceSource("TraceTest");
public void DoSomething()
{
ts.TraceEvent(TraceEventType.Warning, 2, "File Test not found");
}
}
public class C
{
//
//Use a different TraceSource in this class.
//
private static readonly TraceSource ts = new TraceSource("TraceTestTwo");
public void DoSomething()
{
ts.TraceEvent(TraceEventType.Warning, 2, "File Test not found");
}
}
*2. One benefit to using multiple TraceSources is that you have more granular control over your tracing. You can trace via "TraceTest" at one level (or not at all) and via "TraceTestTwo" at a different level (or, again, not at all). You can send each TraceSource to its own TraceListener or send all to the same TraceListener, or mix and match. Compare the ability to tailor the configuration of individual TraceSources to the limitation of only using the static methods on the Trace class. You can configure where the "trace" information goes (which TraceListener(s)) or the level of the "trace" information, but you cannot control the level per class or per functional area like you can when using TraceSources. Finally, one more benefit to multiple TraceSources is the "free" context information that you can get in your output. By default (or optionally, I can't remember), the TraceListener will log the name of the TraceSource that logged a message. So, you can look at that line in your output and get some idea of the class or functional area where it came from without having to put a log of contextual information in the call site. In the code examples above, the trace output from classes A and B will be tagged with "TraceTest" and the trace output from class B will be tagged with "TraceTestTwo".
Please forgive the link bombardment below, but I have posted some pretty good information (if I do say so myself!) about TraceSource and System.Diagnostics in the past.
If you are going to use TraceSource, consider using the library mentioned in this SO post for formatting your output like log4net/NLog:
Does the .Net TraceSource/TraceListener framework have something similar to log4net's Formatters?
See my answer in this post for more info on using TraceSource and some ideas on how you can improve your "TraceSource experience".
More info on TraceSource: Add Trace methods to System.Diagnostics.TraceListener
More info on TraceSource: System.Diagnostics.Debug namespace vs Other logging solutions (log4net, MS Enterprise Library, etc.)
More info on TraceSource: Turning tracing off via app.config
Related
I have built a python library that uses C# code(which is built and stored as a dll), using pythonnet. In that library, I generate logs using the python logger.
mylibrary.py
logger = logging.getLogger('mylibrary')
logger.info('logging from my library')
The root logger is configured from the user code. For example, the handlers for the root logger is set by the user using logger's "addhandler()" method specifying the format, output file etc. Inside my library, I just log (logger.info()...) without configuring anything and the root handler set by the user takes care of writing this to the file.
usercode.py
root_logger = getLogger()
root_logger.addHandler(FileHandler('abc.log'))
root_handler.setFormat(...)
The user can control what my library can log by setting the level of the logger used by my library. The line below in usercode.py sets the logging level of my library's logger to critical so that the library can't log anything below it (logger.info() won't get into abc.log).
getLogger('mylibrary').setLevel(CRITICAL)
The problem comes now. Since I am using C# code in my library,
I want to capture the C# logs into abc.log
I also want to configure the C# log just like I did for python logs
So the the line
getLogger('mylibrary').setLevel(CRITICAL)
in usercode.py should now make sure that only the critical logs in both the python as well as C# get into abc.log
Is there a way to achieve this?
No, you cannot log from both Python and C# at the same time to the same file. The reason for this is that Python's logging (and likely the C# logging too) is not equipped for concurrent logging - even if the log file is not 'locked' by one of them, there is a chance of getting different logs mixed together due to multiple writers.
If you do not own the C# dll you're probably out of luck - unless it would allow you to configure the log file/level from a C# program, there is no magic that Python can do to fix it. However, if you control the source and can build a new dll, consider changing the C# class to allow you to pass in a delegate/lambda (assuming this is implemented in PythonNet), which will simply call back into Python's logger function.
Example:
c# code:
public class CoolImportedFeature
{
private readonly Action<string> LogCallback;
public CoolImportedFeature(string inputA, int inputB, Action<string, string> logCallback)
{
LogCallback = logCallback;
// do other constructor stuff
}
public void SomeMethod()
{
// do something
LogCallback("critical", "An error occurred");
}
}
python code:
def log_callback(log_level, message):
getattr(logger, log_level)(message)
import CoolImportedFeature
feat = CoolImportedFeature("hello", 1, log_callback)
feat.SomeMethod()
Something like that - there is no magic translation between Python's log levels and C#'s, so you will need to do some translation there (or the getattr reflection I used above).
We are wrapping an existing logging library in our own logging service in a C# application to surround it with predefined methods for certain logging situations.
public class LoggingBlockLoggingService : ILoggingService
{
private LogWriter writer;
public LoggingBlockLoggingService(LogWriter writer)
{
this.writer = writer;
}
....//logging convenience methods, LogWarning(), etc...
}
I would like to modify this implementation so that it takes in the Type of the class that instantiates it (the underlying logger, LogWriter in this case, would be a singleton). So either make this implementation (and the interface ILoggingService) generic:
public class LoggingBlockLoggingService<T> : ILoggingService<T>
{
...
private string typeName = typeof(T).FulName;
...
Or add an additional constructor parameter:
public class LoggingBlockLoggingService : ILoggingService
{
private LogWriter writer;
private string typeName;
public LoggingBlockLoggingService(LogWriter writer, Type type)
{
this.writer = writer;
this.typeName = type.FullName;
}
....//Include the typeName in the logs so we know the class that is logging.
}
Is there a way to configure this once in Unity when registering our types? I'd like to avoid having to add an entry for every class that wants to log. Ideally, if someone wants to add logging to a class in our project, they'd just add an ILoggingService to the constructor of the class they are working in, instead of adding another line to our unity config to register each class they are working on.
We are using run time/code configuration, not XML
Yes, you can use:
container.RegisterType(typeof(IMyGenericInterface<>), typeof(MyConcreteGenericClass<>));
In your case, when there's simple direct generic-param--to--generic-param mapping the Unity maybe actually handles that, but I doubt that any more advanced cases are not handled, because something at some point of time must provide the mapping of generic-parameters between the types (liek reordering Key-Value vs. Value-Key etc).
If Dave's answer is not enough, I'm fairly sure that you could write a plugin to Unity/ObjectBuilder that would register a new strategy or set of strategies that would cover just any type mapping you would like, including automatic assembly scanning or materialization of generics.
See the series of articles at http://www.orbifold.net/default/unity-objectbuilder-part-ii/ and the section near
Context.Strategies.AddNew< buildkeymappingstrategy >(UnityBuildStage.TypeMapping);
I have a class in c# to help me log errors (ErrorClass).
The class has 3 methods. Log Error to: File System, Windows Event,
Email.
2 of the 3 methods require settings like "to email", or "directory path".
Settings are stored in the registry
I use dependency injection to instantiate the RegistryClass inside the ErrorClass
.
This is how I instantiate the ErrorHandle Class inside the Registry Class
ErrorHandle _ErrorHandle = new ErrorHandle();
And here is how I instantiate the Registry Class inside the ErrorHandle Class
RegistryTools _GetRegistry = new RegistryTools();
I have a class to help me retrieve values from the registry (RegistryClass)
The registry class needs to handle errors
I use dependency injection to instantiate the errorClass inside the RegistryClass
When I use dependency injection in both classes, an Endless LOOP is created when there is an error.
What is the suggested way or best practice of handling this situation:
Should I access the registry inside the ErrorClass?
Should I not ErrorHandle the RegistryClass?
Should I create a separate ErroHandle procedure for the
RegistryClass?
Don't re-invent this wheel. There is a tried and tested open source logging framework for .NET available, log4net. If you feel the need to use DI with it, you can do that too. log4net uses an XML file for configuration, which is much more accessible and less fraught with peril than dealing with the registry. It also swallows its own errors and makes them accessible via a debugging trace.
What mechanism are you using for DI? If you use setter injection there should be nothing to stop you doing something like:
var logger = new ErrorClass();
var registry = new RegistryClass();
logger.Registry = registry;
registry.Logger = logger;
My current solution has 3 project with 2 app.config (one for common settings and another for service settings). As of now I'm simply creating static classes to act as a mediator to access values. I do this so I don't have to write ConfigurationManager.AppSettings["SomeKey"] everywhere. This works fine until you want to access an app.config file from a different project.
Here is what I'm currently doing (all properties omitted for brevity).
public class ServiceConfiguration
{
public static readonly string SyncEvery = ConfigurationManager.AppSettings["SyncEveryMinutes"];
}
How can I access an app.config file located in another project? I thought perhaps setting VS to copy the file to the output directory would do the trick however my configuration object is still null.
I can't imaging many good reasons to read another app's configuration in the first place, it just opens a can of worms that isn't worth dealing with.
Expose a class that exposes the project's configured values as properties, and access them from a consuming class.
public class FirstProjectClass
{
public static int SyncEveryMinutes
{
get { return (int)ConfigurationManager.AppSetting["SyncEveryMinutes"] };
}
}
public class SecondProjectClass
{
public void ShowConfigedValue()
{
Console.Writeline("Syncing every {0} minutes", FirstProjectClass.SyncEveryMinutes);
}
}
if you've got complex configuration requirements you can also look into custom configuration sections
ConfigurationManager.OpenExeConfiguration can be helpfull:
http://msdn.microsoft.com/en-us/library/system.configuration.configurationmanager.openexeconfiguration.aspx
Also: what Jason said - it is usually a bad idea.
Background
I am writing a class library assembly in C# .NET 3.5 which is used for integration with other applications including third-party Commercial-Off-The-Shelf (COTS) tools. Therefore, sometimes this class library will be called by applications (EXEs) that I control while other times it will be called by other DLLs or applications that I do not control.
Assumptions
I am using C# 3.0, .NET 3.5 SP1, and Visual Studio 2008 SP1
I am using log4net 1.2.10.0 or greater
Constraints
Any solution must:
Allow for the class library to enable and configure logging via it's own configuration file, if the calling application does not configure log4net.
Allow for the class library to enable and configuring logging via the calling applications configuration, if it specifies log4net information
OR
Allow for the class library to enable and configuring logging using it's own configuration file at all times.
Problem
When my stand-alone class library is called by a DLL or application that I do not control (such as a third-party COTS tool) and which doesn't specify log4net configuration information, my class library is unable to do any of it's logging.
Question
How do you configure and enable log4net for a stand-alone class library assembly so that it will log regardless if the calling application provides log4net configuration?
Solution 1
A solution for the first set of constraints is to basically wrap the log4net.LogManager into your own custom LogManager class like Jacob, Jeroen, and McWafflestix have suggested (see code below).
Unfortunately, the log4net.LogManager class is static and C# doesn't support static inheritance, so you couldn't simply inherit from it and override the GetLogger method.
There aren't too many methods in the log4net.LogManager class however, so this is certainly a possibility.
The other drawback to this solution is that if you have an existing codebase (which I do in my case) you would have to replace all existing calls to log4net.LogManager with your wrapper class. Not a big deal with today's refactoring tools however.
For my project, these drawbacks outweighed the benefits of using a logging configuration supplied by the calling application so, I went with Solution 2.
Code
First, you need a LogManager wrapper class:
using System;
using System.IO;
using log4net;
using log4net.Config;
namespace MyApplication.Logging
{
//// TODO: Implement the additional GetLogger method signatures and log4net.LogManager methods that are not seen below.
public static class LogManagerWrapper
{
private static readonly string LOG_CONFIG_FILE= #"path\to\log4net.config";
public static ILog GetLogger(Type type)
{
// If no loggers have been created, load our own.
if(LogManager.GetCurrentLoggers().Length == 0)
{
LoadConfig();
}
return LogManager.GetLogger(type);
}
private void LoadConfig()
{
//// TODO: Do exception handling for File access issues and supply sane defaults if it's unavailable.
XmlConfigurator.ConfigureAndWatch(new FileInfo(LOG_CONFIG_FILE));
}
}
Then in your classes, instead of:
private static readonly ILog log = LogManager.GetLogger(typeof(MyApp));
Use:
private static readonly ILog log = LogManagerWrapper.GetLogger(typeof(MyApp));
Solution 2
For my purposes, I have decided to settle on a solution that meets the second set of constraints. See the code below for my solution.
From the Apache log4net document:
"An assembly may choose to utilize a named logging repository rather than the default repository. This completely separates the logging for the assembly from the rest of the application. This can be very useful to component developers that wish to use log4net for their components but do not want to require that all the applications that use their component are aware of log4net. It also means that their debugging configuration is separated from the applications configuration. The assembly should specify the RepositoryAttribute to set its logging repository."
Code
I placed the following lines in the AssemblyInfo.cs file of my class library:
// Log4Net configuration file location
[assembly: log4net.Config.Repository("CompanyName.IntegrationLibName")]
[assembly: log4net.Config.XmlConfigurator(ConfigFile = "CompanyName.IntegrationLibName.config", Watch = true)]
References
LogManagerMembers
Jacob's Answer
Jeroen's Answer
McWafflestix's Answer
log4net Manual - Repositories
log4NET from a class library (dll)
You can probably code something around the XmlConfigurator class:
public static class MyLogManager
{
// for illustration, you should configure this somewhere else...
private static string configFile = #"path\to\log4net.config";
public static ILog GetLogger(Type type)
{
if(log4net.LogManager.GetCurrentLoggers().Length == 0)
{
// load logger config with XmlConfigurator
log4net.Config.XmlConfigurator.Configure(configFile);
}
return LogManager.GetLogger(type);
}
}
Then in your classes, instead of:
private static readonly ILog log = LogManager.GetLogger(typeof(MyApp));
Use:
private static readonly ILog log = MyLogManager.GetLogger(typeof(MyApp));
Of course, it would be preferable to make this class a service and dynamically configure it with the IoC container of your choice, but you get the idea?
EDIT: Fixed Count() problem pointed out in comments.
In your code you can check if there are any loggers via
log4net.LogManager.GetCurrentLoggers().Count()
You could then for example use an XmlConfigurator to load a default configuration from a file:
log4net.Config.XmlConfigurator.Configure(configFile)
You could do the initialization in a static or regular constructor.
class Sample
{
private static readonly log4net.ILog LOG;
static Sample()
{
if (log4net.LogManager.GetCurrentLoggers().Count() == 0)
{
loadConfig();
}
LOG = log4net.LogManager.GetLogger(typeof(Sample));
}
private static void loadConfig()
{
/* Load your config file here */
}
public void YourMethod()
{
LOG.Info("Your messages");
}
}
In your standalone class library, have a singleton which loads the log4net configuration file using the log4net.Config.XmlConfigurator.
Specifically, you can define all of your code to use your own custom logging class; this class can just be a simple wrapper of the log4net logging calls, with one addition; make a static member which contains the log information you want to log to; initialize that with a call to the XmlConfigurator in the static constructor for that class. That's all you have to do.
You can find a good description here:
log4net: A quick start guide
As the article describes, to configure it fore each assembly separately, create an XML file for your assembly named AssemblyName.dll.log4net and place the following XML code into it:
<?xml version="1.0" encoding="utf-8"?>
<log4net debug="false">
<appender name="XmlSchemaFileAppender" type="log4net.Appender.FileAppender">
<file value="AppLog.xml" />
<appendToFile value="true" />
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<layout type="log4net.Layout.XmlLayout" />
</appender>
<root>
<level value="WARN" />
<appender-ref ref="XmlSchemaFileAppender" />
</root>
</log4net>
It further describes, to instantiate a new logger, simply declare it as a variable for the entire class as follows:
public class LogSample
{
private static readonly log4net.ILog Log
= log4net.LogManager.GetLogger(typeof(LogSample));
// Class Methods Go Here...
}
You can then use the private variable Log inside your class like:
Log.Info("Sample message");
Likewise you can use Log.Error("Error occurred while running myMethod", ex) to log errors along with the exception details.
What I found is the following:
Don't forget to call log4net.Config.XmlConfigurator.Configure(); to activate your configuration
If you need to know the path of the file(s) written, here some code how to obtain it from Log4Net
I hope this helps.
This works for me for a shared library
protected static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.Assembly.GetExecutingAssembly().GetType());