namespace Zeta.EnterpriseLibrary.Logging
{
#region Using directives.
// ----------------------------------------------------------------------
using System;
using System.Globalization;
using System.Net;
using System.IO;
using System.Net.NetworkInformation;
using System.Reflection;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Diagnostics;
using System.Text;
// ----------------------------------------------------------------------
#endregion
/////////////////////////////////////////////////////////////////////////
/// <summary>
/// Independently usable class that returns various strings with
/// environment-related logging information.
/// </summary>
public class LoggingInformation
{
#region Static member.
// ------------------------------------------------------------------
/// <summary>
/// Return a string with a collection of all the discrete information
/// that this class provides.
/// </summary>
/// <value>All.</value>
public static string All
{
get
{
try
{
var additionalInfo = new StringBuilder();
if ( _needMoreInfoEventHandlers != null )
{
foreach (
EventHandler<LoggingInformationNeedMoreInfoEventArgs> handler
in _needMoreInfoEventHandlers )
{
var args =
new LoggingInformationNeedMoreInfoEventArgs();
// Call handler.
Debug.Assert( handler != null );
handler( null, args );
if ( !string.IsNullOrEmpty( args.MoreInformation ) )
{
// Add information.
additionalInfo.AppendLine( args.MoreInformation );
additionalInfo.AppendLine();
}
}
}
var info = new StringBuilder();
info.Append( additionalInfo.ToString().Trim() );
additionalInfo.AppendLine();
additionalInfo.AppendLine();
additionalInfo.AppendLine( WindowsEnvironment );
additionalInfo.AppendLine();
additionalInfo.AppendLine();
additionalInfo.AppendLine( NetworkEnvironment );
additionalInfo.AppendLine();
additionalInfo.AppendLine();
additionalInfo.AppendLine( Assemblies );
return info.ToString().Trim();
}
catch ( Exception x )
{
// As suggested, do catch it here, since it can be
// security issues that prevent gathering the required
// information.
return string.Format(
@"Exception '{0}' ({1}) inside '{2}'. Stack trace follows: {3}",
x.Message,
x.GetType(),
@"Zeta.EnterpriseLibrary.Logging.LoggingInformation.All",
x.StackTrace );
}
}
}
/// <summary>
/// Occurs when [need more info].
/// </summary>
public static event EventHandler<LoggingInformationNeedMoreInfoEventArgs>
NeedMoreInfo
{
add
{
Debug.Assert( value != null );
lock ( _typeLock )
{
if ( _needMoreInfoEventHandlers == null )
{
_needMoreInfoEventHandlers =
new List<EventHandler<LoggingInformationNeedMoreInfoEventArgs>>();
}
Debug.Assert( !_needMoreInfoEventHandlers.Contains( value ) );
_needMoreInfoEventHandlers.Add( value );
}
}
remove
{
lock ( _typeLock )
{
Debug.Assert( _needMoreInfoEventHandlers != null );
Debug.Assert( _needMoreInfoEventHandlers.Contains( value ) );
_needMoreInfoEventHandlers.Remove( value );
}
}
}
/// <summary>
/// Provide Windows-environment information.
/// </summary>
/// <value>The windows environment.</value>
public static string WindowsEnvironment
{
get
{
try
{
string info =
string.Format(
CultureInfo.CurrentCulture,
@"User domain name: {0}," + Environment.NewLine +
@"User name: {1}," + Environment.NewLine +
@"Machine name: {2}," + Environment.NewLine +
@"OS version: {3}," + Environment.NewLine +
@"CLR version: {4}," + Environment.NewLine +
@"Command line: {5}," + Environment.NewLine +
@"Current directory: {6}.",
Environment.UserDomainName ?? @"(null)",
Environment.UserName,
Environment.MachineName,
Environment.OSVersion == null ? @"(null)" : Environment.OSVersion.ToString(),
Environment.Version,
Environment.CommandLine ?? @"(null)",
Environment.CurrentDirectory );
return info;
}
catch ( Exception x )
{
// As suggested, do catch it here, since it can be
// security issues that prevent gathering the required
// information.
return string.Format(
@"Exception '{0}' ({1}) inside '{2}'.",
x.Message,
x.GetType(),
@"Zeta.EnterpriseLibrary.Logging.LoggingInformation.WindowsEnvironment" );
}
}
}
/// <summary>
/// Get the DNS name of the host.
/// </summary>
/// <param name="hostName">Name of the host.</param>
/// <returns></returns>
public static string DnsGetHostEntryName(
string hostName )
{
if ( string.IsNullOrEmpty( hostName ) )
{
return string.Empty;
}
else
{
try
{
var iphe = Dns.GetHostEntry( hostName );
return iphe.HostName;
}
catch ( SocketException )
{
return string.Empty;
}
}
}
/// <summary>
/// Provide network-environment information.
/// </summary>
/// <value>The network environment.</value>
public static string NetworkEnvironment
{
get
{
try
{
var hostName = Dns.GetHostName();
var infos = new StringBuilder();
infos.AppendFormat(
CultureInfo.CurrentCulture,
@"Host name: {0}",
hostName ?? @"(null)" );
foreach ( var i in NetworkInterface.GetAllNetworkInterfaces() )
{
foreach ( var iAddress in i.GetIPProperties().UnicastAddresses )
{
var ipa = iAddress.Address.ToString();
infos.AppendFormat(
CultureInfo.CurrentCulture,
@",{0}IP address: {1}{0}Lookup address: http://zeta-sw.com/geoip.aspx?ip={1}",
Environment.NewLine,
ipa ?? @"(null)" );
}
}
return infos.ToString().TrimEnd().TrimEnd( ',', '.' ).TrimEnd() + @".";
}
catch ( Exception x )
{
// As suggested, do catch it here, since it can be
// security issues that prevent gathering the required
// information.
return string.Format(
@"Exception '{0}' ({1}) inside '{2}'.",
x.Message,
x.GetType(),
@"Zeta.EnterpriseLibrary.Logging.LoggingInformation.NetworkEnvironment" );
}
}
}
/// <summary>
/// Provide information about the current assemblies.
/// </summary>
/// <value>The assemblies.</value>
public static string Assemblies
{
get
{
var assemblies =
new List<AssemblyInfo>
{
new AssemblyInfo(@"Entry assembly", Assembly.GetEntryAssembly()),
new AssemblyInfo(@"Executing assembly", Assembly.GetExecutingAssembly()),
new AssemblyInfo(@"Calling assembly", Assembly.GetCallingAssembly())
};
// Add all loaded assemblies.
// (This is redundant with the above but still OK).
var asms = AppDomain.CurrentDomain.GetAssemblies();
if ( asms != null )
{
int index = 0;
foreach ( var asm in asms )
{
assemblies.Add(
new AssemblyInfo(
string.Format(
@"Domain assembly {0}/{1}.",
index + 1,
asms.Length ),
asm ) );
index++;
}
}
// --
var infos = string.Empty;
var loopCount = 0;
foreach ( var pair in assemblies )
{
var assemblyType = pair.Name;
var assembly = pair.Assembly;
if ( assembly != null )
{
string info = string.Format(
CultureInfo.CurrentCulture,
@"Assembly type: {0}," + Environment.NewLine +
@"Assembly full name: {1}," + Environment.NewLine +
@"Assembly location: {2}," + Environment.NewLine +
@"Assembly date: {3}," + Environment.NewLine +
@"Assembly version: {4}."
,
assemblyType,
assembly.FullName ?? @"(null)",
getAssemblyLocation( assembly ) ?? @"(null)",
getAssemblyLocation( assembly ) == null
? @"(null)"
: getLastFileWriteTime( getAssemblyLocation( assembly ) ).ToString( CultureInfo.CurrentCulture ),
assembly.GetName().Version == null
? @"(null)"
: assembly.GetName().Version.ToString() );
if ( loopCount > 0 )
{
infos += Environment.NewLine + Environment.NewLine;
}
infos += info;
}
loopCount++;
}
return infos;
}
}
/// <summary>
/// Gets the last file write time.
/// </summary>
/// <param name="filePath">The file path.</param>
/// <returns></returns>
private static DateTime getLastFileWriteTime(
string filePath )
{
if ( string.IsNullOrEmpty( filePath ) )
{
return DateTime.MinValue;
}
else if ( !File.Exists( filePath ) )
{
return DateTime.MinValue;
}
else
{
return File.GetLastWriteTime( filePath );
}
}
/// <summary>
/// Dynamic assemblies don't support the location.
/// </summary>
/// <param name="assembly">The assembly.</param>
/// <returns></returns>
private static string getAssemblyLocation(
Assembly assembly )
{
try
{
return assembly.Location;
}
catch ( NotSupportedException )
{
return null;
}
}
// ------------------------------------------------------------------
#endregion
#region Helper class for iterating.
// ------------------------------------------------------------------
/// <summary>
/// Helper class for iterating.
/// </summary>
private class AssemblyInfo
{
#region Public methods.
/// <summary>
/// Initializes a new instance of the <see cref="AssemblyInfo"/>
/// class.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="assembly">The assembly.</param>
public AssemblyInfo(
string name,
Assembly assembly )
{
_name = name;
_assembly = assembly;
}
#endregion
#region Public properties.
/// <summary>
/// Gets the name.
/// </summary>
/// <value>The name.</value>
public string Name
{
get
{
return _name;
}
}
/// <summary>
/// Gets the assembly.
/// </summary>
/// <value>The assembly.</value>
public Assembly Assembly
{
get
{
return _assembly;
}
}
#endregion
#region Private variables.
private readonly string _name;
private readonly Assembly _assembly;
#endregion
}
// ------------------------------------------------------------------
#endregion
#region Private helper.
// ------------------------------------------------------------------
private static readonly object _typeLock = new object();
private static List<EventHandler<LoggingInformationNeedMoreInfoEventArgs>>
_needMoreInfoEventHandlers;
// ------------------------------------------------------------------
#endregion
}
/////////////////////////////////////////////////////////////////////////
}