Commit 8bfb6751 authored by Pavel Vainerman's avatar Pavel Vainerman

(LogDB): предварительно реализовал обработку запроса /logs?logname..

Собственно основной запрос на получение логов.
parent de9ac27b
......@@ -40,8 +40,8 @@
<LogServer name="smplus" port="3333" host="localhost" />
<LogDB name="LogDB">
<logserver name="logserver1" ip="localhost" port="3333" cmd="-s level1"/>
<logserver name="logserver2" ip="localhost" port="3333" cmd=""/>
<logserver name="logserver1" ip="localhost" port="3333" cmd="-s level1" description="SES log"/>
<logserver name="logserver2" ip="localhost" port="3333" cmd="" description="GEU log"/>
</LogDB>
<settings>
......
......@@ -79,6 +79,7 @@ LogDB::LogDB( const string& name , const string& prefix ):
l->ip = sit.getProp("ip");
l->port = sit.getIntProp("port");
l->cmd = sit.getProp("cmd");
l->description = sit.getProp("description");
if( l->name.empty() )
{
......@@ -195,7 +196,7 @@ void LogDB::flushBuffer()
if( !db->insert(qbuf.front()) )
{
dbcrit << myname << "(flushBuffer): error: " << db->error() <<
" lost query: " << qbuf.front() << endl;
" lost query: " << qbuf.front() << endl;
}
qbuf.pop();
......@@ -493,7 +494,7 @@ void LogDB::Log::close()
#ifndef DISABLE_REST_API
// -----------------------------------------------------------------------------
class LogDBRequestHandler:
public Poco::Net::HTTPRequestHandler
public Poco::Net::HTTPRequestHandler
{
public:
......@@ -526,7 +527,7 @@ void LogDB::handleRequest( Poco::Net::HTTPServerRequest& req, Poco::Net::HTTPSer
// В этой версии API поддерживается только GET
if( req.getMethod() != "GET" )
{
auto jdata = respError(resp, HTTPResponse::HTTP_BAD_REQUEST,"method must be 'GET'");
auto jdata = respError(resp, HTTPResponse::HTTP_BAD_REQUEST, "method must be 'GET'");
jdata->stringify(out);
out.flush();
return;
......@@ -548,7 +549,7 @@ void LogDB::handleRequest( Poco::Net::HTTPServerRequest& req, Poco::Net::HTTPSer
{
ostringstream err;
err << "Bad request structure. Must be /api/" << uniset::UHttp::UHTTP_API_VERSION << "/logdb/xxx";
auto jdata = respError(resp, HTTPResponse::HTTP_BAD_REQUEST,err.str());
auto jdata = respError(resp, HTTPResponse::HTTP_BAD_REQUEST, err.str());
jdata->stringify(out);
out.flush();
return;
......@@ -562,11 +563,11 @@ void LogDB::handleRequest( Poco::Net::HTTPServerRequest& req, Poco::Net::HTTPSer
if( cmd == "help" )
{
out << "{ \"help\": ["
"{\"help\": {\"desc\": \"this help\"}},"
"{\"list\": {\"desc\": \"list of logs\"}},"
"{\"read?logname&offset=N&limit=M\": {\"desc\": \"read logs\"}},"
"{\"apidocs\": {\"desc\": \"https://github.com/Etersoft/uniset2\"}}"
"]}";
"{\"help\": {\"desc\": \"this help\"}},"
"{\"list\": {\"desc\": \"list of logs\"}},"
"{\"read?logname&offset=N&limit=M\": {\"desc\": \"read logs\"}},"
"{\"apidocs\": {\"desc\": \"https://github.com/Etersoft/uniset2\"}}"
"]}";
}
else
{
......@@ -576,7 +577,7 @@ void LogDB::handleRequest( Poco::Net::HTTPServerRequest& req, Poco::Net::HTTPSer
}
catch( std::exception& ex )
{
auto jdata = respError(resp, HTTPResponse::HTTP_INTERNAL_SERVER_ERROR,ex.what());
auto jdata = respError(resp, HTTPResponse::HTTP_INTERNAL_SERVER_ERROR, ex.what());
jdata->stringify(out);
}
......@@ -584,8 +585,8 @@ void LogDB::handleRequest( Poco::Net::HTTPServerRequest& req, Poco::Net::HTTPSer
}
// -----------------------------------------------------------------------------
Poco::JSON::Object::Ptr LogDB::respError( Poco::Net::HTTPServerResponse& resp,
Poco::Net::HTTPResponse::HTTPStatus estatus,
const string& message )
Poco::Net::HTTPResponse::HTTPStatus estatus,
const string& message )
{
resp.setStatus(estatus);
resp.setContentType("text/json");
......@@ -601,6 +602,9 @@ Poco::JSON::Object::Ptr LogDB::httpGetRequest( const string& cmd, const Poco::UR
if( cmd == "list" )
return httpGetList(p);
if( cmd == "logs" )
return httpGetLogs(p);
ostringstream err;
err << "Unknown command '" << cmd << "'";
throw uniset::SystemError(err.str());
......@@ -629,37 +633,96 @@ Poco::JSON::Object::Ptr LogDB::httpGetList( const Poco::URI::QueryParameters& p
q << "SELECT COUNT(*), name FROM logs GROUP BY name";
DBResult ret = db->query(q.str());
if( !ret )
return jdata;
for( auto it = ret.begin(); it!=ret.end(); ++it )
for( auto it = ret.begin(); it != ret.end(); ++it )
{
Poco::JSON::Object::Ptr j = new Poco::JSON::Object();
j->set("name", it.as_string("name"));
jlist->add(j);
}
#else
// Получение из конфигурации
// хорошо тем, что если логов ещё не было
// то всё-равно видно, какие доступны потенциально
// плохо тем, что если конфигурацию поменяли (убрали какой-то лог)
// а в БД записи по нему остались, то мы не получим к ним доступ
/*! \todo пока список logservers формируется только в начале (в конструкторе)
* можно не защищаться mutex-ом, т.к. мы его не меняем
* если вдруг в REST API будет возможность добавлять логи.. нужно защищаться
* либо переделывать обработку
*/
for( const auto& s: logservers )
{
Poco::JSON::Object::Ptr j = new Poco::JSON::Object();
j->set("name", s->name);
jlist->add(j);
}
// Получение из конфигурации
// хорошо тем, что если логов ещё не было
// то всё-равно видно, какие доступны потенциально
// плохо тем, что если конфигурацию поменяли (убрали какой-то лог)
// а в БД записи по нему остались, то мы не получим к ним доступ
/*! \todo пока список logservers формируется только в начале (в конструкторе)
* можно не защищаться mutex-ом, т.к. мы его не меняем
* если вдруг в REST API будет возможность добавлять логи.. нужно защищаться
* либо переделывать обработку
*/
for( const auto& s : logservers )
{
Poco::JSON::Object::Ptr j = new Poco::JSON::Object();
j->set("name", s->name);
j->set("description", s->description);
jlist->add(j);
}
#endif
return jdata;
}
// -----------------------------------------------------------------------------
Poco::JSON::Object::Ptr LogDB::httpGetLogs( const Poco::URI::QueryParameters& params )
{
Poco::JSON::Object::Ptr jdata = new Poco::JSON::Object();
std::string logname = params[0].first;
if( logname.empty() )
{
ostringstream err;
err << "BAD REQUEST: unknown logname";
throw uniset::SystemError(err.str());
}
size_t offset = 0;
size_t limit = 0;
for( const auto& p : params )
{
if( p.first == "offset" )
offset = uni_atoi(p.second);
else if( p.first == "limit" )
limit = uni_atoi(p.second);
}
Poco::JSON::Array::Ptr jlist = uniset::json::make_child_array(jdata, "logs");
ostringstream q;
q << "SELECT tms,"
<< " strftime('%d-%m-%Y',datetime(tms,'unixepoch')) as date,"
<< " strftime('%H:%M:%S',datetime(tms,'unixepoch')) as time,"
<< " usec, text FROM logs WHERE name='" << logname << "'";
if( limit > 0 )
q << " ORDER BY tms ASC LIMIT " << offset << "," << limit;
DBResult ret = db->query(q.str());
if( !ret )
return jdata;
for( auto it = ret.begin(); it != ret.end(); ++it )
{
Poco::JSON::Object::Ptr j = new Poco::JSON::Object();
j->set("tms", it.as_string("tms"));
j->set("date", it.as_string("date"));
j->set("time", it.as_string("time"));
j->set("usec", it.as_string("usec"));
j->set("text", it.as_string("text"));
jlist->add(j);
}
return jdata;
}
// -----------------------------------------------------------------------------
#endif
// -----------------------------------------------------------------------------
......@@ -55,11 +55,15 @@ namespace uniset
и сохранении их в БД (sqlite). Помимо этого LogDB выступает в качестве
REST сервиса, позволяющего получать логи за указанный период в виде json.
Реализация намеренно простая, т.к. пока неясно нужно ли это и в каком виде.
Ожидается что контролируемых логов будет не очень много (максимум несколько десятков)
и каждый лог будет генерировать не более 2-5 мегабайт записей. Поэтому sqlite должно хватить.
\section sec_LogDB_Conf Конфигурирвание LogDB
<LogDB name="LogDB" ...>
<logserver name="" ip=".." port=".." cmd=".."/>
<logserver name="" ip=".." port=".." cmd=".."/>
<logserver name="" ip=".." port=".." cmd=".." description=".."/>
<logserver name="" ip=".." port=".." cmd=".." description=".."/>
<logserver name="" ip=".." port=".." cmd=".."/>
</LogDB>
......@@ -73,16 +77,17 @@ namespace uniset
/help - Получение списка доступных команд
/list - список доступных логов
/read?logname&offset=N&limit=M - получение логов 'logname'
/logs?logname&offset=N&limit=M - получение логов 'logname'
Не обязательные параметры:
offset - начиная с,
limit - количество в ответе.
offset - начиная с,
limit - количество в ответе.
\todo Добавить настройки таймаутов, размера буфера, размера для резервирования под строку, количество потоков для http и т.п.
\todo Добавить ротацию БД
\todo REST API: продумать команды и реализовать
\todo Сделать настройку, для формата даты и времени при выгрузке из БД (при формировании json).
\todo Продумать поддержку websocket
\todo Возможно в последствии оптимизировать таблицы (нормализовать) если будет тормозить. Сейчас пока прототип.
*/
......@@ -127,6 +132,7 @@ namespace uniset
Poco::JSON::Object::Ptr respError( Poco::Net::HTTPServerResponse& resp, Poco::Net::HTTPResponse::HTTPStatus s, const std::string& message );
Poco::JSON::Object::Ptr httpGetRequest( const std::string& cmd, const Poco::URI::QueryParameters& p );
Poco::JSON::Object::Ptr httpGetList( const Poco::URI::QueryParameters& p );
Poco::JSON::Object::Ptr httpGetLogs( const Poco::URI::QueryParameters& p );
#endif
std::string myname;
std::unique_ptr<SQLiteInterface> db;
......@@ -147,6 +153,7 @@ namespace uniset
int port = { 0 };
std::string cmd;
std::string peername;
std::string description;
std::shared_ptr<DebugStream> dblog;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment