W jednym z moich ostatnich artykułów (Application Insights – Ukryty Skarb Azure) przeszliśmy przez podstawową konfigurację usługi application insights oraz bardzo podstawową integrację. Jako, że obiecałem wam więcej treści z tematu, tak oto powstał ten artykuł. Zajmiemy się wprowadzeniem integracji z Application Insights na kolejny poziom. Jako, że to kontynuacja, bardzo zachęcam abyś przeczytał wcześniejszy artykuł.
SeriLog
Zacznijmy od rzeczy podstawowej. Jak przy większości moich projektów zaczynam od dogrania i skonfigurowania biblioteki wspomagającej semantyczne logowanie. W moim przypadku jest to SeriLog. Jeżeli korzystasz z np. Log4Net, nie ma czym się przejmować. Dopóki Twoja biblioteka bazuje na interfejsie ILogger
wszystko powinno być w porządku.
Oczywiście, jak to w świecie .NET-a, aby zacząć używać gotowej paczki, musimy ją pobrać do projektu za pomocą nuget-a. Paczka nazwy się Serilog.AspNetCore
.
Następnie należy, w pliku Program.cs
, utworzyć konfigurację logger-a. Ja to robię w następujący sposób:
public class Program
{
public static void Main(string[] args)
{
var configuration = new LoggerConfiguration()
.MinimumLevel.Debug()
.MinimumLevel.Override("Microsoft", LogEventLevel.Verbose)
.MinimumLevel.Override("System", LogEventLevel.Verbose)
.MinimumLevel.Override("Microsoft.AspNetCore.Authentication", LogEventLevel.Verbose)
.Enrich.FromLogContext()
.Enrich.WithProperty("ApplicationName", typeof(Program).Assembly.GetName().Name)
.WriteTo
.Console();
Log.Logger = configuration.CreateLogger();
try
{
Log.Information("Starting Up");
CreateHostBuilder(args).Build().Run();
}
catch (Exception e)
{
Log.Fatal(e, "Application startup failed");
}
finally
{
Log.CloseAndFlush();
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); })
.UseSerilog();
}
Pewnie zastanawiasz się dlaczego w liniach 7-10 ustawiłem minimalny poziom logowania na Verbose
. Jest to jak najbardziej świadomy i celowy zabieg. Jako, że obecnie dodajemy wszystko na pustym projekcie, wysoki poziom szczegółowości logów pomoże zapełnić usługę application insights. W produkcyjnej aplikacji nie radziłbym go stosować. Czasami zbyt duża liczba logów generuje niepotrzebny szum informacyjny i ciężej znaleźć istotne elementy.
W tym miejscu przydałoby się omówić jeszcze jedną kwestię, mianowicie ulokowanie tego kodu w pliku Program.cs
. Wiem, że często spotykaną praktyką jest konfiguracja logger-a w pliku Startup.cs
. Mamy tam dostęp do całej konfiguracji znajdującej się w pliku appsettings.json
co pozwala nam na uniknięcie problemu typu "kury i jajka". Na szczęście jest łatwy do rozwiązania za pomocą np. zmiennych środowiskowych, dzięki czemu uzyskamy logi z startu aplikacji. Dlatego podejście zaprezentowane wyżej jest, przynajmniej moim odczuciu, zdecydowanie lepsze.
Integracja logów i Application Insights
Kolejnym krokiem, który musisz wykonać, jest skonfigurowanie sdk Application Insights w taki sposób, aby zapisywało logi w swojej usłudze. Kiedyś zadanie było dużo prostsze, wystarczyła pomoc biblioteki Serilog.Sinks.ApplicationInsights. Czas jednak mija i wiele się zmienia. Podczas próby wykorzystania kodu znajdującego się w przykładzie tej biblioteki zobaczysz, że TelemetryConfiguration.Active
jest już oznaczone jako depracted
i nie powinieneś opierać swojej implementacji na tym polu statycznym.
Aby zintegrować logi z Application Insights będziesz musiał dograć jeszcze jedną paczkę nuget-a - Microsoft.Extensions.Logging.ApplicationInsights
, która doda swoją implementacje interfejsu ILogger
. Następnie będziesz musiał wrócić do pliku Startup.cs
i dodać konfigurację przedstawioną poniżej:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); })
.UseSerilog()
.ConfigureLogging(builder =>
{
builder.AddApplicationInsights("InstrumentationKey");
});
Cloud Role i Cloud Instance
Ostatnimi elementami, jakie chce poruszyć, są Cloud Role Name
i Cloud Role Instance
.
Cloud Role Name
to nazwa procesu. Może to być nazwa naszej aplikacji, serwisu etc. Jest wykorzystywany np. w panelu ApplicationMap
, aby dokonać wizualizacji przepływu informacji pomiędzy procesami.
Uważam to za szczególnie przydatne, tym bardziej w momencie, kiedy mamy więcej procesów, jak np. Frontend i Backend czy też środowisko mikroserwisowe. W momencie kiedy ta wartość nie jest ustawiona, Application Insights będzie robiło wszystko co w jego mocy, aby nazwać ten proces (np. może użyć nazwy aplikacji jeżeli aplikacja będzie uruchomiona w Azure App Service).
Natomiast Cloud Role Instance
powinno zawierać informację na jakiej instancji procesu Cloud Role Name
działa. Jest to bardzo ważne w momencie, kiedy próbujemy skalować naszą aplikację. Np. gdybyś spróbował zwiększyć liczbę instancji swojej aplikacji i wystawił ją na świat za load balancerem prawdopodobnie chciałbyś mieć informacje, na jakiej instancji występują błędy. Dlatego możemy nadać identyfikator naszej instancji np. "My_Api_Prod_1".
Wymaga to dodania własnej implementacji interfejsu ITelemetryInitializer
gdzie w metodzie Initialize
możesz ustawić wszystkie wartości.
public class ApiTelemetryInitializer : ITelemetryInitializer
{
public void Initialize(ITelemetry telemetry)
{
if (string.IsNullOrEmpty(telemetry.Context.Cloud.RoleName))
{
telemetry.Context.Cloud.RoleName = "My.Api";
telemetry.Context.Cloud.RoleInstance = "My_Api_Prod_1";
}
}
}
Na koniec trzeba powrócić do pliku Startup.cs
, aby zarejestrować utworzoną klasę w kontenerze DI.
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<ITelemetryInitializer, ApiTelemetryInitializer>();
services.AddApplicationInsightsTelemetry();
// ...
}
Podsumowanie
To by było na tyle, konfiguracja powinna śmigać i wyglądać całkiem składnie. W następnym artykule zagłębimy się w panel usługi Application Insights, aby zobaczyć jakie możliwości oferuje.
Jak zwykle mam nadzieje że artykuł się podobał!
Do Następnego!