Splunk Observability Cloud 用 .NET Azure 関数をインストルメンテーションする
スパンを Splunk Observability Cloud にエクスポートするために .NET Azure 関数をインストルメンテーションする方法を学習します。プロセス内の関数と分離された関数の両方がサポートされます。
.NET Azure 関数をインストルメンテーションすることで、関数が実行されるたびにスパンを Splunk Observability Cloud に送信できます。分離されたワーカープロセスとプロセス内関数の両方をインストルメンテーションできます。
テレメトリを Splunk Observability Cloud に送信するために、.NET Azure 関数を OpenTelemetry でインストルメンテーションするには、以下のハイレベルな手順に従ってください:
環境変数の定義
関数の設定で必要な環境変数を設定します。
-
関数アプリで関数を選択します。
-
Settings にアクセスし、次に Configuration にアクセスします。
-
New application setting を選択し、以下の設定を追加します:
Name
値
SPLUNK_ACCESS_TOKENSplunk アクセストークン。アクセストークンを取得するには、「Splunk Observability Cloud を使用したユーザー API アクセストークンの取得と管理」を参照してください。
SPLUNK_REALMSplunk Observability Cloud のレルム(
us0など)。Splunk レルムを見つけるには、「Note about realms」を参照してください。 -
その他必要な設定を追加します。
NuGetを使って必要なライブラリを追加する
Visual StudioのNuGetを使って以下のライブラリを追加します:
- 分離されたワーカープロセス機能
-
-
Include prerelease 設定をアクティブ化します。
-
以下のライブラリの最新版をインストールします:
-
- .NET 6インプロセス機能
-
-
Include prerelease 設定をアクティブ化します。
-
以下のライブラリの指定されたバージョンをインストールします:
注: 実行時の依存関係があるため、プロセス内関数のインストルメンテーションでは、指定されたバージョンのみが動作することが保証されています。 -
- .NET 8インプロセス機能
-
-
Include prerelease 設定をアクティブ化します。
-
以下のライブラリの指定されたバージョンをインストールします:
注: 実行時の依存関係があるため、プロセス内関数のインストルメンテーションでは、指定されたバージョンのみが動作することが保証されています。 -
コード内でOpenTelemetryを初期化する
依存関係を追加したら、関数内で OpenTelemetry を初期化します。
- 分離されたワーカープロセス機能
-
Program.csファイルにスタートアップ初期化を追加します:
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using OpenTelemetry; using OpenTelemetry.Exporter; using OpenTelemetry.ResourceDetectors.Azure; using OpenTelemetry.Resources; using OpenTelemetry.Trace; using System.Diagnostics; // Get environment variables from function configuration // You need a valid Splunk Observability Cloud access token and realm var serviceName = Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME") ?? "Unknown"; var accessToken = Environment.GetEnvironmentVariable("SPLUNK_ACCESS_TOKEN")?.Trim(); var realm = Environment.GetEnvironmentVariable("SPLUNK_REALM")?.Trim(); ArgumentNullException.ThrowIfNull(accessToken, "SPLUNK_ACCESS_TOKEN"); ArgumentNullException.ThrowIfNull(realm, "SPLUNK_REALM"); var tp = Sdk.CreateTracerProviderBuilder() // Use Add[instrumentation-name]Instrumentation to instrument missing services // Use Nuget to find different instrumentation libraries .AddHttpClientInstrumentation(opts => { // This filter prevents background (parent-less) http client activity opts.FilterHttpWebRequest = req => Activity.Current?.Parent != null; opts.FilterHttpRequestMessage = req => Activity.Current?.Parent != null; }) // Use AddSource to add your custom DiagnosticSource source names //.AddSource("My.Source.Name") // Creates root spans for function executions .AddSource("Microsoft.Azure.Functions.Worker") .SetSampler(new AlwaysOnSampler()) .ConfigureResource(configure => configure .AddService(serviceName: serviceName, serviceVersion: "1.0.0") // See https://github.com/open-telemetry/opentelemetry-dotnet-contrib/tree/main/src/OpenTelemetry.ResourceDetectors.Azure // for other types of Azure detectors .AddDetector(new AppServiceResourceDetector())) .AddOtlpExporter(opts => { opts.Endpoint = new Uri($"https://ingest.{realm}.signalfx.com/v2/trace/otlp"); opts.Protocol = OtlpExportProtocol.HttpProtobuf; opts.Headers = $"X-SF-TOKEN={accessToken}"; }) .Build(); var host = new HostBuilder() .ConfigureFunctionsWorkerDefaults() .ConfigureServices(services => services.AddSingleton(tp)) .Build(); host.Run();注: 分離されたワーカープロセス関数をインストルメンテーションする場合、起動時の初期化やパラメータを他の関数にカプセル化することができます。 - .NET 6インプロセス機能
-
スタートアップ関数を定義し、その関数でアセンブリを装飾します。スタートアップ関数は、Azure.Functions.Extensions パッケージを使用して有用なメタデータを収集します。
using Microsoft.Azure.Functions.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection; using OpenTelemetry; using OpenTelemetry.Exporter; using OpenTelemetry.Resources; using OpenTelemetry.Trace; using System; using System.Collections.Generic; // Decorate assembly with startup function [assembly: FunctionsStartup(typeof(OtelManualExample.Startup))] namespace OtelManualExample { public class Startup : FunctionsStartup { public override void Configure(IFunctionsHostBuilder builder) { // Get environment variables from function configuration // You need a valid Splunk Observability Cloud access token and realm var serviceName = Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME") ?? "Unknown"; var accessToken = Environment.GetEnvironmentVariable("SPLUNK_ACCESS_TOKEN")?.Trim(); var realm = Environment.GetEnvironmentVariable("SPLUNK_REALM")?.Trim(); ArgumentNullException.ThrowIfNull(accessToken, "SPLUNK_ACCESS_TOKEN"); ArgumentNullException.ThrowIfNull(realm, "SPLUNK_REALM"); var tp = Sdk.CreateTracerProviderBuilder() // Use Add[instrumentation-name]Instrumentation to instrument missing services // Use Nuget to find different instrumentation libraries .AddHttpClientInstrumentation(opts => // This filter prevents background (parent-less) http client activity opts.Filter = req => Activity.Current?.Parent != null) .AddAspNetCoreInstrumentation() // Use AddSource to add your custom DiagnosticSource source names //.AddSource("My.Source.Name") .SetSampler(new AlwaysOnSampler()) // Add resource attributes to all spans .SetResourceBuilder( ResourceBuilder.CreateDefault() .AddService(serviceName: serviceName, serviceVersion: "1.0.0") .AddAttributes(new Dictionary<string, object>() { { "faas.instance", Environment.GetEnvironmentVariable("WEBSITE_INSTANCE_ID") } })) .AddOtlpExporter(opts => { opts.Endpoint = new Uri($"https://ingest.{realm}.signalfx.com/v2/trace/otlp"); opts.Protocol = OtlpExportProtocol.HttpProtobuf; opts.Headers = $"X-SF-TOKEN={accessToken}"; }) .Build(); builder.Services.AddSingleton(tp); } } } - .NET 8インプロセス機能
-
スタートアップ関数を定義し、その関数でアセンブリを装飾します。スタートアップ関数は、Azure.Functions.Extensions パッケージを使用して有用なメタデータを収集します。
using Microsoft.Azure.Functions.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection; using OpenTelemetry; using OpenTelemetry.Exporter; using OpenTelemetry.Resources; using OpenTelemetry.Trace; using System; using System.Collections.Generic; // Decorate assembly with startup function [assembly: FunctionsStartup(typeof(OtelManualExample.Startup))] namespace OtelManualExample { public class Startup : FunctionsStartup { public override void Configure(IFunctionsHostBuilder builder) { // Get environment variables from function configuration // You need a valid Splunk Observability Cloud access token and realm var serviceName = Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME") ?? "Unknown"; var accessToken = Environment.GetEnvironmentVariable("SPLUNK_ACCESS_TOKEN")?.Trim(); var realm = Environment.GetEnvironmentVariable("SPLUNK_REALM")?.Trim(); ArgumentNullException.ThrowIfNull(accessToken, "SPLUNK_ACCESS_TOKEN"); ArgumentNullException.ThrowIfNull(realm, "SPLUNK_REALM"); var tp = Sdk.CreateTracerProviderBuilder() // Use Add[instrumentation-name]Instrumentation to instrument missing services // Use Nuget to find different instrumentation libraries .AddHttpClientInstrumentation(opts => { // This filter prevents background (parent-less) http client activity opts.FilterHttpRequestMessage = req => Activity.Current?.Parent != null; opts.FilterHttpWebRequest = req => Activity.Current?.Parent != null; }) .AddAspNetCoreInstrumentation() // Use AddSource to add your custom DiagnosticSource source names //.AddSource("My.Source.Name") .SetSampler(new AlwaysOnSampler()) // Add resource attributes to all spans .SetResourceBuilder( ResourceBuilder.CreateDefault() .AddService(serviceName: serviceName, serviceVersion: "1.0.0") .AddAzureAppServiceDetector() .AddAttributes(new Dictionary<string, object>() { { "faas.instance", Environment.GetEnvironmentVariable("WEBSITE_INSTANCE_ID") } })) .AddOtlpExporter(opts => { opts.Endpoint = new Uri($"https://ingest.{realm}.signalfx.com/v2/trace/otlp"); opts.Protocol = OtlpExportProtocol.HttpProtobuf; opts.Headers = $"X-SF-TOKEN={accessToken}"; }) .Build(); builder.Services.AddSingleton(tp); } } }
スパンを送信するコードをインストルメンテーションする
次に、OpenTelemetry を使用してコードをインストルメンテーションします。コードをインストルメンテーションする出発点として、以下の例を使ってください。Azure 関数に環境変数を追加する手順については、Microsoft Azure ドキュメント(https://learn.microsoft.com/en-us/azure/azure-functions/functions-how-to-use-azure-function-app-settings)を参照してください。
- 分離されたワーカープロセス機能
-
次の例では、startヘルパー関数とstopヘルパー関数を使ったインストルメンテーションの方法を示しています:
using System.Diagnostics; using System.Net; using Microsoft.Azure.Functions.Worker; using Microsoft.Azure.Functions.Worker.Http; using Microsoft.Extensions.Logging; namespace OtelManualIsolatedExample { public class ExampleFunction { private readonly ILogger _logger; public ExampleFunction(ILoggerFactory loggerFactory) { _logger = loggerFactory.CreateLogger<ExampleFunction>(); } // Define helper functions for manual instrumentation public static ActivitySource ManualInstrumentationSource = new ActivitySource("manualInstrumentation"); public static Activity? StartActivity(HttpRequestData req, FunctionContext fc) { // Retrieve resource attributes var answer = ManualInstrumentationSource.StartActivity(req.Method.ToUpper() + " " + req.Url.AbsolutePath, ActivityKind.Server); answer?.AddTag("http.url", req.Url); answer?.AddTag("faas.invocation_id", fc.InvocationId.ToString()); answer?.AddTag("faas.name", Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME") + "/" + fc.FunctionDefinition.Name); return answer; } public static HttpResponseData FinishActivity(HttpResponseData response, Activity? activity) { activity?.AddTag("http.status_code", ((int)response.StatusCode)); return response; } [Function("ExampleFunction")] // Add the FunctionContext parameter to capture per-invocation information public HttpResponseData Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req, FunctionContext fc) { using (var activity = StartActivity(req, fc)) { var response = req.CreateResponse(HttpStatusCode.OK); response.Headers.Add("Content-Type", "text/plain; charset=utf-8"); response.WriteString("The current time is " + DateTime.Now.ToLongTimeString()); return FinishActivity(response, activity); } } } } - インプロセス機能
-
次の例は、
faas属性を取得する方法を示しています:public static class ExampleFunction { [FunctionName("ExampleFunction")] // Add the ExecutionContext parameter to capture per-invocation information public static async Task<IActionResult> Run( [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req, ILogger log, ExecutionContext context) { // You can also factor this out into a helper method to use across all functions Activity.Current.AddTag("faas.invocation_id", context.InvocationId.ToString()); Activity.Current.AddTag("faas.name", Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME") + "/" + context.FunctionName); string responseMessage = "The current time is " + DateTime.Now.ToLongTimeString(); return new OkObjectResult(responseMessage); } }
データが入力されていることを確認する
関数を実行し、そのスパンを Splunk APM で検索します。詳細については、「トレース内でのスパンの表示およびフィルタリング」を参照してください。
- 分離されたワーカープロセス機能
-
次の画像は、分離されたワーカープロセス関数によって送信されたスパンを示しています。
faasタグに注意してください。 - インプロセス機能
-
次の画像は、インプロセス関数から送信されたスパンを示しています。
faasタグに注意してください。
トラブルシューティング
__ ___ ___ _ ______ _____________ _____ ________ ___ ___ ___ ____ __ ___ ____ ____ __ ______ _____________ ______ ___ ___ ___ ____ __ ___ _________ _____
_________ __ ______ _____________ _____ _________
-
______ _ ____ __ ___ ______ _______ _______
-
_______ ______ ________
_________ __ ___________ _________ ___ ____ _____ _____
-
___ _ ________ ___ ___ _______ _______ _________ _______ __ ______ ________
-
____ ___ ______ ______________ ____ _____ _____ _______ __ ___________ ____ __________ _________ ___ ______ _________ __________ __ _____ ___ ____ _______