public static string ResolveTracerFactoryNameForAttributeInstrumentation(uint tracerArguments, bool isAsync, string tracerFactoryName)
        {
            if (TracerArgument.IsFlagSet(tracerArguments, TracerFlags.AttributeInstrumentation))
            {
                if (TracerArgument.IsFlagSet(tracerArguments, TracerFlags.WebTransaction) ||
                    TracerArgument.IsFlagSet(tracerArguments, TracerFlags.OtherTransaction))
                {
                    return(isAsync ?
                           "NewRelic.Providers.Wrapper.CustomInstrumentationAsync.OtherTransactionWrapperAsync" :
                           "NewRelic.Providers.Wrapper.CustomInstrumentation.OtherTransactionWrapper");
                }
            }

            return(tracerFactoryName);
        }
        public AfterWrappedMethodDelegate BeforeWrappedMethod(Type type, string methodName, string argumentSignature,
                                                              object invocationTarget, object[] methodArguments, string tracerFactoryName, string metricName,
                                                              uint tracerArguments, ulong functionId)
        {
            InstrumentedMethodInfo instrumentedMethodInfo = default(InstrumentedMethodInfo);
            TrackedWrapper         trackedWrapper;

            if (_functionIdToWrapper.TryGetValue(functionId, out InstrumentedMethodInfoWrapper methodAndWrapper))
            {
                instrumentedMethodInfo = methodAndWrapper.instrumentedMethodInfo;
                trackedWrapper         = methodAndWrapper.wrapper;
            }
            else
            {
                var isAsync = TracerArgument.IsAsync(tracerArguments);

                tracerFactoryName = ResolveTracerFactoryNameForAttributeInstrumentation(tracerArguments, isAsync, tracerFactoryName);

                var method = new Method(type, methodName, argumentSignature, functionId.GetHashCode());
                var transactionNamePriority = TracerArgument.GetTransactionNamingPriority(tracerArguments);
                instrumentedMethodInfo = new InstrumentedMethodInfo((long)functionId, method, tracerFactoryName, isAsync, metricName, transactionNamePriority, TracerArgument.IsFlagSet(tracerArguments, TracerFlags.WebTransaction));

                trackedWrapper = _wrapperMap.Get(instrumentedMethodInfo);

                if (trackedWrapper == null)
                {
                    Log.WarnFormat("WrapperMap.Get unexpectedly returned null for {0}.{1}({2}) in assembly [{3}] (requested wrapper name was {4}).",
                                   instrumentedMethodInfo.Method.Type.FullName,
                                   instrumentedMethodInfo.Method.MethodName,
                                   instrumentedMethodInfo.Method.ParameterTypeNames,
                                   instrumentedMethodInfo.Method.Type.Assembly.FullName,
                                   instrumentedMethodInfo.RequestedWrapperName);

                    return(null);
                }

                _functionIdToWrapper[functionId] = new InstrumentedMethodInfoWrapper(instrumentedMethodInfo, trackedWrapper);
                GenerateLibraryVersionSupportabilityMetric(instrumentedMethodInfo);
            }

            var wrapper = trackedWrapper.Wrapper;

            var transaction = _agent.CurrentTransaction;

            if (Log.IsFinestEnabled)
            {
                transaction.LogFinest($"Attempting to execute {wrapper} found from InstrumentedMethodInfo: {instrumentedMethodInfo}");
            }

            if (transaction.IsFinished)
            {
                if (Log.IsFinestEnabled)
                {
                    if (wrapper.IsTransactionRequired)
                    {
                        transaction.LogFinest($"Transaction has already ended, skipping method {type.FullName}.{methodName}({argumentSignature}).");
                    }
                    else
                    {
                        transaction.LogFinest("Transaction has already ended, detaching from transaction storage context.");
                    }
                }

                transaction.Detach();
                transaction = _agent.CurrentTransaction;

                if (wrapper.IsTransactionRequired)
                {
                    return(Delegates.NoOp);
                }
            }

            if (wrapper.IsTransactionRequired)
            {
                if (!transaction.IsValid)
                {
                    if (Log.IsFinestEnabled)
                    {
                        transaction.LogFinest($"No transaction, skipping method {type.FullName}.{methodName}({argumentSignature})");
                    }

                    return(Delegates.NoOp);
                }

                if (transaction.CurrentSegment.IsLeaf)
                {
                    return(Delegates.NoOp);
                }
            }

            var methodCall             = new MethodCall(instrumentedMethodInfo.Method, invocationTarget, methodArguments);
            var instrumentedMethodCall = new InstrumentedMethodCall(methodCall, instrumentedMethodInfo);

            // if the wrapper throws an exception when executing the pre-method code, make sure the wrapper isn't called again in the future
            try
            {
                using (_agentTimerService.StartNew("BeforeWrappedMethod", type.FullName, methodName))
                {
                    var afterWrappedMethod = wrapper.BeforeWrappedMethod(instrumentedMethodCall, _agent, transaction);
                    return((result, exception) =>
                    {
                        using (_agentTimerService.StartNew("AfterWrappedMethod", type.FullName, methodName))
                        {
                            // if the wrapper throws an exception when executing the post-method code, make sure the wrapper isn't called again in the future
                            try
                            {
                                afterWrappedMethod(result, exception);
                                trackedWrapper.NoticeSuccess();
                            }
                            catch (Exception)
                            {
                                HandleBeforeWrappedMethodException(functionId, trackedWrapper, instrumentedMethodCall, instrumentedMethodInfo);
                                throw;
                            }
                        }
                    });
                }
            }
            catch
            {
                HandleBeforeWrappedMethodException(functionId, trackedWrapper, instrumentedMethodCall, instrumentedMethodInfo);
                throw;
            }
        }