Log file size check and log writing using asp.net - c#

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.

Related

How to empty contents of a log file being used by the same program

I have a C# application which uses log4net to write some log outputs in a file names "logfile.txt" residing in the application directory. I want to empty the contents of the file as soon as it reaches a size of 10GB.
For that I'm using a timer which keeps checking whether the size of the file crosses 10GB.
But I cannot perform any operation on "logfile.txt" since it is being used by other threads to write log outputs and it's throwing me,
System.IO.IOException "The process cannot access the file 'C:\Program Files\MyApps\TestApp1\logfile.txt' because it is being used by another process."
Here is the code of the timer which checks the size of the file "logfile.txt"
private void timer_file_size_check_Tick(object sender, EventArgs e)
{
try
{
string log_file_path = "C:\\Program Files\\MyApps\\TestApp1\\logfile.txt";
FileInfo f = new FileInfo(log_file_path);
bool ex;
long s1;
if (ex = f.Exists)
{
s1 = f.Length;
if (s1 > 10737418240)
{
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
File.Delete(log_file_path);
//File.Create(log_file_path).Close();
//File.Delete(log_file_path);
//var fs = new FileStream(log_file_path, FileMode.Truncate);
}
}
else
{
MDIParent.log.Error("Log file doesn't exists..");
}
}
catch (Exception er)
{
MDIParent.log.Error("Exceptipon :: " + er.ToString());
}
}
You shouldn't delete a log file on your own because log4net can do it for you. If you use RollingFileAppender you can specify the maximum file size (maximumFileSize property). Additionally if you set maxSizeRollBackups property to 0, then the log file will be truncated when it reaches the limit. Please look at this question for an example.

how to make program call different method if exception was caught

I have a try catch statement which handles reading a list of xml files and outputs them to csv files.
Now I want to be able to move faulty xml files to a different folder from the healthy files but am not sure how to do it.
What I have got so far is as below:
bool faultyYN = false;
foreach (string filename in XMLFiles)
{
using (var reader = new StreamReader(filename))
{
string shortFileName = Path.GetFileNameWithoutExtension(filename);
XMLShredder.DataFile df = null;
try
{
var sw = new Stopwatch();
sw.Start();
df = Shredder.ShredDocument(XDocument.Load(reader, LoadOptions.SetLineInfo));
sw.Stop();
var elapsed = sw.ElapsedMilliseconds;
_log.InfoFormat(" Shredded file <{0}> in {1}ms", shortFileName, elapsed);
string outputFileName = Path.Combine(outputDirectory, shortFileName) + ".csv";
sw.Reset();
sw.Start();
using (var writer = new ChunkedShreddedFileWriter(outputFileName))//full file path
{
new DataFileCsvWriter().Write(df,
writer);
}
sw.Stop();
var elapsed2 = sw.ElapsedMilliseconds;
_log.InfoFormat(" Wrote file <{0}> in {1}ms", shortFileName, elapsed2);
}
catch (XmlException e)
{
_log.Error(String.Format("Reading failed due to incorrect structure in XML Document. File Name : <{0}>. Error Message : {1}.", shortFileName, e.Message), e);
faultyYN = true;
}
catch (IOException e)
{
_log.Error(String.Format("Reading failed due to IO Exception. File Name : <{0}>. Error Message : {1}.", shortFileName, e.Message), e);
}
if(bool faultyYN == true)
{
MoveFaultyXML(faultyXMLDirectory, shortFileName);
}
}
TidyUp(XMLFiles);//deletes the files after the process has finished.
}
I have tried adding the Move faulty files to faulty directory after the catch but the files still keep getting deleted.
So basically the method that does not work as I don't know where I should be calling it from is "MoveFaultyXML(faultyXMLDirectory, shortFileName)".
I have read on the net that I shouldn't be using a an exception to branch out but in this case I couldn't think of an alternative solution. The exception has to be thrown for me to know that there is something wrong with the file.
If there is another way of dealing with this which is better practice or if this way works but I am doing it wrong then please help me and I would really appreciate it.
Thanks,
Jetnor.
First solution that comes to my mind would be to:
Move the MoveFaultyXML(faultyXMLDirectory, shortFileName); call to do it within the appropriate catch block:
catch (XmlException e)
{
//log
MoveFaultyXML(faultyXMLDirectory, shortFileName);
}
You don't need the boolean faultyYN.
Now you can create a class representing your XML file (instead of storing just file names in your XMLFiles list):
public class XMLFile
{
public string FileName { get; set; }
public bool Delete { get; set; }
}
And set the Delete flag to 'false' if you move the file.
In the TidyUp delete only files with this flag set to 'true'.
An alternative solution would be to:
Replace foreach() with
for(int i=XMLFiles.Count - 1; i >= 0; i--)
{
string filename = XMLFiles[i];
//the rest of your code
}
Change the catch block with the XMLException to:
catch (XmlException e)
{
//log
MoveFaultyXML(faultyXMLDirectory, shortFileName);
XMLFiles.RemoveAt(i);
}
This way when you get to CleanUp function, any files that were moved are no longer on the list to be deleted.
The `XmlException' is thrown when the XML is incorrect, so it is inside this catch block that you have to call your MoveFaultyXML.
Additional Notes:
Don't add YN to boolean names. Use something like xmlIsFaulty = true. This makes the code easier to read because then you have conditional statements like
if(xmlIsFaulty){MoveFaultyXml();}
which even a non-programmar can understand.
In this code, you're redeclaring the faultyYN variable which should given an error.
if(bool faultyYN == true)
{
MoveFaultyXML(faultyXMLDirectory, shortFileName);
}
After you've declared the variable at the start of the method, you do not need to declare it again.
This is because TidyUp(XMLFiles); still gets executed after your exception is caught, you can move TidyUp(XMLFiles); to within the try block or only call it in catch blocks which are needed.

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

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

Trouble Moving files in C#?

I am making a software that will move files from the downloads folder to a specific sub folder in a directory. The sub folder is selected by the user by a combobox. I keep getting this error: System.IO.IOException: Cannot create a file when that file already exists. Also, these error come up on people's computer who install my program...exceptions and things. How do i turn it off. Also, why do i get this error? Here is my code:
string pathUser4 = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
string pathDownload4 = (pathUser4 + #"\Downloads\");
string sourceFile = pathDownload4 + listBox1.Text;
string pathdoc5 = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string pathDownload5 = (pathdoc5 + #"\iracing\setups\");
string destinationFile = pathDownload5 + comboBox1.Text;
File.Move(sourceFile, destinationFile);
if (comboBox1.Text == "Select File Destination")
{
MessageBox.Show("Please Select A Destination Folder", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
Each File.Move should be wrapped in a try/catch block as you can never expect an IO operation to execute without error. It could be something as simple as the user having a file handle open, or the file existing in the destination folder, either way, you don't want a single file to throw an exception that stops the entire operation. You will want to catch the exceptions and log them either to an error log file or to the event log, this way you can see the errors that occurred but it will not interrupt anything.
Secondly, for any desktop application I would add global error handling to log any uncaught errors. You can do this by putting this code at the beginning of your program,
AppDomain.CurrentDomain.UnhandledException += (a, exception) => File.AppendAllText("errorlog.txt", exception.ToString() + "\n"
This will keep the user from ever seeing ugly exceptions being thrown. Also be sure you are not giving the users the .pdb files as this will cause exceptions to contain paths of the computer it was compiled on which can contain your username and other sensitive information you wouldn't want a client to see.
You can register the global exception handling when the main window is initialized, you want it to be the first thing you do before any thing else because again you never know when an exception will be thrown so you have to think defensively.
public partial class MainWindow : Window
{
public MainWindow()
{
AppDomain.CurrentDomain.UnhandledException += (a, exception) => File.AppendAllText("errorlog.txt", exception.ToString() + "\n");
InitializeComponent();
}
}
C# uses exceptions extensively so it will be good concept for you to study up on if you are not familiar with this type of error handling. All exceptions derive from the Exception class so when you write catch (Exception e) this will catch all exceptions (because a base reference can hold an object of a derived type), however if you know the specific exception a method will throw you can catch a more specific exception (always before the more general catch) and handle it in a specific way. In this example you may have an IOException from the File.Move() that you want to catch and handle differently.
try
{
string pathUser4 = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
string pathDownload4 = (pathUser4 + #"\Downloads\");
string sourceFile = pathDownload4 + listBox1.Text;
string pathdoc5 = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string pathDownload5 = (pathdoc5 + #"\iracing\setups\");
string destinationFile = pathDownload5 + comboBox1.Text;
File.Move(sourceFile, destinationFile);
if (comboBox1.Text == "Select File Destination")
{
MessageBox.Show("Please Select A Destination Folder", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
catch (Exception e)
{
File.AppendAllText("ErrorLog.txt", e.ToString() + "\n");
}
The example code from MSDN for File.Move should get you pointed at the various things you need to deal with, such as an already existing file and basic error handling.
using System;
using System.IO;
class Test
{
public static void Main()
{
string path = #"c:\temp\MyTest.txt";
string path2 = #"c:\temp2\MyTest.txt";
try
{
if (!File.Exists(path))
{
// This statement ensures that the file is created,
// but the handle is not kept.
using (FileStream fs = File.Create(path)) {}
}
// Ensure that the target does not exist.
if (File.Exists(path2))
File.Delete(path2);
// Move the file.
File.Move(path, path2);
Console.WriteLine("{0} was moved to {1}.", path, path2);
// See if the original exists now.
if (File.Exists(path))
{
Console.WriteLine("The original file still exists, which is unexpected.");
}
else
{
Console.WriteLine("The original file no longer exists, which is expected.");
}
}
catch (Exception e)
{
Console.WriteLine("The process failed: {0}", e.ToString());
}
}
}
The error may caused by your code, or by some invalid input.
As #Despertar mentioned, I suggest all the program include error handling and log features in your code. It will be very helpful for your debug.
But I suggest use open source log library, not do it by yourself. For example, log4net, NLog, etc.

check for event log file full while sending email from windows service

i am sending email from windows service. It threw an error of "event log file is full" sometimes when i am writing the event entry into event viewer.
How to check whether it is full or not?
thanks
You use OverflowAction property of EventLog class
More information : http://msdn.microsoft.com/en-us/library/system.diagnostics.eventlog.overflowaction.aspx
Event Log size is based on storage size and not by number of entries so it would be little bit difficult to figure out if event log is full or not. For example,
bool logFull = false;
EventLog e = ... // get the needed event log
var sizeKB = e.MaximumKilobytes; // event log size
// Check current event log size
var regEntry = Rgistry.LocalMachine.OpenSubKey("System\\CurrentControlSet\\Services\\EventLog\\" + e.Log);
if (regEntry != null)
{
var filePath = regEntry.GetValue("File");
if (filePath != null)
{
var file = new FileInfo(filePath.ToString());
if (file.Exists)
{
var fileSize = (file.Length + 1023) / 1024;
logFull = (fileSize >= sizeKB); // a 1K margin
}
}
}
So above code is using 1KB margin to decide if log file is full or not. As such, I would suggest that you always wrap your event entry writing code inside exception block to avoid rare scenario where current log may take you beyond event log size.

Categories

Resources