使用 Azure 表存储进行日志记录





5.00/5 (3投票s)
讨论了如何使用 Azure 表存储来存储 Microsoft Windows Azure 基于云的部署的应用程序的日志信息。
引言
IIS 日志一直是诊断网站问题的通用起点。更有经验的开发人员通常还会使用各种可用的日志解决方案或自定义日志解决方案,在代码中添加特定于应用程序的日志记录。Windows Azure 的基于云的范例为这些日志的存储方式和位置带来了额外的复杂性。本文讨论了如何使用 Azure 表存储实现自定义日志记录。
在 Azure 部署中,已记录的数据需要存储在代码正在运行的 Azure 实例之外。Azure 实例是瞬态实体,它们在有限的时间内运行应用程序的一部分。应将存储在本地实例上的任何数据视为随时可能丢失。有多种技术可用于定期将记录的数据迁移到 Azure 表存储,但由于延迟,这些技术也可能带来潜在的数据丢失风险。直接日志记录到 Azure 表存储可避免此问题。
在部署除最简单的 Azure 应用程序以外的任何内容时,了解云部署固有的多主机性质非常重要。尽管为每个实例设置单独的日志有其好处,但大多数云应用程序分析都将关注整个应用程序性能的整体视图。因此,为 Azure 应用程序的所有实例提供一个通用的日志存储共享位置具有许多优势。如有必要,还可以在日志中标识特定于实例的信息,以帮助诊断。
Azure 表存储是 Microsoft Azure 产品的一部分,它提供了一种廉价、快速、非关系型的基于表模型的存储解决方案。Microsoft 会根据存储的数据量和发生的数据事务数量收费。然而,这些费用非常合理。在撰写本文时,10GB 的存储空间每月费用不到 1 美元,而另外 1 美元可以购买 1000 万次存储事务。显然,这些价格会不断变化,仅用于说明 Azure 表存储的竞争力。当前费用可在 Windows Azure 定价详情中查看。
一旦您的 Azure 存储帐户中存储了日志,就可以直接从您的日志分析软件访问这些日志,或者将其下载到您自己的网络中更方便的位置。需要注意的重要一点是,Azure 存储帐户与您的 Azure 服务完全分开。存储帐户独立于您的服务,并且可以被任何连接到 Internet 的应用程序独立访问,前提是该应用程序拥有适当的私钥,或者存储帐户的某些部分已获得公共访问权限。您的 Azure 应用程序只是存储服务的另一个使用者。
背景
本文中的信息是在我的第一个 Azure 项目中研究的。很快就发现,常用的日志记录到本地文件存储或事件日志的方法无法奏效。
我一直很喜欢过去使用事件日志来存储日志,因为这似乎是一个非常自然的地方来保存这些信息。我通常为每个网站或 Web 服务创建一个新的事件日志,我仍然认为这是一个保存信息的自然位置,并且符合 Windows 操作系统其余部分的理念。因此,我首先尝试让我的新 Azure 服务使用此方法进行日志记录。
事实证明这是一项相当复杂的工作,需要在 Azure 实例中添加启动代码来初始化新的事件日志。通常,在将应用程序部署到 Windows 环境时,我经常通过安装程序进行部署,并让该安装程序初始化事件日志。在 Azure 的情况下,这是不可能的,因为所有内容都在创建 Azure 实例时初始化。此外,在 Azure 部署的整个生命周期中,实例可能会被 Azure 系统替换/重新映像,这意味着每次都需要运行任何初始化代码。这意味着需要将特权启动代码添加到您的 Azure 部署中,以确保每次在 Azure 实例上重新初始化事件日志。
然而,在使上述事件日志记录代码正确工作后,很快就发现这两种基本原因都不是正确的方法。首先,Azure 实例的瞬态性质意味着本地存储日志数据只能被视为临时的。如果 Azure 实例被 Azure 系统移动到另一个虚拟机,您所有的日志数据都将丢失。其次,Azure 部署的性质是一个高度分布式架构。对于分布在多个实例上的网站或 Web 服务,您真正关心的是应用程序级别的日志记录粒度,而不是实例级别的日志记录粒度。
然后,我转向了各种其他解决方案,包括将本地存储定期传输到其他存储位置,最后才确定 Azure 表存储为我的最终解决方案。起初,我一直不愿意使用表存储,因为我知道它有“每笔交易”的费用。然而,一旦我意识到交易的实际成本如此之低,之后似乎几乎是理所当然的。
Using the Code
与 Azure 表存储交互的最简单方法是继承 `Microsoft.WindowsAzure.StorageClient.TableServiceEntity` 类。此类中的 `public` 属性将自动从 Azure 表存储和检索。列将自动生成以匹配每个属性。读取数据时,将简单地忽略没有匹配属性的列。Azure 表列是其中存储的所有行的所有属性的超集。
下面是一个用于存储日志数据的简单类的示例。
public class Message : TableServiceEntity
{
private const string MessagePartionKey = "LogEntry";
private const string DateFormat = "yyyyMMdd ; HH:mm:ss:fffffff";
private const string RowKeyFormat = "{0} - {1}";
public string LogMessage { get; set; }
public Message()
{
}
public Message(string logMessage)
{
PartitionKey = MessagePartionKey;
string date = DateTime.Now.ToUniversalTime().ToString(DateFormat);
RowKey = string.Format(RowKeyFormat, date, Guid.NewGuid().ToString());
LogMessage = logMessage;
}
}
我们必须设置 `TableServiceEntity` 的两个继承属性。`PartitionKey` 标识数据将存储的分区。将所有日志数据保留在同一个分区中是有意义的,因此我们将分区键设置为“LogEntry
”。`RowKey` 是此记录的唯一键,也是行的主(也是唯一)键。为了确保数据按正确的顺序返回,我们使用有序的日期/时间戳作为主键的前缀。但是,因为我们需要保证其唯一性,特别是在多实例云应用程序中,其中数据构造代码很可能在同一时间在多个实例中运行,因此我们在键的后缀加上一个 GUID。
`LogMessage` 属性是我们在这个简单类中使用的唯一其他属性。但是,您可以在此处添加任意数量的其他属性;例如,“Date
”、“Severity
”、“Requested URL
”等。
注意:空的默认构造函数是必需的,以便 Azure 类库在从 Azure 表读取数据时可以创建此类的实例。
要从我们的 Azure 应用程序存储日志消息,我们可以使用以下方法:
public void StoreNewLogMessage(Message logMessage)
{
CloudStorageAccount account;
if (CloudStorageAccount.TryParse(CloudStorageAccountName, out account))
{
CloudTableClient tableClient = account.CreateCloudTableClient();
tableClient.CreateTableIfNotExist(LogName);
TableServiceContext tableContext = tableClient.GetDataServiceContext();
tableContext.AddObject(LogName, logMessage);
tableContext.SaveChangesWithRetries();
}
else
HandleInternalError("Cloud storage could not be opened.", logMessage);
}
上述 Azure 类都包含在 `Microsoft.WindowsAzure.StorageClient` 命名空间中。上述代码假定在其他地方定义了属性或常量来提供
CloudStorageAccountName
(Azure 存储帐户的连接字符串)LogName
(Azure 表存储帐户内日志表的名称)
首先,代码打开到 Azure 表存储帐户的可写连接。上面的 `CloudStorageAccountName` 属性/常量应返回一个 Azure 连接字符串,其中包含具有修改权限的密钥。例如:AccountName=[您的存储帐户名称];AccountKey=[您的主访问密钥];DefaultEndpointsProtocol=https
。
接下来,创建一个 `CloudTableClient` 实例以提供对指定存储帐户中表的访问。`CreateTableIfNotExist` 调用相当直观,它是一种防御性方法,可确保表始终存在。然后,代码创建一个 `TableServiceContext`,该上下文允许访问帐户中的任何表。`AddObject` 调用将给定的对象(继承自 `TableServiceEntity`)添加到指定表中。最后,我们必须使用 `SaveChangesWithRetries` 提交更改。“retries”的名称来自该函数能够应对基于 Internet 的连接固有的问题性质,并在出现暂时性问题时执行多次重试。
该函数以调用另一个函数 `HandleInternalError` 结束,您需要将其实现为一个通用错误处理方法,以指示您的存储帐户出现了严重问题。在我管理的 Azure 网站中,我从这个函数中定期发送 SMTP 电子邮件,作为一种紧急技术,确保发送的电子邮件数量受到限制,以防止垃圾邮件泛滥!
所有上述代码都维护和更新在我用于日志记录的 Azure 表存储页面上:使用 Windows Azure 表进行日志记录。
摘要
总而言之,Azure 表存储是实现基于云的共享日志解决方案的一种极其经济高效的方法。一如既往,有许多现成的解决方案可用于日志功能,但在各种场景中总会有自定义编写解决方案的需求。上述技术应该为任何希望为其 Azure 云部署开发自定义日志解决方案的人提供一个坚实的基础。