使用 Application Insights 在 Azure Function 中出现内存不足异常

Out of Memory Exception in Azure Function using Application Insights

我有一个发送 emails/texts 的队列触发 azure 函数。我使用 application insights 来跟踪自定义事件,这样我就可以监控电子邮件、文本和错误的数量。偶尔我会在创建 Telemetry Client 对象时出现内存不足异常。这是我的 TelemetryHandler.cs class:

internal class TelemetryHandler : IDisposable
{
    private const string EmailSentEvent = "Email Sent";
    private const string EmailFailedEvent = "Email Failure";
    private const string TextSentEvent = "Text Sent";
    private const string ErrorEvent = "Error";
    private readonly TelemetryClient telemetryClient;
    private readonly TelemetryConfiguration telemetryConfiguration;
    private readonly Dictionary<string, string> properties;
    private readonly Dictionary<string, double> metrics;

    public TelemetryHandler()
    {
        telemetryConfiguration = new(Environment.GetEnvironmentVariable("APPINSIGHTS_INSTRUMENTATIONKEY"));
        telemetryClient = new TelemetryClient(telemetryConfiguration);
        metrics = new();
        properties = new();
    }

    public void InitializeFromMessage(EmailTextMessage emailTextMessage)
    {
        properties.Add("Tenant ID", emailTextMessage.TenantID);
        properties.Add("User", emailTextMessage.User);
        properties.Add("Payload", emailTextMessage.Payload.ToString());
    }

    public void FinalizeSendEmailEvent(string messageID)
    {
        properties.Add("Postmark Message ID", messageID);
        metrics.Add("Emails", 1);
        telemetryClient.TrackEvent(EmailSentEvent, properties, metrics);
    }

    public void FinalizeSendTextEvent(string sid)
    {
        properties.Add("Twilio Message Sid", sid);
        metrics.Add("Texts", 1);
        telemetryClient.TrackEvent(TextSentEvent, properties, metrics);
    }

    public void FinalizeSendEmailFailure(string errorMessage)
    {
        properties.Add("Error Message", errorMessage);
        metrics.Add("Failed", 1);
        telemetryClient.TrackEvent(EmailFailedEvent, properties, metrics);
    }

    public void FinalizeErrorEvent(Exception ex)
    {
        StringBuilder message = new(ex.Message);
        Exception exception = ex;
        while(exception.InnerException != null)
        {
            message.Append($"{Environment.NewLine}{exception.InnerException.Message}");
            exception = exception.InnerException;
        }
        properties.Add("Message", message.ToString());
        properties.Add("Stack Trace", ex.StackTrace);
        metrics.Add("Error", 1);
        telemetryClient.TrackEvent(ErrorEvent, properties, metrics);
    }

    public void Dispose()
    {
        if(telemetryConfiguration != null)
            telemetryConfiguration.Dispose();   
    }
}

知道什么可能导致内存不足异常以及如何修复吗?它发生在队列中有大量消息等待(比如超过 15 条)时,但我已将批处理大小减少到 4。我无法从错误日志中获取堆栈跟踪,只是它发生在我的 TelemetryHandler 构造函数中。我也在函数末尾处理我的 TelemetryHandler class(它处理 TelemtryConfiguration 对象)。感谢任何帮助。

我已经接受了将我的 TelemetryHandler class 用作单例并使用 DI 将其作为参数传递的建议。这是 class:

的界面和更新版本
internal interface ITelemetryHandler
{
    void FinalizeSendEmailEvent(string tenantID, string user, EmailMessage emailMessage, string messageID);
    void FinalizeSendEmailFailure(string tenantID, string user, EmailMessage emailMessage, string errorMessage);
    void FinalizeSendTextEvent(string tenantID, string user, TextMessage textMessage, string sid);
    void FinalizeErrorEvent(EmailTextMessage emailTextMessage, Exception ex);
}
internal class TelemetryHandler : ITelemetryHandler, IDisposable
{
    private const string EmailSentEvent = "Email Sent";
    private const string EmailFailedEvent = "Email Failure";
    private const string TextSentEvent = "Text Sent";
    private const string ErrorEvent = "Error";
    private readonly TelemetryClient telemetryClient;
    private readonly TelemetryConfiguration telemetryConfiguration;

    public TelemetryHandler()
    {
        telemetryConfiguration = new(Environment.GetEnvironmentVariable("APPINSIGHTS_INSTRUMENTATIONKEY"));
        telemetryClient = new TelemetryClient(telemetryConfiguration);
    }

    public void FinalizeSendEmailEvent(string tenantID, string user, EmailMessage emailMessage, string messageID)
    {
        Dictionary<string, string> properties = new();
        Dictionary<string, double> metrics = new();
        properties.Add("Tenant ID", tenantID);
        properties.Add("User", user);
        properties.Add("Payload", JsonConvert.SerializeObject(emailMessage));
        properties.Add("Postmark Message ID", messageID);
        metrics.Add("Emails", 1);
        telemetryClient.TrackEvent(EmailSentEvent, properties, metrics);
    }

    public void FinalizeSendTextEvent(string tenantID, string user, TextMessage textMessage, string sid)
    {
        Dictionary<string, string> properties = new();
        Dictionary<string, double> metrics = new();
        properties.Add("Tenant ID", tenantID);
        properties.Add("User", user);
        properties.Add("Payload", JsonConvert.SerializeObject(textMessage));
        properties.Add("Twilio Message Sid", sid);
        metrics.Add("Texts", 1);
        telemetryClient.TrackEvent(TextSentEvent, properties, metrics);
    }

    public void FinalizeSendEmailFailure(string tenantID, string user, EmailMessage message, string errorMessage)
    {
        Dictionary<string, string> properties = new();
        Dictionary<string, double> metrics = new();
        properties.Add("Tenant ID", tenantID);
        properties.Add("User", user);
        properties.Add("Payload", message.ToString());
        properties.Add("Error Message", errorMessage);
        metrics.Add("Failed", 1);
        telemetryClient.TrackEvent(EmailFailedEvent, properties, metrics);
    }

    public void FinalizeErrorEvent(EmailTextMessage emailTextMessage, Exception ex)
    {
        Dictionary<string, string> properties = new();
        Dictionary<string, double> metrics = new();
        if (emailTextMessage != null)
        {
            properties.Add("Tenant ID", emailTextMessage.TenantID);
            properties.Add("User", emailTextMessage.User);
            properties.Add("Payload", emailTextMessage.Payload.ToString());
        }
        StringBuilder message = new(ex.Message);
        Exception exception = ex;
        while (exception.InnerException != null)
        {
            message.Append($"{Environment.NewLine}{exception.InnerException.Message}");
            exception = exception.InnerException;
        }
        properties.Add("Message", message.ToString());
        properties.Add("Stack Trace", ex.StackTrace);
        metrics.Add("Error", 1);
        telemetryClient.TrackEvent(ErrorEvent, properties, metrics);
    }

    public void Dispose()
    {
        if (telemetryConfiguration != null)
            telemetryConfiguration.Dispose();
    }
}

下面是它作为单例的用法:

[assembly: FunctionsStartup(typeof(FWT.EmailText.Startup))]

namespace FWT.EmailText;

class Startup : FunctionsStartup
{
    public override void Configure(IFunctionsHostBuilder builder)
    {
        builder.Services.AddSingleton<ITelemetryHandler>((s) => {
            return new TelemetryHandler();
        });
    }
}
class EmailTextHandler
{
    private readonly ITelemetryHandler telemetryHandler;

    public EmailTextHandler(ITelemetryHandler telemetryHandler)
    {
        this.telemetryHandler = telemetryHandler;
    }

    [FunctionName("EmailTextHandler")]
    public async Task Run([QueueTrigger("%QueueName%", Connection = "QueueStorageAccount")] string queueMessage, ILogger log)
    {
        //Function code that accesses telemetryHandler
    }
}

对于可能需要它的任何人,我使用这篇文章在 azure 函数中设置 singleton/DI:https://docs.microsoft.com/en-us/azure/azure-functions/functions-dotnet-dependency-injection