2011-10-25 8 views
5

Me gustaría agregar un registro a mi aplicación. Escogí una biblioteca de registro, pero me gustaría poder cambiar a una biblioteca diferente sin tener que alterar ningún código que use el registro.C++ diseño de contenedor de registro

Por lo tanto, necesito algún tipo de contenedor de registro que sea lo suficientemente flexible como para utilizar prácticamente cualquier funcionalidad de la biblioteca de registro subyacente.

¿Alguna sugerencia para dicho diseño de envoltura?

EDITAR: una característica que debo tener en este envoltorio es el etiquetado de componentes. Quiero que mi clase de algoritmo tenga "X:" aparezca delante de sus líneas de registro, y mi clase de administrador aparezca "Y:". Cómo propagar estas etiquetas en el registro sublingual y cómo crear el mecanismo de nomenclatura de etiqueta de componente es una cuestión de diseño importante aquí.

+2

¿Realmente * necesita una envoltura? Pregúntalo tú mismo. ¿Existe realmente la posibilidad de que va a cambiar su registrador (varias veces, tal vez) o que va a utilizar múltiples marcos de registro? Escribir un contenedor toma recursos (tiempo), y se pueden poner en otras cosas, si no * realmente * necesita el contenedor. – Xeo

+0

gracias, puedes confiar en que realmente lo hago. En aras de la simplicidad, estoy dispuesto a conformarme con una versión muy simplificada del registro con la función de etiquetado que mencioné. – Leo

Respuesta

2

Su mejor opción es hacer que la interfaz sea lo más simple posible. Separe por completo la interfaz del usuario del registro de cómo se implementa realmente el registro.

Las preocupaciones transversales siempre son caras de mantener, por lo que complicar las cosas te hará odiar la vida.

Algunos biblioteca sólo quiere algo tan simple como esto:

void logDebug(const std::string &msg); 
void logWarning(const std::string &msg); 
void logError(const std::string &msg); 

No deben añadir o especificar cualquier más contexto. Nadie puede usar la información de todos modos, así que no te excedas en diseñarla.

Si comienza a agregar más información a sus llamadas de registro, es más difícil reutilizar el código de cliente que lo usa. Por lo general, verá esta superficie cuando los componentes se utilizan a diferentes niveles de abstracción. Especialmente cuando algún código de bajo nivel proporciona información de depuración que solo es relevante para niveles más altos.

Esto no obliga a su implementación de registro (o incluso la interfaz a la que se ajusta la implementación del registro) a nada, por lo que puede cambiarla cada vez que lo desee.

ACTUALIZACIÓN:

medida en que el etiquetado, que es una preocupación de alto nivel. Voy a especular que no pertenece al registro, pero eso no está ni aquí ni allá.

Manténgalo fuera de las especificaciones del mensaje de registro. El código de bajo nivel no debe dar a un camión volador quién es usted o su gerente.

No sé cómo se especifica X o Y en su ejemplo. Cómo lo haces no es realmente obvio a partir de la descripción que se nos da. Solo voy a usar una cadena para la demostración, pero debes reemplazarla con algo seguro, si es posible.

Si esto está siempre activado, entonces solo tener un contexto de instancia (probablemente una variable global) podría ser apropiado. Cuando inicie sesión, configure el contexto y olvídese de él. Si alguna vez no está configurado, tira con extremo prejuicio. Si no puede tirar cuando no está configurado, entonces no siempre está activado.

void setLoggingContext("X:"); 

Si esto cambia en diferentes niveles de abstracción, consideraría una implementación RAII basada en pila.

LoggingTag tag("X:"); 

yo no estoy seguro de lo que sus necesidades están en el escenario cuando las diferentes tramas de pila pasan en diferentes valores. Pude ver donde la parte superior o inferior de la pila sería razonable para diferentes casos de uso.

void foo() { 
    LoggingTag tag("X:"); 
    logWarning("foo"); 
    bar(); 
    baz(); 
} 

void bar() { 
    LoggingTag tag("Y:"); 
    logWarning("bar"); 
    baz(); 
} 

void baz() { 
    logWarning("baz"); 
} 

De cualquier forma, esto no debería afectar la forma en que agrega un mensaje al registro. La función baz no tiene el contexto para especificar LoggingTag. Es muy importante que al usar logWarning no se conozcan las etiquetas por este motivo.

Si desea etiquetar basándose en algún tipo, puede hacer algo tan simple como este.

struct LoggingTag { 
    LoggingTag(const std::string &tag_) : tag(tag_) {} 
    template<typename T> 
    static LoggingTag ByType() { 
     return LoggingTag(typeid(T).name()); 
    } 
    std::string tag; 
}; 

void foo() { 
    LoggingTag tag = LogginTag::ByType<int>(); 
} 

Esto no fuerza que alguien utilice typeid(T).name() si no quieren, pero se dio la conveniencia haría.

+0

Una preocupación que tengo que tener en cuenta es el etiquetado de componentes diferentes. – Leo

+0

Tenía esta sintaxis en mente: Log log = GetLogger (MyClassName); log.Warn (...). No estoy seguro de qué MyClassName debería ser, si es una cadena, ¿de dónde la obtengo? Si es un truco para convertir un nombre de clase en una cadena, ¿cómo se puede hacer? – Leo

+0

@ user991339 No entiendo lo que está preguntando en absoluto. ¿Estás intentando registrar un tipo? –

1

me gusta este enfoque:

class Log { 
public: 
    virtual logString(const std::string&)=0; 
}; 

template <typename T> 
Log& operator<<(Log& logger, const T& object) { 
     std::stringstream converter; 
     converter << object; 
     logger.logString(converter.str()); 
     return logger; 
} 

simple y rápido! Todo lo que necesita hacer es volver a implementar el método logString ...

+0

Esta es una buena idea. ¿Puedes sugerir una forma de implementar la función de etiquetado? – Leo

+0

@CatPlusPlus Se puede modificar fácilmente para usar plantillas ... –

+0

@ user991339 Agregaría un atributo de etiqueta al registrador y un bool para indicar un inicio de línea, así que en el primer registro recibe el sello de la etiqueta. Puede especializar al operador << para el caso endl para que sepa que la siguiente entrada es un inicio de línea. –

0

Eche un vistazo a la biblioteca zf_log. Es muy pequeño (~ 2000k líneas, ~ 10KB cuando se compila) y rápido (consulte la tabla de comparación en README.md). Está muy cerca de lo que describes como envoltorio. Le proporciona una API abstracta que puede usar en su proyecto y le permite especificar qué implementación de registro real usar. Vea el ejemplo custom_output.c donde syslog se utiliza como recurso de salida. También podría usarse de forma privada dentro de las bibliotecas sin riesgo de entrar en conflicto con otro código que podría usar esta biblioteca (consulte ZF_LOG_LIBRARY_PREFIX para obtener más información). Incluso si no es exactamente lo que está buscando, supongo que podría ser un buen ejemplo para su envoltura.