logger for c++

i am writing a logger class in c++ i want to redirect all the logs from different files to a function which decides where to write the log.

for example,

func A()
{
int a =10;
std::string b = "test";
cout<<"printing"<<a<<b<<endl;
}

func B()
{
unsigned long t = 6700;
cout<<t<<endl;
}

Instead of cout i want to have a function which takes variable arguments and sent to common function which finally prints or writes into a file. New to c++, please let me know if anyone knows how to do this
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
using namespace std;

template <typename T>
void writeToLog(T t) {
   std::cout << t << std::endl;
}

void log() {} // base case - do nothing

template <typename T, typename...Ts>
void log(T &&first, Ts&&... rest) {
    writeToLog(std::forward<T>(first));
    // Forward the rest.
    log(std::forward<Ts>(rest)...);
}


int main() 
{
	log("beans", 3, 6.7);
	log("apple toast", 'f');
}


Replace the contents of writeToLog with code that actually does what you want. To file or some such.
void foo(ostream& stream)


foo(cout)
foo(filevar)

that lets you pass in where to put it.
Last edited on
No need for recursion: use a fold expression
1
2
3
4
5
template <typename... Ts>
void log(Ts&&... args) 
{ 
  (void(writeToLog(std::forward<Ts>(args))), ...); 
}
Last edited on
Can that be simplified to this, or am I missing something?

1
2
3
4
5
template <typename... Ts>
void log(Ts&&... args) 
{
    (writeToLog(std::forward<Ts>(args)), ...);
}

Can that be simplified

In the scope of @Repeater's tiny example program, yes.

In general, log is interested in built-in operator, and not some user overload of it. It's possible to call the user's operator, for example, if writeToLog is overloaded for a user type, or there's ADL involved:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>
#include <string>
#include <vector>

// library code
template <typename T>
void writeToLog(T&& x) 
{
  std::cout << x; 
}

template <typename... Ts>
void log(Ts&&... args) 
{
  // should be (void(writeToLog(std::forward<Ts>(args))), ...);
  (writeToLog(std::forward<Ts>(args)), ...);
}

// some client code
struct foo 
{
    foo operator,(foo) = delete; 
};

foo writeToLog(foo x) 
{
    return x; 
}
  
int main()
{
    log(foo{}, foo{}); // tries to call explicitly deleted function
}


Typically, conversions to void appear in generic libraries around uses of operator, for this reason.
Last edited on
Consider using the cerr instead stream instead of cout. This is the standard error stream. On UNIX and Linux, it defaults to the cout stream. You can redirect it on the command line:

myprog 2>myLogFile

If you must send it to a function (and about the only reason I can think of is to change the log destination at runtime), then the most flexible thing is to do is create custom streambuf class. Then you can create a logging stream that uses the streambuf and you get all the ostream goodness in the logging.
I missed one thing , this can have log level and level might change anytime and output should be based on the latest log level
That sounds like an exercise for the reader.

If you want to use one for real that's similar to mbozzi's excellent examples, there's this: https://github.com/gabime/spdlog (git@github.com:gabime/spdlog.git)
Last edited on
Sorry just wanted to get an idea and i got it from the answers. Thank you so much everyone for the help
i'm really new in c++, but i think have an idea for the "logging" process.

The point is, to send a message -the same - to a (hard) file and - alternatively - to the console or even to both, in order to have information for debugging the behaivor of the program.

The function "works" (please see ) but by now i have to use two instructions for each message: the ostringstream assignament and the call for the function.

The control is the "int dstny" wich will say where the message should be sent. Of course i made a small version just for the case, and the "hard" file use is not shown here.

i'm including my code so it could be clearer.

my state of the art is make the line 22

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Logging messages

#include    <iostream>
#include 	<string>  
#include 	<sstream>   
#include    <iomanip>
using namespace std;
 
    void   dsplCln (int dstny, ostringstream & bufi ) {	// call by reference
	// if (dstny=3) file3<<bufi;  if(dstny>=2) file2<<buf); ...  
	cout    << bufi.str() <<endl;
	bufi.str(""); bufi.clear();
}	

   int main(){
  	ostringstream  buff_1;
  	int 	iVal=345, dstny=3;

	buff_1 << "Another one " << iVal +101 <<"\n";
        dsplCln  (dstny, buff_1 );

//	dsplCln (2, ( buff_1 << "This is a long string ") );

	system("pause");
	return 0;
} 


An help is welcome.
Topic archived. No new replies allowed.