Desde una perspectiva de diseño, ¿cuáles son las mejores prácticas para iniciar sesión? [cerrado]

11

Quiero agregar el registro a una aplicación en la que estoy trabajando actualmente. He agregado el registro antes, eso no es un problema aquí.

Pero desde una perspectiva de diseño en un lenguaje orientado a objetos, ¿cuáles son las mejores prácticas para el registro que siguen OOP y patrones?

Nota: Actualmente estoy haciendo esto en C #, por lo que los ejemplos en C # son obviamente bienvenidos. También me gustaría ver ejemplos en Java y Ruby.


Editar: estoy usando log4net. Simplemente no sé cuál es la mejor manera de enchufarlo.

Edgar Gonzalez
fuente

Respuestas:

6

La mejor práctica que recomendaría es usar log4j en lugar de rodar el suyo. (Que se ha portado desde Java a C # y Ruby, por lo que se aplica a los 3 idiomas que le interesan).

Si lee esa página del manual, descubrirá varias otras mejores prácticas. Como ser liviano, configurable fuera de su aplicación, poder subir y bajar el registro de diferentes partes de su aplicación de forma independiente, etc.

btilly
fuente
5

Donde trabajo escribimos muchas aplicaciones de escritorio .NET. Normalmente implementamos 2 eventos en nuestros componentes, uno para registrar información y el otro para registrar excepciones (aunque a menudo dejamos que las excepciones broten en lugar de generar el evento por separado. Depende de la situación). Con esta arquitectura, ninguna de nuestras bibliotecas necesita saber cómo se implementa el registro o cómo se usa, almacena o trata la información. Luego, hacemos que la aplicación maneje los eventos de registro de una manera apropiada para esa aplicación. Hace algunos años, esta arquitectura hizo que nuestra transición del uso del registro de MS Enterprise Library al componente de registro de BitFactory fuera una transición muy simple.

Ali
fuente
+1 por usar un patrón Evento / Observador: cambia el observador, has cambiado el registro
Matthieu M.
2

Personalmente, tomo el marco de registro de elección (en mi caso, Entlib porque trabajo con .NET) y escribo un aspecto AOP para el registro.

Luego puede atribuir cualquier método / propiedades / clases / espacios de nombres y agregarles registros sin saturar la fuente.

Steven Evers
fuente
Suena muy interesante, pero tengo reservas sobre lo que podría registrar y cuán informativo sería el registro (es decir, más que la "simple" instrumentación de métodos). Me encantaría ver un ejemplo práctico de este enfoque para ver qué se puede y qué no se puede hacer. Especialmente porque recién estoy comenzando con una nueva aplicación y me gustaría ver dónde / qué tan lejos podría llevar esto.
Marjan Venema
@marjan Venema: la documentación posterior a la publicación tiene un ejemplo de un aspecto que registra la entrada / salida de un método. doc.sharpcrafters.com/postsharp/2.0/##PostSharp.chm/html/… En el caso de Post sharp, entrelaza el código del atributo a la fuente en el momento de la compilación, por lo que no afecta el rendimiento como lo hacen otros.
Steven Evers
1

El sistema en el que estoy trabajando actualmente utiliza una arquitectura y mensajes dirigidos por eventos, de modo que la mayoría de las acciones en nuestro sistema son el resultado de un comando y resultan en eventos (como clases DTO que se envían, en lugar de un evento delegado estándar). Adjuntamos controladores de eventos cuyo único propósito es manejar el registro. Este diseño nos ayuda a no repetirnos y a no tener que modificar el código existente para agregar / cambiar la funcionalidad.

Aquí hay un ejemplo de una de esas clases de registro, que maneja todos los eventos que se registrarán desde una sección estrecha de nuestra aplicación (aquellos relacionados con una fuente de contenido particular desde la que importamos).

No diré necesariamente que esta es una práctica recomendada, ya que parece cambiar de opinión sobre qué y cómo registrar a menudo, y cada vez que necesito usar un registro para diagnosticar un problema, inevitablemente encuentro formas de mejorar el registro. Información que registro.

Sin embargo, diré que registrar la información pertinente (especialmente en una forma de búsqueda Ctrl-F / find) es la parte más importante.

La segunda parte más importante es alejar el código de registro de su lógica principal: puede hacer que un método sea feo, largo y complicado muy rápidamente.

public class MctLogger :
    IEventHandler<StoryImported>,
    IEventHandler<StoryScanned>,
    IEventHandler<SourceDirectoryMissing>,
    IEventHandler<SourceDirectoryAccessError>,
    IEventHandler<CannotCreateScannedStoryDirectory>,
    IEventHandler<CannotReadStoryDocument>,
    IEventHandler<StorySkippedPastCutoff>,
    IEventHandler<StorySkippedDuplicateUniqueId>,
    IEventHandler<StorySkippedByFilter>
{

    public void Observe(StoryImported e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.StoryImported");
        log.Info("Story Unique ID: {Story.UniqueId}, Content ID: {ContentId}, Title: {Story.Headline}".SmartFormat(e));
    }

    public void Observe(StoryScanned e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.StoryScanned");
        log.Info("Story Unique ID: {Story.UniqueId}, File: {FilePath}, Title: {Story.Headline}".SmartFormat(e));
    }

    public void Observe(SourceDirectoryMissing e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.SourceDirectoryMissing");
        log.Error("Directory: " + e.Directory);
    }

    public void Observe(SourceDirectoryAccessError e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.SourceDirectoryAccessError");
        log.Error(e.Exception, "Exception: " + e.Exception.Message);
    }

    public void Observe(CannotCreateScannedStoryDirectory e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.CannotCreateScannedStoryDirectory");
        log.Error(e.Exception, "Directory: {Directory}, Exception: {Exception.Message}".SmartFormat(e));
    }

    public void Observe(CannotReadStoryDocument e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.CannotReadStoryDocument");
        if (e.Exception == null) {
            log.Warn("File: {FilePath}".SmartFormat(e));
        }
        else {
            log.Warn(e.Exception, "File: {FilePath}, Exception: {Exception.Message}".SmartFormat(e));
        }
    }

    public void Observe(StorySkippedPastCutoff e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.StorySkippedPastCutoff");
        log.Warn("Story Unique ID: {Story.UniqueId}, File: {FilePath}, Title: {Story.Headline}".SmartFormat(e));
    }

    public void Observe(StorySkippedDuplicateUniqueId e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.StorySkippedDuplicateUniqueId");
        log.Warn("Story Unique ID: {Story.UniqueId}, File: {FilePath}, Title: {Story.Headline}".SmartFormat(e));
    }

    public void Observe(StorySkippedByFilter e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.StorySkippedByFilter");
        log.Warn("Story Unique ID: {Story.UniqueId}, Reason: {Reason}, File: {FilePath}, Title: {Story.Headline}".SmartFormat(e));
    }
}
quentin-starin
fuente
1

Como han dicho otros, el uso log4jo la log4neto algún otro marco de registro bien construido.

Realmente no me gusta que el código de registro se interponga en el camino de la lógica empresarial. Por eso lo uso Log4PostSharp. Eso significa que puedo usar la Programación Orientada a Aspectos para anotar métodos como este:

[Log(LogLevel.Info, "Counting characters.")]
int CountCharacters(string arg) 
{
    return arg.Length;
}

O cada método en un ensamblaje como este:

[assembly: Log(AttributeTargetTypes = "*", 
 EntryLevel = LogLevel.Debug, ExitLevel = LogLevel.Debug, 
 ExceptionLevel = LogLevel.Error)]
Peter K.
fuente
0

No estoy seguro de si algún marco hace esto, pero desde una perspectiva de diseño, modelaría que la información debe registrarse principalmente en tres categorías:

  1. método de rastreo de nivel
  2. registro de excepciones
  3. Los desarrolladores de información adicional sobre el tiempo de ejecución creen que es vital para investigar en caso de una falla en el tiempo de ejecución (o cualquier comportamiento relacionado con la situación de solo tiempo de ejecución).

Para las dos primeras categorías, mi marco de registro ideal debería manejarlas como un proceso posterior a la compilación y transparente para los desarrolladores. Sería bueno agregar declarativamente el registro a los ensamblados, algo como lo siguiente:

Trace YourNamespace.* [public methods|constructors]
{  # options
   ignore trivial methods,
   format: "{time stamp}: {method name}({parameter list})",
   condition: "{Context.Instance.UserID in (12432,23432)}",
}

Exception YourNamespace.Program.Main [unhandled exceptions]
{
  format: "{time stamp}: {Context.Instance.UserId} {exception}",
  action: die,  # options are throw, swallow,
}

Para la tercera categoría, los programadores pueden simplemente crear uno o más métodos de "registro" dedicados y aprovechar el rastreo para la primera categoría. Los métodos de registro no hacen más que servir un punto auxiliar al que se pueden aplicar las reglas de seguimiento.

Codismo
fuente