System.IO.Log命名空间定义用于登录到面向记录的顺序I/O系统的接口.
可以实现自己的诊断日志记录系统或事务处理系统,以便实现事务持久性.
system.io.log.dll 的默认位置为 %programfiles%\Reference Assemblies\Microsoft\Framework\v3.0\
简单文件日志系统
描述FileRecordSequence类型,该类型是基于文件系统中的单一日志文件的记录序列.它是在IRecordSequence接口在基于简单文件的日志上简单实现.
public sealed class FileRecordSequence : IRecordSequence,IDisposable
例子:
代码
public class MyLog
{
string logName = "test.log";
FileRecordSequence sequence = null;
bool delete = true;
public MyLog()
{
sequence = New FileRecordSequence(logName);
}
public void AppendRecords()
{
SequenceNumber previous = SequenceNumber.Invalid;
previous = sequence.Append(CreateData("Hello Wordl!"), SequenceNumber.Invalid,SequenceNumber.Invalid,RecordAppendOption.ForceFlush);
}
public void ReadRecords()
{
Encoding enc = Encoding.Unicode;
foreach(LogRecord record in this.sequence.ReadLogRecords(this.sequence.BaseSequenceNumber,LogRecordEnumeratorType.Next)
{
byte[] data = new byte[record.Data.Length];
record.Data.Read(data, 0, (int)record.Data.Length);
string mystr = enc.GetString(data);
}
}
public void Cleanup()
{
sequence.Dispose();
if(delete)
{
try{ File.Delete(this.logName); }
catch(Exception e) {}
}
}
//把str转成ArraySegment<byte>数组
public static IList<ArraySegment<byte>> CreateData(string str)
{
Encoding enc = Encoding.Unicode;
byte[] array = enc.GetBytes(str);
ArraySegment<byte>[] segments = new ArraySegment<byte>[1];
segments[0] = new ArraySegment<byte>(array);
return Array.AsReadOnly<ArraySegment<byte>>(segments);
}
}
{
string logName = "test.log";
FileRecordSequence sequence = null;
bool delete = true;
public MyLog()
{
sequence = New FileRecordSequence(logName);
}
public void AppendRecords()
{
SequenceNumber previous = SequenceNumber.Invalid;
previous = sequence.Append(CreateData("Hello Wordl!"), SequenceNumber.Invalid,SequenceNumber.Invalid,RecordAppendOption.ForceFlush);
}
public void ReadRecords()
{
Encoding enc = Encoding.Unicode;
foreach(LogRecord record in this.sequence.ReadLogRecords(this.sequence.BaseSequenceNumber,LogRecordEnumeratorType.Next)
{
byte[] data = new byte[record.Data.Length];
record.Data.Read(data, 0, (int)record.Data.Length);
string mystr = enc.GetString(data);
}
}
public void Cleanup()
{
sequence.Dispose();
if(delete)
{
try{ File.Delete(this.logName); }
catch(Exception e) {}
}
}
//把str转成ArraySegment<byte>数组
public static IList<ArraySegment<byte>> CreateData(string str)
{
Encoding enc = Encoding.Unicode;
byte[] array = enc.GetBytes(str);
ArraySegment<byte>[] segments = new ArraySegment<byte>[1];
segments[0] = new ArraySegment<byte>(array);
return Array.AsReadOnly<ArraySegment<byte>>(segments);
}
}
LogRecordSequence 类提供记录序列接口在公用日志文件系统 (CLFS) 日志之上的实现。除了标准的面向记录的功能,此类还提供一个策略模型,用于避免出现日志满的状况并允许多个客户端复用同一个物理文件。它与 LogStore 类一起使用,该类提供了直接操作和管理 CLFS 日志文件的接口。LogStore 类和 LogRecordSequence 类之间的关系类似于磁盘文件和 FileStream 对象之间的关系。磁盘文件提供实际存储区,并具有长度和上次访问时间等属性,而 FileStream 对象提供文件的视图,可用来在实际存储区中读取和写入数据。与之类似,LogStore 类具有策略和磁盘范围集合等属性;而 LogRecordSequence 类提供面向记录的数据读取和写入机制。
代码
using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.IO.Log;
namespace MyLogRecordSequence
{
public class MyLog
{
string logName = "test.log";
string logContainer = "MyExtent0";
int containerSize = 32 * 1024;
LogRecordSequence sequence = null;
bool delete = true;
// These are used in the TailPinned event handler.
public static LogRecordSequence MySequence = null;
public static bool AdvanceBase = true;
public MyLog()
{
// Create a LogRecordSequence.
sequence = new LogRecordSequence(this.logName,
FileMode.CreateNew,
FileAccess.ReadWrite,
FileShare.None);
// At least one container/extent must be added for Log Record Sequence.
sequence.LogStore.Extents.Add(this.logContainer, this.containerSize);
MySequence = sequence;
}
public void AddExtents()
{
// Add two additional extents. The extents are
// of the same size as the first extent.
sequence.LogStore.Extents.Add("MyExtent1");
sequence.LogStore.Extents.Add("MyExtent2");
}
public void EnumerateExtents()
{
LogStore store = sequence.LogStore;
Console.WriteLine("Enumerating Log Extents...");
Console.WriteLine(" Extent Count: {0} extents", store.Extents.Count);
Console.WriteLine(" Extents Are...");
foreach (LogExtent extent in store.Extents)
{
Console.WriteLine(" {0} ({1}, {2})",
Path.GetFileName(extent.Path),
extent.Size,
extent.State);
}
Console.WriteLine(" Free Extents: {0} Free", store.Extents.FreeCount);
}
public void SetLogPolicy()
{
Console.WriteLine();
Console.WriteLine("Setting current log policy...");
// SET LOG POLICY
LogPolicy policy = sequence.LogStore.Policy;
// Set AutoGrow policy. This enables the log to automatically grow
// when the existing extents are full. New extents are added until
// we reach the MaximumExtentCount extents.
// AutoGrow policy is supported only in Windows Vista and not available in R2.
//policy.AutoGrow = true;
// Set the Growth Rate in terms of extents. This policy specifies
// "how much" the log should grow.
policy.GrowthRate = new PolicyUnit(2, PolicyUnitType.Extents);
// Set the AutoShrink policy. This enables the log to automatically
// shrink if the available free space exceeds the shrink percentage.
// AutoGrow/shrink policy is supported only in Windows Vista and not available in R2.
//policy.AutoShrinkPercentage = new PolicyUnit(30, PolicyUnitType.Percentage);
// Set the PinnedTailThreshold policy.
// A tail pinned event is triggered when there is no
// log space available and log space may be freed by advancing the base.
// The user must handle the tail pinned event by advancing the base of the log.
// If the user is not able to move the base of the log, the user should report with exception in
// the tail pinned handler.
// PinnedTailThreashold policy dictates the amount of space that the TailPinned event requests
// for advancing the base of the log. The amount of space can be in percentage or in terms of bytes
// which is rounded off to the nearest containers in CLFS. The default is 35 percent.
policy.PinnedTailThreshold = new PolicyUnit(10, PolicyUnitType.Percentage);
// Set the maximum extents the log can have.
policy.MaximumExtentCount = 6;
// Set the minimum extents the log can have.
policy.MinimumExtentCount = 2;
// Set the prefix for new containers that are added.
// when AutoGrow is enabled.
//policy.NewExtentPrefix = "MyLogPrefix";
// Set the suffix number for new containers that are added.
// when AutoGrow is enabled.
policy.NextExtentSuffix = 3;
// Commit the log policy.
policy.Commit();
// Refresh updates the IO.Log policy properties with current log policy
// set in the log.
policy.Refresh();
// LOG POLICY END
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Setting up IO.Log provided capabilities...
//
// SET RETRY APPEND
// IO.Log provides a mechanism similar to AutoGrow.
// If the existing log is full and an append fails, setting RetryAppend
// invokes the CLFS policy engine to add new extents and re-tries
// record appends. If MaximumExtent count has been reached,
// a SequenceFullException is thrown.
//
sequence.RetryAppend = true;
// RETRY APPEND END
// REGISTER FOR TAILPINNED EVENT NOTIFICATIONS
// Register for TailPinned Event by passing in an event handler.
// An event is raised when the log full condition is reached.
// The user should either advance the base sequence number to the
// nearest valid sequence number recommended in the tail pinned event or
// report a failure that it is not able to advance the base sequence
// number.
//
sequence.TailPinned += new EventHandler<TailPinnedEventArgs>(HandleTailPinned);
Console.WriteLine("Done...");
}
public void ShowLogPolicy()
{
Console.WriteLine();
Console.WriteLine("Showing current log policy...");
LogPolicy policy = sequence.LogStore.Policy;
Console.WriteLine(" Minimum extent count: {0}", policy.MinimumExtentCount);
Console.WriteLine(" Maximum extent count: {0}", policy.MaximumExtentCount);
Console.WriteLine(" Growth rate: {0}", policy.GrowthRate);
Console.WriteLine(" Pinned tail threshold: {0}", policy.PinnedTailThreshold);
Console.WriteLine(" Auto shrink percent: {0}", policy.AutoShrinkPercentage);
Console.WriteLine(" Auto grow enabled: {0}", policy.AutoGrow);
Console.WriteLine(" New extent prefix: {0}", policy.NewExtentPrefix);
Console.WriteLine(" Next extent suffix: {0}", policy.NextExtentSuffix);
}
// Append records. Appending three records.
public void AppendRecords()
{
Console.WriteLine("Appending Log Records...");
SequenceNumber previous = SequenceNumber.Invalid;
previous = sequence.Append(CreateData("Hello World!"), SequenceNumber.Invalid, SequenceNumber.Invalid, RecordAppendOptions.ForceFlush);
previous = sequence.Append(CreateData("This is my first Logging App"), SequenceNumber.Invalid, SequenceNumber.Invalid, RecordAppendOptions.ForceFlush);
previous = sequence.Append(CreateData("Using LogRecordSequence..."), SequenceNumber.Invalid, SequenceNumber.Invalid, RecordAppendOptions.ForceFlush);
Console.WriteLine("Done...");
}
// Read the records added to the log.
public void ReadRecords()
{
Encoding enc = Encoding.Unicode;
Console.WriteLine();
Console.WriteLine("Reading Log Records...");
try
{
foreach (LogRecord record in this.sequence.ReadLogRecords(this.sequence.BaseSequenceNumber, LogRecordEnumeratorType.Next))
{
byte[] data = new byte[record.Data.Length];
record.Data.Read(data, 0, (int)record.Data.Length);
string mystr = enc.GetString(data);
Console.WriteLine(" {0}", mystr);
}
}
catch (Exception e)
{
Console.WriteLine("Exception {0} {1}", e.GetType(), e.Message);
}
Console.WriteLine();
}
public void FillLog()
{
bool append = true;
while (append)
{
try
{
sequence.Append(CreateData(16 * 1024), SequenceNumber.Invalid, SequenceNumber.Invalid, RecordAppendOptions.ForceFlush);
}
catch (SequenceFullException)
{
Console.WriteLine("Log is Full...");
append = false;
}
}
}
// Dispose the record sequence and delete the log file.
public void Cleanup()
{
// Dispose the sequence
sequence.Dispose();
// Delete the log file.
if (delete)
{
try
{
// This deletes the base log file and all the extents associated with the log.
LogStore.Delete(this.logName);
}
catch (Exception e)
{
Console.WriteLine("Exception {0} {1}", e.GetType(), e.Message);
}
}
}
// Converts the given data to an Array of ArraySegment<byte>
public static IList<ArraySegment<byte>> CreateData(string str)
{
Encoding enc = Encoding.Unicode;
byte[] array = enc.GetBytes(str);
ArraySegment<byte>[] segments = new ArraySegment<byte>[1];
segments[0] = new ArraySegment<byte>(array);
return Array.AsReadOnly<ArraySegment<byte>>(segments);
}
public static IList<ArraySegment<byte>> CreateData(int size)
{
byte[] array = new byte[size];
Random rand = new Random();
rand.NextBytes(array);
ArraySegment<byte>[] segments = new ArraySegment<byte>[1];
segments[0] = new ArraySegment<byte>(array);
return Array.AsReadOnly<ArraySegment<byte>>(segments);
}
public static SequenceNumber GetAdvanceBaseSeqNumber(SequenceNumber recTargetSeqNum)
{
SequenceNumber targetSequenceNumber = SequenceNumber.Invalid;
Console.WriteLine("Getting actual target sequence number...");
//
// Implement the logic for returning a valid sequence number closer to
// recommended target sequence number.
//
return targetSequenceNumber;
}
public static void HandleTailPinned(object arg, TailPinnedEventArgs tailPinnedEventArgs)
{
Console.WriteLine("TailPinned has fired");
// Based on the implementation of a logging application, the log base can be moved
// to free up more log space and if it is not possible to move the
// base, the application should report by throwing an exception.
if(MyLog.AdvanceBase)
{
try
{
// TailPnnedEventArgs has the recommended sequence number and its generated
// based on PinnedTailThreshold policy.
// This does not map to an actual sequence number in the record sequence
// but an approximation and potentially frees up the threshold % log space
// when the log base is advanced to a valid sequence number closer to the
// recommended sequence number.
// The user should use this sequence number to locate a closest valid sequence
// number to advance the base of the log.
SequenceNumber recommendedTargetSeqNum = tailPinnedEventArgs.TargetSequenceNumber;
// Get the actual Target sequence number.
SequenceNumber actualTargetSeqNum = MyLog.GetAdvanceBaseSeqNumber(recommendedTargetSeqNum);
MySequence.AdvanceBaseSequenceNumber(actualTargetSeqNum);
}
catch (Exception e)
{
Console.WriteLine("Exception thrown {0} {1}", e.GetType(), e.Message);
}
}
else
{
// Report back Error if under some conditions the log cannot
// advance the base sequence number.
Console.WriteLine("Reporting Error! Unable to move the base sequence number!");
throw new IOException();
}
}
}
class LogSample
{
static void Main(string[] args)
{
// Create log record sequence.
MyLog log = new MyLog();
// Add additional extents.
log.AddExtents();
// Enumerate the current log extents.
log.EnumerateExtents();
// Set log policies and register for TailPinned event notifications.
log.SetLogPolicy();
log.ShowLogPolicy();
// Append a few records and read the appended records.
log.AppendRecords();
log.ReadRecords();
// Fill the Log to trigger log growth...and subsequent TailPinned notifications.
log.FillLog();
log.EnumerateExtents();
log.Cleanup();
}
}
}
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.IO.Log;
namespace MyLogRecordSequence
{
public class MyLog
{
string logName = "test.log";
string logContainer = "MyExtent0";
int containerSize = 32 * 1024;
LogRecordSequence sequence = null;
bool delete = true;
// These are used in the TailPinned event handler.
public static LogRecordSequence MySequence = null;
public static bool AdvanceBase = true;
public MyLog()
{
// Create a LogRecordSequence.
sequence = new LogRecordSequence(this.logName,
FileMode.CreateNew,
FileAccess.ReadWrite,
FileShare.None);
// At least one container/extent must be added for Log Record Sequence.
sequence.LogStore.Extents.Add(this.logContainer, this.containerSize);
MySequence = sequence;
}
public void AddExtents()
{
// Add two additional extents. The extents are
// of the same size as the first extent.
sequence.LogStore.Extents.Add("MyExtent1");
sequence.LogStore.Extents.Add("MyExtent2");
}
public void EnumerateExtents()
{
LogStore store = sequence.LogStore;
Console.WriteLine("Enumerating Log Extents...");
Console.WriteLine(" Extent Count: {0} extents", store.Extents.Count);
Console.WriteLine(" Extents Are...");
foreach (LogExtent extent in store.Extents)
{
Console.WriteLine(" {0} ({1}, {2})",
Path.GetFileName(extent.Path),
extent.Size,
extent.State);
}
Console.WriteLine(" Free Extents: {0} Free", store.Extents.FreeCount);
}
public void SetLogPolicy()
{
Console.WriteLine();
Console.WriteLine("Setting current log policy...");
// SET LOG POLICY
LogPolicy policy = sequence.LogStore.Policy;
// Set AutoGrow policy. This enables the log to automatically grow
// when the existing extents are full. New extents are added until
// we reach the MaximumExtentCount extents.
// AutoGrow policy is supported only in Windows Vista and not available in R2.
//policy.AutoGrow = true;
// Set the Growth Rate in terms of extents. This policy specifies
// "how much" the log should grow.
policy.GrowthRate = new PolicyUnit(2, PolicyUnitType.Extents);
// Set the AutoShrink policy. This enables the log to automatically
// shrink if the available free space exceeds the shrink percentage.
// AutoGrow/shrink policy is supported only in Windows Vista and not available in R2.
//policy.AutoShrinkPercentage = new PolicyUnit(30, PolicyUnitType.Percentage);
// Set the PinnedTailThreshold policy.
// A tail pinned event is triggered when there is no
// log space available and log space may be freed by advancing the base.
// The user must handle the tail pinned event by advancing the base of the log.
// If the user is not able to move the base of the log, the user should report with exception in
// the tail pinned handler.
// PinnedTailThreashold policy dictates the amount of space that the TailPinned event requests
// for advancing the base of the log. The amount of space can be in percentage or in terms of bytes
// which is rounded off to the nearest containers in CLFS. The default is 35 percent.
policy.PinnedTailThreshold = new PolicyUnit(10, PolicyUnitType.Percentage);
// Set the maximum extents the log can have.
policy.MaximumExtentCount = 6;
// Set the minimum extents the log can have.
policy.MinimumExtentCount = 2;
// Set the prefix for new containers that are added.
// when AutoGrow is enabled.
//policy.NewExtentPrefix = "MyLogPrefix";
// Set the suffix number for new containers that are added.
// when AutoGrow is enabled.
policy.NextExtentSuffix = 3;
// Commit the log policy.
policy.Commit();
// Refresh updates the IO.Log policy properties with current log policy
// set in the log.
policy.Refresh();
// LOG POLICY END
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Setting up IO.Log provided capabilities...
//
// SET RETRY APPEND
// IO.Log provides a mechanism similar to AutoGrow.
// If the existing log is full and an append fails, setting RetryAppend
// invokes the CLFS policy engine to add new extents and re-tries
// record appends. If MaximumExtent count has been reached,
// a SequenceFullException is thrown.
//
sequence.RetryAppend = true;
// RETRY APPEND END
// REGISTER FOR TAILPINNED EVENT NOTIFICATIONS
// Register for TailPinned Event by passing in an event handler.
// An event is raised when the log full condition is reached.
// The user should either advance the base sequence number to the
// nearest valid sequence number recommended in the tail pinned event or
// report a failure that it is not able to advance the base sequence
// number.
//
sequence.TailPinned += new EventHandler<TailPinnedEventArgs>(HandleTailPinned);
Console.WriteLine("Done...");
}
public void ShowLogPolicy()
{
Console.WriteLine();
Console.WriteLine("Showing current log policy...");
LogPolicy policy = sequence.LogStore.Policy;
Console.WriteLine(" Minimum extent count: {0}", policy.MinimumExtentCount);
Console.WriteLine(" Maximum extent count: {0}", policy.MaximumExtentCount);
Console.WriteLine(" Growth rate: {0}", policy.GrowthRate);
Console.WriteLine(" Pinned tail threshold: {0}", policy.PinnedTailThreshold);
Console.WriteLine(" Auto shrink percent: {0}", policy.AutoShrinkPercentage);
Console.WriteLine(" Auto grow enabled: {0}", policy.AutoGrow);
Console.WriteLine(" New extent prefix: {0}", policy.NewExtentPrefix);
Console.WriteLine(" Next extent suffix: {0}", policy.NextExtentSuffix);
}
// Append records. Appending three records.
public void AppendRecords()
{
Console.WriteLine("Appending Log Records...");
SequenceNumber previous = SequenceNumber.Invalid;
previous = sequence.Append(CreateData("Hello World!"), SequenceNumber.Invalid, SequenceNumber.Invalid, RecordAppendOptions.ForceFlush);
previous = sequence.Append(CreateData("This is my first Logging App"), SequenceNumber.Invalid, SequenceNumber.Invalid, RecordAppendOptions.ForceFlush);
previous = sequence.Append(CreateData("Using LogRecordSequence..."), SequenceNumber.Invalid, SequenceNumber.Invalid, RecordAppendOptions.ForceFlush);
Console.WriteLine("Done...");
}
// Read the records added to the log.
public void ReadRecords()
{
Encoding enc = Encoding.Unicode;
Console.WriteLine();
Console.WriteLine("Reading Log Records...");
try
{
foreach (LogRecord record in this.sequence.ReadLogRecords(this.sequence.BaseSequenceNumber, LogRecordEnumeratorType.Next))
{
byte[] data = new byte[record.Data.Length];
record.Data.Read(data, 0, (int)record.Data.Length);
string mystr = enc.GetString(data);
Console.WriteLine(" {0}", mystr);
}
}
catch (Exception e)
{
Console.WriteLine("Exception {0} {1}", e.GetType(), e.Message);
}
Console.WriteLine();
}
public void FillLog()
{
bool append = true;
while (append)
{
try
{
sequence.Append(CreateData(16 * 1024), SequenceNumber.Invalid, SequenceNumber.Invalid, RecordAppendOptions.ForceFlush);
}
catch (SequenceFullException)
{
Console.WriteLine("Log is Full...");
append = false;
}
}
}
// Dispose the record sequence and delete the log file.
public void Cleanup()
{
// Dispose the sequence
sequence.Dispose();
// Delete the log file.
if (delete)
{
try
{
// This deletes the base log file and all the extents associated with the log.
LogStore.Delete(this.logName);
}
catch (Exception e)
{
Console.WriteLine("Exception {0} {1}", e.GetType(), e.Message);
}
}
}
// Converts the given data to an Array of ArraySegment<byte>
public static IList<ArraySegment<byte>> CreateData(string str)
{
Encoding enc = Encoding.Unicode;
byte[] array = enc.GetBytes(str);
ArraySegment<byte>[] segments = new ArraySegment<byte>[1];
segments[0] = new ArraySegment<byte>(array);
return Array.AsReadOnly<ArraySegment<byte>>(segments);
}
public static IList<ArraySegment<byte>> CreateData(int size)
{
byte[] array = new byte[size];
Random rand = new Random();
rand.NextBytes(array);
ArraySegment<byte>[] segments = new ArraySegment<byte>[1];
segments[0] = new ArraySegment<byte>(array);
return Array.AsReadOnly<ArraySegment<byte>>(segments);
}
public static SequenceNumber GetAdvanceBaseSeqNumber(SequenceNumber recTargetSeqNum)
{
SequenceNumber targetSequenceNumber = SequenceNumber.Invalid;
Console.WriteLine("Getting actual target sequence number...");
//
// Implement the logic for returning a valid sequence number closer to
// recommended target sequence number.
//
return targetSequenceNumber;
}
public static void HandleTailPinned(object arg, TailPinnedEventArgs tailPinnedEventArgs)
{
Console.WriteLine("TailPinned has fired");
// Based on the implementation of a logging application, the log base can be moved
// to free up more log space and if it is not possible to move the
// base, the application should report by throwing an exception.
if(MyLog.AdvanceBase)
{
try
{
// TailPnnedEventArgs has the recommended sequence number and its generated
// based on PinnedTailThreshold policy.
// This does not map to an actual sequence number in the record sequence
// but an approximation and potentially frees up the threshold % log space
// when the log base is advanced to a valid sequence number closer to the
// recommended sequence number.
// The user should use this sequence number to locate a closest valid sequence
// number to advance the base of the log.
SequenceNumber recommendedTargetSeqNum = tailPinnedEventArgs.TargetSequenceNumber;
// Get the actual Target sequence number.
SequenceNumber actualTargetSeqNum = MyLog.GetAdvanceBaseSeqNumber(recommendedTargetSeqNum);
MySequence.AdvanceBaseSequenceNumber(actualTargetSeqNum);
}
catch (Exception e)
{
Console.WriteLine("Exception thrown {0} {1}", e.GetType(), e.Message);
}
}
else
{
// Report back Error if under some conditions the log cannot
// advance the base sequence number.
Console.WriteLine("Reporting Error! Unable to move the base sequence number!");
throw new IOException();
}
}
}
class LogSample
{
static void Main(string[] args)
{
// Create log record sequence.
MyLog log = new MyLog();
// Add additional extents.
log.AddExtents();
// Enumerate the current log extents.
log.EnumerateExtents();
// Set log policies and register for TailPinned event notifications.
log.SetLogPolicy();
log.ShowLogPolicy();
// Append a few records and read the appended records.
log.AppendRecords();
log.ReadRecords();
// Fill the Log to trigger log growth...and subsequent TailPinned notifications.
log.FillLog();
log.EnumerateExtents();
log.Cleanup();
}
}
}