How to synchronize writing to file in ASP.Net global.asax? - c#

I am writing to a file, that is created for each date of the year, through code below. This code runs whenever, an unhandled exception occurs in an ASP.Net app. My problem is when many users are using the website, then this code could be hit due to several errors occurring at the same time, which could result in multiple requests to create or write to same file. What is the solution in this case so only one request executes the code related to writing to a file?
private void Application_Error(object sender, EventArgs e)
{
// Code that runs when an unhandled error occurs
string errorGuid = Guid.NewGuid().ToString("D");
if (HttpContext.Current.Server.GetLastError() != null)
{
Exception err = HttpContext.Current.Server.GetLastError();
string header = String.Format("/*****\t\t{0}:{1}\t\t*****/", "Start", errorGuid);
string footer = String.Format("/*****\t\t{0}:{1}\t\t*****/", "End", errorGuid);
string errorText = String.Format("{0}{5}Exception DateTime: {1}{5}Reference #: {2}{5}Exception:{5}=========={5}{3}{5}{4}{5}", header, System.DateTime.Now, errorGuid, err.ToString(), footer, Environment.NewLine);
// '~/ErrorReports/Logs/ErrorLog.log' must exist, else we will get an error
using (System.IO.TextWriter write = new System.IO.StreamWriter(HttpContext.Current.Server.MapPath("~/ErrorReports/Logs/ErrorLog_" + DateTime.Now.ToShortDateString() + ".log"), true, System.Text.Encoding.UTF8))
{
write.WriteLine(errorText);
write.Close();
}
}
}

1 - you can use the singleton pattern and create a class that will handle this file creation/append or
2 - use "lock"
3 - as suggested, use elmah

Related

Why am I unable to write to this file?

I wrote an Async method in C# to write to a file, however I keep on getting the following exception:
The process cannot access the file
'C:\XXX\XXX\XXX\XXX\EventBuffer.txt' because it is being used by
another process.
I've had a look at similar questions already posted on SO such as this one, and this one but it seems like the cause of my issue is different.
I used a process monitor to see which processes are trying to access the directory in which the file is in but the only process trying to access it is the one I'm debugging (Will post a snippet soon of the debug process window).
It isn't that file access was being attempted before it was closed upon last access, because I can get the exception when I attempt to access the file for the first time. I have tried to implement a delay after the StreamWriter is instantiated incase the write method was being attempted, I wasn't using the using block before and was disposing of the object using it's dispose methods but in one of the similar questions a this solved the issue.
public static async void UpdateEventBufferFile(EventDetails EventDtls)
{
string line;
try
{
using (StreamWriter EventBufferFile = new StreamWriter(FilePath, true)) // creates the file
{
//All barcode data space sperated for split detection
line = EventDtls.SiteID + " " + EventDtls.McID + " " + EventDtls.EventID + " "
+ EventDtls.EventDT + " " + EventDtls.AdditionalInfo;
await Task.Run(() => LogFileManager.SystematicLog(" Events " + line + " added to buffer file", " BufferFileWriter.cs"));
await EventBufferFile.WriteLineAsync(line); //no need for new line char WriteLine does that
await EventBufferFile.FlushAsync();
//The using block is suffice to dispose of the object the below is no longer required
//EventBufferFile.Dispose();
//EventBufferFile.Close();
//EventBufferFile = null;
}
}
catch (Exception ex)
{
}
}
I have near identical methods utilised within other classes that don't cause the same issue, which annoys me quite a bit.
The method is not being invoked from within a Loop. Invocation is done in a seprate static class in the method below:
public static void AddCentralEvents(int SiteID, int McID, int EventID, DateTime EventDT, string AdditionalInfo)
{
EventDetails EventDetailsObj = new EventDetails();
EventDetailsObj.SiteID = SiteID;
EventDetailsObj.McID = McID;
EventDetailsObj.EventID = EventID;
EventDetailsObj.EventDT = EventDT;
EventDetailsObj.AdditionalInfo = AdditionalInfo;
Task.Run(() => BufferFileWriter.UpdateEventBufferFile(EventDetailsObj));
}
The error is self explanatory, you are using an ASYNC method (in a loop perhaps) and while your first task hasn't completed it's run (i.e. written to the file) that's why you are ending up with that error.
Have you tried writing with a synchronized method? If you have a requirement to periodically write to a file (i.e. logging) use a logging framework.
I recommend using log4net It is 'one of the' the best out there.

How to take mvc c# server errors +number to line number

Hi I've coded my MVC c# application and it's all fine, however there does seem to be a few bugs. This being one of my first applications, I'm not surprised.
The application though is internal and so I do get good feedback from the users.
They do give me the screens shots 'Server Error in ........ Application'
This gives me the controller action which does help narrow down the error.
However how do I turn the +number at end of the line to an actual line number.
I'm aware that this is some sort of byte offset, but getting a rough idea of the line number would be helpful. Is there a plugin or something I can use?
Or is there another way to handle these. I've got a Base controller that all the controllers extend from - I've seen some things that say you can use this to write to a file to give you information about the error. If I made it a generic file (similar to the php error file) then that would help me with any application I make.
You can handle server errors in Global.asax inside Application_Error() method. Create a well designed error page and save it somewhere inside your project. In global asax create a method and put error handling code inside it. See below for example code.
protected void Application_Error()
{
if (httpContext.AllErrors != null)
{
// you can handle message
var message = HttpUtility.HtmlEncode(httpContext.AllErrors[0]);
//you can redirect ugly server error page to the one you created
httpContext.Response.Redirect($"~/Error/Global");
}
}
Just developing on what hhh's answer here.
This is what I've got at the end.
protected void Application_Error()
{
if (this.Context.AllErrors != null)
{
var p = Path.Combine(Server.MapPath("~"), "Errors.log");
var message = DateTime.Now.ToString();
message = message + " " + this.Context.User.Identity.Name;
message = message + " " + this.Context.Request.Url;
message = message + Environment.NewLine;
message = message + "Post";
message = message + Environment.NewLine;
string[] keys = this.Context.Request.Form.AllKeys;
for (int i = 0; i < keys.Length; i++)
{
message = message+keys[i] + ":" + this.Context.Request.Form[keys[i]];
message = message + Environment.NewLine;
}
message = message + Environment.NewLine;
// you can handle message
message = message+ HttpUtility.HtmlEncode(this.Context.AllErrors[0]);
message = message + Environment.NewLine;
message = message + "----------------------------------";
message = message + Environment.NewLine;
System.IO.File.AppendAllText(p, message);
//you can redirect ugly server error page to the one you created
}
}
Basically giving you a file with all the key variables in the there.
Feel free to modify as you wish.

Log file size check and log writing using asp.net

I want to log error in a web application designed in asp.net webforms.
Currently i use following code to log error when ever exception is raised
void Application_Error(object sender, EventArgs e)
{
// Code that runs when an unhandled error occurs
Exception exc = Server.GetLastError();
// Include enterprise logic for logging exceptions
// Get the absolute path to the log file
string logFile = "~/App_Data/ErrorLog.txt";
logFile = HttpContext.Current.Server.MapPath(logFile);
// Open the log file for append and write the log
using (StreamWriter sw = new StreamWriter(logFile, true))
{
sw.WriteLine("********** {0} **********", DateTime.Now);
if (exc.InnerException != null)
{
sw.Write("Inner Exception Type: ");
sw.WriteLine(exc.InnerException.GetType().ToString());
sw.Write("Inner Exception: ");
sw.WriteLine(exc.InnerException.Message);
//sw.Write("Inner Source: ");
sw.WriteLine(exc.InnerException.Source);
if (exc.InnerException.StackTrace != null)
{
// sw.WriteLine("Inner Stack Trace: ");
// sw.WriteLine(exc.InnerException.StackTrace);
}
}
sw.Write("Exception Type: ");
sw.WriteLine(exc.GetType().ToString());
sw.WriteLine("Exception: " + exc.Message);
// sw.WriteLine("Source: " + source);
//sw.WriteLine("Stack Trace: ");
if (exc.StackTrace != null)
{
// sw.WriteLine(exc.StackTrace);
// sw.WriteLine();
}
// sw.Close();
}
}
How can i modify this code so that i can check the file size first to see if it has reached 1MB size. If logfile has reached the 1MB size then i will create another file with date label etc.
You can use FileInfo in this way:
FileInfo fi = new FileInfo(logFile);
if(fi.length > 1024) {
// create new file
}
Another option is to use a powerful logging library such as NLog. It allows you to specify maximum file size for a log file and also to automatically delete old files.
As you can see, these options and many other are automatically handled based on a xml configuration.

WriteAllText doesn't seem to get called [duplicate]

This question already has answers here:
VS2010 does not show unhandled exception message in a WinForms Application on a 64-bit version of Windows
(5 answers)
Closed 9 years ago.
I am trying to save GetDirectories as a txt file, but somewhere my program fails.
private void Form1_Load(object sender, EventArgs e)
{
var directoryInfo = new System.IO.DirectoryInfo(#"g:\FTP\");
int directoryCount = directoryInfo.GetDirectories().Length;
...
var directoryInfo11 = new System.IO.DirectoryInfo(#"q:\FTP\");
int directoryCount11 = directoryInfo11.GetDirectories().Length;
int directoryCountMain = directoryCount + directoryCount2 +
directoryCount3 + directoryCount4 + directoryCount5 +
directoryCount6 + directoryCount7 + directoryCount8 +
directoryCount9 + directoryCount10 + directoryCount11;
string text = "Total Releases: ";
// WriteAllText creates a file, writes the specified string to the
// file, and then closes the file.
System.IO.File.WriteAllText(#"c:\test\ik.txt", text + directoryCountMain);
}
I don't get an error or anything, It looks like my code is skipped as I tried placing a MessageBox.Show below the code but It got ignored.
This won't solve your problem, but at least will shorten your code and make it maintainable. Replace your code with following, it will do the same.
var ftpDirs = new string[] { "g:/FTP/", ... };
int subDirsCount = 0;
foreach(var dir in ftpDirs)
{
subDirsCount += new DirectoryInfo(dir).GetDirectories().Length;
}
string text = "Total Releases: ";
File.WriteAllText(#"c:\test\ik.txt", string.Format("{0}{1}", text, subDirsCount));
Do not forget to add following at the top of the file.
using System.IO;
Place a breakpoint on the first statement in Form1_Load and see if it gets hit. If not, you probably need to subscribe to this event in your code.
If it gets hit, step through your code and find the line where it fails.
Note that Form_Load does not catch exceptions by default, so it will appear as though other lines were skipped. There are ways to solve it, just follow the above link.
I think this directories and path you have given doesnt exists or wrong therefore throwing exception when trying to get info
var directoryInfo11 = new System.IO.DirectoryInfo(#"q:\FTP\");
Add try catch block around your code and see if its throwing excpetion.

multiple file download using SkyDrive API

I have the following code where I'm trying to download 3 different files from the users SkyDrive Account.
I'm using the SkyDrive API for Windows Phone development.
client.DownloadCompleted += new EventHandler<LiveDownloadCompletedEventArgs>(OnDownloadCompletedVI);
client.DownloadAsync(fileIdVehicleItems);
client.DownloadCompleted += new EventHandler<LiveDownloadCompletedEventArgs>(OnDownloadCompletedHI);
client.DownloadAsync(fileIdHistoryItems);
client.DownloadCompleted += new EventHandler<LiveDownloadCompletedEventArgs>(OnDownloadCompletedRI);
client.DownloadAsync(fileIdRepairItems);
When I run this, the only method that gets called is the OnDownloadCompletedVI. All the files that are being downloaded are running through this method which is causing an error.
What am I doing incorrectly?
Update
I have the following method, but I have 2 other similar methods that do the exact same thing except it loads different objects (based off of the downloaded files).
The error I'm currently receiving:
An exception of type 'System.ArgumentException' occurred in
mscorlib.ni.dll but was not handled in user code
void OnDownloadCompletedVI(object sender, LiveDownloadCompletedEventArgs e)
{
if (e.Result != null)
{
using (var stream_vi = e.Result)
{
StreamReader SRVI = new StreamReader(stream_vi);
string contentVI = "";
contentVI = SRVI.ReadToEnd();
StringReader rdr_vi = new StringReader(contentVI);
XmlSerializer serializer = new XmlSerializer(typeof(ObservableCollection<vehicle>));
ObservableCollection<vehicle> importedVehicles = new ObservableCollection<vehicle>();
importedVehicles = (ObservableCollection<vehicle>)serializer.Deserialize(rdr_vi);
StorageHelper.Save<ObservableCollection<vehicle>>(App.vehicleData, importedVehicles);
}
//e.Result.Close();
}
else
{
infoTextBlock.Text = "Error downloading file: " + e.Error.ToString();
}
}
Actually all three methods should be called. Of course, if the first method is called and throws an exception the other two won't trigger.
What you can do is either create a new client for each call, or download them in order, so when the OnDownloadCompletedVI method is complete, remove the event handler for OnDownloadCompletedVI and add the one for OnDownloadCompletedHI and then trigger the client.DownloadAsync(fileIdHistoryItems); at the end of the method.

Categories

Resources