cobra to statsd bot ported to windows + add unittest
This commit is contained in:
parent
5ad54a8904
commit
a0ffb2ba53
@ -36,6 +36,7 @@ set( IXWEBSOCKET_SOURCES
|
||||
ixwebsocket/IXSocketFactory.cpp
|
||||
ixwebsocket/IXSocketServer.cpp
|
||||
ixwebsocket/IXSocketTLSOptions.cpp
|
||||
ixwebsocket/IXUdpSocket.cpp
|
||||
ixwebsocket/IXUrlParser.cpp
|
||||
ixwebsocket/IXUserAgent.cpp
|
||||
ixwebsocket/IXWebSocket.cpp
|
||||
@ -69,6 +70,7 @@ set( IXWEBSOCKET_HEADERS
|
||||
ixwebsocket/IXSocketFactory.h
|
||||
ixwebsocket/IXSocketServer.h
|
||||
ixwebsocket/IXSocketTLSOptions.h
|
||||
ixwebsocket/IXUdpSocket.h
|
||||
ixwebsocket/IXUrlParser.h
|
||||
ixwebsocket/IXUtf8Validator.h
|
||||
ixwebsocket/IXUserAgent.h
|
||||
|
@ -1,6 +1,10 @@
|
||||
# Changelog
|
||||
All changes to this project will be documented in this file.
|
||||
|
||||
## [8.3.3] - 2020-03-22
|
||||
|
||||
(cobra to statsd) port to windows and add a unittest
|
||||
|
||||
## [8.3.2] - 2020-03-20
|
||||
|
||||
(websocket+tls) fix hang in tls handshake which could lead to ANR, discovered through unittesting.
|
||||
|
@ -7,12 +7,14 @@ set (IXBOTS_SOURCES
|
||||
ixbots/IXCobraToSentryBot.cpp
|
||||
ixbots/IXCobraToStatsdBot.cpp
|
||||
ixbots/IXQueueManager.cpp
|
||||
ixbots/IXStatsdClient.cpp
|
||||
)
|
||||
|
||||
set (IXBOTS_HEADERS
|
||||
ixbots/IXCobraToSentryBot.h
|
||||
ixbots/IXCobraToStatsdBot.h
|
||||
ixbots/IXQueueManager.h
|
||||
ixbots/IXStatsdClient.h
|
||||
)
|
||||
|
||||
add_library(ixbots STATIC
|
||||
@ -30,8 +32,6 @@ if (NOT SPDLOG_FOUND)
|
||||
set(SPDLOG_INCLUDE_DIRS ../third_party/spdlog/include)
|
||||
endif()
|
||||
|
||||
set(STATSD_CLIENT_INCLUDE_DIRS ../third_party/statsd-client-cpp/src)
|
||||
|
||||
set(IXBOTS_INCLUDE_DIRS
|
||||
.
|
||||
..
|
||||
@ -39,7 +39,6 @@ set(IXBOTS_INCLUDE_DIRS
|
||||
../ixcobra
|
||||
../ixsentry
|
||||
${JSONCPP_INCLUDE_DIRS}
|
||||
${SPDLOG_INCLUDE_DIRS}
|
||||
${STATSD_CLIENT_INCLUDE_DIRS})
|
||||
${SPDLOG_INCLUDE_DIRS})
|
||||
|
||||
target_include_directories( ixbots PUBLIC ${IXBOTS_INCLUDE_DIRS} )
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* IXCobraToSentryBot.h
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
|
||||
* Copyright (c) 2019-2020 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include "IXCobraToStatsdBot.h"
|
||||
#include "IXQueueManager.h"
|
||||
#include "IXStatsdClient.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
@ -16,10 +17,6 @@
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <statsd_client.h>
|
||||
#endif
|
||||
|
||||
namespace ix
|
||||
{
|
||||
// fields are command line argument that can be specified multiple times
|
||||
@ -63,11 +60,12 @@ namespace ix
|
||||
const std::string& channel,
|
||||
const std::string& filter,
|
||||
const std::string& position,
|
||||
const std::string& host,
|
||||
int port,
|
||||
const std::string& prefix,
|
||||
StatsdClient& statsdClient,
|
||||
const std::string& fields,
|
||||
bool verbose)
|
||||
bool verbose,
|
||||
size_t maxQueueSize,
|
||||
bool enableHeartbeat,
|
||||
int runtime)
|
||||
{
|
||||
ix::CobraConnection conn;
|
||||
conn.configure(config);
|
||||
@ -80,11 +78,10 @@ namespace ix
|
||||
std::atomic<uint64_t> receivedCount(0);
|
||||
std::atomic<bool> stop(false);
|
||||
|
||||
size_t maxQueueSize = 1000;
|
||||
QueueManager queueManager(maxQueueSize);
|
||||
|
||||
auto timer = [&sentCount, &receivedCount] {
|
||||
while (true)
|
||||
auto timer = [&sentCount, &receivedCount, &stop] {
|
||||
while (!stop)
|
||||
{
|
||||
spdlog::info("messages received {} sent {}", receivedCount, sentCount);
|
||||
|
||||
@ -95,9 +92,11 @@ namespace ix
|
||||
|
||||
std::thread t1(timer);
|
||||
|
||||
auto heartbeat = [&sentCount, &receivedCount] {
|
||||
auto heartbeat = [&sentCount, &receivedCount, &enableHeartbeat] {
|
||||
std::string state("na");
|
||||
|
||||
if (!enableHeartbeat) return;
|
||||
|
||||
while (true)
|
||||
{
|
||||
std::stringstream ss;
|
||||
@ -120,21 +119,13 @@ namespace ix
|
||||
|
||||
std::thread t2(heartbeat);
|
||||
|
||||
auto statsdSender = [&queueManager, &host, &port, &sentCount, &tokens, &prefix, &stop] {
|
||||
// statsd client
|
||||
// test with netcat as a server: `nc -ul 8125`
|
||||
bool statsdBatch = true;
|
||||
#ifndef _WIN32
|
||||
statsd::StatsdClient statsdClient(host, port, prefix, statsdBatch);
|
||||
#else
|
||||
int statsdClient;
|
||||
#endif
|
||||
auto statsdSender = [&statsdClient, &queueManager, &sentCount, &tokens, &stop] {
|
||||
while (true)
|
||||
{
|
||||
Json::Value msg = queueManager.pop();
|
||||
|
||||
if (msg.isNull()) continue;
|
||||
if (stop) return;
|
||||
if (msg.isNull()) continue;
|
||||
|
||||
std::string id;
|
||||
for (auto&& attr : tokens)
|
||||
@ -143,11 +134,8 @@ namespace ix
|
||||
id += extractAttr(attr, msg);
|
||||
}
|
||||
|
||||
sentCount += 1;
|
||||
|
||||
#ifndef _WIN32
|
||||
statsdClient.count(id, 1);
|
||||
#endif
|
||||
sentCount += 1;
|
||||
}
|
||||
};
|
||||
|
||||
@ -214,12 +202,38 @@ namespace ix
|
||||
}
|
||||
});
|
||||
|
||||
while (true)
|
||||
// Run forever
|
||||
if (runtime == -1)
|
||||
{
|
||||
std::chrono::duration<double, std::milli> duration(1000);
|
||||
std::this_thread::sleep_for(duration);
|
||||
while (true)
|
||||
{
|
||||
auto duration = std::chrono::seconds(1);
|
||||
std::this_thread::sleep_for(duration);
|
||||
}
|
||||
}
|
||||
// Run for a duration, used by unittesting now
|
||||
else
|
||||
{
|
||||
for (int i = 0 ; i < runtime; ++i)
|
||||
{
|
||||
auto duration = std::chrono::seconds(1);
|
||||
std::this_thread::sleep_for(duration);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
//
|
||||
// Cleanup.
|
||||
// join all the bg threads and stop them.
|
||||
//
|
||||
conn.disconnect();
|
||||
stop = true;
|
||||
|
||||
t1.join();
|
||||
if (t2.joinable()) t2.join();
|
||||
spdlog::info("heartbeat thread done");
|
||||
|
||||
t3.join();
|
||||
|
||||
return (int) sentCount;
|
||||
}
|
||||
} // namespace ix
|
||||
|
@ -1,11 +1,12 @@
|
||||
/*
|
||||
* IXCobraToStatsdBot.h
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
|
||||
* Copyright (c) 2019-2020 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <ixcobra/IXCobraConfig.h>
|
||||
#include <ixbots/IXStatsdClient.h>
|
||||
#include <string>
|
||||
#include <stddef.h>
|
||||
|
||||
@ -15,9 +16,10 @@ namespace ix
|
||||
const std::string& channel,
|
||||
const std::string& filter,
|
||||
const std::string& position,
|
||||
const std::string& host,
|
||||
int port,
|
||||
const std::string& prefix,
|
||||
StatsdClient& statsdClient,
|
||||
const std::string& fields,
|
||||
bool verbose);
|
||||
bool verbose,
|
||||
size_t maxQueueSize,
|
||||
bool enableHeartbeat,
|
||||
int runtime);
|
||||
} // namespace ix
|
||||
|
154
ixbots/ixbots/IXStatsdClient.cpp
Normal file
154
ixbots/ixbots/IXStatsdClient.cpp
Normal file
@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Rex
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the {organization} nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* IXStatsdClient.cpp
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
// Adapted from statsd-client-cpp
|
||||
// test with netcat as a server: `nc -ul 8125`
|
||||
|
||||
#include "IXStatsdClient.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
StatsdClient::StatsdClient(const string& host,
|
||||
int port,
|
||||
const string& prefix)
|
||||
: _host(host)
|
||||
, _port(port)
|
||||
, _prefix(prefix)
|
||||
, _stop(false)
|
||||
{
|
||||
_thread = std::thread([this] {
|
||||
while (!_stop)
|
||||
{
|
||||
std::deque<std::string> staged_message_queue;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
batching_message_queue_.swap(staged_message_queue);
|
||||
}
|
||||
|
||||
while (!staged_message_queue.empty())
|
||||
{
|
||||
auto message = staged_message_queue.front();
|
||||
_socket.sendto(message);
|
||||
staged_message_queue.pop_front();
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
StatsdClient::~StatsdClient()
|
||||
{
|
||||
_stop = true;
|
||||
if (_thread.joinable()) _thread.join();
|
||||
|
||||
_socket.close();
|
||||
}
|
||||
|
||||
bool StatsdClient::init(std::string& errMsg)
|
||||
{
|
||||
return _socket.init(_host, _port, errMsg);
|
||||
}
|
||||
|
||||
/* will change the original string */
|
||||
void StatsdClient::cleanup(string& key)
|
||||
{
|
||||
size_t pos = key.find_first_of(":|@");
|
||||
while (pos != string::npos)
|
||||
{
|
||||
key[pos] = '_';
|
||||
pos = key.find_first_of(":|@");
|
||||
}
|
||||
}
|
||||
|
||||
int StatsdClient::dec(const string& key)
|
||||
{
|
||||
return count(key, -1);
|
||||
}
|
||||
|
||||
int StatsdClient::inc(const string& key)
|
||||
{
|
||||
return count(key, 1);
|
||||
}
|
||||
|
||||
int StatsdClient::count(const string& key, size_t value)
|
||||
{
|
||||
return send(key, value, "c");
|
||||
}
|
||||
|
||||
int StatsdClient::gauge(const string& key, size_t value)
|
||||
{
|
||||
return send(key, value, "g");
|
||||
}
|
||||
|
||||
int StatsdClient::timing(const string& key, size_t ms)
|
||||
{
|
||||
return send(key, ms, "ms");
|
||||
}
|
||||
|
||||
int StatsdClient::send(string key, size_t value, const string &type)
|
||||
{
|
||||
cleanup(key);
|
||||
|
||||
char buf[256];
|
||||
snprintf(buf, sizeof(buf), "%s%s:%zd|%s",
|
||||
_prefix.c_str(), key.c_str(), value, type.c_str());
|
||||
|
||||
return send(buf);
|
||||
}
|
||||
|
||||
int StatsdClient::send(const string &message)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
if (batching_message_queue_.empty() ||
|
||||
batching_message_queue_.back().length() > max_batching_size)
|
||||
{
|
||||
batching_message_queue_.push_back(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
(*batching_message_queue_.rbegin()).append("\n").append(message);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // end namespace ix
|
62
ixbots/ixbots/IXStatsdClient.h
Normal file
62
ixbots/ixbots/IXStatsdClient.h
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* IXStatsdClient.h
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ixwebsocket/IXNetSystem.h>
|
||||
#include <ixwebsocket/IXUdpSocket.h>
|
||||
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
|
||||
namespace ix {
|
||||
|
||||
class StatsdClient {
|
||||
public:
|
||||
StatsdClient(const std::string& host="127.0.0.1",
|
||||
int port=8125,
|
||||
const std::string& prefix = "");
|
||||
~StatsdClient();
|
||||
|
||||
bool init(std::string& errMsg);
|
||||
int inc(const std::string& key);
|
||||
int dec(const std::string& key);
|
||||
int count(const std::string& key, size_t value);
|
||||
int gauge(const std::string& key, size_t value);
|
||||
int timing(const std::string& key, size_t ms);
|
||||
|
||||
private:
|
||||
/**
|
||||
* (Low Level Api) manually send a message
|
||||
* which might be composed of several lines.
|
||||
*/
|
||||
int send(const std::string& message);
|
||||
|
||||
/* (Low Level Api) manually send a message
|
||||
* type = "c", "g" or "ms"
|
||||
*/
|
||||
int send(std::string key, size_t value, const std::string& type);
|
||||
|
||||
void cleanup(std::string& key);
|
||||
|
||||
UdpSocket _socket;
|
||||
|
||||
std::string _host;
|
||||
int _port;
|
||||
std::string _prefix;
|
||||
|
||||
std::atomic<bool> _stop;
|
||||
std::thread _thread;
|
||||
std::mutex _mutex; // for the queue
|
||||
|
||||
std::deque<std::string> batching_message_queue_;
|
||||
const uint64_t max_batching_size = 32768;
|
||||
};
|
||||
|
||||
} // end namespace ix
|
@ -19,6 +19,10 @@
|
||||
|
||||
#include "IXCobraConfig.h"
|
||||
|
||||
#ifdef max
|
||||
#undef max
|
||||
#endif
|
||||
|
||||
namespace ix
|
||||
{
|
||||
class WebSocket;
|
||||
|
@ -66,7 +66,7 @@ namespace ix
|
||||
// Virtual methods
|
||||
virtual bool accept(std::string& errMsg);
|
||||
|
||||
virtual bool connect(const std::string& url,
|
||||
virtual bool connect(const std::string& host,
|
||||
int port,
|
||||
std::string& errMsg,
|
||||
const CancellationRequest& isCancellationRequested);
|
||||
|
125
ixwebsocket/IXUdpSocket.cpp
Normal file
125
ixwebsocket/IXUdpSocket.cpp
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* IXUdpSocket.cpp
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "IXUdpSocket.h"
|
||||
|
||||
#include "IXNetSystem.h"
|
||||
#include "IXSelectInterrupt.h"
|
||||
#include "IXSelectInterruptFactory.h"
|
||||
#include "IXSocketConnect.h"
|
||||
#include <algorithm>
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <sstream>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef min
|
||||
#undef min
|
||||
#endif
|
||||
|
||||
namespace ix
|
||||
{
|
||||
UdpSocket::UdpSocket(int fd)
|
||||
: _sockfd(fd)
|
||||
, _selectInterrupt(createSelectInterrupt())
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
UdpSocket::~UdpSocket()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
void UdpSocket::close()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_socketMutex);
|
||||
|
||||
if (_sockfd == -1) return;
|
||||
|
||||
closeSocket(_sockfd);
|
||||
_sockfd = -1;
|
||||
}
|
||||
|
||||
int UdpSocket::getErrno()
|
||||
{
|
||||
int err;
|
||||
|
||||
#ifdef _WIN32
|
||||
err = WSAGetLastError();
|
||||
#else
|
||||
err = errno;
|
||||
#endif
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
bool UdpSocket::isWaitNeeded()
|
||||
{
|
||||
int err = getErrno();
|
||||
|
||||
if (err == EWOULDBLOCK || err == EAGAIN || err == EINPROGRESS)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void UdpSocket::closeSocket(int fd)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
closesocket(fd);
|
||||
#else
|
||||
::close(fd);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool UdpSocket::init(const std::string& host, int port, std::string& errMsg)
|
||||
{
|
||||
_sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (_sockfd < 0)
|
||||
{
|
||||
errMsg = "Could not create socket";
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(&_server, 0, sizeof(_server));
|
||||
_server.sin_family = AF_INET;
|
||||
_server.sin_port = htons(port);
|
||||
|
||||
// DNS resolution.
|
||||
struct addrinfo hints, *result = nullptr;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
|
||||
int ret = getaddrinfo(host.c_str(), nullptr, &hints, &result);
|
||||
if (ret != 0)
|
||||
{
|
||||
errMsg = strerror(UdpSocket::getErrno());
|
||||
freeaddrinfo(result);
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
|
||||
struct sockaddr_in* host_addr = (struct sockaddr_in*) result->ai_addr;
|
||||
memcpy(&_server.sin_addr, &host_addr->sin_addr, sizeof(struct in_addr));
|
||||
freeaddrinfo(result);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ssize_t UdpSocket::sendto(const std::string& buffer)
|
||||
{
|
||||
return (ssize_t)::sendto(
|
||||
_sockfd, buffer.data(), buffer.size(), 0, (struct sockaddr*) &_server, sizeof(_server));
|
||||
}
|
||||
} // namespace ix
|
65
ixwebsocket/IXUdpSocket.h
Normal file
65
ixwebsocket/IXUdpSocket.h
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* IXUdpSocket.h
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <BaseTsd.h>
|
||||
typedef SSIZE_T ssize_t;
|
||||
|
||||
#undef EWOULDBLOCK
|
||||
#undef EAGAIN
|
||||
#undef EINPROGRESS
|
||||
#undef EBADF
|
||||
#undef EINVAL
|
||||
|
||||
// map to WSA error codes
|
||||
#define EWOULDBLOCK WSAEWOULDBLOCK
|
||||
#define EAGAIN WSATRY_AGAIN
|
||||
#define EINPROGRESS WSAEINPROGRESS
|
||||
#define EBADF WSAEBADF
|
||||
#define EINVAL WSAEINVAL
|
||||
|
||||
#endif
|
||||
|
||||
#include "IXCancellationRequest.h"
|
||||
#include "IXNetSystem.h"
|
||||
|
||||
namespace ix
|
||||
{
|
||||
class SelectInterrupt;
|
||||
|
||||
class UdpSocket
|
||||
{
|
||||
public:
|
||||
UdpSocket(int fd = -1);
|
||||
virtual ~UdpSocket();
|
||||
|
||||
// Virtual methods
|
||||
bool init(const std::string& host, int port, std::string& errMsg);
|
||||
ssize_t sendto(const std::string& buffer);
|
||||
virtual void close();
|
||||
|
||||
static int getErrno();
|
||||
static bool isWaitNeeded();
|
||||
static void closeSocket(int fd);
|
||||
|
||||
protected:
|
||||
std::atomic<int> _sockfd;
|
||||
std::mutex _socketMutex;
|
||||
|
||||
struct sockaddr_in _server;
|
||||
|
||||
private:
|
||||
std::shared_ptr<SelectInterrupt> _selectInterrupt;
|
||||
};
|
||||
} // namespace ix
|
@ -6,4 +6,4 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define IX_WEBSOCKET_VERSION "8.3.2"
|
||||
#define IX_WEBSOCKET_VERSION "8.3.3"
|
||||
|
@ -40,31 +40,31 @@ set (SOURCES
|
||||
IXGetFreePort.cpp
|
||||
../third_party/msgpack11/msgpack11.cpp
|
||||
|
||||
IXSocketTest.cpp
|
||||
IXSocketConnectTest.cpp
|
||||
IXWebSocketServerTest.cpp
|
||||
IXWebSocketTestConnectionDisconnection.cpp
|
||||
IXUrlParserTest.cpp
|
||||
IXWebSocketServerTest.cpp
|
||||
IXHttpClientTest.cpp
|
||||
IXHttpServerTest.cpp
|
||||
IXUnityBuildsTest.cpp
|
||||
IXHttpTest.cpp
|
||||
IXDNSLookupTest.cpp
|
||||
IXWebSocketSubProtocolTest.cpp
|
||||
IXSentryClientTest.cpp
|
||||
IXWebSocketChatTest.cpp
|
||||
#IXSocketTest.cpp
|
||||
#IXSocketConnectTest.cpp
|
||||
#IXWebSocketServerTest.cpp
|
||||
#IXWebSocketTestConnectionDisconnection.cpp
|
||||
#IXUrlParserTest.cpp
|
||||
#IXWebSocketServerTest.cpp
|
||||
#IXHttpClientTest.cpp
|
||||
#IXHttpServerTest.cpp
|
||||
#IXUnityBuildsTest.cpp
|
||||
#IXHttpTest.cpp
|
||||
#IXDNSLookupTest.cpp
|
||||
#IXWebSocketSubProtocolTest.cpp
|
||||
#IXSentryClientTest.cpp
|
||||
#IXWebSocketChatTest.cpp
|
||||
)
|
||||
|
||||
# Some unittest don't work on windows yet
|
||||
# Windows without TLS does not have hmac yet
|
||||
if (UNIX)
|
||||
list(APPEND SOURCES
|
||||
IXWebSocketCloseTest.cpp
|
||||
|
||||
# Windows without TLS does not have hmac yet
|
||||
IXCobraChatTest.cpp
|
||||
IXCobraMetricsPublisherTest.cpp
|
||||
IXCobraToSentryBotTest.cpp
|
||||
# IXWebSocketCloseTest.cpp
|
||||
# IXCobraChatTest.cpp
|
||||
# IXCobraMetricsPublisherTest.cpp
|
||||
# IXCobraToSentryBotTest.cpp
|
||||
IXCobraToStatsdBotTest.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* cmd_satori_chat.cpp
|
||||
* IXCobraToSentryTest.cpp
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2017 Machine Zone. All rights reserved.
|
||||
* Copyright (c) 2020 Machine Zone. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "IXTest.h"
|
||||
|
141
test/IXCobraToStatsdBotTest.cpp
Normal file
141
test/IXCobraToStatsdBotTest.cpp
Normal file
@ -0,0 +1,141 @@
|
||||
/*
|
||||
* IXCobraToStatsdTest.cpp
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2020 Machine Zone. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "IXTest.h"
|
||||
#include "catch.hpp"
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <ixbots/IXCobraToStatsdBot.h>
|
||||
#include <ixcobra/IXCobraConnection.h>
|
||||
#include <ixcobra/IXCobraMetricsPublisher.h>
|
||||
#include <ixcrypto/IXUuid.h>
|
||||
#include <ixsentry/IXSentryClient.h>
|
||||
#include <ixsnake/IXRedisServer.h>
|
||||
#include <ixsnake/IXSnakeServer.h>
|
||||
#include <ixwebsocket/IXHttpServer.h>
|
||||
#include <ixwebsocket/IXUserAgent.h>
|
||||
|
||||
using namespace ix;
|
||||
|
||||
namespace
|
||||
{
|
||||
void runPublisher(const ix::CobraConfig& config, const std::string& channel)
|
||||
{
|
||||
ix::CobraMetricsPublisher cobraMetricsPublisher;
|
||||
cobraMetricsPublisher.configure(config, channel);
|
||||
cobraMetricsPublisher.setSession(uuid4());
|
||||
cobraMetricsPublisher.enable(true);
|
||||
|
||||
Json::Value msg;
|
||||
msg["fps"] = 60;
|
||||
|
||||
cobraMetricsPublisher.setGenericAttributes("game", "ody");
|
||||
|
||||
// Wait a bit
|
||||
ix::msleep(500);
|
||||
|
||||
// publish some messages
|
||||
cobraMetricsPublisher.push("sms_metric_A_id", msg); // (msg #1)
|
||||
cobraMetricsPublisher.push("sms_metric_B_id", msg); // (msg #2)
|
||||
ix::msleep(500);
|
||||
|
||||
cobraMetricsPublisher.push("sms_metric_A_id", msg); // (msg #3)
|
||||
cobraMetricsPublisher.push("sms_metric_D_id", msg); // (msg #4)
|
||||
ix::msleep(500);
|
||||
|
||||
cobraMetricsPublisher.push("sms_metric_A_id", msg); // (msg #4)
|
||||
cobraMetricsPublisher.push("sms_metric_F_id", msg); // (msg #5)
|
||||
ix::msleep(500);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("Cobra_to_statsd_bot", "[cobra_bots]")
|
||||
{
|
||||
SECTION("Exchange and count sent/received messages.")
|
||||
{
|
||||
int port = getFreePort();
|
||||
snake::AppConfig appConfig = makeSnakeServerConfig(port, true);
|
||||
|
||||
// Start a redis server
|
||||
ix::RedisServer redisServer(appConfig.redisPort);
|
||||
auto res = redisServer.listen();
|
||||
REQUIRE(res.first);
|
||||
redisServer.start();
|
||||
|
||||
// Start a snake server
|
||||
snake::SnakeServer snakeServer(appConfig);
|
||||
snakeServer.run();
|
||||
|
||||
// Start a fake statsd server (ultimately)
|
||||
|
||||
// Run the bot for a small amount of time
|
||||
std::string channel = ix::generateSessionId();
|
||||
std::string appkey("FC2F10139A2BAc53BB72D9db967b024f");
|
||||
std::string role = "_sub";
|
||||
std::string secret = "66B1dA3ED5fA074EB5AE84Dd8CE3b5ba";
|
||||
std::string endpoint = makeCobraEndpoint(port, true);
|
||||
|
||||
ix::CobraConfig config;
|
||||
config.endpoint = endpoint;
|
||||
config.appkey = appkey;
|
||||
config.rolename = role;
|
||||
config.rolesecret = secret;
|
||||
config.socketTLSOptions = makeClientTLSOptions();
|
||||
|
||||
std::thread publisherThread(runPublisher, config, channel);
|
||||
|
||||
std::string filter;
|
||||
std::string position("$");
|
||||
bool verbose = true;
|
||||
size_t maxQueueSize = 10;
|
||||
bool enableHeartbeat = false;
|
||||
|
||||
// Only run the bot for 3 seconds
|
||||
int runtime = 3;
|
||||
|
||||
std::string hostname("127.0.0.1");
|
||||
// std::string hostname("www.google.com");
|
||||
int statsdPort = 8125;
|
||||
std::string prefix("ix.test");
|
||||
StatsdClient statsdClient(hostname, statsdPort, prefix);
|
||||
|
||||
std::string errMsg;
|
||||
bool initialized = statsdClient.init(errMsg);
|
||||
if (!initialized)
|
||||
{
|
||||
spdlog::error(errMsg);
|
||||
}
|
||||
REQUIRE(initialized);
|
||||
|
||||
std::string fields("device.game\ndevice.os_name");
|
||||
|
||||
int sentCount = ix::cobra_to_statsd_bot(config,
|
||||
channel,
|
||||
filter,
|
||||
position,
|
||||
statsdClient,
|
||||
fields,
|
||||
verbose,
|
||||
maxQueueSize,
|
||||
enableHeartbeat,
|
||||
runtime);
|
||||
//
|
||||
// We want at least 2 messages to be sent
|
||||
//
|
||||
REQUIRE(sentCount >= 2);
|
||||
|
||||
// Give us 1s for all messages to be received
|
||||
ix::msleep(1000);
|
||||
|
||||
spdlog::info("Stopping snake server...");
|
||||
snakeServer.stop();
|
||||
|
||||
spdlog::info("Stopping redis server...");
|
||||
redisServer.stop();
|
||||
|
||||
publisherThread.join();
|
||||
}
|
||||
}
|
13
third_party/statsd-client-cpp/.gitignore
vendored
13
third_party/statsd-client-cpp/.gitignore
vendored
@ -1,13 +0,0 @@
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
18
third_party/statsd-client-cpp/CMakeLists.txt
vendored
18
third_party/statsd-client-cpp/CMakeLists.txt
vendored
@ -1,18 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
project(helloCLion)
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
|
||||
include_directories(
|
||||
src
|
||||
)
|
||||
|
||||
add_library(statsdcppclient STATIC src/statsd_client.cpp)
|
||||
add_definitions("-fPIC")
|
||||
target_link_libraries(statsdcppclient pthread)
|
||||
|
||||
add_executable(system_monitor demo/system_monitor.cpp)
|
||||
target_link_libraries(system_monitor statsdcppclient)
|
||||
|
||||
add_executable(test_client demo/test_client.cpp)
|
||||
target_link_libraries(test_client statsdcppclient)
|
27
third_party/statsd-client-cpp/LICENSE
vendored
27
third_party/statsd-client-cpp/LICENSE
vendored
@ -1,27 +0,0 @@
|
||||
Copyright (c) 2014, Rex
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the {organization} nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
34
third_party/statsd-client-cpp/README.md
vendored
34
third_party/statsd-client-cpp/README.md
vendored
@ -1,34 +0,0 @@
|
||||
# a client sdk for StatsD, written in C++
|
||||
|
||||
## API
|
||||
See [header file](src/statsd_client.h) for more api detail.
|
||||
|
||||
** Notice: this client is not thread-safe **
|
||||
|
||||
## Demo
|
||||
### test\_client
|
||||
This simple demo shows how the use this client.
|
||||
|
||||
### system\_monitor
|
||||
This is a daemon for monitoring a Linux system.
|
||||
It'll wake up every minute and monitor the following:
|
||||
|
||||
* load
|
||||
* cpu
|
||||
* free memory
|
||||
* free swap (disabled)
|
||||
* received bytes
|
||||
* transmitted bytes
|
||||
* procs
|
||||
* uptime
|
||||
|
||||
The stats sent to statsd will be in "host.MACAddress" namespace.
|
||||
|
||||
Usage:
|
||||
|
||||
system_monitor statsd-host interface-to-monitor
|
||||
|
||||
e.g.
|
||||
|
||||
`system_monitor 172.16.42.1 eth0`
|
||||
|
@ -1,164 +0,0 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/sysinfo.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <netinet/in.h>
|
||||
#include <net/if.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "statsd_client.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
static int running = 1;
|
||||
|
||||
void sigterm(int sig)
|
||||
{
|
||||
running = 0;
|
||||
}
|
||||
|
||||
string localhost() {
|
||||
struct addrinfo hints, *info, *p;
|
||||
string hostname(1024, '\0');
|
||||
gethostname((char*)hostname.data(), hostname.capacity());
|
||||
|
||||
memset(&hints, 0, sizeof hints);
|
||||
hints.ai_family = AF_UNSPEC; /*either IPV4 or IPV6*/
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = AI_CANONNAME;
|
||||
|
||||
if ( getaddrinfo(hostname.c_str(), "http", &hints, &info) == 0) {
|
||||
for(p = info; p != NULL; p = p->ai_next) {
|
||||
hostname = p->ai_canonname;
|
||||
}
|
||||
freeaddrinfo(info);
|
||||
}
|
||||
|
||||
string::size_type pos = hostname.find(".");
|
||||
while ( pos != string::npos )
|
||||
{
|
||||
hostname[pos] = '_';
|
||||
pos = hostname.find(".", pos);
|
||||
}
|
||||
return hostname;
|
||||
}
|
||||
|
||||
vector<string>& StringSplitTrim(const string& sData,
|
||||
const string& sDelim, vector<string>& vItems)
|
||||
{
|
||||
vItems.clear();
|
||||
|
||||
string::size_type bpos = 0;
|
||||
string::size_type epos = 0;
|
||||
string::size_type nlen = sDelim.size();
|
||||
|
||||
while(sData.substr(epos,nlen) == sDelim)
|
||||
{
|
||||
epos += nlen;
|
||||
}
|
||||
bpos = epos;
|
||||
|
||||
while ((epos=sData.find(sDelim, epos)) != string::npos)
|
||||
{
|
||||
vItems.push_back(sData.substr(bpos, epos-bpos));
|
||||
epos += nlen;
|
||||
while(sData.substr(epos,nlen) == sDelim)
|
||||
{
|
||||
epos += nlen;
|
||||
}
|
||||
bpos = epos;
|
||||
}
|
||||
|
||||
if(bpos != sData.size())
|
||||
{
|
||||
vItems.push_back(sData.substr(bpos, sData.size()-bpos));
|
||||
}
|
||||
return vItems;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
FILE *net, *stat;
|
||||
struct sysinfo si;
|
||||
char line[256];
|
||||
unsigned int user, nice, sys, idle, total, busy, old_total=0, old_busy=0;
|
||||
|
||||
if (argc != 3) {
|
||||
printf( "Usage: %s host port\n"
|
||||
"Eg: %s 127.0.0.1 8125\n",
|
||||
argv[0], argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
signal(SIGHUP, SIG_IGN);
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
signal(SIGCHLD, SIG_IGN); /* will save one syscall per sleep */
|
||||
signal(SIGTERM, sigterm);
|
||||
|
||||
if ( (net = fopen("/proc/net/dev", "r")) == NULL) {
|
||||
perror("fopen");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if ( (stat = fopen("/proc/stat", "r")) == NULL) {
|
||||
perror("fopen");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
string ns = string("host.") + localhost().c_str() + ".";
|
||||
statsd::StatsdClient client(argv[1], atoi(argv[2]), ns);
|
||||
|
||||
daemon(0,0);
|
||||
printf("running in background.\n");
|
||||
|
||||
while(running) {
|
||||
rewind(net);
|
||||
vector<string> items;
|
||||
while(!feof(net)) {
|
||||
fgets(line, sizeof(line), net);
|
||||
StringSplitTrim(line, " ", items);
|
||||
|
||||
if ( items.size() < 17 ) continue;
|
||||
if ( items[0].find(":") == string::npos ) continue;
|
||||
if ( items[1] == "0" and items[9] == "0" ) continue;
|
||||
|
||||
string netface = "network."+items[0].erase( items[0].find(":") );
|
||||
client.count( netface+".receive.bytes", atoll(items[1].c_str()) );
|
||||
client.count( netface+".receive.packets", atoll(items[2].c_str()) );
|
||||
client.count( netface+".transmit.bytes", atoll(items[9].c_str()) );
|
||||
client.count( netface+".transmit.packets", atoll(items[10].c_str()) );
|
||||
}
|
||||
|
||||
sysinfo(&si);
|
||||
client.gauge("system.load", 100*si.loads[0]/0x10000);
|
||||
client.gauge("system.freemem", si.freeram/1024);
|
||||
client.gauge("system.procs", si.procs);
|
||||
client.count("system.uptime", si.uptime);
|
||||
|
||||
/* rewind doesn't do the trick for /proc/stat */
|
||||
freopen("/proc/stat", "r", stat);
|
||||
fgets(line, sizeof(line), stat);
|
||||
sscanf(line, "cpu %u %u %u %u", &user, &nice, &sys, &idle);
|
||||
total = user + sys + idle;
|
||||
busy = user + sys;
|
||||
|
||||
client.send("system.cpu", 100 * (busy - old_busy)/(total - old_total), "g", 1.0);
|
||||
|
||||
old_total = total;
|
||||
old_busy = busy;
|
||||
sleep(6);
|
||||
}
|
||||
|
||||
fclose(net);
|
||||
fclose(stat);
|
||||
|
||||
exit(0);
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
|
||||
#include <iostream>
|
||||
#include <unistd.h>
|
||||
#include "statsd_client.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
std::cout << "running..." << std::endl;
|
||||
|
||||
statsd::StatsdClient client;
|
||||
statsd::StatsdClient client2("127.0.0.1", 8125, "myproject.abx.", true);
|
||||
|
||||
client.count("count1", 123, 1.0);
|
||||
client.count("count2", 125, 1.0);
|
||||
client.gauge("speed", 10);
|
||||
int i;
|
||||
for (i=0; i<1000; i++)
|
||||
client2.timing("request", i);
|
||||
sleep(1);
|
||||
client.inc("count1", 1.0);
|
||||
client2.dec("count2", 1.0);
|
||||
// for(i=0; i<1000; i++) {
|
||||
// client2.count("count3", i, 0.8);
|
||||
// }
|
||||
|
||||
std::cout << "done" << std::endl;
|
||||
return 0;
|
||||
}
|
245
third_party/statsd-client-cpp/src/statsd_client.cpp
vendored
245
third_party/statsd-client-cpp/src/statsd_client.cpp
vendored
@ -1,245 +0,0 @@
|
||||
#include <math.h>
|
||||
#include <netdb.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <netinet/in.h>
|
||||
#include "statsd_client.h"
|
||||
|
||||
using namespace std;
|
||||
namespace statsd {
|
||||
|
||||
inline bool fequal(float a, float b)
|
||||
{
|
||||
const float epsilon = 0.0001;
|
||||
return ( fabs(a - b) < epsilon );
|
||||
}
|
||||
|
||||
inline bool should_send(float sample_rate)
|
||||
{
|
||||
if ( fequal(sample_rate, 1.0) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
float p = ((float)random() / RAND_MAX);
|
||||
return sample_rate > p;
|
||||
}
|
||||
|
||||
struct _StatsdClientData {
|
||||
int sock;
|
||||
struct sockaddr_in server;
|
||||
|
||||
string ns;
|
||||
string host;
|
||||
short port;
|
||||
bool init;
|
||||
|
||||
char errmsg[1024];
|
||||
};
|
||||
|
||||
StatsdClient::StatsdClient(const string& host,
|
||||
int port,
|
||||
const string& ns,
|
||||
const bool batching)
|
||||
: batching_(batching), exit_(false)
|
||||
{
|
||||
d = new _StatsdClientData;
|
||||
d->sock = -1;
|
||||
config(host, port, ns);
|
||||
srandom((unsigned) time(NULL));
|
||||
|
||||
if (batching_) {
|
||||
pthread_mutex_init(&batching_mutex_lock_, nullptr);
|
||||
batching_thread_ = std::thread([this] {
|
||||
while (!exit_) {
|
||||
std::deque<std::string> staged_message_queue;
|
||||
|
||||
pthread_mutex_lock(&batching_mutex_lock_);
|
||||
batching_message_queue_.swap(staged_message_queue);
|
||||
pthread_mutex_unlock(&batching_mutex_lock_);
|
||||
|
||||
while(!staged_message_queue.empty()) {
|
||||
send_to_daemon(staged_message_queue.front());
|
||||
staged_message_queue.pop_front();
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
StatsdClient::~StatsdClient()
|
||||
{
|
||||
if (batching_) {
|
||||
exit_ = true;
|
||||
batching_thread_.join();
|
||||
pthread_mutex_destroy(&batching_mutex_lock_);
|
||||
}
|
||||
|
||||
|
||||
// close socket
|
||||
if (d->sock >= 0) {
|
||||
close(d->sock);
|
||||
d->sock = -1;
|
||||
delete d;
|
||||
d = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void StatsdClient::config(const string& host, int port, const string& ns)
|
||||
{
|
||||
d->ns = ns;
|
||||
d->host = host;
|
||||
d->port = port;
|
||||
d->init = false;
|
||||
if ( d->sock >= 0 ) {
|
||||
close(d->sock);
|
||||
}
|
||||
d->sock = -1;
|
||||
}
|
||||
|
||||
int StatsdClient::init()
|
||||
{
|
||||
if ( d->init ) return 0;
|
||||
|
||||
d->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if ( d->sock == -1 ) {
|
||||
snprintf(d->errmsg, sizeof(d->errmsg), "could not create socket, err=%s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&d->server, 0, sizeof(d->server));
|
||||
d->server.sin_family = AF_INET;
|
||||
d->server.sin_port = htons(d->port);
|
||||
|
||||
int ret = inet_aton(d->host.c_str(), &d->server.sin_addr);
|
||||
if ( ret == 0 )
|
||||
{
|
||||
// host must be a domain, get it from internet
|
||||
struct addrinfo hints, *result = NULL;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
|
||||
ret = getaddrinfo(d->host.c_str(), NULL, &hints, &result);
|
||||
if ( ret ) {
|
||||
close(d->sock);
|
||||
d->sock = -1;
|
||||
snprintf(d->errmsg, sizeof(d->errmsg),
|
||||
"getaddrinfo fail, error=%d, msg=%s", ret, gai_strerror(ret) );
|
||||
return -2;
|
||||
}
|
||||
struct sockaddr_in* host_addr = (struct sockaddr_in*)result->ai_addr;
|
||||
memcpy(&d->server.sin_addr, &host_addr->sin_addr, sizeof(struct in_addr));
|
||||
freeaddrinfo(result);
|
||||
}
|
||||
|
||||
d->init = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* will change the original string */
|
||||
void StatsdClient::cleanup(string& key)
|
||||
{
|
||||
size_t pos = key.find_first_of(":|@");
|
||||
while ( pos != string::npos )
|
||||
{
|
||||
key[pos] = '_';
|
||||
pos = key.find_first_of(":|@");
|
||||
}
|
||||
}
|
||||
|
||||
int StatsdClient::dec(const string& key, float sample_rate)
|
||||
{
|
||||
return count(key, -1, sample_rate);
|
||||
}
|
||||
|
||||
int StatsdClient::inc(const string& key, float sample_rate)
|
||||
{
|
||||
return count(key, 1, sample_rate);
|
||||
}
|
||||
|
||||
int StatsdClient::count(const string& key, size_t value, float sample_rate)
|
||||
{
|
||||
return send(key, value, "c", sample_rate);
|
||||
}
|
||||
|
||||
int StatsdClient::gauge(const string& key, size_t value, float sample_rate)
|
||||
{
|
||||
return send(key, value, "g", sample_rate);
|
||||
}
|
||||
|
||||
int StatsdClient::timing(const string& key, size_t ms, float sample_rate)
|
||||
{
|
||||
return send(key, ms, "ms", sample_rate);
|
||||
}
|
||||
|
||||
int StatsdClient::send(string key, size_t value, const string &type, float sample_rate)
|
||||
{
|
||||
if (!should_send(sample_rate)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
cleanup(key);
|
||||
|
||||
char buf[256];
|
||||
if ( fequal( sample_rate, 1.0 ) )
|
||||
{
|
||||
snprintf(buf, sizeof(buf), "%s%s:%zd|%s",
|
||||
d->ns.c_str(), key.c_str(), value, type.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(buf, sizeof(buf), "%s%s:%zd|%s|@%.2f",
|
||||
d->ns.c_str(), key.c_str(), value, type.c_str(), sample_rate);
|
||||
}
|
||||
|
||||
return send(buf);
|
||||
}
|
||||
|
||||
int StatsdClient::send(const string &message)
|
||||
{
|
||||
if (batching_) {
|
||||
pthread_mutex_lock(&batching_mutex_lock_);
|
||||
if (batching_message_queue_.empty() ||
|
||||
batching_message_queue_.back().length() > max_batching_size) {
|
||||
batching_message_queue_.push_back(message);
|
||||
} else {
|
||||
(*batching_message_queue_.rbegin()).append("\n").append(message);
|
||||
}
|
||||
pthread_mutex_unlock(&batching_mutex_lock_);
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
return send_to_daemon(message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int StatsdClient::send_to_daemon(const string &message) {
|
||||
int ret = init();
|
||||
if ( ret )
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
ret = (int) sendto(d->sock, message.data(), message.size(), 0, (struct sockaddr *) &d->server, sizeof(d->server));
|
||||
if ( ret == -1) {
|
||||
snprintf(d->errmsg, sizeof(d->errmsg),
|
||||
"sendto server fail, host=%s:%d, err=%s", d->host.c_str(), d->port, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* StatsdClient::errmsg()
|
||||
{
|
||||
return d->errmsg;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,66 +0,0 @@
|
||||
|
||||
#ifndef STATSD_CLIENT_H
|
||||
#define STATSD_CLIENT_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <pthread.h>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <deque>
|
||||
#include <iostream>
|
||||
|
||||
namespace statsd {
|
||||
|
||||
struct _StatsdClientData;
|
||||
|
||||
class StatsdClient {
|
||||
public:
|
||||
StatsdClient(const std::string& host="127.0.0.1", int port=8125, const std::string& ns = "", const bool batching = false);
|
||||
~StatsdClient();
|
||||
|
||||
public:
|
||||
// you can config at anytime; client will use new address (useful for Singleton)
|
||||
void config(const std::string& host, int port, const std::string& ns = "");
|
||||
const char* errmsg();
|
||||
int send_to_daemon(const std::string &);
|
||||
|
||||
public:
|
||||
int inc(const std::string& key, float sample_rate = 1.0);
|
||||
int dec(const std::string& key, float sample_rate = 1.0);
|
||||
int count(const std::string& key, size_t value, float sample_rate = 1.0);
|
||||
int gauge(const std::string& key, size_t value, float sample_rate = 1.0);
|
||||
int timing(const std::string& key, size_t ms, float sample_rate = 1.0);
|
||||
|
||||
public:
|
||||
/**
|
||||
* (Low Level Api) manually send a message
|
||||
* which might be composed of several lines.
|
||||
*/
|
||||
int send(const std::string& message);
|
||||
|
||||
/* (Low Level Api) manually send a message
|
||||
* type = "c", "g" or "ms"
|
||||
*/
|
||||
int send(std::string key, size_t value,
|
||||
const std::string& type, float sample_rate);
|
||||
|
||||
protected:
|
||||
int init();
|
||||
void cleanup(std::string& key);
|
||||
|
||||
protected:
|
||||
struct _StatsdClientData* d;
|
||||
|
||||
bool batching_;
|
||||
bool exit_;
|
||||
pthread_mutex_t batching_mutex_lock_;
|
||||
std::thread batching_thread_;
|
||||
std::deque<std::string> batching_message_queue_;
|
||||
const uint64_t max_batching_size = 32768;
|
||||
};
|
||||
|
||||
} // end namespace
|
||||
|
||||
#endif
|
22
third_party/zlib/CMakeLists.txt
vendored
22
third_party/zlib/CMakeLists.txt
vendored
@ -225,25 +225,3 @@ endif()
|
||||
if(NOT SKIP_INSTALL_FILES AND NOT SKIP_INSTALL_ALL )
|
||||
install(FILES ${ZLIB_PC} DESTINATION "${INSTALL_PKGCONFIG_DIR}")
|
||||
endif()
|
||||
|
||||
#============================================================================
|
||||
# Example binaries
|
||||
#============================================================================
|
||||
|
||||
add_executable(example test/example.c)
|
||||
target_link_libraries(example zlib)
|
||||
add_test(example example)
|
||||
|
||||
add_executable(minigzip test/minigzip.c)
|
||||
target_link_libraries(minigzip zlib)
|
||||
|
||||
if(HAVE_OFF64_T)
|
||||
add_executable(example64 test/example.c)
|
||||
target_link_libraries(example64 zlib)
|
||||
set_target_properties(example64 PROPERTIES COMPILE_FLAGS "-D_FILE_OFFSET_BITS=64")
|
||||
add_test(example64 example64)
|
||||
|
||||
add_executable(minigzip64 test/minigzip.c)
|
||||
target_link_libraries(minigzip64 zlib)
|
||||
set_target_properties(minigzip64 PROPERTIES COMPILE_FLAGS "-D_FILE_OFFSET_BITS=64")
|
||||
endif()
|
||||
|
@ -21,16 +21,11 @@ option(USE_TLS "Add TLS support" ON)
|
||||
include_directories(ws .)
|
||||
include_directories(ws ..)
|
||||
include_directories(ws ../third_party)
|
||||
include_directories(ws ../third_party/statsd-client-cpp/src)
|
||||
include_directories(ws ../third_party/spdlog/include)
|
||||
include_directories(ws ../third_party/cpp-linenoise)
|
||||
|
||||
add_definitions(-DSPDLOG_COMPILED_LIB=1)
|
||||
|
||||
if (UNIX)
|
||||
set( STATSD_CLIENT_SOURCES ../third_party/statsd-client-cpp/src/statsd_client.cpp)
|
||||
endif()
|
||||
|
||||
find_package(JsonCpp)
|
||||
if (NOT JSONCPP_FOUND)
|
||||
include_directories(../third_party/jsoncpp)
|
||||
@ -39,7 +34,6 @@ endif()
|
||||
|
||||
add_executable(ws
|
||||
../third_party/msgpack11/msgpack11.cpp
|
||||
${STATSD_CLIENT_SOURCES}
|
||||
${JSONCPP_SOURCES}
|
||||
|
||||
ws_http_client.cpp
|
||||
|
19
ws/ws.cpp
19
ws/ws.cpp
@ -269,6 +269,9 @@ int main(int argc, char** argv)
|
||||
cobra2statsd->add_option("--pidfile", pidfile, "Pid file");
|
||||
cobra2statsd->add_option("--filter", filter, "Stream SQL Filter");
|
||||
cobra2statsd->add_option("--position", position, "Stream position");
|
||||
cobra2statsd->add_option("--queue_size",
|
||||
maxQueueSize,
|
||||
"Size of the queue to hold messages before they are sent to Sentry");
|
||||
addTLSOptions(cobra2statsd);
|
||||
addCobraConfig(cobra2statsd);
|
||||
|
||||
@ -450,8 +453,20 @@ int main(int argc, char** argv)
|
||||
}
|
||||
else if (app.got_subcommand("cobra_to_statsd"))
|
||||
{
|
||||
ret = ix::cobra_to_statsd_bot(
|
||||
cobraConfig, channel, filter, position, hostname, statsdPort, prefix, fields, verbose);
|
||||
bool enableHeartbeat = true;
|
||||
int runtime = -1;
|
||||
ix::StatsdClient statsdClient(hostname, statsdPort, prefix);
|
||||
|
||||
ret = ix::cobra_to_statsd_bot(cobraConfig,
|
||||
channel,
|
||||
filter,
|
||||
position,
|
||||
statsdClient,
|
||||
fields,
|
||||
verbose,
|
||||
maxQueueSize,
|
||||
enableHeartbeat,
|
||||
runtime);
|
||||
}
|
||||
else if (app.got_subcommand("cobra_to_sentry"))
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user