Windows Azure - очистка таблицы WADLogs
Я прочитал противоречивую информацию о том, будет ли таблица WADLogsTable, используемая DiagnosticMonitor в Windows Azure, автоматически удалять старые записи журнала.
Я предполагаю, что это не так, а вместо этого будет расти вечно - стоить мне денег.:)
Если это так, есть ли у кого-нибудь хороший пример кода, как вручную удалить старые записи журнала из этой таблицы? Возможно, основываясь на отметке времени? Я бы периодически запускал этот код из рабочей роли.
6 ответов
Данные в таблицах, созданных Windows Azure Diagnostics, не удаляются автоматически.
Однако командлеты Windows Azure PowerShell содержат командлеты специально для этого случая.
PS D: \> помогите очистить-WindowsAzureLog
NAME Clear-WindowsAzureLog
ОПИСАНИЕ Удаляет данные журнала трассировки Windows Azure из учетной записи хранения.
SYNTAX Clear-WindowsAzureLog [-DeploymentId] [-From] [-To] [-StorageAccountName] [-StorageAccountKey] [-UseD evelopmentStorage] [-StorageAccountCredentials] []
Clear-WindowsAzureLog [-DeploymentId <String>] [-FromUtc <DateTime>] [-ToUt c <DateTime>] [-StorageAccountName <String>] [-StorageAccountKey <String>] [-UseDevelopmentStorage] [-StorageAccountCredentials <StorageCredentialsAcc ountAndKey>] [<CommonParameters>]
Необходимо указать параметр -ToUtc, и все журналы до этой даты будут удалены.
Если в Azure необходимо выполнить задачу очистки в рабочей роли, код командлетов C# можно использовать повторно. Командлеты PowerShell публикуются на условиях разрешительной публичной лицензии MS.
По сути, только 3 файла необходимы без других внешних зависимостей: DiagnosticsOperationException.cs, WadTableExtensions.cs, WadTableServiceEntity.cs.
Обновлена функция Chriseyre2000. Это обеспечивает гораздо большую производительность в тех случаях, когда вам нужно удалить многие тысячи записей: поиск по PartitionKey и пошаговый пошаговый процесс. И помните, что лучший выбор - запускать его рядом с хранилищем (в облачном сервисе).
public static void TruncateDiagnostics(CloudStorageAccount storageAccount,
DateTime startDateTime, DateTime finishDateTime, Func<DateTime,DateTime> stepFunction)
{
var cloudTable = storageAccount.CreateCloudTableClient().GetTableReference("WADLogsTable");
var query = new TableQuery();
var dt = startDateTime;
while (true)
{
dt = stepFunction(dt);
if (dt>finishDateTime)
break;
var l = dt.Ticks;
string partitionKey = "0" + l;
query.FilterString = TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.LessThan, partitionKey);
query.Select(new string[] {});
var items = cloudTable.ExecuteQuery(query).ToList();
const int chunkSize = 200;
var chunkedList = new List<List<DynamicTableEntity>>();
int index = 0;
while (index < items.Count)
{
var count = items.Count - index > chunkSize ? chunkSize : items.Count - index;
chunkedList.Add(items.GetRange(index, count));
index += chunkSize;
}
foreach (var chunk in chunkedList)
{
var batches = new Dictionary<string, TableBatchOperation>();
foreach (var entity in chunk)
{
var tableOperation = TableOperation.Delete(entity);
if (batches.ContainsKey(entity.PartitionKey))
batches[entity.PartitionKey].Add(tableOperation);
else
batches.Add(entity.PartitionKey, new TableBatchOperation {tableOperation});
}
foreach (var batch in batches.Values)
cloudTable.ExecuteBatch(batch);
}
}
}
Вы можете просто сделать это на основе метки времени, но это будет очень неэффективно, так как всю таблицу нужно будет сканировать. Вот пример кода, который может помочь при создании ключа раздела для предотвращения "полного" сканирования таблицы. http://blogs.msdn.com/b/avkashchauhan/archive/2011/06/24/linq-code-to-query-windows-azure-wadlogstable-to-get-rows-which-are-stored-after-a-specific-datetime.aspx
Вот моя немного другая версия решения @Chriseyre2000, использующая асинхронные операции и запросы PartitionKey. В моем случае он предназначен для непрерывной работы в рамках рабочей роли. Это может быть немного проще в памяти, если у вас есть много записей для очистки.
static class LogHelper
{
/// <summary>
/// Periodically run a cleanup task for log data, asynchronously
/// </summary>
public static async void TruncateDiagnosticsAsync()
{
while ( true )
{
try
{
// Retrieve storage account from connection-string
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(
CloudConfigurationManager.GetSetting( "CloudStorageConnectionString" ) );
CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
CloudTable cloudTable = tableClient.GetTableReference( "WADLogsTable" );
// keep a weeks worth of logs
DateTime keepThreshold = DateTime.UtcNow.AddDays( -7 );
// do this until we run out of items
while ( true )
{
TableQuery query = new TableQuery();
query.FilterString = string.Format( "PartitionKey lt '0{0}'", keepThreshold.Ticks );
var items = cloudTable.ExecuteQuery( query ).Take( 1000 );
if ( items.Count() == 0 )
break;
Dictionary<string, TableBatchOperation> batches = new Dictionary<string, TableBatchOperation>();
foreach ( var entity in items )
{
TableOperation tableOperation = TableOperation.Delete( entity );
// need a new batch?
if ( !batches.ContainsKey( entity.PartitionKey ) )
batches.Add( entity.PartitionKey, new TableBatchOperation() );
// can have only 100 per batch
if ( batches[entity.PartitionKey].Count < 100)
batches[entity.PartitionKey].Add( tableOperation );
}
// execute!
foreach ( var batch in batches.Values )
await cloudTable.ExecuteBatchAsync( batch );
Trace.TraceInformation( "WADLogsTable truncated: " + query.FilterString );
}
}
catch ( Exception ex )
{
Trace.TraceError( "Truncate WADLogsTable exception {0}", ex.Message );
}
// run this once per day
await Task.Delay( TimeSpan.FromDays( 1 ) );
}
}
}
Чтобы начать процесс, просто вызовите его из метода OnStart в вашей рабочей роли.
// start the periodic cleanup
LogHelper.TruncateDiagnosticsAsync();
Вот решение, которое пересекается на основе метки времени. (Протестировано против SDK 2.0)
Он использует сканирование таблицы, чтобы получить данные, но если запустить, скажем, один раз в день, не будет слишком болезненным:
/// <summary>
/// TruncateDiagnostics(storageAccount, DateTime.Now.AddHours(-1));
/// </summary>
/// <param name="storageAccount"></param>
/// <param name="keepThreshold"></param>
public void TruncateDiagnostics(CloudStorageAccount storageAccount, DateTime keepThreshold)
{
try
{
CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
CloudTable cloudTable = tableClient.GetTableReference("WADLogsTable");
TableQuery query = new TableQuery();
query.FilterString = string.Format("Timestamp lt datetime'{0:yyyy-MM-ddTHH:mm:ss}'", keepThreshold);
var items = cloudTable.ExecuteQuery(query).ToList();
Dictionary<string, TableBatchOperation> batches = new Dictionary<string, TableBatchOperation>();
foreach (var entity in items)
{
TableOperation tableOperation = TableOperation.Delete(entity);
if (!batches.ContainsKey(entity.PartitionKey))
{
batches.Add(entity.PartitionKey, new TableBatchOperation());
}
batches[entity.PartitionKey].Add(tableOperation);
}
foreach (var batch in batches.Values)
{
cloudTable.ExecuteBatch(batch);
}
}
catch (Exception ex)
{
Trace.TraceError(string.Format("Truncate WADLogsTable exception {0}", ex), "Error");
}
}
Если вас не волнует какое-либо содержимое, просто удалите таблицу. Диагностика Azure просто воссоздает его.
Немного обновленный код Chriseyre2000:
с использованием ExecuteQuerySegmented вместо ExecuteQuery
соблюдение лимита TableBatchOperation в 100 операций
очистка всех таблиц Azure
public static void TruncateAllAzureTables(CloudStorageAccount storageAccount, DateTime keepThreshold) { TruncateAzureTable(storageAccount, "WADLogsTable", keepThreshold); TruncateAzureTable(storageAccount, "WADCrashDump", keepThreshold); TruncateAzureTable(storageAccount, "WADDiagnosticInfrastructureLogsTable", keepThreshold); TruncateAzureTable(storageAccount, "WADPerformanceCountersTable", keepThreshold); TruncateAzureTable(storageAccount, "WADWindowsEventLogsTable", keepThreshold); } public static void TruncateAzureTable(CloudStorageAccount storageAccount, string aTableName, DateTime keepThreshold) { const int maxOperationsInBatch = 100; var tableClient = storageAccount.CreateCloudTableClient(); var cloudTable = tableClient.GetTableReference(aTableName); var query = new TableQuery { FilterString = $"Timestamp lt datetime'{keepThreshold:yyyy-MM-ddTHH:mm:ss}'" }; TableContinuationToken continuationToken = null; do { var queryResult = cloudTable.ExecuteQuerySegmented(query, continuationToken); continuationToken = queryResult.ContinuationToken; var items = queryResult.ToList(); var batches = new Dictionary<string, List<TableBatchOperation>>(); foreach (var entity in items) { var tableOperation = TableOperation.Delete(entity); if (!batches.TryGetValue(entity.PartitionKey, out var batchOperationList)) { batchOperationList = new List<TableBatchOperation>(); batches.Add(entity.PartitionKey, batchOperationList); } var batchOperation = batchOperationList.FirstOrDefault(bo => bo.Count < maxOperationsInBatch); if (batchOperation == null) { batchOperation = new TableBatchOperation(); batchOperationList.Add(batchOperation); } batchOperation.Add(tableOperation); } foreach (var batch in batches.Values.SelectMany(l => l)) { cloudTable.ExecuteBatch(batch); } } while (continuationToken != null); }