/// <summary>
		/// Logs message to file. Depending on Alerts configuration, this may also result in sending an SMTP message.
		/// </summary>
		/// <param name="message">Message text to be logged (and may be sent)</param>
		/// <param name="logFile">Log file. If omitted, LogFilePathName, AppSettings["LogFile"] or Log.Log files will be used</param>
		/// <param name="insertBlankLines">Inserts empty lines before and after the message when writing to the log.</param>
		/// <param name="logLevel">Defines what the minimum level of logging should be configured (see LogLevel property) in order to log this message and AlertsLogLevel property to mail this message. This allows changing the level of details logged without changing and recompiling the application.Default=ExceptionsOnly</param>
		/// <param name="localLogLevel">Redefines global log level for this particular call</param>
		/// <param name="messageID">Unique ID of the message. It is required so that the message does not cause Alert more than once in a period configured (AlertResetIntervalMinutes property)</param>
		public static void LogMessage(string message, string logFile, enuBlankLines insertBlankLines, LogLevels logLevel, int localLogLevel, string messageID){
			#region Immediately check if we are logging before collecting any info
			if(_blnUseLowestLogLevelIfLocalLevelConfigured && localLogLevel>0){//If it is 0 it means it is not set and -1 is passed to this proc when the param is omitted
				if(((int)logLevel>_intLogLevel||(int)logLevel>localLogLevel) && (int)logLevel>_intAlertsLogLevel) return;
			}
			else{
				if((int)logLevel>_intLogLevel && (int)logLevel>localLogLevel && (int)logLevel>_intAlertsLogLevel) return;
			}
			#endregion
			
			string strAction="Start";
			string strMessageToThrowToCaller="";
			System.Text.StringBuilder sb=new System.Text.StringBuilder(TimeStamp);
			try{
				string strMessageFallBack="";
				
				#region Get the calling method
				System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace();
				System.Diagnostics.StackFrame stackFrame;
				System.Reflection.MethodBase methodBase=null;
				for(int i=1;i<10;i++){//The trace may be full of our internal stuff which we don't want
					stackFrame = stackTrace.GetFrame(i);
					methodBase = stackFrame.GetMethod();
					if(methodBase.DeclaringType.Name!="Logging")break; 
				}
				#endregion
				#region Create the message's meta tag. It is needed for both - Log and Alert
				strAction="MetaTag";
				sb.Append("\t");
				sb.Append(System.Threading.Thread.CurrentThread.GetHashCode().ToString());
				sb.Append(" ");sb.Append(System.Threading.Thread.CurrentThread.Name);
				sb.Append("\t");sb.Append(((int)logLevel));sb.Append("/");sb.Append(localLogLevel);sb.Append("/");sb.Append(_intLogLevel);sb.Append("/");sb.Append(_intAlertsLogLevel);
				sb.Append("\t");sb.Append(System.Environment.MachineName);
				sb.Append("\t");sb.Append(System.Environment.UserName);
				sb.Append("\t");sb.Append(methodBase.DeclaringType.Name);sb.Append(".");sb.Append(methodBase.Name);sb.Append(": ");
				sb.Append(message);
				methodBase=null;stackFrame=null;stackTrace=null;
				#endregion
				
				#region Log To File
				//NOT EVERY USER CAN WRITE TO EVENT LOG, SO WE KEEP IT AS A FALL-BACK
				//WHEN LOG FILE IS UNACCESSIBLE WE ATTEMPT TO LOG TO BACKUP LOG AND EVENT LOG.
				if((_blnUseLowestLogLevelIfLocalLevelConfigured && ((int)logLevel<=_intLogLevel && ((int)logLevel<=localLogLevel || localLogLevel<=0))) 
				|| !_blnUseLowestLogLevelIfLocalLevelConfigured && ((int)logLevel<=_intLogLevel || (int)logLevel<=localLogLevel)){
					//Then we actually worry about the file we are writing into
					logFile=GetLogFilePathName(logFile);//That exuses every caller from calling GetLogFilePathName(logFile), especially if this method figures out that we are not writing this call
					
					if(! _blnLogFileSizeExceeded){//That refers to the globally-configured file unless there happened to be a local file that tripped that flag
						#region Check File Size
						strAction="File Size";
						try {
							// Check size of the log file and do not allow more than 2 Mb
							System.IO.FileInfo fi = new System.IO.FileInfo(logFile);
							if(fi.Exists){
								if(fi.Length > _lngLogFileSizeLimitBytes){
									sb=new System.Text.StringBuilder("\r\n\r\nThe log file size has exceeded ");
									sb.Append(_srtLogFileSizeLimitMb);
									sb.Append("Mb limit, and no logging will be performed to this file anymore. Fix the cause of the excessive logging or reconfigure the limit.");
									_blnLogFileSizeExceeded = true;
									#region fix the log level to a level of sending an alert, so that the user is notified about not writing to the log
									if((int)logLevel > _intAlertsLogLevel){
										logLevel=(LogLevels)_intAlertsLogLevel;
									}
									#endregion
								}
							}
						}catch{}//do not abuse resources.
						#endregion
						
						#region Insert blank lines as requested
						switch(insertBlankLines){
							case enuBlankLines.None:
							break;
							case enuBlankLines.DoubleTopAndBottom://THat what was in previous implementation
								sb.Insert(0,Environment.NewLine+Environment.NewLine);sb.AppendLine();
							break;
							case enuBlankLines.TopAndBottom:
								sb.Insert(0,Environment.NewLine);sb.AppendLine();
							break;
							case enuBlankLines.Top:
								sb.Insert(0,Environment.NewLine);
							break;
							case enuBlankLines.Bottom:
								sb.AppendLine();
							break;
							default:
								throw new NotImplementedException("insertBlankLines="+insertBlankLines.ToString()+" is not implemented");
						}
						#endregion
						
						strAction="Log to " + logFile;
						strMessageFallBack=WriteLineToFileWithExceptionReturned(logFile,sb.ToString());

						if(strMessageFallBack!=""){
							#region fix the log level to a level of sending an alert, so that the user is notified about failure of writing to the log
							if((int)logLevel > _intAlertsLogLevel){
								logLevel=(LogLevels)_intAlertsLogLevel;
							}
							#endregion
									
							#region Fall back to other media
							//We need to alter the message itself because now it also goes to Email alert
							sb.Insert(0,TimeStamp + "\tCould not log message to [" + logFile+"] file. \r\nReason: ["+strMessageFallBack+"]\r\nMessage attempted to log:[");
							sb.Append("]");
							strAction="Log to fallback file";
							WriteLineToFileWithExceptionReturned(LogFileFallback,"\r\n"+sb.ToString());
							
							//try to write to EventLog (will need permission to create a registry key, which may be haemorrhoid
							//To enable writing to event log, fire regedt32.exe tool and navigate to
							// HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog key. Select Permissions in Security menu (on top)
							// and add ASPNET (for website) or another account you are using for service, etc.
							strAction="Log to EventLog";
							try{
								//System.Security.Policy.ApplicationSecurityInfo asi = new System.Security.Policy.ApplicationSecurityInfo(AppDomain.CurrentDomain.ActivationContext);
								//System.Diagnostics.EventLog.WriteEntry(asi.ApplicationId.Name,strMessageFallBack,System.Diagnostics.EventLogEntryType.Error);
								System.Diagnostics.EventLog.WriteEntry(AppDomain.CurrentDomain.FriendlyName,strMessageFallBack,System.Diagnostics.EventLogEntryType.Error);
							}
							catch(Exception exx){//report the problem to fallback file
								strAction="Log EventLog failure to LogFileFallback";
								sb.Insert(0,TimeStamp+"\tCould not log message to EventLog when logging to the primry log file failed\r\nReason: ["+exx.Message+"], User="******"\r\nMessage attempted to log:[");
								sb.Append("]");
								string strFailure=WriteLineToFileWithExceptionReturned(LogFileFallback,"\r\n"+sb.ToString());
									if(strFailure!=""){
										strAction="Create message to throw to caller";
										strMessageToThrowToCaller=
										"Failed to write to fallback log file ["+LogFileFallback+"] Error=["+strFailure
										+"\r\nAlso failed to write to either ["+logFile+"] OR Windows Event Log. Error:{"+strMessageFallBack
										+"}.\r\nTo enable writing to Event Log, start regedit.exe (regedt32.exe in older Windows) tool and navigate to "
										+@"//HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog key. Select Permissions in Security menu (on top) or in context menu "
										+"and add ASPNET (for website) or another account you are using for service, etc.\r\n"
										+"This account also needs Write permission for directory configured for the log file, and ideally for directory where this dll resides, for fail-back logging.";
									}
							}
							#endregion
						}//if(strMessageFallBack!="")
					}
					
					
				}
				#endregion
				
				#region ALERTS
				if((int)logLevel <= _intAlertsLogLevel){//We do not bother with local log levels when sending alerts. Local log levels are for active troubleshooting.
					bool blnSend=false;
					#region Figure out message key
					string strKey="";
					strAction="Figure out message key";
					if(messageID.Length > 0){
						strKey=messageID;
					}
					else{
						if(_blnAlertsUseMessageText){
							if(message.Length > MESSAGE_RECOGNITION_LEN){
								strKey=message.Substring(0,MESSAGE_RECOGNITION_LEN);
							}
							else{strKey=message;}
						}
					}
					#endregion
					#region Update Journal with new key
					strAction="Update Journal with new key";
					if (strKey.Length > 0) {//we can surpress repetitive alerts from this piece of code
						if(_htbAlerts.ContainsKey(strKey)){
							DateTime dtm = (DateTime)_htbAlerts[strKey];
							if(dtm.AddMinutes(_dblAlertResetIntervalMinutes)<DateTime.Now){
								//Update the key
								_htbAlerts[strKey]=DateTime.Now;
								blnSend=true;
							}
						}
						else{
							_htbAlerts.Add(strKey,DateTime.Now);
							blnSend=true;
						}
					}
					#endregion
					#region Ensure that the journal does not grow too big
					if(_htbAlerts.Count>_intAlertsJournalSize){
						DateTime dtmMinDate = DateTime.MaxValue;
						string strMinDateKey = "";
						DateTime dtmCutOff = DateTime.Now.AddMinutes(-_dblAlertResetIntervalMinutes);
						string strKeysToRemove="";
						strAction="List Journal messages to remove";
						System.Collections.IDictionaryEnumerator enu = _htbAlerts.GetEnumerator();
						try{
							while(enu.MoveNext()){
								DateTime dtmCurr = (DateTime)enu.Value;
								string strCurr = (string)enu.Key;
								if(dtmCurr<dtmCutOff){
									strKeysToRemove+="|"+strCurr;
								}
								if(dtmCurr<dtmMinDate){
									dtmMinDate=dtmCurr;
									strMinDateKey=strCurr;
								}
							}
						}
						catch{}//concurrent process may invalidate the enumerator at any time. Let it be so.
						enu=null;
						
						strAction="Remove old messages from Journal";
						if(strKeysToRemove.Length>0){
							char[] chaSeparator = {'|'};
							string[] strKeysBeRemoved = strKeysToRemove.Remove(0,1).Split(chaSeparator);
							for(int i=0;i<strKeysBeRemoved.Length;i++){
								try{
									_htbAlerts.Remove(strKeysBeRemoved[i]);
								}
								catch{}
							}
						}
						else{//There are no obsolete keys. Remove the oldest one
							try{
								_htbAlerts.Remove(strMinDateKey);
							}
							catch{}
						}
					}
					#endregion
					
					#region Send the Alert
					if(blnSend){
						strAction="Send SMTP message";
						string strSmtpErrors;
						bool blnSmtpSent=SendSMTP(_strAlertsRecipients,"EXCEPTION OCCURED"
						,System.AppDomain.CurrentDomain.FriendlyName + " at " + System.Environment.MachineName + ":\r\n\r\n"
						+sb.ToString()+ "\r\nSee log file "+ logFile+" for details.\r\n\r\n\r\n\r\n"
						,MailPriority.High, out strSmtpErrors);
						if (!blnSmtpSent ||strSmtpErrors!="") {
							if(!_blnLogFileSizeExceeded){
							strAction="Attempt writing SMTP failure to "+LogFilePathName;
								if(blnSmtpSent){
	//								WriteLineToFile(LogFilePathName,"\r\n"+TimeStamp+"\tThe Alert had been sent, but Primary SMTP servers failed: "+
									WriteLineToFileWithExceptionReturned(LogFilePathName,"\r\n"+TimeStamp+"\tThe Alert had been sent, but Primary SMTP servers failed: "+
									strSmtpErrors+"\r\n");//We do not care if the log file is accesible or not. We worried about this before
									//So we can warn about this problem by email
									strAction="Attempt sending SMTP failure by email";
									SendSMTP(_strAlertsRecipients,"EXCEPTION OCCURED"
									,System.AppDomain.CurrentDomain.FriendlyName + " at " + System.Environment.MachineName 
									+":\r\n\r\nCan't send messages using primary SMTP servers:\r\n"+strSmtpErrors
									+"\r\nSee log file "+ logFile+" for details.\r\n\r\n\r\n\r\n"
									,MailPriority.High, out strSmtpErrors);
								}
								else{
	//								WriteLineToFile(LogFilePathName,"\r\n"+TimeStamp+"\tCOULD NOT SEND ALERT: "+
									WriteLineToFileWithExceptionReturned(LogFilePathName,"\r\n"+TimeStamp+"\tCOULD NOT SEND ALERT: "+
									strSmtpErrors+"\r\n");//We do not care if the log file is accesible or not. We worried about this before
								}
							}
						}
					}
					#endregion
				}
				#endregion
			}
			catch(Exception e){//here we catch any unexpected exception
				sb=new System.Text.StringBuilder(TimeStamp);
				sb.Append("\tUNEXPECTED ERROR.\r\nAction=[");
				sb.Append(strAction);sb.Append("]\r\nError=[");sb.Append(e.Message);sb.Append("], User="******"\rLocationce=[");sb.Append(e.Source);sb.Append("]\r\nStack Trace=[");sb.Append(e.StackTrace);sb.Append("]");
//				WriteLineToFile("FallBack.log",strMessage);
				WriteLineToFileWithExceptionReturned(LogFileFallback,sb.ToString());
			}
			if(strMessageToThrowToCaller!="")throw new Exception(strMessageToThrowToCaller);
			
			#region Show Pop-up desktop form if asked and warranted
			if(_blnInteractive && (int)logLevel<3){//Warnings, Errors and Fatal
				frmError objErrorForm = new frmError();
				objErrorForm.txtError.Text = sb.ToString();
				objErrorForm.butViewLog.Tag=_strLogFilePathName;
				objErrorForm.ShowDialog();
			}
			#endregion
		}
		/// <summary>
		/// This overload is iseful when there is no actual exception in the system, but we want an event to treat as an exception
		/// </summary>
		/// <param name="exceptionMessage">Exception message</param>
		/// <param name="throwBack">Should an exception be thrown back into the system?</param>
		/// <param name="logFile">Log file if different from globally configured</param>
		/// <param name="exceptionID">Unique ID assigned in your code. Required so that the same exception does not send SMTP alerts every second</param>
		public static void LogException(string exceptionMessage,bool throwBack,string logFile,string exceptionID) {
			//logFile=GetLogFilePathName(logFile);
			string strMsg = exceptionMessage+((_blnSuppressAssembliesOutput)?"":GetAssembliesEngaged());
			LogMessage(strMsg,logFile,true,LogLevels.Fatal,-1,exceptionID);
			if (_blnInteractive) {
				frmError objErrorForm = new frmError();
				objErrorForm.txtError.Text = strMsg;
				objErrorForm.butViewLog.Tag=_strLogFilePathName;
				objErrorForm.ShowDialog();
			}
			if (throwBack) { throw new Exception(exceptionMessage); }
		}