C# Singleton Logging Class
I am trying to figure开发者_如何学Go out the best strategy for logging on the async-IO web server I am working on. I thought the easiest way was to have a singleton class that keeps Filestreams open for the appropriate log files so I could just do something like:
Util.Logger.GetInstance().LogAccess(str);
Or something like that.
My class looks like this:
public sealed class Logger {
private static StreamWriter sw;
private static readonly Logger instance = new Logger();
private Logger() {
sw = new StreamWriter(logfile);
}
public static Logger GetInstance() {
return instance;
}
public void LogAccess(string str) {
sw.WriteLine(str);
}
}
This is all just in my head really, and I'm looking for suggestions on how to make it better and also make sure I'm doing it properly. The biggest thing is I need it to be thread safe, which obviously it is not in its current state. Not sure of the best way to do that.
This is taken care of for you automatically if you use NLog - you define all of your loggers in a .config file and then you access all of them via the static LogManager class, which is a Singleton.
Here's an example which illustrates the thread-safe nature of NLog:
https://github.com/nlog/nlog/wiki/Tutorial#Adding_NLog_to_an_application
There's a method TextWriter.Synchronized which produces a thread-safe version of TextWriter. Try that.
a) Do not include "Log" in method names. It obvious that a logger logs. .Warning, .Error, etc are better method names since they describe which level the log entry has.
b) Create a background thread that writes to the log.
c) Enqueue entries from the logging methods and signal the worker thread.
d) Use (I don't know if I remember the method names correctly)
var methodInfo = new StackFrame(1).GetMethod();
var classAndMethod = methodInfo.DeclaringType.Name + "." + methodInfo.Name;
to get the calling method.
Doing that will give you only one thread that access the file.
Maybe you should try NLog or Log4net. Both of them are wonderful log framework.
But if you do want to write your own log component, Lock is a must when you output log messages. It's common that buffering log messages in memory and write them to file once in a time.
Another framework that addreses these issues for you is the Object Guy's logging framework. It can optionally log in the background. Multiple threads can log to the same file. And multiple processes can log to the same file.
I don't think there's any simple way around locking if you want to write to the same file from multiple threads.
So the simple solution is to add a lock
around any calls to the StreamWriter
. Alternatively you could buffer the output in memory and only write it to the file once in a while which still requires locking, but the lock contention would be a lot lower. If you go to that length, though, you might as well go with a proper logging framework like log4net, which is thread-safe.
Or you could use a class with only shared methods..
Imports System.Threading
Public Class Logger
Private Shared ReadOnly syncroot As New Object
Public Shared Sub log(ByVal vInt As Integer)
ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf logThread), CStr(vInt))
End Sub
Public Shared Sub log(ByVal vStr As String)
ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf logThread), vStr)
End Sub
Private Shared Sub logThread(ByVal o As Object)
Dim str As String = CStr(o)
SyncLock syncroot
Using objWriter As New System.IO.StreamWriter(GetLogPath, True)
objWriter.WriteLine(str)
objWriter.Close()
End Using
End SyncLock
End Sub
Private Shared Function GetLogPath() As String
Return "logs.txt"
End Function
End Class
I found it more usable this way than using a singleton :
Logger.log("Something to log")
Cheers
精彩评论