コード例 #1
0
        public void set_default_parameters_when_is_created()
        {
            var watch = new Stopwatch();

            watch.Start();
            var highResolution = Stopwatch.IsHighResolution;

            watch.Stop();

            var i = watch.ElapsedMilliseconds;

            var options = new BeatPulseOptions();

            options.Path
            .Should().Be(BeatPulseKeys.BEATPULSE_DEFAULT_PATH);

            options.Timeout
            .Should().Be(-1);

            options.DetailedOutput
            .Should().BeFalse();

            options.CacheOutput
            .Should().BeFalse();

            options.CacheDuration
            .Should().Be(0);

            options.DetailedErrors
            .Should().BeFalse();

            options.CacheMode
            .Should().Be(CacheMode.Header);
        }
コード例 #2
0
        public async Task <IEnumerable <LivenessResult> > IsHealthy(string path, BeatPulseOptions options, HttpContext httpContext)
        {
            _logger.LogInformation($"BeatPulse is checking health on all registered liveness on [BeatPulsePath]/{path}.");

            using (_logger.BeginScope($"BeatPulse is checking health status on all registered liveness from [BeatPulsePath]/{path} path."))
            {
                var livenessResults = new List <LivenessResult>();

                foreach (var registration in _beatPulseContext.GetAllLiveness(path))
                {
                    var liveness = _beatPulseContext
                                   .CreateLivenessFromRegistration(registration);

                    var executionContext = LivenessExecutionContext
                                           .FromRegistration(registration, showDetailedErrors: options.DetailedErrors);

                    var livenessResult = await RunLiveness(liveness, executionContext, options);

                    await RunTrackers(livenessResult);

                    livenessResults.Add(livenessResult);

                    if (!livenessResult.IsHealthy && !options.DetailedOutput)
                    {
                        //if is unhealthy and not detailed options is true return inmediatly

                        _logger.LogWarning($"Liveness {executionContext.Name} is not healthy. Breaking liveness execution because detailed output is false.");

                        return(livenessResults);
                    }
                }

                return(livenessResults);
            }
        }
コード例 #3
0
        private static bool MatchesBeatPulseRequestPath(this HttpContext context, BeatPulseOptions options)
        {
            var routeValues     = new RouteValueDictionary();
            var templateMatcher = GetTemplateMatcher(options);

            return(templateMatcher.TryMatch(context.Request.Path, routeValues));
        }
コード例 #4
0
        public void evaluate_the_beatpulse_path()
        {
            var options = new BeatPulseOptions();

            var httpContext = new DefaultHttpContext();

            httpContext.Request.Method = "GET";
            httpContext.Request.Path   = "/hc";

            httpContext.GetBeatPulseRequestPath(options)
            .Should().BeEquivalentTo(string.Empty);

            httpContext.Request.Path = "/hc/sqlserver";

            httpContext.GetBeatPulseRequestPath(options)
            .Should().BeEquivalentTo("sqlserver");

            options.ConfigurePath("health");

            httpContext.Request.Path = "/health";

            httpContext.GetBeatPulseRequestPath(options)
            .Should().BeEquivalentTo(string.Empty);

            httpContext.Request.Path = "/health/sqlserver";

            httpContext.GetBeatPulseRequestPath(options)
            .Should().BeEquivalentTo("sqlserver");
        }
コード例 #5
0
        public static string GetBeatPulseRequestPath(this HttpContext context, BeatPulseOptions options)
        {
            var routeValues     = new RouteValueDictionary();
            var templateMatcher = GetTemplateMatcher(options);

            templateMatcher.TryMatch(context.Request.Path, routeValues);
            return(routeValues[BeatPulseKeys.BEATPULSE_PATH_SEGMENT_NAME].ToString());
        }
コード例 #6
0
        private static TemplateMatcher GetTemplateMatcher(BeatPulseOptions options)
        {
            var templateMatcher = new TemplateMatcher(TemplateParser.Parse($"{options.Path}/{{{BeatPulseKeys.BEATPULSE_PATH_SEGMENT_NAME}}}"),
                                                      new RouteValueDictionary()
            {
                { BeatPulseKeys.BEATPULSE_PATH_SEGMENT_NAME, string.Empty }
            });

            return(templateMatcher);
        }
コード例 #7
0
        public void change_default_detailed_errors()
        {
            var options = new BeatPulseOptions();

            options.DetailedErrors
            .Should().BeFalse();

            options.ConfigureDetailedOutput(detailedOutput: true, includeExceptionMessages: true);

            options.DetailedErrors
            .Should().BeTrue();
        }
コード例 #8
0
        public void change_default_beatpulse_path()
        {
            var options = new BeatPulseOptions();

            options.Path
            .Should().Be(BeatPulseKeys.BEATPULSE_DEFAULT_PATH);

            options.ConfigurePath("health");

            options.Path
            .Should().Be("health");
        }
コード例 #9
0
        public void change_default_timeout()
        {
            var options = new BeatPulseOptions();

            options.Timeout
            .Should().Be(-1);

            options.ConfigureTimeout(1000);

            options.Timeout
            .Should().Be(1000);
        }
コード例 #10
0
        public static IApplicationBuilder UseBeatPulse(this IApplicationBuilder app, Action <BeatPulseOptions> setup)
        {
            var options = new BeatPulseOptions();

            setup?.Invoke(options);

            app.MapWhen(context => context.IsBeatPulseRequest(options), appBuilder =>
            {
                appBuilder.UseMiddleware <BeatPulseMiddleware>(options);
            });

            return(app);
        }
コード例 #11
0
        public static IWebHostBuilder UseBeatPulse(this IWebHostBuilder hostBuilder, Action <BeatPulseOptions> setup = null)
        {
            var options = new BeatPulseOptions();

            setup?.Invoke(options);

            hostBuilder.ConfigureServices(defaultServices =>
            {
                defaultServices.AddSingleton <IStartupFilter>(new BeatPulseFilter(options));
            });

            return(hostBuilder);
        }
コード例 #12
0
        async Task <LivenessResult> RunLiveness(IBeatPulseLiveness liveness, BeatPulseOptions options, HttpContext httpContext)
        {
            _logger.LogInformation($"Executing liveness {liveness.Name}.");

            var livenessResult = new LivenessResult(liveness.Name, liveness.Path);

            livenessResult.StartCounter();

            try
            {
                using (var cancellationTokenSource = new CancellationTokenSource())
                {
                    var livenessTask = liveness.IsHealthy(httpContext, _environment.IsDevelopment(), cancellationTokenSource.Token);

                    if (await Task.WhenAny(livenessTask, Task.Delay(options.Timeout, cancellationTokenSource.Token)) == livenessTask)
                    {
                        // The liveness is executed successfully and get the results
                        var(message, healthy) = await livenessTask;
                        livenessResult.StopCounter(message, healthy);

                        _logger.LogInformation($"The liveness {liveness.Name} is executed.");
                    }
                    else
                    {
                        // The liveness is timeout ( from configured options)
                        _logger.LogWarning($"The liveness {liveness.Name} is timeout");

                        cancellationTokenSource.Cancel();
                        livenessResult.StopCounter(BeatPulseKeys.BEATPULSE_TIMEOUT_MESSAGE, false);
                    }
                }
            }
            catch (Exception ex)
            {
                // The uri executed is now well formed, dns not found
                // or any unexpected errror
                _logger.LogError(ex, $"The liveness {liveness.Name} is unhealthy.");

                var message = _environment.IsDevelopment()
                    ? ex.Message : string.Format(BeatPulseKeys.BEATPULSE_HEALTHCHECK_DEFAULT_ERROR_MESSAGE, liveness.Name);

                livenessResult.StopCounter(message, false);
            }

            return(livenessResult);
        }
コード例 #13
0
        public void change_default_cachemode()
        {
            var options = new BeatPulseOptions();

            options.CacheOutput
            .Should().BeFalse();

            options.ConfigureOutputCache(10, CacheMode.HeaderAndServerMemory);

            options.CacheOutput
            .Should().BeTrue();

            options.CacheMode
            .Should().Be(CacheMode.HeaderAndServerMemory);

            options.CacheDuration
            .Should().Be(10);
        }
コード例 #14
0
        public async Task <IEnumerable <LivenessResult> > IsHealthy(string path, BeatPulseOptions options, HttpContext httpContext)
        {
            _logger.LogInformation($"BeatPulse is checking health on [BeatPulsePath]/{path}");

            if (String.IsNullOrEmpty(path))
            {
                var livenessResults = new List <LivenessResult>();

                foreach (var liveness in _beatPulseContext.AllLiveness)
                {
                    var healthCheckResult = await RunLiveness(liveness, options, httpContext);

                    await RunTrackers(healthCheckResult);

                    livenessResults.Add(healthCheckResult);

                    if (!healthCheckResult.IsHealthy && !options.DetailedOutput)
                    {
                        //if is unhealthy and not detailed options is true return inmediatly

                        _logger.LogWarning($"Liveness {liveness.Name} is not healthy");

                        return(livenessResults);
                    }
                }

                return(livenessResults);
            }
            else
            {
                var liveness = _beatPulseContext.FindLiveness(path);

                if (liveness != null)
                {
                    var livenessResult = await RunLiveness(liveness, options, httpContext);

                    return(new[] { livenessResult });
                }
            }

            return(Enumerable.Empty <LivenessResult>());
        }
コード例 #15
0
        public void evaluate_if_some_request_is_beatpulse_request_when_port_is_configured()
        {
            var options = new BeatPulseOptions()
                          .ConfigurePath("hc")
                          .ConfigurePort(5000);

            var httpContext = new DefaultHttpContext();

            httpContext.Request.Method       = "GET";
            httpContext.Request.Path         = "/hc";
            httpContext.Connection.LocalPort = 5000;

            httpContext.IsBeatPulseRequest(options)
            .Should().BeTrue();

            httpContext.Connection.LocalPort = 6000;

            httpContext.IsBeatPulseRequest(options)
            .Should().BeFalse();
        }
コード例 #16
0
        public void return_true_if_request_is_beaptulse_request()
        {
            var options = new BeatPulseOptions();

            var httpContext = new DefaultHttpContext();

            httpContext.Request.Method = "GET";
            httpContext.Request.Path   = "/hc";

            httpContext.IsBeatPulseRequest(options)
            .Should().BeTrue();

            options.ConfigurePath("health");

            httpContext.Request.Method = "GET";
            httpContext.Request.Path   = "/health";

            httpContext.IsBeatPulseRequest(options)
            .Should().BeTrue();
        }
コード例 #17
0
        public void deepclone_create_clone()
        {
            var options = new BeatPulseOptions();

            options.ConfigureDetailedOutput(includeExceptionMessages: true);

            options.DetailedErrors
            .Should().BeTrue();

            var clone = options.DeepClone();

            object.ReferenceEquals(options, clone)
            .Should().BeFalse();

            clone.ConfigureDetailedOutput(includeExceptionMessages: false);

            options.DetailedErrors
            .Should().BeTrue();

            clone.DetailedErrors
            .Should().BeFalse();
        }
コード例 #18
0
 public static bool IsBeatPulseRequest(this HttpContext context, BeatPulseOptions options)
 {
     return(context.Request.Method == HttpMethods.Get &&
            context.MatchesBeatPulseRequestPath(options) &&
            context.MatchesBeatPulsePort(options));
 }
コード例 #19
0
 public BeatPulseFilter(BeatPulseOptions options)
 {
     this._options = options;
 }
コード例 #20
0
 private static bool MatchesBeatPulsePort(this HttpContext context, BeatPulseOptions options)
 {
     return(options.Port.HasValue ? context.Connection.LocalPort == options.Port : true);
 }
コード例 #21
0
        async Task <LivenessResult> RunLiveness(IBeatPulseLiveness liveness, LivenessExecutionContext executionContext, BeatPulseOptions options)
        {
            _logger.LogInformation($"Executing liveness {executionContext.Name}.");

            var clock = Clock.StartNew();

            try
            {
                using (var cancellationTokenSource = new CancellationTokenSource())
                {
                    var livenessTask = liveness.IsHealthy(executionContext, cancellationTokenSource.Token);

                    if (await Task.WhenAny(livenessTask, Task.Delay(options.Timeout, cancellationTokenSource.Token)) == livenessTask)
                    {
                        _logger.LogInformation($"The liveness {executionContext.Name} is executed.");

                        return((await livenessTask)
                               .SetEnforced(name: executionContext.Name, path: executionContext.Path, duration: clock.Elapsed(), detailedErrors: options.DetailedErrors));
                    }
                    else
                    {
                        _logger.LogWarning($"The liveness {executionContext.Name} return timeout, execution is cancelled.");

                        cancellationTokenSource.Cancel();

                        return(LivenessResult.TimedOut()
                               .SetEnforced(name: executionContext.Name, path: executionContext.Path, duration: clock.Elapsed(), detailedErrors: options.DetailedErrors));
                    }
                }
            }
            catch (Exception ex)
            {
                // The uri executed is now well formed, dns not found
                // or any other unexpected exceptions from liveness executions.

                _logger.LogError(ex, $"The liveness {executionContext.Name} is unhealthy.");

                return(LivenessResult.UnHealthy(ex)
                       .SetEnforced(name: executionContext.Name, path: executionContext.Path, duration: clock.Elapsed(), detailedErrors: options.DetailedErrors));
            }
        }