Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
U
uniset2
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
1
Issues
1
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
UniSet project repositories
uniset2
Commits
de9ac27b
Commit
de9ac27b
authored
Sep 05, 2017
by
Pavel Vainerman
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
(LogDB): начало работы на REST API. Накидал скелет релизации,
а также функцию получения списка доступных логов (/list).
parent
8299f380
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
131 additions
and
107 deletions
+131
-107
test.xml
conf/test.xml
+1
-1
LogDB.cc
extensions/LogDB/LogDB.cc
+112
-101
LogDB.h
extensions/LogDB/LogDB.h
+16
-3
create_test_db.sh
extensions/LogDB/create_test_db.sh
+2
-2
No files found.
conf/test.xml
View file @
de9ac27b
...
@@ -41,7 +41,7 @@
...
@@ -41,7 +41,7 @@
<LogDB
name=
"LogDB"
>
<LogDB
name=
"LogDB"
>
<logserver
name=
"logserver1"
ip=
"localhost"
port=
"3333"
cmd=
"-s level1"
/>
<logserver
name=
"logserver1"
ip=
"localhost"
port=
"3333"
cmd=
"-s level1"
/>
<
!-- <logserver name="logserver2" ip="localhost" port="3333" cmd=""/> --
>
<
logserver
name=
"logserver2"
ip=
"localhost"
port=
"3333"
cmd=
""
/
>
</LogDB>
</LogDB>
<settings>
<settings>
...
...
extensions/LogDB/LogDB.cc
View file @
de9ac27b
...
@@ -195,7 +195,7 @@ void LogDB::flushBuffer()
...
@@ -195,7 +195,7 @@ void LogDB::flushBuffer()
if
(
!
db
->
insert
(
qbuf
.
front
())
)
if
(
!
db
->
insert
(
qbuf
.
front
())
)
{
{
dbcrit
<<
myname
<<
"(flushBuffer): error: "
<<
db
->
error
()
<<
dbcrit
<<
myname
<<
"(flushBuffer): error: "
<<
db
->
error
()
<<
" lost query: "
<<
qbuf
.
front
()
<<
endl
;
" lost query: "
<<
qbuf
.
front
()
<<
endl
;
}
}
qbuf
.
pop
();
qbuf
.
pop
();
...
@@ -208,7 +208,7 @@ void LogDB::addLog( LogDB::Log* log, const string& txt )
...
@@ -208,7 +208,7 @@ void LogDB::addLog( LogDB::Log* log, const string& txt )
ostringstream
q
;
ostringstream
q
;
q
<<
"INSERT INTO log(tms,usec,name,text) VALUES('"
q
<<
"INSERT INTO log
s
(tms,usec,name,text) VALUES('"
<<
tm
.
tv_sec
<<
"','"
// timestamp
<<
tm
.
tv_sec
<<
"','"
// timestamp
<<
tm
.
tv_nsec
<<
"','"
// usec
<<
tm
.
tv_nsec
<<
"','"
// usec
<<
log
->
name
<<
"','"
<<
log
->
name
<<
"','"
...
@@ -493,7 +493,7 @@ void LogDB::Log::close()
...
@@ -493,7 +493,7 @@ void LogDB::Log::close()
#ifndef DISABLE_REST_API
#ifndef DISABLE_REST_API
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
class
LogDBRequestHandler
:
class
LogDBRequestHandler
:
public
Poco
::
Net
::
HTTPRequestHandler
public
Poco
::
Net
::
HTTPRequestHandler
{
{
public
:
public
:
...
@@ -516,138 +516,149 @@ Poco::Net::HTTPRequestHandler* LogDB::createRequestHandler( const Poco::Net::HTT
...
@@ -516,138 +516,149 @@ Poco::Net::HTTPRequestHandler* LogDB::createRequestHandler( const Poco::Net::HTT
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
void
LogDB
::
handleRequest
(
Poco
::
Net
::
HTTPServerRequest
&
req
,
Poco
::
Net
::
HTTPServerResponse
&
resp
)
void
LogDB
::
handleRequest
(
Poco
::
Net
::
HTTPServerRequest
&
req
,
Poco
::
Net
::
HTTPServerResponse
&
resp
)
{
{
dbinfo
<<
myname
<<
"(handleRequest): ...."
<<
endl
;
using
Poco
::
Net
::
HTTPResponse
;
using
Poco
::
Net
::
HTTPResponse
;
// std::ostream& out = resp.send();
std
::
ostream
&
out
=
resp
.
send
();
resp
.
setContentType
(
"text/json"
);
try
try
{
{
respError
(
resp
,
HTTPResponse
::
HTTP_INTERNAL_SERVER_ERROR
,
"Unknown request"
);
// В этой версии API поддерживается только GET
}
if
(
req
.
getMethod
()
!=
"GET"
)
catch
(
std
::
exception
&
ex
)
{
{
auto
jdata
=
respError
(
resp
,
HTTPResponse
::
HTTP_BAD_REQUEST
,
"method must be 'GET'"
);
dbcrit
<<
myname
<<
"(handleRequest): request error: "
<<
ex
.
what
()
<<
endl
;
jdata
->
stringify
(
out
);
respError
(
resp
,
HTTPResponse
::
HTTP_INTERNAL_SERVER_ERROR
,
ex
.
what
());
out
.
flush
();
}
return
;
}
#if 0
// В этой версии API поддерживается только GET
if( req.getMethod() != "GET" )
{
resp.setStatus(HTTPResponse::HTTP_BAD_REQUEST);
resp.setContentType("text/json");
std::ostream& out = resp.send();
Poco::JSON::Object jdata;
jdata.set("error", resp.getReasonForStatus(resp.getStatus()));
jdata.set("ecode", (int)resp.getStatus());
jdata.set("message", "method must be 'GET'");
jdata.stringify(out);
out.flush();
return;
}
Poco::URI uri(req.getURI());
Poco
::
URI
uri
(
req
.
getURI
());
if( log->is_info() )
dblog3
<<
req
.
getHost
()
<<
": query: "
<<
uri
.
getQuery
()
<<
endl
;
log->info() << req.getHost() << ": query: " << uri.getQuery() << endl;
std::vector<std::string> seg;
std
::
vector
<
std
::
string
>
seg
;
uri.getPathSegments(seg);
uri
.
getPathSegments
(
seg
);
// example: http://host:port/api/version/ObjectName
// example: http://host:port/api/version/logdb/..
if( seg.size() < 3
if
(
seg
.
size
()
<
4
|| seg[0] != "api"
||
seg
[
0
]
!=
"api"
|| seg[1] != UHTTP_API_VERSION
||
seg
[
1
]
!=
uniset
::
UHttp
::
UHTTP_API_VERSION
|| seg[2].empty() )
||
seg
[
2
].
empty
()
{
||
seg
[
2
]
!=
"logdb"
)
resp.setStatus(HTTPResponse::HTTP_BAD_REQUEST);
{
resp.setContentType("text/json");
ostringstream
err
;
std::ostream& out = resp.send();
err
<<
"Bad request structure. Must be /api/"
<<
uniset
::
UHttp
::
UHTTP_API_VERSION
<<
"/logdb/xxx"
;
Poco::JSON::Object jdata;
auto
jdata
=
respError
(
resp
,
HTTPResponse
::
HTTP_BAD_REQUEST
,
err
.
str
());
jdata.set("error", resp.getReasonForStatus(resp.getStatus()));
jdata
->
stringify
(
out
);
jdata.set("ecode", (int)resp.getStatus());
out
.
flush
();
jdata.set("message", "BAD REQUEST STRUCTURE");
return
;
jdata.stringify(out);
}
out.flush();
return;
}
const std::string objectName(seg[2]);
auto
qp
=
uri
.
getQueryParameters
();
auto qp = uri.getQueryParameters();
resp.setStatus(HTTPResponse::HTTP_OK);
resp
.
setStatus
(
HTTPResponse
::
HTTP_OK
);
resp.setContentType("text/json");
string
cmd
=
seg
[
3
];
std::ostream& out = resp.send();
try
if
(
cmd
==
"help"
)
{
if( objectName == "help" )
{
{
out
<<
"{
\"
help
\"
: ["
out
<<
"{
\"
help
\"
: ["
"{\"help\": {\"desc\": \"this help\"}},"
"{
\"
help
\"
: {
\"
desc
\"
:
\"
this help
\"
}},"
"{\"list\": {\"desc\": \"list of objects\"}},"
"{
\"
list
\"
: {
\"
desc
\"
:
\"
list of logs
\"
}},"
"{\"ObjectName\": {\"desc\": \"ObjectName information\"}},"
"{
\"
read?logname&offset=N&limit=M
\"
: {
\"
desc
\"
:
\"
read logs
\"
}},"
"{\"ObjectName/help\": {\"desc\": \"help for ObjectName\"}},"
"{
\"
apidocs
\"
: {
\"
desc
\"
:
\"
https://github.com/Etersoft/uniset2
\"
}}"
"{\"apidocs\": {\"desc\": \"https://github.com/Etersoft/uniset2\"}}"
"]}"
;
"]}";
}
else if( objectName == "list" )
{
auto json = registry->httpGetObjectsList(qp);
json->stringify(out);
}
else if( seg.size() == 4 && seg[3] == "help" ) // /api/version/ObjectName/help
{
auto json = registry->httpHelpByName(objectName, qp);
json->stringify(out);
}
else if( seg.size() >= 4 ) // /api/version/ObjectName/xxx..
{
auto json = registry->httpRequestByName(objectName, seg[3], qp);
json->stringify(out);
}
}
else
else
{
{
auto json =
registry->httpGetByName(objectName
, qp);
auto
json
=
httpGetRequest
(
cmd
,
qp
);
json
->
stringify
(
out
);
json
->
stringify
(
out
);
}
}
}
}
// catch( Poco::JSON::JSONException jsone )
// {
// std::cout << "JSON ERROR: " << jsone.message() << std::endl;
// }
catch
(
std
::
exception
&
ex
)
catch
(
std
::
exception
&
ex
)
{
{
ostringstream err;
auto
jdata
=
respError
(
resp
,
HTTPResponse
::
HTTP_INTERNAL_SERVER_ERROR
,
ex
.
what
());
err << ex.what();
jdata
->
stringify
(
out
);
resp.setStatus(HTTPResponse::HTTP_INTERNAL_SERVER_ERROR);
resp.setContentType("text/json");
Poco::JSON::Object jdata;
jdata.set("error", err.str());
jdata.set("ecode", (int)resp.getStatus());
jdata.stringify(out);
}
}
out
.
flush
();
out
.
flush
();
#endif
}
}
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
void
LogDB
::
respError
(
Poco
::
Net
::
HTTPServerResponse
&
resp
,
Poco
::
JSON
::
Object
::
Ptr
LogDB
::
respError
(
Poco
::
Net
::
HTTPServerResponse
&
resp
,
Poco
::
Net
::
HTTPResponse
::
HTTPStatus
estatus
,
Poco
::
Net
::
HTTPResponse
::
HTTPStatus
estatus
,
const
string
&
message
)
const
string
&
message
)
{
{
resp
.
setStatus
(
estatus
);
resp
.
setStatus
(
estatus
);
resp
.
setContentType
(
"text/json"
);
resp
.
setContentType
(
"text/json"
);
std
::
ostream
&
out
=
resp
.
send
();
Poco
::
JSON
::
Object
::
Ptr
jdata
=
new
Poco
::
JSON
::
Object
();
Poco
::
JSON
::
Object
::
Ptr
jdata
=
new
Poco
::
JSON
::
Object
();
jdata
->
set
(
"error"
,
resp
.
getReasonForStatus
(
resp
.
getStatus
()));
jdata
->
set
(
"error"
,
resp
.
getReasonForStatus
(
resp
.
getStatus
()));
jdata
->
set
(
"ecode"
,
(
int
)
resp
.
getStatus
());
jdata
->
set
(
"ecode"
,
(
int
)
resp
.
getStatus
());
jdata
->
set
(
"message"
,
message
);
jdata
->
set
(
"message"
,
message
);
jdata
->
stringify
(
out
);
return
jdata
;
out
.
flush
();
}
// -----------------------------------------------------------------------------
Poco
::
JSON
::
Object
::
Ptr
LogDB
::
httpGetRequest
(
const
string
&
cmd
,
const
Poco
::
URI
::
QueryParameters
&
p
)
{
if
(
cmd
==
"list"
)
return
httpGetList
(
p
);
ostringstream
err
;
err
<<
"Unknown command '"
<<
cmd
<<
"'"
;
throw
uniset
::
SystemError
(
err
.
str
());
}
// -----------------------------------------------------------------------------
Poco
::
JSON
::
Object
::
Ptr
LogDB
::
httpGetList
(
const
Poco
::
URI
::
QueryParameters
&
p
)
{
if
(
!
db
)
{
ostringstream
err
;
err
<<
"DB unavailable.."
;
throw
uniset
::
SystemError
(
err
.
str
());
}
Poco
::
JSON
::
Object
::
Ptr
jdata
=
new
Poco
::
JSON
::
Object
();
Poco
::
JSON
::
Array
::
Ptr
jlist
=
uniset
::
json
::
make_child_array
(
jdata
,
"logs"
);
#if 0
// Получение из БД
// хорошо тем, что возвращаем список реально доступных логов (т.е. тех что есть в БД)
// плохо тем, что если в конфигурации добавили какие-то логи, но в БД
// ещё ничего не попало, мы их не увидим
ostringstream q;
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 )
{
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
);
}
#endif
return
jdata
;
}
}
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
#endif
#endif
...
...
extensions/LogDB/LogDB.h
View file @
de9ac27b
...
@@ -26,6 +26,7 @@
...
@@ -26,6 +26,7 @@
#include <chrono>
#include <chrono>
#include <ev++.h>
#include <ev++.h>
#include <sigc++/sigc++.h>
#include <sigc++/sigc++.h>
#include <Poco/JSON/Object.h>
#include "UniSetTypes.h"
#include "UniSetTypes.h"
#include "LogAgregator.h"
#include "LogAgregator.h"
#include "DebugStream.h"
#include "DebugStream.h"
...
@@ -68,12 +69,22 @@ namespace uniset
...
@@ -68,12 +69,22 @@ namespace uniset
http-сервер. Параметры запуска можно указать при помощи:
http-сервер. Параметры запуска можно указать при помощи:
--prefix-httpserver-host и --prefix-httpserver-port.
--prefix-httpserver-host и --prefix-httpserver-port.
Запросы принимаются по: api/version/logdb/...
\todo Добавить настройки таймаутов, размера буфера, размера для резервирования под строку,...
/help - Получение списка доступных команд
\todo Реализовать посылку команд
/list - список доступных логов
/read?logname&offset=N&limit=M - получение логов 'logname'
Не обязательные параметры:
offset - начиная с,
limit - количество в ответе.
\todo Добавить настройки таймаутов, размера буфера, размера для резервирования под строку, количество потоков для http и т.п.
\todo Добавить ротацию БД
\todo Добавить ротацию БД
\todo REST API: продумать команды и реализовать
\todo REST API: продумать команды и реализовать
\todo Продумать поддержку websocket
\todo Продумать поддержку websocket
\todo Возможно в последствии оптимизировать таблицы (нормализовать) если будет тормозить. Сейчас пока прототип.
*/
*/
class
LogDB
:
class
LogDB
:
public
EventLoopServer
public
EventLoopServer
...
@@ -113,7 +124,9 @@ namespace uniset
...
@@ -113,7 +124,9 @@ namespace uniset
void
onCheckBuffer
(
ev
::
timer
&
t
,
int
revents
);
void
onCheckBuffer
(
ev
::
timer
&
t
,
int
revents
);
void
addLog
(
Log
*
log
,
const
std
::
string
&
txt
);
void
addLog
(
Log
*
log
,
const
std
::
string
&
txt
);
#ifndef DISABLE_REST_API
#ifndef DISABLE_REST_API
void
respError
(
Poco
::
Net
::
HTTPServerResponse
&
resp
,
Poco
::
Net
::
HTTPResponse
::
HTTPStatus
s
,
const
std
::
string
&
message
);
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
);
#endif
#endif
std
::
string
myname
;
std
::
string
myname
;
std
::
unique_ptr
<
SQLiteInterface
>
db
;
std
::
unique_ptr
<
SQLiteInterface
>
db
;
...
...
extensions/LogDB/create_test_db.sh
View file @
de9ac27b
...
@@ -8,8 +8,8 @@ sqlite3 $dbname <<"_EOF_"
...
@@ -8,8 +8,8 @@ sqlite3 $dbname <<"_EOF_"
PRAGMA
foreign_keys
=
ON
;
PRAGMA
foreign_keys
=
ON
;
DROP TABLE IF EXISTS log
;
DROP TABLE IF EXISTS log
s
;
CREATE TABLE log
(
CREATE TABLE log
s
(
id
INTEGER PRIMARY KEY AUTOINCREMENT,
id
INTEGER PRIMARY KEY AUTOINCREMENT,
tms timestamp KEY default
(
strftime
(
'%s'
,
'now'
))
,
tms timestamp KEY default
(
strftime
(
'%s'
,
'now'
))
,
usec INTEGER
(
5
)
NOT NULL,
usec INTEGER
(
5
)
NOT NULL,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment