// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics.Contracts; using System.Globalization; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using System.Web.Http.Controllers; using System.Web.Http.Filters; using System.Web.Http.Properties; using System.Web.Http.Services; namespace System.Web.Http.Tracing.Tracers { /// /// Tracer for . /// internal class HttpActionDescriptorTracer : HttpActionDescriptor, IDecorator { private const string ExecuteMethodName = "ExecuteAsync"; private readonly HttpActionDescriptor _innerDescriptor; private readonly ITraceWriter _traceWriter; public HttpActionDescriptorTracer(HttpControllerContext controllerContext, HttpActionDescriptor innerDescriptor, ITraceWriter traceWriter) : base(controllerContext.ControllerDescriptor) { Contract.Assert(innerDescriptor != null); Contract.Assert(traceWriter != null); _innerDescriptor = innerDescriptor; _traceWriter = traceWriter; } public HttpActionDescriptor Inner { get { return _innerDescriptor; } } public override ConcurrentDictionary Properties { get { return _innerDescriptor.Properties; } } public override HttpActionBinding ActionBinding { get { return _innerDescriptor.ActionBinding; } set { _innerDescriptor.ActionBinding = value; } } public override Collection SupportedHttpMethods { get { return _innerDescriptor.SupportedHttpMethods; } } public override string ActionName { get { return _innerDescriptor.ActionName; } } public override IActionResultConverter ResultConverter { get { return _innerDescriptor.ResultConverter; } } public override Type ReturnType { get { return _innerDescriptor.ReturnType; } } public override Task ExecuteAsync(HttpControllerContext controllerContext, IDictionary arguments, CancellationToken cancellationToken) { return _traceWriter.TraceBeginEndAsync( controllerContext.Request, TraceCategories.ActionCategory, TraceLevel.Info, _innerDescriptor.GetType().Name, ExecuteMethodName, beginTrace: (tr) => { tr.Message = Error.Format(SRResources.TraceInvokingAction, FormattingUtilities.ActionInvokeToString(ActionName, arguments)); }, execute: () => { return _innerDescriptor.ExecuteAsync(controllerContext, arguments, cancellationToken); }, endTrace: (tr, value) => { tr.Message = Error.Format(SRResources.TraceActionReturnValue, FormattingUtilities.ValueToString(value, CultureInfo.CurrentCulture)); }, errorTrace: null); } public override Collection GetCustomAttributes() { return _innerDescriptor.GetCustomAttributes(); } public override Collection GetCustomAttributes(bool inherit) { return _innerDescriptor.GetCustomAttributes(inherit); } public override Collection GetFilters() { List filters = new List(_innerDescriptor.GetFilters()); List returnFilters = new List(filters.Count); for (int i = 0; i < filters.Count; i++) { if (FilterTracer.IsFilterTracer(filters[i])) { returnFilters.Add(filters[i]); } else { IEnumerable filterTracers = FilterTracer.CreateFilterTracers(filters[i], _traceWriter); foreach (IFilter filterTracer in filterTracers) { returnFilters.Add(filterTracer); } } } return new Collection(returnFilters); } public override Collection GetFilterPipeline() { List filters = new List(_innerDescriptor.GetFilterPipeline()); List returnFilters = new List(filters.Count); for (int i = 0; i < filters.Count; i++) { // If this filter has been wrapped already, use as is if (FilterTracer.IsFilterTracer(filters[i].Instance)) { returnFilters.Add(filters[i]); } else { IEnumerable filterTracers = FilterTracer.CreateFilterTracers(filters[i], _traceWriter); foreach (FilterInfo filterTracer in filterTracers) { returnFilters.Add(filterTracer); } } } return new Collection(returnFilters); } public override Collection GetParameters() { return _innerDescriptor.GetParameters(); } } }