How to use custom logger with websocketpp? - c++

I am creating a telemetry server using websocketpp, and have followed the example here. My application will be running as a linux daemon which starts on boot, and therefore I won't be able to write logs to standard out. I would therefore like to add a customer logger using spdlog, and understand that it can be done based on what's on this page. Looks like I need to use the websocketpp::log::stub interface to create my own customer logger. The issue is, the documentation on this is quite limited regarding logging, and I am not sure where to begin and how to incorporate it in the context of the telemetry server example linked above. I am not sure how to specify the logger when I define my server: typedef websocketpp::server<websocketpp::config::asio> server;.
How do I go about extending the stub class, and how do I initialize my server with this customer logger?
The only sample code I could find is in this thread here, but based on the linked comment this code is no longer relevant after V 0.3.x+.

For anyone wanting sample code using spdlog as a customer logger, here it is:
Create a new file customerLogger.hpp with the contents:
#pragma once
#include <websocketpp/logger/basic.hpp>
#include <websocketpp/common/cpp11.hpp>
#include <websocketpp/logger/levels.hpp>
#include "spdlog/logger.h"
#include "spdlog/sinks/rotating_file_sink.h"
namespace websocketpp {
namespace log {
/// Basic logger that outputs to syslog
template <typename concurrency, typename names>
class myLogger : public basic<concurrency, names> {
public:
typedef basic<concurrency, names> base;
/// Construct the logger
/**
* #param hint A channel type specific hint for how to construct the logger
*/
myLogger<concurrency,names>(channel_type_hint::value hint =
channel_type_hint::access)
: basic<concurrency,names>(hint), m_channel_type_hint(hint) {
auto max_size = 1048576 * 5;
auto max_files = 3;
auto rotating_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt> ("/var/logs/my_logger.log", max_size, max_files);
m_logger = std::make_shared<spdlog::logger>("my_logger", rotating_sink);
m_logger->flush_on(spdlog::level::info);
m_logger->set_level(spdlog::level::level_enum::info);
}
/// Construct the logger
/**
* #param channels A set of channels to statically enable
* #param hint A channel type specific hint for how to construct the logger
*/
myLogger<concurrency,names>(level channels, channel_type_hint::value hint =
channel_type_hint::access)
: basic<concurrency,names>(channels, hint), m_channel_type_hint(hint) {
auto max_size = 1048576 * 5;
auto max_files = 3;
auto rotating_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt> ("/var/logs/my_logger.log", max_size, max_files);
m_logger = std::make_shared<spdlog::logger>("my_logger", rotating_sink);
m_logger->flush_on(spdlog::level::info);
m_logger->set_level(spdlog::level::level_enum::info);
}
/// Write a string message to the given channel
/**
* #param channel The channel to write to
* #param msg The message to write
*/
void write(level channel, std::string const & msg) {
write(channel, msg.c_str());
}
/// Write a cstring message to the given channel
/**
* #param channel The channel to write to
* #param msg The message to write
*/
void write(level channel, char const * msg) {
scoped_lock_type lock(base::m_lock);
if (!this->dynamic_test(channel)) { return; }
if (m_channel_type_hint == channel_type_hint::access) {
m_logger->info(msg);
} else {
if (channel == elevel::devel) {
m_logger->debug(msg);
} else if (channel == elevel::library) {
m_logger->debug(msg);
} else if (channel == elevel::info) {
m_logger->info(msg);
} else if (channel == elevel::warn) {
m_logger->warn(msg);
} else if (channel == elevel::rerror) {
m_logger->error(msg);
} else if (channel == elevel::fatal) {
m_logger->critical(msg);
}
}
}
private:
std::shared_ptr<spdlog::logger> m_logger;
typedef typename base::scoped_lock_type scoped_lock_type;
channel_type_hint::value m_channel_type_hint;
};
} // log
} // websocketpp
Next, create another file, customConfig.hpp which has the following content:
#pragma once
#include "./customLogger.hpp"
#include <websocketpp/logger/syslog.hpp>
#include <websocketpp/extensions/permessage_deflate/enabled.hpp>
// Custom server config based on bundled asio config
struct my_config : public websocketpp::config::asio {
// Replace default stream logger with the custom logger
typedef websocketpp::log::myLogger<concurrency_type, websocketpp::log::elevel> elog_type;
typedef websocketpp::log::myLogger<concurrency_type, websocketpp::log::alevel> alog_type;
};
typedef websocketpp::server<my_config> my_server;
Finally, when you want to create the server, you simple do my_server endpoint;

Building a custom logger has two steps. First, write a policy class with the appropriate interface then create a custom config that uses that policy.
To write the policy class websocketpp::log::stub is a minimal implementation that doesn't actually do anything (it is primarily used for stubbing out logging in the unit tests) but it demonstrates and documents the interface that a logging class needs to implement. The logging class does not need to be a subclass of websocketpp::log::stub. You can look at other examples in the websocketpp/logger/* folder. The syslog logger in particular might be interesting as an example of a logging policy that outputs to something other than standard out.
To set up the custom config you will create a config class. It can be standalone or a subclass of one of the standard ones, like websocketpp::config::asio, that just overrides a small number of things. Your config might only override the loggers, for example. Once created, you will pass your config class into the endpoint template parameter instead of websocketpp::config::asio.
More details about what you can override at compile time via this config system can be found at https://docs.websocketpp.org/reference_8config.html. There is an example on this page that shows a custom config that replaces the default logger (among other changes).

Related

Not properly executed overridden code using an INET extended module

I have to extend the UdpBasicApp module from INET4 in OMNeT++ 5.6.1 (Ubuntu 18.04.4) and execute two overridden methods (initialize and handleMessageWhenUp).
It seems an easy task, and looked like it was all fine when I built the project (momentarily stored inside the "examples" folder in inet4) with no errors but, after starting the simulation the code isn't successfully overridden. The original code (the one from UdpBasicApp) is executed instead.
In fact, after inspecting the code in the editor, the methods' name isn't bold (as should be) and the keywords aren't colored as they normally should be.
My C++ skill is not high (nor my OMNeT one) hence I'm not sure what is really happening.
The extended simple module is called "PriorityApp" (I added just an integer field more).
This is the .ned code (not sure if you need it)
package inet.examples.MyProject.src;
import inet.applications.udpapp.UdpBasicApp;
// The module extends UdpBasicApp adding a priority parameter
simple PriorityApp extends UdpBasicApp
{
parameters:
int priority = default(0); // priority 0 = higher priority
}
This is the .h code
#ifndef __INET4_PRIORITYAPP_H_
#define __INET4_PRIORITYAPP_H_
#include "inet/applications/udpapp/UdpBasicApp.h"
namespace inet {
class PriorityApp : public UdpBasicApp
{
protected:
int priority;
virtual void initialize(int stage) override;
virtual void handleMessageWhenUp(cMessage *msg) override;
};
} // namespace inet
#endif // ifndef __INET_PRIORITYAPP_H
This is the .cc code
#include "PriorityApp.h"
namespace inet {
Define_Module(PriorityApp);
void PriorityApp::initialize(int stage)
{
msg = new cMessage("priority");
EV << "Example Message" << endl;
}
void PriorityApp::handleMessageWhenUp(cMessage *msg)
{
}
} //namespace inet
This is the .ini file
[General]
[Config SwitchedNet1G]
description = "1Gbps Switched Network"
network = SwitchedNet
**.dataRate = 1Gbps # 1 Gbps channel datarate
# Priority stuff
**.host[0].app[0].priority = 1
# Dst Params
**.sink.app[*].typename = "UdpSink" # Destination host (it receives messages)
**.sink.app[*].localPort = 2500
# Src Params
**.host[0].app[0].packetName = "Host0-Data"
**.host[*].app[0].typename = "PriorityApp" # Source host (they send messages)
**.host[*].app[0].destPort = 2500
**.host[*].app[0].destAddresses = "sink"
**.host[0].app[0].sendInterval = 3s
**.host[*].app[0].startTime = uniform (0s, 3s)
# EthInterface Setup
**.eth[*].qEncap.typename = "Ieee8021qEncap" # 8021Q Encapsulation
**.etherSwitch.numEthInterfaces = 2 # Switch ethInterfaces total number (2)
**.numEthInterfaces = 1
# Global Params
**.messageLength = 100B
**.numHosts = 1
**.numApps = 1 # every host has 1 app (UdpBasicApp or UdpSink)
There are two independent faults here:
1.) You should NOT add C++ code into INET's examples folder. That folder is NOT designated as a source folder, so the build system will not pick up, build and link to the executable, meaning that your code is not present in the model. That's one reason why you are not getting your new behavior.
2.) Even if you place your source code in the src folder along with the UdpBasicApp and the build system would properly link it in, you still have to properly set up the NED part. Specifically you have to specify the #class property to tell NED which C++ class you intend to use to specify behavior: #class(PriorityApp) Otherwise your PriorityApp module would inherit the value of #class property from UdpBasicApp and would use the CdpBasicApp C++ class.
import inet.applications.udpapp.UdpBasicApp;
// The module extends UdpBasicApp adding a priority parameter
simple PriorityApp extends UdpBasicApp
{
#class(PriorityApp)
parameters:
int priority = default(0); // priority 0 = higher priority
}

Custom resource in Tensorflow

For some reasons, I need to implement a custom resource for Tensorflow. I tried to get inspiration from lookup table implementations. If I understood well, I need to implement 3 TF operations:
creation of my resource
initialization of the resource (e.g. populate the hash table in case of the lookup table)
implementation of the find / lookup / query step.
To facilitate the implementation, I'm relying on tensorflow/core/framework/resource_op_kernel.h. I get the following error
[F tensorflow/core/lib/core/refcount.h:90] Check failed: ref_.load() == 0 (1 vs. 0)
1] 29701 abort python test.py
Here is the full code to reproduce the issue:
using namespace tensorflow;
/** CUSTOM RESOURCE **/
class MyVector : public ResourceBase {
public:
string DebugString() override { return "MyVector"; };
private:
std::vector<int> vec_;
};
/** CREATE VECTOR **/
REGISTER_OP("CreateMyVector")
.Attr("container: string = ''")
.Attr("shared_name: string = ''")
.Output("resource: resource")
.SetIsStateful();
class MyVectorOp : public ResourceOpKernel<MyVector> {
public:
explicit MyVectorOp(OpKernelConstruction* ctx) : ResourceOpKernel(ctx) {}
private:
Status CreateResource(MyVector** resource) override {
*resource = CHECK_NOTNULL(new MyVector);
if(*resource == nullptr) {
return errors::ResourceExhausted("Failed to allocate");
}
return Status::OK();
}
Status VerifyResource(MyVector* vec) override {
return Status::OK();
}
};
REGISTER_KERNEL_BUILDER(Name("CreateMyVector").Device(DEVICE_CPU), MyVectorOp)
and then, after compiling, the error can be reproduced with this Python snippet of code:
test_module = tf.load_op_library('./test.so')
my_vec = test_module.create_my_vector()
with tf.Session() as s:
s.run(my_vec)
As a side question, I'd be interested in having tutorials / guidelines to implement custom resources. In particular, I'd like to have information about what needs to be implemented for checkpoints / graph export / serialization / etc.
Thanks a lot.
Add -DNDEBUG to your build flags.
This workaround is explained in TF issue 17316.

How to use audioDeviceIOCallback, audioDeviceAboutToStart and audioDeviceStopped in a simple audio recorder program

Using Juce, I tried to create a simple audio recorder from the demo runner.
I've extracted the AudioRecordingDemo to create audio recorder based on a simple audio project.
When I'm clicking on the start recording button, the sampleRate still have the default value:
void startRecording(const File& file)
{
stop();
if (sampleRate > 0)
{
// Create an OutputStream to write to our destination file...
sampleRate = 0.0
In the AudioRecordingDemo, the audioDeviceAboutToStart increment the sample rate. But I don't have any AudioIODevice in my main component.
void audioDeviceAboutToStart(AudioIODevice* device) override
{
sampleRate = device->getCurrentSampleRate();
}
The AudioIODeviceCallback that sets up the input and ouput is also never called in my code. I tried to use it in my MainComponent class without success.
I also tried to make the MainComponent class inherits from the AudioIODeviceCallback :
class MainComponent : public Component,
private AudioIODeviceCallback,
private MidiInputCallback
{
public:
//...
I found this method in the Build a multi-polyphonic synthesiser tutorial.
But when I tried this, I got an overwriting error in the main class.
So here is my problem, how to use the AudioIODeviceCallback, audioDeviceAboutToStart and audioDeviceStopped that are defined in the AudioRecordingDemo class in my project ?
You can find the source code here.
I fixed my problem by adding the audioDeviceIOCallback to my MainComponent Class :
void audioDeviceIOCallback (const float** /*inputChannelData*/, int /*numInputChannels*/,
float** outputChannelData, int numOutputChannels,
int numSamples) override
{
// make buffer
AudioBuffer<float> buffer (outputChannelData, numOutputChannels, numSamples);
// clear it to silence
buffer.clear();
}
The source code up to date : SimpleAudioRecorder

Using GoogleMock to mock an instance created by code under test

I have create an interface (here's an example):
class DataStream
{
virtual std::string read(std::string terminator) = 0;
virtual size_t write(std::string data) = 0;
};
For which there exists a concrete implementation, such as:
class SerialDataStream : public DataStream
{
public:
// NOTE: This constructor will throw an exception if the
// serial port cannot be opened.
SerialDataStream(string port, int baudrate);
std::string read(std::string terminator);
size_t write(std::string data);
}
And the interface is used, for example:
class SomeThing
{
public:
SomeThing(std::shared_ptr<DataStream> stream);
}
Using GoogleMock, testing the SomeThing class is fairly straight forward, all you need to do is create a mock implementation for the interface, for example:
class MockDataStream : public DataStream
{
public:
MOCK_METHOD1(read, size_t(std::vector<uint8_t>&));
MOCK_METHOD1(write, size_t(std::vector<uint8_t>&));
}
Where the test would look something like this:
std::shared_ptr<MockDataStream> mock_stream(nullptr);
mock_stream = std::make_shared<MockDataStream>();
EXPECT_CALL(*mock_stream, write("START")).Times(AtLeast(1));
EXPECT_CALL(*mock_stream, read("\n")).Times(AtLeast(1));
SomeThing some_thing = SomeThing(mock_stream);
Which is pretty cool as it easily allows me to unittest how the SomeThing class uses the DataStream interface.
However, there also exists some code whose job it is to create new (concrete) DataStream objects and I'm finding using GoogleMock to test this a little more tricky. For example, here is a snippet of some code that needs to be tested:
std::shared_ptr<DataStream> datastream(nullptr);
// Try and open the serial port:
try
{
std::shared_ptr<SerialDataStream> serialstream =
std::make_shared<SerialDataStream>("/dev/tty99", 115200);
}
catch (...)
{
// Returns a nullptr
return datastream;
}
// Check if there is a known device on the other end:
datastream = std::static_pointer_cast<DataStream>(serialstream);
if (!device_is_connected(datastream))
{
datastream = nullptr;
}
return datastream;
I'm struggling to find an effective method to test this code with GoogleMock:
I would like to mock the constructor (of SerialDataStream) so it throws an exception and the failure path is executed as expected.
I would like to test the successful path where the private API "device_is_connected" uses the newly created datastream object.
Is it the case that I have no option other than to create a fake SerialDataStream implementation and use dependency injection to test the code which creates concrete DataStream objects?
If this is the case, I'll just have to make the API "device_is_connected" public so I can simply test it with a mock implementation of the interface (as above) to test, for example:
datastream.write("DISCOVER");
string response = datastream.read("\n");
if (discovery_ok(response))
{
// do stuff
}
I'm fairly certain I've answered my own question and will have no option but to fake the SerialDataStream class and use dependency injection, and make private APIs public and simply test them via GoogleMock, but I'm open to suggestions if there's a better way I could/should be doing things here.
Looking at the code snipped you provided, I found myself asking:
What exactly should the function do? It seems like it
(1) creates a SerialDataStream and
(2) checks if a device is connected.
You may could break the function into two (individually testable) parts.
Still there is the question on how to handle the std::shared_ptr<SerialDataStream> serialstream = std::make_shared<SerialDataStream>("/dev/tty99", 115200); - at some point you will need to tackle the (hidden) new there.
And I agree with you - dependency injection might be the solution there. Turning whatever class or function ends up responsible for creating into a template class/function would allow to write you (e.g.)
template<typename T>
std::shared_ptr<DataStream> createDatastream()
{
std::shared_ptr<DataStream> datastream(nullptr);
std::shared_ptr<T> datastream = std::make_shared<T>("/dev/tty99", 115200);
return datastream;
}
And then instantiate the class/function with SerialDataStream in your application, while using a MockDataStream to test the function(s).

Builder pattern - configuration file reading

I'm facing a design problem. I want to separate building objects with a builder pattern, but the problem is that objects have to be built from configuration file.
So far I have decided that all objects, created from configuration, will be stored in DataContext class (container for all objects), because these objects states will be updated from a transmission (so it's easier to have them in one place).
I'm using external library for reading from XML file - and my question is how to hide it - is it better to inject it to concreteBuilder class? I have to notice that builder class will have to create lots of objects and at the end - connect them between each other.
Base class could look like that:
/*
* IDataContextBuilder
* base class for building data context object
* and sub obejcts
*/
class IDataContextBuilder {
public:
/*
* GetResult()
* returns result of building process
*/
virtual DataContext * GetResult () = 0;
/*
* Virtual destructor
*/
virtual ~IDataContextBuilder() { }
};
class ConcreteDataContextBuilder {
public:
ConcreteDataContextBuilder(pugi::xml_node & rootNode);
DataContext * GetResult ();
}
How to implement it correctly? What could be better pattern to build classes from configuration files?
I don't see a problem with that, but maybe you could inject another 'Director' class that receives a specific builder, loads the config files, and produces objects calling the respective builder-subclasses.
What I mean:
class DataContextDirector {
public:
void SetBuilder(IDataContextBuilder* builder);
void SetConfig(const std::string& configFilePath); // or whatever
DataContext* ProduceObject() {
// pseudo-code here:
// myBuilder->setup(xmlNodeOfConfig);
// return myBuilder->GetResult();
}
};