Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

New Lightweight C++ Logging Framework

98 views
Skip to first unread message

Mr Flibble

unread,
Oct 31, 2020, 7:11:13 PM10/31/20
to
New lightweight C++ logging framework:

#include <thread>
#include <iostream>
#include <neolib/app/ostream_logger.hpp>

namespace neolog = neolib::logger;

enum class category : int32_t
{
Red,
Green,
Blue,
Black,
White
};

const neolog::category Red{ category::Red };
const neolog::category Green{ category::Green };
const neolog::category Blue{ category::Blue };
const neolog::category Black{ category::Black };
const neolog::category White{ category::White };

void output_log_messages(neolog::i_logger& logger0, neolog::i_logger& logger1)
{
for (int i = 0; i < 1000; ++i)
{
logger0 << Red << neolog::severity::Info << "[tid: " << std::this_thread::get_id() << "] [" << std::hex << "0x" << i << "] (Red) Info message 1" << neolog::endl;
logger0 << Green << neolog::severity::Debug << "[tid: " << std::this_thread::get_id() << "] [" << std::hex << "0x" << i << "] (Green) Debug message 1" << neolog::endl;
logger0 << Blue << neolog::severity::Debug << "[tid: " << std::this_thread::get_id() << "] [" << std::hex << "0x" << i << "] (Blue) Debug message 2" << neolog::endl;
logger0 << Black << neolog::severity::Info << "[tid: " << std::this_thread::get_id() << "] [" << std::hex << "0x" << i << "] (Black) Info message 2" << neolog::endl;
logger0 << White << neolog::severity::Info << "[tid: " << std::this_thread::get_id() << "] [" << std::hex << "0x" << i << "] (White) Info message 3" << neolog::endl;

logger1 << neolog::severity::Info << "**** LOGGER1 MESSAGE ****" << neolog::endl;
}
}

int main()
{
try
{
neolog::ostream_logger<0> logger0{ std::cout };
logger0.set_filter_severity(neolog::severity::Debug);
logger0.create_logging_thread();

neolog::ostream_logger<1> logger1{ std::cerr };
logger1.create_logging_thread();

/* std::ofstream ofs{ "c:\\tmp\\test.log" };
neolog::ostream_logger<2> logger2{ ofs };
logger2.create_logging_thread();
logger0.copy_to(logger2); */

logger0.register_category(category::Red);
logger0.register_category(category::Green);
logger0.register_category(category::Blue);
logger0.register_category(category::Black);
logger0.register_category(category::White);

logger0.disable_category(category::White);
// logger2.enable_category(category::White);

std::thread thread1{ [&]()
{
output_log_messages(logger0, logger1);
} };

std::thread thread2{ [&]()
{
output_log_messages(logger0, logger1);
} };

std::thread thread3{ [&]()
{
output_log_messages(logger0, logger1);
} };

output_log_messages(logger0, logger1);

thread1.join();
thread2.join();
thread3.join();
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
catch (...)
{
std::cerr << "unknown exception" << std::endl;
}
}

https://github.com/i42output/neolib/blob/master/include/neolib/app/i_logger.hpp

/Flibble

--
¬

Öö Tiib

unread,
Nov 1, 2020, 7:55:33 AM11/1/20
to
On Sunday, 1 November 2020 01:11:13 UTC+2, Mr Flibble wrote:
> New lightweight C++ logging framework:

Looks nice. Perhaps worth documenting (as video is bad document).

Some things confuse me. For example why class logger is friend of
its base class i_logger? There are nothing private in
i_logger.

What I use in logging is possibility to set the lower bound
of logging level (you call it "severity") compile time ... so
for example if I set it to "info" level then "debug" and "trace"
level logging compiles to NOP. That can perhaps be done outside
of your framework.

Mr Flibble

unread,
Nov 1, 2020, 8:59:12 AM11/1/20
to
Hi,

The friendship is needed as i_logger::flush is protected and used by logger::flush:

for (auto& copy : copies())
copy->flush(aMessage);

/Flibble

--
¬

Öö Tiib

unread,
Nov 2, 2020, 9:33:03 AM11/2/20
to
But derived class has already access to protected members of its base?


Mr Flibble

unread,
Nov 2, 2020, 3:31:01 PM11/2/20
to
Only for if the context is the `this` pointer:

class foo
{
protected:
void f1() {}
};

class bar : public foo
{
public:
void f2(foo& o)
{
o.f1();
}
};

int main()
{
bar o1;
bar o2;
o2.f2(o);
}

prog.cpp: In member function ‘void bar::f2(foo&)’:
prog.cpp:12:8: error: ‘void foo::f1()’ is protected within this context
o.f1();
^
prog.cpp:4:7: note: declared protected here
void f1() {}

/Flibble

--
¬

Öö Tiib

unread,
Nov 4, 2020, 5:59:56 PM11/4/20
to
It is not about this pointer but about base class objects of same
type. That works:

class foo
{
protected:
void f1() {}
};

class bar : public foo
{
public:
void f2(bar& o)
{
o.f1();
}
};

int main()
{
bar o1;
bar o2;
o2.f2(o1);
}




Chris M. Thomasson

unread,
Nov 4, 2020, 7:39:16 PM11/4/20
to
On 10/31/2020 4:10 PM, Mr Flibble wrote:
> New lightweight C++ logging framework:

nice. Fwiw, iirc, I used a little logging framework where each thread
had local memory it stored log info, and a single logging thread that
would episodically grab all from each thread. So, a single atomic xchg
was used from the logging thread to gather and report. It was a simple
distributed multiple producer single consumer setup.

log:

A thread enters a log in its local memory.


log thread:

grabs from every threads logs.

reports to file.

repeats.

It scales fairly well.

>

Mr Flibble

unread,
Nov 5, 2020, 1:27:57 PM11/5/20
to
However f2 has to be void f2(foo& o) which doesn't work.

/Flibble

--
¬

Mr Flibble

unread,
Nov 5, 2020, 2:15:34 PM11/5/20
to
Good idea, just implemented it. Available on github now.

/Flibble

--
¬

Chris M. Thomasson

unread,
Nov 5, 2020, 7:03:31 PM11/5/20
to
On 11/5/2020 11:15 AM, Mr Flibble wrote:
> On 05/11/2020 00:38, Chris M. Thomasson wrote:
>> On 10/31/2020 4:10 PM, Mr Flibble wrote:
>>> New lightweight C++ logging framework:
>>
>> nice. Fwiw, iirc, I used a little logging framework where each thread
>> had local memory it stored log info,

Forgot to say that I created it. Iirc, I wrote about it on Usenet
somewhere many years ago.


and a single logging thread that
>> would episodically grab all from each thread. So, a single atomic xchg
>> was used from the logging thread to gather and report. It was a simple
>> distributed multiple producer single consumer setup.
>>
>> log:
>>
>> A thread enters a log in its local memory.
>>
>>
>> log thread:
>>
>> grabs from every threads logs.
>>
>> reports to file.
>>
>> repeats.
>>
>> It scales fairly well.
>>
>>>
>
> Good idea, just implemented it.  Available on github now.

Nice! The scheme worked well. The consumer thread, or the logger if you
will, was able to take a threads entire log list using a single atomic
operation. Atomic Exchange. So, if you had say, 10 worker threads, the
single log thread would use exactly 10 atomic exchange op's per "log
collection" cycle.

Öö Tiib

unread,
Nov 6, 2020, 6:47:00 AM11/6/20
to
Certainly it is so but that is where my confusion started. I do
not understand why logger has to access i_logger objects that
are not logger's base subobjects? Feels a bit like case of
diffused responsibilities.

Mr Flibble

unread,
Nov 6, 2020, 10:51:16 AM11/6/20
to
I suggest you read up on the SOLID principles and idea that it makes good engineering sense to depend on abstractions and not concretions.

/Flibble

--
¬
0 new messages