【问题标题】:Architecture for logging with shared DLLs使用共享 DLL 进行日志记录的体系结构
【发布时间】:2019-10-01 04:45:26
【问题描述】:

我一直在为我们的产品编写日志代码,但遇到了架构问题。我们有两个用 C++ 为 Window 编写的命令行可执行文件,分别称为 Foo 和 Bar,它们依赖于我们将命名为 Core 的 DLL。我想从 Core 中登录。

问题是,这些日志条目应该在哪里结束?如果我运行 Foo,我想在 Foo.log 中看到它们,如果我运行 Bar,它们应该在 Bar.log 中。如果我同时运行 Foo 和 Bar 怎么办? (我想我已经对运行多个 Foo 或 Bar 副本的情况进行了排序,有效地锁定了日志文件。

一种想法是,Core 可以保留“当有人发出日志记录请求时我需要调用的所有记录器”的列表。这意味着要编写一个全新的 API,并且 DLL 中的日志记录与 exe 或静态库中的日志记录不同。这并不理想。如果代码在 lib 中,我什至可能不知道代码在哪里结束!

我查看了 log4cplus,并提升了日志记录,但也无法了解这将如何与这些组件一起使用,所以我有点想不通。不过,这肯定是一个已解决的问题?!​​

【问题讨论】:

  • 在您的DllMain 中,处理DLL_PROCESS_ATTACH,图what process you are in,并相应地设置记录器。
  • 我没有看到任何问题。在每个可执行文件中创建日志记录上下文时,没有什么可以阻止您指定应将日志写入何处。
  • @VTT 在我的 DLL 中我有一个记录器 logger = LogManager::GetLogger("core")。如果 Foo 和 Bar 都运行,它们都将尝试将记录器配置为写入它们的日志文件(因此是竞争条件)。
  • dll本身不加载,加载到进程中,每次调用dllmain。点击上面的链接。
  • 由于FooBar 是不同的进程,每个进程都有自己的x

标签: c++ windows logging dll


【解决方案1】:

函数指针是关键,IMO。

我通常在最顶层的 .exe 代码中实现记录器,并在启动时将函数指针一直向下传递。这是一个例子。

enum struct eLogLevel : uint8_t
{
    Error,
    Warning,
    Info,
    Debug
};
// C function pointer to write messages to arbitrary destination. Messages are better to be UTF8.
using pfnLogMessage = void( *)( eLogLevel lvl, const char* msg );

// DLL API, call this once, immediately after loading your DLL. Keep the level + function pointer in static variables. 
// Unless messing with DLL segments, static variables are per-process, i.e. two processes will have different copy of these variables.
// Good idea to also call on shutdown, passing nullptr, before closing the underlying implementation stream.
void initializeLogging( eLogLevel maxLevelToLog, pfnLogMessage fnLogMessage );

// Invoke that function pointer to actually log stuff.
void logMessage( eLogLevel lvl, const CStringA& msg );

// Convenience wrappers to format and write various messages.
// Moar examples: https://github.com/Const-me/vis_avs_dx/blob/master/avs_dx/DxVisuals/Utils/logger.h
inline void logMessageV( eLogLevel lvl, const char* pszFormat, va_list args )
{
    CStringA str;
    str.FormatV( pszFormat, args );
    logMessage( lvl, str );
}

#define LOG_MESSAGE_FORMAT( lvl ) va_list args; va_start( args, pszFormat ); logMessageV( lvl, pszFormat, args ); va_end( args );

inline void logError( const char* pszFormat, ... )
{
    LOG_MESSAGE_FORMAT( eLogLevel::Error );
}

【讨论】:

  • 当我有单个应用程序的单个实例与日志记录 DLL 对话时,这可以正常工作。但是,当我有(或/或)多个应用程序的多个实例与日志记录 DLL 通信时,它会中断。
  • @JulianGold 该方法不使用全局资源,由调用者进程来实现该函数指针。除非您弄乱 DLL 段,否则它不会中断,即将全局变量保留在 DLL 的 SHARED 读/写部分中。可能发生的情况是,您试图打开同一个文件以从多个进程写入。如果您想在同一个文件中记录多个进程,并且不记录太多内容,即性能不是问题,您可以使用FILE_SHARE_WRITE 标志打开/创建文件,并使用命名互斥锁进行同步。跨度>
猜你喜欢
  • 1970-01-01
  • 2017-05-18
  • 2012-12-29
  • 1970-01-01
  • 1970-01-01
  • 2010-09-22
  • 2020-01-01
  • 2019-09-12
相关资源
最近更新 更多