.NET Framework Tracing SDK
Instana でのトレースは自動的に行われますが、カスタムコード、特定のアプリケーション領域、または社内コンポーネントについてさらに詳細な情報を得たい場合は、以下に詳述するように、 Instana の「.NET Tracing SDK」を使用することができます。
SDK のインストール
.NET 用の SDK は、nuget.org の公式フィードで NuGet パッケージとして提供されています。 Instana.ManagedTracing.Sdk を検索して見つけ、プロジェクトに追加します。 インストール後、プロジェクトには 2 つの追加参照 (Instana.ManagedTracing.Sdk および Instana.ManagedTracing.Api) があります。
独自のコードのトレース
以下の例では、分散トレース機能をコードに追加する方法を説明しています。
単純なスパンの作成
メソッド起動をトレースするには、CustomSpan.Create API を使用するのが最も単純な方法です。
public void MyTracedMethod()
{
using(var span = CustomSpan.Create())
{
// your original code here
}
}
このコードは、メソッドとその実行時間を表す中間スパンを作成します。
中間スパンを使用する代わりに、エントリー・スパンまたはエグジット・スパンを作成する場合は、そのための便利な API (それぞれの名前は CustomSpan.CreateEntry および CustomSpan.CreateExit) があります。
単純なスパンの作成と例外のキャプチャー
作成したスパンに、メソッドの本体の処理中に発生したエラーの注釈を付ける場合は、次のような CustomSpan.SetError API を使用することで、手動で行うことがもちろん可能です。
public void MyTracedMethod()
{
using(var span = CustomSpan.Create())
{
try
{
// your original code here
}
catch(Exception e)
{
span.SetError(e);
}
}
}
より単純で推奨される方法は、代わりに CustomSpan.WrapAction または CustomSpan.Wrap<T> API を使用することです。
public void MyTracedMethod()
{
using(var span = CustomSpan.Create())
{
// setting the second argument to "false" will prevent exceptions from being thrown. Instead they will be
// captured in the span and swallowed. Setting it to true will let you handle exceptions yourself.
span.WrapAction(
()=>{
// your original code here
}, true);
}
}
ラップするコード・ブロックが以降の処理に必要なものを返す場合は、代わりに CustomSpan.Wrap<T> API を使用してください。
public void MyTracedMethod()
{
using(var span = CustomSpan.Create())
{
// setting the second argument to "false" will prevent exceptions from being thrown. Instead they will be
// captured in the span and swallowed. Setting it to true will let you handle exceptions yourself.
bool result = span.Wrap<bool>(
()=>{
// your original code here
return myBooleanValue;
}, true);
}
}
ここまで、エントリー、エグジット、中間の各スパンの作成方法を説明しました。 また、 SetError API または WrapAction / Wrap<T> API を使用して例外をキャプチャーする方法についても学習しました。
スパンへのデータの追加
スパン自体は、タイミング、呼び出しスタック、および名前だけで構成されています。 問題はありませんが、大半のシナリオではあまり役に立ちません。 スパンにデータを追加したら、どうなるでしょうか。 スパンには、 Data および Tagsを含めることができます。この場合、 CustomSpan クラスは単純な API を提供します。
public void MyTracedMethod(string userName, string someSuperRelevantData)
{
using(var span = CustomSpan.Create())
{
span.SetData("username", userName);
span.SetData("relevant", someSuperRelevantData);
span.WrapAction(
()=>{
// your original code here
}, true);
}
}
データは、バックエンドに転送され、トレースをダウンロードすることで UI からダウンロードできます。 ただし、ここに記載されたデータは、 Instana のUIには表示されません。
スパンへのタグの追加
SetData を使用する代わりに、ストリングの配列をキーとして使用する SetTag を使用することもできます (スパンに階層として渡すデータを構造化するために使用できます)。
public void MyTracedMethod(string userName, string someSuperRelevantData)
{
using(var span = CustomSpan.Create())
{
span.SetTag("username", userName);
span.SetTag("relevant", someSuperRelevantData);
span.WrapAction(
()=>{
// your original code here
}, true);
}
}
タグは、「呼び出しの詳細」ビューに直接表示され、無制限分析で検索することもできます。
サービスへのカスタム・スパンのマッピング
通常は、Instana のアプリケーション・パースペクティブでカスタム・スパンを論理サービスに関連付けます。 これは、単にストリングを取る SetServicename API を呼び出すだけで簡単に行うことができます。 SDK を使用して実装されたエンドポイントを区別するために、SetEndpointName API を使用して、さらに詳細なマッピングのためにエンドポイントを指定することもできます。
public void MyTracedMethod(string userName, string someSuperRelevantData)
{
using(var span = CustomSpan.Create())
{
span.SetServiceName("AwesomeSDKService");
span.SetEndpointName("TracingEndpoint");
.
.
.
}
}
すべてのスパンにサービスとエンドポイントを設定できますが、重要な点として、これらの設定は、INTERMEDIATE スパンの場合には破棄されることに注意してください (設定は直前の ENTRY から継承されます)。
スパンの結果の取り込み
インスツルメントするメソッドによって値が返されるとします。 この値をトレースで使用すると、トラブルシューティングに役立つと仮定します。 SetResult API を入力してください。
public bool MyTracedMethod(string userName, string someSuperRelevantData)
{
using(var span = CustomSpan.Create())
{
bool result = span.Wrap<bool>(
()=>{
// your original code here
return resultingBoolean;
}, true);
span.SetResult(result.ToString());
return result;
}
}
スパンのネスト
スパンのネストは、あるメソッドを別のメソッドから呼び出すだけで簡単です。 完全を期すために、下記に例を示します。 ここでは、1 つのメソッドがエントリーとして機能し、その子スパンが中間であることを想定しています。
public bool MyTracedEntryMethod(string userName, string someSuperRelevantData)
{
using(var span = CustomSpan.CreateEntry())
{
bool result = span.Wrap<bool>(
()=>{
List<string> data = this.GetSomeDataFromSomewhere();
// do some heavy processing
return theResultICameUpWith;
}, true);
span.SetResult(result.ToString());
return result;
}
}
private List<string> GetSomeDataFromSomewhere()
{
using(var span = CustomSpan.Create())
{
List<string> result = span.WrapAction(
()=>{
// read data from somewhere...
return theListICameUpWith;
}, true);
span.SetResult(result.ToString());
return result;
}
}
上記の結果として、エントリー・スパンが中間スパンを子として持つことになります。 技術的には、ネストに深さの制限はありませんが、深い再帰でスパンを作成しないでください。
分散トレースの distributed の検討
ここまで説明してきたすべてのスパンは、1 つのサービスに制限されていました。 同じようにアクティビティーをトレースする他のコンポーネントに到達することはありません。 サービス境界を超えて真の分散トレースを実現するには、相関を適用する必要があります。
トレースの相関は、エグジット呼び出しで相関データを設定する方法と、このデータを取得して他のコンポーネントのエントリーでこのコンテキストを「継続」する方法を記述します。
そのために、CustomSpan には、CustomSpan.CreateExit メソッドおよび CustomSpan.CreateEntry メソッドの多重定義があります。
CustomSpan.CreateExit は Action<string, string> を引数として使用できますが、 CustomSpan.CreateEntry は Func<DistributedTraceInformation>を使用します。
その仕組みを説明します。
ある Message クラスがあると仮定し、それを呼び出すリモートサービスに渡すものとします。
public class Message
{
public Message()
{
this.Tags = new Dictionary<string, string>();
}
public Dictionary<string, string> Tags { get; private set; }
public int Payload { get; set; }
public void AddTag(string tagName, string tagValue)
{
this.Tags.Add(tagName, tagValue);
}
}
ここで関連する部分は、2 つのストリングを使用する AddTag メソッドです。 これは、CustomSpan.CreateExit に提供する必要があるシグニチャーです。
CreateExit を呼び出すときにこのメソッドを使用すると、Message のインスタンスに相関データが書き込まれます。
public void MyLocalEntryMethod()
{
// this methd will create an entry span and then call our method
// that communicates with the remote-service (and thus create our exit span)
using(var span = CustomSpan.CreateEntry())
{
span.WrapAction(()=>{
CallRemoteService();
})
}
}
public void CallRemoteService()
{
Message message = new Message();
using(var exitSpan = CustomSpan.CreateExit(this, message.AddTag))
{
exitSpan.WrapAction( ()=>{
var service = new RemoteService();
service.ValidateRequest(message);
}
}
}
service.ValidateRequest(message) への呼び出しにより、メッセージがローカル・コンポーネントのスコープを出るときに、タグのリストに相関データが入れられます。 これらが呼び出し先のサイトでどのように抽出されるかを示します。
public void ValidateRequest(Message message)
{
using(var span = CustomSpan.CreateEntry(this, ()=>ExtractCorrelationData(message))
{
// do whatever this method is supposed to do, we only care for extraction
// if the correlation-data anyway :-)
}
}
private DistributedTraceInformation ExtractCorrelationData(Message message)
{
var dti = new DistributedTraceInformation();
dti.ParentSpanId = Convert.ToInt64(message.Tags[TracingConstants.ExternalParentSpanIdHeader], 16);
dti.TraceId = Convert.ToInt64(message.Tags[TracingConstants.ExternalTraceIdHeader], 16);
return dti;
}
ここで関連する部分は、ExtractCorrelationData です。 エグジットの作成時に Message.AddTag メソッドを使用したため、SDK は、サービスへの送信前に、関連する ID (親スパン ID とトレース ID) をメッセージのタグ・リストに書き込みます。
これで、サービスはタグを読み取ることで、これらの値を再び取得できるようになります(これらのタグは、前述のコードサンプルでキーとして使用されている定数から特定できます)。 作成する DistributedTraceInformation のインスタンスは、新しく作成されたエグジットを既存のトレースに付加するために SDK によって使用されます。 作成するエントリー・スパンは、ローカル・サービスのエグジットに対応します。
トレースの分割
自動トレースが多すぎたり、作成されたトレースが長すぎて理解できない場合があります。 例えば、長時間のバックグラウンド・タスクの進行中にコールバックを介してサーバーがクライアントに更新をプッシュするときに、長い二重 WCF 通信でこれが発生する場合があります。 このような通信の結果、長すぎるため UI に表示できない可能性のある長いトレースが生じます。
このような場合は、トレースを複数のトレースに分ける (具体的には、クライアントへの各コールバックを別々のトレースに分ける) オプションがあります。 これを行うには、CustomSpan から CreateEntryForNewTrace を使用して、現在のトレースを停止し、その時点から新しいトレースを作成することができます。 これは、クライアントの呼び出しの直前にサーバー・サイドで行うことができます。
var callbackChanell = OperationContext.Current.GetCallbackChannel<IMathResult>();
if (callbackChanell != null)
{
using (var span = CustomSpan.CreateEntryForNewTrace(this))
{
callbackChanell.SendStatusUpdate(new MathArguments() { InParam = args.InParam, Progress = (float)i / args.InParam, Result = generator.Next(1000, 9999) });
}
}
お分かりになりましたか?
これで、SDK を使用して最初のカスタム・トレースを作成できるでしょう。 ご質問がある場合 (またはご希望がある場合) SDK については、 サポートからお気軽にご連絡ください。 トレースをお楽しみください。