Microsoft Azure の仮想マシンで動かしている WCF サービスでも、テレメトリを Azure Application Insights に送りたくて、方法を調べたら次のがヒットした。
ただ、こいつは正式リリースされてないし、そもそも開発終わってだいぶ経過してる。WCF のオペレーションのテレメトリを送信するだけなら、自分で書いた方が早い。全てのオペレーションに対応できるよう、サービスビヘイビアを書いてみた。
using System; using System.Collections.ObjectModel; using System.Linq; using System.ServiceModel; using System.ServiceModel.Channels; using System.ServiceModel.Description; using System.ServiceModel.Dispatcher; using Microsoft.ApplicationInsights; using Microsoft.ApplicationInsights.DataContracts; using Microsoft.ApplicationInsights.Extensibility; namespace WcfAppInsightsSample { internal class Program { static void Main(string[] args) { var host = new ServiceHost(typeof(GreetingService)); host.AddServiceEndpoint( typeof(IGreetingService), new NetTcpBinding(), $"net.tcp://localhost:808/{nameof(IGreetingService)}"); host.Description.Behaviors.Add(new TelemetryBehavior()); host.Open(); Console.WriteLine("Enter で終了"); Console.ReadLine(); host.Close(); } } [ServiceContract] public interface IGreetingService { [OperationContract] string Hello(string name); } public class GreetingService : IGreetingService { public string Hello(string name) { return $"Hello, {name}"; } } sealed class TelemetryBehavior : IServiceBehavior, IOperationBehavior { readonly TelemetryClient _telemetryClient; public TelemetryBehavior() { _telemetryClient = new TelemetryClient(TelemetryConfiguration.Active); } void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { var operations = serviceDescription.Endpoints.SelectMany(x => x.Contract.Operations); foreach (var o in operations) { if (o.OperationBehaviors.Contains(typeof(TelemetryBehavior)) == false) { o.OperationBehaviors.Add(this); } } } void IOperationBehavior.ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation) { if ((dispatchOperation.Invoker is TelemetryOperationInvoker) == false) { dispatchOperation.Invoker = new TelemetryOperationInvoker( inner: dispatchOperation.Invoker, operationDescription: operationDescription, telemetryClient: _telemetryClient); } } void IServiceBehavior.AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) { } void IOperationBehavior.AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters) { } void IOperationBehavior.ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation) { } void IServiceBehavior.Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { } void IOperationBehavior.Validate(OperationDescription operationDescription) { } sealed class TelemetryOperationInvoker : IOperationInvoker { readonly IOperationInvoker _inner; readonly OperationDescription _operationDescription; readonly TelemetryClient _telemetryClient; public TelemetryOperationInvoker( IOperationInvoker inner, OperationDescription operationDescription, TelemetryClient telemetryClient) { _inner = inner; _operationDescription = operationDescription; _telemetryClient = telemetryClient; } public bool IsSynchronous => _inner.IsSynchronous; public object[] AllocateInputs() => _inner.AllocateInputs(); // 非同期は使っていないので手抜き実装 public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state) => _inner.InvokeBegin(instance, inputs, callback, state); public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result) => _inner.InvokeEnd(instance, out outputs, result); // StartOperation と StopOperation を使って、 // Application Insights にテレメトリを送信できる。 public object Invoke(object instance, object[] inputs, out object[] outputs) { var requestTelemetry = new RequestTelemetry { Name = $"{_operationDescription.DeclaringContract.Name}/{_operationDescription.Name}", }; var operation = _telemetryClient.StartOperation(requestTelemetry); try { var result = _inner.Invoke(instance, inputs, out outputs); requestTelemetry.Success = true; requestTelemetry.ResponseCode = "200"; return result; } catch (Exception ex) { requestTelemetry.Success = false; _telemetryClient.TrackException(ex); throw; } finally { _telemetryClient.StopOperation(operation); } } } } }
あとは環境変数 APPLICATIONINSIGHTS_CONNECTION_STRING に接続文字列を設定したら、Application Insights にテレメトリを送信できるようになった。