public static void UseHealthEndpoint(this IApplicationBuilder app) { app.Use(async(context, next) => { if (context.Request.Path.Value.Equals("/health")) { // Perform IP access check if (MicroserviceConfiguration.AllowedIpAddresses != null && context.Request.HttpContext.Connection.RemoteIpAddress != null && !MicroserviceConfiguration.AllowedIpAddresses.Contains( context.Request.HttpContext.Connection.RemoteIpAddress)) { context.Response.StatusCode = 403; await next(); } var status = HealthCheckRegistry.GetStatus(); if (!status.IsHealthy) { // Return a service unavailable status code if any of the checks fail context.Response.StatusCode = 503; } context.Response.Headers["Content-Type"] = "application/json"; await context.Response.WriteAsync(JsonConvert.SerializeObject(status)); } else { await next(); } }); }
public void Correctly_Report_When_There_Are_HealthChecks() { HealthCheckRegistry registry = new HealthCheckRegistry(); registry.Register("test-health-check", new HealthCheck(() => HealthCheck.Result.Healthy())); Assert.That(registry.HasHealthChecks, Is.True); }
public HealthFixture() { HealthCheckRegistry = new HealthCheckRegistry(); var healthStatusProvider = new DefaultHealthProvider( _loggerFactory.CreateLogger <DefaultHealthProvider>(), HealthCheckRegistry); }
public void HealthCheck_RegistryDoesNotThrowOnDuplicateRegistration() { HealthCheckRegistry registry = new HealthCheckRegistry(); registry.RegisterHealthCheck(new HealthCheck("test", () => { })); Action action = () => registry.RegisterHealthCheck(new HealthCheck("test", () => { })); action.ShouldNotThrow<InvalidOperationException>(); }
public void HealthCheck_RegistryStatusIsHealthyIfAllChecksAreHealthy() { HealthCheckRegistry registry = new HealthCheckRegistry(); registry.RegisterHealthCheck(new HealthCheck("ok", () => { })); registry.RegisterHealthCheck(new HealthCheck("another", () => HealthCheckResult.Healthy())); var status = registry.GetStatus(); status.IsHealthy.Should().BeTrue(); status.Results.Length.Should().Be(2); }
public void HealthCheck_RegistryStatusIsFailedIfOneCheckFails() { HealthCheckRegistry registry = new HealthCheckRegistry(); registry.RegisterHealthCheck(new HealthCheck("ok", () => { })); registry.RegisterHealthCheck(new HealthCheck("bad", () => HealthCheckResult.Unhealthy())); var status = registry.GetStatus(); status.IsHealthy.Should().BeFalse(); status.Results.Length.Should().Be(2); }
public void Can_register_process_virtual_memory_check() { var healthChecks = Enumerable.Empty <HealthCheck>(); var name = "virtual memory"; var registry = new HealthCheckRegistry(healthChecks); registry.AddProcessVirtualMemorySizeCheck(name, 100); registry.Checks.Should().NotBeEmpty(); registry.Checks.Single().Value.Name.Should().Be(name); }
public void Configuration(IAppBuilder app) { // Setup logging ApplicationLog.AddConsole(); Logger logger = ApplicationLog.CreateLogger <Startup>(); logger.Info("Initializing service"); // Build an IConfiguration instance using the ConfigurationBuilder as normal Dictionary <string, string> collection = new Dictionary <string, string>() { { "key1", "value1" }, { "key2", "value2" } }; var config1 = new ConfigurationBuilder().AddInMemoryCollection(collection).Build(); var config3 = new ConfigurationBuilder().AddJsonFile("Config.json").Build(); // AppConfig is a static class that groups together instances of IConfiguration and makes them available statically anywhere in the application AppConfig.AddConfigurationObject(config1, "memorySource"); AppConfig.AddConfigurationObject(config3, "jsonSource"); // The above configuration sources can now be referenced easily with a static helper function Console.WriteLine("key1 key in memorySource: " + AppConfig.Get("memorySource", "key1")); Console.WriteLine("config:setting key in jsonSource: " + AppConfig.Get("jsonSource", "config:setting")); // Runtime configuration can be updated easily as well AppConfig.Set("jsonSource", "config:setting", "http://localhost:5001"); Console.WriteLine("Modified config:setting key in jsonSource: " + AppConfig.Get("jsonSource", "config:setting")); // Redis health check (Requires StackExchange.Redis) //HealthCheckRegistry.RegisterHealthCheck("Redis", () => RedisHealthCheck.CheckHealth("localhost")); // PostgreSQL health check (Requires Npgsql) //HealthCheckRegistry.RegisterHealthCheck("Postgresql", () => PostgresqlHealthCheck.CheckHealth("Host=localhost;Username=postgres;Password=postgres;Database=postgres")); // SQL Server health check (Requires System.Data.SqlClient) //HealthCheckRegistry.RegisterHealthCheck("SqlServer", () => SqlServerCheck.CheckHealth("Server=localhost;Database=master;User Id=sa;Password=password; ")); // HealthCheckRegistry.RegisterHealthCheck("mongodb", () => MongoHealthCheck.CheckHealth("mongodb://localhost:27017")); /* * Health checks are simply functions that return either healthy or unhealthy with an optional message string */ HealthCheckRegistry.RegisterHealthCheck("MyCustomMonitor", () => HealthResponse.Healthy("Test Message")); HealthCheckRegistry.RegisterHealthCheck("MyCustomMonitor2", () => HealthResponse.Healthy("Test Message2")); HealthCheckRegistry.RegisterHealthCheck("SampleOperation", () => SampleHealthCheckOperation()); // Activate the info endpoint app.UseInfoEndpoint(); // Activate the environment endpoint app.UseEnvironmentEndpoint(); // Activate the health endpoint app.UseHealthEndpoint(); }
public async Task Can_execute_process_virtual_memory_check() { var healthChecks = Enumerable.Empty <HealthCheck>(); var name = "virtual memory"; var registry = new HealthCheckRegistry(healthChecks); registry.AddProcessVirtualMemorySizeCheck(name, long.MaxValue); var check = registry.Checks.FirstOrDefault(); var result = await check.Value.ExecuteAsync().ConfigureAwait(false); result.Check.Status.Should().Be(HealthCheckStatus.Healthy); }
public async Task Can_execute_ping_check() { var healthChecks = Enumerable.Empty <HealthCheck>(); var name = "github ping"; var registry = new HealthCheckRegistry(healthChecks); registry.AddPingCheck(name, "github.com", TimeSpan.FromSeconds(10)); var check = registry.Checks.FirstOrDefault(); var result = await check.Value.ExecuteAsync().ConfigureAwait(false); result.Check.Status.Should().Be(HealthCheckStatus.Healthy); }
public void HealthCheck_RegistryExecutesCheckOnEachGetStatus() { HealthCheckRegistry registry = new HealthCheckRegistry(); int count = 0; registry.RegisterHealthCheck(new HealthCheck("test", () => { count++; })); count.Should().Be(0); registry.GetStatus(); count.Should().Be(1); registry.GetStatus(); count.Should().Be(2); }
public static void UseHealthEndpoint(this IAppBuilder app) { app.Use(async(context, next) => { if (context.Request.Path.Value.StartsWith("/health")) { HealthCheckRegistry.HealthStatus status = HealthCheckRegistry.GetStatus(); if (!status.IsHealthy) { // Return a service unavailable status code if any of the checks fail context.Response.StatusCode = 503; } context.Response.Headers.Set("Content-Type", "application/json"); await context.Response.WriteAsync(JsonConvert.SerializeObject(status)); } else { await next(); } }); }
private static IHealthCheckRegistry RegisterHealthCheckRegistry(IServiceProvider provider, Action <IHealthCheckRegistry> setupAction = null) { var logFactory = provider.GetRequiredService <ILoggerFactory>(); var logger = logFactory.CreateLogger <HealthCheckRegistry>(); var autoScannedHealthChecks = Enumerable.Empty <HealthCheck>(); try { autoScannedHealthChecks = provider.GetRequiredService <IEnumerable <HealthCheck> >(); } catch (InvalidOperationException ex) { logger.LogError( new EventId(5000), ex, "Failed to load auto scanned health checks, health checks won't be registered"); } var factory = new HealthCheckRegistry(autoScannedHealthChecks); setupAction?.Invoke(factory); return(factory); }
public async Task Should_be_unhealthy_when_task_is_cancelled() { var healthChecks = Enumerable.Empty <HealthCheck>(); var name = "custom with cancellation token"; var registry = new HealthCheckRegistry(healthChecks); registry.Register( name, async cancellationToken => { await Task.Delay(2000, cancellationToken); return(HealthCheckResult.Healthy()); }); var token = new CancellationTokenSource(); token.CancelAfter(200); var check = registry.Checks.FirstOrDefault(); var result = await check.Value.ExecuteAsync(token.Token).ConfigureAwait(false); result.Check.Status.Should().Be(HealthCheckStatus.Unhealthy); }
/// <summary> /// Add services to the application and configure service provider /// </summary> /// <param name="services">Collection of service descriptors</param> /// <param name="configuration">Configuration root of the application</param> /// <returns>Configured service provider</returns> public static (IEngine, NetProOption) ConfigureApplicationServices(this IServiceCollection services, IConfiguration configuration, IHostEnvironment hostEnvironment) { //文件查找组件注入 services.AddFileProcessService(); var netProOption = services.ConfigureStartupConfig <NetProOption>(configuration.GetSection(nameof(NetProOption))); services.ConfigureStartupConfig <HostingConfig>(configuration.GetSection("Hosting")); if (string.IsNullOrWhiteSpace(netProOption.ApplicationName)) { netProOption.ApplicationName = hostEnvironment.ApplicationName; } if (hostEnvironment.EnvironmentName == Environments.Development) { Console.Title = hostEnvironment.ApplicationName; Console.ForegroundColor = ConsoleColor.Cyan; Console.WriteLine(Figgle.FiggleFonts.Varsity.Render(new string(new char[] { 'N', 'e', 't', 'P', 'r', 'o' }))); Console.ResetColor(); //使用dotnet watch run 启动后可以调试此进程id Console.WriteLine($"[{DateTime.Now:HH:mm:ss} {hostEnvironment.EnvironmentName}] dotnet process id:{Process.GetCurrentProcess().Id}"); } //most of API providers require TLS 1.2 nowadays ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; services.AddHttpContextAccessor(); services.AddHttpClient(); ////create default file provider //CoreHelper.DefaultFileProvider = new NetProFileProvider(hostEnvironment); //日志初始化配置 services.ConfigureSerilogConfig(configuration); //create, initialize and configure the engine var engine = EngineContext.Create(); engine.ConfigureServices(services, configuration, netProOption); if (netProOption.ThreadMinCount > 2) { if (ThreadPool.SetMinThreads(Environment.ProcessorCount * netProOption.ThreadMinCount, Environment.ProcessorCount * netProOption.ThreadMinCount)) { ThreadPool.GetMinThreads(out int work, out int comple); ThreadPool.GetAvailableThreads(out int worktemp, out int completemp); Console.WriteLine($"[{DateTime.Now:HH:mm:ss} 核心数为:{Environment.ProcessorCount}--默认线程最小为:{work}--Available:{worktemp}"); } else { Console.WriteLine($"[{DateTime.Now:HH:mm:ss} 最小线程数设置大于系统提供,设置失效!!"); } } if (configuration.GetValue <bool>("Apollo:Enabled", false)) { Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] apollo已开启"); Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] Apollo MetaServer={configuration.GetValue<string>("Apollo:MetaServer")}"); HealthCheckRegistry.RegisterHealthCheck("apollo", () => { var uri = new Uri(configuration.GetValue <string>("Apollo:MetaServer")); using (var tcpClient = new System.Net.Sockets.TcpClient(uri.Host, uri.Port)) { if (tcpClient.Connected) { Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] pollo:Env={configuration.GetValue<string>("Apollo:Env")}"); Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] Apollo:Cluster={configuration.GetValue<string>("Apollo:Cluster")}"); return(HealthResponse.Healthy($"{uri.Host}:{uri.Port}connection successful; pollo:Env={configuration.GetValue<string>("Apollo:Env")}--Apollo:Cluster={configuration.GetValue<string>("Apollo:Cluster")}")); } return(HealthResponse.Unhealthy($"Apollo{uri.Host}:{uri.Port} connection failed")); }
/// <summary> /// Add services to the application and configure service provider /// </summary> /// <param name="services">Collection of service descriptors</param> /// <param name="configuration">Configuration root of the application</param> /// <returns>Configured service provider</returns> public static (IEngine, NetProOption) ConfigureApplicationServices(this IServiceCollection services, IConfiguration configuration, IHostEnvironment hostEnvironment) { var netProOption = services.ConfigureStartupConfig <NetProOption>(configuration.GetSection(nameof(NetProOption))); services.ConfigureStartupConfig <HostingConfig>(configuration.GetSection("Hosting")); if (string.IsNullOrWhiteSpace(netProOption.ApplicationName)) { netProOption.ApplicationName = hostEnvironment.ApplicationName; } if (hostEnvironment.EnvironmentName == Environments.Development) { Console.WriteAscii("Hello NetPro", Color.FromArgb(244, 212, 255)); //使用dotnet watch run 启动后可以调试此进程id Console.WriteLine($"[{DateTime.Now:HH:mm:ss} {hostEnvironment.EnvironmentName}] dotnet process id:{Process.GetCurrentProcess().Id}"); } //most of API providers require TLS 1.2 nowadays ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; services.AddHttpContextAccessor(); //create default file provider CoreHelper.DefaultFileProvider = new NetProFileProvider(hostEnvironment); //日志初始化配置 services.ConfigureSerilogConfig(configuration); //create, initialize and configure the engine var engine = EngineContext.Create(); engine.ConfigureServices(services, configuration, netProOption); if (int.TryParse(configuration.GetValue <string>("NetPro:ThreadMinCount"), out int threadMinCount) && threadMinCount > 2) { if (ThreadPool.SetMinThreads(Environment.ProcessorCount * threadMinCount, Environment.ProcessorCount * threadMinCount)) { ThreadPool.GetMinThreads(out int work, out int comple); ThreadPool.GetAvailableThreads(out int worktemp, out int completemp); Console.WriteLine($"核心数为:{Environment.ProcessorCount}--默认线程最小为:{work}--Available:{worktemp}"); } else { Console.WriteLine("最小线程数设置大于系统提供,设置失效!!"); } } if (!configuration.GetValue <bool>("Apollo:Enabled", false)) { HealthCheckRegistry.RegisterHealthCheck("apollo", () => { var uri = new Uri(configuration.GetValue <string>("Apollo:MetaServer")); using (var tcpClient = new System.Net.Sockets.TcpClient(uri.Host, uri.Port)) { if (tcpClient.Connected) { Console.WriteLine($"pollo:Env:{configuration.GetValue<string>("Apollo:Env")}"); Console.WriteLine($"Apollo:Cluster:{configuration.GetValue<string>("Apollo:Cluster")}"); return(HealthResponse.Healthy($"{uri.Host}:{uri.Port}连接正常--pollo:Env:{configuration.GetValue<string>("Apollo:Env")}--Apollo:Cluster:{configuration.GetValue<string>("Apollo:Cluster")}")); } return(HealthResponse.Unhealthy($"Apollo{uri.Host}:{uri.Port}连接不正常")); } }); } return(engine, netProOption); }
public void Configure(IApplicationBuilder app) { // Add logging ApplicationLog.AddConsole(); ApplicationLog.AddFile("Test.log"); Logger logger = ApplicationLog.CreateLogger <Startup>(); logger.Info("Initializing service"); // Build an IConfiguration instance using the ConfigurationBuilder as normal Dictionary <string, string> collection = new Dictionary <string, string>() { { "key1", "value1" }, { "key2", "value2" } }; var config1 = new ConfigurationBuilder().AddInMemoryCollection(collection).Build(); var config2 = new ConfigurationBuilder().SetBasePath(_env.ContentRootPath).AddIniFile("hosting.ini").Build(); var config3 = new ConfigurationBuilder().SetBasePath(_env.ContentRootPath).AddJsonFile("config.json").Build(); // AppConfig is a static class that groups together instances of IConfiguration and makes them available statically anywhere in the application AppConfig.AddConfigurationObject(config1, "memorySource"); AppConfig.AddConfigurationObject(config2, "iniSource"); AppConfig.AddConfigurationObject(config3, "jsonSource"); // The above configuration sources can now be referenced easily with a static helper function Console.WriteLine("key1 key in memorySource: " + AppConfig.Get("memorySource", "key1")); Console.WriteLine("server.urls key in iniSource: " + AppConfig.Get("iniSource", "server.urls")); // Runtime configuration can be updated easily as well AppConfig.Set("iniSource", "server.urls", "http://localhost:5001"); Console.WriteLine("Modified server.urls key in iniSource: " + AppConfig.Get("iniSource", "server.urls")); /* * Health checks are simply functions that return either healthy or unhealthy with an optional message string */ HealthCheckRegistry.RegisterHealthCheck("MyCustomMonitor", () => HealthResponse.Healthy("Test Message")); HealthCheckRegistry.RegisterHealthCheck("MyCustomMonitor2", () => HealthResponse.Healthy("Test Message2")); HealthCheckRegistry.RegisterHealthCheck("SampleOperation", () => SampleHealthCheckOperation()); /* * Some bundled health checks that can be used */ // Redis health check (Requires StackExchange.Redis) //HealthCheckRegistry.RegisterHealthCheck("Redis", () => RedisHealthCheck.CheckHealth("localhost")); // PostgreSQL health check (Requires Npgsql) //HealthCheckRegistry.RegisterHealthCheck("Postgresql", () => PostgresqlHealthCheck.CheckHealth("Host=localhost;Username=postgres;Password=postgres;Database=postgres")); // SQL Server health check (Requires System.Data.SqlClient) //HealthCheckRegistry.RegisterHealthCheck("SqlServer", () => SqlServerCheck.CheckHealth("Server=localhost;Database=master;User Id=sa;Password=password; ")); // RavenDB health check //HealthCheckRegistry.RegisterHealthCheck("ravendb", () => RavenDbHealthCheck.CheckHealth("Url=http://192.168.153.55:8080;DefaultDatabase=<system>")); // MongoDB health check //HealthCheckRegistry.RegisterHealthCheck("mongodb", () => MongoHealthCheck.CheckHealth("mongodb://localhost:27017")); /* * Uncomment the below line to only allow access to the actuator endpoints from localhost * * Allowed patterns are: * * 1. CIDR range: "192.168.0.0/24", "fe80::/10" * 2. Single address: "127.0.0.1", ":;1" * 3. Begin end range: "169.258.0.0-169.258.0.255" * 4. Bit mask range: "192.168.0.0/255.255.255.0" * * NOTE: Currently this feature is not supported under Kestrel self-host as it does not set the client's IP address in HttpContext */ //MicroserviceBootstrap.AllowedIpAddresses = IpAddressRange.Parse("127.0.0.0/8"); // Activate /health endpoint app.UseHealthEndpoint(); /* * Activate /env endpoint * * The ApplicationConfiguration element of the env endpoint will only contain data if the AppConfig helper class is * used to manage application configuration */ app.UseEnvironmentEndpoint(); /* * The compiler directive below is only required if you plan to target .NET core as well as the full CLR * If you don't target dnxcore50 in your project.json you can remove the below #if and just call UseInfoEndpoint() * without any parameters */ // Activate /info endpoint #if NETCOREAPP1_0 // Required for .NET Core until the relevant APIs are added app.UseInfoEndpoint(typeof(Startup).GetTypeInfo().Assembly.GetName()); #else app.UseInfoEndpoint(); #endif }