【问题标题】:Get name of calling function, line number and file name in c++获取C++中调用函数的名称、行号和文件名
【发布时间】:2025-12-07 14:55:02
【问题描述】:

我已经编写了以下代码来在我的日志文件中写入日志。 这段代码可以很好地记录消息,但现在我必须将它集成到多个文件中,我需要调用者的文件路径、调用者函数名称和行号。

请帮助我实现这一目标。

#include "Source.h"

bool CLogManager::fileOpenError = false;
std::string CLogManager::logFileName = "";
CLogManager* CLogManager::logManager = NULL;
FILE* CLogManager::file = NULL;

CLogManager :: CLogManager(){}
CLogManager :: ~CLogManager()
{
    if (file)
        fclose(file);
}
CLogManager* CLogManager::getInstance()
{
    if(logManager==NULL)
    {
        logManager = new CLogManager();
        logFileName = currentDateTime();
    }
    return logManager;
}
const std::string CLogManager::currentDateTime()
{
    time_t now = time(0);
    char currTime[30];
    strftime(currTime, sizeof(currTime), "Log_%Y_%m_%dT%H_%M_%S.xml", localtime(&now));
    return currTime;
}
void CLogManager::Log (char *message)
{
    file = fopen(logFileName.c_str(), "a+");
    if(file == NULL) 
    {
        if(fileOpenError == false)
        {
            std::cout << "There was an error While opening Log File."<<std::endl;
            fileOpenError = true;
        }
        return;
    }
    fputs(message, file);
    fputs("\n", file);
}

int main ()
{
    CLogManager::getInstance();
    CLogManager::Log("Sorry some error occured");
    CLogManager::Log("Please try again");
    CLogManager::Log("Wait");
    return 0;
}

【问题讨论】:

  • y 不使用 log4net 进行日志记录,而不是创建自己的类? log4net 还会为您提供函数名称和其他一些详细信息。不确定行号。您可以从 Log4net C# 制作一个 dll 并在您的 C++ 代码中使用它
  • 我知道的唯一方法是使用宏。像::#define log(msg) CLogManager::Log(std::string(msg) + " " + __FILE__ + "[" + std::to_string(__LINE__) + "]").
  • 读取*.com/questions/353180/…获取函数名

标签: c++ error-logging


【解决方案1】:

当我需要快速的“printf”日志记录时,我会使用这个 marco 来记录带有文件名和行的消息:

#define _MSG(msg) do{ std::cerr << __FILE__ << "(@" << __LINE__ << "): " << msg << '\n'; } while( false )

上述宏会将msg 注入std::cerr 管道。您可以取出您需要的零件或根据您的目的对其进行修改。它取决于标准定义的__FILE____LINE__ 宏:

__FILE__ 当前源文件的假定名称(字符串文字)。

__LINE__ 当前源代码行的假定行号(在当前源文件中)(一个整数 常数)。

函数名不是那么容易获取的,我觉得也没有什么好的获取方式。

如果你想通过函数进行记录,我会定义一些宏,或者创建函数,将 intchar* 分别用于行和文件。类似log(int line, char* source_file, string message)

【讨论】:

  • 你能帮我把这个宏合并到我的代码中吗?
【解决方案2】:

从 C++ 20 开始,您可以使用std::source_location

来自CPPReference.com上的示例:

#include <iostream>
#include <string_view>
#include <source_location>
 
void log(const std::string_view message,
         const std::source_location location = 
               std::source_location::current())
{
    std::cout << "file: "
              << location.file_name() << "("
              << location.line() << ":"
              << location.column() << ") `"
              << location.function_name() << "`: "
              << message << '\n';
}
 
template <typename T> void fun(T x)
{
    log(x);
}
 
int main(int, char*[])
{
    log("Hello world!");
    fun("Hello C++20!");
}

输出:

file: main.cpp(23:8) `int main(int, char**)`: Hello world!
file: main.cpp(18:8) `void fun(T) [with T = const char*]`: Hello C++20!

【讨论】:

    【解决方案3】:

    要利用您已经编写的 LogManager 类,您可以执行以下操作:

    void CLogManager::Log(char *message, std::string FileName = "Unset", std::string FunctionName = "Unset", int LineNumber = -1)
    {
    ...
    }
    

    然后,您现在想在任何地方使用 Logging 功能,您只需这样做:

    ::Log(message);
    

    但是,如果您想包含文件/功能/行信息,您可以这样做:

    ::Log(message, __FILE__, __FUNCTION__, __LINE__);
    

    您可以将默认值从“未设置”调整为您想要的任何内容(包括“”)。我还可以在该函数中建议您可以根据 FileName 参数(传递给函数)是否为默认值而使输出有所不同。这样你的日志文件就会看起来很干净。

    【讨论】: