Last active
June 14, 2016 01:00
-
-
Save crmckenzie/9b87e78d4b4ddb550de98a277d2aae2f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class ServiceHealthStatus | |
{ | |
public string Type { get; set; } | |
public string Status { get; set; } | |
public string[] Messages { get; set; } | |
public string Name { get; set; } | |
public string Data { get; set; } | |
} | |
public class ServiceHealthController : ApiController | |
{ | |
// GET api/ServiceHealth | |
[Route("api/v1/ServiceHealth")] | |
[ResponseType(typeof(ServiceHealthStatus))] | |
public IHttpActionResult Get() | |
{ | |
var health = GetServiceHealth().ToArray(); | |
if (health.Any(row => row.Status.ToLower() != "up")) | |
{ | |
var response = Request.CreateResponse(HttpStatusCode.ExpectationFailed, health); | |
return ResponseMessage(response); | |
} | |
return Ok(health); | |
} | |
private IEnumerable<ServiceHealthStatus> GetServiceHealth() | |
{ | |
var results = new List<ServiceHealthStatus>(); | |
var viewModel = new AboutViewModel(); | |
try | |
{ | |
// these foreach blocks are deliberately NOT LINQ-ified so that | |
// if an exception occurs we'll still have the first items from each | |
// collection in the results list. | |
// | |
var connectionStringHealth = GetConnectionStringHealth(viewModel); | |
foreach (var result in connectionStringHealth) | |
{ | |
results.Add(result); | |
} | |
var serviceEndpointHealth = GetServiceEndpointHealth(viewModel); | |
foreach (var result in serviceEndpointHealth) | |
{ | |
results.Add(result); | |
} | |
var rabbitHealth = GetRabbitHealth(viewModel); | |
foreach (var result in rabbitHealth) | |
{ | |
results.Add(result); | |
} | |
var couchbaseHealth = GetCouchbaseHealth(viewModel); | |
foreach (var result in couchbaseHealth) | |
{ | |
results.Add(result); | |
} | |
} | |
catch (Exception e) | |
{ | |
results.Add(new ServiceHealthStatus() | |
{ | |
Type = "Unknown", | |
Status = "error", | |
Messages = new[] {e.Message}, | |
Name = "Exception", | |
}); | |
} | |
return results; | |
} | |
private IEnumerable<ServiceHealthStatus> GetCouchbaseHealth(AboutViewModel viewModel) | |
{ | |
foreach (var url in viewModel.Couchbase.Servers.Urls) | |
{ | |
var healthStatusType = "Couchbase"; | |
var name = "Couchbase"; | |
yield return GetServiceHealthStatusForUrl(url, name, healthStatusType); | |
} | |
} | |
private IEnumerable<ServiceHealthStatus> GetRabbitHealth(AboutViewModel viewModel) | |
{ | |
foreach (var url in viewModel.Rabbit.Urls) | |
{ | |
var healthStatusType = "Rabbit"; | |
var name = "Rabbit"; | |
var httpGetUrl = url.Replace("amqp", "http").Replace("5672", "15672"); // use the web client port | |
yield return GetServiceHealthStatusForUrl(httpGetUrl, name, healthStatusType); | |
} | |
} | |
private static IEnumerable<ServiceHealthStatus> GetServiceEndpointHealth(AboutViewModel viewModel) | |
{ | |
foreach (var endpoint in viewModel.ServiceEndpoints) | |
{ | |
var healthStatusType = "Service"; | |
var url = endpoint.Link; | |
var name = endpoint.Name; | |
yield return GetServiceHealthStatusForUrl(url, name, healthStatusType); | |
} | |
} | |
private static ServiceHealthStatus GetServiceHealthStatusForUrl(string url, string name, string healthStatusType) | |
{ | |
Exception exception = null; | |
HttpResponseMessage response = null; | |
try | |
{ | |
var uri = new Uri(url, UriKind.Absolute); | |
var client = new HttpClient(); | |
response = client.GetAsync(uri).Result; | |
} | |
catch (Exception e) | |
{ | |
exception = e; | |
} | |
var status = (exception == null && response != null && response.StatusCode == HttpStatusCode.OK) | |
? "up" | |
: "down"; | |
var messages = new List<string>(); | |
if (response != null && response.StatusCode != HttpStatusCode.Accepted) | |
{ | |
var content = response.Content.ReadAsStringAsync().Result; | |
messages.Add(content); | |
} | |
while (exception != null) | |
{ | |
messages.Add(exception.Message); | |
exception = exception.InnerException; | |
} | |
return new ServiceHealthStatus() | |
{ | |
Name = name, | |
Data = url, | |
Status = status, | |
Type = healthStatusType, | |
Messages = messages.ToArray(), | |
}; | |
} | |
private static IEnumerable<ServiceHealthStatus> GetConnectionStringHealth(AboutViewModel viewModel) | |
{ | |
foreach (var connectionString in viewModel.ConnectionStrings.Cast<ConnectionStringSettings>()) | |
{ | |
Exception exception = null; | |
var str = connectionString.ConnectionString.Replace("Connect Timeout=30", "Connect Timeout=5"); | |
using (var connection = new SqlConnection(str)) | |
{ | |
try | |
{ | |
connection.Open(); | |
} | |
catch (Exception e) | |
{ | |
exception = e; | |
} | |
var status = (exception == null) ? "up" : "down"; | |
var messages = (exception == null) ? new string[] {} : new string[] {exception.Message}; | |
yield return new ServiceHealthStatus() | |
{ | |
Type = "Database", | |
Name = connectionString.Name, | |
Data = connectionString.ConnectionString, | |
Status = status, | |
Messages = messages | |
}; | |
} | |
} | |
} | |
} | |
// Comment out the line below if your project does not use Couchbase. | |
#define Couchbase | |
#if Couchbase | |
// If this line is giving you a compilation error because your app | |
// does not use Couchbase, | |
// Comment out the Couchbase preprocessor directive at the top of this file. | |
using Couchbase.Configuration.Client.Providers; | |
#endif | |
public class AboutViewModel | |
{ | |
private static ICollection<KeyValuePair<string, string>> GetApplicationConfigurationValues(string sectionName) | |
{ | |
var section = (NameValueCollection)ConfigurationManager.GetSection(sectionName); | |
if (section != null) | |
return | |
section.AllKeys.Select(key => new KeyValuePair<string, string>(key, section[key])).ToList(); | |
Debug.WriteLine("Unable to find '{0}' section in configuration file.", sectionName); | |
return new List<KeyValuePair<string, string>>(); | |
} | |
private ServiceEndpointViewModel MapServiceEndpoint(KeyValuePair<string, string> s) | |
{ | |
return new ServiceEndpointViewModel() | |
{ | |
Name = s.Key, | |
Location = s.Value, | |
Link = this.GetDocumentationUrl(s.Value) | |
}; | |
} | |
private string GetDocumentationUrl(string value) | |
{ | |
if (string.IsNullOrWhiteSpace(value) || value.EndsWith(".svc") || | |
!Uri.IsWellFormedUriString(value, UriKind.Absolute)) | |
return value; | |
return string.Format("http://{0}/swagger", new Uri(value).Host); | |
} | |
private static AuthorizationConfigurationViewModel MapAuthorization(KeyValuePair<string, string> s) | |
{ | |
string str = string.IsNullOrWhiteSpace(s.Value) ? "{None Defined}" : s.Value; | |
return new AuthorizationConfigurationViewModel() | |
{ | |
Name = s.Key, | |
Groups = str | |
}; | |
} | |
private List<LoggerConfigurationViewModel> GetLoggingConfiguration() | |
{ | |
var allRepositories = LogManager.GetAllRepositories(); | |
var loggers = allRepositories | |
.SelectMany(r => r.GetCurrentLoggers() | |
.Cast<Logger>()) | |
.Where(l => l.Appenders.Cast<IAppender>().Any()) | |
.Select(l => this.MapLogger(l)) | |
.ToList() | |
; | |
var loggersFromHierarchies = allRepositories | |
.Cast<Hierarchy>() | |
.Where(h => h.Root != null) | |
.Select(h => this.MapLogger(h.Root, "Root")) | |
.ToList() | |
; | |
loggers.AddRange(loggersFromHierarchies); | |
return loggers; | |
} | |
private LoggerConfigurationViewModel MapLogger(Logger l, string name = null) | |
{ | |
return new LoggerConfigurationViewModel() | |
{ | |
Name = name ?? l.Name, | |
LoggingLevel = l.EffectiveLevel.DisplayName, | |
Appenders = this.MapAppenderCollection(l.Appenders) | |
}; | |
} | |
private List<LogAppenderConfigurationViewModel> MapAppenderCollection(AppenderCollection appenderCollection) | |
{ | |
return appenderCollection.Cast<AppenderSkeleton>().Select(this.MapAppender).ToList(); | |
} | |
private LogAppenderConfigurationViewModel MapAppender(AppenderSkeleton a) | |
{ | |
return new LogAppenderConfigurationViewModel() | |
{ | |
Name = a.Name, | |
Type = a.GetType().FullName, | |
LayoutName = a.Layout.GetType().FullName, | |
Properties = GetProperties(a), | |
LayoutProperties = GetProperties(a.Layout) | |
}; | |
} | |
private static Dictionary<string, object> GetProperties<T>(T item) | |
{ | |
Type type = item.GetType(); | |
PropertyInfo[] properties = type.GetProperties(); | |
Func<PropertyInfo, object> elementSelector = | |
p => type.GetProperty(p.Name).GetValue((object)(T)item); | |
return properties.ToDictionary(p => p.Name, elementSelector); | |
} | |
public AboutViewModel() | |
{ | |
ApplicationName = this.GetType().Assembly.GetName().Name; | |
AppSettings = ConfigurationManager.AppSettings; | |
ConnectionStrings = ConfigurationManager.ConnectionStrings; | |
Assemblies = AssemblyViewModel.FromAssembly(Assembly.GetExecutingAssembly()); | |
ServiceEndpoints = GetApplicationConfigurationValues("service.addresses").Select(this.MapServiceEndpoint).ToList(); | |
Authorizations = GetApplicationConfigurationValues("authorization").Select(MapAuthorization).ToList(); | |
LoggingConfiguration = GetLoggingConfiguration(); | |
Rabbit = RabbitViewModel.From(GetApplicationConfigurationValues("rabbit")); | |
Couchbase = CouchbaseConfigurationViewModel.FromAssembly(Assembly.GetExecutingAssembly()); | |
} | |
public string ApplicationName { get; set; } | |
public string ApplicationSetName { get; set; } | |
public IEnumerable<AssemblyViewModel> Assemblies { get; set; } | |
public IEnumerable<ServiceEndpointViewModel> ServiceEndpoints { get; set; } | |
public IEnumerable<AuthorizationConfigurationViewModel> Authorizations { get; set; } | |
public IEnumerable<LoggerConfigurationViewModel> LoggingConfiguration { get; set; } | |
public NameValueCollection AppSettings { get; set; } | |
public ConnectionStringSettingsCollection ConnectionStrings { get; set; } | |
public RabbitViewModel Rabbit { get; set; } | |
public CouchbaseConfigurationViewModel Couchbase { get; set; } | |
} | |
public class AssemblyViewModel | |
{ | |
public static IEnumerable<AssemblyViewModel> FromAssembly(Assembly assembly) | |
{ | |
var dependentAssemblies = GetDependentAssemblies(assembly); | |
var viewModels = dependentAssemblies | |
.Select(a => new AssemblyViewModel | |
{ | |
AssemblyName = a.Name, | |
AssemblyVersion = a.Version.ToString() | |
}) | |
.OrderBy(a => a.AssemblyName); | |
return viewModels; | |
} | |
private static IEnumerable<AssemblyName> GetDependentAssemblies(Assembly assembly) | |
{ | |
var list = new List<AssemblyName>(); | |
var codebase = new Uri(assembly.CodeBase); | |
var binPath = codebase.LocalPath; | |
var binFolder = Path.GetDirectoryName(binPath); | |
if (binFolder != null) | |
{ | |
var files = Directory.GetFiles(binFolder, "*.dll"); | |
list.AddRange(files.Select(Assembly.LoadFrom).Select(a => a.GetName())); | |
} | |
return list; | |
} | |
public string AssemblyName { get; set; } | |
public string AssemblyVersion { get; set; } | |
} | |
public class AuthorizationConfigurationViewModel | |
{ | |
public string Name { get; set; } | |
public string Groups { get; set; } | |
} | |
public class LoggerConfigurationViewModel | |
{ | |
public string Name { get; set; } | |
public string LoggingLevel { get; set; } | |
public List<LogAppenderConfigurationViewModel> Appenders { get; set; } | |
} | |
public class LogAppenderConfigurationViewModel | |
{ | |
public string Name { get; set; } | |
public string Type { get; set; } | |
public string LayoutName { get; set; } | |
public Dictionary<string, object> Properties { get; set; } | |
public Dictionary<string, object> LayoutProperties { get; set; } | |
} | |
public class RabbitViewModel | |
{ | |
public RabbitViewModel() | |
{ | |
Urls = new string[] { }; | |
} | |
public string[] Urls { get; set; } | |
public static RabbitViewModel From(ICollection<KeyValuePair<string, string>> rabbitSection) | |
{ | |
const string key = "RabbitMqServerUrl"; | |
if (!rabbitSection.Any(row => row.Key == key)) | |
{ | |
return new RabbitViewModel(); | |
} | |
var rawValue = rabbitSection.First(row => row.Key == key).Value; | |
if (string.IsNullOrWhiteSpace(rawValue)) | |
{ | |
return new RabbitViewModel(); | |
} | |
var urls = rawValue | |
.Split('|') | |
.Select(row => row.Trim()) | |
.Select(row => | |
{ | |
var expression = @"amqp:\/\/\w+:"; | |
if (Regex.IsMatch(row, expression)) | |
{ | |
var match = Regex.Match(row, expression); | |
var startOfPassword = match.Length; | |
var endOfPassword = row.IndexOf("@"); | |
var charArray = row.ToCharArray(); | |
for (var i = startOfPassword; i < endOfPassword; i++) | |
{ | |
charArray[i] = '*'; | |
} | |
var result = new string(charArray); | |
return result; | |
} | |
return row; | |
}) | |
.ToArray() | |
; | |
return new RabbitViewModel() | |
{ | |
Urls = urls, | |
}; | |
} | |
} | |
public class ServiceEndpointViewModel | |
{ | |
public string Name { get; set; } | |
public string Location { get; set; } | |
public string Link { get; set; } | |
} | |
[XmlRoot(ElementName = "couchbase")] | |
public class CouchbaseConfigurationViewModel | |
{ | |
public CouchbaseConfigurationViewModel() | |
{ | |
Buckets = new List<CouchbaseBucketViewModel>(); | |
ConnectionPool = new CouchbaseConnectionPoolViewModel(); | |
Servers = new CouchbaseServersViewModel() | |
{ | |
Urls = new List<string>(), | |
}; | |
} | |
[XmlAttribute("useSsl")] | |
public bool UseSsl { get; set; } | |
[XmlElement("servers")] | |
public CouchbaseServersViewModel Servers { get; set; } | |
public List<CouchbaseBucketViewModel> Buckets { get; set; } | |
#if Couchbase | |
public static CouchbaseConnectionPoolViewModel Create(ConnectionPoolElement element) | |
{ | |
return new CouchbaseConnectionPoolViewModel() | |
{ | |
Name = element.Name, | |
MaxSize = element.MaxSize, | |
MinSize = element.MinSize, | |
SendTimeout = element.SendTimeout, | |
}; | |
} | |
public static CouchbaseBucketViewModel Create(BucketElement row) | |
{ | |
return new CouchbaseBucketViewModel() | |
{ | |
UseSsl = row.UseSsl, | |
OperationLifespan = row.OperationLifespan, | |
Name = row.Name, | |
ConnectionPool = Create(row.ConnectionPool), | |
ObserveTimeout = row.ObserveTimeout, | |
ObserveInterval = row.ObserveInterval, | |
UseEnhancedDurability = row.UseEnhancedDurability, | |
}; | |
} | |
#endif | |
public static CouchbaseConfigurationViewModel FromAssembly(Assembly assembly) | |
{ | |
#if Couchbase | |
var section = ConfigurationManager.GetSection("couchbase") as CouchbaseClientSection; | |
var servers = section.Servers | |
.Cast<UriElement>() | |
.Select(row => | |
{ | |
return row.Uri.OriginalString; | |
}) | |
.ToList() | |
; | |
var buckets = section.Buckets | |
.Cast<BucketElement>() | |
.Select(Create) | |
.ToList(); | |
var result = new CouchbaseConfigurationViewModel() | |
{ | |
ConnectionPool = Create(section.ConnectionPool), | |
Servers = new CouchbaseServersViewModel() | |
{ | |
Urls = servers, | |
}, | |
Buckets = buckets, | |
UseSsl = section.UseSsl, | |
ApiPort = section.ApiPort, | |
DirectPort = section.DirectPort, | |
EnableConfigHeartBeat = section.EnableConfigHeartBeat, | |
EnableOperationTiming = section.EnableOperationTiming, | |
EnableTcpKeepAlives = section.EnableTcpKeepAlives, | |
Expect100Continue = section.Expect100Continue | |
}; | |
return result; | |
#else | |
return new CouchbaseConfigurationViewModel(); | |
#endif | |
} | |
public bool Expect100Continue { get; set; } | |
public bool EnableTcpKeepAlives { get; set; } | |
public bool EnableOperationTiming { get; set; } | |
public bool EnableConfigHeartBeat { get; set; } | |
public int DirectPort { get; set; } | |
public int ApiPort { get; set; } | |
public CouchbaseConnectionPoolViewModel ConnectionPool { get; set; } | |
} | |
public class CouchbaseBucketViewModel | |
{ | |
[XmlAttribute("name")] | |
public string Name { get; set; } | |
[XmlAttribute("useSsl")] | |
public bool UseSsl { get; set; } | |
[XmlAttribute("password")] | |
public string Password { get; set; } | |
[XmlAttribute("openLifespan")] | |
public uint? OperationLifespan { get; set; } | |
[XmlElement("connectionPool")] | |
public CouchbaseConnectionPoolViewModel ConnectionPool { get; set; } | |
public int ObserveTimeout { get; set; } | |
public int ObserveInterval { get; set; } | |
public bool UseEnhancedDurability { get; set; } | |
} | |
public class CouchbaseConnectionPoolViewModel | |
{ | |
public string Name { get; set; } | |
public long MaxSize { get; set; } | |
public long MinSize { get; set; } | |
public long SendTimeout { get; set; } | |
} | |
public class CouchbaseServersViewModel | |
{ | |
public string Bucket { get; set; } | |
public string BucketPassword { get; set; } | |
public List<string> Urls { get; set; } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment