使用Application Insights的Azure函数中出现内存不足异常



我有一个队列触发的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类(它处理TelemetryConfiguration对象(。感谢您的帮助。

我建议将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

最新更新