Моя проблема касается регистрации библиотечных классов (классов, которые используются внутри библиотек),
В настоящее время мы используем log4cxx
но log4j
Библиотека реализует те же понятия.
Скажем, у меня есть процесс, в котором есть несколько сущностей, A, B и C. Каждый из них использует много разных классов и функций, четко разделенных в коде.
A, B и C используют много библиотечных классов, функций, объектов, ресурсов и иногда даже глобальных переменных (устаревший код, я ничего не могу с этим поделать …) — давайте назовем их всех foo
Регистрация A, B и C оказалась проблема производительности, журнал взорвется, когда мы установим уровень журнала для отладки. После просмотра нашей системы мы пришли к следующим выводам:
foo
печать в журнал, нам нужно увидеть, какой объект вызвал его, A, B или C.foo
мы хотим иметь возможность изменять уровень отладки отдельно для каждого foo
foo
следует рассматривать как общую библиотеку, она не может напрямую зависеть от A, B или C.foo
(например, тот же экземпляр нашего класса обработки ресурсов используется A, B и C), в журнале мы хотели бы увидеть, какой класс используется foo
,Это то, что мы придумали до сих пор —
A, B и C будут иметь отдельные регистраторы. Глобальная переменная (хранящаяся в другой библиотеке со всеми нашими помощниками и оболочками) всегда будет поддерживать текущие отчеты журнала. Каждый раз, когда объект начинает обрабатывать свою логику, он устанавливает глобальную переменную в соответствующий регистратор. Когда foo
хочет сообщить в журнал, он сообщает через глобальную переменную и добавив его имя (и контекст) в сообщение журнала.
Проблема в том, что такое ощущение, что должно быть что-то, что уже делает это, решение не чувствует себя чистым, с такой глобальной переменной …
Мы что-то здесь не так делаем?
Есть ли лучшее решение для этого?
Я не знаю о существующем решении. Я бы, вероятно, придумал регистратор с интерфейсом, подобным следующему (будь то отдельная реализация или просто обертка на вашей существующей):
class Logger {
public:
enum Level {
...
};
static Logger* Instance();
void Log(Level level, const char* module, const char* msg);
void AddModuleFilter(const char* context, const char* module, Level level);
void SetThreadLocalContext(const char* context);
...
};
Основным отличием от обычных библиотек журналов является контекстно-зависимый модуль фильтра. У нас могут быть такие настройки, как следующие, чтобы установить разные уровни в зависимости от того, кто делает вызов (контекст):
// foo calls by entity A have the DEBUG log level
Logger::Instance()->AddModuleFilter("A", "foo", Logger::DEBUG);
// foo calls by entity B have the WARNING log level
Logger::Instance()->AddModuleFilter("B", "foo", Logger::WARNING);
Тогда поток вызовов будет таким:
// In entity A
Logger::Instance()->SetThreadLocalContext("A");
// Call function foo
Logger::Instance()->Log(log_level, "foo", log_msg);
Не уверен, что такой интерфейс соответствует вашим целям. Во всяком случае, я всегда считаю интерфейс первым хорошим подходом. Если у вас есть понятный интерфейс, его реализация должна быть легкой.
Если отдельная библиотека сама является классом, вы можете использовать переменную уровня класса в библиотеке для хранения ссылки на экземпляр регистратора. Я не часто программирую на C ++ или Java, но я думаю, что переменная уровня класса является «статической» в C ++ и Java.
Это все еще глобальная переменная внизу, но, по крайней мере, это глобальная переменная в классе (как переменная класса с именем debugLog.log
где debugLog
это имя класса).