StreamWriter not updating its path on new day - c#

I have a program that's writing to a log file called "appname_yyyyMMdd.log", where appname is the name of my app, and yyyyMMdd is the current date; a sample log file name might be "loglistener_20110615.log" . Anyway, my app creates the log file fine, and it updates it as planned. However, once the date changes, the app doesn't log anything, and it doesn't create a new file. In other words, since today is 6/15, I need it to create a file called "loglistener_20110616.log" after midnight tonight, and I need it to continue logging to that new file.
Here are code excerpts:
public static void LogInfo(string format, params object[] args)
{
lock (_logLock)
{
using (StreamWriter sw = File.AppendText(GetLogPath()))
{
sw.WriteLine(GetTimeStamp() + String.Format(format, args));
}
}
}
private static string GetLogPath()
{
string appName = "loglistener";
string today = DateTime.Today.ToString("yyyyMMdd");
string fileName = appName + "_" + today + ".log";
string fullLogPath = AppDomain.CurrentDomain.BaseDirectory + fileName;
return fullLogPath;
}
I checked this similar question, but that question describes a different scenario (with a non-applicable fix).
UPDATE - Just in case googlers land on this page, I later discovered a different cause altogether w/ this. My log is logging info from an email-listening service. The service itself had a problem where it was timing out after a half-hour. So, my problem wasn't w/ CreateText / AppendText... My problem was there was nothing to log. Very annoying, but I hope other people won't be misled by this question/answer.

You should check to make sure that the file exists first.
From the File.AppendText Documentation
Type: System.IO.StreamWriter A
StreamWriter that appends UTF-8
encoded text to an existing file.
public static void LogInfo(string format, params object[] args)
{
lock (_logLock)
{
using (StreamWriter sw = File.Exists(GetLogPath) ? File.AppendText(GetLogPath()) : File.CreateText(GetLogPath())
{
sw.WriteLine(GetTimeStamp() + String.Format(format, args));
}
}
}
Try that instead
After looking at the comments and re-reading the documentation.
File.AppendText
Should always work, regardless of file existence.

Related

The process cannot access the file 'C:\file.txt' because it is being used by another process

I am trying to log each method on my program, I have the application deployed on IIS Server and the user just called me and said the email functionality is not working so I need to basically run the application but log each step into a txt file.
I am declaring the below as a global value:
StreamWriter writer = new StreamWriter("C:\\file.txt");
Then I use it like below in my code:
Method 1
{
if (file1.HasFile)
{
writer.WriteLine("Has File");
}
}
Method 2
private Boolean InsertUpdateData(SqlCommand cmd)
{
writer.WriteLine("Insert Started" + DateTime.Now.ToString());
}
So in my case method one runs fine and it writes Has File but when it goes into the second method I get the file is already open which is correct how can I work around this?
Thanks
Global Value - declared at top
namespace WorkOrderManagement
{
public partial class CreateWorkOrder : System.Web.UI.Page
{
bool successfull;
string path;
string name;
string content;
string datas;
string ext;
bool affectedrows;
string seasonalsupervisor;
private string sLogFormat;
private string sErrorTime;
StreamWriter writer = new StreamWriter("C:\\file.txt");
I really suggest you to discard the idea to have a global variable to represent a stream and then try to use it in different methods. This is simple in a desktop application, but a lot more complex in an ASP.NET application.
There are simple alternatives that could atomically write your log text and leave the file unlocked.
For example you could have a method like this
public static class Log
{
public static string _file = "log.txt";
public static object _locked = new object();
public static void AppendToLog(string text)
{
lock(_locked)
{
string path = Server.MapPath("~/APP_DATA");
File.AppendAllText(Path.Combine(path, _file), text + Environment.NewLine);
}
}
}
Now you can call the log write with
Log.AppendToLog("My message");
I want to underline two important things here. First I don't write in the root drive of the server. This is a bad practice and always a source of problems when you deploy your ASP.NET application in a server where you have not permissions to use anything outside your site root. Thus the ASP.NET system defines a particular folder called APP_DATA under your site root where your application should have read/write permissions.
Second point to notice is the use of the lock keyword. This is necessary in an environment like ASP.NET where two users could reach a point of the code where you need to write to the common log file. As MSDN explains it
The lock keyword ensures that one thread does not enter a critical
section of code while another thread is in the critical section. If
another thread tries to enter a locked code, it will wait, block,
until the object is released.
you can also do this to close the file stream
using (StreamWriter writer = new StreamWriter("C:\\file.txt"))
{
//your code here
}
//this automatically closes the stream, and it is more recommended.
Close the stream after writing the file
Method 1
{
if (file1.HasFile)
{
writer.WriteLine("Has File");
writer.Close();
}
}

Console.Readline("") repeating at restart

I have this question,
My boss wants a program which you can enter a path, Console.ReadLine(directory);
This is irrelevant, I got this part working. In fact, the whole code/program is working as it should be.
The point of the path is that the program scans all the files in the given directory/subdirectories for the last write time. He want to pay minimum effort in this. So the plan is to use Windows to start this program once every 24 hours.
Only problem with this "minimum effort" part is that you have to enter the path EVERYTIME when it's started. So it actually doesn't go automaticly.
The question is: is there a way to avoid this?
For example Thread.Sleep(); and when it's done sleeping goto a label right under the Console.ReadLine(directory);?
So not once a day, but sleeping for 24 hours and for 1 whole minute working?
If it's any help, here's the code:
using System.IO;
using System.Security.Permissions;
namespace CheckWithinTime
{
class Program
{
public static void Main(string[] args)
{
Console.WriteLine("Which folder do you wish to scan?");
string path = Console.ReadLine();
//copies everything from the console to a .txt file
FileStream filestream = new FileStream(#"C:\Logs\Log.txt", FileMode.Create);
var streamwriter = new StreamWriter(filestream);
streamwriter.AutoFlush = true;
Console.SetOut(streamwriter);
Console.SetError(streamwriter);
//this is the path which you type in a the beginning of the program
string[] files = Directory.GetFiles(path, "*.*", System.IO.SearchOption.AllDirectories);
List<string> updatedFiles = new List<string>();
DateTime from = DateTime.Now.AddDays(-1);
DateTime to = DateTime.Now;
foreach (string name in files)
{
FileInfo file = new FileInfo(name);
string fullname = file.FullName;
//checks if the last writed time occured less then 24 hours ago, if it's not it will not be loggeed
if (file.LastWriteTime >= from && file.LastWriteTime <= to)
{
updatedFiles.Add(name);
Console.WriteLine(file.FullName + " ; " + "last changed at >> " + " ; " + file.LastWriteTime.ToString());
//Console.WriteLine("File created at >> " + file.CreationTime.ToString());
//Console.WriteLine("File last opened at >> " + file.LastAccessTime.ToString());
Console.WriteLine();
}
}
streamwriter.Close();
filestream.Close();
//The mail class basicly sends an e-mail to the server with the log file, but this is irrelevant
Mail();
}
}
}
It used to be just a simple file system watcher. After that it was like it is now, but without the Console.ReadLine() part. And now he wants to give a path.
If anyone can tell me a way to avoid the Console.ReadLine(), but only use call it when you need it. I would appreciate it!
Sorry in advance for my big texts.
The best way to do this would be to either create an XML file or use a notepad file and have windows run a task manager.
You can set up a program to run every so often in windows task manager and all you need to do is save the path to a file and have your C# console application read that file and get the path, the path will always be store and the program will run all the time.
We do this at work; a program has been running for 4 months doing through a bunch of paths and we don't edit it anymore.
Xml file/config is overkill for one setting. Pass it in to the command line string[] args:
string path;
if(args.Length > 0)
{
path = args[0];
Console.WriteLine("Using path: " + path);
}
else
{
Console.WriteLine("Which folder do you wish to scan?");
path = Console.ReadLine();
}
Then use task scheduler as already suggested, but pass the path as a command line argument. You can also then launch it with different paths without your app supporting multiple paths.

StreamWriter not writing data

I am using StraemWriter to log text messages to a log file. The log file should be created if it doesn't exist, appended to if the file creation date is less than a given time or recreated if created before that time. I am using the class/code below
public static class LogIt
{
private const string LOG_FNAME = #"Logfile.log";
public static void WriteMsg(string msg)
{
bool append = true;
if (File.Exists(LOG_FNAME))
{
//DateTime delDate = DateTime.Now.AddDays(-1);
DateTime delDate = DateTime.Now.AddMinutes(-30);
DateTime fileCreatedDate = File.GetCreationTime(LOG_FNAME);
if (DateTime.Compare(fileCreatedDate, delDate) < 0)
{
Console.WriteLine("DELETE FILE");
File.Delete(LOG_FNAME);
}
}
using (StreamWriter sw = new StreamWriter(LOG_FNAME, append))
{
sw.WriteLine(msg);
}
Console.WriteLine(msg);
}
}
This class is used by a simple console app run by the Task Scheduler which runs every x minutes.
The message are written as follows:
LogIt.WriteMsg("Log this message");
The messages are logged file when the file is initially created however when the file creation date is past the delete date, the file is recreated but no subsequent messages are ever written to the file.
Any ideas on why?
For some reason the file has the initial creation date (first time a file with that path ever created) as the creation date even if it is recreated after deleting. You can check the file properties and see that the log file creation date is always the same. A work around would be to update the file creation date in code whenever you recreate the file. You can use FileInfo class for that.
#MPD No problem. Here is the implementation of the workaround I suggested. Give it a try and let me know if that works.
private const string LOG_FNAME = #"Logfile.log";
public static void WriteMsg(string msg)
{
bool deleted = false;
bool append = true;
if (File.Exists(LOG_FNAME))
{
//DateTime delDate = DateTime.Now.AddDays(-1);
DateTime delDate = DateTime.Now.AddMinutes(-30);
DateTime fileCreatedDate = File.GetCreationTime(LOG_FNAME);
if (DateTime.Compare(fileCreatedDate, delDate) < 0)
{
Console.WriteLine("DELETE FILE");
File.Delete(LOG_FNAME);
//record that file was deleted and a new one will be created
deleted = true;
}
}
using (StreamWriter sw = new StreamWriter(LOG_FNAME, append))
{
sw.WriteLine(msg);
}
if (deleted)
{
//a new file is created. Make sure the creation time is set
FileInfo fi = new FileInfo(LOG_FNAME);
fi.CreationTime = DateTime.Now;
}
Console.WriteLine(msg);
}
I guess you are running this code on Windows 2003 (or maybe XP). If so: When you create a file in some directory at time T1 and the delete it and then re-create it; surprise surprise it has T1 as creation date!
I know this just because I had the same problem on Windows 2003!
BTW I use NLog now and (IMHO) it's perfect and has everything I need.

Handling XSLT file names c#

Wondering how to best deal with a problem I am having with xsltransform. Long story short, everything works in my test environment, but it crashes when I run it on the server due to the filenames it tries to deal with, which are output from another program, over which I have no control.
For example. "4Copy (2) of Fed_Around_Six__TFVC020-12.mov.xml" a simple # would solve this, but it's actually running on a service, and this service gets all files of that type in the directory and processes them one by one.
string[] filepaths = Directory.GetFiles(path, Filetype);
I keep the file name variable in:
FileInfo f = new FileInfo(filepaths[i]);
But the method I use for the transform:
myXslTransform = new XslCompiledTransform();
myXslTransform.Transform(filename,OutputFileName);
Only accepts (String, String) and thus when it sees "4Copy (2) of Fed_Around_Six__TFVC020-12.mov.xml" it has a heart attack and cuts it off.
I was thinking save the original name, rename, remove whitespace, transform, and rename back. But I think there is a smarter way to handle it out there, just not sure where to look. Is there a way of telling C# to handle a variable as a literal? Or a different transform method that accepts these weird filenames with very bad naming conventions?
Any insight that helps would be great!
The error & exception message I recieve from the Eventvwr is
Cannot Translate
\\9g031\Export\4Copy (2) of Fed_Around_Six__TFVC020-12.mov.xml
OutputName = \\9g031\Export\done\4Copy (2) of Fed_Around_Six__TFVC020-12.mov.xml
XSL LOC = C:\CXS.xsl
System.IO.IOException: The specified path is invalid.
private void PreformTranslation(FileInfo FileName, String OutputFileName , String result)
{
try
{
XslCompiledTransform myXslTransform;
myXslTransform = new XslCompiledTransform();
myXslTransform.Load(XSLname);
EventLog.WriteEntry(FileName.ToString(), OutputFileName);
myXslTransform.Transform(FileName.Name,OutputFileName);
EventLog.WriteEntry("TranslationComplete");
if (File.Exists(path + result))
{
MoveVideoFiles(path + result, outputPath + result);
}
// Rename(OutputFileName, FileName, Out);
}
catch (Exception e)
{
EventLog.WriteEntry("Cannot Translate " + FileName + " OutputName = " + OutputFileName + " \r\n"+
"XSL LOC = " + XSLname + "\r\n" + e);
}
}
The default directory when running a service is something like "windows/system32" and this isn't the directory of the executable.
This is probably the reason the XML file isn't found.

Way to get unique filename if specified filename already exists (.NET)

Is there a built in .NET function to get a unique filename if a filename already exists? So if I try and save MyDoc.doc and it already exists, the file will save with name MyDoc(1).doc, the same way a browser download works for example.
If not, what is the most efficient way to achieve this result?
I am using the File.Move function at the moment btw.
EDIT:
Here's another solution I came up with after Steven Sudit's comment:
static void Main(string[] args)
{
CopyFile(new FileInfo(#"D:\table.txt"), new FileInfo(#"D:\blah.txt"));
}
private static void CopyFile(FileInfo source, FileInfo destination)
{
int attempt = 0;
FileInfo originalDestination = destination;
while (destination.Exists || !TryCopyTo(source, destination))
{
attempt++;
destination = new FileInfo(originalDestination.FullName.Remove(
originalDestination.FullName.Length - originalDestination.Extension.Length)
+ " (" + attempt + ")" + originalDestination.Extension);
}
}
private static bool TryCopyTo(FileInfo source, FileInfo destination)
{
try
{
source.CopyTo(destination.FullName);
return true;
}
catch
{
return false;
}
}
check the name against Regex *.\(\d+\), if it doesn't match, add (1), if it matches increment the number in brackets.
I don't know, but it's not hard to build one yourself:
if filename does not exists then
save file as filename
else
n = 1
while filename(n) exists:
n += 1
save file as filename(n)
As the other answers shows, there are multiple ways of doing this, but one thing to be aware of is if other processes than your can create files you have to be careful, since if you check that a filename is available, by the time you save your new file, some other process might already have saved a file using that name and you'll overwrite that file.

Categories