Application Insights SDK におけるフィルター処理および前処理
テレメトリが SDK から送信される前に、テレメトリをフィルター処理、変更、またはエンリッチするコードを記述できます。 この処理では、HTTP 要求のコレクションや依存関係のコレクションなど、標準的なテレメトリ モジュールから送信されるデータも対象となります。
ITelemetryProcessor
を実装すると、テレメトリが SDK から送信される前にフィルタリングによってテレメトリを変更または破棄することができます。 たとえば、ロボットからの要求を除外することでテレメトリの量を削減することができます。 サンプリングとは異なり、送信または破棄される内容は完全に制御できますが、集計されたログに基づくメトリックに影響します。 項目を破棄する方法によっては、関連する項目間を移動できなくなる可能性もあります。ITelemetryInitializer
を実装して、アプリから送信されたテレメトリのプロパティを追加または変更します。 たとえば、算出値や、ポータルでデータをフィルター処理するのに使用できるバージョン番号を追加することが可能です。サンプリング は、統計に影響を与えることなくテレメトリの量を削減します。 関連するデータ ポイントが一緒に保持されるので、問題の診断時にそれらのデータ ポイント間を移動することができます。 ポータルでは、サンプリングを補正するために合計数が乗算されます。
Note
SDK API は、カスタム イベントとメトリックの送信に使用します。
前提条件
アプリケーションに適した SDK をアプリにインストールします (ASP.NET、ASP.NET Core、Non-HTTP/Worker for .NET/.NET Core、または JavaScript)。
Filtering
この手法では、テレメトリ ストリームに含める内容またはテレメトリ ストリームから除外する内容を直接制御できます。 フィルター処理を使用すると、Application Insights への送信対象からテレメトリ項目を除外することができます。 フィルター処理は、サンプリングと共に使用することも、個別に使用することもできます。
テレメトリのフィルター処理を行うには、テレメトリ プロセッサを記述し、それを TelemetryConfiguration
に登録します。 すべてのテレメトリはプロセッサを経由します。 ストリームから削除するか、チェーン内の次のプロセッサに渡すかを選択できます。 HTTP 要求コレクターや依存関係コレクターなどの標準的なモジュールのテレメトリに加えて、自身で追跡したテレメトリも含まれます。 たとえば、ロボットからの要求や成功した依存関係の呼び出しについてのテレメトリをフィルターで除外できます。
警告
プロセッサを使用して SDK から送信されるテレメトリをフィルター処理すると、ポータルに表示される統計にゆがみが生じ、関連項目を追跡するのが困難になる可能性があります。
代わりに、 サンプリングの使用を検討します。
.NET アプリケーション
ITelemetryProcessor
を実装します。テレメトリ プロセッサによって処理のチェーンが構築されます。 テレメトリ プロセッサをインスタンス化するときは、チェーン内の次のプロセッサへの参照が与えられます。 テレメトリ データ ポイントが process メソッドに渡されると、作業が実行され、そのチェーンの次のテレメトリ プロセッサが呼び出されます (呼び出されないこともあります)。
using Microsoft.ApplicationInsights.Channel; using Microsoft.ApplicationInsights.Extensibility; using Microsoft.ApplicationInsights.DataContracts; public class SuccessfulDependencyFilter : ITelemetryProcessor { private ITelemetryProcessor Next { get; set; } // next will point to the next TelemetryProcessor in the chain. public SuccessfulDependencyFilter(ITelemetryProcessor next) { this.Next = next; } public void Process(ITelemetry item) { // To filter out an item, return without calling the next processor. if (!OKtoSend(item)) { return; } this.Next.Process(item); } // Example: replace with your own criteria. private bool OKtoSend (ITelemetry item) { var dependency = item as DependencyTelemetry; if (dependency == null) return true; return dependency.Success != true; } }
プロセッサを追加します。
次のスニペットを ApplicationInsights.config に挿入します。
<TelemetryProcessors> <Add Type="WebApplication9.SuccessfulDependencyFilter, WebApplication9"> <!-- Set public property --> <MyParamFromConfigFile>2-beta</MyParamFromConfigFile> </Add> </TelemetryProcessors>
名前付きのパブリック プロパティをクラス内に指定することにより、.config ファイルから文字列値を渡すことができます。
警告
.config ファイル内の型名とプロパティ名をコード内のクラスおよびプロパティ名と慎重に照合してください。 存在しない型またはプロパティが .config ファイルによって参照されていると、SDK は何も通知せずにテレメトリの送信に失敗する場合があります。
または、コード内でフィルターを初期化することもできます。 適切な初期化クラス (たとえば
Global.asax.cs
の AppStart) で、プロセッサをチェーンに挿入します。var builder = TelemetryConfiguration.Active.DefaultTelemetrySink.TelemetryProcessorChainBuilder; builder.Use((next) => new SuccessfulDependencyFilter(next)); // If you have more processors: builder.Use((next) => new AnotherProcessor(next)); builder.Build();
この時点より後に作成されたテレメトリ クライアントではプロセッサが使用されます。
フィルターの例
人工的な要求
ボットと Web テストを除外します。 メトリックス エクスプローラーで人工的なソースを除外することもできますが、SDK 自体でそのソースをフィルター処理することにより、トラフィックとインジェストのサイズが削減されます。
public void Process(ITelemetry item)
{
if (!string.IsNullOrEmpty(item.Context.Operation.SyntheticSource)) {return;}
// Send everything else:
this.Next.Process(item);
}
失敗した認証
"401" 応答が返された要求を除外します。
public void Process(ITelemetry item)
{
var request = item as RequestTelemetry;
if (request != null &&
request.ResponseCode.Equals("401", StringComparison.OrdinalIgnoreCase))
{
// To filter out an item, return without calling the next processor.
return;
}
// Send everything else
this.Next.Process(item);
}
リモートの依存関係の高速呼び出しを除外する
低速な呼び出しのみの診断を実行する場合は、高速呼び出しを除外します。
Note
このフィルター処理によって、ポータルに表示される統計にゆがみが生じます。
public void Process(ITelemetry item)
{
var request = item as DependencyTelemetry;
if (request != null && request.Duration.TotalMilliseconds < 100)
{
return;
}
this.Next.Process(item);
}
依存関係の問題の診断
このブログ では、依存関係に対して定期的な ping を自動送信することによって依存関係の問題を診断するプロジェクトについて説明します。
Java アプリケーション
Java でのテレメトリ プロセッサとその実装の詳細については、Java テレメトリ プロセッサのドキュメントを参照してください。
JavaScript Web アプリケーション
ITelemetryInitializer を使用して、JavaScript Web アプリケーションからのテレメトリをフィルター処理できます。
テレメトリ初期化子のコールバック関数を作成します。 コールバック関数は、パラメーターとして
ITelemetryItem
を受け取ります。これは、処理されるイベントを指します。 このコールバックからfalse
が返されると、テレメトリ項目がフィルターで除外されます。var filteringFunction = (envelope) => { if (envelope.data.someField === 'tobefilteredout') { return false; } return true; };
次のテレメトリ初期化子のコールバックを追加します。
appInsights.addTelemetryInitializer(filteringFunction);
プロパティの追加/変更:ITelemetryInitializer
テレメトリを追加情報でエンリッチしたり、標準のテレメトリ モジュールによって設定されたテレメトリのプロパティをオーバーライドしたりするには、テレメトリ初期化子を使用します。
たとえば、Web 向けの Application Insights パッケージでは HTTP 要求に関するテレメトリが収集されます。 既定では、応答コードが >=400 の要求はすべて失敗としてフラグが設定されます。 これに対して 400 を成功として処理する場合は、"success" プロパティを設定するテレメトリ初期化子を指定できます。
テレメトリ初期化子を指定すると、Track*() メソッドのいずれかが呼び出されるたびに、テレメトリ初期化子も呼び出されます。 この初期化子には、標準のテレメトリ モジュールによって呼び出される Track()
メソッドも含まれます。 規約により、これらのモジュールでは、初期化子によって既に設定されているプロパティは設定されません。 テレメトリ初期化子は、テレメトリ プロセッサを呼び出す前に呼び出されるため、初期化子によって実行されるエンリッチメントはプロセッサに表示されます。
.NET アプリケーション
初期化子を定義する
using System; using Microsoft.ApplicationInsights.Channel; using Microsoft.ApplicationInsights.DataContracts; using Microsoft.ApplicationInsights.Extensibility; namespace MvcWebRole.Telemetry { /* * Custom TelemetryInitializer that overrides the default SDK * behavior of treating response codes >= 400 as failed requests * */ public class MyTelemetryInitializer : ITelemetryInitializer { public void Initialize(ITelemetry telemetry) { var requestTelemetry = telemetry as RequestTelemetry; // Is this a TrackRequest() ? if (requestTelemetry == null) return; int code; bool parsed = Int32.TryParse(requestTelemetry.ResponseCode, out code); if (!parsed) return; if (code >= 400 && code < 500) { // If we set the Success property, the SDK won't change it: requestTelemetry.Success = true; // Allow us to filter these requests in the portal: requestTelemetry.Properties["Overridden400s"] = "true"; } // else leave the SDK to set the Success property } } }
初期化子を読み込む
ApplicationInsights.config で:
<ApplicationInsights> <TelemetryInitializers> <!-- Fully qualified type name, assembly name: --> <Add Type="MvcWebRole.Telemetry.MyTelemetryInitializer, MvcWebRole"/> ... </TelemetryInitializers> </ApplicationInsights>
または、Global.aspx.cs などのコード内で初期化子をインスタンス化することもできます。
protected void Application_Start() { // ... TelemetryConfiguration.Active.TelemetryInitializers.Add(new MyTelemetryInitializer()); }
詳細については、このサンプルを参照してください。
JavaScript テレメトリ初期化子
必要に応じて、JavaScript テレメトリ初期化子を挿入します。 Application Insights JavaScript SDK のテレメトリ初期化子の詳細については、「テレメトリ初期化子」を参照してください。
JavaScript (Web) SDK ローダー スクリプト構成に onInit コールバック関数を追加して、テレメトリ初期化子を挿入します。
<script type="text/javascript">
!(function (cfg){function e(){cfg.onInit&&cfg.onInit(n)}var x,w,D,t,E,n,C=window,O=document,b=C.location,q="script",I="ingestionendpoint",L="disableExceptionTracking",j="ai.device.";"instrumentationKey"[x="toLowerCase"](),w="crossOrigin",D="POST",t="appInsightsSDK",E=cfg.name||"appInsights",(cfg.name||C[t])&&(C[t]=E),n=C[E]||function(g){var f=!1,m=!1,h={initialize:!0,queue:[],sv:"8",version:2,config:g};function v(e,t){var n={},i="Browser";function a(e){e=""+e;return 1===e.length?"0"+e:e}return n[j+"id"]=i[x](),n[j+"type"]=i,n["ai.operation.name"]=b&&b.pathname||"_unknown_",n["ai.internal.sdkVersion"]="javascript:snippet_"+(h.sv||h.version),{time:(i=new Date).getUTCFullYear()+"-"+a(1+i.getUTCMonth())+"-"+a(i.getUTCDate())+"T"+a(i.getUTCHours())+":"+a(i.getUTCMinutes())+":"+a(i.getUTCSeconds())+"."+(i.getUTCMilliseconds()/1e3).toFixed(3).slice(2,5)+"Z",iKey:e,name:"Microsoft.ApplicationInsights."+e.replace(/-/g,"")+"."+t,sampleRate:100,tags:n,data:{baseData:{ver:2}},ver:undefined,seq:"1",aiDataContract:undefined}}var n,i,t,a,y=-1,T=0,S=["js.monitor.azure.com","js.cdn.applicationinsights.io","js.cdn.monitor.azure.com","js0.cdn.applicationinsights.io","js0.cdn.monitor.azure.com","js2.cdn.applicationinsights.io","js2.cdn.monitor.azure.com","az416426.vo.msecnd.net"],o=g.url||cfg.src,r=function(){return s(o,null)};function s(d,t){if((n=navigator)&&(~(n=(n.userAgent||"").toLowerCase()).indexOf("msie")||~n.indexOf("trident/"))&&~d.indexOf("ai.3")&&(d=d.replace(/(\/)(ai\.3\.)([^\d]*)$/,function(e,t,n){return t+"ai.2"+n})),!1!==cfg.cr)for(var e=0;e<S.length;e++)if(0<d.indexOf(S[e])){y=e;break}var n,i=function(e){var a,t,n,i,o,r,s,c,u,l;h.queue=[],m||(0<=y&&T+1<S.length?(a=(y+T+1)%S.length,p(d.replace(/^(.*\/\/)([\w\.]*)(\/.*)$/,function(e,t,n,i){return t+S[a]+i})),T+=1):(f=m=!0,s=d,!0!==cfg.dle&&(c=(t=function(){var e,t={},n=g.connectionString;if(n)for(var i=n.split(";"),a=0;a<i.length;a++){var o=i[a].split("=");2===o.length&&(t[o[0][x]()]=o[1])}return t[I]||(e=(n=t.endpointsuffix)?t.location:null,t[I]="https://"+(e?e+".":"")+"dc."+(n||"services.visualstudio.com")),t}()).instrumentationkey||g.instrumentationKey||"",t=(t=(t=t[I])&&"/"===t.slice(-1)?t.slice(0,-1):t)?t+"/v2/track":g.endpointUrl,t=g.userOverrideEndpointUrl||t,(n=[]).push((i="SDK LOAD Failure: Failed to load Application Insights SDK script (See stack for details)",o=s,u=t,(l=(r=v(c,"Exception")).data).baseType="ExceptionData",l.baseData.exceptions=[{typeName:"SDKLoadFailed",message:i.replace(/\./g,"-"),hasFullStack:!1,stack:i+"\nSnippet failed to load ["+o+"] -- Telemetry is disabled\nHelp Link: https://go.microsoft.com/fwlink/?linkid=2128109\nHost: "+(b&&b.pathname||"_unknown_")+"\nEndpoint: "+u,parsedStack:[]}],r)),n.push((l=s,i=t,(u=(o=v(c,"Message")).data).baseType="MessageData",(r=u.baseData).message='AI (Internal): 99 message:"'+("SDK LOAD Failure: Failed to load Application Insights SDK script (See stack for details) ("+l+")").replace(/\"/g,"")+'"',r.properties={endpoint:i},o)),s=n,c=t,JSON&&((u=C.fetch)&&!cfg.useXhr?u(c,{method:D,body:JSON.stringify(s),mode:"cors"}):XMLHttpRequest&&((l=new XMLHttpRequest).open(D,c),l.setRequestHeader("Content-type","application/json"),l.send(JSON.stringify(s)))))))},a=function(e,t){m||setTimeout(function(){!t&&h.core||i()},500),f=!1},p=function(e){var n=O.createElement(q),e=(n.src=e,t&&(n.integrity=t),n.setAttribute("data-ai-name",E),cfg[w]);return!e&&""!==e||"undefined"==n[w]||(n[w]=e),n.onload=a,n.onerror=i,n.onreadystatechange=function(e,t){"loaded"!==n.readyState&&"complete"!==n.readyState||a(0,t)},cfg.ld&&cfg.ld<0?O.getElementsByTagName("head")[0].appendChild(n):setTimeout(function(){O.getElementsByTagName(q)[0].parentNode.appendChild(n)},cfg.ld||0),n};p(d)}cfg.sri&&(n=o.match(/^((http[s]?:\/\/.*\/)\w+(\.\d+){1,5})\.(([\w]+\.){0,2}js)$/))&&6===n.length?(d="".concat(n[1],".integrity.json"),i="@".concat(n[4]),l=window.fetch,t=function(e){if(!e.ext||!e.ext[i]||!e.ext[i].file)throw Error("Error Loading JSON response");var t=e.ext[i].integrity||null;s(o=n[2]+e.ext[i].file,t)},l&&!cfg.useXhr?l(d,{method:"GET",mode:"cors"}).then(function(e){return e.json()["catch"](function(){return{}})}).then(t)["catch"](r):XMLHttpRequest&&((a=new XMLHttpRequest).open("GET",d),a.onreadystatechange=function(){if(a.readyState===XMLHttpRequest.DONE)if(200===a.status)try{t(JSON.parse(a.responseText))}catch(e){r()}else r()},a.send())):o&&r();try{h.cookie=O.cookie}catch(k){}function e(e){for(;e.length;)!function(t){h[t]=function(){var e=arguments;f||h.queue.push(function(){h[t].apply(h,e)})}}(e.pop())}var c,u,l="track",d="TrackPage",p="TrackEvent",l=(e([l+"Event",l+"PageView",l+"Exception",l+"Trace",l+"DependencyData",l+"Metric",l+"PageViewPerformance","start"+d,"stop"+d,"start"+p,"stop"+p,"addTelemetryInitializer","setAuthenticatedUserContext","clearAuthenticatedUserContext","flush"]),h.SeverityLevel={Verbose:0,Information:1,Warning:2,Error:3,Critical:4},(g.extensionConfig||{}).ApplicationInsightsAnalytics||{});return!0!==g[L]&&!0!==l[L]&&(e(["_"+(c="onerror")]),u=C[c],C[c]=function(e,t,n,i,a){var o=u&&u(e,t,n,i,a);return!0!==o&&h["_"+c]({message:e,url:t,lineNumber:n,columnNumber:i,error:a,evt:C.event}),o},g.autoExceptionInstrumented=!0),h}(cfg.cfg),(C[E]=n).queue&&0===n.queue.length?(n.queue.push(e),n.trackPageView({})):e();})({
src: "https://js.monitor.azure.com/scripts/b/ai.3.gbl.min.js",
crossOrigin: "anonymous", // When supplied this will add the provided value as the cross origin attribute on the script tag
onInit: function (sdk) {
sdk.addTelemetryInitializer(function (envelope) {
envelope.data = envelope.data || {};
envelope.data.someField = 'This item passed through my telemetry initializer';
});
}, // Once the application insights instance has loaded and initialized this method will be called
// sri: false, // Custom optional value to specify whether fetching the snippet from integrity file and do integrity check
cfg: { // Application Insights Configuration
connectionString: "YOUR_CONNECTION_STRING"
}});
</script>
telemetryItem で使用できる非カスタム プロパティの概要については、Application Insights エクスポート データ モデルに関するページを参照してください。
任意の数の初期化子を追加できます。 これらは、追加された順序で呼び出されます。
OpenCensus Python テレメトリ プロセッサ
OpenCensus Python のテレメトリ プロセッサは、エクスポート前にテレメトリを処理するために呼び出される単なるコールバック関数です。 コールバック関数は、パラメーターとして エンベロープ データ型を受け入れる必要があります。 テレメトリをエクスポートから除外するには、コールバック関数が False
を返すようにします。 エンベロープの Azure Monitor データ型のスキーマについては、GitHub を参照してください。
Note
cloud_RoleName
を変更するには、tags
フィールドの ai.cloud.role
属性を変更します。
def callback_function(envelope):
envelope.tags['ai.cloud.role'] = 'new_role_name'
# Example for log exporter
import logging
from opencensus.ext.azure.log_exporter import AzureLogHandler
logger = logging.getLogger(__name__)
# Callback function to append '_hello' to each log message telemetry
def callback_function(envelope):
envelope.data.baseData.message += '_hello'
return True
handler = AzureLogHandler(connection_string='InstrumentationKey=<your-instrumentation_key-here>')
handler.add_telemetry_processor(callback_function)
logger.addHandler(handler)
logger.warning('Hello, World!')
# Example for trace exporter
import requests
from opencensus.ext.azure.trace_exporter import AzureExporter
from opencensus.trace import config_integration
from opencensus.trace.samplers import ProbabilitySampler
from opencensus.trace.tracer import Tracer
config_integration.trace_integrations(['requests'])
# Callback function to add os_type: linux to span properties
def callback_function(envelope):
envelope.data.baseData.properties['os_type'] = 'linux'
return True
exporter = AzureExporter(
connection_string='InstrumentationKey=<your-instrumentation-key-here>'
)
exporter.add_telemetry_processor(callback_function)
tracer = Tracer(exporter=exporter, sampler=ProbabilitySampler(1.0))
with tracer.span(name='parent'):
response = requests.get(url='https://www.wikipedia.org/wiki/Rabbit')
# Example for metrics exporter
import time
from opencensus.ext.azure import metrics_exporter
from opencensus.stats import aggregation as aggregation_module
from opencensus.stats import measure as measure_module
from opencensus.stats import stats as stats_module
from opencensus.stats import view as view_module
from opencensus.tags import tag_map as tag_map_module
stats = stats_module.stats
view_manager = stats.view_manager
stats_recorder = stats.stats_recorder
CARROTS_MEASURE = measure_module.MeasureInt("carrots",
"number of carrots",
"carrots")
CARROTS_VIEW = view_module.View("carrots_view",
"number of carrots",
[],
CARROTS_MEASURE,
aggregation_module.CountAggregation())
# Callback function to only export the metric if value is greater than 0
def callback_function(envelope):
return envelope.data.baseData.metrics[0].value > 0
def main():
# Enable metrics
# Set the interval in seconds in which you want to send metrics
exporter = metrics_exporter.new_metrics_exporter(connection_string='InstrumentationKey=<your-instrumentation-key-here>')
exporter.add_telemetry_processor(callback_function)
view_manager.register_exporter(exporter)
view_manager.register_view(CARROTS_VIEW)
mmap = stats_recorder.new_measurement_map()
tmap = tag_map_module.TagMap()
mmap.measure_int_put(CARROTS_MEASURE, 1000)
mmap.record(tmap)
# Default export interval is every 15.0s
# Your application should run for at least this amount
# of time so the exporter will meet this interval
# Sleep can fulfill this
time.sleep(60)
print("Done recording metrics")
if __name__ == "__main__":
main()
必要な数だけプロセッサを追加できます。 これらは、追加された順序で呼び出されます。 1 つのプロセッサから例外がスローされた場合、次のプロセッサには影響しません。
TelemetryInitializers の例
カスタム プロパティを追加する
次のサンプル初期化子は、追跡されたすべてのテレメトリにカスタム プロパティを追加します。
public void Initialize(ITelemetry item)
{
var itemProperties = item as ISupportProperties;
if(itemProperties != null && !itemProperties.Properties.ContainsKey("customProp"))
{
itemProperties.Properties["customProp"] = "customValue";
}
}
クラウド ロール名を追加する
次のサンプル初期化子によって、追跡されたすべてのテレメトリにクラウド ロール名が設定されます。
public void Initialize(ITelemetry telemetry)
{
if (string.IsNullOrEmpty(telemetry.Context.Cloud.RoleName))
{
telemetry.Context.Cloud.RoleName = "MyCloudRoleName";
}
}
位置情報マッピングに使用されるクライアント IP アドレスを制御する
次のサンプル初期化子は、テレメトリ インジェスト中に、クライアント ソケット IP アドレスの代わりに位置情報マッピングに使用されるクライアント IP を設定します。
public void Initialize(ITelemetry telemetry)
{
var request = telemetry as RequestTelemetry;
if (request == null) return true;
request.Context.Location.Ip = "{client ip address}"; // Could utilize System.Web.HttpContext.Current.Request.UserHostAddress;
return true;
}
ITelemetryProcessor と ITelemetryInitializer
テレメトリ プロセッサとテレメトリ初期化子は何が違うのでしょうか。
- それらを使って実行できることにはいくつかの重複があります。 どちらもテレメトリのプロパティを追加または変更するために使用できますが、その用途には初期化子を使用することをお勧めします。
- テレメトリ初期化子は、常にテレメトリ プロセッサの前に実行されます。
- テレメトリ初期化子は、複数回呼び出すことができます。 規約により、既に設定されているプロパティは設定されません。
- テレメトリ プロセッサを使用すると、テレメトリ項目を完全に置換または破棄できます。
- 登録されているテレメトリ初期化子はすべて、テレメトリ項目ごとに呼び出されます。 テレメトリ プロセッサの場合、SDK で呼び出しが保証されるのは、最初のテレメトリ プロセッサのみです。 その他のプロセッサが呼び出されるかどうかは、先行するテレメトリ プロセッサによって決まります。
- 追加のプロパティでテレメトリをエンリッチしたり、既存のプロパティをオーバーライドしたりするには、テレメトリ初期化子を使用します。 テレメトリ プロセッサを使用してテレメトリを除外します。
Note
JavaScript には、ITelemetryInitializer を使用してイベントをフィルター処理できるテレメトリー初期化子のみがあります。
ApplicationInsights.config のトラブルシューティング
- 完全修飾された型名とアセンブリ名が正しいことを確認します。
- applicationinsights.config ファイルが出力ディレクトリ内に存在し、最近の変更がすべて含まれていることを確認します。