前回のGoに引き続き、今度は.NETでのzero-code計装を試してみる。
Debian GNU/Linux上に.NET 9環境をセットアップした。
.NETは完全初見なのだけど、Webサーバーは簡単に作れるらしいとのことで作成。
mkdir dotnet-web cd dotnet-web dotnet new web
これで各種ファイルが用意される。Program.cs
にすでにWebサーバーのコードが入っている。
var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); app.MapGet("/", () => "Hello World!"); app.Run();
超簡単だな…! エラーを出すパターンをCoPilot受けながら適当に補完してみる。
var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); app.MapGet("/", () => "Hello World!"); app.MapGet("/error", (HttpContext context) => throw new Exception("Error!")); app.MapGet("/500", () => { return Results.Problem("Error!", statusCode: 500); }); app.Run();
dotnet build
でbin/Debug/net9.0/dotnet-web
ができた。
bin/Debug/net9.0/dotnet-web
を実行し、curlなどでhttp://localhost:5000
、http://localhost:5000/error
、http://localhost:5000/500
にアクセスして結果を確認。
$ curl http://localhost:5000 Hello World! $ curl http://localhost:5000/error (何も出ないけどサーバー側は例外が出ている) $ curl http://localhost:5000/500 {"type":"https://tools.ietf.org/html/rfc9110#section-15.6.1","title":"An error occurred while processing your request.","status":500,"detail":"Error!"}
アプリケーションができたので、.NET zero-code instrumentationに従ってzero-code計装をやってみる。
例のごとくOpenTelemetry Collectorをローカルで適当な設定で動かす。
receivers: otlp: protocols: http: exporters: debug: verbosity: detailed service: pipelines: traces: receivers: [otlp] exporters: [debug]
.NETのzero-code計装ライブラリのセットアップ。
curl -sSfL https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/latest/download/otel-dotnet-auto-install.sh -O sh ./otel-dotnet-auto-install.sh chmod +x $HOME/.otel-dotnet-auto/instrument.sh
現在のシェルに環境変数を取り込む。これで、今のシェル内で.NETアプリケーションを動かすとzero-code計装が有効な状態になっている。
. $HOME/.otel-dotnet-auto/instrument.sh
サービス名やバージョンを指定してアプリケーションを実行。
OTEL_SERVICE_NAME=dotnet-zerocode OTEL_RESOURCE_ATTRIBUTES=service.version=1.0.0 bin/Debug/net9.0/dotnet-web
これらはLinuxの例だけど、ドキュメントにはWindowsアプリケーション・Windowsサービス・ASP.NETでのやり方も書いてある。
ではcurlからアクセスして、Collectorにトレースが送られてくるのを見る。
2025-01-19T09:08:36.212+0900 info Traces {"kind": "exporter", "data_type": "traces", "name": "debug", "resource spans": 1, "spans": 2} 2025-01-19T09:08:36.212+0900 info ResourceSpans #0 Resource SchemaURL: Resource attributes: -> os.type: Str(linux) -> os.description: Str(Debian GNU/Linux 12 (bookworm)) -> os.build_id: Str(6.1.0-30-amd64) -> os.name: Str(Debian GNU/Linux) -> os.version: Str(12) -> host.name: Str(...) -> host.id: Str(86fdb17091114232ba104aea4bc5250d) -> process.owner: Str(kmuto) -> process.pid: Int(175395) -> process.runtime.description: Str(.NET 9.0.1) -> process.runtime.name: Str(.NET) -> process.runtime.version: Str(9.0.1) -> container.id: Str(8dcad6c724a5) -> telemetry.distro.name: Str(opentelemetry-dotnet-instrumentation) -> telemetry.distro.version: Str(1.9.0) -> telemetry.sdk.name: Str(opentelemetry) -> telemetry.sdk.language: Str(dotnet) -> telemetry.sdk.version: Str(1.9.0) -> service.name: Str(dotnet-zerocode) -> service.version: Str(1.0.0) ScopeSpans #0 ScopeSpans SchemaURL: InstrumentationScope Microsoft.AspNetCore Span #0 Trace ID : d28fbe5ac778ff1c7bc29adf76ad2d63 Parent ID : ID : 622c1673e23ecb0e Name : GET / Kind : Server Start time : 2025-01-19 00:08:32.1050609 +0000 UTC End time : 2025-01-19 00:08:32.1881126 +0000 UTC Status code : Unset Status message : Attributes: -> server.address: Str(localhost) -> server.port: Int(5000) -> http.request.method: Str(GET) -> url.scheme: Str(http) -> url.path: Str(/) -> network.protocol.version: Str(1.1) -> user_agent.original: Str(curl/7.88.1) -> http.route: Str(/) -> http.response.status_code: Int(200) Span #1 Trace ID : c64908bdcf00dc51d84bb946a55a6e30 Parent ID : ID : d999c17988b97b14 Name : GET /error Kind : Server Start time : 2025-01-19 00:08:34.8026373 +0000 UTC End time : 2025-01-19 00:08:34.8154717 +0000 UTC Status code : Error Status message : Attributes: -> server.address: Str(localhost) -> server.port: Int(5000) -> http.request.method: Str(GET) -> url.scheme: Str(http) -> url.path: Str(/error) -> network.protocol.version: Str(1.1) -> user_agent.original: Str(curl/7.88.1) -> error.type: Str(System.Exception) -> http.route: Str(/error) -> http.response.status_code: Int(500) {"kind": "exporter", "data_type": "traces", "name": "debug"} 2025-01-19T09:08:41.233+0900 info Traces {"kind": "exporter", "data_type": "traces", "name": "debug", "resource spans": 1, "spans": 1} 2025-01-19T09:08:41.233+0900 info ResourceSpans #0 Resource SchemaURL: Resource attributes: -> os.type: Str(linux) -> os.description: Str(Debian GNU/Linux 12 (bookworm)) -> os.build_id: Str(6.1.0-30-amd64) -> os.name: Str(Debian GNU/Linux) -> os.version: Str(12) -> host.name: Str(...) -> host.id: Str(86fdb17091114232ba104aea4bc5250d) -> process.owner: Str(kmuto) -> process.pid: Int(175395) -> process.runtime.description: Str(.NET 9.0.1) -> process.runtime.name: Str(.NET) -> process.runtime.version: Str(9.0.1) -> container.id: Str(8dcad6c724a5) -> telemetry.distro.name: Str(opentelemetry-dotnet-instrumentation) -> telemetry.distro.version: Str(1.9.0) -> telemetry.sdk.name: Str(opentelemetry) -> telemetry.sdk.language: Str(dotnet) -> telemetry.sdk.version: Str(1.9.0) -> service.name: Str(dotnet-zerocode) -> service.version: Str(1.0.0) ScopeSpans #0 ScopeSpans SchemaURL: InstrumentationScope Microsoft.AspNetCore Span #0 Trace ID : bd5555309bda6d0a5c055169ee9f5749 Parent ID : ID : 8f2b25897bd33a16 Name : GET /500 Kind : Server Start time : 2025-01-19 00:08:37.6004667 +0000 UTC End time : 2025-01-19 00:08:37.6297872 +0000 UTC Status code : Error Status message : Attributes: -> server.address: Str(localhost) -> server.port: Int(5000) -> http.request.method: Str(GET) -> url.scheme: Str(http) -> url.path: Str(/500) -> network.protocol.version: Str(1.1) -> user_agent.original: Str(curl/7.88.1) -> http.route: Str(/500) -> http.response.status_code: Int(500) {"kind": "exporter", "data_type": "traces", "name": "debug"}
なるほど、リソース情報もまるっと入ってきている。