public static InstrumentAttribute GetInstrumentAttribute(this ICustomAttributeProvider provider) { // Since we might have assy version differences, we're stuck buggering about with reflection Attribute matchingAttribute = provider.GetCustomAttributes(false).OfType<Attribute>().FirstOrDefault(x => x.GetType().Name.EndsWith("InstrumentAttribute")); InstrumentAttribute toReturn = null; if (matchingAttribute != null) { toReturn = new InstrumentAttribute(); Type matchingAttributeType = matchingAttribute.GetType(); foreach (var prop in matchingAttributeType.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance)) { PropertyInfo equivalentProperty = typeof(InstrumentAttribute).GetProperty(prop.Name); if (equivalentProperty != null) { if (prop.PropertyType.IsEnum) { equivalentProperty.SetValue(toReturn, Convert.ToInt32(prop.GetValue(matchingAttribute))); } else { equivalentProperty.SetValue(toReturn, prop.GetValue(matchingAttribute)); } } } } return toReturn; }
public void GetInstrumentationSet_ExcludesGeneratedMethods() { var context = new InstrumentAttribute { Scopes = InstrumentationScopes.Methods }; var result = _testDiscoverer.GetInstrumentationSet("dummy path", context, x => x == _classType); Assert.IsFalse(result.Any(x => x.Target == _classType.GetMethods(BindingFlags.Public).First(method => method.IsCompilerGenerated))); }
public virtual IEnumerable<InstrumentationTarget> GetInstrumentationSet(string assemblyPath, InstrumentAttribute context, Predicate<ITypeDetails> typeFilter) { var toReturn = new List<InstrumentationTarget>(); _logger.InfoFormat("Processing assembly {0}", assemblyPath); if (typeFilter == null) { typeFilter = x => true; } var allTypes = this.GetTypes(assemblyPath).Where(x => x.IsClass && !x.IsNested && typeFilter(x)); _logger.DebugFormat("Found {0} types", allTypes.Count()); InstrumentAttribute assyContext = null; if (allTypes.Any()) { assyContext = allTypes.First().Assembly.InstrumentationContext; } foreach (var t in allTypes) { toReturn.AddRange(GetInstrumentationSet(t, InstrumentAttribute.GetEffectiveInstrumentationContext(assyContext, context))); } return toReturn; }
public void GetInstrumentationSet_IncludesGeneratedMethods_IfSpecifiedByInitialContext() { var context = new InstrumentAttribute { Scopes = InstrumentationScopes.Methods, IncludeCompilerGeneratedCode = true }; var result = _testDiscoverer.GetInstrumentationSet("dummy path", context, x => x == _classType); Assert.IsTrue(result.Any(x => x.Target == _classType.GetMethods(BindingFlags.Public).First(method => method.IsCompilerGenerated))); }
public void GetInstrumentationSet_IncludesGeneratedMethods_IfSpecifiedByMethodContext() { var initialContext = new InstrumentAttribute { Scopes = InstrumentationScopes.Methods }; var methodContext = new InstrumentAttribute { IncludeCompilerGeneratedCode = true }; // Apply a method-level context to the generated method ((DummyMethodDetails) _classType.GetMethods(BindingFlags.Public).First(x => x.IsCompilerGenerated)).InstrumentationContext = methodContext; var result = _testDiscoverer.GetInstrumentationSet("dummy path", initialContext, x => x == _classType); Assert.IsTrue(result.Any(x => x.Target == _classType.GetMethods(BindingFlags.Public).First(method => method.IsCompilerGenerated))); }
public void GetInstrumentationSet_MergesInitialAndAssemblyLevelContext_IfBothSupplied() { var initialContext = new InstrumentAttribute { Metric = Metric.Unscoped, MetricName = "Initial metric", Scopes = InstrumentationScopes.All, IncludeCompilerGeneratedCode = true }; var assyContext = new InstrumentAttribute { Metric = Metric.Scoped, MetricName = "Assy metric", Scopes = InstrumentationScopes.All }; var expectedContext = new InstrumentAttribute { Metric = NRConfig.Metric.Scoped, MetricName = "Assy metric", Scopes = InstrumentationScopes.All, IncludeCompilerGeneratedCode = true }; DummyAssembly.Instance.InstrumentationContext = assyContext; _testDiscoverer.GetInstrumentationSet("dummy path", initialContext, x => true); EqualityHelper.AreObjectsEquivalentByPublicProperties(_testDiscoverer.FirstContext, expectedContext, false); }
public void CciAndReflectedStrategies_ProduceSameOutput_WithExplicitAllPublicNonPublicContext() { var cci = new NRConfigManager.Infrastructure.Cci.CciInstrumentationDiscoverer(); var reflected = new NRConfigManager.Infrastructure.Reflected.ReflectedInstrumentationDiscoverer(); var context = new InstrumentAttribute { Scopes = InstrumentationScopes.All }; var cciSet = cci.GetInstrumentationSet("TestAssembly.dll", context, x => true); var reflectedSet = reflected.GetInstrumentationSet("TestAssembly.dll", context, x => true); // Render both var cciSetRendered = NRConfigManager.Rendering.Renderer.Render(cciSet); var reflectedSetRendered = NRConfigManager.Rendering.Renderer.Render(reflectedSet); Assert.IsTrue(EqualityHelper.AreObjectsEquivalentByPublicProperties(cciSetRendered, reflectedSetRendered, true)); }
public static InstrumentAttribute GetInstrumentAttribute(this IReference reference) { var attributes = reference.Attributes ?? Enumerable.Empty<ICustomAttribute>(); var matchingAttribute = attributes.Where(x => TypeHelper.GetTypeName(x.Type).EndsWith("InstrumentAttribute")).FirstOrDefault(); InstrumentAttribute toReturn = null; Type instrumentAttributeType = typeof(InstrumentAttribute); if (matchingAttribute != null) { toReturn = new InstrumentAttribute(); foreach (var namedArgument in matchingAttribute.NamedArguments) { var matchingProperty = instrumentAttributeType.GetProperty(namedArgument.ArgumentName.Value, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic); if (matchingProperty != null) { matchingProperty.SetValue(toReturn, (namedArgument.ArgumentValue as IMetadataConstant).Value); } } } return toReturn; }
protected virtual IEnumerable<InstrumentationTarget> GetInstrumentationSet(ITypeDetails t, InstrumentAttribute context) { var toReturn = new List<InstrumentationTarget>(); if (!t.IsGenericTypeDefinition) { // Does the type have an Instrument attribute? var typeLevelAttribute = t.InstrumentationContext; typeLevelAttribute = InstrumentAttribute.GetEffectiveInstrumentationContext(typeLevelAttribute, context); if (t.IsCompilerGenerated && (typeLevelAttribute == null || !typeLevelAttribute.IncludeCompilerGeneratedCode)) { // Bail out early - we've found a compiler-generated method and haven't been told to include 'em _logger.DebugFormat("Skipping type {0} - compiler-generated and configuration set to skip", t.FullName); return toReturn; } _logger.DebugFormat("Processing type {0}", t.FullName); var baseBindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly; var propBindingFlags = baseBindingFlags; var methodBindingFlags = baseBindingFlags; var ctorBindingFlags = baseBindingFlags; if (typeLevelAttribute == null) { propBindingFlags |= BindingFlags.NonPublic | BindingFlags.Public; methodBindingFlags |= BindingFlags.NonPublic | BindingFlags.Public; ctorBindingFlags |= BindingFlags.NonPublic | BindingFlags.Public; } else { if ((typeLevelAttribute.Scopes & InstrumentationScopes.PublicProperties) == InstrumentationScopes.PublicProperties) { propBindingFlags |= BindingFlags.Public; } if ((typeLevelAttribute.Scopes & InstrumentationScopes.NonPublicProperties) == InstrumentationScopes.NonPublicProperties) { propBindingFlags |= BindingFlags.NonPublic; } if ((typeLevelAttribute.Scopes & InstrumentationScopes.PublicMethods) == InstrumentationScopes.PublicMethods) { methodBindingFlags |= BindingFlags.Public; } if ((typeLevelAttribute.Scopes & InstrumentationScopes.NonPublicMethods) == InstrumentationScopes.NonPublicMethods) { methodBindingFlags |= BindingFlags.NonPublic; } if ((typeLevelAttribute.Scopes & InstrumentationScopes.PublicConstructors) == InstrumentationScopes.PublicConstructors) { ctorBindingFlags |= BindingFlags.Public; } if ((typeLevelAttribute.Scopes & InstrumentationScopes.NonPublicConstructors) == InstrumentationScopes.NonPublicConstructors) { ctorBindingFlags |= BindingFlags.NonPublic; } } _logger.DebugFormat("Prop flags {0}, Ctor flags {1}, Method flags {2}", propBindingFlags, ctorBindingFlags, methodBindingFlags); // Instrument everything in this type, irrespective of its member-level // details foreach (var methodDetails in t.GetMethods(methodBindingFlags)) { _logger.DebugFormat("Examining method {0}", methodDetails.ToString()); var attr = InstrumentAttribute.GetEffectiveInstrumentationContext(methodDetails.InstrumentationContext, typeLevelAttribute); if (attr != null && (!methodDetails.IsCompilerGenerated || attr.IncludeCompilerGeneratedCode)) { toReturn.Add(GetInstrumentationTarget(methodDetails, attr)); } } foreach (var propertyDetails in t.GetProperties(propBindingFlags)) { _logger.DebugFormat("Examining property {0}.{1}", t.FullName, propertyDetails.Name); var getMethod = propertyDetails.GetMethod; var setMethod = propertyDetails.SetMethod; if (getMethod != null && getMethod.ContainsGenericParameters) { getMethod = null; } if (setMethod != null && setMethod.ContainsGenericParameters) { setMethod = null; } if (getMethod != null) { var getMethodAttr = InstrumentAttribute.GetEffectiveInstrumentationContext(propertyDetails.InstrumentationContext, getMethod.InstrumentationContext, typeLevelAttribute); if (getMethodAttr != null) { toReturn.Add(GetInstrumentationTarget(getMethod, getMethodAttr)); } } if (setMethod != null) { var setMethodAttr = InstrumentAttribute.GetEffectiveInstrumentationContext(propertyDetails.InstrumentationContext, setMethod.InstrumentationContext, typeLevelAttribute); if (setMethodAttr != null) { toReturn.Add(GetInstrumentationTarget(setMethod, setMethodAttr)); } } } foreach (var constructorDetails in t.GetConstructors(ctorBindingFlags).Where(x => !x.ContainsGenericParameters)) { _logger.DebugFormat("Examining method {0}", constructorDetails.ToString()); var attr = InstrumentAttribute.GetEffectiveInstrumentationContext(constructorDetails.InstrumentationContext, typeLevelAttribute); if (attr != null) { toReturn.Add(GetInstrumentationTarget(constructorDetails, attr)); } } // Process nested types recursively, rather than enumerating them from the get-go so that we can apply // instrumentation scoping recursively too - the nested class will take on the instrumentation configuration // of the containing type, if any, or whatever's been passed down by initial settings foreach (var nested in t.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static)) { toReturn.AddRange(GetInstrumentationSet(nested, typeLevelAttribute)); } } else { _logger.DebugFormat("Skipping type {0} - generic types not supported", t.FullName); } return toReturn; }
public void GetInstrumentationSet_RequestsPublicProperties_WhenPublicPropertiesSpecifiedInContext() { var context = new InstrumentAttribute { Scopes = InstrumentationScopes.PublicProperties }; _testDiscoverer.GetInstrumentationSet("dummy path", context, x => x == _classType); Assert.IsTrue(_classType.LastGetPropertiesFlags.HasFlag(BindingFlags.Public)); Assert.IsFalse(_classType.LastGetPropertiesFlags.HasFlag(BindingFlags.NonPublic)); }
protected virtual InstrumentationTarget GetInstrumentationTarget(IMethodDetails method, InstrumentAttribute context) { return new InstrumentationTarget(method, context.MetricName, context.Name, context.TransactionNamingPriority, context.Metric); }
public void GetInstrumentationSet_UsesAssemblyLevelContext_IfNoneExplicitlySupplied() { var assyContext = new InstrumentAttribute { Metric = Metric.Scoped, MetricName = "Test metric", Scopes = InstrumentationScopes.All }; DummyAssembly.Instance.InstrumentationContext = assyContext; _testDiscoverer.GetInstrumentationSet("dummy path", null, x => true); EqualityHelper.AreObjectsEquivalentByPublicProperties(_testDiscoverer.FirstContext, assyContext, false); }
/// <summary> /// Generates an instrumentation context that represents the configuration supplied by an ordered /// collection of contexts, where contexts earlier in the list take higher precedence. /// </summary> /// <param name="attrs">Zero or more InstrumentAttribute objects representing full or partial /// contexts to be combined.</param> /// <returns>An InstrumentAttribute representing the context that best describes the supplied /// hierarchy of full or partial contexts.</returns> public static InstrumentAttribute GetEffectiveInstrumentationContext(params InstrumentAttribute[] attrs) { // Working through the array, assuming that the top-most items are the most important InstrumentAttribute toReturn = new InstrumentAttribute(); bool setMetricName = false, setName = false, setTransactionNamingPriority = false, setMetric = false, setScopes = false, setIncludeCompilerGenerated = false; if (attrs != null) { foreach (var attr in attrs) { if (attr == null) { continue; } else if (setMetricName && setName && setTransactionNamingPriority && setMetric && setIncludeCompilerGenerated && setScopes) { break; } if (attr.MetricName != null && !setMetricName) { toReturn.MetricName = attr.MetricName; setMetricName = true; } if (attr.Name != null && !setName) { toReturn.Name = attr.Name; setName = true; } if (attr.TransactionNamingPriority != null && !setTransactionNamingPriority) { toReturn.TransactionNamingPriority = attr.TransactionNamingPriority; setTransactionNamingPriority = true; } if (attr.Metric != Metric.Unspecified && !setMetric) { toReturn.Metric = attr.Metric; setMetric = true; } if (!setScopes) { toReturn.Scopes = attr.Scopes; setScopes = true; } if (attr.IncludeCompilerGeneratedCodeSet && !setIncludeCompilerGenerated) { toReturn.IncludeCompilerGeneratedCode = attr.IncludeCompilerGeneratedCode; setIncludeCompilerGenerated = true; } } } bool anyNonNullAttrs = false; if (attrs != null) { foreach (var attr in attrs) { if (attr != null) { anyNonNullAttrs = true; break; } } } if (!anyNonNullAttrs) { toReturn = null; } return toReturn; }
public bool Execute() { InstrumentAttribute assemblyAttribute = null; if (this.AutomaticInstrumentationScopes != InstrumentationScopes.None) { assemblyAttribute = new InstrumentAttribute() { Scopes = this.AutomaticInstrumentationScopes }; assemblyAttribute.IncludeCompilerGeneratedCode = this.IncludeCompilerGeneratedCode; } var targets = new List<InstrumentationTarget>(); foreach (string assyPath in this.InputPaths) { InstrumentationDiscovererBase discoverer = null; if (this.UseReflectionBasedDiscovery) { _logger.Info("Using legacy reflection-based discovery on request"); discoverer = new ReflectedInstrumentationDiscoverer(); } else { discoverer = new CciInstrumentationDiscoverer(); } targets.AddRange(discoverer.GetInstrumentationSet(assyPath, assemblyAttribute, x => true)); } _logger.Info(string.Format("Processed {0} targets", targets.Count)); if (targets.Count > MAX_TARGETS_BEFORE_WARNING) { _logger.Warn(string.Format("WARNING - New Relic recommend instrumenting no more than {0} targets to avoid performance issues.", MAX_TARGETS_BEFORE_WARNING)); _logger.Warn(string.Format("See https://newrelic.com/docs/dotnet/CustomInstrumentation.html for more information")); } string tempPath = Path.GetTempFileName(); using (FileStream w = new FileStream(tempPath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None)) { Renderer.RenderToStream(targets, w); } // Delete existing file, if required if (File.Exists(this.OutputPath)) { File.Delete(this.OutputPath); } File.Move(tempPath, this.OutputPath); return true; }
public void GetInstrumentationSet_UsesOuterTypeContext_WhenNestedTypeHasNoExplicitContext() { var typeContext = new InstrumentAttribute { Scopes = InstrumentationScopes.All }; _classType.InstrumentationContext = typeContext; _testDiscoverer.GetInstrumentationSet("dummy path", new InstrumentAttribute(), x => true); Assert.IsTrue(EqualityHelper.AreObjectsEquivalentByPublicProperties(typeContext, _testDiscoverer.ScannedTypes[_nestedType], false)); }
public void GetInstrumentationSet_UsesSuppliedInitialContext() { var context = new InstrumentAttribute { Metric = Metric.Scoped, MetricName = "Test metric", Scopes = InstrumentationScopes.All }; _testDiscoverer.GetInstrumentationSet("dummy path", context, x => true); EqualityHelper.AreObjectsEquivalentByPublicProperties(_testDiscoverer.FirstContext, context, false); }
public void GetInstrumentationSet_RequestsNoConstructors_WhenNotRequiredByContext() { var context = new InstrumentAttribute { Scopes = InstrumentationScopes.None }; _testDiscoverer.GetInstrumentationSet("dummy path", context, x => x == _classType); Assert.IsFalse(_classType.LastGetConstructorsFlags.HasFlag(BindingFlags.Public)); Assert.IsFalse(_classType.LastGetConstructorsFlags.HasFlag(BindingFlags.NonPublic)); }
public void GetInstrumentationSet_RequestsStaticAndNonStaticAndDeclaredOnlyProperties() { var context = new InstrumentAttribute { Scopes = InstrumentationScopes.None }; _testDiscoverer.GetInstrumentationSet("dummy path", context, x => x == _classType); Assert.IsTrue(_classType.LastGetPropertiesFlags.HasFlag(BindingFlags.Static)); Assert.IsTrue(_classType.LastGetPropertiesFlags.HasFlag(BindingFlags.Instance)); Assert.IsTrue(_classType.LastGetPropertiesFlags.HasFlag(BindingFlags.DeclaredOnly)); }
protected override IEnumerable<InstrumentationTarget> GetInstrumentationSet(ITypeDetails t, InstrumentAttribute context) { if (this.FirstContext == null) { this.FirstContext = context; } this.ScannedTypes[t] = context; return base.GetInstrumentationSet(t, context); }