I am new to g3log. I compiled and ran one of the examples I found in the repo, but was not able to ,modify it to print the log messages to the console rather than a file.
The following lines from the example set the logger:
auto worker = g3::LogWorker::createLogWorker();
auto handle= worker->addDefaultLogger(argv[0], path_to_log_file);
g3::initializeLogging(worker.get());
Where the line in the middle sets the prefix of the log and gives the path where the log file should be created.
How can I modify this code to print to the console\both the console and the file?
Thanks,
Omer.
You could define a customized sink class with a message receiver function that forward the
stripped message to console without formatting. Below is simplest form of the implementation:
class CustomSink {
public:
void forwardLogToStdout(g3::LogMessageMover logEntry) {
std::cout << logEntry.get().message() << std::endl;
}
};
So, in your code, you only need to replacing the worker->addDefaultLogger(...) by this snippet:
auto handle = worker->addSink(std::make_unique<CustomSink>(), &CustomSink::forwardLogToStdout);
from g3log github to implement colored console output you need:
struct ColorCoutSink {
// Linux xterm color
// http://stackoverflow.com/questions/2616906/how-do-i-output-coloured-text-to-a-linux-terminal
enum FG_Color {YELLOW = 33, RED = 31, GREEN=32, WHITE = 97};
FG_Color GetColor(const LEVELS level) const
{
if (level.value == WARNING.value) { return YELLOW; }
if (level.value == DEBUG.value) { return GREEN; }
if (g3::internal::wasFatal(level)) { return RED; }
return WHITE;
}
void ReceiveLogMessage(g3::LogMessageMover logEntry)
{
auto level = logEntry.get()._level;
auto color = GetColor(level);
std::cout << "\033[" << color << "m" << logEntry.get().toString() << "\033[m" << std::endl;
}
};
// in main.cpp, main() function
auto sinkHandle = logworker->addSink(std::make_unique<CustomSink>(),
&CustomSink::ReceiveLogMessage);
So if you remove color "things", the logger will output to console
Related
I have the following code, and can be compiled, but when I run it, it fails with error of missing resource.
I have checked the cascade installer and everything is clicked and installed. How could I fix this?
#include <TDocStd_Application.hxx>
#include <TDataStd_Integer.hxx>
int main()
{
Handle(TDocStd_Application) app = new TDocStd_Application;
Handle(TDocStd_Document) doc;
app->NewDocument("BinOcaf", doc);
if (doc.IsNull())
{
std::cout << "Error: cannot create an OCAF document." << std::endl;
return 1;
}
// to access the main label, the transient data framework
TDF_Label mainLab = doc->Main();
// attach some integer value to this label
TDataStd_Integer::Set(mainLab, 1002);
// save document to file
PCDM_StoreStatus sstatus = app->SaveAs(doc, "C:/Users/Administrator/Desktop/test.cbf");
if (sstatus != PCDM_SS_OK)
{
app->Close(doc);
std::cout << "cannot write OCAF document." << std::endl;
return 1;
}
// release the data of doc
app->Close(doc);
return 0;
}
Ok, so after some head scratching I realized one thing. Forgot to define format.
just add the line of code to the main function would fix the problem.
BinDrivers::DefineFormate(app);
I am on the latest commit of spdlog (there is an issue regarding std output, apparently resolved), and am switching my output from std::cout to spdlog.
My google tests redirect std::cout so I can check the output of stub functions:
class MyTest : public testing::Test
{
protected:
void SetUp() override
{
sbuf = std::cout.rdbuf();
std::cout.rdbuf(buffer.rdbuf());
auto console = spdlog::stdout_color_mt("console");
auto err_logger = spdlog::stderr_color_mt("stderr");
}
void TearDown() override
{
std::cout.rdbuf(sbuf);
}
std::stringstream buffer;
std::streambuf* sbuf;
}
then use as follows inside a test;
doSomethingThatWritesToStdOut();
std::string teststr = buffer.str();
EXPECT_TRUE(teststr.find("Some output string") != std::string::npos);
This doesn't work when I change the content of doSomethingThatWritesToStdOut to
spdlog::get("console")->debug("Some output string\n");
The teststr value is empty..
If I do the following
spdlog::get("console")->debug("Some output string\n");
std::cout << "Some output string\n";
Then I can see one instance of "Some output string" in teststr. How can I capture the output of this logger (or change the logger) so I can test in google tests?
As I've mentioned in the comments, I had thought spdlog output to std::cout due to an earlier issue, but in fact that was related to stdout.. (facepalm)
This is nice and easy, it turns out! By using an ostream_sink, the output can be sent to a specified ostream;
I set a logger up in my test SetUp() function as follows
auto ostream_logger = spdlog::get("gtest_logger");
if (!ostream_logger)
{
auto ostream_sink = std::make_shared<spdlog::sinks::ostream_sink_st>(_oss);
ostream_logger = std::make_shared<spdlog::logger>("gtest_logger", ostream_sink);
ostream_logger->set_pattern(">%v<");
ostream_logger->set_level(spdlog::level::debug);
}
spdlog::set_default_logger(ostream_logger);
where _oss is a std::ostringstream.
Then my tests just look at the contents of _oss, and clear it after each check:
std::string test = _oss.str();
// check the derived class is constructed
EXPECT_TRUE(test.find("Constructing test class") != std::string::npos);
_oss.str("");
The existing code using spdlog::debug, spdlog::trace etc doesn't need changing at all.
For me, the accepted answer didn't work, because some functions would get the logger by name, instead of the default logger.
If you don't need to worry about thread safety, and you're in a similar situation, then you can simply change your logger's sink, instead of creating a new logger:
struct LoggerState {
spdlog::sink_ptr oldSink {nullptr};
spdlog::level::level_enum oldLevel;
};
LoggerState redirectLogger(
std::shared_ptr<spdlog::logger>& log,
std::ostringstream& oss,
spdlog::level::level_enum newLevel
){
LoggerState ls;
ls.oldLevel = log->level();
std::vector<spdlog::sink_ptr>& sinks { log->sinks() };
assert(sinks.size() == 1);
ls.oldSink = std::move(sinks[0]);
sinks[0] = std::make_shared<spdlog::sinks::ostream_sink_st>(oss);
log->set_pattern( "[%l] %v" );
log->set_level(newLevel);
return ls;
}
void resetLogger(
std::shared_ptr<spdlog::logger>& log,
const LoggerState& ls
){
log->sinks()[0] = std::move(ls.oldSink);
log->set_level(ls.oldLevel);
}
Use it in your code as such:
std::shared_ptr<spdlog::logger> yourLogger { /* get your logger */ };
std::ostringstream oss;
LoggerState oldState = redirectLogger(yourLogger, oss, spdlog::level::warn);
/* do something that produces logger output */
resetLogger(yourLogger, oldState);
/* now oss.str() holds the captured output,
* and your old logger should be good as new */
I know, I know - that question title is very much all over the place. However, I am not sure what could be an issue here that is causing what I am witnessing.
I have the following method in class Project that is being unit tested:
bool Project::DetermineID(std::string configFile, std::string& ID)
{
std::ifstream config;
config.open(configFile);
if (!config.is_open()) {
WARNING << "Failed to open the configuration file for processing ID at: " << configFile;
return false;
}
std::string line = "";
ID = "";
bool isConfigurationSection = false;
bool isConfiguration = false;
std::string tempID = "";
while (std::getline(config, line))
{
std::transform(line.begin(), line.end(), line.begin(), ::toupper); // transform the line to all capital letters
boost::trim(line);
if ((line.find("IDENTIFICATIONS") != std::string::npos) && (!isConfigurationSection)) {
// remove the "IDENTIFICATIONS" part from the current line we're working with
std::size_t idStartPos = line.find("IDENTIFICATIONS");
line = line.substr(idStartPos + strlen("IDENTIFICATIONS"), line.length() - idStartPos - strlen("IDENTIFICATIONS"));
boost::trim(line);
isConfigurationSection = true;
}
if ((line.find('{') != std::string::npos) && isConfigurationSection) {
std::size_t bracketPos = line.find('{');
// we are working within the ids configuration section
// determine if this is the first character of the line, or if there is an ID that precedes the {
if (bracketPos == 0) {
// is the first char
// remove the bracket and keep processing
line = line.substr(1, line.length() - 1);
boost::trim(line);
}
else {
// the text before { is a temp ID
tempID = line.substr(0, bracketPos - 1);
isConfiguration = true;
line = line.substr(bracketPos, line.length() - bracketPos);
boost::trim(line);
}
}
if ((line.find("PORT") != std::string::npos) && isConfiguration) {
std::size_t indexOfEqualSign = line.find('=');
if (indexOfEqualSign == std::string::npos) {
WARNING << "Unable to determine the port # assigned to " << tempID;
}
else {
std::string portString = "";
portString = line.substr(indexOfEqualSign + 1, line.length() - indexOfEqualSign - 1);
boost::trim(portString);
// confirm that the obtained port string is not an empty value
if (portString.empty()) {
WARNING << "Failed to obtain the \"Port\" value that is set to " << tempID;
}
else {
// attempt to convert the string to int
int workingPortNum = 0;
try {
workingPortNum = std::stoi(portString);
}
catch (...) {
WARNING << "Failed to convert the obtained \"Port\" value that is set to " << tempID;
}
if (workingPortNum != 0) {
// check if this port # is the same port # we are publishing data on
if (workingPortNum == this->port) {
ID = tempID;
break;
}
}
}
}
}
}
config.close();
if (ID.empty())
return false;
else
return true;
}
The goal of this method is to parse any text file for the ID portion, based on matching the port # that the application is publishing data to.
Format of the file is like this:
Idenntifications {
ID {
port = 1001
}
}
In a separate Visual Studio project that unit tests various methods, including this Project::DetermineID method.
#define STRINGIFY(x) #x
#define EXPAND(x) STRINGIFY(x)
TEST_CLASS(ProjectUnitTests) {
Project* parser;
std::string projectDirectory;
TEST_METHOD_INITIALIZE(ProjectUnitTestInitialization) {
projectDirectory = EXPAND(UNITTESTPRJ);
projectDirectory.erase(0, 1);
projectDirectory.erase(projectDirectory.size() - 2);
parser = Project::getClass(); // singleton method getter/initializer
}
// Other test methods are present and pass/fail accordingly
TEST_METHOD(DetermineID) {
std::string ID = "";
bool x = parser ->DetermineAdapterID(projectDirectory + "normal.cfg", ID);
Assert::IsTrue(x);
}
};
Now, when I run the tests, DetermineID fails and the stack trace states:
DetermineID
Source: Project Tests.cpp line 86
Duration: 2 sec
Message:
Assert failed
Stack Trace:
ProjectUnitTests::DetermineID() line 91
Now, in my test .cpp file, TEST_METHOD(DetermineID) { is present on line 86. But that method's } is located on line 91, as the stack trace indicates.
And, when debugging, the unit test passes, because the return of x in the TEST_METHOD is true.
Only when running the test individually or running all tests does that test method fail.
Some notes that may be relevant:
This is a single-threaded application with no tasks scheduled (no race condition to worry about supposedly)
There is another method in the Project class that also processes a file with an std::ifstream same as this method does
That method has its own test method that has been written and passes without any problems
The test method also access the "normal.cfg" file
Yes, this->port has an assigned value
Thus, my questions are:
Why does the stack trace reference the closing bracket for the test method instead of the single Assert within the method that is supposedly failing?
How to get the unit test to pass when it is ran? (Since it currently only plasses during debugging where I can confirm that x is true).
If the issue is a race condition where perhaps the other test method is accessing the "normal.cfg" file, why does the test method fail even when the method is individually ran?
Any support/assistance here is very much appreciated. Thank you!
In order to read all shapes within my step file, I have succeed to extract all shapes using STEPControl_reader.
Now I want to find a way (OCAF/XDE ?) to extract a tree view containing which shape is contained by another one.
Could anyone give some pointers to examples extracting those informations in step file using OCAF or XDE.
I have a difficulty to understand the official document because it has a lack of examples.
EDIT:
By using the Mayo project:
You can use the XdeDocumentItem::XdeDocumentItem(...) constructor and recursively creating the nodes.
XdeDocumentItem::XdeDocumentItem(const Handle_TDocStd_Document &doc)
: m_cafDoc(doc),
m_shapeTool(XCAFDoc_DocumentTool::ShapeTool(doc->Main())),
m_colorTool(XCAFDoc_DocumentTool::ColorTool(doc->Main())){
this->rebuildAssemblyTree();}
The method rebuildAssemblyTree is like that:
for (const TDF_Label& rootLabel : this->topLevelFreeShapes())
this->deepBuildAssemblyTree(0, rootLabel);
You may use class XCAFPrs_DocumentExplorer for stack-alike traverse within OCCT 7.4.0+, and the following code snippet (based on XDisplay Draw Harness command) for traverse with recursive function calls:
//! Handle document root shapes.
int traverseDocument (const Handle(TDocStd_Document)& theDoc)
{
TDF_LabelSequence aLabels;
XCAFDoc_DocumentTool::ShapeTool (theDoc->Main())->GetFreeShapes (aLabels);
for (TDF_LabelSequence::Iterator aLabIter (myLabels); aLabIter.More(); aLabIter.Next())
{
const TDF_Label& aLabel = aLabIter.Value();
if (traverseLabel (aLabel, "", TopLoc_Location()) == 1)
{
return 1;
}
}
return 0;
}
//! Handle single label.
int traverseLabel (const TDF_Label& theLabel,
const TCollection_AsciiString& theNamePrefix,
const TopLoc_Location& theLoc)
{
TCollection_AsciiString aName;
{
Handle(TDataStd_Name) aNodeName;
if (theLabel.FindAttribute (TDataStd_Name::GetID(), aNodeName))
{
aName = aNodeName->Get(); // instance name
}
if (aName.IsEmpty())
{
TDF_Label aRefLabel;
if (XCAFDoc_ShapeTool::GetReferredShape (theLabel, aRefLabel)
&& aRefLabel.FindAttribute (TDataStd_Name::GetID(), aNodeName))
{
aName = aNodeName->Get(); // product name
}
}
}
aName = theNamePrefix + aName;
TDF_Label aRefLabel = theLabel;
XCAFDoc_ShapeTool::GetReferredShape (theLabel, aRefLabel);
if (XCAFDoc_ShapeTool::IsAssembly (aRefLabel))
{
aName += "/";
const TopLoc_Location aLoc = theLoc * XCAFDoc_ShapeTool::GetLocation (theLabel);
for (TDF_ChildIterator aChildIter (aRefLabel); aChildIter.More(); aChildIter.Next())
{
if (traverseLabel (aChildIter.Value(), aName, aLoc) == 1)
{
return 1;
}
}
return 0;
}
std::cout << aName << " ";
return 0;
}
I am trying to use a rich edit control to output some text on the screen:
Monday Press 1.
Your day is Monday
Tuesday Press 2.
I can't really find any simple examples of how to do this. all i have been able to sort out is setting the window text (setWindowText), but everything else is escaping me.
Any short examples?
Despite the comments, I'm going to answer the question you asked, about how to format data in a Rich Edit control. A few years ago, I had to do this, and came up with something that I could treat a little like an IOstream (if I were doing it today, I'd probably do it a bit differently, but such is life).
First, code to act like an IOstream, but write to a rich-edit control:
// rich_stream.h:
#ifndef RICH_STREAM_H
#define RICH_STREAM_H
class rich_stream {
CRichEditCtrl &ctrl;
public:
rich_stream(CRichEditCtrl &ctrl_) : ctrl(ctrl_) { }
void add_text(char const *txt) {
ctrl.SetSel(-1,-1);
ctrl.ReplaceSel(txt);
}
void add_int(int val) {
CString temp;
temp.Format("%d", val);
add_text(temp);
}
void set_char_format(CHARFORMAT &fmt) {
ctrl.SetSelectionCharFormat(fmt);
}
};
inline rich_stream &operator<<(rich_stream &s, char const *t) {
s.add_text(t);
return s;
}
inline rich_stream &operator<<(rich_stream &s, CHARFORMAT &fmt) {
s.set_char_format(fmt);
return s;
}
inline CString nl() {
return CString("\n\n");
}
inline rich_stream &operator<<(rich_stream &s, CString (*f)()) {
s.add_text(f());
return s;
}
inline rich_stream &operator<<(rich_stream &s, int val) {
s.add_int(val);
return s;
}
#endif
Then, I'd use this something like:
CHARFORMAT bold;
memset(&bold, 0, sizeof(bold));
bold.cbSize = sizeof(bold);
bold.dwMask = CFM_BOLD | CFM_FACE | CFM_SIZE;
bold.dwEffects = CFE_BOLD;
strcpy(bold.szFaceName, "Times");
bold.yHeight = 14 * 20;
CHARFORMAT normal;
memset(&normal, 0, sizeof(normal));
normal.cbSize = sizeof(normal);
normal.dwMask = CFM_BOLD | CFM_FACE | CFM_SIZE;
normal.dwEffects = 0;
strcpy(normal.szFaceName, "Times");
normal.yHeight = 14 * 20;
// ...
rich_stream txt(GetRichEditCtrl());
txt << bold << "Heading 1: " << normal << info1 << nl
<< bold << "Heading 2: " << normal << info2 << nl
<< bold << "Heading 3: " << normal << info3;
If I were doing this today, I'd almost certainly create a small class as a wrapper for a CHARFORMAT so I could construct the formatting objects a little more cleanly. I'd probably also at least think hard about implementing it as a normal iostream with a stream buffer that inserted data into the rich edit control (but at the time I didn't know streams well enough to know I should do that).
Glancing at it, there are a few other things that aren't really exactly right either -- add_text uses SetSel(-1, -1);. This should really retrieve the current length of the text (e.g., with GetWindowTextLength, and set the selection to just after the end.
Use Wordpad, it's an RichEdit control too. It will generate your RTF in a way that's naturally compatible with your control.