I added sink to file via boost::log::add_file_log and console output via boost::log::add_console_log. I am calling a logger via BOOST_LOG_SEV and everything workds perfectely. But there is a place, where a want output only to the file.
How I can disable cosole output in certain place?
You could achieve this with attributes and filters. For example, you could set up a filter in your console sink to suppress any log records that have (or don't have, depending on your preference) a particular attribute value attached.
boost::log::add_console_log
(
...
boost::log::keywords::filter = !boost::log::expressions::has_attr("NoConsole")
...
);
Then you could set this attribute in the code region that shouldn't output logs in the console. For example, you could use a scoped attribute:
BOOST_LOG_SCOPED_THREAD_ATTR("NoConsole", true);
BOOST_LOG(logger) << "No console output";
You can use whatever method of setting the attribute - as a thread-local attribute, or a logger-specific, it doesn't matter.
The important difference from temporarily removing the sink is that the solution with attributes will not affect other threads that may be logging while you're suspending console output.
You can easely do it with remove_sink() function.
console_sink = boost::log::add_console_log(std::cout);
boost::log::core::get()->remove_sink(console_sink);
After that you can call an add_console_log() again and enable console output.
Related
I am trying to use the Windows Event Logging service and in struggle with RegisterEventSource owing to the insufficient/ambiguous description.
Let's assume "App1.exe" is the event source name that will be used from an application program App1.exe.
And I will use my own custom event log subkey named "MyCustLog" under which "App1.exe" subkey will be created.
So, my custom event log subkey for App1.exe will look like "... \MyCustLog\App1.exe" under which EventMessageFile/CategoryMessageFile/CategoryCount will be set as "App1.exe"/"App1.exe"/3, respectively.
Now, there happens and/or will happen to be another "App1.exe" event source name registered under a 3rd party custom event log key as shown below:
HKEY_LOCAL_MACHINE
SYSTEM
CurrentControlSet
Services
Eventlog
Application
Security
System
3rdPartyLog
App1.exe
After I create my own custom event log key it looks like:
HKEY_LOCAL_MACHINE
SYSTEM
CurrentControlSet
Services
Eventlog
Application
Security
System
3rdPartyLog
App1.exe
MyCustLog
App1.exe
Now, I call RegisterEventSource follows:
HANDLE hEvtSrc = RegisterEventSource(0, "App1.exe");
=> returns an event log handle to hEvtSrc, but how can I be sure that the handle is returned for MyCustLog's App1.exe, not 3rdPartyLog's App1.exe?
What I have tried:
I tried the following:
HANDLE hEvtSrc2 = RegisterEventSource(0, "MyCustLog\\App1.exe");
=> this also successfully returns an event log handle but not sure if it is for MyCustLog's App1.exe, nonetheless. Moreover, an event fired by the following ReportEvent with this handle shows failure description in its property window from Windows Event Log Viewer.
CONST LPCTSTR pBadCommand = _T("The command that was not valid");
DWORD dwEventDataSize = ((DWORD)_tcslen(pBadCommand) + 1) * sizeof(TCHAR);
BOOL bRet = ReportEvent(hEventLog, EVENTLOG_ERROR_TYPE, UI_CATEGORY, MSG_INVALID_COMMAND, NULL, 0, dwEventDataSize, NULL, (LPVOID)pBadCommand);
The failure message says: The description for event id MSG_INVALID_COMMAND cannot be found from the source MyCustLog\App1.exe
I also tried the third way:
HANDLE hEvtSrc3 = RegisterEventSource(0, "MyCustLog/App1.exe");
=> this again successfully returns an event log handle, but still an event fired by ReportEvent with this handle shows the almost the same failure description in its property window from event viewer. The only difference in failure message is MyCustLog/App1.exe instead of MyCustLog\App1.exe.
Therefore, "MyCustLog\App1.exe" or "MyCustLog/App1.exe" cannot be used instead of "App1.exe" to get the right handle of my own for RegisterEventSource. So we are back to the square one again.
So, once again, how can I be sure the returned handle for RegisterEventSource(0, "App1.exe") is for MyCustLog\App1.exe, not for 3rdPartyLog\App1.exe?
Below is shown the .mc file in use for App1.exe.
[Message file]
; // App1.mc
; // This is the header section.
SeverityNames=(Success=0x0:STATUS_SEVERITY_SUCCESS
Informational=0x1:STATUS_SEVERITY_INFORMATIONAL
Warning=0x2:STATUS_SEVERITY_WARNING
Error=0x3:STATUS_SEVERITY_ERROR
)
FacilityNames=(System=0x0:FACILITY_SYSTEM
Runtime=0x2:FACILITY_RUNTIME
Stubs=0x3:FACILITY_STUBS
Io=0x4:FACILITY_IO_ERROR_CODE
)
LanguageNames=(English=0x409:MSG00409)
; // The following are the categories of events.
MessageIdTypedef=WORD
MessageId=0x1
SymbolicName=NETWORK_CATEGORY
Language=English
Network Events
.
MessageId=0x2
SymbolicName=DATABASE_CATEGORY
Language=English
Database Events
.
MessageId=0x3
SymbolicName=UI_CATEGORY
Language=English
UI Events
.
; // The following are the message definitions.
MessageIdTypedef=DWORD
MessageId=0x100
Severity=Error
Facility=Runtime
SymbolicName=MSG_INVALID_COMMAND
Language=English
The command is not valid.
.
MessageId=0x101
Severity=Error
Facility=System
SymbolicName=MSG_BAD_FILE_CONTENTS
Language=English
File %1 contains content that is not valid.
.
MessageId=0x102
Severity=Warning
Facility=System
SymbolicName=MSG_RETRIES
Language=English
There have been %1 retries with %2 success! Disconnect from
the server and try again later.
.
MessageId=0x103
Severity=Informational
Facility=System
SymbolicName=MSG_COMPUTE_CONVERSION
Language=English
%1 %%4096 = %2 %%4097.
.
; // The following are the parameter strings */
MessageId=0x1000
Severity=Success
Facility=System
SymbolicName=QUARTS_UNITS
Language=English
quarts%0
.
MessageId=0x1001
Severity=Success
Facility=System
SymbolicName=GALLONS_UNITS
Language=English
gallons%0
.
If there are multiple App1.exe event sources, there is no way to tell RegisterEventSource() which source to use. RegisterEventSource() only accepts a source name, not a log name.
In fact, the Event Sources documentation says:
You cannot use a source name that has already been used as a log name. In addition, source names cannot be hierarchical; that is, they cannot contain the backslash character ("").
Which is why registering MyCustLog\App1.exe doesn't work, as it is an illegal source name. And why registering MyCustLog/App1.exe doesn't differentiate App1.exe between multiple logs, as / is not a hierarchy delimiter so RegisterEventSource() treats MyCustLog/App1.exe as the entire source name, not as App1.exe underneath MyCustLog.
So, since the source name for both logs is just App1.exe, you must use RegisterEventSource("App1.exe"), and so it will use the the first App1.exe it finds. And if it doesn't find any, it will fallback to using the Application log, which doesn't support category/message files.
So, make sure your event source is named uniquely. For instance, by NOT using your app's filename, but rather more descriptive names, eg:
HKEY_LOCAL_MACHINE
SYSTEM
CurrentControlSet
Services
Eventlog
My Custom Log
My Custom App Name
You can point My Custom App Name to the actual App1.exe file in its EventMessageFile/CategoryMessageFile values as needed.
And then you can use RegisterEventSource("My Custom App Name").
You should have a look at your local Registry to see how other apps register their event sources. It is actually rare for filenames to be used as key names. For example, here is what is registered in the eventlog key on my local system. As you can see, none of the log keys or app keys use filenames for their names:
So what is the advantage of using startTrackedFlow over startFlow?
The difference is defined in the official documentation:
The process of starting a flow returns a FlowHandle that you can use to observe the result, and which also contains a permanent identifier for the invoked flow in the form of the StateMachineRunId. Should you also wish to track the progress of your flow (see Progress tracking) then you can invoke your flow instead using CordaRPCOps.startTrackedFlowDynamic or any of its corresponding CordaRPCOps.startTrackedFlow extension functions. These will return a FlowProgressHandle, which is just like a FlowHandle except that it also contains an observable progress field.
I would like to add some extra data to my log output depending on where it was logged. For instance I might have System A and System B, both spitting out log data, but perhaps I'm only interested in System B's log lines now so I could filter based on the tag.
In a previous system I had a log function that looked like LOG(level, tag, message) but I could also see a solution that could involve instantiating a logger with a tag for each system that would pipe to a default logger that catches all messages. So spdlog::tagged_logger systemALogger("System A");
This is almost the answer since loggers can have names, I could use the names as a tag, but can loggers redirect to a default logger? The default logger has several sinks that I would have to attach to the named logging solution.
So the final question would be, is there a way to add a custom tag to log messages in spdlog?
Once you have your log configured, you can clone() it and pass a custom logger name that will appear in the log output (given you have not change the formatting).
auto logger = spdlog::default_logger()->clone("my_logger");
logger->info("message from custom logger");
spdlog::info("message from default logger");
The contents of the basic-log.txt is as follows:
[2020-12-02 09:35:58.801] [my_logger] [info] message from custom logger
[2020-12-02 09:35:58.802] [info] message from default logger
I'm trying to implement the default window with the default trigger to evaluate the behavior but it's not emitting any result.
According to Apache Beam:
The default trigger for a PCollection is based on event time, and
emits the results of the window when the Beam’s watermark passes the
end of the window, and then fires each time late data arrives.
If you are using both the default windowing configuration and
the default trigger, the default trigger emits exactly once, and late
data is discarded. This is because the default windowing configuration
has an allowed lateness value of 0.
my code:
Nb_items = lines | beam.CombineGlobally(beam.combiners.CountCombineFn()).without_defaults() \
| 'print' >> beam.ParDo(PrintFn())
It only emits the data if I set a trigger
Nb_items = lines | 'window' >> beam.WindowInto(window.GlobalWindows(),
trigger=trigger.AfterProcessingTime(10),
accumulation_mode=trigger.AccumulationMode.DISCARDING) \
| 'CountGlobally' >> beam.CombineGlobally(beam.combiners.CountCombineFn()).without_defaults() \
| 'print' >> beam.ParDo(PrintFn())
How can I observe the default behavior without setting a trigger?
Is the problem in the combine transform?
If your input PCollection
uses the default global windowing, the default behavior is to return a
PCollection containing one item. That item’s value comes from the
accumulator in the combine function that you specified when applying
Combine
The current issue is that the watermark never reaches the end of the GlobalWindow. To have a default trigger, you can use any other window where the watermark can reach the end, e.g.: 'window' >> beam.WindowInto(window.FixedWindows(10))
As Guillaume rightly asks, if you're running on Batch, triggers are basically ignored.
See Sources Below:
https://github.com/apache/beam/blob/828b897a2439437d483b1bd7f2a04871f077bde0/examples/java/src/main/java/org/apache/beam/examples/complete/game/LeaderBoard.java#L274
For more information regarding Google Cloud Dataflow
https://stackoverflow.com/a/54151029/12149235
I use several severity_channel_logger_mt instances throughout the project with different "Channel" attributes. However, for one specific log line, I'd like to set the "Channel" attribute directly in the call. Using the macro BOOST_LOG_CHANNEL_SEV(logger, channel, severity), this is actually not difficult to do. However, this changes the "Channel" attribute. Subsequent logging calls will not use the initial channel attribute, but the changed one from the last logging call.
The only way I found to change the channel attribute back to the original value is: to mis-use the open_record() function of the logger object.
My Question: is there a more elegant way of doing this? Is there perhaps a specific function that allows setting attributes of a logger directly?
Code snippet to highlight the procedure:
auto & lg = global_logger::get();
BOOST_LOG_CHANNEL_SEV(lg, "test-subsystem", exec_severity) << "Message 1";
// misuse the open_record call to set the channel attribute
// reset channel name back to "global"
auto rc = lg.open_record(boost::log::keywords::channel = "global" );
rc.reset(); // attempt to clean-up a bit
BOOST_LOG_CHANNEL(lg, exec_severity) << "Message 2";
In the above example, "Message 1" shall come from "test-subsystem", but the other messages shall come from "global" channel. If the open_record() and rc.reset(); lines are commented out, both messages come from the "test-system"
Update:
I ended up implementing a slightly different solution
I created a dedicated logger for these log messages
I use BOOST_LOG_CHANNEL_SEV() to log to this logger which takes an argument to set the "Channel" name on each call
The updated code snippet from above looks like this:
auto & stlog = global_logger::get();
auto & lg = special_logger::get();
BOOST_LOG_CHANNEL_SEV(lg, "test-subsystem", exec_severity) << "Message 1";
// this log line will be in channel "global" again
BOOST_LOG_SEV(stlog, exec_severity) << "Message 2";
is there a more elegant way of doing this?
As you can see in the channel feature reference section, there is a channel method, which can be used to set the channel name. This method is inherited by the logger.
However, it is generally advised to avoid modifying the channel name for performance reasons. When you have multiple distinct subsystems with corresponding channel names it is better to dedicate a separate logger for each subsystem. Otherwise you're paying performance overhead for setting channel name on each log record along with the necessary thread synchronization.