-
Notifications
You must be signed in to change notification settings - Fork 27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/thread safe logging #139
base: develop
Are you sure you want to change the base?
Changes from all commits
c7f0675
0407bb1
d9c9862
d4825ae
2d73e73
82a17f5
60c5399
225e43a
88e5eed
0faf773
4e59bf0
ae98034
346a23c
0519c53
5dd813f
3459dde
1e89c86
7c3c1bc
6ddfc94
c948b30
bfdce18
8444660
25fac98
2bf8c5e
0d49f7a
3f8b957
0ba7337
1968f5d
86ec904
8caa48e
19507a3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,6 +20,7 @@ | |
|
||
namespace eckit { | ||
|
||
class VoidBuffer; | ||
class ChannelBuffer; | ||
class LogTarget; | ||
|
||
|
@@ -29,8 +30,7 @@ typedef void (*channel_callback_t)(void* data, const char* msg); | |
|
||
/// Output channel that is an std::ostream but more functional | ||
|
||
class Channel : public std::ostream, private NonCopyable { | ||
|
||
class Channel: public std::ostream, private NonCopyable { | ||
public: // methods | ||
Channel(LogTarget* = 0); | ||
|
||
|
@@ -39,7 +39,7 @@ class Channel : public std::ostream, private NonCopyable { | |
bool operator!() const; | ||
operator bool() const; | ||
|
||
void indent(const char* prefix = ""); | ||
void indent(const char* space = ""); | ||
void unindent(); | ||
|
||
void setStream(std::ostream& out); | ||
|
@@ -51,40 +51,56 @@ class Channel : public std::ostream, private NonCopyable { | |
void setCallback(channel_callback_t cb, void* data = 0); | ||
void addCallback(channel_callback_t cb, void* data = 0); | ||
|
||
void setTarget(LogTarget*); | ||
void addTarget(LogTarget*); | ||
void setTarget(LogTarget* target); | ||
void addTarget(LogTarget* target); | ||
|
||
void reset(); | ||
|
||
private: // members | ||
protected: // methods | ||
explicit Channel(VoidBuffer* dummy); | ||
|
||
private: // methods | ||
explicit Channel(ChannelBuffer* buffer); | ||
|
||
virtual void print(std::ostream& s) const; | ||
|
||
friend std::ostream& operator<<(std::ostream& os, const Channel& c) { | ||
c.print(os); | ||
return os; | ||
} | ||
|
||
void print(std::ostream& s) const; | ||
|
||
ChannelBuffer* buffer_; | ||
private: // members | ||
ChannelBuffer* buffer_ {nullptr}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this should be a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is currently explicitly deleted in the constructor. Please change to unique_ptr. Is a good change. |
||
|
||
friend class Log; | ||
}; | ||
|
||
|
||
//---------------------------------------------------------------------------------------------------------------------- | ||
|
||
|
||
class AutoIndent { | ||
|
||
Channel& channel_; | ||
|
||
public: | ||
AutoIndent(Channel& channel, const char* prefix = "") : | ||
channel_(channel) { channel_.indent(prefix); } | ||
AutoIndent(Channel& channel, const char* prefix = ""): channel_(channel) { channel_.indent(prefix); } | ||
~AutoIndent() { channel_.unindent(); } | ||
}; | ||
|
||
//---------------------------------------------------------------------------------------------------------------------- | ||
|
||
class EmptyChannel: public Channel { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Two remarks: 1.) This should be named 2.) Is this type actually needed? A |
||
public: // methods | ||
EmptyChannel(); | ||
|
||
~EmptyChannel() = default; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this needs to be made virtual with |
||
|
||
private: // methods | ||
void print(std::ostream& s) const override { s << "EmptyChannel()"; } | ||
|
||
friend class Log; | ||
}; | ||
|
||
//---------------------------------------------------------------------------------------------------------------------- | ||
|
||
} // namespace eckit | ||
|
||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,20 +23,25 @@ namespace eckit { | |
|
||
//---------------------------------------------------------------------------------------------------------------------- | ||
|
||
ChannelBuffer::ChannelBuffer(std::size_t size) : std::streambuf(), target_(0), buffer_(size) { | ||
ASSERT(size); | ||
ChannelBuffer::ChannelBuffer(const std::size_t size): buffer_(size) { | ||
init(); | ||
} | ||
|
||
void ChannelBuffer::init() { | ||
setp(buffer_.data(), buffer_.data() + buffer_.size()); | ||
} | ||
|
||
ChannelBuffer::~ChannelBuffer() { | ||
reset(); | ||
if (target_) { | ||
target_->detach(); | ||
target_ = nullptr; | ||
} | ||
} | ||
|
||
bool ChannelBuffer::active() const { | ||
return target_ != 0; | ||
} | ||
|
||
|
||
void ChannelBuffer::setTarget(LogTarget* target) { | ||
ASSERT(target); | ||
|
||
|
@@ -72,7 +77,7 @@ bool ChannelBuffer::dumpBuffer() { | |
// Explicitly check that `pptr()` is not larger than end of buffer. Racecondition can end up adding larger values. | ||
target_->write(buffer_.data(), std::min(pptr(), buffer_.data() + buffer_.size())); | ||
} | ||
setp(buffer_.data(), buffer_.data() + buffer_.size()); | ||
init(); | ||
return true; | ||
} | ||
|
||
|
@@ -108,11 +113,11 @@ void ChannelBuffer::setStream(std::ostream& out) { | |
setTarget(new OStreamTarget(out)); | ||
} | ||
|
||
void ChannelBuffer::addFile(const std::string& path, size_t bufferSize) { | ||
void ChannelBuffer::addFile(const std::string& path, const std::size_t bufferSize) { | ||
setTarget(new TeeTarget(target_, new FileTarget(path, bufferSize))); | ||
} | ||
|
||
void ChannelBuffer::setFile(const std::string& path, size_t bufferSize) { | ||
void ChannelBuffer::setFile(const std::string& path, const std::size_t bufferSize) { | ||
setTarget(new FileTarget(path, bufferSize)); | ||
} | ||
|
||
|
@@ -144,4 +149,26 @@ void ChannelBuffer::print(std::ostream& s) const { | |
|
||
//---------------------------------------------------------------------------------------------------------------------- | ||
|
||
VoidBuffer::VoidBuffer(): ChannelBuffer(0) { } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this can be defaulted |
||
|
||
VoidBuffer::~VoidBuffer() = default; | ||
|
||
bool VoidBuffer::dumpBuffer() { | ||
return true; | ||
} | ||
|
||
std::streambuf::int_type VoidBuffer::overflow(std::streambuf::int_type ch) { | ||
return ch; | ||
} | ||
|
||
std::streambuf::int_type VoidBuffer::sync() { | ||
return 0; | ||
} | ||
|
||
void VoidBuffer::print(std::ostream& os) const { | ||
os << "VoidBuffer"; | ||
} | ||
|
||
//---------------------------------------------------------------------------------------------------------------------- | ||
|
||
} // namespace eckit |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,7 +16,10 @@ | |
#ifndef eckit_log_ChannelBuffer_h | ||
#define eckit_log_ChannelBuffer_h | ||
|
||
#include <cstddef> | ||
#include <ostream> | ||
#include <streambuf> | ||
#include <string> | ||
#include <vector> | ||
|
||
#include "eckit/log/Channel.h" | ||
|
@@ -28,15 +31,12 @@ namespace eckit { | |
|
||
class LogTarget; | ||
|
||
/// Stream buffer to be usedby Channel | ||
/// Stream buffer to be used by Channel | ||
class ChannelBuffer : public std::streambuf, private NonCopyable { | ||
private: // types | ||
static constexpr const std::size_t DEFAULT_SIZE = 1024; | ||
|
||
private: // methods | ||
/// constructor, taking ownership of stream | ||
ChannelBuffer(std::size_t size = 1024); | ||
|
||
~ChannelBuffer() override; | ||
|
||
bool active() const; | ||
|
||
void reset(); | ||
|
@@ -50,13 +50,19 @@ class ChannelBuffer : public std::streambuf, private NonCopyable { | |
void setStream(std::ostream& out); | ||
void addStream(std::ostream& out); | ||
|
||
void setFile(const std::string& path, size_t bufferSize = 4 * 1024); | ||
void addFile(const std::string& path, size_t bufferSize = 4 * 1024); | ||
void setFile(const std::string& path, std::size_t bufferSize = 4 * 1024); | ||
void addFile(const std::string& path, std::size_t bufferSize = 4 * 1024); | ||
|
||
void setCallback(channel_callback_t cb, void* data = 0); | ||
void addCallback(channel_callback_t cb, void* data = 0); | ||
|
||
protected: // methods | ||
ChannelBuffer(std::size_t size = DEFAULT_SIZE); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not in favor of this change because I believe this to be going into the wrong direction. Ideally |
||
|
||
~ChannelBuffer() override; | ||
|
||
void init(); | ||
|
||
/// override this to change buffer behavior | ||
/// @returns true if no error occured | ||
virtual bool dumpBuffer(); | ||
|
@@ -69,24 +75,45 @@ class ChannelBuffer : public std::streambuf, private NonCopyable { | |
/// @see dumpBuffer | ||
int_type sync() override; | ||
|
||
protected: // members | ||
LogTarget* target_; | ||
|
||
std::vector<char> buffer_; | ||
|
||
private: | ||
friend std::ostream& operator<<(std::ostream& os, const ChannelBuffer& c) { | ||
c.print(os); | ||
return os; | ||
} | ||
|
||
void print(std::ostream& s) const; | ||
virtual void print(std::ostream& s) const; | ||
|
||
protected: // members | ||
LogTarget* target_ {nullptr}; | ||
|
||
std::vector<char> buffer_; | ||
|
||
friend class Channel; | ||
}; | ||
|
||
//---------------------------------------------------------------------------------------------------------------------- | ||
|
||
/// Channel buffer that voidify output streams | ||
class VoidBuffer: public ChannelBuffer { | ||
private: // methods | ||
VoidBuffer(); | ||
|
||
~VoidBuffer(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. needs to be virtual / override as it is polymorphically destroyed from |
||
|
||
protected: // methods | ||
bool dumpBuffer() override; | ||
|
||
int_type overflow(int_type ch) override; | ||
|
||
int_type sync() override; | ||
|
||
private: // methods | ||
void print(std::ostream& os) const override; | ||
|
||
friend class EmptyChannel; | ||
}; | ||
|
||
//---------------------------------------------------------------------------------------------------------------------- | ||
|
||
} // namespace eckit | ||
|
||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From a first glance I disagree with the renaming because It looks like I can use an arbitrary character sequence here while I interpret indentation either as
<space>
vs<tab>
or a numerical value e.g. 5 spaces.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also disagree with this renaming. Although, that said, it is plausible to use non-space indents (e.g. ">>>" or "...")