使用应用程序见解中的Azure函数中的记忆例外

发布于 2025-01-21 07:11:08 字数 2729 浏览 1 评论 0原文

我有一个队列触发了发送电子邮件/文本的Azure函数。我使用应用程序见解来跟踪自定义事件,以便我可以监视电子邮件,文本和错误的数量。有时,在创建遥测客户端对象时,我会摆脱内存异常。这是我的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。发生在我的遥测者构造函数中。我也在功能末尾处置了我的远程汉汉类(它处理遥控物体对象)。任何帮助都将受到赞赏。

I have a queue triggered azure function that sends emails/texts. I use application insights to track custom events so I can monitor the number of emails, texts, errors. Occasionally I will get out of memory exceptions when creating the Telemetry Client object. Here is my 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(
quot;{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();   
    }
}

Any idea what might be causing the out of memory exception and how to fix? It occurs when there are a large number of messages waiting on the queue (say more than 15), but I have reduced the batch size down to 4. I can't get a stack trace from the log on the error, only that it is occurring in my TelemetryHandler constructor. I am disposing of my TelemetryHandler class at the end of the function as well (which disposes the TelemtryConfiguration object). Any help is appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

感情洁癖 2025-01-28 07:11:08

我已经提出了将我的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-en-us/azure/ azure-functions/functions-dotnet依赖性注入

I've gone with the suggestion of using my TelemetryHandler class as a singleton and using DI to pass it as a parameter. Here is the interface and updated version of the 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(
quot;{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();
    }
}

And here is how it is used as a singleton:

[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
    }
}

For anyone that might need it, I used this article to set up singleton/DI inside an azure function: https://learn.microsoft.com/en-us/azure/azure-functions/functions-dotnet-dependency-injection

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文