mirror of
https://github.com/tstack/lnav.git
synced 2024-09-11 13:05:51 +03:00
[hist] cleanup the hist_source and add support for graphing a single column at a time
This commit is contained in:
parent
8908e2569f
commit
5ba26cc5f7
@ -99,8 +99,8 @@ public:
|
||||
++this->gr_next_field;
|
||||
};
|
||||
|
||||
void grep_end_batch(grep_proc &gp) { this->analyze(); };
|
||||
void grep_end(grep_proc &gp) { this->analyze(); };
|
||||
void grep_end_batch(grep_proc &gp) { };
|
||||
void grep_end(grep_proc &gp) { };
|
||||
|
||||
private:
|
||||
|
||||
|
@ -236,6 +236,13 @@ through the file.
|
||||
to the log view and move to the line number that was
|
||||
selected in the "log_line" column.
|
||||
|
||||
TAB/Shift+TAB In the SQL result view, cycle through the columns that
|
||||
are graphed. Initially, all number values are displayed
|
||||
in a stacked graph. Pressing TAB will change the display
|
||||
to only graph the first column. Repeatedly pressing TAB
|
||||
will cycle through the columns until they are all graphed
|
||||
again.
|
||||
|
||||
p Enable or disable the display of the fields that the
|
||||
log message parser knows about or has discovered.
|
||||
This overlay is temporarily enabled when the semicolon
|
||||
|
@ -32,6 +32,8 @@
|
||||
#include <math.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <numeric>
|
||||
|
||||
#include "lnav_util.hh"
|
||||
#include "hist_source.hh"
|
||||
|
||||
@ -40,7 +42,6 @@ using namespace std;
|
||||
hist_source::hist_source()
|
||||
: hs_bucket_size(1),
|
||||
hs_group_size(100),
|
||||
hs_analyzed(false),
|
||||
hs_label_source(NULL),
|
||||
hs_token_bucket(NULL)
|
||||
{ }
|
||||
@ -53,8 +54,6 @@ void hist_source::text_value_for_line(textview_curses &tc,
|
||||
int grow = row / (this->buckets_per_group() + 1);
|
||||
int brow = row % (this->buckets_per_group() + 1);
|
||||
|
||||
assert(this->hs_analyzed);
|
||||
|
||||
if (brow == 0) {
|
||||
unsigned long width;
|
||||
vis_line_t height;
|
||||
@ -68,15 +67,17 @@ void hist_source::text_value_for_line(textview_curses &tc,
|
||||
this->hs_token_bucket = NULL;
|
||||
}
|
||||
else {
|
||||
bucket_group_t bg = this->hs_group_keys[grow];
|
||||
std::map<bucket_group_t, bucket_array_t>::iterator group_iter;
|
||||
bucket_t::iterator iter;
|
||||
int bucket_index;
|
||||
|
||||
bucket_index = brow - 1;
|
||||
this->hs_token_bucket = &(this->hs_groups[bg][bucket_index]);
|
||||
group_iter = this->hs_groups.begin();
|
||||
advance(group_iter, grow);
|
||||
this->hs_token_bucket = &(group_iter->second[bucket_index]);
|
||||
if (this->hs_label_source != NULL) {
|
||||
this->hs_label_source->
|
||||
hist_label_for_bucket((bg * this->hs_group_size) +
|
||||
hist_label_for_bucket((group_iter->first * this->hs_group_size) +
|
||||
(bucket_index * this->hs_bucket_size),
|
||||
*this->hs_token_bucket,
|
||||
value_out);
|
||||
@ -90,29 +91,44 @@ void hist_source::text_attrs_for_line(textview_curses &tc,
|
||||
{
|
||||
int grow = row / (this->buckets_per_group() + 1);
|
||||
int brow = row % (this->buckets_per_group() + 1);
|
||||
bucket_group_t bg = this->hs_group_keys[grow];
|
||||
int bucket_index = brow - 1;
|
||||
|
||||
assert(this->hs_analyzed);
|
||||
|
||||
if (this->hs_token_bucket != NULL) {
|
||||
std::map<bucket_group_t, bucket_array_t>::iterator group_iter;
|
||||
view_colors & vc = view_colors::singleton();
|
||||
unsigned long width, avail_width;
|
||||
bucket_t::iterator iter;
|
||||
vis_line_t height;
|
||||
struct line_range lr;
|
||||
|
||||
group_iter = this->hs_groups.begin();
|
||||
advance(group_iter, grow);
|
||||
|
||||
tc.get_dimensions(height, width);
|
||||
avail_width = width - this->hs_token_bucket->size();
|
||||
|
||||
bucket_stats_t overall_stats;
|
||||
|
||||
for (std::map<bucket_type_t, bucket_stats_t>::iterator stats_iter =
|
||||
this->hs_bucket_stats.begin();
|
||||
stats_iter != this->hs_bucket_stats.end();
|
||||
++stats_iter) {
|
||||
if (!this->is_bucket_graphed(stats_iter->first))
|
||||
continue;
|
||||
overall_stats.merge(stats_iter->second);
|
||||
}
|
||||
|
||||
lr.lr_start = 0;
|
||||
for (iter = this->hs_token_bucket->begin();
|
||||
iter != this->hs_token_bucket->end();
|
||||
iter++) {
|
||||
double percent = (double)(iter->second - this->hs_min_count) /
|
||||
(this->hs_max_count - this->hs_min_count);
|
||||
double percent = (double)(iter->second - overall_stats.bs_min_count) /
|
||||
overall_stats.width();
|
||||
int amount, attrs;
|
||||
|
||||
if (!this->is_bucket_graphed(iter->first))
|
||||
continue;
|
||||
|
||||
attrs = vc.
|
||||
reverse_attrs_for_role(this->get_role_for_type(iter->first));
|
||||
amount = (int)rint(percent * avail_width);
|
||||
@ -129,11 +145,11 @@ void hist_source::text_attrs_for_line(textview_curses &tc,
|
||||
lr.lr_start = lr.lr_end;
|
||||
}
|
||||
|
||||
this->hs_label_source->hist_attrs_for_bucket((bg * this->hs_group_size) +
|
||||
(bucket_index *
|
||||
this->hs_bucket_size),
|
||||
*this->hs_token_bucket,
|
||||
value_out);
|
||||
this->hs_label_source->hist_attrs_for_bucket(
|
||||
(group_iter->first * this->hs_group_size) +
|
||||
(bucket_index * this->hs_bucket_size),
|
||||
*this->hs_token_bucket,
|
||||
value_out);
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,8 +159,6 @@ void hist_source::add_value(unsigned int value,
|
||||
{
|
||||
bucket_group_t bg;
|
||||
|
||||
this->hs_analyzed = false;
|
||||
|
||||
bg = bucket_group_t(value / this->hs_group_size);
|
||||
|
||||
bucket_array_t &ba = this->hs_groups[bg];
|
||||
@ -157,44 +171,9 @@ void hist_source::add_value(unsigned int value,
|
||||
this->hs_bucket_size][bt];
|
||||
|
||||
bc += amount;
|
||||
}
|
||||
|
||||
void hist_source::analyze(void)
|
||||
{
|
||||
std::map<bucket_group_t, bucket_array_t>::iterator iter;
|
||||
|
||||
this->hs_group_keys.clear();
|
||||
this->hs_min_count = 3.40282347e+38F;
|
||||
this->hs_max_count = 0.0;
|
||||
for (iter = this->hs_groups.begin();
|
||||
iter != this->hs_groups.end();
|
||||
iter++) {
|
||||
bucket_array_t::iterator ba_iter;
|
||||
|
||||
for (ba_iter = iter->second.begin();
|
||||
ba_iter != iter->second.end();
|
||||
ba_iter++) {
|
||||
bucket_count_t total = 0.0;
|
||||
bucket_t::iterator b_iter;
|
||||
|
||||
for (b_iter = ba_iter->begin();
|
||||
b_iter != ba_iter->end();
|
||||
b_iter++) {
|
||||
if (b_iter->second != 0.0 &&
|
||||
b_iter->second < this->hs_min_count) {
|
||||
this->hs_min_count = b_iter->second;
|
||||
}
|
||||
total += b_iter->second;
|
||||
}
|
||||
if (total > this->hs_max_count) {
|
||||
this->hs_max_count = total;
|
||||
}
|
||||
}
|
||||
|
||||
this->hs_group_keys.push_back(iter->first);
|
||||
}
|
||||
|
||||
sort(this->hs_group_keys.begin(), this->hs_group_keys.end());
|
||||
|
||||
this->hs_analyzed = true;
|
||||
|
||||
bucket_stats_t &stats = this->hs_bucket_stats[bt];
|
||||
|
||||
stats.bs_max_count = max(stats.bs_max_count, bc);
|
||||
stats.bs_min_count = min(stats.bs_min_count, bc);
|
||||
}
|
||||
|
@ -32,7 +32,10 @@
|
||||
#ifndef __hist_source_hh
|
||||
#define __hist_source_hh
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include <map>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@ -118,7 +121,10 @@ public:
|
||||
return this->hs_group_size / this->hs_bucket_size;
|
||||
};
|
||||
|
||||
void clear(void) { this->hs_groups.clear(); };
|
||||
void clear(void) {
|
||||
this->hs_groups.clear();
|
||||
this->hs_bucket_stats.clear();
|
||||
};
|
||||
|
||||
size_t text_line_count()
|
||||
{
|
||||
@ -148,8 +154,13 @@ public:
|
||||
int brow = row % (this->buckets_per_group() + 1);
|
||||
int retval = 0;
|
||||
|
||||
if (!this->hs_group_keys.empty()) {
|
||||
bucket_group_t bg = this->hs_group_keys[grow];
|
||||
if (!this->hs_groups.empty()) {
|
||||
std::map<bucket_group_t, bucket_array_t>::const_iterator iter;
|
||||
|
||||
iter = this->hs_groups.begin();
|
||||
std::advance(iter, grow);
|
||||
|
||||
bucket_group_t bg = iter->first;
|
||||
|
||||
if (brow > 0) {
|
||||
brow -= 1;
|
||||
@ -165,15 +176,13 @@ public:
|
||||
{
|
||||
vis_line_t retval;
|
||||
|
||||
if (!this->hs_group_keys.empty()) {
|
||||
if (!this->hs_groups.empty()) {
|
||||
bucket_group_t bg(value / this->hs_group_size);
|
||||
|
||||
std::vector<bucket_group_t>::iterator lb;
|
||||
std::map<bucket_group_t, bucket_array_t>::iterator lb;
|
||||
|
||||
lb = lower_bound(this->hs_group_keys.begin(),
|
||||
this->hs_group_keys.end(),
|
||||
bg);
|
||||
retval = vis_line_t(distance(this->hs_group_keys.begin(), lb) *
|
||||
lb = this->hs_groups.lower_bound(bg);
|
||||
retval = vis_line_t(distance(this->hs_groups.begin(), lb) *
|
||||
(this->buckets_per_group() + 1));
|
||||
retval += vis_line_t(1 +
|
||||
(value % this->hs_group_size) /
|
||||
@ -198,8 +207,6 @@ public:
|
||||
{
|
||||
bucket_group_t bg;
|
||||
|
||||
this->hs_analyzed = false;
|
||||
|
||||
bg = bucket_group_t(value / this->hs_group_size);
|
||||
|
||||
bucket_array_t &ba = this->hs_groups[bg];
|
||||
@ -209,24 +216,50 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
void analyze(void);
|
||||
std::vector<bucket_type_t> &get_displayed_buckets() {
|
||||
return this->hs_displayed_buckets;
|
||||
};
|
||||
|
||||
bool is_bucket_graphed(bucket_type_t type) const {
|
||||
return (this->hs_displayed_buckets.empty() ||
|
||||
std::find(this->hs_displayed_buckets.begin(),
|
||||
this->hs_displayed_buckets.end(),
|
||||
type) != this->hs_displayed_buckets.end());
|
||||
};
|
||||
|
||||
protected:
|
||||
typedef std::vector<bucket_t> bucket_array_t;
|
||||
|
||||
struct bucket_stats_t {
|
||||
bucket_stats_t() :
|
||||
bs_min_count(std::numeric_limits<bucket_count_t>::max()),
|
||||
bs_max_count(0)
|
||||
{
|
||||
};
|
||||
|
||||
void merge(const bucket_stats_t &rhs) {
|
||||
this->bs_min_count = std::min(this->bs_min_count, rhs.bs_min_count);
|
||||
this->bs_max_count += rhs.bs_max_count;
|
||||
};
|
||||
|
||||
bucket_count_t width() const {
|
||||
return fabs(this->bs_max_count - this->bs_min_count);
|
||||
};
|
||||
|
||||
bucket_count_t bs_min_count;
|
||||
bucket_count_t bs_max_count;
|
||||
};
|
||||
|
||||
std::map<bucket_type_t, view_colors::role_t> hs_type2role;
|
||||
|
||||
std::map<bucket_group_t, bucket_array_t> hs_groups;
|
||||
std::vector<bucket_group_t> hs_group_keys;
|
||||
std::map<bucket_type_t, bucket_stats_t> hs_bucket_stats;
|
||||
|
||||
unsigned int hs_bucket_size; /* hours */
|
||||
unsigned int hs_group_size; /* days */
|
||||
bucket_count_t hs_min_count;
|
||||
bucket_count_t hs_max_count;
|
||||
/** Indicates that all of the data has been analyze()'d. */
|
||||
bool hs_analyzed;
|
||||
label_source *hs_label_source;
|
||||
|
||||
bucket_t *hs_token_bucket;
|
||||
std::vector<bucket_type_t> hs_displayed_buckets;
|
||||
};
|
||||
#endif
|
||||
|
57
src/lnav.cc
57
src/lnav.cc
@ -526,7 +526,6 @@ static void rebuild_hist(size_t old_count, bool force)
|
||||
~logline::LEVEL__FLAGS));
|
||||
}
|
||||
}
|
||||
hs.analyze();
|
||||
hist_view.reload_data();
|
||||
hist_view.set_top(hs.row_for_value(old_time));
|
||||
}
|
||||
@ -1650,6 +1649,60 @@ static void handle_paging_key(int ch)
|
||||
}
|
||||
break;
|
||||
|
||||
case '\t':
|
||||
if (tc == &lnav_data.ld_views[LNV_DB])
|
||||
{
|
||||
hist_source &hs = lnav_data.ld_db_source;
|
||||
db_label_source &dls = lnav_data.ld_db_rows;
|
||||
std::vector<bucket_type_t> &displayed = hs.get_displayed_buckets();
|
||||
std::vector<bool>::iterator start_iter, iter;
|
||||
|
||||
start_iter = dls.dls_headers_to_graph.begin();
|
||||
if (!displayed.empty()) {
|
||||
advance(start_iter, (int)displayed[0] + 1);
|
||||
}
|
||||
displayed.clear();
|
||||
iter = find(start_iter,
|
||||
dls.dls_headers_to_graph.end(),
|
||||
true);
|
||||
if (iter != dls.dls_headers_to_graph.end()) {
|
||||
bucket_type_t type;
|
||||
|
||||
type = bucket_type_t(distance(dls.dls_headers_to_graph.begin(), iter));
|
||||
displayed.push_back(type);
|
||||
}
|
||||
tc->reload_data();
|
||||
}
|
||||
break;
|
||||
|
||||
// XXX I'm sure there must be a better way to handle the difference between
|
||||
// iterator and reverse_iterator.
|
||||
case KEY_BTAB:
|
||||
if (tc == &lnav_data.ld_views[LNV_DB])
|
||||
{
|
||||
hist_source &hs = lnav_data.ld_db_source;
|
||||
db_label_source &dls = lnav_data.ld_db_rows;
|
||||
std::vector<bucket_type_t> &displayed = hs.get_displayed_buckets();
|
||||
std::vector<bool>::reverse_iterator start_iter, iter;
|
||||
|
||||
start_iter = dls.dls_headers_to_graph.rbegin();
|
||||
if (!displayed.empty()) {
|
||||
advance(start_iter, dls.dls_headers_to_graph.size() - (int)displayed[0]);
|
||||
}
|
||||
displayed.clear();
|
||||
iter = find(start_iter,
|
||||
dls.dls_headers_to_graph.rend(),
|
||||
true);
|
||||
if (iter != dls.dls_headers_to_graph.rend()) {
|
||||
bucket_type_t type;
|
||||
|
||||
type = bucket_type_t(distance(dls.dls_headers_to_graph.begin(), --iter.base()));
|
||||
displayed.push_back(type);
|
||||
}
|
||||
tc->reload_data();
|
||||
}
|
||||
break;
|
||||
|
||||
case 'x':
|
||||
if (tc == &lnav_data.ld_views[LNV_LOG]) {
|
||||
lnav_data.ld_log_source.toggle_user_mark(&BM_EXAMPLE,
|
||||
@ -2003,6 +2056,7 @@ static void rl_callback(void *dummy, readline_curses *rc)
|
||||
|
||||
lnav_data.ld_bottom_source.grep_error("");
|
||||
hs.clear();
|
||||
hs.get_displayed_buckets().clear();
|
||||
dls.clear();
|
||||
retcode = sqlite3_prepare_v2(lnav_data.ld_db.in(),
|
||||
rc->get_value().c_str(),
|
||||
@ -2050,7 +2104,6 @@ static void rl_callback(void *dummy, readline_curses *rc)
|
||||
}
|
||||
|
||||
if (retcode == SQLITE_DONE) {
|
||||
hs.analyze();
|
||||
lnav_data.ld_views[LNV_LOG].reload_data();
|
||||
lnav_data.ld_views[LNV_DB].reload_data();
|
||||
lnav_data.ld_views[LNV_DB].set_left(0);
|
||||
|
@ -761,7 +761,6 @@ static string com_summarize(string cmdline, vector<string> &args)
|
||||
}
|
||||
|
||||
if (retcode == SQLITE_DONE) {
|
||||
hs.analyze();
|
||||
lnav_data.ld_views[LNV_LOG].reload_data();
|
||||
lnav_data.ld_views[LNV_DB].reload_data();
|
||||
lnav_data.ld_views[LNV_DB].set_left(0);
|
||||
|
@ -79,6 +79,9 @@ public:
|
||||
};
|
||||
strong_int &operator++(void) { this->value++; return *this; };
|
||||
strong_int &operator--(void) { this->value--; return *this; };
|
||||
bool operator==(const strong_int &rhs) const {
|
||||
return this->value == rhs.value;
|
||||
};
|
||||
T *out(void) { return &this->value; };
|
||||
private:
|
||||
T value;
|
||||
|
@ -40,7 +40,6 @@ int main(int argc, char *argv[])
|
||||
hist_source hs;
|
||||
|
||||
assert(hs.text_line_count() == 0);
|
||||
hs.analyze();
|
||||
|
||||
hs.add_value(1, bucket_type_t(1));
|
||||
assert(hs.text_line_count() == 101);
|
||||
@ -49,4 +48,4 @@ int main(int argc, char *argv[])
|
||||
assert(hs.text_line_count() == 101);
|
||||
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user