Share via


Agregar capacidades de registro a una aplicación

En este tema se describe cómo puede utilizar las funcionalidades proporcionadas por System.IO.Log para agregar capacidades de registro a su aplicación.

FileRecordSequence o LogRecordSequence

Para elegir un registro basado en archivos simple y un registro basado en el Sistema de Archivos de Registro Común (CLFS), debe considerar los cuatro criterios siguientes: compatibilidad con la plataforma, variedad de características, solidez y rendimiento.

Los registros basados en archivos simples están disponibles en todas las plataformas con las cuales System.IO.Log es compatible, mientras que los registros basados en CLFS sólo están disponibles en las plataformas Windows Server 2003 R2 y Windows Vista. Además, algunas características de CLFS están deshabilitadas en Windows Server 2003 R2. Esto puede afectar al modo en que su aplicación administra condiciones concretas como el control de crecimiento automático. Para obtener más información sobre esas limitaciones, vea LogRecordSequence.

Directiva y multiplexación son dos características que contribuyen a la variedad de características de la clase LogRecordSequence basada en CLFS, comparada con la clase FileRecordSequence basada en archivos simple. Establecer las directivas mediante la estructura LogPolicy permite un control muy minucioso sobre las características de mantenimiento, como el crecimiento y la reducción automáticos del tamaño del registro, los contenedores de tamaño mínimo y de tamaño máximo y los umbrales anclados por cola. Estas características también permiten la creación de registros circulares utilizando la clase LogRecordSequence basada en CLFS. La multiplexación, la capacidad de manipular las secuencias de archivo de NTFS, puede mejorar el rendimiento y la comodidad dentro de una aplicación única o en varias aplicaciones que funcionan juntas como una unidad. Por lo tanto, las aplicaciones diseñadas para escenarios de ejecución prolongada o rendimiento muy alto son las que más se benefician de utilizar la clase LogRecordSequence, en contraste con la clase FileRecordSequence.

Además, CLFS proporciona ventajas de solidez y rendimiento. CLFS está diseñado para ser utilizado por aplicaciones de alto rendimiento o en un entorno de la empresa. Si las restricciones de aplicación permiten la ejecución en una plataforma compatible con CLFS, la clase LogRecordSequence no sólo permitirá más opciones al controlar el mantenimiento del archivo de registro mediante la clase LogPolicy, sino que también llevará a un rendimiento de E/S mejorado.

Clases principales en System.IO.Log

Lo siguientes son tres de las clases más importantes en System.IO.Log. Para obtener más información sobre su uso, vea su documentación de referencia respectiva.

Utilizar System.IO.Log

Abrir un registro y agregar las extensiones

Abrir un registro y agregar las extensiones es normalmente la primera tarea para agregar capacidades de registro a su aplicación. Observe que sólo se pueden agregar extensiones al utilizar la clase LogRecordSequence basada en CLFS.

Debería considerar la ubicación del registro, así como el número y tamaño de las extensiones que se van a agregar al registro inicialmente. Sólo la cuenta de usuario bajo la cual se está ejecutando la aplicación limita la ubicación de almacenamiento. Una ubicación válida puede estar en cualquier parte del sistema local donde este usuario tenga acceso de escritura. Sin embargo, el número de extensiones y su tamaño requieren una consideración más específica de la aplicación. Al agregar la extensión inicial al registro, se debe proporcionar un tamaño. Este tamaño se utiliza para todas las extensiones adicionales agregadas al registro, tanto manualmente como a través del comportamiento de crecimiento automático. Además, para aprovechar muchas características proporcionadas por la clase LogPolicy, al menos dos extensiones deben estar presentes en todo momento para una secuencia determinada.

El ejemplo siguiente muestra cómo crear un registro y agregarle dos contenedores de extensión de 1MB.

LogRecordSequence recordSequence = new LogRecordSequence("application.log", FileMode.CreateNew);
recordSequence.LogStore.Extents.Add("app_extent0", 1024 * 1024);
recordSequence.LogStore.Extents.Add("app_extent1");

Establecer una directiva

Establecer directivas de registro mediante la estructura LogPolicy debería ser la segunda tarea en el desarrollo de su aplicación de registro. Observe que esta tarea sólo se puede realizar al utilizar la clase LogRecordSequence basada en CLFS y que se debe establecer cada vez que se abre un registro porque no se conservan las directivas.

Una directiva contiene opciones importantes como recuento de la extensión máxima y crecimiento automático. A lo largo del curso de la duración de una aplicación, las cargas variantes pueden hacer que el conjunto inicial de extensiones se vacíe, resultando posiblemente en SequenceFullException durante la ejecución. Para evitar que esto pase, a menudo es deseable permitir que el registro crezca automáticamente para alojar de forma transparente esta carga adicional. Observe que se admite la suma manual de extensiones durante SequenceFullException, y que se puede utilizar en lugar de crecimiento automático transparente.

Al trabajar con un registro circular, también debería establecer un recuento de la extensión máximo. Además, la estructura LogPolicy ofrece varios valores auxiliares comunes como la configuración de la tasa de crecimiento y reducción automáticos. La manipulación de estos valores puede tener efectos significativos en el rendimiento de la aplicación y se debería ajustar en función de la tasa de E/S para cualquier registro determinado dentro del sistema.

El ejemplo siguiente muestra cómo establecer una directiva del registro para crecimiento automático hasta un máximo de 100 extensiones, creciendo en 5 extensiones cada vez.

recordSequence.LogStore.Policy.AutoGrow = true;
recordSequence.LogStore.Policy.MaximumExtentCount = 100;
recordSequence.LogStore.Policy.GrowthRate = new PolicyUnit(5, PolicyUnitType.Extents);
recordSequence.LogStore.Policy.Commit();
recordSequence.LogStore.Policy.Refresh();

Anexar un registro

Debería considerar varias técnicas para proporcionar los datos en un formato entendido por los métodos Append.

Los métodos Append actuales se han optimizado para administrar matrices de bytes y listas de matrices de bytes. Sin embargo, también se puede utilizar junto con clases de serialización de mayor nivel. El formato de registro resultante todavía se debe entender para aprovechar eficazmente las características proporcionadas por la clase IRecordSequence. A continuación se muestra un ejemplo de código de la serialización de alto nivel que utiliza las características basadas en DataContractAttribute en System.Runtime.Serialization.Formatters.

SomeDataContractClass someClassInstance = new SomeDataContractClass(…);
ArraySegment<byte>[] recordData = new ArraySegment<byte>[1];

using (MemoryStream formatStream = new MemoryStream())
{
   IFormatter formatter = new NetDataContractSerializer();
   formatter.Serialize(formatStream, someClassInstance);
   formatStream.Flush();
   recordData[0] = new ArraySegment<byte>(formatStream.GetBuffer());
}

recordSequence.Append(recordData, …);

Al anexar los registros a una secuencia del registro, debería considerar cuidadosamente las restricciones espaciales y temporales de su aplicación. Por ejemplo, su aplicación puede exigir que una operación anexada sólo tenga éxito si queda espacio suficiente en el registro para escribir registros adicionales en el futuro utilizando la clase ReservationCollection. Los sistemas del procesamiento de transacciones basados en ARIES pueden hacer referencia normalmente a registros como registros de compensación o de deshacer, que se utilizan cuando se necesita revertir el trabajo. De igual forma, algunos sistemas tienen reglas severas que rigen el momento en que se deberían escribir los registros. Seleccionar qué registros se vaciarán al disco y a cuáles se les permitirá el vaciado lento se basa exclusivamente en requisitos de aplicación y restricciones de sistema. Estas consideraciones pueden afectar al rendimiento y a la exactitud.

Las opciones de espacio y tiempo se exponen al usuario a través de los diferentes métodos Append. El ejemplo siguiente muestra cómo establecer esas opciones anexando tres registros y garantizando el espacio para registros posteriores. Sólo obliga a un vaciado después de la última operación de escritura en registro.

// Assume recordData is smaller or equal to 1000bytes 
// Reserve space in the log for three 1000byte records ReservationCollection reservations = recordSequence.CreateReservationCollection();
reservations.Add(1000);
reservations.Add(1000);
reservations.Add(1000);

// The following three appends are guaranteed to succeed if execution 
// flows to here
recordSequence.Append(recordData1,
                                   userSqn,
                                   previousSqn,
                                   RecordAppendOptions.None,    // No flush
                                   reservations);
recordSequence.Append(recordData2,
                                   userSqn,
                                   previousSqn,
                                   RecordAppendOptions.None,    // No flush
                                   reservations);
recordSequence.Append(recordData3,
                                   userSqn,
                                   previousSqn,
                                   RecordAppendOptions.ForceFlush,    // Flush
                                   reservations);

Si la propiedad RetryAppend está establecida en true, y una operación de anexado no puede realizarse porque no hay ningún espacio en la secuencia, la secuencia del registro intentará liberar espacio y reintentará la operación. La implementación exacta de liberación de espacio varía. Por ejemplo, la clase LogRecordSequence invocará el motor de directiva de CLFS, tal y como se describe en la sección "Liberación de espacio mediante TailPinnedEvent."

Reservar

Las reservas se pueden realizar de dos maneras como se muestra en los ejemplos siguientes. Puede adoptar los ejercicios de los ejemplos para el procesamiento robusto. Observe que esta tarea sólo se puede realizar al utilizar la clase LogRecordSequence basada en CLFS.

Utilizar el método ReserveAndAppend

ReservationCollection reservations = recordSequence.CreateReservationCollection();
long[] lengthOfUndoRecords = new long[] { 1000 };
recordSequence.ReserveAndAppend(recordData,
                                                     userSqn,
                                                     previousSqn,
                                                     RecordSequenceAppendOptions.None,
                                                     reservations,
                                                     lengthOfUndoRecords);
recordSequence.Append(undoRecordData,    // If necessary …
                                    userSqn,
                                    previousSqn,
                                    RecordSequenceAppendOptions.ForceFlush,
                                    reservations);

Utilizar el enfoque manual

ReservationCollection reservations = recordSequence.CreateReservationCollection();
reservations.Add(lengthOfUndoRecord);
try
{
   recordSequence.Append(recordData, userSqn, previousSqn, RecordAppendOptions.None);
}
catch (Exception)
{
   reservations.Remove(lengthOfUndoRecord);
   throw;
}

recordSequence.Append(undoRecordData, userSqn, previousSqn, RecordAppendOptions.ForceFlush, reservations);

Leer el registro

La lectura del registro puede producirse en cualquier momento durante la duración de una aplicación. Sin embargo, dependiendo de la situación, puede ser necesario para recorrer en iteración los registros almacenados en el registro en clasificaciones diferentes. Además de las direcciones estándar especificadas por Next y Previous para el registro, también puede recorrer en iteración en un orden definido por el usuario que se especifica al anexar cada registro individual a la secuencia. También debería tener en cuenta que Previous no siempre es secuencial en lo que se refiere al registro físico, dado que el usuario puede especificar el registro anterior anexado por el subproceso de ejecución actual durante una operación Append.

El ejemplo siguiente lee un registro en orden secuencial directo empezando por el registro con el número de secuencia 'startSqn.'

foreach (LogRecord record in recordSequence.ReadLogRecords(startSqn, LogRecordEnumeratorType.Next))
{
   Stream dataStream = record.Data;
   // Process dataStream, which can be done using deserialization
}

Liberar espacio mediante TailPinnedEvent

Uno de los enfoques que una secuencia del registro puede utilizar para liberar espacio está desencadenando el evento TailPinned. Este evento indica que es necesario mover hacia delante la cola de la secuencia (es decir, el número de secuencia base) para liberar espacio.

Se puede desencadenar el evento en cualquier momento que la secuencia del registro decida que debe liberar espacio por cualquier razón. Por ejemplo, el motor de directiva de CLFS puede decidir desencadenar el evento cuando determina que las colas de dos clientes del registro que comparten el mismo archivo de registro están demasiado separadas. Se puede liberar espacio escribiendo áreas de reinicio o truncando el registro y utilizando el método AdvanceBaseSequenceNumber para liberar espacio. El ejemplo siguiente muestra el segundo enfoque.

recordSequence.RetryAppend = true;
recordSequence.TailPinned += new EventHandler<TailPinnedEventArgs>(HandleTailPinned);

void HandleTailPinned(object sender, TailPinnedEventArgs tailPinnedEventArgs)
{
   // tailPinnedEventArgs.TargetSequenceNumber is the target 
   // sequence number to free up space to.  
   // However, this sequence number is not necessarily valid.  We have
   // to use this sequence number as a starting point for finding a
   // valid point within the log to advance toward. You need to
   // identify a record with a sequence number equal to, or greater
   // than TargetSequenceNumber; let's call this 
   // realTargetSequenceNumber. Once found, move the base

   recordSequence.AdvanceBaseSequenceNumber(realTargetSequenceNumber);

}

También puede llamar al método WriteRestartArea fuera del evento TailPinned para liberar espacio. Una área de reinicio es similar a un punto de control en otros sistemas de procesamiento de registro. Llamar a este método indica que la aplicación considera que todos los registros anteriores antes del área de reinicio están completos y son utilizables para futuros anexos del registro. De modo similar a cualquier otro registro, el registro escrito por este método requiere espacio disponible real en el registro para funcionar.

Multiplexación

Al tener varias aplicaciones que funcionan juntas como una unidad, puede utilizar la clase LogRecordSequence basada en CLFS para manipular una secuencia de archivo de NTFS única. El ejemplo siguiente muestra cómo crear un registro multiplexado con dos secuencias. Intercalando operaciones de anexado y de lectura se realizan las entradas de registro.

namespace MyMultiplexLog
{
    class MyMultiplexLog
    {
        static void Main(string[] args)
        {
            try
            {
                string myLog = "MyMultiplexLog";
                string logStream1 = "MyMultiplexLog::MyLogStream1";
                string logStream2 = "MyMultiplexLog::MyLogStream2";
                int containerSize = 32 * 1024;

                LogRecordSequence sequence1 = null;
                LogRecordSequence sequence2 = null;

                Console.WriteLine("Creating Multiplexed log with two streams");

                // Create log stream 1
                sequence1 = new LogRecordSequence(logStream1, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);

                // Log Extents are shared between the two streams. 
                // Add two extents to sequence1.
                sequence1.LogStore.Extents.Add("MyExtent0", containerSize);
                sequence1.LogStore.Extents.Add("MyExtent1");

                // Create log stream 2
                sequence2 = new LogRecordSequence(logStream2, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);

                // Start Appending in two streams with interleaving appends.

                SequenceNumber previous1 = SequenceNumber.Invalid;
                SequenceNumber previous2 = SequenceNumber.Invalid;

                Console.WriteLine("Appending interleaving records in stream1 and stream2...");
                Console.WriteLine();

                // Append two records in stream1
                previous1 = sequence1.Append(CreateData("MyLogStream1: Hello World!"), SequenceNumber.Invalid, SequenceNumber.Invalid, RecordAppendOptions.ForceFlush);
                previous1 = sequence1.Append(CreateData("MyLogStream1: This is my first Logging App"), previous1, previous1, RecordAppendOptions.ForceFlush);

                // Append two records in stream2
                previous2 = sequence2.Append(CreateData("MyLogStream2: Hello World!"), SequenceNumber.Invalid, SequenceNumber.Invalid, RecordAppendOptions.ForceFlush);
                previous2 = sequence2.Append(CreateData("MyLogStream2: This is my first Logging App"), previous2, previous2, RecordAppendOptions.ForceFlush);

                // Append the third record in stream1
                previous1 = sequence1.Append(CreateData("MyLogStream1: Using LogRecordSequence..."), previous1, previous1, RecordAppendOptions.ForceFlush);
                
                // Append the third record in stream2
                previous2 = sequence2.Append(CreateData("MyLogStream2: Using LogRecordSequence..."), previous2, previous2, RecordAppendOptions.ForceFlush);
                
                // Read log records from stream1 and stream2

                Encoding enc = Encoding.Unicode;
                Console.WriteLine();
                Console.WriteLine("Reading Log Records from stream1...");
                foreach (LogRecord record in sequence1.ReadLogRecords(sequence1.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);
                }

                Console.WriteLine();             
                Console.WriteLine("Reading the log records from stream2...");
                foreach (LogRecord record in sequence2.ReadLogRecords(sequence2.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);
                }

                Console.WriteLine();

                // Cleanup...
                sequence1.Dispose();
                sequence2.Dispose();

                LogStore.Delete(myLog);

                Console.WriteLine("Done...");
            }
            catch (Exception e)
            {
                Console.WriteLine("Exception thrown {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);
        }

    }
}

Control de excepciones

Las aplicaciones que usan System.IO.Log deben estar preparadas para administrar failfasts resultantes de la infraestructura. En algunos escenarios, System.IO.Log no utiliza las excepciones para comunicar los errores a una aplicación. En su lugar, las excepciones se utilizan principalmente para errores recuperables sobre los que puede actuar un desarrollador de aplicaciones. Sin embargo, se producirán failfasts cuando una ejecución más extensa resultaría en archivos de registro dañados o potencialmente inutilizables. Cuando se producen failfasts, ninguna otra acción de aplicación está disponible para rectificar el problema y una aplicación se debe preparar para finalizar.

Para obtener más información sobre failfast, vea FailFast.

Muchas de las excepciones iniciadas por System.IO.Log proceden de las implementaciones del registro internas. A continuación se presenta una lista de algunas de las excepciones importantes que debería considerar.

  • SequenceFullException: Esta excepción puede o no ser grave. Si AutoGrow está establecido en true, y hay espacio suficiente para crecer, todavía se puede iniciar un SequenceFullException. Esto sucede porque la implementación de crecimiento automático es inherentemente una operación asincrónica y no atómica y no se puede garantizar que se complete de repente la tasa de crecimiento. Por ejemplo, si la tasa de crecimiento está establecida en 100 extensiones, puede llevar un tiempo considerable agregar las 100 extensiones al almacén del registro. Esto puede hacer que esta excepción se inicie intermitentemente durante este horario.

  • Controlador de TailPinned: Se crea un informe para la implementación del registro interna sobre una excepción iniciada dentro del evento TailPinned. Esto indica la incapacidad de una aplicación para mover el número de secuencia base. Se iniciará SequenceFullException, ya que el evento TailPinned es la última oportunidad de una aplicación para evitar las condiciones de registro completo.

  • ReservationNotFoundException: Esta excepción se produce al intentar anexar registros mediante una colección de reservas, y ya se han tomado todas las reservas del tamaño adecuado.

Consulte también

Referencia

System.IO.Log
LogRecordSequence
FileRecordSequence

Otros recursos

Registrar la compatibilidad en System.IO.Log

Enviar comentarios sobre este tema a Microsoft.

Copyright © 2007 Microsoft Corporation. Reservados todos los derechos.