使用 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
我有一个发送 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