Skip to content

Instantly share code, notes, and snippets.

@crmckenzie
Last active June 14, 2016 01:00
Show Gist options
  • Save crmckenzie/9b87e78d4b4ddb550de98a277d2aae2f to your computer and use it in GitHub Desktop.
Save crmckenzie/9b87e78d4b4ddb550de98a277d2aae2f to your computer and use it in GitHub Desktop.
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