Is it possible to add values to the customDimensions property of records in requests in ApplicationInsights? How?
I've found that logger.BeginScope can add them to traces -- the value must be Dictionary<string, object> (Passing Dictionary<string, string> does not work.)
Adding the properties to Activity.Current.AddTag has no effect.
Nor does adding properties to TelemetryClient.Properties.
(This is deprecated but nobody seems to know what to do instead.)
The answer is to use a custom ITelemetryInitializer like this
internal class TelemetryInitializer : ITelemetryInitializer
{
private static readonly string[] _Headers = new string[] { "Referer" /*sic */, "X-Forwarded-For", };
private readonly IHttpContextAccessor _httpContextAccessor;
public TelemetryInitializer(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public void Initialize(ITelemetry item)
{
ISupportProperties tcp = item as ISupportProperties;
if (item == null || _httpContextAccessor.HttpContext == null) { return; }
LogTags tags = new LogTags();
tags.AddTag("BuildName", BuildNameAttribute.GetBuildName());
tags.AddTag("Method", _httpContextAccessor.HttpContext.Request.Method);
tags.AddTag("Host", _httpContextAccessor.HttpContext.Request.Host.Value);
// RequestPath is added automaticallt.
tags.AddTags(_httpContextAccessor.HttpContext.Request.Headers.Where(z => _Headers.Contains(z.Key)).SelectMany(z => z.Value.Select(y => new KeyValuePair<string, string>(z.Key, y))));
tags.AddTags(_httpContextAccessor.HttpContext.Request.Query.SelectMany(z => z.Value.Select(y => new KeyValuePair<string, string>(z.Key, y))));
if (_httpContextAccessor.HttpContext.Request.HasFormContentType && _httpContextAccessor.HttpContext.Request.Form != null)
{
tags.AddTags(_httpContextAccessor.HttpContext.Request.Form.Where(z => z.Key.ToLower().Contains("password")).SelectMany(z => z.Value.Select(y => new KeyValuePair<string, string>(z.Key, y.Length > 21 ? y.Substring(0, 21) + "..." : y))));
tags.AddTags(_httpContextAccessor.HttpContext.Request.Form.Files.Select(z => new KeyValuePair<string, string>("File", $"{z.ContentType} '{z.FileName}' {z.Length}")));
}
if (_httpContextAccessor.HttpContext.User.Identity?.IsAuthenticated ?? false)
{
tags.AddTags(_httpContextAccessor.HttpContext.User.Claims.Select(z => new KeyValuePair<string, string>(z.Type, z.Value)));
}
else
{
tags.AddTag("authentication", "none");
}
foreach (KeyValuePair<string, object> tag in tags.GetAllTags())
{
tcp.Properties[tag.Key] = tag.Value.ToString();
}
}
}
and
public static ILoggingBuilder AddLogging(this ILoggingBuilder builder, bool isDevelopment)
{
builder.ClearProviders();
Activity.DefaultIdFormat = ActivityIdFormat.W3C;
//builder.Services.AddApplicationInsightsTelemetry("key");
//Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.Active.TelemetryChannel.DeveloperMode = true;
if (isDevelopment)
{
builder.AddBetterConsoleFormatter();
//builder.AddHttpRequestLogger(); // Just can't get this to work :(
//builder.AddBetterConsoleLogger(); // The singleton doesn't work very well and I don't understand why correlation IDs are omitted/
//builder.AddConsole();
builder.AddDebug();
}
else
{
// Add ApplicationInsights
builder.AddApplicationInsights();
builder.Services.AddApplicationInsightsTelemetry();
builder.Services.AddSingleton<ITelemetryInitializer, TelemetryInitializer>();
}
The IHttpContextAccessor deals with all that nastyness -- I was surprised to find that this is a singleton rather than scoped.
It turns out that this is not called until late on in the request pipeline which means that the auth middleware has run and therefore the .User is populated.
Related
I'm using ApplicationInsights and I want to add the request, and after that the response, to the logging properties.
To achieve this I am implementing my own ITelemetryInitializer. It looks exactly like this.
public class MyInitializer : ITelemetryInitializer
{
private readonly IHttpContextAccessor _httpContextAccessor;
public MyInitializer(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public void Initialize(ITelemetry telemetry)
{
var requestTelemetry = telemetry as RequestTelemetry;
if (requestTelemetry == null || _httpContextAccessor?.HttpContext?.Request == null
|| requestTelemetry.Properties.ContainsKey("RequestBody"))
{
return;
}
var request = _httpContextAccessor?.HttpContext?.Request;
request?.EnableRewind();
if (request.Method.Equals(HttpMethod.Post.ToString(), StringComparison.InvariantCultureIgnoreCase)
|| request.Method.Equals(HttpMethod.Put.ToString(), StringComparison.InvariantCultureIgnoreCase))
{
using (var reader = new StreamReader(request.Body, Encoding.UTF8, true, 1024, true))
{
var requestBody = reader.ReadToEnd();
requestTelemetry.Properties.Add("RequestBody", requestBody);
}
}
}
}
In startup I've added this
services.AddHttpContextAccessor();
services.AddSingleton<ITelemetryInitializer, MyInitializer>();
services.AddApplicationInsightsTelemetry();
The error I get is:
ObjectDisposedException: Cannot access a disposed object.
Object name: FileBufferingReadStream.
Microsoft.AspNetCore.WebUtilities.FileBufferingReadStream.ThrowIfDisposed()
I've used .EnableRewind as well as instructing the StreamReader to leave the file open. Despite this my request is still null when it actually hits my controller, or even when it hits my initializer again for a second pass (setting the response information).
Any suggestions are welcome.
Additionally I tried adding a piece of middleware to ensure .EnableRewind was on for everything, but this did nothing. I'd prefer not to have to add any additional middleware since I'd like there to be no other dependencies.
app.Use(async (context, next) =>
{
context.Request.EnableRewind();
await next();
});
Thanks.
As always the solution ends up being a single line of code. I owe Mr Gunnar Peipman a thanks for his blog post Reading request body in ASP.NET Core.
The line:
request.Body.Seek(0, SeekOrigin.Begin);
The code
public class MyInitializer : ITelemetryInitializer
{
private readonly IHttpContextAccessor _httpContextAccessor;
public MyInitializer(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public void Initialize(ITelemetry telemetry)
{
var requestTelemetry = telemetry as RequestTelemetry;
if (requestTelemetry == null || _httpContextAccessor?.HttpContext?.Request == null
|| requestTelemetry.Properties.ContainsKey("RequestBody"))
{
return;
}
var request = _httpContextAccessor?.HttpContext?.Request;
request?.EnableRewind();
if (request.Method.Equals(HttpMethod.Post.ToString(), StringComparison.InvariantCultureIgnoreCase)
|| request.Method.Equals(HttpMethod.Put.ToString(), StringComparison.InvariantCultureIgnoreCase))
{
using (var reader = new StreamReader(request.Body, Encoding.UTF8, true, 1024, true))
{
var requestBody = reader.ReadToEnd();
request.Body.Seek(0, SeekOrigin.Begin);
requestTelemetry.Properties.Add("RequestBody", requestBody);
}
}
}
}
I am mapping from a JObject to a custom class and it works fine, but i'd rather tell it to do this default type of mapping once instead of having to do a .MapFrom for every property. Most of the source "properties" are just the lowercase underscored version of the Pascal case property names on the destination.
Since JObject doesnt have the values I want as properties, i have to index into them via the MapFrom (I can't use SourceMemberNamingConvention/DestinationMemberNamingConvention). So I want an expression that i can use via something like ForAllOtherMembers to apply this default value retrieval from the JObject but I can't it to work...what options do i have?
I have tried using ResolveUsing method that I would have to also use ConstructedBy method but ResolveUsing returns a void (now? in version 5) so I couldn't do a .ConstructedBy() on it.
Below I am having to do each MapFrom even though they follow the same access pattern:
MapperConfiguration MapperConfig = new MapperConfiguration(cfg => {
cfg.CreateMap<string, bool>().ConvertUsing<BooleanTypeConverter>();
cfg.CreateMap<JObject, FiscalYear>()
.ForMember("EmployeeName",
options => options.MapFrom(jo => jo["employee_name"]))
.ForMember("YearName",
options => options.MapFrom(jo => jo["year_name"]));
UPDATE:
I went ahead and just did it more the manual way and replaced strings with a delegate
public static class AutoMapperConfig
{
public static MapperConfiguration MapperConfig = new MapperConfiguration(cfg => {
cfg.CreateMap<string, bool>().ConvertUsing<BooleanTypeConverter>();
cfg.CreateMap<JToken, FiscalYear>()
.Map(d => d.EmployeeName)
.Map(d => d.Year, "int_year")
.Map(d => d.Name, "year");
});
}
public static class MappingExpressionExtensions
{
private static readonly MatchEvaluator ToSnakeCaseEvaluator = m =>
{
string match = m.ToString();
return "_" + char.ToLower(match[0]) + match.Substring(1);
};
public static IMappingExpression<JToken, TDest> Map<TDest, TDestProp>(this IMappingExpression<JToken, TDest> map,
Expression<Func<TDest, TDestProp>> propertyExpression, string sourceName = null)
{
var propertyName = propertyExpression.PropertyName();
sourceName = string.IsNullOrWhiteSpace(sourceName)
? new Regex("([A-Z])").Replace(propertyName, ToSnakeCaseEvaluator).TrimStart('_')
: sourceName;
var propType = typeof(TDestProp);
if (propType == typeof(bool))
{
// in order for BooleanTypeConverter to work, convert to string and run as a normal mapping
map.ForMember(propertyExpression.PropertyName(), o => o.MapFrom(jo => jo[sourceName].ToString()));
return map;
}
var isNullableGenericType = propType.IsGenericType && propType.GetGenericTypeDefinition() == typeof(Nullable<>);
var underlyingType = isNullableGenericType ? Nullable.GetUnderlyingType(propType) : propType;
TypeConverter converter = TypeDescriptor.GetConverter(underlyingType);
map.ForMember(propertyName,
o => o.MapFrom(jo => (isNullableGenericType && string.IsNullOrWhiteSpace(jo[sourceName].ToString()))
|| (IsNumeric(jo[sourceName]) && string.IsNullOrWhiteSpace(jo[sourceName].ToString()))
? 0 : converter.ConvertFrom(jo[sourceName].ToString())));
return map;
}
public static bool IsNumeric(object expression)
{
if (expression == null)
return false;
double number;
return Double.TryParse(Convert.ToString(expression, CultureInfo.InvariantCulture),
NumberStyles.Any, NumberFormatInfo.InvariantInfo, out number);
}
}
public class BooleanTypeConverter : ITypeConverter<string, bool>
{
/// <summary>
/// Automapper version compatible with version 4.x
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public bool Convert(ResolutionContext context)
{
switch (context.SourceValue.ToString().ToLower().Trim())
{
case "true":
case "yes":
case "y":
case "1":
return true;
}
return false;
}
//// Automapper version compatible with version 5.0+
//public bool Convert(string source, bool destination, ResolutionContext context)
//{
// switch (source.ToLower().Trim())
// {
// case "true":
// case "yes":
// case "y":
// case "1":
// return true;
// }
// return false;
//}
// put this in static class
public static string PropertyName<T, TProperty>(this Expression<Func<T, TProperty>> property)
{
if (property.Body is MemberExpression)
return ((MemberExpression) property.Body).Member.Name;
return ((MemberExpression) ((UnaryExpression) property.Body).Operand).Member.Name;
}
}
How can I apply a behavior to all interface in a specific namespace?
I know how to apply a behavior to a concrete interface like IMyBlFacade,
but I don't want to do that for all interfaces separately, but in one shot.
Is implementing a ICallHandler obsolete when using custom IInterfaceBehaviors?
As I understand both build up a pipeline for interception.
What is the benefit of using ootb callhandlers and custom callhandlers over IInterfacebehaviors?
I don't want it to be like this:
unity.RegisterType<IMyService, MyService>(
new ContainerControlledLifetimeManager(),
new Interceptor<InterfaceInterceptor>(),
new InterceptionBehavior<OutputInterceptionBehavior>());
rather like this (pseudo code):
unity.addInterceptor<InterfaceInterceptor>()
.addMachingRule<namespace>("mynamespace")
.addBehaviors(...);
So it is partly possible using Unity's RegistrationByConvention.
As far as I have understood, you can only do simple mappings.
For more complex mappings, for example using various InjectionMembers, you have to map them manually.
You have to inherit from RegistrationConvention to build your own convention implementation.
public class UnityRegistrationByConventions : RegistrationConvention
{
private readonly IUnityContainer _container;
List<string> _assemblyNameFilter;
List<string> _namespaceFilterForClasses;
public UnityRegistrationByConventions(IUnityContainer container, List<string> assemblyNameFilter = null, List<string> namespaceFilterForClasses = null)
{
_container = container;
_assemblyNameFilter = assemblyNameFilter;
_namespaceFilterForClasses = namespaceFilterForClasses;
}
public override Func<Type, IEnumerable<Type>> GetFromTypes()
{
return WithMappings.FromMatchingInterface;
}
public override Func<Type, IEnumerable<InjectionMember>> GetInjectionMembers()
{
return (t => new List<InjectionMember>(){
new Interceptor<InterfaceInterceptor>(),
new InterceptionBehavior<LoggingInterceptionBehavior>(), // 1
new InterceptionBehavior<ExceptionInterceptionBehavior>(), // 2
new InterceptionBehavior<CachingInterceptionBehavior>(), // 3
new InterceptionBehavior<ValidationInterceptionBehavior>()} as IEnumerable<InjectionMember>);
}
public override Func<Type, LifetimeManager> GetLifetimeManager()
{
return t => WithLifetime.Transient(t);
}
public override Func<Type, string> GetName()
{
return (type => (this._container.Registrations.Select(x => x.RegisteredType)
.Any(r => type.GetInterfaces().Contains(r) == true) == true) ? WithName.TypeName(type) : WithName.Default(type));
}
public override IEnumerable<Type> GetTypes()
{
var allAssemblies = AppDomain.CurrentDomain.GetAssemblies().Where(a => _assemblyNameFilter.Contains(a.FullName.Split(',')[0]));
List<Type> allClasses = new List<Type>();
foreach (var assembly in allAssemblies)
{
var classArray = assembly.GetTypes().Where(t => t.IsPublic &&
!t.IsAbstract &&
t.IsClass == true &&
_namespaceFilterForClasses.Contains(t.Namespace));
if (classArray != null && classArray.Count() > 0)
allClasses.AddRange(classArray);
}
return allClasses;
}
}
and apply the convention like this
var rby = new UnityRegistrationByConventions(unityContainer, assFilter, classNamespaceFilter);
I use Moq to test some behaviour. I want to verify set view's model property as new instance of Message class:
[TestFixture]
public class MessageFormPresenterTests
{
private NameValueCollection queryString;
private Mock<IDatabase> mockDatabase;
private Mock<HttpContextBase> mockHttpContext;
private Mock<HttpRequestBase> mockRequest;
private Mock<HttpResponseBase> mockResponce;
private Mock<IMessageFormView> mockView;
private MessageFormPresenter presenter;
[SetUp]
public void SetUp()
{
this.queryString = new NameValueCollection();
this.mockDatabase = new Mock<IDatabase>();
this.mockHttpContext = new Mock<HttpContextBase>();
this.mockRequest = new Mock<HttpRequestBase>();
this.mockResponce = new Mock<HttpResponseBase>();
this.mockView = new Mock<IMessageFormView>();
this.mockHttpContext.SetupGet(c => c.Request).Returns(this.mockRequest.Object);
this.mockHttpContext.SetupGet(c => c.Response).Returns(this.mockResponce.Object);
this.mockRequest.SetupGet(r => r.QueryString).Returns(this.queryString);
this.presenter = new MessageFormPresenter(this.mockView.Object) { Database = this.mockDatabase.Object, HttpContext = this.mockHttpContext.Object };
}
[Test]
public void ViewLoad_QueryStringNotHasMessageIdParameter_PopulateViewModelAsNewMessage()
{
// Act
this.mockView.Raise(v => v.Load += null, new EventArgs());
// Assert
this.mockView.VerifySet(v => v.Model = It.Is<Message>(m => m.Id == 0));
}
}
But this test pass even if i not write this functionality in my MessageFormPresenter. When i set breakpoint to action call (v.Model = It.Is(m => m.Id == 0)) and debug my test, Model property returned as "Castle.Proxies.MessageProxy" type and has all properties with default values (value of Id property is 0, of course).
Why is this happening? I don't setup anything to return and Moq should return null by default.
P.S. Excuse me for my poor english. I hope you understand what i'm talking about)
UPDATE1: MessageFormPresenter class:
public class MessageFormPresenter : Presenter<IMessageFormView>
{
private IDatabase database;
public MessageFormPresenter(IMessageFormView view)
: base(view)
{
View.Load += this.View_Load;
}
public IDatabase Database
{
get { return this.database ?? (this.database = DatabaseFactory.DatabaseInstance); }
set { this.database = value; }
}
private void View_Load(object sender, EventArgs e)
{
int messageId;
var messageParam = Request.QueryString[QueryParamNames.MessageId];
if (messageParam == null)
{
View.Model = new Message();
return;
}
if (!View.IsAdmin)
{
Response.Redirect(PageUrls.AccessDenied, true);
return;
}
if (int.TryParse(
messageParam,
NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite,
CultureInfo.InvariantCulture,
out messageId))
{
View.Model = this.Database.Single<Message>(messageId);
}
else
{
Response.Redirect(PageUrls.DefaultPage, true);
}
}
}
Base class Presenter<IMessageFormView> provided by WebFormsMvp framework.
UPDATE2: If i use strict behaviour TargetInvocationException will be thrown in base constructor of MessageFormPresenter, even if i setup all properties of Mock<IMessageFormView>.
I am using ASP.NET MVC 3.
Can someone please help me clarify what's happening here:
var person = new PersonRepository().Get();
var personViewModel = new PersonViewModel();
personViewModel.InjectFrom<LoopValueInjection>(person)
.InjectFrom<CountryToLookup>(person);
I have a grid on my Index view. Each row is an instance of a CategoryViewModel. So what I do is to get a list of all the categories and then map each Category to a CategoryViewModel, and then pass this list of CategoryViewModels to the view. Hou would I do a mapping like that?
IEnumerable<Category> categoryList = categoryService.GetAll();
I thought the following would work but it doesn't:
// Mapping
IList<CategoryViewModel> viewModelList = new List<CategoryViewModel>();
viewModelList.InjectFrom(categoryList);
AFAIK value injecter doesn't support automatic collection mapping like AutoMapper but you could use a simple LINQ expression and operate on each element:
IEnumerable<Category> categoryList = categoryService.GetAll();
IList<CategoryViewModel> viewModelList = categoryList
.Select(x => new CategoryViewModel().InjectFrom(x)).Cast<CategoryViewModel>()
.ToList();
//source list
IEnumerable<string> items = new string[] { "1", "2" };
// target list
List<int> converted = new List<int>();
// inject all
converted.InjectFrom(items);
And the extension method:
public static ICollection<TTo> InjectFrom<TFrom, TTo>(this ICollection<TTo> to, IEnumerable<TFrom> from) where TTo : new()
{
foreach (var source in from)
{
var target = new TTo();
target.InjectFrom(source);
to.Add(target);
}
return to;
}
ICollection<T> is the interface that got least features but a Add method.
Update
An example using more proper models:
var persons = new PersonRepository().GetAll();
var personViewModels = new List<PersonViewModel>();
personViewModels.InjectFrom(persons);
Update - Inject from different sources
public static ICollection<TTo> InjectFrom<TFrom, TTo>(this ICollection<TTo> to, params IEnumerable<TFrom>[] sources) where TTo : new()
{
foreach (var from in sources)
{
foreach (var source in from)
{
var target = new TTo();
target.InjectFrom(source);
to.Add(target);
}
}
return to;
}
Usage:
var activeUsers = new PersonRepository().GetActive();
var lockedUsers = new PersonRepository().GetLocked();
var personViewModels = new List<PersonViewModel>();
personViewModels.InjectFrom(activeUsers, lockedUsers);
Use this function definition
public static object InjectCompleteFrom(this object target, object source)
{
if (target.GetType().IsGenericType &&
target.GetType().GetGenericTypeDefinition() != null &&
target.GetType().GetGenericTypeDefinition().GetInterfaces() != null &&
target.GetType().GetGenericTypeDefinition().GetInterfaces()
.Contains(typeof(IEnumerable)) &&
source.GetType().IsGenericType &&
source.GetType().GetGenericTypeDefinition() != null &&
source.GetType().GetGenericTypeDefinition().GetInterfaces() != null &&
source.GetType().GetGenericTypeDefinition().GetInterfaces()
.Contains(typeof(IEnumerable)))
{
var t = target.GetType().GetGenericArguments()[0];
var tlist = typeof(List<>).MakeGenericType(t);
var addMethod = tlist.GetMethod("Add");
foreach (var sourceItem in source as IEnumerable)
{
var e = Activator.CreateInstance(t).InjectFrom<CloneInjection>(sourceItem);
addMethod.Invoke(target, new[] { e });
}
return target;
}
else
{
return target.InjectFrom(source);
}
}
For those like me who prefer shortest notations possible
public static ICollection<TTarget> InjectFromList<TTarget, TOrig>(this ICollection<TTarget> target, ICollection<TOrig> source) where TTarget : new()
{
source.Select(r => new TTarget().InjectFrom(r))
.Cast<TTarget>().ToList().ForEach(e => target.Add(e));
return target;
}
public static ICollection<TTarget> InjectFromList<TTarget, TOrig>(this ICollection<TTarget> target, params ICollection<TOrig>[] sources) where TTarget : new()
{
sources.ToList().ForEach(s => s.ToList().Select(r => new TTarget().InjectFrom(r))
.Cast<TTarget>().ToList().ForEach(e => target.Add(e)));
return target;
}
Create a generic list mapper:
public class ValueMapper
{
public static TResult Map<TResult>(object item) where TResult : class
{
return item == null ? null : Mapper.Map<TResult>(item);
}
public static IEnumerable<TResult> MapList<TResult>(IEnumerable<object> items) where TResult : class
{
return items?.Select(i => Mapper.Map<TResult>(i));
}
}
Now you can reference the ValueMapper class wherever you want, and call both Map and MapList
var mydtos = ValueMapper.MapList<MyDto>(dtos);
var mydto = ValueMapper.Map<MyDto>(dto);