{"id":1096,"date":"2026-01-24T16:19:35","date_gmt":"2026-01-24T15:19:35","guid":{"rendered":"https:\/\/blog.adameczek.pl\/?p=1096"},"modified":"2026-01-24T17:19:33","modified_gmt":"2026-01-24T16:19:33","slug":"monitoring-aplikacji-net","status":"publish","type":"post","link":"https:\/\/blog.adameczek.pl\/index.php\/2026\/01\/24\/monitoring-aplikacji-net\/","title":{"rendered":"Monitoring aplikacji .NET"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">Co to takiego?<\/h2>\n\n\n\n<p>Domy\u015blamy si\u0119, \u017ce aplikacja mo\u017ce w jaki\u015b spos\u00f3b udost\u0119pnia\u0107 informacj\u0119 o swoim stanie. Takie kontrole stanu mog\u0105 by\u0107 u\u017cytecznymi mechanizmami zar\u00f3wno dla aplikacji monolitycznych, jak i mikrous\u0142ug. W przypadku aplikacji \u201ena produkcji\u201d cz\u0119sto pojawia si\u0119 nawet oczekiwanie, aby \u201ejako\u015b\u201d monitorowa\u0107 co w kodzie szele\u015bci i czy aby nie piszczy albo gorzej \u2013 zgrzyta. Wtedy na og\u00f3\u0142 patrzymy sobie na logi, albo obrazki na <em>Grafanie<\/em>. Bardziej zaawansowani generuj\u0105 maile z alertami. Czasem troch\u0119 po partyzancku, \u201eaby co\u015b tam by\u0142o\u201d. <\/p>\n\n\n\n<p>A\u017c tu pewnego dnia IT za\u017c\u0105da\u0142o kontroli stanu aplikacji na klastrze. Wymy\u015blili sobie, \u017ce skoro Kubernetes pozwala sprawdzi\u0107 czy aplikacja wci\u0105\u017c dzia\u0142a i jak dzia\u0142a, to chcieliby, \u017ceby backend zaimplementowa\u0142 tak\u0105 kontrol\u0119 stanu. Nie rozpisuj\u0105c sie tu o tym, jakie zaprojektowali dzia\u0142ania, kiedy ten stan nie b\u0119dzie zadowalaj\u0105cy, powiem tylko, \u017ce rozgorza\u0142a dyskusja ile to z tym b\u0119dzie roboty. Niby rzecz prosta, ale okaza\u0142o si\u0119, \u017ce to niecodzienne \u017c\u0105danie IT rozgrza\u0142o g\u0142owy. \u017beby jako\u015b temat ogarn\u0105\u0107 po stronie backendu .NETowego, zrobi\u0142em instrukcj\u0119 na firmowym conflu, kt\u00f3r\u0105 tu bez zb\u0119dnej zw\u0142oki przytocz\u0119.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Rodzaje kontroli stanu push i pull<\/h2>\n\n\n\n<p>Kontrola stanu mo\u017ce przebiega\u0107 w dw\u00f3ch kierunkach: albo kontrolowany system periodycznie raportuje sw\u00f3j stan do systemu monitoruj\u0105cego bez pytania. To nazywa si\u0119 <em>heartbeat<\/em> lub <em>push<\/em>. Dzia\u0142a nawet za NAT czy firewallem, ale brak aktywno\u015bci mo\u017ce oznacza\u0107 zar\u00f3wno awari\u0119, jak i problem z sieci\u0105. <\/p>\n\n\n\n<p>Drugi spos\u00f3b okre\u015blany jako <em>pooling<\/em> lub <em>pull<\/em>, polega na tym, \u017ce aplikacja odpowiada na \u017c\u0105danie systemu monitoruj\u0105cego. Warunek \u2212 aplikacja musi by\u0107 osi\u0105galna sieciowo.<\/p>\n\n\n\n<p><em>Kubernetes<\/em>, <em>load balancery<\/em> maj\u0105 wbudowany mechanizm badania stanu przez wys\u0142anie \u017c\u0105dania do aplikacji \u2013 <em>pull<\/em>. Mo\u017cna odpyta\u0107 aplikacj\u0119 czy \u017cyje (odpowiada) \u2013 <em>liveness probe<\/em>. Je\u015bli apka nie odezwie si\u0119, to pod zostanie ubity i postawiony na nowo z nadziej\u0105, \u017ce to za\u0142atwi problem. Wersja zaawansowana to dodatkowa kontrola podsystem\u00f3w aplikacji \u2013 <em>readiness probe<\/em>. Taki test powinien sprawdzi\u0107, czy wszystkie \u017cywotne us\u0142ugi dzia\u0142aj\u0105 poprawnie (czy baza odpowiada w sko\u0144czonym czasie, czy odbierane s\u0105 zdarzenia z Kafki itd).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Kontrola stanu w ASP.NET <\/h2>\n\n\n\n<p>Od .NET6 mamy w pe\u0142ni funkcjonalny mechanizm <em>health check<\/em> w postaci endpoint\u00f3w http, czyli system <em>pull<\/em>. My\u015bl\u0119, \u017ce lepiej jest pokaza\u0107 dzia\u0142aj\u0105cy kod ni\u017c opowiada\u0107 albo pokaza\u0107 zrzut ekranu, dlatego od razu b\u0119dzie przyk\u0142ad dzia\u0142aj\u0105cej aplikacji. Przyk\u0142ad b\u0119dzie w .NET10. Wyprodukujemy aplikacj\u0119, kt\u00f3ra udost\u0119pni dedykowane endpointy. Ich odpytanie uruchomi testy a one zwr\u00f3c\u0105 rezultat u\u017cyteczny dla systemu monitoruj\u0105cego. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Struktura aplikacji<\/h3>\n\n\n\n<p>\u017beby pozosta\u0107 w trendzie, u\u017cyj\u0119 <em>Clean Architecture.<\/em> W listingach b\u0119d\u0105 podane przestrzenie nazw <em>namespace<\/em>. Gdyby kto\u015b chcia\u0142 wykona\u0107 przyk\u0142ad razem ze mn\u0105 (do czego zach\u0119cam), to \u0142atwo b\u0119dzie umiejscowi\u0107 kod u siebie.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img decoding=\"async\" width=\"213\" height=\"156\" src=\"https:\/\/blog.adameczek.pl\/wp-content\/uploads\/2026\/01\/image.png\" alt=\"\" class=\"wp-image-1104\"\/><\/figure>\n<\/div>\n\n\n<h3 class=\"wp-block-heading\">Niezb\u0119dne sk\u0142adniki<\/h3>\n\n\n\n<p>Przepis na <em>healthchecki <\/em>jest nast\u0119puj\u0105cy:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Zaimplementuj <em>IHealthCheck <\/em>dla ka\u017cedgo testu, jaki ma by\u0107 uruchomiony<\/li>\n\n\n\n<li>Zaimplementuj odpowiednie serwisy w warstwie <em>Infrastructure<\/em><\/li>\n\n\n\n<li>Zmapuj testy do endpoint\u00f3w<\/li>\n\n\n\n<li>Zarejestruj serwisy w DI<\/li>\n\n\n\n<li>Skonfiguruj aplikacj\u0119<\/li>\n<\/ul>\n\n\n\n<p>Czas na implementacj\u0119. Na pocz\u0105tek utw\u00f3rz katalog i now\u0105 \u201epust\u0105\u201d aplikacj\u0119 asp.net<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>mkdir .\/healthchecks\ncd healthchecks\ndotnet new web<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Mapowanie<\/h4>\n\n\n\n<p>To nie jest pierwszy sk\u0142adnik w przepisie, ale troch\u0119 wyja\u015bnia co i dlaczego za chwil\u0119 wykonamy. <\/p>\n\n\n\n<p>Tu dziej\u0105 si\u0119 dwie rzeczy: tworzone s\u0105 endpointy oraz mapowane s\u0105 testy. Zapytanie <em>\/health\/ready<\/em> uruchomi testy zawieraj\u0105ce tag <strong>\u201eready\u201d<\/strong>. S\u0105 to testy sprawdzaj\u0105ce, czy aplikacja jest gotowa do pracy wraz ze wszystkimi niezb\u0119dnymi sk\u0142adnikami. Takich sk\u0142adnik\u00f3w jest wi\u0119cej ni\u017c jeden, dlatego szukamy wszystkich otagowanych <strong>\u201eready\u201d<\/strong>. Test <em>liveness probe<\/em> pod adresem <em>\/health\/live<\/em> jest du\u017co prostrzy i jest jeden. Mapuj\u0119 go wed\u0142ug nazwy <strong>\u201elive\u201d<\/strong>.<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism off-numbers lang-csharp\" data-lang=\"C#\"><code>namespace Infrastructure.HealthChecks.Extensions;\n\npublic static class WebApplicationExtensions\n{\n    public static void MapAppHealthChecks(this WebApplication app)\n    {\n        app.MapHealthChecks(&quot;\/health\/ready&quot;, new HealthCheckOptions\n        {\n            Predicate = check => check.Tags.Contains(&quot;ready&quot;)\n        });\n        app.MapHealthChecks(&quot;\/health\/live&quot;, new HealthCheckOptions\n        {\n            Predicate = check => check.Name == &quot;live&quot;\n        });\n    }<\/code><\/pre><\/div>\n\n\n\n<h4 class=\"wp-block-heading\">Implementacja <em>health check<\/em>\u00f3w<\/h4>\n\n\n\n<p>Na pocz\u0105tek potrzebne modele.<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism off-numbers lang-csharp\" data-lang=\"C#\"><code>namespace healthchecks.Infrastructure.Persistence.DataModels;\n\npublic class DbHealthStatus\n{\n    public HealthStatus Status { get; set; }\n    public double ResponseTime { get; set; }\n    public string Message { get; set; } = null!;\n    public DateTime SystemTime { get; set; }\n    public string? UserName { get; set; }\n    public string? SessionId { get; set; }\n}\n\npublic enum HealthStatus\n{\n    Critical,\n    Error,\n    Slow,\n    Ready\n}<\/code><\/pre><\/div>\n\n\n\n<p>Test bazy danych polega na uruchomioniu procedury, kt\u00f3ra wykona proste obliczenia i zwr\u00f3ci czas, nazw\u0119 u\u017cytkownika i komunikat, w kt\u00f3rym okre\u015bli sw\u00f3j stan.<\/p>\n\n\n\n<p>Implementacja testu bazy danych (interfejsu <em>IHealthCheck<\/em>) polega na uruchomieniu przez serwis <em>IDbStatusService<\/em> procedury sk\u0142adowanej i interpretacji zwr\u00f3conych danych. Je\u015bli baza danych jest w pe\u0142ni sprawna, to \u017c\u0105danie <em>GET: \/health\/ready<\/em> zwr\u00f3ci kod 200 i tekst <em>Healthy<\/em> w body odpowiedzi. Je\u015bli baza b\u0119dzie spowolniona, to dostaniemy 200 z tekstem <em>Degraded<\/em>. W przypadku awarii bazy (w domy\u015ble chwilowej) dostaniemy 503 z opisem <em>Unhealthy<\/em>. Status 503 to kod b\u0142\u0119du <em>transient<\/em>, czyli takiego, po kt\u00f3rym mo\u017cemy oczekiwa\u0107, \u017ce za wkr\u00f3tce samoczynnie ust\u0105pi. Kubernetes mo\u017ce w takim przypadku odci\u0105\u0107 taki<em>pod <\/em>od puli sprawnych i spr\u00f3bowa\u0107 pod\u0142\u0105czy\u0107 po chwili.<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism off-numbers lang-csharp\" data-lang=\"C#\"><code>using DbStatus = healthchecks.Infrastructure.Persistence.DataModels;\n\nnamespace healthchecks.Infrastructure.HealthChecks;\n\ninternal class DatabaseHealthCheck(IServiceScopeFactory scopeFactory) : IHealthCheck\n{\n    public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken ct = default)\n    {\n        try\n        {\n            using var scope = scopeFactory.CreateScope();\n            var dbStatusService = scope.ServiceProvider.GetRequiredService<IDbStatusService>();\n            var result = await dbStatusService.GetStatus(ct);\n\n            return result.Status switch\n            {\n                DbStatus.HealthStatus.Ready => HealthCheckResult\n                    .Healthy(&quot;Database connected&quot;),\n                DbStatus.HealthStatus.Slow => HealthCheckResult\n                    .Degraded(&quot;Database connected, but response is slow&quot;),\n                _ => HealthCheckResult.Unhealthy(&quot;Database check failed&quot;),\n            };\n        }\n        catch (Exception ex)\n        {\n            return HealthCheckResult.Unhealthy(&quot;Database connection failed&quot;, ex);\n        }\n    }\n}<\/code><\/pre><\/div>\n\n\n\n<h4 class=\"wp-block-heading\">Serwis IDbStatusService<\/h4>\n\n\n\n<p>Potrzebny jest inrerfejs<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism off-numbers lang-csharp\" data-lang=\"C#\"><code>namespace healthchecks.Infrastructure.HealthChecks.Interfaces;\n\npublic interface IDbStatusService\n{\n    Task<DbHealthStatus> GetStatus(CancellationToken ct);\n}<\/code><\/pre><\/div>\n\n\n\n<p>Nasz <code>DatabaseHealthCheck<\/code> wo\u0142a metod\u0119 <code>IDbStatusService.GetStatus()<\/code>. Konkretna implementacja b\u0119dzie zale\u017cna od tego jak\u0105 baz\u0119 danych testujemy i sposobu w jaki nawi\u0105zujemy z ni\u0105 po\u0142\u0105czenie. <\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-csharp\" data-lang=\"C#\"><code>namespace healthchecks.Infrastructure.Persistence.Services;\n\npublic class DbStatusService(ILogger<DbStatusService> logger) : IDbStatusService\n{\n    public async Task<DbHealthStatus> GetStatus(CancellationToken ct)\n    {\n        try\n        {\n            \/\/ TODO: Implement actual database health check logic\n            var result = new DbHealthStatus \n            {\n                Status = HealthStatus.Ready \n            };\n\n            \/\/ check your db system time zone and modify this accordingly to get UTC time\n            var timeZoneInfoResult = TimeZoneInfo\n                .TryFindSystemTimeZoneById(&quot;Eastern Standard Time&quot;, out var timeZoneInfo);\n            if (timeZoneInfoResult)\n                result.SystemTime = TimeZoneInfo\n                    .ConvertTimeToUtc(result.SystemTime, timeZoneInfo!);\n\n            return result;\n        }\n        catch (Exception ex)\n        {\n            logger.LogError(ex, &quot;Error getting database status&quot;);\n            throw;\n        }\n    }\n}<\/code><\/pre><\/div>\n\n\n\n<p>W przyk\u0142adzie w linii 12 zwracam HealthStatus.Ready, ale w rzeczywisto\u015bci nale\u017ca\u0142oby zmapowa\u0107 odpowied\u017a uzyskan\u0105 z bazy. Je\u015bli odpowiedzi z bazy w og\u00f3le nie b\u0119dzie, to ju\u017c jest obs\u0142u\u017cone w bloku catch <code>DatabaseHealthCheck.CheckHealthAsync()<\/code>.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Procedura sk\u0142adowana<\/h4>\n\n\n\n<p>Oto procedura sk\u0142adowana. To przyk\u0142ad dla bazy <em>Oracle<\/em>.Tu wida\u0107, \u017ce wynik procedury powinien by\u0107 mapowany na  <code>DbHealthStatus<\/code> W procedurze pobierany jest czas SYSTIMESTAMP, kt\u00f3ry mo\u017cna por\u00f3wna\u0107 z czasem aplikacji i obs\u0142u\u017cy\u0107 ewentualne r\u00f3\u017cnice (status <em>Error<\/em> przy r\u00f3\u017cnicy > pr\u00f3g). Liczony jest czas wykonania procedury. W przyk\u0142adzie 1ms jest uznana za czas normalny, a d\u0142u\u017cszy czas przek\u0142ada si\u0119 na <em>Slow<\/em>. Jest te\u017c proste dzia\u0142anie matematyczne. Generalnie chodzi o to, \u017ceby procedura by\u0142a lekka, ale testowa\u0142a stan bazy.<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism off-numbers lang-sql\" data-lang=\"SQL\"><code>CREATE OR REPLACE PROCEDURE READINESS_TEST (\n\n    p_max_response_time IN NUMBER := 1,\n    p_cursor OUT SYS_REFCURSOR\n)\nIS\n    v_start_time   TIMESTAMP(6);\n    v_end_time     TIMESTAMP(6);\n    v_current_time DATE;\n    v_systimestamp TIMESTAMP(6);\n    v_user_name    VARCHAR2(30);\n    v_session_id   VARCHAR2(20);\n    v_status       VARCHAR2(20);\n    v_message      VARCHAR2(100);\n    v_sys_time     VARCHAR2(30);\n    v_response_ms  NUMBER;\n    v_math_result  NUMBER;\n    v_max_time     NUMBER;\n    \nBEGIN\n    v_start_time := SYSTIMESTAMP;\n    v_systimestamp := SYSTIMESTAMP;\n    v_status := &#39;INIT&#39;;\n    v_message := &#39;Starting&#39;;\n    v_response_ms := 0;\n\n    v_max_time := NVL(p_max_response_time, 1);\n    \n    BEGIN\n        SELECT SYSDATE INTO v_current_time FROM DUAL;\n        SELECT SUBSTR(USER, 1, 20) INTO v_user_name FROM DUAL;\n        SELECT SUBSTR(TO_CHAR(SYS_CONTEXT(&#39;USERENV&#39;, &#39;SESSIONID&#39;)), 1, 15) INTO v_session_id FROM DUAL;\n        SELECT POWER(2, 3) INTO v_math_result FROM DUAL;\n        \n        v_end_time := SYSTIMESTAMP;\n        v_response_ms := EXTRACT(DAY FROM (v_end_time - v_start_time)) * 86400000 +\n                         EXTRACT(HOUR FROM (v_end_time - v_start_time)) * 3600000 +\n                         EXTRACT(MINUTE FROM (v_end_time - v_start_time)) * 60000 +\n                         EXTRACT(SECOND FROM (v_end_time - v_start_time)) * 1000;\n        \n        v_sys_time := TO_CHAR(v_systimestamp, &#39;YYYY-MM-DD HH24:MI:SS&#39;);\n        \n        IF v_response_ms <= v_max_time THEN\n            v_status := &#39;READY&#39;;\n            v_message := &#39;OK&#39;;\n        ELSE\n            v_status := &#39;SLOW&#39;;\n            v_message := &#39;TIMEOUT&#39;;\n        END IF;\n        \n    EXCEPTION\n        WHEN OTHERS THEN\n            v_status := &#39;ERROR&#39;;\n            v_message := &#39;FAILED&#39;;\n            v_response_ms := -1;\n            v_sys_time := TO_CHAR(SYSTIMESTAMP, &#39;YYYY-MM-DD HH24:MI:SS&#39;);\n    END;\n    \n    OPEN p_cursor FOR\n        SELECT v_status as Status,\n               v_response_ms as ResponseTime,\n               v_message as Message,\n               v_sys_time as SystemTime,\n               v_user_name as UserName,\n               v_session_id as SessionId\n        FROM DUAL;\n        \nEXCEPTION\n    WHEN OTHERS THEN\n        OPEN p_cursor FOR\n            SELECT &#39;CRITICAL&#39; as Status,\n                   -1 as ResponseTime,\n                   &#39;CRITICAL&#39; as Message,\n                   TO_CHAR(SYSTIMESTAMP, &#39;YYYY-MM-DD HH24:MI:SS&#39;) as SystemTime,\n                   &#39;UNKNOWN&#39; as UserName,\n                   &#39;UNKNOWN&#39; as SessionId\n            FROM DUAL;\nEND READINESS_TEST;<\/code><\/pre><\/div>\n\n\n\n<h4 class=\"wp-block-heading\">Rejestracja w kontenerze DI<\/h4>\n\n\n\n<p>Rejestracja to kolejny punkt przepisu. Do <code>IServiceCollection<\/code> dodaj\u0119 serwis bazodanowy i implementacj\u0119 <code>DatabaseHealthCheck<\/code>. Zwr\u00f3\u0107 uwag\u0119j, \u017ce <em>liveness probe<\/em> jest zdefiniowana jedn\u0105 linijk\u0105 nr 9. Je\u015bli aplikacja \u201e\u017cyje\u201d to na \u017c\u0105danie <em>GET: \/health\/live<\/em> zwr\u00f3ci 200-OK, a je\u015bli nie, to wyst\u0105pi timeout. <\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-csharp\" data-lang=\"C#\"><code>namespace healthchecks.Infrastructure.HealthChecks.Extensions;\n\npublic static class ServiceCollectionExtensions\n{\n    public static IServiceCollection AddAppHealthChecks(this IServiceCollection services)\n    {\n        services.AddHealthChecks()\n            .AddCheck<DatabaseHealthCheck>(&quot;database&quot;, tags: [&quot;ready&quot;])\n            .AddCheck(&quot;live&quot;, () => HealthCheckResult.Healthy(&quot;application&quot;));\n\n        services.AddScoped<IDbStatusService, DbStatusService>();\n\n        return services;\n    }\n}<\/code><\/pre><\/div>\n\n\n\n<h4 class=\"wp-block-heading\">Konfiguracja aplikacji<\/h4>\n\n\n\n<p>To ostatni punkt przepisu. Wykorzystujemy metody rozszerzaj\u0105ce zdefiniowane wcze\u015bniej, Usuwamy zawarto\u015b\u0107 <em>Program.cs<\/em> i wklejamy<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism off-numbers lang-csharp\" data-lang=\"C#\"><code>var builder = WebApplication.CreateBuilder(args);\n\n\/\/ health check services registration\nbuilder.Services.AddAppHealthChecks();\nvar app = builder.Build();\n\/\/ endpoint&#39;s mapping\nWebApplicationExtensions.MapAppHealthChecks(app);\nawait app.RunAsync();<\/code><\/pre><\/div>\n\n\n\n<p>W <em>launchsettings.json<\/em> ustaw port dla \u017c\u0105da\u0144 http na 5000. Ten sam podaj IT, \u017ceby mogli skonfigurowa\u0107 testy po swojej stronie.<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism off-numbers lang-json\" data-lang=\"JSON\"><code>{\n  &quot;$schema&quot;: &quot;https:\/\/json.schemastore.org\/launchsettings.json&quot;,\n  &quot;profiles&quot;: {\n    &quot;http&quot;: {\n      &quot;launchBrowser&quot;: false,\n      &quot;applicationUrl&quot;: &quot;http:\/\/localhost:5000&quot;,\n      &quot;environmentVariables&quot;: {\n        &quot;ASPNETCORE_ENVIRONMENT&quot;: &quot;Development&quot;\n      }\n    }\n  }\n}<\/code><\/pre><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Obliczanie wynik\u00f3w wielu test\u00f3w<\/h2>\n\n\n\n<p>W linii 8 listingu <code>AddAppHealthChecks<\/code> rejestrowany jest <code>DatabaseHealthCheck<\/code> o tagu \u201eready\u201d. Wcze\u015bniej napisa\u0142em, \u017ce <em>readiness probe<\/em> powinna sprawdzi\u0107 wszystkie istotne sk\u0142adniki, \u017ceby uzna\u0107 aplikacj\u0119 za gotow\u0105 do pracy. I \u017ce robimy to przez rejestracj\u0119 kolejnych check\u00f3w z tagiem \u201eready\u201d, np. <code>KafkaHealthCheck<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.AddCheck<KafkaHealthCheck>(\"messaging\", tags: [\"ready\"])<\/code><\/pre>\n\n\n\n<p>Co si\u0119 stanie, je\u015bli testy zwr\u00f3c\u0105 r\u00f3\u017cne wyniki? Odpowied\u017a jest kr\u00f3tka: zwyci\u0105\u017ca najgorszy. Framework oblicza wynik ostateczny w nast\u0119puj\u0105cy spos\u00f3b:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Healthy < Degraded < Unhealthy<\/code><\/pre>\n\n\n\n<p>I jest to zgodne z tym czego si\u0119 spodziewamy bo testach.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Test dzia\u0142ania<\/h2>\n\n\n\n<p>Uruchom aplikacj\u0119 i wstaw do przegl\u0105darki \u017c\u0105danie:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>http:\/\/localhost:5000\/health\/ready<\/code><\/pre>\n\n\n\n<p>W odpowiedzi dostaniesz napis Healthy. W postmanie zobaczysz jeszcze status 200 OK.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Podsumowanie<\/h2>\n\n\n\n<p>Artyku\u0142 na\u015bwietli\u0142 tematyk\u0119 testowania stanu aplikacji. W taki sam spos\u00f3b mo\u017cesz bada\u0107 stan monolit\u00f3w i mikroserwis\u00f3w.<\/p>\n\n\n\n<p>By\u0142a te\u017c okazja do prze\u015bledzenia implementacji <em>health check<\/em> i zbudowania dzia\u0142aj\u0105cej aplikacji demonstracyjnej. Wszyskto zosta\u0142o obja\u015bnienione. Wiesz te\u017c, jak dodawa\u0107 kolejne testy. Mam nadziej\u0119, \u017ce si\u0119 przyda \ud83d\ude42 <\/p>\n\n\n\n<p>Jak zwykle kod jest na <a href=\"https:\/\/github.com\/madameczek\/healthchecks\">Githubie<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Co to takiego? Domy\u015blamy si\u0119, \u017ce aplikacja mo\u017ce w jaki\u015b spos\u00f3b udost\u0119pnia\u0107 informacj\u0119 o swoim stanie. Takie kontrole stanu mog\u0105 by\u0107 u\u017cytecznymi mechanizmami zar\u00f3wno dla aplikacji monolitycznych, jak i mikrous\u0142ug. W przypadku aplikacji \u201ena produkcji\u201d cz\u0119sto pojawia si\u0119 nawet oczekiwanie, aby \u201ejako\u015b\u201d monitorowa\u0107 co w kodzie szele\u015bci i czy aby nie piszczy albo gorzej \u2013 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":822,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"ocean_post_layout":"","ocean_both_sidebars_style":"","ocean_both_sidebars_content_width":0,"ocean_both_sidebars_sidebars_width":0,"ocean_sidebar":"","ocean_second_sidebar":"","ocean_disable_margins":"enable","ocean_add_body_class":"","ocean_shortcode_before_top_bar":"","ocean_shortcode_after_top_bar":"","ocean_shortcode_before_header":"","ocean_shortcode_after_header":"","ocean_has_shortcode":"","ocean_shortcode_after_title":"","ocean_shortcode_before_footer_widgets":"","ocean_shortcode_after_footer_widgets":"","ocean_shortcode_before_footer_bottom":"","ocean_shortcode_after_footer_bottom":"","ocean_display_top_bar":"default","ocean_display_header":"default","ocean_header_style":"","ocean_center_header_left_menu":"","ocean_custom_header_template":"","ocean_custom_logo":0,"ocean_custom_retina_logo":0,"ocean_custom_logo_max_width":0,"ocean_custom_logo_tablet_max_width":0,"ocean_custom_logo_mobile_max_width":0,"ocean_custom_logo_max_height":0,"ocean_custom_logo_tablet_max_height":0,"ocean_custom_logo_mobile_max_height":0,"ocean_header_custom_menu":"","ocean_menu_typo_font_family":"","ocean_menu_typo_font_subset":"","ocean_menu_typo_font_size":0,"ocean_menu_typo_font_size_tablet":0,"ocean_menu_typo_font_size_mobile":0,"ocean_menu_typo_font_size_unit":"px","ocean_menu_typo_font_weight":"","ocean_menu_typo_font_weight_tablet":"","ocean_menu_typo_font_weight_mobile":"","ocean_menu_typo_transform":"","ocean_menu_typo_transform_tablet":"","ocean_menu_typo_transform_mobile":"","ocean_menu_typo_line_height":0,"ocean_menu_typo_line_height_tablet":0,"ocean_menu_typo_line_height_mobile":0,"ocean_menu_typo_line_height_unit":"","ocean_menu_typo_spacing":0,"ocean_menu_typo_spacing_tablet":0,"ocean_menu_typo_spacing_mobile":0,"ocean_menu_typo_spacing_unit":"","ocean_menu_link_color":"","ocean_menu_link_color_hover":"","ocean_menu_link_color_active":"","ocean_menu_link_background":"","ocean_menu_link_hover_background":"","ocean_menu_link_active_background":"","ocean_menu_social_links_bg":"","ocean_menu_social_hover_links_bg":"","ocean_menu_social_links_color":"","ocean_menu_social_hover_links_color":"","ocean_disable_title":"default","ocean_disable_heading":"default","ocean_post_title":"","ocean_post_subheading":"","ocean_post_title_style":"","ocean_post_title_background_color":"","ocean_post_title_background":0,"ocean_post_title_bg_image_position":"","ocean_post_title_bg_image_attachment":"","ocean_post_title_bg_image_repeat":"","ocean_post_title_bg_image_size":"","ocean_post_title_height":0,"ocean_post_title_bg_overlay":0.5,"ocean_post_title_bg_overlay_color":"","ocean_disable_breadcrumbs":"default","ocean_breadcrumbs_color":"","ocean_breadcrumbs_separator_color":"","ocean_breadcrumbs_links_color":"","ocean_breadcrumbs_links_hover_color":"","ocean_display_footer_widgets":"default","ocean_display_footer_bottom":"default","ocean_custom_footer_template":"","_jetpack_memberships_contains_paid_content":false,"ocean_post_oembed":"","ocean_post_self_hosted_media":"","ocean_post_video_embed":"","ocean_link_format":"","ocean_link_format_target":"self","ocean_quote_format":"","ocean_quote_format_link":"post","ocean_gallery_link_images":"on","ocean_gallery_id":[],"footnotes":""},"categories":[10,21],"tags":[16,15,30],"class_list":["post-1096","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-net","category-c","tag-asp-net","tag-csharp","tag-observability","entry","has-media"],"jetpack_featured_media_url":"https:\/\/blog.adameczek.pl\/wp-content\/uploads\/2024\/05\/Csharp_logo.png","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/blog.adameczek.pl\/index.php\/wp-json\/wp\/v2\/posts\/1096","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.adameczek.pl\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.adameczek.pl\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.adameczek.pl\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.adameczek.pl\/index.php\/wp-json\/wp\/v2\/comments?post=1096"}],"version-history":[{"count":34,"href":"https:\/\/blog.adameczek.pl\/index.php\/wp-json\/wp\/v2\/posts\/1096\/revisions"}],"predecessor-version":[{"id":1142,"href":"https:\/\/blog.adameczek.pl\/index.php\/wp-json\/wp\/v2\/posts\/1096\/revisions\/1142"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.adameczek.pl\/index.php\/wp-json\/wp\/v2\/media\/822"}],"wp:attachment":[{"href":"https:\/\/blog.adameczek.pl\/index.php\/wp-json\/wp\/v2\/media?parent=1096"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.adameczek.pl\/index.php\/wp-json\/wp\/v2\/categories?post=1096"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.adameczek.pl\/index.php\/wp-json\/wp\/v2\/tags?post=1096"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}