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.
¿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
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