JSON output

This commit is contained in:
2025-08-05 05:30:13 +02:00
parent 08daf76d81
commit 8b6c7c7bc8
4 changed files with 83 additions and 6 deletions

View File

@@ -75,6 +75,9 @@ garbage collection period in number of refresh. default is 50.
\fB-P\fP
Show only processes with the specified pid(s).
.TP
\fB-j\fP
Output in JSON format.
.TP
\fB-f\fP
EXPERIMENTAL: specify string pcap filter (like tcpdump). This may be removed or changed in a future version.
.TP

View File

@@ -49,6 +49,8 @@ extern int viewMode;
extern bool showcommandline;
extern bool showBasename;
extern bool output_json;
extern unsigned refreshlimit;
extern unsigned refreshcount;
@@ -90,6 +92,7 @@ public:
void show(int row, unsigned int proglen, unsigned int devlen);
void log();
void json();
double sent_value;
double recv_value;
@@ -232,6 +235,48 @@ void Line::log() {
<< recv_value << std::endl;
}
#include <iomanip>
std::string escape_json(const std::string &s) {
std::ostringstream o;
for (auto c = s.cbegin(); c != s.cend(); c++) {
switch (*c) {
case '"': o << "\\\""; break;
case '\\': o << "\\\\"; break;
case '\b': o << "\\b"; break;
case '\f': o << "\\f"; break;
case '\n': o << "\\n"; break;
case '\r': o << "\\r"; break;
case '\t': o << "\\t"; break;
default:
if ('\x00' <= *c && *c <= '\x1f') {
o << "\\u"
<< std::hex << std::setw(4) << std::setfill('0') << static_cast<int>(*c);
} else {
o << *c;
}
}
}
return o.str();
}
void Line::json() {
std::cout << "{";
std::cout << "\"name\": \"" << escape_json(m_name) << "\"";
std::cout << ", ";
std::cout << "\"pid\": \"" << m_pid << "\"";
std::cout << ", ";
std::cout << "\"uid\": \"" << m_uid << "\"";
std::cout << ", ";
std::cout << "\"devicename\": \"" << devicename << "\"";
std::cout << ", ";
std::cout << "\"sent\": " << sent_value;
std::cout << ", ";
std::cout << "\"recv\": " << recv_value;
std::cout << "}";
}
int get_devlen(Line *lines[], int nproc, int rows) {
int devlen = MIN_COLUMN_WIDTH_DEV;
int curlen;
@@ -343,6 +388,28 @@ void show_trace(Line *lines[], int nproc) {
}
}
char* get_iso8601_timestamp() {
static char buffer[32];
time_t now = time(NULL);
struct tm *utc = gmtime(&now);
strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%SZ", utc);
return buffer;
}
void show_json(Line *lines[], int nproc) {
/* print them */
std::cout << "{\"timestamp\": \""<< get_iso8601_timestamp() << "\", \"processes\": [";
for (int i = 0; i < nproc; i++) {
if(i>0){
std::cout << ",";
}
lines[i]->json();
delete lines[i];
}
std::cout << "]}"<< std::endl;
}
void show_ncurses(Line *lines[], int nproc) {
int rows; // number of terminal rows
int cols; // number of terminal columns
@@ -453,7 +520,9 @@ void do_refresh() {
/* sort the accumulated lines */
qsort(lines, nproc, sizeof(Line *), GreatestFirst);
if (tracemode || DEBUG)
if (output_json)
show_json(lines, nproc);
else if (tracemode || DEBUG)
show_trace(lines, nproc);
else
show_ncurses(lines, nproc);

View File

@@ -69,6 +69,7 @@ static void help(bool iserror) {
output << " b: display the program basename instead of the fullpath\n";
output << " m: switch between total (kB, bytes, MB) and throughput (kB/s, "
" MB/s, GB/s) mode\n";
output << " j: json output\n";
}
void quit_cb(int /* i */) {
@@ -80,7 +81,7 @@ void quit_cb(int /* i */) {
}
void forceExit(bool success, const char *msg, ...) {
if ((!tracemode) && (!DEBUG)) {
if ((!tracemode) && (!DEBUG) && (!output_json)) {
exit_ui();
}
@@ -141,7 +142,7 @@ void clean_up() {
}
procclean();
if ((!tracemode) && (!DEBUG))
if ((!tracemode) && (!DEBUG) && (!output_json))
exit_ui();
}
@@ -153,7 +154,7 @@ int main(int argc, char **argv) {
int garbage_collection_period = 50;
int opt;
while ((opt = getopt(argc, argv, "Vhxtpsd:v:c:laf:Cbg:P:")) != -1) {
while ((opt = getopt(argc, argv, "Vhxtpsd:v:c:laf:Cbg:P:j")) != -1) {
switch (opt) {
case 'V':
versiondisplay();
@@ -204,6 +205,9 @@ int main(int argc, char **argv) {
case 'P':
pidsToWatch.insert((pid_t)atoi(optarg));
break;
case 'j':
output_json = true;
break;
default:
help(true);
exit(EXIT_FAILURE);
@@ -300,7 +304,7 @@ int main(int argc, char **argv) {
struct dpargs *userdata = (dpargs *)malloc(sizeof(struct dpargs));
if ((!tracemode) && (!DEBUG)) {
if ((!tracemode) && (!DEBUG) && (!output_json)) {
init_ui();
}
@@ -328,7 +332,7 @@ int main(int argc, char **argv) {
time_t const now = ::time(NULL);
if (last_refresh_time + refreshdelay <= now) {
last_refresh_time = now;
if ((!DEBUG) && (!tracemode)) {
if ((!DEBUG) && (!tracemode) && (!output_json)) {
// handle user input
ui_tick();
}

View File

@@ -63,6 +63,7 @@ bool showcommandline = false;
bool showBasename = false;
// viewMode: kb/s or total
int viewMode = VIEWMODE_KBPS;
bool output_json = false;
const char version[] = " version " VERSION;
timeval curtime;