What's the best approach to logging? [closed]
My (local, windows/mono) app logs important events to a text file. In case of a sudden crash/failure/forced exit, no data should remain unwritten (as far as it's possible). Thus I currently use a simple append-to-text-file approach:
Public Shared Sub LogAppEvent(ByVal EventData As String)
Dim Config As ConfigHandler = ConfigHandler.GetSingleton()
Dim AppLog As New IO.StreamWriter(Config.GetUserFilesRootDir() & ConfigOptions.AppLogName, True)
AppLog.WriteLine(String.Format("[{0}] {1}", Date.Now.ToString(), EventData))
AppLog.Close()
End Sub
This is highly sub-optimal, but log events are pretty rare. Would you recommand moving to the System.Diagnostics
logging class开发者_JS百科?
Or maybe would you suggest another solution?
If a super basic approach like this is functionally sufficient for your needs, you can stick with it. But, you might ask yourself a few questions to make sure:
Is it possible for events to be logged concurrently by multiple threads? This function is not threadsafe
Is error notification needed?
Is there a risk of the log files growing without bounds with no automated pruning?
Would you benefit from more extensive logging so that you had more info about events leading up to an error?
Would you benefit from more detail about errors (stack trace, exception details, etc)
Does your program run on multiple computers? If so, how do logs get to you?
Is there any need/value in tools to help analyze log files (either singularly or finding patterns such as common errors across many log files)?
If you decide that you have other requirements, there are a number of free logging frameworks such as NLog or log4net that can help record more detailed logs, and there are several commercial products such as GIBRALTAR and SmartInspect available that can help with log management and analysis.
As mentioned earlier, NLog and log4net are both good logging frameworks. As Jeff mentions above, System.Diagnostics is also a reasonable choice (for more than just logging to the EventLog). Trying to add some value to my answer, rather than just parroting what has already been said, you can enhance your System.Diagnostics logging by using TraceSources and by using the free Ukadc.Diagnostics library from codeplex.
With TraceSources you can created "named loggers", similar to how you can do it in NLog and log4net. These TraceSources can be configured to log at certain levels (varying per TraceSource) and they can be sent to various destinations (TraceListeners). All TraceSources can log to the same listener or some can log to some listeners while others log to other listeners. Any TraceSource can also be sent to multiple TraceListeners.
Here is how you would use TraceSources in code (assume that TraceSource "abc" has been configured in the app.config file to log "Info" and higher priority messages and has been configured to log to the file "log.txt").
public class MyClass
{
static TraceSource ts = new TraceSource("abc"); //Common idiom for NLog and log4net, not sure if as common for TraceSource
public void Func1(int x)
{
ts.Information("Entering Func1");
ts.Verbose("x = {0}", x); //Won't log if "abc" is configured to log Info and HIGHER messgaes
ts.Information("Exiting Func1");
}
}
One huge benefit of Ukadc.Diagnostics, over "plain" TraceSources, is that you can configure NLog/log4net style output formatting so you can have much more control over what fields show up in your logging output and what format.
Three things that are pretty useful from NLog/log4net that are not available in System.Diagnostics are:
Ability to automatically log call site information (method/function)
Additional logging context (GDC - global logging properties, MDC - thread logging properties in NLog/log4net parlance). System.Diagnostics does have Trace.CorrelationManager.LogicalOperationStack, which is similar to NDC.
Hierarichal loggers.
Hierarichical loggers means that you could configure an "ancestor" logger and any "descendent" loggers will inherit those settings. For example, say that you have a class whose fully (namespace) qualified type name is Namespace1.Namespace2.Class. With NLog/log4net you could configure logging information (level, destination) for "Namespace1" and if you requested a logger based on the fully qualified name of any type in Namespace1, it would inherit Namespace1's settings. You can achieve something similar to this using TraceSources by looking at how Castle implemented their TraceSource-based logging abstraction. In particular, look at the Initialize function. It is pretty easy to get working (in your own thin wrapper around TraceSource) and, as a side benefit, makes the configuration of your TraceSources a little bit easier since you don't have to configure every single TraceSource individually. Note that you could easily add the ability to have a "root" configuration by configuring a TraceSource called "" and adding some code in the Castle scheme to default to the "" configuration if no actual ancestors are found. You could then, for example, configure "*" to log at, say Verbose, and then specifically configure certain TraceSources (either by class or by namespace) to be off or at a different level. Without hierarichal loggers, to do the same thing with TraceSources would required you to configure every single TraceSource to log at "Verbose" that you want at verbose.
While I talked a lot about loggers for classes and namespaces, NLog, log4net, and TraceSources all also allow you to define your logger names as arbitrary strings. As such, you cold define a logger hierarchy by functional area rather than by namespace/class:
Database
Database.Connect
Database.Query
Database.Update
Database.SQL
Amazon
Amazon.Books
Amazon.Books.Fiction
Amazon.Books.Nonfiction
Amazon.Electronics
Amazon.Electronics.Video
Amazon.Electronics.Music
Amazon.Electronics.Computer
So, you could turn on "Amazon" logging and all Amazon stuff log (without having to explicitly configure each and every "child" TraceSource) and Database stuff would not. Or, you could turn Amazon on and Amazon.Electronics off and only the Amazon.Books (and children) would log.
Finally, if you do go with NLog or log4net, it is worth mentioning that NLog has just made a new release, NLog 2.0, (in beta).
Nlog and log4Net are popular choices for logging in .net projects.
Yes, you can consider System.Diagnostics. As long as you're not writing a large number of events, the advantage to the Windows Event Log is that administrators have one place to look for all events from all applications.
Here is some VB.NET code that can help you should you decide to go this route:
Imports System.Diagnostics
Public Function WriteToEventLog(ByVal Entry As String, _
Optional ByVal AppName As String = "VB.NET Application", _
Optional ByVal EventType As _
EventLogEntryType = EventLogEntryType.Information, _
Optional ByVal LogName As String = "Application") As Boolean
'*************************************************************
'PURPOSE: Write Entry to Event Log using VB.NET
'PARAMETERS: Entry - Value to Write
' AppName - Name of Client Application. Needed
' because before writing to event log, you must
' have a named EventLog source.
' EventType - Entry Type, from EventLogEntryType
' Structure e.g., EventLogEntryType.Warning,
' EventLogEntryType.Error
' LogName: Name of Log (System, Application;
' Security is read-only) If you
' specify a non-existent log, the log will be
' created
'RETURNS: True if successful, false if not
'EXAMPLES:
'1. Simple Example, Accepting All Defaults
' WriteToEventLog "Hello Event Log"
'2. Specify EventSource, EventType, and LogName
' WriteToEventLog("Danger, Danger, Danger", "MyVbApp", _
' EventLogEntryType.Warning, "System")
'
'NOTE: EventSources are tightly tied to their log.
' So don't use the same source name for different
' logs, and vice versa
'******************************************************
Dim objEventLog As New EventLog()
Try
'Register the App as an Event Source
If Not objEventLog.SourceExists(AppName) Then
objEventLog.CreateEventSource(AppName, LogName)
End If
objEventLog.Source = AppName
'WriteEntry is overloaded; this is one
'of 10 ways to call it
objEventLog.WriteEntry(Entry, EventType)
Return True
Catch Ex As Exception
Return False
End Try
End Function
I use public variable and timer appending new lines to txt file.
E.g.
Public Class Form1
Public WriteToLog = ""
Dim LogFilePath As String = My.Application.Info.DirectoryPath & "\applog.txt"
....
Public Function Log_to_txt(ByVal text As String)
Dim CurDate As String
CurDate = Format(Now, "General Date")
WriteToLog = WriteToLog & "[" & CurDate & "] " & text & vbCrLf
Return True
End Function
...
Log_to_txt("Application started...")
...
Private Sub WriteToLogTimer_Tick(sender As Object, e As EventArgs) Handles WriteToLogTimer.Tick
Dim CatchLog As String = WriteToLog
WriteToLog = ""
Try
My.Computer.FileSystem.WriteAllText(LogFilePath, CatchLog, True)
Catch ex As IOException
End Try
End Sub
精彩评论