是否可以使用 New Relic 来监控 Azure Function 应用程序?

Is it possible to use New Relic to monitor Azure Function app?

我们有一个 Azure Functions,每隔几秒 运行s。我们想使用 New Relic 让我们知道这段代码是否抛出异常或开始出现异常。我已经在 VM 上安装了 New Relic 代理,并通过 NuGet 引入了 NewRelic.Agent.Api DLL。我已将 NewRelic.AppName 设置为 AzureFunctionAppTest 并设置 NEWRELIC_LICENSEKEY 环境变量。日志文件表明我正在连接到 NewRelic。现在,我 运行 正在编写以下代码:

#r "Microsoft.windowsazure.storage"

using System;
using System.Threading.Tasks;
using Microsoft.WindowsAzure.Storage.Table;

public static void Run(TimerInfo myTimer, TraceWriter log, CloudTable syncTimesTable)
{
    NewRelic.Api.Agent.NewRelic.SetTransactionName("SyncEvents", "GetRoutineData");

    var rnd = new Random(DateTime.Now.Millisecond).Next(30);
    log.Info($"Logging {rnd}");

    var eventAttributes = new Dictionary<string, object>() {{"result", "Success!"}, {"Count", rnd.ToString()}};
    NewRelic.Api.Agent.NewRelic.RecordCustomEvent("DevicesSynced", eventAttributes);

    NewRelic.Api.Agent.NewRelic.NoticeError(new SyncException("This is another custom error"));
}

public class SyncException : Exception
{
    public SyncException(string msg) : base(msg)
    {
    }
}

所以,基本上我是在尝试设置事务名称,然后我生成一个随机数并将其记录为自定义事件,然后我尝试记录一个自定义错误。

我让这个 运行 稍等一下,然后使用 New Relic 仪表板进行检查。这是我得到的:

我感觉我 实际上 在我的 Azure 函数代码中的事务中,所以 New Relic 不允许我设置名称。我也有一种感觉,即使我使用 NoticeError 记录了一个错误,它也没有链接到任何交易,因此它没有出现在错误分析中。

我的问题:

谢谢!

更新:

我尝试在 Extensions 目录中添加一个名为 CustomInstrumentation.xml 的文件,根据 New Relic instructions 具有以下内容:

<?xml version="1.0" encoding="utf-8"?>
<extension
    xmlns="urn:newrelic-extension">
    <instrumentation>
        <!-- Define the method which triggers the creation of a transaction. -->
        <tracerFactory name="NewRelic.Agent.Core.Tracer.Factories.BackgroundThreadTracerFactory" metricName="Background/AzureDeviceSync">
            <match assemblyName="Microsoft.Azure.WebJobs.Script" className="Microsoft.Azure.WebJobs.Script.Description.FunctionInvokerBase">
                <exactMethodMatcher methodName="Invoke" />
            </match>
        </tracerFactory>
    </instrumentation>
</extension>

但是,我没有发现任何行为差异。我尝试了几种不同的方法名称匹配(例如 InvokeCoreInvoke),但似乎没有什么不同。

几个问题:

新遗迹文档说:

To view the transaction: From the New Relic menu bar, select APM > Applications > (selected app) > Monitoring > Transactions > Type > (selected type). (The type is defined by Category/Name.) You can then view all custom transactions of the selected type.

它显示了带有自定义选项的下拉菜单的图片:

但是,我的 New Relic 界面没有任何类型的 "Type" 下拉菜单:

此外,如果我检测我的代码以显示 Function 应用程序中的堆栈跟踪,这就是我得到的。也许它会有所帮助:

Stack:

   at System.Environment.GetStackTrace(Exception e, Boolean needFileInfo)
   at System.Environment.get_StackTrace()
   at Submission#0.Run(TimerInfo myTimer, TraceWriter log, CloudTable syncTimesTable) in :line 9
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
   at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at Microsoft.Azure.WebJobs.Script.Description.DotNetFunctionInvoker.<InvokeCore>d__23.MoveNext()
   at System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Start[TStateMachine](TStateMachine& stateMachine)
   at Microsoft.Azure.WebJobs.Script.Description.DotNetFunctionInvoker.InvokeCore(Object[] parameters, FunctionInvocationContext context)
   at Microsoft.Azure.WebJobs.Script.Description.FunctionInvokerBase.<Invoke>d__29.MoveNext()
   at System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Start[TStateMachine](TStateMachine& stateMachine)
   at Microsoft.Azure.WebJobs.Script.Description.FunctionInvokerBase.Invoke(Object[] parameters)
   at Host.Functions.LogNewRelicAlert(TimerInfo myTimer, TraceWriter log, CloudTable syncTimesTable, ExecutionContext _context)
   at lambda_method(Closure , Functions , Object[] )
   at Microsoft.Azure.WebJobs.Host.Executors.TaskMethodInvoker`1.InvokeAsync(TReflected instance, Object[] arguments)
   at Microsoft.Azure.WebJobs.Host.Executors.FunctionInvoker`1.<InvokeAsync>d__8.MoveNext()
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.InvokeMoveNext(Object stateMachine)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.Run()
   at System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.RunAction(Object state)
   at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
   at System.Threading.ThreadPoolWorkQueue.Dispatch()
   at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()

我想知道是否有任何类型的日志文件显示是否确实加载了自定义事务设置以及何时创建了新事务。现在我猜我的 XML 匹配错了。

还有一件事。如果我添加以下行:

NewRelic.Api.Agent.NewRelic.SetTransactionName("Custom", "Testing");

在我的 Function 应用程序中,我会在 New Relic 日志中看到以下警告:

2017-02-01 21:45:25,005 NewRelic WARN: Agent API Error: An error occurred invoking API method "SetTransactionName" - "System.InvalidOperationException: The API method called is only valid from within a transaction. This error can occur if you call the API method from a thread other than the one the transaction started on.

我认为这清楚地表明此时我们实际上并未在事务中,除非它发生在另一个线程上(如果是这种情况我不会感到惊讶)。

其他想法?

您应该试一试自定义交易:

https://docs.newrelic.com/docs/agents/net-agent/instrumentation/net-custom-transactions

以下是我认为适用的值:

  1. 程序集名称:Microsoft.Azure.WebJobs.Script
  2. 类名:Microsoft.Azure.WebJobs.Script.Description.FunctionInvokerBase
  3. 方法名称:InvokeCore

我相信这会在 this method call 上添加一个事务,它是所有用户函数执行的通用包装器。

终于成功了!好吧,至少我认为它会起作用。这是我在 /Extensions 目录中的 .XML 文件:

<?xml version="1.0" encoding="utf-8"?>
<extension
    xmlns="urn:newrelic-extension">
    <instrumentation>
        <!-- Define the method which triggers the creation of a transaction. -->
        <tracerFactory
           name="NewRelic.Agent.Core.Tracer.Factories.BackgroundThreadTracerFactory"
           metricName="Limeade/AzureDeviceSync">
            <match assemblyName="Microsoft.Azure.WebJobs.Script" className="Microsoft.Azure.WebJobs.Script.Description.DotNetFunctionInvoker">
                <exactMethodMatcher methodName="InvokeCore" />
            </match>
        </tracerFactory>
    </instrumentation>
</extension>

这个文件叫什么并不重要,因为 New Relic 会加载 Extensions 目录中的每个 XML 文件。

以下是我学到的一些东西:

  1. 当您更改任何 New Relic 配置时,包括扩展 XML 文件,您必须重置 IIS。但是,SCM 上的 "Restart Site" 按钮实际上并没有 kill IIS 进程。您必须进入 Kudu 中的 Process Explorer 并关闭 w3wp.exe 进程。如果您不知道要杀死哪个进程,可以在您的函数应用程序中添加 log.Info($"Currently running in PID: {System.Diagnostics.Process.GetCurrentProcess().Id}"); 以查找。

  2. New Relic 默认会生成易混淆的交易名称,例如Microsoft.Azure.WebJobs.Script.Description.DotNetFunctionInvoker/InvokeCore。我找不到在 XML 文件中设置事务名称的方法。但是,您可以在您的 Azure 函数代码中设置新事务名称,如下所示:

    NewRelic.Api.Agent.NewRelic.SetTransactionName("SyncEvents", "GetRoutineData");

  3. 如果从您的 Run 方法中抛出一个异常并将该异常记录为 New Relic 中的错误,那将是非常好的,但是经过相当多的尝试我无法得到这个工作。我认为这些异常在某个地方被吃掉了,我无法找出正确的跟踪器配置组合来在它们丢失之前拦截它们。但是,您可以使用 NoticeError() 调用: NewRelic.Api.Agent.NewRelic.NoticeError(new SyncException("This is a custom error")); 这似乎是一样的,这些错误将显示在 New Relic 的 ErrorsError analytics 中。缺点是您可能必须重构一堆 Azure 函数代码以捕获所有错误、注意它们,然后在必要时重新抛出它们。

我花了两天时间让它工作,所以我希望这对某人有帮助!!