还在探索C#...请跟我一起光顾。
这是我的自定义“logger”类,它可以帮助我在项目中创建日志文件。
namespace MyProject
{
class Logger
{
private FileInfo fFile;
private DirectoryInfo dDir;
/// <summary>Add a new entry to the log file.</summary>
/// <param name="sData">The line to add.</param>
public void Add(string sData)
{
DateTime CurrTime = DateTime.Now;
if (fFile.Length > 1048576)
{
fFile.MoveTo(Path.Combine(dDir.FullName, CurrTime.ToShortDateString() + fFile.Name));
fFile = new FileInfo(Path.Combine(dDir.FullName,fFile.Name));
using (StreamWriter sw = fFile.CreateText())
{
sw.WriteLine("{0:u}|{1}", CurrTime, sData);
}
}
else
{
using (StreamWriter sw = fFile.AppendText())
{
sw.WriteLine("{0:u}|{1}", CurrTime, sData);
}
}
}
/// <summary>Logger instance</summary>
/// <param name="sFile">Full name of the file to use as logs. Ex : "MyLogs.txt"</param>
public Logger(string sFile)
{
dDir = new DirectoryInfo(Path.Combine(MyProject.AppPath, "logs"));
if (!dDir.Exists)
{
dDir.Create();
}
fFile = new FileInfo(Path.Combine(dDir.FullName,sFile));
if (!fFile.Exists)
{
using (StreamWriter sw = fFile.CreateText())
{
sw.WriteLine("{0:u}|Logger Started", DateTime.Now);
}
}
else
{
Add("Logger Started");
}
}
}
}
我对此代码的问题显然有时候,Logger.Add在该记录器的新实例有时间创建文件之前被调用。所以我的程序崩溃说“找不到文件”,但是,文件最终会在最后创建,如果我使用相同的日志文件名重新启动我的程序,一切正常(因为文件现在存在...)
除了确保在创建文件之前未调用logger.add,有没有办法“锁定”该类?
我已经尝试了锁定方法,但它没有用...锁定(这个)没有做任何事情,我不能在方法本身上使用它。
答案 0 :(得分:2)
问题在于IO操作已被缓存。这在理论上应该不是问题,但实际上它是。
您可以在构造函数中调用sw.Flush()。这将强制文件从缓存到磁盘,从而创建文件。
if (!fFile.Exists)
{
using (StreamWriter sw = fFile.CreateText())
{
sw.WriteLine("{0:u}|Logger Started", DateTime.Now);
sw.Flush();
}
}
答案 1 :(得分:2)
实际上引起的异常不是因为文件不存在,而是因为FileInfo实例是陈旧的!您在文件不存在时创建了FileInfo,并且此时它会获取文件状态的快照。当我测试它时,在Add方法中调用fFile.Length
时会抛出异常。如果我添加了对fFile.Refresh()
的调用,我发现它有效:
...
DateTime currTime = DateTime.Now;
fFile.Refresh();
if (fFile.Length > 1048576)
...
见这里:
http://msdn.microsoft.com/en-us/library/system.io.filesysteminfo.refresh.aspx
“在尝试获取属性信息之前必须进行刷新,否则信息将过时。”
答案 2 :(得分:1)
我个人不会这样做,锁定课程。我会把代码输出来创建日志文件并使其成为自己的方法。然后当调用add方法时,添加一些逻辑来检查日志文件是否存在(在我看来,这个逻辑也应该是它自己的方法)。如果文件存在,继续使用日志记录,如果没有,则创建日志文件(使用上面提取的方法),然后一旦该方法成功返回,继续编写日志。
答案 3 :(得分:1)
在方法中,检查类是否“准备就绪”(无论可能是什么),如果它不是等待,则返回错误或您需要做的任何事情。
答案 4 :(得分:1)
您可以尝试使用FileSystemWatcher类并处理事件以使类准备就绪。这样,事件可以作为委托添加,并且只有在创建了相关文件时才会调用。 Thread.Sleep可能是处理器密集型的,并且在此过程期望完成时会导致其他进程锁定。
答案 5 :(得分:1)
在构造函数中,您要设置字段文件和目录,而应该有一个作为流写器的字段,并在Add方法中使用THAT。
答案 6 :(得分:1)
您希望在创建文件之前阻止add方法运行 - 您可以使用EventWaitHandle或其他一些线程控制机制(可能使用monitor类)来执行此操作。
并且还使用Lock
一次限制文件的使用到一个线程,你不希望两个线程同时运行Add
- 并且都试图一次移动文件
EventWaitHandle _handle = new EventWaitHandle (false, EventResetMode.ManualReset);
Object _fileLock = new Object();
public Logger(string sFile)
{
// Do as you do here
_handle.Set();
}
public void Add(string sData)
{
_handle.WaitOne (); // Blocks the thread untill Set() is called on _handle
Lock(_fileLock){ // Only one thread may enter at a time
// Do as you do already
}
}
第二个想法,在你的ctor代码周围使用Lock(_fileLock){
应该和WaitHandle做同样的工作。
答案 7 :(得分:1)
当构造为与编译器工作相关的fas时,该类应该已准备就绪。除此之外,任何逻辑都必须编程。
fFile.MoveTo(Path.Combine(dDir.FullName, CurrTime.ToShortDateString() + fFile.Name));
fFile = new FileInfo(Path.Combine(dDir.FullName,fFile.Name));
这看起来像竞争条件,需要序列化。即使在使用锁(perl / log4perl)之后我们也有一个关闭的问题,并最终为每个进程更喜欢单独的日志文件。 除非有多个Logger对象存活(因为我们处理文件时甚至跨进程),否则它应该不是问题。
奇迹,C#中没有log4等价物。
答案 8 :(得分:0)
你在这里使用多个线程吗?如果你是,你将需要采取不同的方法。例如,如果两个线程同时尝试调用Add并且一个或两个尝试移动文件,则会遇到竞争条件。
当程序运行时,任何其他进程都可以移动或锁定您的日志文件吗?如果有,您的程序是否需要崩溃?如果是这种情况,您将需要进行大量更改,因为所有文件I / O操作都可能失败。
如果这两种情况都不适用,那么看起来您的问题是该文件存储在某个文件系统上,该文件系统在创建文件和您能够观察之间会出现一些延迟那个文件。我不认为这是NTFS或FAT32的情况。您是否将日志文件存储在网络共享中?在不知道更多的情况下,我不能想到比使用rockinthesixstring的测试建议测试文件是否存在以及是否使用短暂的Sleep()调用直到它(或直到你等了足够长的时间才清楚它出了问题 - 也许有人从你身下删除了文件。)
请注意,如果问题 与文件系统相关,则类中没有任何锁定可以提供帮助,也不会调用Flush,因为“using”语句确保StreamWriter已经存在处理,除了与Flush具有相同的效果之外,它将关闭文件,因此Flush是多余的。
所有这一切,为什么你要关闭文件并一直打开它?如果您的程序崩溃,是否确保不会丢失任何内容?如果是这样,你可能最好遵循jmoreno的建议来保持TextWriter打开,但是在你写完每一行之后调用Flush。