using System; using System.IO; using System.Runtime.InteropServices; using System.Threading; using System.Web.Mail; using UDDI; namespace UDDI.Diagnostics { /// ******************************************************************** /// public enum SeverityType /// -------------------------------------------------------------------- /// /// Severity codes for error reporting. /// /// ******************************************************************** /// public enum SeverityType : int { None = 0, Error = 1, Warning = 2, FailAudit = 3, PassAudit = 4, Info = 5, Verbose = 6 } /// ******************************************************************** /// public enum CategoryType /// -------------------------------------------------------------------- /// /// Category codes for specifying where an error occurred. /// /// ******************************************************************** /// public enum CategoryType : int { None = 0, Config = 1, Soap = 2, Data = 3, Authorization = 4, Website = 5, Replication = 6 } /// ******************************************************************** /// public enum OperatorMessageType /// -------------------------------------------------------------------- /// /// Error message codes for the various operator messages. /// /// ******************************************************************** /// public enum OperatorMessageType : int { None = 100, // // Client messages (100 level) // InvalidUserId = 101, // // Server messages (200 level) // ConfigError = 200, CannotReadSettings = 201, MissingSetting = 202, ConfigInvalid = 203, UnableToPublishCounter = 204, CouldNotCreateEventLog = 205, PassportNotConfigured = 206, UnknownLoginURL = 207, PassportSiteUnavailable = 208, CannotRetrieveClientXml = 209, InvalidTicketFormat = 210, StartingReplicationSession = 211, StartingReplicationWithNode = 212, EndingReplicationWithNode = 213, ReplicationSessionSummary = 214, NoReplicationOperators = 215, CannotRetrieveHighWaterMarks = 216, ErrorCommunicatingWithNode = 217, ValidationError = 218, UnknownOperator = 219, CouldNotSendMail = 220, InvalidKey = 221 // // if you exceed 300, you must edit uddievents.mc and add more event numbers // } /// ******************************************************************** /// public class Debug /// -------------------------------------------------------------------- /// /// Manages error reporting and logging. /// /// ******************************************************************** /// public class Debug { private static ReaderWriterLock readWriteLock = new ReaderWriterLock(); // // The default values of these settings should be kept in sync with the values stored in the UDO_config table // in the database. // private static CachedSetting debuggerLevel = new CachedSetting( "Debug.DebuggerLevel", SeverityType.Verbose ); private static CachedSetting eventLogLevel = new CachedSetting( "Debug.EventLogLevel", SeverityType.Warning ); private static CachedSetting fileLogLevel = new CachedSetting( "Debug.FileLogLevel", SeverityType.None ); // // SECURITY: Default location of the log file needs to be changed // It should not go to the root of the c: drive. This location may not be // accessible. // private static CachedSetting logFilename = new CachedSetting( "Debug.LogFilename", @"c:\uddi.log" ); private static DateTime lastLogEvent = DateTime.Now; private static TextWriter textStream = null; /// **************************************************************** /// static Debug [constructor] /// ---------------------------------------------------------------- /// /// /// **************************************************************** /// static Debug() { // // Register a configuration change handler for the logFilename // setting. // logFilename.ConfigChange += new Config.ConfigChangeHandler( LogFilename_OnChange ); // // Register a configuration change handler for the file log level setting. When this // setting becomes SeverityType.None, we'll release our handle to the log file (if we have one). // fileLogLevel.ConfigChange += new Config.ConfigChangeHandler( FileLogLevel_OnChange ); } /// **************************************************************** /// private FileLogLevel_OnChange [static] /// ---------------------------------------------------------------- /// /// Handler for file log level configuration change events. /// /// **************************************************************** /// private static void FileLogLevel_OnChange() { try { SeverityType newSeverity = ( SeverityType ) fileLogLevel.GetInt(); // // If logging is turned off, release our handle to the log file. // if( SeverityType.None == newSeverity ) { ReleaseLogFile(); } } catch { // // We should never get any exceptions from this, but eat them if we do. We don't want to // put our file logging in an inconsistent state. // } } /// **************************************************************** /// private LogFilename_OnChange [static] /// ---------------------------------------------------------------- /// /// Handler for logFilename configuration change events. /// /// **************************************************************** /// private static void LogFilename_OnChange() { ReleaseLogFile(); } private static void ReleaseLogFile() { // // Acquire the writer lock. // readWriteLock.AcquireWriterLock( Timeout.Infinite ); try { // // Close the text stream, if open. We do this so that the file // logging method will reopen with the current log filename. // if( null != textStream ) { textStream.Close(); textStream = null; } } finally { readWriteLock.ReleaseWriterLock(); } } /// **************************************************************** /// public Enter [static] /// ---------------------------------------------------------------- /// /// Writes trace information indicating the beginning of /// execution of a function /// /// ---------------------------------------------------------------- /// /// The trace information generated by this function /// is published at the verbose level. /// /// **************************************************************** /// public static void Enter() { #if DEBUG System.Diagnostics.StackTrace trace = new System.Diagnostics.StackTrace( 1, false ); System.Reflection.MethodBase method = trace.GetFrame( 0 ).GetMethod(); Debug.Write( SeverityType.Verbose, CategoryType.None, "Entering " + method.ReflectedType.FullName + "." + method.Name + "..." ); #endif } /// **************************************************************** /// public Leave [static] /// ---------------------------------------------------------------- /// /// Writes trace information indicating the end of execution of /// a function /// /// ---------------------------------------------------------------- /// /// The trace information generated by this function /// is published at the verbose level. /// /// **************************************************************** /// public static void Leave() { #if DEBUG System.Diagnostics.StackTrace trace = new System.Diagnostics.StackTrace( 1, false ); System.Reflection.MethodBase method = trace.GetFrame( 0 ).GetMethod(); Debug.Write( SeverityType.Verbose, CategoryType.None, "Leaving " + method.ReflectedType.FullName + "." + method.Name ); #endif } /// **************************************************************** /// public OperatorMessage [static] /// ---------------------------------------------------------------- /// /// Writes an operator message to the event log. /// /// ---------------------------------------------------------------- /// /// The severity of the message. /// /// /// /// The category of the message. /// /// /// /// Operator message type. /// /// /// /// Message to write to the event log. /// /// **************************************************************** /// public static void OperatorMessage( SeverityType severity, CategoryType category, OperatorMessageType messageId, string message ) { try { // // Store the entry in the event log as an error. // WriteEventLog( severity, category, messageId, message ); // // Also write this as a warning level message to any debug message // listeners (other than event log, since we already logged this // message there). // if( (int)severity <= debuggerLevel.GetInt() ) { WriteDebugger( severity, category, "Operator message [" + messageId.ToString() + "]: " + message ); } if( (int)severity <= fileLogLevel.GetInt() ) { WriteFileLog( severity, category, "Operator message [" + messageId.ToString() + "]: " + message ); } } catch( Exception ) { } } /// **************************************************************** /// public Assert [static] /// ---------------------------------------------------------------- /// /// Checks for a condition and displays a message if false. /// /// ---------------------------------------------------------------- /// /// The condition to verify. If the condition is false, the /// given message is displayed. /// /// /// /// LocalizaitonKey to use for the Exception. /// /// ---------------------------------------------------------------- /// /// Calls to Assert are ignored in non-debug builds. /// /// **************************************************************** /// [System.Diagnostics.Conditional( "DEBUG" )] [System.Diagnostics.DebuggerHidden()] public static void Assert( bool condition, string message ) { if( false == condition ) { try { // // Build the assert message. We'll attempt to build a pretty stack // trace, eliminating our frame from the list. // string trace = System.Environment.StackTrace; int pos = trace.IndexOf( "Debug.Assert" ); if( pos >= 0 && pos < trace.Length - 1 ) { pos = trace.IndexOf( "at", pos + 1 ); if( pos >= 0 ) trace = " " + trace.Substring( pos ); } string text = "Assertion failed: " + message + "\r\n\r\n" + "Stack trace:\r\n" + trace; // // Write the assertion to the debugger and break. // WriteDebugger( SeverityType.Error, CategoryType.None, text ); System.Diagnostics.Debugger.Break(); } catch( Exception ) { } finally { throw new UDDIException( ErrorType.E_fatalError, message ); } } } /// **************************************************************** /// public VerifyKey [static] /// ---------------------------------------------------------------- /// /// Checks for a tModelKey of the form uuid:xxx...xxx /// /// ---------------------------------------------------------------- /// /// A string presumed to begin with "uuid:" and followed by a properly formed Guid /// /// /// ---------------------------------------------------------------- /// /// Message to display if the assertion fails. /// /// /// ---------------------------------------------------------------- /// /// Calls to this function are honored, even in non-debug /// builds. /// /// **************************************************************** /// [System.Diagnostics.DebuggerHidden()] public static void VerifyKey( string key ) { Debug.Verify( null != key, "UDDI_ERROR_INVALIDKEYPASSED_INVALIDTMODELKEY", ErrorType.E_invalidKeyPassed ); Debug.Verify( key.Length >= 5 && key.Substring( 0, 5 ).ToLower().Equals( "uuid:" ), "UDDI_ERROR_INVALIDKEYPASSED_NOUUIDONTMODELKEY", ErrorType.E_invalidKeyPassed ); Debug.Verify( 41 == key.Length, "UDDI_ERROR_INVALIDKEYPASSED_INVALIDTMODELKEYLENGTH" , ErrorType.E_invalidKeyPassed ); /*try { Debug.Verify( null != key, "A valid tModelKey is required", ErrorType.E_fatalError ); Debug.Verify( key.Length >= 5 && key.Substring( 0, 5 ).ToLower().Equals( "uuid:" ), "Specified tModelKey does not begin with uuid:" ); Debug.Verify( 41 == key.Length, "Specified tModelKey is not the correct length" ); } catch( Exception e ) { throw new UDDIException( ErrorType.E_invalidKeyPassed, e.Message ); }*/ } /// **************************************************************** /// public Verify [static] /// ---------------------------------------------------------------- /// /// Checks for a condition and displays a message if false. /// /// ---------------------------------------------------------------- /// /// The condition to verify. If the condition is false, the /// given message is displayed. /// /// /// /// Message to display if the assertion fails. /// /// ---------------------------------------------------------------- /// /// Calls to this function are honored, even in non-debug /// builds. /// /// **************************************************************** /// [System.Diagnostics.DebuggerHidden()] public static void Verify( bool condition, string message ) { Verify( condition, message, ErrorType.E_fatalError ); } /// **************************************************************** /// public Verify [static] /// ---------------------------------------------------------------- /// /// Checks for a condition and displays a message if false. /// /// ---------------------------------------------------------------- /// /// The condition to verify. If the condition is false, the /// given message is displayed. /// /// /// /// Message to display if the assertion fails. /// /// /// /// Specifies the type of error to throw should the condition /// evaluate to false. /// /// ---------------------------------------------------------------- /// /// Calls to this function are honored, even in non-debug /// builds. /// /// **************************************************************** [System.Diagnostics.DebuggerHidden()] public static void Verify( bool condition, string message, ErrorType errorType ) { Verify( condition,message,errorType,null ); } /// **************************************************************** /// public Verify [static] /// ---------------------------------------------------------------- /// /// Checks for a condition and displays a message if false. /// /// ---------------------------------------------------------------- /// /// The condition to verify. If the condition is false, the /// given message is displayed. /// /// /// /// Message to display if the assertion fails. /// /// /// /// Specifies the type of error to throw should the condition /// evaluate to false. /// /// /// Error string format arguments used for localized messages. /// /// ---------------------------------------------------------------- /// /// Calls to this function are honored, even in non-debug /// builds. /// /// **************************************************************** [System.Diagnostics.DebuggerHidden()] public static void Verify( bool condition, string message, ErrorType errorType, params object[] format ) { if( false == condition ) { try { // // Build the verify failure message. We'll attempt to // build a pretty stack trace, eliminating our frame from // the list. // string trace = System.Environment.StackTrace; int pos = trace.IndexOf( "Debug.Verify" ); if( pos >= 0 && pos < trace.Length - 1 ) { pos = trace.IndexOf( "at", pos + 1 ); if( pos >= 0 ) trace = " " + trace.Substring( pos ); } string text = "Verify failed: " + message + "\r\n\r\n" + "Stack trace:\r\n" + trace; // // Write the verify failure to the debugging logs. // Write( SeverityType.Error, CategoryType.None, text ); } catch( Exception ) { } finally { throw new UDDIException( errorType, message,format ); } } } /// **************************************************************** /// public VerifySetting [static] /// ---------------------------------------------------------------- /// /// Verifies that the specified setting exists, and writes an /// error to the debugger and operator log if not. /// /// ---------------------------------------------------------------- /// /// Configuration setting to verify. /// /// **************************************************************** /// [System.Diagnostics.DebuggerHidden()] public static void VerifySetting( string key ) { if( false == Config.SettingExists( key ) ) { OperatorMessage( SeverityType.Error, CategoryType.Config, OperatorMessageType.MissingSetting, "Missing required setting: " + key ); Verify( false, "Server configuration error" ); } } /// **************************************************************** /// public WriteIf [static] /// ---------------------------------------------------------------- /// /// Writes a message to the debugging logs if the specified /// condition is true. /// /// ---------------------------------------------------------------- /// /// The condition to verify. If the condition is true, the /// given message is displayed. /// /// /// /// The severity type. /// /// /// /// The category type. /// /// /// /// The message to log. /// /// **************************************************************** /// public static void WriteIf( bool condition, SeverityType severity, CategoryType category, string message ) { if( true == condition ) Write( severity, category, message ); } /// **************************************************************** /// public Write [static] /// ---------------------------------------------------------------- /// /// Writes a message to the debugging logs. /// /// ---------------------------------------------------------------- /// /// The Severity type. /// /// /// /// The category type. /// /// /// /// The message to log. /// /// ---------------------------------------------------------------- /// /// Debugging information is logged to the debugger, log file, /// and event log depending on the level of the following /// configuration settings: /// /// Debug.DebuggerLevel /// Debug.EventLogLevel /// Debug.FileLogLevel /// /// Where the trace level is the highest severity to log: /// 0 = none /// 1 = errors /// 2 = warnings /// 3 = failure audit messages /// 4 = success audit messages /// 5 = information messages /// 6 = all (verbose) /// /// **************************************************************** /// public static void Write( SeverityType severity, CategoryType category, string message ) { // // Write to the debugger if the level setting is high enough. // if( (int)severity <= debuggerLevel.GetInt() ) WriteDebugger( severity, category, message ); // // Write to the event log if the level setting is high enough. // if( (int)severity <= eventLogLevel.GetInt() ) WriteEventLog( severity, category, OperatorMessageType.None, message ); // // Write to the file log if the level setting is high enough. // if( (int)severity <= fileLogLevel.GetInt() ) WriteFileLog( severity, category, message ); } /// **************************************************************** /// public WriteDebugger [static] /// ---------------------------------------------------------------- /// /// Writes a message to the debugger. /// /// ---------------------------------------------------------------- /// /// The severity type. /// /// /// /// The category type. /// /// /// /// The message. /// /// **************************************************************** /// public static void WriteDebugger( SeverityType severity, CategoryType category, string message ) { try { string text = string.Format( "{0,-4} {1,-4} {2}", severity.ToString().Substring( 0, 4 ).ToUpper(), category.ToString().Substring( 0, 4 ).ToUpper(), message.Replace( "\r\n", "\r\n\t\t" ) ); // // Write out the debugger message. // System.Diagnostics.Trace.WriteLine( text ); } catch( Exception ) { // // WriteDebugger is used as a last ditch logging mechanism // in many cases. This should never throw an exception, // but if it does, there really isn't any more we can do // about it. We'll simply eat the error. // } } /// **************************************************************** /// public WriteFileLog [static] /// ---------------------------------------------------------------- /// /// Writes a message to the file log. /// /// ---------------------------------------------------------------- /// /// The severity type. /// /// /// /// The category type. /// /// /// /// The message. /// /// **************************************************************** /// public static void WriteFileLog( SeverityType severity, CategoryType category, string message ) { readWriteLock.AcquireReaderLock( Constants.ReadLockTimeout ); try { // // If the log filename has not yet been initialized, there is nothing we // can log yet. // if( null == logFilename ) return; // // Open the logging file, if it is not already. // if( null == textStream ) { // // Get the full path to our log file. // string logFilePath = logFilename.GetString(); // // Pull off the directory of the log file and see if we need to // create it. // string logFileDirectory = Path.GetDirectoryName( logFilePath ); if( false == Directory.Exists( logFileDirectory ) ) { Directory.CreateDirectory( logFileDirectory ); } // // Append to an existing log file, or create a new one. // FileStream file = File.Open( logFilePath, FileMode.Append, FileAccess.Write, FileShare.ReadWrite ); textStream = TextWriter.Synchronized( new StreamWriter( file, System.Text.Encoding.UTF8 ) ); textStream.NewLine = "\r\n"; } // // Build a time string of the format: YYMMDD:hhmmss // DateTime time = DateTime.Now; string timeString = string.Format( "{0,-4:D4}/{1:D2}/{2:D2} {3:D2}:{4:D2}:{5:D2}", time.Year, time.Month, time.Day, time.Hour, time.Minute, time.Second ); // // Write out the file log entry. // textStream.WriteLine( severity.ToString().Substring( 0, 4 ).ToUpper() + " " + category.ToString().Substring( 0, 4 ).ToUpper() + " " + timeString + " " + message.Replace( "\r\n", "\r\n\t\t" ) ); textStream.Flush(); } catch( Exception e ) { WriteDebugger( SeverityType.Error, CategoryType.None, "Could not write to log file " + logFilename + ".\r\n\r\nDetails:\r\n" + e.ToString() ); // // If for whatever reason we could not write output to the log file, we dump the logfile name, // and the reason to the event log. // WriteEventLog( SeverityType.Error, CategoryType.None, OperatorMessageType.None, "Could not write to log file " + logFilename + ".\r\n\r\nDetails:\r\n" + e.ToString() ); } finally { readWriteLock.ReleaseReaderLock(); } } /// **************************************************************** /// public WriteEventLog [static] /// ---------------------------------------------------------------- /// /// Writes a message to the event log. /// /// ---------------------------------------------------------------- /// /// The severity type. /// /// /// /// The category type. /// /// /// /// The message type. /// /// /// /// The message. /// /// **************************************************************** /// public static void WriteEventLog( SeverityType severity, CategoryType category, OperatorMessageType messageId, string message ) { readWriteLock.AcquireReaderLock( Constants.ReadLockTimeout ); try { // // Map the severity type to an event log entry type. // System.Diagnostics.EventLogEntryType entryType; switch( severity ) { case SeverityType.Error: entryType = System.Diagnostics.EventLogEntryType.Error; break; case SeverityType.Warning: entryType = System.Diagnostics.EventLogEntryType.Warning; break; case SeverityType.PassAudit: entryType = System.Diagnostics.EventLogEntryType.SuccessAudit; break; case SeverityType.FailAudit: entryType = System.Diagnostics.EventLogEntryType.FailureAudit; break; default: // // SeverityType.Info and Verbose are mapped to info // entryType = System.Diagnostics.EventLogEntryType.Information; break; } System.Diagnostics.EventLog.WriteEntry( "UDDIRuntime", message, entryType, (int)messageId, (short)category ); } catch( Exception e ) { WriteDebugger( SeverityType.Error, CategoryType.None, "Could not write to event log.\r\n\r\nDetails:\r\n" + e.ToString() ); } finally { readWriteLock.ReleaseReaderLock(); } } // // Note: All calls to this function should occur in administrator initiated // processes. No calls to this function should as a result of an HTTP request from // a SOAP API. // This is verified to be true on 3/1/2002 by creeves // public static void OperatorMail( SeverityType severity, CategoryType category, OperatorMessageType messageId, string message ) { string mailTo = Config.GetString( "Debug.MailTo", null ); if( null == mailTo ) { Debug.Write( SeverityType.Info, CategoryType.Config, "Skipping send of operator mail. Configuration setting 'Debug.MailTo' not set." ); return; } Debug.VerifySetting( "Debug.MailFrom" ); try { string mailCc = Config.GetString( "Debug.MailCc", null ); string mailSubject = Config.GetString( "Debug.MailSubject", "Operator message from {0}. Severity: {1}, Category: {2}" ); MailMessage mail = new MailMessage(); mail.To = mailTo; mail.From = Config.GetString( "Debug.MailFrom" ); mail.Subject = String.Format( mailSubject, System.Environment.MachineName, severity.ToString(), category.ToString(), (int)messageId ); if( null != mailCc ) mail.Cc = mailCc; mail.BodyFormat = MailFormat.Text; mail.Body = "SEVERITY: " + severity.ToString() + "\r\n" + "CATEGORY: " + category.ToString() + "\r\n" + "EVENT ID: " + (int)messageId + "\r\n\r\n" + message; SmtpMail.Send( mail ); } catch( Exception e ) { Debug.OperatorMessage( SeverityType.Error, CategoryType.None, OperatorMessageType.CouldNotSendMail, "Could not send operator mail.\r\n\r\nDetails:\r\n\r\n" + e.ToString() ); } } } }