Error Logging in C# Website - c#

I am planning to implement error logging writing something similar to this:
public static void WriteError(string errorMessage)
{
try
{
string path = "~/Error/" + DateTime.Today.ToString("dd-mm-yy") + ".txt";
if (!File.Exists(System.Web.HttpContext.Current.Server.MapPath(path)))
{
File.Create(System.Web.HttpContext.Current.Server.MapPath(path)).Close();
}
using (StreamWriter w = File.AppendText(System.Web.HttpContext.Current.Server.MapPath(path)))
{
w.WriteLine("\r\nLog Entry : ");
w.WriteLine("{0}", DateTime.Now.ToString(CultureInfo.InvariantCulture));
string err = "Error in: " + System.Web.HttpContext.Current.Request.Url.ToString() +
". Error Message:" + errorMessage;
w.WriteLine(err);
w.WriteLine("__________________________");
w.Flush();
w.Close();
}
}
catch (Exception ex)
{
WriteError(ex.Message);
}
}
My question is this: Since it is a website, I will have multiple users simultaneously. In that case, if multiple users encounter exceptions at the same time and try to write to the same file, it will again give me an exception right? In that case, how do I implement error logging correctly for simultaneous users? Or will this work?
Thanks in advance.

I'd recommend you use an established logging framework such as NLog or log4net. I have used log4net on many projects in the past and it handles this scenario perfectly.
EDIT:
Just as a further comment, as well as handling log messages from multiple threads log4net also allows you to manage how large your log files grow and provide a built in rolling log mechanism via the RollingFileAppender.

Beside using NLog or other log library, the way you lock this cases is with mutex. I suggest mutex, and not lock() because can catch all pools/threads that may throw an error.
On MSDN there are the details about mutex and examples:
http://msdn.microsoft.com/en-us/library/system.threading.mutex.aspx
a simple example
public static void WriteError(string errorMessage)
{
var mut = new Mutex(true, "LogMutexName");
try
{
// Wait until it is safe to enter.
mut.WaitOne();
// here you open write close your file
}
finally
{
// Release the Mutex.
mut.ReleaseMutex();
}
}

Related

Handling AppDomain.CurrentDomain.UnhandledException in windows service

I have window service which acts as a sync software. I want to add unhanded exception logging on my service, so I modified my program.cs like this:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.ControlAppDomain)]
static void Main()
{
// Register Unhandled Exception Handler
AppDomain.CurrentDomain.UnhandledException +=
new UnhandledExceptionEventHandler(UnhandledExceptionHandler);
// Run Service
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new Service()
};
ServiceBase.Run(ServicesToRun);
}
static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args)
{
// Get Exception
Exception ex = (Exception)args.ExceptionObject;
// Generate Error
string ErrorMessage = String.Format(
"Error: {0}\r\n" +
"Runtime Terminating: {1}\r\n----- ----- ----- ----- ----- -----\r\n\r\n" +
"{2}\r\n\r\n####################################\r\n",
ex.Message,
args.IsTerminating,
ex.StackTrace.Trim());
// Write Error To File
try
{
using (StreamWriter sw = File.AppendText("UnhandledExceptions.log"))
sw.WriteLine(errorMessage);
}
catch { }
}
}
Then on my Service.cs file, in the OnStart method, I added a throw new Exception("test"); to see if unhanded exceptions are being logged to file as expected.
When I start my service, it stops immediately as expected; however it doesn't seem to be logging the exception to the specified file.
Any idea what I am doing wrong here? Thanks in advance for any help.
Before you ask, my service runs as Local Service and the directory where my service .exe runs from (c:\mysync) already has Local Service added in the security tab with full read/write access.
OnStart is called in Service base class inside try-catch block. If an exception happens on this stage it catches it and just set a status 1 as a result and do not throw it further:
string[] args = (string[]) state;
try
{
this.OnStart(args);
.....
}
catch (Exception ex)
{
this.WriteEventLogEntry(Res.GetString("StartFailed", new object[1]
{
(object) ((object) ex).ToString()
}), EventLogEntryType.Error);
this.status.currentState = 1;
}
As a result you can find a record in EventLogs, but you can't catch it as an unhanded domain exception, as there is no such exception.
using (StreamWriter sw = File.AppendText("UnhandledExceptions.log"))
It is forever a really bad idea to not use full path names for files (like c:\foo\bar.log). Especially in a service, you have very little control over the default directory for your service. Because it is started by the service control manager, not by the user from the command prompt or a desktop shortcut.
So high odds that you are just looking at the wrong file. The real one probably ended up being written to c:\windows\system32 (or syswow64). The operating system directories are normally write protected but that doesn't work for a service, they run with a highly privileged account so can litter the hard drive anywhere.
Always use full path names. Using the EventLog instead is highly recommended.

sporadic error while getting all messages in Windows messagequeue

In C# ASP.NET 3.5 web application running on Windows Server 2003, I get the following error once in a while:
"Object reference not set to an instance of an object.: at System.Messaging.Interop.MessagePropertyVariants.Unlock()
at System.Messaging.Message.Unlock()
at System.Messaging.MessageQueue.ReceiveCurrent(TimeSpan timeout, Int32 action, CursorHandle cursor, MessagePropertyFilter filter, MessageQueueTransaction internalTransaction, MessageQueueTransactionType transactionType)
at System.Messaging.MessageEnumerator.get_Current()
at System.Messaging.MessageQueue.GetAllMessages()".
The line of code that throws this error is:
Message[] msgs = Global.getOutputQueue(mode).GetAllMessages();
where Global.getOutputQueue(mode) gives the messagequeue I want to get messages from.
Update:
Global.getPool(mode).WaitOne();
commonClass.log(-1, "Acquired pool: " + mode, "Report ID: " + unique_report_id);
............../* some code /
..............
lock(getLock(mode))
{
bool yet_to_get = true;
int num_retry = 0;
do
{
try
{
msgs = Global.getOutputQueue(mode).GetAllMessages();
yet_to_get = false;
}
catch
{
Global.setOutputQueue(mode);
msgs = Global.getOutputQueue(mode).GetAllMessages();
yet_to_get = false;
}
++num_retry;
}
while (yet_to_get && num_retry < 2);
}
... / some code*/
....
finally
{
commonClass.log(-1, "Released pool: " + mode, "Report ID: " + unique_report_id);
Global.getPool(mode).Release();
}
Your description and this thread suggests a timing issue. I would create the MessageQueue object infrequently (maybe only once) and have Global.getOutputQueue(mode) return a cached version, seems likely to get around this.
EDIT: Further details suggest you have the opposite problem. I suggest encapsulating access to the message queue, catching this exception and recreating the queue if that exception occurs. So, replace the call to Global.getOutputQueue(mode).GetAllMessages() with something like this:
public void getAllOutputQueueMessages()
{
try
{
return queue_.GetAllMessages();
}
catch (Exception)
{
queue_ = OpenQueue();
return queue_.GetAllMessages();
}
}
You'll notice I did not preserve your mode functionality, but you get the idea. Of course, you have to duplicate this pattern for other calls you make to the queue, but only for the ones you make (not the whole queue interface).
This is an old thread, but google brought me here so I shall add my findings.
I agree with user: tallseth that this is a timing issue.
After the message queue is created it is not instantly available.
try
{
return _queue.GetAllMessages().Length;
}
catch (Exception)
{
System.Threading.Thread.Sleep(4000);
return _queue.GetAllMessages().Length;
}
try adding a pause if you catch an exception when accessing a queue which you know has been created.
On a related note
_logQueuePath = logQueuePath.StartsWith(#".\") ? logQueuePath : #".\" + logQueuePath;
_queue = new MessageQueue(_logQueuePath);
MessageQueue.Create(_logQueuePath);
bool exists = MessageQueue.Exists(_logQueuePath);
running the MessageQueue.Exists(string nameofQ); method immediately after creating the queue will return false. So be careful when calling code such as:
public void CreateQueue()
{
if (!MessageQueue.Exists(_logQueuePath))
{
MessageQueue.Create(_logQueuePath);
}
}
As it is likely to throw an exception stating that the queue you are trying to create already exists.
-edit: (Sorry I don't have the relevant link for this new info)
I read that a newly created MessageQueue will return false on MessageQueue.Exists(QueuePath)until it has received at least one message.
Keeping this and the earlier points i mentioned in mind has gotten my code running reliably.

better way of invoking thread and logging to text file in C#

I have a program which write logging as Text File.
namespace logging
{
using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
public class log
{
private static string lpath;
[DllImport("kernel32")]
private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath);
public static void Info(string user, string info, string txt)
{
StreamWriter writer;
string str = Assembly.GetExecutingAssembly().GetName().CodeBase.ToString();
.....
if (!File.Exists(path))
{
writer = new StreamWriter(path, true);
writer.WriteLine(str3 + " " + info + " => " + txt);
writer.Flush();
writer.Close();
}
else
{
writer = File.AppendText(path);
writer.Write(str3 + " " + info + " => " + txt + "\n");
writer.Flush();
writer.Close();
}
}
}
}
Then I have a function.
private void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Thread thread_BP_Print = new Thread(BoardingPass_Print);
thread_BP_Print.Start();
// Simultaneously, do something on the main thread.
BaggageTag_Print();
bgw.RunWorkerAsync();
}
Two of the inner functions (BoardingPass_Print() and BaggageTag_Print()) call the same logging function.
logging.log.Info(username, "Query Trace > ", sql);
Because of my invoking thread methods, I think I face the error:
The process cannot access the file 'C:\Folder\2012-01-17-Name.txt' because it is being used by another process.
Could anyone give me better solution to interact with thread and logging to text file without facing error message like this?
Every suggestion will be really appreciated.
You would have to make the writing thread-safe (with lock). Your current code also is not exception-safe.
You can solve both issues by using the right library method.
You cannot (easily) assure safe access to a file from multiple processes, but as long as its only written by 1 instance of the application you can sync the threads.
private static object locker = new object();
public static void Info(string user, string info, string txt)
{
string str = Assembly.GetExecutingAssembly().GetName().CodeBase.ToString();
string str3 = ...
lock (locker)
{
File.AppendAllText(path, str3 + " " + info + " => " + txt + "\n");
}
}
AppendAllText is a static method, it will handle the new/existing file issue and the resource management correctly.
You could use log4net, which is thread safe, instead of writing your own log function.
The classic version would be:
Have a dedicated logger thread, most of the time waiting on an
AutoResetEvent
Have a (threadsafe) Queue
Logging is initiated by writing to that Queue and setting the
AutoResetEvent
The logger thread wakes up, empties the queue into the file, then
sleeps again
You can chose from a choice of predeveloped frameworks for that or roll your own in a few lines, if you want to avoid the dependency.
Like others have said use NLog, log4Net, Enterprise Library, etc. as rolling your own logging logic will not be a comprehensive as the offerings already available.
However if you want to keep things as they are... one suggestion might be to send the log information to a queue (say MSMQ) and have another separate process poll the queue and save the information to your text files.
If you don't want to use any logging framework, you must at least use lock statement to be thread-safe.
You can also use Singleton instead of static method and keep your log file open until appliaction ends.

.Net Runtime Error Stopping Service; Unhandled IOException

I have been put in charge of updating a service that reads a text file and creates pdf files from sections of the text file and emails out the pdfs. I recently made some changes to the service and used the .Net 4.0 Framework. When updating on the server, the 4.0 Framework was installed before I could move my files and start the service successfully - it was using 2.0 previously. The service runs until it reaches code that attempts to clean up the directory with the pdf files. The service Stops when the code attempts to delete a pdf file which is "in use by another process". The code looks for this exception and is supposed to wait for about 30 seconds and try the delete again. Locally, I run this service through a test Harness and it loops through until the file becomes available to delete (on average it takes about 5 minutes), but on the server the Service stops with the Unhandled IOException found in the Application Event log. I don't understand why it will continue processing locally but not on the server. Any ideas or other information would greatly be appreciated.
Here is the error in the Event Log:
The process was terminated due to an unhandled exception.
Exception Info: System.IO.IOException
Stack:
at System.IO.__Error.WinIOError(Int32, System.String)
at System.Console.GetBufferInfo(Boolean, Boolean ByRef)
at processorName.FileProcessingThread.CleanUpDirectory(System.Object)
at System.Threading.ThreadHelper.ThreadStart_Context(System.Object)
at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
at System.Threading.ThreadHelper.ThreadStart(System.Object)
Also this error appears in the event log as well:
EventType clr20r3, P1 myServiceName.exe, P2 1.0.1.0, P3 4db6e85d, P4 mscorlib, P5 4.0.0.0, P6 4d53693b, P7 3dab, P8 23b, P9 system.io.ioexception, P10 NIL.
Here is the code that is supposed to handle the exception, wait and retry to delete the files.
while (!isDone)
{
bool myRetVal = true;
string myErrorMsg = String.Empty;
string myFile = String.Empty;
//give it time to finish processing
Thread.Sleep(30 * 1000);
//
//delete Files in folder
foreach (string file in Directory.GetFiles(myDirectory, "*.PDF"))
{ //delete all the *.PDF files
try
{
File.Delete(file);
}
catch (Exception ex)
{
myErrorMsg = "...\r\n" + ex.Message;
m_Log.Writeline("Unable to delete "+ file + ". MESSAGE:"+ myErrorMsg,3);
myFile = file;
myRetVal = false;
}
}
//
//now move the in-progress File to the processed directory
if (myRetVal)
{
foreach (string file in Directory.GetFiles(myDirectory, "*.InProgress"))
{
try
{
if (File.Exists(m_ConfigFile.ProcessedFolderName + Path.GetFileNameWithoutExtension(file) + ".done"))
{
File.Delete(file);
}
else
{
File.Move(file, m_ConfigFile.ProcessedFolderName + Path.GetFileNameWithoutExtension(file) + ".done");
}
}
catch (Exception ex)
{
if (ex.Message.Contains("file already exists"))
{
}
else
{
myErrorMsg = "...\r\n" + ex.Message;
myFile = file;
myRetVal = false;
}
}
}
}
//
//if empty, delete the SendMailFolder subfolder
if (myRetVal)
{
try
{
if (Directory.GetFiles(myDirectory, "*.*").Length == 0) Directory.Delete(myDirectory);
}
catch (Exception ex)
{
myErrorMsg = "...\r\n" + ex.Message;
myFile = myDirectory;
myRetVal = false;
}
}
if (Console.CursorLeft > 0) Console.WriteLine("\r\n");
if (myRetVal)
{
Console.WriteLine(DateTime.Now.ToLocalTime() + " CleanUp SendMailFolder...Done");
isDone = true;
}
else
{
if (myErrorMsg.Contains("is being used by another process."))
{
myErrorMsg = " is still in use.";
Console.WriteLine(DateTime.Now.ToLocalTime() + " CleanUp SendMailFolder..." + Path.GetFileName(myFile) + myErrorMsg);
}
else
{
Console.WriteLine(DateTime.Now.ToLocalTime() + " CleanUp SendMailFolder..." + Path.GetFileName(myFile) + myErrorMsg);
m_Log.Writeline(DateTime.Now.ToLocalTime() + " CleanUp SendMailFolder..." + Path.GetFileName(myFile) + myErrorMsg, 3);
isDone = true;
}
}
}
You're trying to use the Console class in a service, which doesn't have a console window associated with it. You should use some alternate form of logging that doesn't assume there's a console window. log4net, as one example, allows you to configure multiple "appenders", such as a console, file, and event log appender, to use simultaneously (and which will simply be ignored if they aren't appropriate).
EDIT to clarify:
You can create a console window manually with the AllocConsole P/Invoke call if you absolutely need to. By default, a service doesn't interact with your desktop, so creating a console window would be a bad idea. For that to work you would need to configure the service with the "Allow service to interact with the desktop" setting turned on. This has security implications, particularly for machines that have multiple users, so I would advise against it.
It looks like there error is happening before your try catch. Given that you should see the specific message output by your logging (I assume it goes to the Event Log). I don't see a message that looks like what is in your log. You might want to have an app domain exception handler to catch any exceptions and log them.
Also you should ensure your m_log routine has proper error handling as that could be the culprit as well.

How to Log Exception in a file?

I want to be able to do logging in every catch block. Something like this.
catch (Exception exception)
{
Logger.Write(exception);
}
and then the settings in the configuration will pick up the Message and StackTrace property etc using customer listener.
I would like to use Enterprise Library Logging Application Block. I'm sure someone must have done this already.
Others have posted some good links on getting the Logging Application Block (LAB) working so I won't duplicate that here.
In terms of formatting your exception you have 3 choices that I can think of:
Use the default Exception.ToString() implementation (it's not bad)
Write a Custom Formatter that integrates into the LAB.
Write a helper function that performs the formatting and passes the string into the Write method.
If option 1 doesn't meet your needs then I would recommend going with option 3 (since option 2 is overkill).
A simple example would be something like:
catch (Exception exception)
{
Logger.Write(LogHelper.CreateExceptionString(exception));
}
...
public static string CreateExceptionString(Exception e)
{
StringBuilder sb = new StringBuilder();
CreateExceptionString(sb, e);
return sb.ToString();
}
private static void CreateExceptionString(
StringBuilder sb,
Exception e,
string indent = "")
{
if (indent.Length > 0)
{
sb.AppendFormat("{0}Inner ", indent);
}
sb.AppendFormat("Exception Found:\n{0}Type: {1}",
indent, e.GetType().FullName);
sb.AppendFormat("\n{0}Message: {1}",
indent, e.Message);
sb.AppendFormat("\n{0}Source: {1}",
indent, e.Source);
sb.AppendFormat("\n{0}Stacktrace: {1}",
indent, e.StackTrace);
if (e.InnerException != null)
{
sb.Append("\n");
CreateExceptionString(sb, e.InnerException, indent + " ");
}
}
Microsoft has provided extensive guidance on this here: Developing Applications Using the Logging Application Block
It's well worth familiarizing yourself with what they have to say, as it's quite powerful.
If you want something a little more down to earth, some working examples are provided in this blog article: How To Configure and Use the Logging Application Block
If you're still having trouble after reading those, edit your post with more specific details about what seems to be the problem.
#iSid Is this what you are asking for
public static void printException(Exception ex){
Console.WriteLine("HelpLink = {0}", ex.HelpLink);
Console.WriteLine("Message = {0}", ex.Message);
Console.WriteLine("Source = {0}", ex.Source);
Console.WriteLine("StackTrace = {0}", ex.StackTrace);
Console.WriteLine("TargetSite = {0}", ex.TargetSite);
}
You need to add an appropriate listener for Text files - see this post, its for ASP.NET but the config files work the same way across applications.

Categories