I implemented a Utility class in my Windows Form app with a log method. It seems to be creating the log.txt file fine, but not writing anything to it. No other programs are using this particular text file.
using System;
using System.IO;
using System.Text;
namespace program1{
static class Utils {
static Utils() { }
private static readonly string FilePath = TestEnvironment.PATH + #"\log.txt";
private static void CheckFile()
{
if (File.Exists(FilePath)) return;
using (FileStream fs = File.Create(FilePath)) {
Byte[] info = new UTF8Encoding(true).GetBytes("");
fs.Write(info, 0, info.Length);
fs.Close();
}
}
public static string Log(string code, string message) {
StreamWriter _w = File.AppendText(FilePath);
CheckFile();
string log = ("\r\n" + code + ": \n");
log += String.Format("{0} {1}\n", DateTime.Now.ToLongTimeString(),
DateTime.Now.ToLongDateString());
log += String.Format(" :{0}\n", message);
log += String.Format("-------------------------------");
_w.WriteLine(log);
_w.Close();
return log;
}
public static string LogDump() {
StreamReader _r = File.OpenText(FilePath);
string output = "";
string line;
while ((line = _r.ReadLine()) != null) {
output += line;
}
_r.Close();
return output;
}
}
}
Is it perhaps not liking the String.Formats?
According to MSDN:
A stream’s encoder is not flushed unless you explicitly call Flush or dispose of the object.
Either dispose the StreamWriter instance you're creating (ideally by enclosing it in a using block, or alternatively by explicitly calling Dispose()):
using (StreamWriter _w = File.AppendText(FilePath))
{
...
}
Or explicitly call Flush():
_w.Flush();
You don't need the CheckFile() method. AppendText() will create the file if necessary.
The real problem is how you're writing the file. Change your method to this:
public static string Log(string code, string message)
{
string log;
using (var writer = File.AppendText(FilePath))
{
log = ("\r\n" + code + ": \n");
log += String.Format("{0} {1}\n", DateTime.Now.ToLongTimeString(),
DateTime.Now.ToLongDateString());
log += String.Format(" :{0}\n", message);
log += String.Format("-------------------------------");
writer.WriteLine(log);
}
return log;
}
To clarify, the using block calls Dispose() on the StreamWriter. This flushes content to the file.
Related
I want to add simple logger in to my app.
For this purpose I want to use StreamWriter.
Code:
private StreamWriter OutputStream;
OutputStream = new StreamWriter(this.LogFilePath, true);
// .... message - log from app
DateTime now = DateTime.Now;
message = string.Format("[{0:yyyy-MM-dd H:mm:ss}] {1}", now, message
if (OutputStream != null)
{
OutputStream.WriteLine(message);
OutputStream.Flush();
}
As result all strings are correctly captured and output is correct, but sometimes it can write empty string with invisible characters at the end:
sample:
[1970-08-31 14:56:26] Command response -> !c:65:f9:1b:82:97
and if i check this with some tool that can show invisible characters, I can see next:
As result ~600 lines of log - 125 mb.
I have found that reason could be next:
That happens. When you append a file first its size is corrected in
the directory (and that's transactional in NTFS) and then the actual
new data is written. There's good chance that if you shut down the
system you end up with a file appended with lots of null bytes because
data writes are not transactional unlike metadata (file size) writes.
There's no absolute solution to this problem.
Also tried to
check characters with isControl other similar checks;
tried to Trim last characters;
checked docs - looks like all correct
Any advice?
In case someone faced with same issue - reason for me unknown and i may only guess.... but I rewrite logic with log system and bug disappear:
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using UnityEngine;
public class EventLogger : MonoBehaviour
{
private string logFileName = "btlog.txt";
public bool EchoToConsole = true;
public bool AddTimeStamp = true;
public bool EnableFileStorage = true;
private string LogFilePath
{
get
{
return Path.Combine(Application.persistentDataPath, logFileName);
}
}
private static EventLogger Singleton = null;
const string format = "yyyy-MM-dd HH:mm:ss.fffffff";
public static EventLogger Instance
{
get { return Singleton; }
}
void Awake()
{
if (Singleton != null)
{
UnityEngine.Debug.LogError("Multiple EventLogger Singletons exist!");
return;
}
Singleton = this;
if (this.EnableFileStorage)
{
if (File.Exists(LogFilePath))
{
long length = new FileInfo(LogFilePath).Length;
int limit = 1024 * 1024 * 5; // 5mb
if (length > limit)
{
File.Delete(LogFilePath);
Log("log file removed");
}
}
Log("-------------------");
Log("NEW SESSION STARTED");
}
}
private async Task Write(string message)
{
if (this.EnableFileStorage)
{
if (AddTimeStamp)
{
DateTime now = DateTime.Now;
string strDate = now.ToString(format);
string trimmed = new string(message.Where(c => !char.IsControl(c)).ToArray());
message = string.Format("[{0}] {1}", strDate, trimmed);
}
using (StreamWriter outputStream = new StreamWriter(this.LogFilePath, true))
{
await outputStream.WriteLineAsync(message);
}
if (EchoToConsole)
{
UnityEngine.Debug.Log(message);
}
}
}
[Conditional("DEBUG"), Conditional("PROFILE")]
public static void Log(string Message)
{
if (EventLogger.Instance != null)
{
_ = EventLogger.Instance.Write(Message);
}
else
{
UnityEngine.Debug.Log(Message);
}
}
}
I am adding logs to my projects. The logline shows timestamp + current method + current program. I do know how to check currentmethod, but this will always be the loggingmethod itself. How do I find the method that called for the logging method?
The coded attached is doing exactly what I want.
But it would be nice to add the part that gives the current method and project (this.GetType().Name+ currentMethodName) in the actual LogMessageToFile method.
LOGGER.cs
using System.IO;
using System;
namespace LoggerSpace
{
class Logger {
public string GetTempPath()
{
string path = System.Environment.GetEnvironmentVariable("TEMP");
if (!path.EndsWith("\\")) path += "\\";
return path;
}
public void LogMessageToFile(string msg)
{
System.IO.StreamWriter sw = System.IO.File.AppendText(
GetTempPath() + "My Log File.txt");
Console.Write(GetTempPath());
try
{
string logLine = System.String.Format(
"{0:G}: {1}.", System.DateTime.Now, msg);
sw.WriteLine(logLine);
}
finally
{
sw.Close();
}
}
}
}
CODEwithADDEDlogging.cs
using LoggerSpace;
using System.Diagnostics;
private void button2_Click(object sender, EventArgs y)
{
//LOG PART
var st = new StackTrace();
var sf = st.GetFrame(0);
var currentMethodName = sf.GetMethod();
var instance = new Logger();
instance.LogMessageToFile("Button Clicked, Clicktrader, from:"+ this.GetType().Name+ currentMethodName);
}
Use the CallerMemberNameAttribute for this. Something like this:
void LogSomething(string message, [CallerMemberName]string caller="")
{
// caller will have the function or property name of the caller to LogSomething
}
You can also get the source file name and line number with other attributes, all described in the link.
public void LogMessageToFile(string msg,
[CallerMemberName]string propertyName = null
[CallerFilePath] string sourceFilePath = ""
[CallerLineNumber] int sourceLineNumber = 0)
{
}
lets you catch the calling function, the file it was called from and the line number within that file.
Actually, I want to log the data in such a way that it should have the methods that the application goes through in c#, and if there is an error then the error content also should be logged. the problem is where to call the log methods inside of catch or inside of every method? as I have nearly 200 methods.
I wrote the code like this:
public static bool Logging(System.Reflection.MethodBase methodInfo)
{
var fullMethodName = methodInfo.DeclaringType.FullName + "." + methodInfo.Name;
if (error_flag == false)
{
using (StreamWriter outputFile = new StreamWriter(path + #"\log.txt", true))
{
outputFile.WriteLine(fullMethodName + ": OK");
}
}
else
{
using (StreamWriter outputFile = new StreamWriter(path + #"\log.txt", true))
{
outputFile.WriteLine("\n\n --> Error in : " + fullMethodName);
}
}
return true;
}
//Logging Method 2
public static bool WriteErrorLog(Exception ErrorMessage)
{
using (StreamWriter outputFile = new StreamWriter(path + #"\log.txt", true))
{
outputFile.WriteLine("{0} Exception caught.", ErrorMessage);
}
return true;
}
and I have to call those methods from where??
I would suggest the following approach:
static void Main()
{
try
{
Log.Info("Start of process");
string input = StepOne();
StepTwo(input);
Log.Info("End of process");
}
catch(Exception e)
{
Log.Error(e);
}
}
public static string StepOne()
{
Log.Info("StepOne Completed");
return "Step One";
}
public static void StepTwo(string input)
{
Log.Info("StepTwo, input: " + input);
throw new ArgumentNullException("input");
}
Rather than rolling your own, use an existing logging framework, for example Log4Net.
Add a try catch at the highest possible layer, let errors bubble up until you can actually do something sensible with them.
Add logging messages to functions where it is sensible to do so, this will be the most useful information, for example you can log method inputs.
Avoid using reflection to get the method name, you probably don't need to log individual method names. All of that information will be in the stack trace anyway.
I have a c# app (Windows Service) that fires a timer event that reads files in a directory and sends out SMS using the data in the files. Next time the event fires, it tries to move the processed files in the "Processed" directory to a "Completed" directory before processing the new files. I keep getting a "File in use by another process" exception, although I am pretty sure that I dispose of everything that uses the files. If I stop the service and start it again, the files is released. Any ideas?
//Code that fires the timer
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Timers;
namespace SmsWindowsService
{
public partial class SmsWindowsService : ServiceBase
{
private static System.Timers.Timer aTimer;
public SmsWindowsService()
{
InitializeComponent();
if (!System.Diagnostics.EventLog.SourceExists("MatterCentreSMSSource"))
{
System.Diagnostics.EventLog.CreateEventSource(
"MatterCentreSMSSource", "MatterCentreSMSLog");
}
elMatterCentreSMS.Source = "MatterCentreSMSSource";
elMatterCentreSMS.Log = "MatterCentreSMSLog";
}
protected override void OnStart(string[] args)
{
string logText = string.Empty;
logText = "MatterCentreSMS Service started successfully on " + DateTime.Now;
WriteEventLog(logText);
//Create a timer with a ten second interval.
aTimer = new System.Timers.Timer(10000);
//Hook up the Elapsed event for the timer.
aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
//Set the Interval to 5 minutes.
//aTimer.Interval = 300000;
aTimer.Interval = 60000;
aTimer.Enabled = true;
// If the timer is declared in a long-running method, use
// KeepAlive to prevent garbage collection from occurring
// before the method ends.
//GC.KeepAlive(aTimer);
GC.Collect();
}
protected override void OnStop()
{
string logText = string.Empty;
logText = "MatterCentreSMS Service stopped on " + DateTime.Now;
WriteEventLog(logText);
}
private void WriteEventLog(string logText)
{
elMatterCentreSMS.WriteEntry(logText);
}
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
string ex = string.Empty;
SendSms s = new SendSms();
ex = s.ProcessSms();
if (ex.Length > 1)
WriteEventLog(ex);
//ex = RestartService("SmsWindowsService", 60000);
//WriteEventLog(ex);
}
public string RestartService(string serviceName, int timeoutMilliseconds)
{
ServiceController service = new ServiceController(serviceName);
try
{
int millisec1 = Environment.TickCount;
TimeSpan timeout = TimeSpan.FromMilliseconds(timeoutMilliseconds);
service.Stop();
service.WaitForStatus(ServiceControllerStatus.Stopped, timeout);
// count the rest of the timeout
int millisec2 = Environment.TickCount;
timeout = TimeSpan.FromMilliseconds(timeoutMilliseconds - (millisec2 - millisec1));
service.Start();
service.WaitForStatus(ServiceControllerStatus.Running, timeout);
return "MatterCentreSMS Service successfully restarted on " + DateTime.Now;
}
catch (Exception e)
{
return Convert.ToString(e);
}
}
}
}
//Code that reads the file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
namespace SmsWindowsService
{
class Message
{
private string filePath;
public Message(string filePath)
{
this.filePath = filePath;
}
public string readSMS(string filePath)
{
const string searchmessage = "[B-->]";
StreamReader smsmessage = new StreamReader(filePath);
try
{
FileInfo filenameinfo = new FileInfo(filePath);
if (filenameinfo.Exists == false)
throw new SMSReaderException(String.Format("SMS Message {0} cannot be found ...", filePath), filePath);
smsmessage = filenameinfo.OpenText();
string smsoutput = smsmessage.ReadToEnd();
int endpos = smsoutput.IndexOf(searchmessage);
smsoutput = smsoutput.Substring(endpos + searchmessage.Length);
smsoutput = smsoutput.Replace("&", "&");
smsoutput = smsoutput.Replace("\"", """);
smsoutput = smsoutput.Replace("'", "'");
filenameinfo = null;
smsmessage.Close();
smsmessage.Dispose();
return smsoutput;
}
catch(Exception e)
{
throw new Exception("Help", e.InnerException);
}
finally
{
smsmessage.Close();
smsmessage.Dispose();
}
}
}
public class SMSReaderException : System.IO.FileNotFoundException
{
public SMSReaderException(string message, string filename)
: base(message, filename)
{
}
}
}
//Code that connects to web service and send sms
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Data;
using System.IO;
using System.Net;
using System.Configuration;
using SmsWindowsService.EsendexSendSmsService;
namespace SmsWindowsService
{
class SendSms
{
string filePath = string.Empty;
string directoryPath = string.Empty;
string directoryPathProcessing = string.Empty;
string directoryPathCompleted = string.Empty;
string smsLogfileDirectory = string.Empty;
string smsLogfilePath = string.Empty;
string mattercentreSMS = string.Empty;
string messageBody = string.Empty;
string messageId = string.Empty;
string messageStatus = string.Empty;
string dateTodayString = string.Empty;
long mobileNumber;
EsendexSendSmsService.SendService send;
public SendSms()
{
directoryPath = ConfigurationSettings.AppSettings[#"directoryPath"];
directoryPathProcessing = ConfigurationSettings.AppSettings[#"directoryPathProcessing"];
directoryPathCompleted = ConfigurationSettings.AppSettings[#"directoryPathCompleted"];
smsLogfileDirectory = ConfigurationSettings.AppSettings[#"smsLogfileDirectory"];
dateTodayString = DateTime.Now.ToString("yyyy/MM/dd");
smsLogfilePath = smsLogfileDirectory + dateTodayString.Replace(#"/", "_") + ".txt";
send = new EsendexSendSmsService.SendService();
}
public string ProcessSms()
{
string ex = string.Empty;
try
{
DirectoryInfo di = new DirectoryInfo(directoryPathProcessing);
ex = MoveFilesToCompleted(directoryPathProcessing, directoryPathCompleted);
if (ex.Length > 1)
return ex;
ex = MoveFilesToProcessing(directoryPath, directoryPathProcessing);
if (ex.Length > 1)
return ex;
FileInfo[] subFilesProcessing = di.GetFiles();
foreach (FileInfo subFile in subFilesProcessing)
{
filePath = directoryPathProcessing + subFile.Name;
Message sms = new Message(filePath);
mattercentreSMS = sms.readSMS(filePath);
MessageDetails d = new MessageDetails(mattercentreSMS);
mobileNumber = d.GetMobileNumber();
messageBody = d.GetMessageBody();
ex = SetHeader();
if (ex.Length > 1)
return ex;
ex = SetProxy();
if (ex.Length > 1)
return ex;
//Send the message and get the returned messageID and send status
messageId = send.SendMessage(Convert.ToString(mobileNumber), messageBody, EsendexSendSmsService.MessageType.Text);
messageStatus = Convert.ToString(send.GetMessageStatus(messageId));
ex = WriteLogFile(messageId, subFile.Name, messageStatus);
if (ex.Length > 1)
return ex;
send.Dispose();
}
di = null;
subFilesProcessing = null;
return ex;
}
catch (Exception e)
{
return Convert.ToString(e);
}
}
private string MoveFilesToCompleted(string directoryPathProcessing, string directoryPathCompleted)
{
DirectoryInfo din = new DirectoryInfo(directoryPathProcessing);
try
{
FileInfo[] subFiles = din.GetFiles();
foreach (FileInfo subFile in subFiles)
{
subFile.MoveTo(directoryPathCompleted + subFile.Name);
}
subFiles = null;
return "";
}
catch (Exception e)
{
return Convert.ToString(e);
}
finally
{
din = null;
}
}
private string MoveFilesToProcessing(string directoryPath, string directoryPathProcessing)
{
DirectoryInfo din = new DirectoryInfo(directoryPath);
try
{
FileInfo[] subFiles = din.GetFiles();
foreach (FileInfo subFile in subFiles)
{
subFile.MoveTo(directoryPathProcessing + subFile.Name);
}
subFiles = null;
return "";
}
catch (Exception e)
{
return Convert.ToString(e);
}
finally
{
din = null;
}
}
private string SetHeader()
{
try
{
//Setup account details in the header
EsendexSendSmsService.MessengerHeader header = new EsendexSendSmsService.MessengerHeader();
header.Account = ConfigurationSettings.AppSettings[#"smsServiceUrl"];
header.Username = ConfigurationSettings.AppSettings[#"smsServiceUsername"];
header.Password = ConfigurationSettings.AppSettings[#"smsServicePassword"];
// set the SOAP header Authentication values
send.MessengerHeaderValue = header;
return "";
}
catch (Exception e)
{
return Convert.ToString(e);
}
}
private string SetProxy()
{
try
{
//Create a web proxy object as the proxy server block direct request to esendex
WebProxy myProxy = new WebProxy(ConfigurationSettings.AppSettings[#"proxyaddress"], true);
myProxy.Credentials = new NetworkCredential(ConfigurationSettings.AppSettings[#"username"], ConfigurationSettings.AppSettings[#"password"]);
WebRequest.DefaultWebProxy = myProxy;
send.Proxy = myProxy;
return "";
}
catch (Exception e)
{
return Convert.ToString(e);
}
}
private string WriteLogFile(string messageId, string smsFileName, string messageStatus)
{
try
{
if (File.Exists(smsLogfilePath))
{
//file is not empty - append log entry to file
using (StreamWriter writeSmsLog = File.AppendText(smsLogfilePath))
{
writeSmsLog.WriteLine(messageId + " " + smsFileName + " " + DateTime.Now + " " + messageStatus);
writeSmsLog.Close();
}
}
else
{
FileStream fs = File.OpenWrite(smsLogfilePath);
fs.Flush();
fs.Close();
fs.Dispose();
using (StreamWriter writeSmsLog = new StreamWriter(smsLogfilePath, true))
{
writeSmsLog.WriteLine("Message_ID File_Name Date_Sent Status");
writeSmsLog.WriteLine("======================================================================================================================================");
writeSmsLog.WriteLine(messageId + " " + smsFileName + " " + DateTime.Now + " " + messageStatus);
writeSmsLog.Close();
}
}
return "";
}
catch (Exception e)
{
return Convert.ToString(e);
}
}
}
}
Any ideas?
You're running a virus checker in an entirely different process. It is detecting that the file has changed and is locking it momentarily in order to check it to see if the edit you just performed to the file introduced a virus. It'll unlock it in a couple of milliseconds.
Disabling your virus checker is a bad idea. Instead, you're just going to have to live with it; write your code to be robust in a world where there are lots of processes vying for locks on files.
StreamReader smsmessage = new StreamReader(filePath);
try
{
FileInfo filenameinfo = new FileInfo(filePath);
....
smsmessage = filenameinfo.OpenText();
...
You are initializing smsmessage twice, but only disposing one of those instances. The first line constructs a StreamReader, and then you overwrite your reference to that instance with the instance created by filenameinfo.OpenText(). That leaves you with an instance that no longer has any references and hasn't been disposed. That instance might be holding a lock on the file and you have no guarantees on when it will be disposed. Even if it isn't holding a lock, you should still fix this.
I have a slight problem. What my application is supose to do, is to watch a folder for any newly copied file with the extention '.XSD' open the file and assign the lines to an array. After that the data from the array should be inserted into a MySQL database, then move the used file to another folder if it's done.
The problem is that the application works fine with the first file, but as soon as the next file is copied to the folder I get this exception for example: 'The process cannot access the file 'C:\inetpub\admission\file2.XPD' because it is being used by another process'.
If two files on the onther hand is copied at the same time there's no problem at all.
The following code is on the main window:
public partial class Form1 : Form
{
static string folder = specified path;
static FileProcessor processor;
public Form1()
{
InitializeComponent();
processor = new FileProcessor();
InitializeWatcher();
}
static FileSystemWatcher watcher;
static void InitializeWatcher()
{
watcher = new FileSystemWatcher();
watcher.Path = folder;
watcher.Created += new FileSystemEventHandler(watcher_Created);
watcher.EnableRaisingEvents = true;
watcher.Filter = "*.XPD";
}
static void watcher_Created(object sender, FileSystemEventArgs e)
{
processor.QueueInput(e.FullPath);
}
}
As you can see the file's path is entered into a queue for processing which is on another class called FileProcessor:
class FileProcessor
{
private Queue<string> workQueue;
private Thread workerThread;
private EventWaitHandle waitHandle;
public FileProcessor()
{
workQueue = new Queue<string>();
waitHandle = new AutoResetEvent(true);
}
public void QueueInput(string filepath)
{
workQueue.Enqueue(filepath);
if (workerThread == null)
{
workerThread = new Thread(new ThreadStart(Work));
workerThread.Start();
}
else if (workerThread.ThreadState == ThreadState.WaitSleepJoin)
{
waitHandle.Set();
}
}
private void Work()
{
while (true)
{
string filepath = RetrieveFile();
if (filepath != null)
ProcessFile(filepath);
else
waitHandle.WaitOne();
}
}
private string RetrieveFile()
{
if (workQueue.Count > 0)
return workQueue.Dequeue();
else
return null;
}
private void ProcessFile(string filepath)
{
string xName = Path.GetFileName(filepath);
string fName = Path.GetFileNameWithoutExtension(filepath);
string gfolder = specified path;
bool fileInUse = true;
string line;
string[] itemArray = null;
int i = 0;
#region Declare Db variables
//variables for each field of the database is created here
#endregion
#region Populate array
while (fileInUse == true)
{
FileStream fs = new FileStream(filepath, FileMode.Open, FileAccess.Read,
FileShare.ReadWrite);
StreamReader reader = new StreamReader(fs);
itemArray = new string[75];
while (!reader.EndOfStream == true)
{
line = reader.ReadLine();
itemArray[i] = line;
i++;
}
fs.Flush();
reader.Close();
reader.Dispose();
i = 0;
fileInUse = false;
}
#endregion
#region Assign Db variables
//here all the variables get there values from the array
#endregion
#region MySql Connection
//here the connection to mysql is made and the variables are inserted into the db
#endregion
#region Test and Move file
if (System.IO.File.Exists(gfolder + xName))
{
System.IO.File.Delete(gfolder + xName);
}
Directory.Move(filepath, gfolder + xName);
#endregion
}
}
The problem I get occurs in the Populate array region. I read alot of other threads and was lead to believe that by flushing the file stream would help...
I am also thinking of adding a try..catch for if the file process was successful, the file is moved to gfolder and if it failed, moved to bfolder
Any help would be awesome
Tx
You're not disposing of your FileStream instance, so a lock remains on the file. Change your code to use using blocks:
using (var fileStream = new FileStream(...))
{
using (var reader = new StreamReader(fileStream))
{
}
}
These using blocks will ensure the instances are correctly disposed of.
Also, why are you calling Flush on the file stream? You're not writing anything with it...
I would suggest :
1° use the using syntax on StreamReader
2° use the using syntax on FileStream