Windows Azure - очистка WADLogsTable
Я прочитал противоречивую информацию о том, будет ли таблица WADLogsTable, используемая DiagnosticMonitor в Windows Azure, автоматически обрезать старые записи журнала.
Я предполагаю, что это не так, и вместо этого будет расти вечно - стоило бы мне денег.:)
Если это так, у кого-нибудь есть хороший пример кода, как вручную очистить старые записи журнала из этой таблицы? Возможно, на основе временной метки? Я периодически запускаю этот код из рабочей роли.
Ответы
Ответ 1
Данные в таблицах, созданных с помощью Windows Azure Diagnostics, не удаляются автоматически.
Однако Командлеты Windows Azure PowerShell содержат командлеты специально для этого случая.
PS D: \ > help Clear-WindowsAzureLog
NAME Clear-WindowsAzureLog
СИНТАКСИС Удаляет данные журнала трассировки Windows Azure из учетной записи хранилища.
СИНТАКСИС 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 внутри рабочей роли, код командлетов С# можно повторно использовать. Командлеты PowerShell публикуются под разрешительной публичной лицензией MS.
В принципе, есть только 3 файла, необходимые без других внешних зависимостей: DiagnosticsOperationException.cs, WadTableExtensions.cs, WadTableServiceEntity.cs.
Ответ 2
Обновлена функция 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);
}
}
}
Ответ 3
Вы могли бы просто сделать это на основе метки времени, но это было бы очень неэффективно, так как вся таблица должна быть отсканирована. Вот пример кода, который может помочь, когда создается ключ раздела, чтобы предотвратить "полное" сканирование таблицы. 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
Ответ 4
Вот решение, которое транслируется на основе метки времени. (Протестировано против 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");
}
}
Ответ 5
Вот моя немного другая версия решения @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();
Ответ 6
Если вы не заботитесь о каком-либо содержимом, просто удалите таблицу. Azure Diagnostics просто воссоздает его.