views:

64

answers:

5

Hi all,

I've made my Logger, that logs a string, a static class with a static so I can call it from my entire project without having to make an instance of it. quite nice, but I want to make it run in a seperate thread, since accessing the file costs time

is that possible somehow and what's the best way to do it?

Its a bit of a short discription, but I hope the idea is clear. if not, please let me know.

Thanks in advance!

btw: any other improvements on my code are welcome as well, i have the feeling not everthing is as efficient as it can be:

internal static class MainLogger
    {
        internal static void LogStringToFile(string logText)
        {
            DateTime timestamp = DateTime.Now;
            string str = timestamp.ToString("dd-MM-yy  HH:mm:ss ", CultureInfo.InvariantCulture) + "\t" + logText + "\n";
            const string filename = Constants.LOG_FILENAME;
            FileInfo fileInfo = new FileInfo(filename);
            if (fileInfo.Exists)
            {
                if (fileInfo.Length > Constants.LOG_FILESIZE)
                {
                    File.Create(filename).Dispose();
                }
            }
            else
            {
                File.Create(filename).Dispose();
            }
            int i = 0;
            while(true)
            {
                try
                {
                    using (StreamWriter writer = File.AppendText(filename))
                    {
                        writer.WriteLine(str);
                    }
                    break;
                }
                catch (IOException)
                {
                    Thread.Sleep(10);
                    i++;
                    if (i >= 8)
                    {
                        throw new IOException("Log file \"" + Constants.LOG_FILENAME + "\" not accessible after 5 tries");
                    }
                }
            }
        }
    }
enter code here
+3  A: 

sry, but you may not reinvent the wheel:
choose log4net (or any other (enterprise) logging-engine) as your logger!

Andreas Niedermair
I agree. This problem has been solved.
drachenstern
I absolutely agree on not writing a new logger from scratch. But I am not sure if log4net (or something similar) supports running the I/O operations on a separate thread, if they use asynchronous I/O or what ever. So it may be valid to stack a bit of code on top of an existing logger to achieve this.
Daniel Brückner
it is supported, as log4net as an internal queue ... we use log4net in various multi-threaded scenarios, and it works!
Andreas Niedermair
Thanks, obviously I'm not the only one struggling with this :). However, I want to make something entirely myself (i like re-inventing wheels ;) )
SirLenz0rlot
A: 

What you have in this case is a typical producer consumer scenario - many threads produce log entries and one thread writes them out to a file. The MSDN has an article with sample code for this scenario.

Daniel Brückner
A: 

For starters, your logging mechanism should generally avoid throwing exceptions. Frequently logging mechanisms are where errors get written to, so things get ugly when they also start erroring.

I would look into the BackgroundWorker class, as it allows you to fork off threads that can do the logging for you. That way your app isn't slowed down, and any exceptions raised are simply ignored.

JustLoren
+1  A: 

If you're doing this as an exercise (just using a ready made logger isn't an option) you could try a producer / consumer system.

  1. Either make an Init function for your logger, or use the static constructor - inside it, launch a new System.Threading.Thread, which just runs through a while(true) loop.
  2. Create a new Queue<string> and have your logging function enqueue onto it.
  3. Your while(true) loop looks for items on the queue, dequeues them, and logs them.
  4. Make sure you lock your queue before doing anything with it on either thread.
Tesserex
The logging thread will grab 100% CPU time on one core if it is down this way...
Daniel Brückner
Thnanks, this should be easy. I already made similar code for that to communicate with the COMport (which isn't even grabbing 1% of my CPU btw)
SirLenz0rlot
+1  A: 

Ok, simply put you need to create a ThreadSafe static class. Below are some code snippets, a delegate that you call from any thread, this points to the correct thread, which then invokes the WriteToFile function.

When you start the application that you want to log against, pass it the following, where LogFile is the filename and path of your log file.

Log.OnNewLogEntry += Log.WriteToFile (LogFile, Program.AppName);

Then you want to put this inside your static Logging class. The wizard bit is the ThreadSafeAddEntry function, this will make sure you are in the correct Thread for writing the line of code away.

public delegate void AddEntryDelegate(string entry, bool error);

public static Form mainwin;

public static event AddEntryDelegate OnNewLogEntry;

public static void AddEntry(string entry) {
  ThreadSafeAddEntry( entry, false );
}

private static void ThreadSafeAddEntry (string entry, bool error)
    {
    try
        {
        if (mainwin != null && mainwin.InvokeRequired)  // we are in a different thread to the main window
            mainwin.Invoke (new AddEntryDelegate (ThreadSafeAddEntry), new object [] { entry, error });  // call self from main thread
        else
            OnNewLogEntry (entry, error);
        }
    catch { }
    }

public static AddEntryDelegate WriteToFile(string filename, string appName) {
    //Do your WriteToFile work here
    }
}

And finally to write a line...

Log.AddEntry ("Hello World!");
Sres
Thanks, I'm going to use something similar together with the solution of Tesserex
SirLenz0rlot