c# - 使用 Application Insights 的 Azure Function 中出现内存不足异常

标签 c# azure visual-studio

我有一个队列触发的 azure 功能,用于发送电子邮件/文本。我使用应用程序洞察来跟踪自定义事件,以便我可以监控电子邮件、文本和错误的数量。创建 Telemetry Client 对象时有时会出现内存不足的异常。这是我的 TelemetryHandler.cs 类:

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 类(它处理 TelemtryConfiguration 对象)。如有任何帮助,我们将不胜感激。

最佳答案

我接受了使用 TelemetryHandler 类作为单例并使用 DI 将其作为参数传递的建议。这是该类的界面和更新版本:

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://learn.microsoft.com/en-us/azure/azure-functions/functions-dotnet-dependency-injection

关于c# - 使用 Application Insights 的 Azure Function 中出现内存不足异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71875795/

相关文章:

C# 检查用户输入的特定文件名并打开文件

visual-studio - 是否可以在VS IDE中进行构建时进行检测?

c# - WPF:使用 MVVM 时更改数据绑定(bind)

mongodb - 是否可以在cosmos mongodb中的多重嵌套字段上创建索引?

c# - 加密文件并将其发送通过

c# - WebJob ServiceBus 主题权限

asp.net-mvc - 在 Azure Web 服务发布期间预编译

c++ - 在mfc中如何实现可停靠对话框?

c# - 使用构建后操作创建 nuget 包

c# - 为所有实体设置级联删除