Inheritance: System.Attribute
        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);
        }
示例#13
0
        /// <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);
            }