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 @@
<LogDB
name=
"LogDB"
>
<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>
<settings>
...
...
extensions/LogDB/LogDB.cc
View file @
de9ac27b
...
...
@@ -195,7 +195,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
();
...
...
@@ -208,7 +208,7 @@ void LogDB::addLog( LogDB::Log* log, const string& txt )
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_nsec
<<
"','"
// usec
<<
log
->
name
<<
"','"
...
...
@@ -493,7 +493,7 @@ void LogDB::Log::close()
#ifndef DISABLE_REST_API
// -----------------------------------------------------------------------------
class
LogDBRequestHandler
:
public
Poco
::
Net
::
HTTPRequestHandler
public
Poco
::
Net
::
HTTPRequestHandler
{
public
:
...
...
@@ -516,138 +516,149 @@ Poco::Net::HTTPRequestHandler* LogDB::createRequestHandler( const Poco::Net::HTT
// -----------------------------------------------------------------------------
void
LogDB
::
handleRequest
(
Poco
::
Net
::
HTTPServerRequest
&
req
,
Poco
::
Net
::
HTTPServerResponse
&
resp
)
{
dbinfo
<<
myname
<<
"(handleRequest): ...."
<<
endl
;
using
Poco
::
Net
::
HTTPResponse
;
// std::ostream& out = resp.send();
std
::
ostream
&
out
=
resp
.
send
();
resp
.
setContentType
(
"text/json"
);
try
{
respError
(
resp
,
HTTPResponse
::
HTTP_INTERNAL_SERVER_ERROR
,
"Unknown request"
);
}
catch
(
std
::
exception
&
ex
)
{
dbcrit
<<
myname
<<
"(handleRequest): request error: "
<<
ex
.
what
()
<<
endl
;
respError
(
resp
,
HTTPResponse
::
HTTP_INTERNAL_SERVER_ERROR
,
ex
.
what
());
}
#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;
}
// В этой версии API поддерживается только GET
if
(
req
.
getMethod
()
!=
"GET"
)
{
auto
jdata
=
respError
(
resp
,
HTTPResponse
::
HTTP_BAD_REQUEST
,
"method must be 'GET'"
);
jdata
->
stringify
(
out
);
out
.
flush
();
return
;
}
Poco::URI uri(req.getURI());
Poco
::
URI
uri
(
req
.
getURI
());
if( log->is_info() )
log->info() << req.getHost() << ": query: " << uri.getQuery() << endl;
dblog3
<<
req
.
getHost
()
<<
": query: "
<<
uri
.
getQuery
()
<<
endl
;
std::vector<std::string> seg;
uri.getPathSegments(seg);
std
::
vector
<
std
::
string
>
seg
;
uri
.
getPathSegments
(
seg
);
// example: http://host:port/api/version/ObjectName
if( seg.size() < 3
|| seg[0] != "api"
|| seg[1] != UHTTP_API_VERSION
|| seg[2].empty() )
{
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", "BAD REQUEST STRUCTURE");
jdata.stringify(out);
out.flush();
return;
}
// example: http://host:port/api/version/logdb/..
if
(
seg
.
size
()
<
4
||
seg
[
0
]
!=
"api"
||
seg
[
1
]
!=
uniset
::
UHttp
::
UHTTP_API_VERSION
||
seg
[
2
].
empty
()
||
seg
[
2
]
!=
"logdb"
)
{
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
());
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.setContentType("text/json");
std::ostream& out = resp.send();
resp
.
setStatus
(
HTTPResponse
::
HTTP_OK
);
string
cmd
=
seg
[
3
];
try
{
if( objectName == "help" )
if
(
cmd
==
"help"
)
{
out
<<
"{
\"
help
\"
: ["
"{\"help\": {\"desc\": \"this help\"}},"
"{\"list\": {\"desc\": \"list of objects\"}},"
"{\"ObjectName\": {\"desc\": \"ObjectName information\"}},"
"{\"ObjectName/help\": {\"desc\": \"help for ObjectName\"}},"
"{\"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);
"{
\"
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
{
auto json =
registry->httpGetByName(objectName
, qp);
auto
json
=
httpGetRequest
(
cmd
,
qp
);
json
->
stringify
(
out
);
}
}
// catch( Poco::JSON::JSONException jsone )
// {
// std::cout << "JSON ERROR: " << jsone.message() << std::endl;
// }
catch
(
std
::
exception
&
ex
)
{
ostringstream err;
err << ex.what();
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);
auto
jdata
=
respError
(
resp
,
HTTPResponse
::
HTTP_INTERNAL_SERVER_ERROR
,
ex
.
what
());
jdata
->
stringify
(
out
);
}
out
.
flush
();
#endif
}
// -----------------------------------------------------------------------------
void
LogDB
::
respError
(
Poco
::
Net
::
HTTPServerResponse
&
resp
,
Poco
::
Net
::
HTTPResponse
::
HTTPStatus
estatus
,
const
string
&
message
)
Poco
::
JSON
::
Object
::
Ptr
LogDB
::
respError
(
Poco
::
Net
::
HTTPServerResponse
&
resp
,
Poco
::
Net
::
HTTPResponse
::
HTTPStatus
estatus
,
const
string
&
message
)
{
resp
.
setStatus
(
estatus
);
resp
.
setContentType
(
"text/json"
);
std
::
ostream
&
out
=
resp
.
send
();
Poco
::
JSON
::
Object
::
Ptr
jdata
=
new
Poco
::
JSON
::
Object
();
jdata
->
set
(
"error"
,
resp
.
getReasonForStatus
(
resp
.
getStatus
()));
jdata
->
set
(
"ecode"
,
(
int
)
resp
.
getStatus
());
jdata
->
set
(
"message"
,
message
);
jdata
->
stringify
(
out
);
out
.
flush
();
return
jdata
;
}
// -----------------------------------------------------------------------------
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
...
...
extensions/LogDB/LogDB.h
View file @
de9ac27b
...
...
@@ -26,6 +26,7 @@
#include <chrono>
#include <ev++.h>
#include <sigc++/sigc++.h>
#include <Poco/JSON/Object.h>
#include "UniSetTypes.h"
#include "LogAgregator.h"
#include "DebugStream.h"
...
...
@@ -68,12 +69,22 @@ namespace uniset
http-сервер. Параметры запуска можно указать при помощи:
--prefix-httpserver-host и --prefix-httpserver-port.
Запросы принимаются по: api/version/logdb/...
\todo Добавить настройки таймаутов, размера буфера, размера для резервирования под строку,...
\todo Реализовать посылку команд
/help - Получение списка доступных команд
/list - список доступных логов
/read?logname&offset=N&limit=M - получение логов 'logname'
Не обязательные параметры:
offset - начиная с,
limit - количество в ответе.
\todo Добавить настройки таймаутов, размера буфера, размера для резервирования под строку, количество потоков для http и т.п.
\todo Добавить ротацию БД
\todo REST API: продумать команды и реализовать
\todo Продумать поддержку websocket
\todo Возможно в последствии оптимизировать таблицы (нормализовать) если будет тормозить. Сейчас пока прототип.
*/
class
LogDB
:
public
EventLoopServer
...
...
@@ -113,7 +124,9 @@ namespace uniset
void
onCheckBuffer
(
ev
::
timer
&
t
,
int
revents
);
void
addLog
(
Log
*
log
,
const
std
::
string
&
txt
);
#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
std
::
string
myname
;
std
::
unique_ptr
<
SQLiteInterface
>
db
;
...
...
extensions/LogDB/create_test_db.sh
View file @
de9ac27b
...
...
@@ -8,8 +8,8 @@ sqlite3 $dbname <<"_EOF_"
PRAGMA
foreign_keys
=
ON
;
DROP TABLE IF EXISTS log
;
CREATE TABLE log
(
DROP TABLE IF EXISTS log
s
;
CREATE TABLE log
s
(
id
INTEGER PRIMARY KEY AUTOINCREMENT,
tms timestamp KEY default
(
strftime
(
'%s'
,
'now'
))
,
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