Posted by: Cirilo Meggiolaro | 12/6/2008

Tip of the day #53 – Logging data with System.IO.Log namespace

The System.IO.Log namespace provides features for logging information to a sequential system and also provides access to the regular log file system (CLFS) available on both Windows Vista and Windows Server 2003 R2.

When a record is sent to a sequential log file, a unique numeric identifier is attributed to this record. If you create two different sequences, the records are not comparable.

Main objects

IRecordSequence interface: The interface provides operations similar to a stream used to read and write sequential logs. The following methods and properties are available:

Main Operations

  • Append: Adds records to the log;

Overloads:

sequenceFile.Append(ArraySegment<byte>, SequenceNumber, SequenceNumber, RecordAppendOptions);
sequenceFile.Append(IList<ArraySegment<byte>>, SequenceNumber, SequenceNumber, RecordAppendOptions);
sequenceFile.Append(ArraySegment<byte>, SequenceNumber, SequenceNumber, RecordAppendOptions, ReservationCollection);
sequenceFile.Append(IList<ArraySegment<byte>>, SequenceNumber, SequenceNumber, RecordAppendOptions, ReservationCollection);

  • ReadLogRecords: Reads records from the log;

Overloads:

sequenceFile.ReadLogRecords(SequenceNumber, LogRecordEnumeratorType);

  • WriteRestartArea: Writes a restart area to the log;
  • ReadRestartArea: Reads a record from the log;
  • Flush: Sends the records to the log.

Properties

  • BaseSequenceNumber: sequence number of the first record;
  • LastSequenceNumber: sequence number that is larger than the last record identifier from the sequence;
  • RestartSequenceNumber: sequence number of the last written restart area;
  • MaximumRecordLength: size of the largest record from the sequence;
  • ReservedBytes: total size of all reservations in this sequence;
  • RetryAppend: defines a Boolean value that if set to true the sequence tries to free space if the Append method fails.

FileRecordSequence class

The FileRecordSequence class provides features to read and write records to a single log file. The process would be pretty straightforward if you would not have to convert the strings appended to the record file. The Append method expects a list of byte array segments.

Full Example

static void Main(string[] args)
{
  /// Declares a file record sequence.
  FileRecordSequence sequenceFile = null;

  /// Stores the log fine name.
  string logFileName = “example.log”;

  try
  {
    /// Creates a new instance of a
    /// file record sequence object.
    sequenceFile = new FileRecordSequence(logFileName, FileAccess.ReadWrite);

    /// Defines the first sequence number
    SequenceNumber previousRecord = SequenceNumber.Invalid;

    /// Writes records to the log file.
    previousRecord = sequenceFile.Append(CreateData(“First record.”), SequenceNumber.Invalid, previousRecord, RecordAppendOptions.ForceFlush);
    previousRecord = sequenceFile.Append(CreateData(“Second record.”), SequenceNumber.Invalid, previousRecord, RecordAppendOptions.ForceFlush);
    previousRecord = sequenceFile.Append(CreateData(“Third record.”), SequenceNumber.Invalid, previousRecord, RecordAppendOptions.ForceFlush);
    previousRecord = sequenceFile.Append(CreateData(“Fourth record.”), SequenceNumber.Invalid, previousRecord, RecordAppendOptions.ForceFlush);

    /// Loop through records available
    /// in the file record sequence object.
    foreach (LogRecord record in sequenceFile.ReadLogRecords(sequenceFile.BaseSequenceNumber, LogRecordEnumeratorType.Next))
    {
      Console.WriteLine(ReadData(record));
    }
  }
   catch (Exception ex)
  {
    Console.WriteLine(“Error: “ + ex.Message);
  }
  finally
  {
    /// Releases resources
    if (sequenceFile != null)
    {
      sequenceFile.Dispose();
      sequenceFile = null;
    }
  }
}

private static string ReadData(LogRecord record)
{
  /// Creates the encoding object.
  Encoding enc = Encoding.Unicode;

  /// Creates an array of bytes.
  byte[] recordData = new byte[record.Data.Length];

  /// Populates the array of bytes
  /// with the record content.
  record.Data.Read(recordData, 0, (int)record.Data.Length);

  /// Returns a string representation
  /// from the array of bytes content.
  return enc.GetString(recordData);
}

private static IList<ArraySegment<byte>> CreateData(string str)
{
  /// Creates the encoding object.
  Encoding enc = Encoding.Unicode;

  /// Gets bytes for the entry text.
  byte[] textArray = enc.GetBytes(str);

  /// Creates an array segment.
  ArraySegment<byte>[] segments = new ArraySegment<byte>[1];

  /// Populates the array segment
  /// with the text array.
  segments[0] = new ArraySegment<byte>(textArray);

  /// Returns a list of byte array segments
  return Array.AsReadOnly<ArraySegment<byte>>(segments);
}

LogRecordSequence class

Differently from the FileRecordSequence class that works on top of a single file, the LogRecordSequence class reads from and writes information to a LogStore class that provides features to manage the logs on the Common File Log System (CFLS).

Imagine the log record sequence as a storage place where you may want to store some different containers, with different sizes. These containers are known as Extents.

Example:

static void Main(string[] args)
{
  /// Declares a file record sequence.
  LogRecordSequence sequenceLog = null;

  /// Stores the log fine name.
  string logFileName = “example.log”;

  try
  {
    /// Creates a new instance of a log record sequence object.
    sequenceLog = new LogRecordSequence(logFileName, FileMode.OpenOrCreate);

    /// Creates a container / extent, defining the name and size of it.
    sequenceLog.LogStore.Extents.Add(“MyContainer”, 32 * 1024);

    /// If set to true then the CFLS will create
    /// a new container if the log is full and
    /// the Append fails.
    sequenceLog.RetryAppend = true;

    /// Registers the event handler that
    /// is raised when the log is full.
    sequenceLog.TailPinned += new EventHandler<TailPinnedEventArgs>(sequenceLog_TailPinned);

    /// Defines the first sequence number
    SequenceNumber previousRecord = SequenceNumber.Invalid;

    /// Writes records to the log file.
    previousRecord = sequenceLog.Append(CreateData(“First record.”), SequenceNumber.Invalid, previousRecord, RecordAppendOptions.ForceFlush);
    previousRecord = sequenceLog.Append(CreateData(“Second record.”), SequenceNumber.Invalid, previousRecord, RecordAppendOptions.ForceFlush);
    previousRecord = sequenceLog.Append(CreateData(“Third record.”), SequenceNumber.Invalid, previousRecord, RecordAppendOptions.ForceFlush);
    previousRecord = sequenceLog.Append(CreateData(“Fourth record.”), SequenceNumber.Invalid, previousRecord, RecordAppendOptions.ForceFlush);

    /// Loop through records available
    /// in the file record sequence object.
    foreach (LogRecord record in sequenceLog.ReadLogRecords(sequenceLog.BaseSequenceNumber, LogRecordEnumeratorType.Next))
    {
      Console.WriteLine(ReadData(record));
    }
  }
   catch (Exception ex)
  {
    Console.WriteLine(“Error: “ + ex.Message);
  }
  finally
  {
    /// Releases resources
    if (sequenceLog != null)
    {
      sequenceLog.Dispose();
      sequenceLog = null;
    }
  }
}

static void sequenceLog_TailPinned(object sender, TailPinnedEventArgs e)
{
   /// Do something
}

private static string ReadData(LogRecord record)
{
  /// Creates the encoding object.
  Encoding enc = Encoding.Unicode;

  /// Creates an array of bytes.
  byte[] recordData = new byte[record.Data.Length];

  /// Populates the array of bytes
  /// with the record content.
  record.Data.Read(recordData, 0, (int)record.Data.Length);

  /// Returns a string representation
  /// from the array of bytes content.
  return enc.GetString(recordData);
}

private static IList<ArraySegment<byte>> CreateData(string str)
{
  /// Creates the encoding object.
  Encoding enc = Encoding.Unicode;

  /// Gets bytes for the entry text.
  byte[] textArray = enc.GetBytes(str);

  /// Creates an array segment.
  ArraySegment<byte>[] segments = new ArraySegment<byte>[1];

  /// Populates the array segment
  /// with the text array.
  segments[0] = new ArraySegment<byte>(textArray);

  /// Returns a list of byte array segments
  return Array.AsReadOnly<ArraySegment<byte>>(segments);
}

LogExtent class

The LogExtent class represents the container that is added to the log store. A log store object may contain more than one extent. The constructor accepts a name and the size.

LogPolicy Class

The LogPolicy class exposes properties that define how to handle scenarios like when a log is full, shrink options and so on. The following code snippet describes the main properties:

  /// Adds the log store policy class to the new log policy object.
  policy = sequenceLog.LogStore.Policy;

  /// Available only on Windows Vista
  policy.AutoGrow = true;

  /// Available only on Windows Vista.
  policy.AutoShrinkPercentage = 30;

  /// Defines the amount of space that
  /// will be requested to be released
  /// when the log is full.
  policy.PinnedTailThreshold = new PolicyUnit(10, PolicyUnitType.Percentage);

  /// Defines the minimum number of
  /// containers the store can have.
  policy.MinimumExtentCount = 2;

  /// Defines the maximum number of
  /// containers the store can have.
  policy.MaximumExtentCount = 6;

  /// Applies the policy
  policy.Commit();

  /// Refresh the properties.
  policy.Refresh();

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories

%d bloggers like this: