上一次我們講了 OpenTelemetry Logs。今天繼續來說說 OpenTelemetry Traces。 在今天的微服務和雲原生環境中,理解和監控系統的行為變得越來越重要。在當下我們實現一個功能可能需要調用了 N 個方法,涉及到 N 個服務。方法之間的調用如蜘蛛網一樣。分散式追蹤這個時候就至 ...
上一次我們講了 OpenTelemetry Logs
。今天繼續來說說 OpenTelemetry Traces
。
在今天的微服務和雲原生環境中,理解和監控系統的行為變得越來越重要。在當下我們實現一個功能可能需要調用了 N 個方法,涉及到 N 個服務。方法之間的調用如蜘蛛網一樣。分散式追蹤這個時候就至關重要。它可以把我們程式的調用鏈可視化。這對於運維人員監控程式狀態,開發人員 trouble shooting 都非常用幫助。
什麼是 OpenTelemetry Traces
OpenTelemetry Traces
是 OpenTelemetry
提供的一種遙測數據類型,用於記錄和描述在分散式系統中的單個操作或工作單元的生命周期。
在 OpenTelemetry
中,一個 Trace
可以被視為由一系列相關的事件組成的時間線,這些事件被稱為 Spans
。每個 Span
可以包含多個屬性、註釋和事件,用於描述在該 Span 的生命周期中發生的特定操作或事件。
例如,一個 HTTP 請求可以被表示為一個 Span
,其中包含了請求的開始時間、結束時間、HTTP 方法、URL、狀態碼等信息。如果這個請求還調用了其他的服務或資料庫,那麼這些調用也可以被表示為與原始請求 Span
相關聯的子 Span
。
註意:
Span
是OpenTelemetry
定義的概念,在 .NET 中使用Activity
表示一個Span
。
以上的話呢比較官方,是我用 chatGPT 生成的。還是直接用代碼來演示一下效果大家好理解。
示例:追蹤 Http 與 Database
在日常的開發活動中,http
與 database
操作基本就是涵蓋了 99%
的場景。很多時候我們希望監控應用程式對每個請求的響應速度,以及其中資料庫操作的耗時。這是一個非常非常常見的需求。以下使用一個用戶登錄介面來演示。
安裝依賴
<PackageReference Include="Npgsql" Version="8.0.3" />
<PackageReference Include="Npgsql.OpenTelemetry" Version="8.0.3" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.8.1" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.8.1" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.8.1" />
使用 nuget 安裝以上包。
註入服務
var otel = builder.Services.AddOpenTelemetry();
// Configure OpenTelemetry Resources with the application name
otel.ConfigureResource(resource => resource
.AddService(builder.Environment.ApplicationName));
otel.WithTracing(tracing =>
{
tracing
.AddAspNetCoreInstrumentation()
.AddNpgsql()
.AddOtlpExporter(otlpOptions =>
{
otlpOptions.Protocol = OtlpExportProtocol.HttpProtobuf;
otlpOptions.Endpoint = new Uri("http://192.168.0.1:5341/ingest/otlp/v1/traces");
});
});
跟 Logs 類似,我們使用 WithTracing 擴展方法來對 Traces 進行配置。
- 調用
AddAspNetCoreInstrumentation
方法來添加對AspNetCore
框架的跟蹤支持。這將自動跟蹤應用程式中的HTTP請求和響應,並生成相應的跟蹤數據。 - 調用
AddNpgsql
方法來添加對Npgsql
庫的跟蹤支持。這將自動跟蹤應用程式中使用Npgsql
庫進行的資料庫操作,並生成相應的跟蹤數據。 - 我們調用
AddOtlpExporter
方法來添加一個OTLP
(OpenTelemetry Protocol)導出器。這個導出器將把跟蹤數據發送到指定的OTLP
接收端。在這裡,我們將跟蹤數據發送到"http://192.168.0.201:5341/ingest/otlp/v1/traces"這個地址。
登錄代碼
public class UserRepository
{
private readonly string _connectionString = "Host=127.0.0.1;Username=postgres;Password=123456";
public async Task<User> GetUserAsync(string username, string password)
{
using var conn = new NpgsqlConnection(_connectionString);
conn.Open();
using var cmd = new NpgsqlCommand("SELECT * FROM t_users WHERE username = @username AND password = @password", conn);
cmd.Parameters.AddWithValue("username", username);
cmd.Parameters.AddWithValue("password", password);
using var reader = await cmd.ExecuteReaderAsync();
if (reader.Read())
{
return new User
{
Id = reader.GetString(0),
Username = reader.GetString(1),
Password = reader.GetString(2),
// 其他欄位...
};
}
return null;
}
}
public class User
{
public string Id { get; set; }
public string Username { get; set; }
public string Password { get; set; }
// 其他欄位...
}
[HttpPost]
public async Task<string> Login([FromBody] LoginModel model)
{
var user = await new UserRepository().GetUserAsync(model.Username, model.Password);
if (user != null)
{
return "ok";
}
return "error";
}
平平無奇的代碼,簡單演示一下用用戶名密碼進行登錄。在這裡我想指出的一個點是:
其中並沒有任何
Trace
的代碼會侵入到我們的業務中。
在 Seq 中查看 Trace
以上就是所有的關鍵代碼。讓我們運行程式使用 postman 調用登錄介面。打開 Seq
界面進行查看。
Trace 的信息已經到了 Seq 里。可以看到整個 POST Account 介面耗時 326ms,其中 postgres 耗時 42 ms。點擊每一行都有更詳細的屬性。比如 postgres 里包含了 connection string,sql statement 等非常有用的信息。
示例:自定義 Trace 內容
以上示例能是使用現成的庫進行 Trace
。雖然絕大多數情況下已經夠用了。但是有的時候我們想更加詳細的對我們的程式進行追蹤,那麼就需要自己來定義 Span
(Activity)來實現了。以下就讓我們通過一個獲取天氣的介面來演示如何自定義 Activity
。
添加 Trace 的 source
otel.WithTracing(tracing =>
{
tracing
.AddSource("MyTraceSample")
....
});
編寫介面
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private readonly ILogger<WeatherForecastController> _logger;
private readonly ActivitySource _source;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
_source = new ActivitySource("MyTraceSample", "1.0.0");
}
[HttpGet]
public async Task<string> Get([FromQuery]string city)
{
_logger.LogInformation("Hello WeatherForecast");
using (var activity = _source.StartActivity("CallWeatherForecast")) {
activity?.AddTag("city", city);
await Task.Delay(100);
await GetWeatherInfoFromWebservice();
await FormatWeatherInfo();
}
return "24°c";
}
async Task GetWeatherInfoFromWebservice()
{
using (var activity = _source.StartActivity("GetWeatherInfoFromWebservice"))
{
await Task.Delay(200);
}
}
async Task FormatWeatherInfo()
{
using (var activity = _source.StartActivity("FormatWeatherInfo"))
{
await Task.Delay(300);
}
}
}
在 Controller
的 Get
方法可以接受一個 city
的參數,然後調用 GetWeatherInfoFromWebservice
模擬從其他服務獲取數據,再調用 FormatWeatherInfo
方法來模擬對獲取的天氣數據進行格式化。每個方法中都加入了 Task.Delay
來模擬耗時。
首先我們會實例化一個 ActivitySource
。然後在每個需要追蹤的方法最頂上調用 _source.StartActivity
得到一個 Activity
實例。這時候 Activity
就開始計時了。但是為啥沒有 Stop
呢?
顯然是 using
幫我們調用了。 以上代碼可能對業務代碼侵入的比較嚴重,那麼可以使用 AOP
技術進行解耦。這裡就不展開了。
在 Seq 中查看自定義的 Trace
運行程式,使用 postman 進行調用。然後打開 Seq
界面查看 Trace
。
通過以上圖片可以清晰看到:GET WeatherForecast
介面調用了 CallWeatherForecast
, CallWeatherForecast
又調用了 GetWeatherInfoFromWebservice
與 FormatWeatherInfo
。以及這些方法與整個 http
請求的耗時。可以說是非常非常直觀。
點擊 CallWeatherForecast
這一行,還可以看到我們設置的 tag
的內容。
總結
以上我們可以看到如果你想對 http
介面以及 database
操作進行追蹤,只需要簡單的幾行代碼就可以完成而且全程無侵入。如果你想對程式進行更細緻的追蹤還可以使用自定義的 Activity
進行擴展,整個過程也毫無難度。希望這篇內容對想要學習 .NET
程式可觀測的同學有所幫助。
關註我的公眾號一起玩轉技術
QQ群:1022985150 VX:kklldog 一起探討學習.NET技術
作者:Agile.Zhou(kklldog)
出處:http://www.cnblogs.com/kklldog/
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。