Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple log widgets displaying different log targets. #30

Closed
pms1969 opened this issue Mar 1, 2023 · 13 comments
Closed

Multiple log widgets displaying different log targets. #30

pms1969 opened this issue Mar 1, 2023 · 13 comments

Comments

@pms1969
Copy link

pms1969 commented Mar 1, 2023

Firstly, Thank you for producing this crate. I have thus far found it excellent.

What I'd like to do is have 2 separate log widgets, both displaying different log data dependent on the log target. I'm pretty sure I know the answer is that it's just not possible, but I was wondering if you could shed some insight as to what I might need to do to achieve that goal.

I basically want log::info!(target: "x", "This is log X"); and log::info!(target: "y", "This is log Y"); to show up in different log panes (widgets).

 ---Logs 1-----------------------Logs 2-----------------------
| This is log X              | This is log Y                  |
|                            |                                |
 -------------------------------------------------------------

☝️ A bit like that; stylistic hell I know 😃

@gin66
Copy link
Owner

gin66 commented Mar 1, 2023

If I understand you correctly, you like to have two pure lists without the "Smart". If you look in the demo.rs, there is this code:

    let tui_w: TuiLoggerWidget = TuiLoggerWidget::default()
        .block(
            Block::default()
                .title("Independent Tui Logger View")
                .border_style(Style::default().fg(Color::White).bg(Color::Black))
                .borders(Borders::ALL),
        )
        .output_separator('|')
        .output_timestamp(Some("%F %H:%M:%S%.3f".to_string()))
        .output_level(Some(TuiLoggerLevelOutput::Long))
        .output_target(false)
        .output_file(false)
        .output_line(false)
        .style(Style::default().fg(Color::White).bg(Color::Black));
    t.render_widget(tui_w, chunks[2]);

The TuiLoggerWidget can receive a TuiWidgetState as parameter to .state() e.g. after .style(). And that TuiWidgetState allows to configure the TuiLoggerWidget at runtime using set_level_for_target() and set_default_display_level().

So you would need to instantiate and render two TuiLoggerWidgets with different states. That should be it.

Hope this helps

@pms1969
Copy link
Author

pms1969 commented Mar 2, 2023

Thanks @gin66 , I did try that just for one, but had a terrible time trying to get it to work, to the point I gave up thinking it was meant to be an internal representation. I can't remember what my trouble was, but I'll try again and report back.

@pms1969
Copy link
Author

pms1969 commented Mar 2, 2023

OK, So this is what I have:

    {
        let lws = TuiWidgetState::new().set_level_for_target("task_log", log::LevelFilter::Debug);
        // let mut tui_lw: TuiLoggerWidget = TuiLoggerWidget::default();
        // let tui_lw = tui_lw.state(&lws);
        let widget = TuiLoggerWidget::default()
            .block(
                Block::default()
                    .title(" Logs ")
                    .border_style(Style::default().fg(Color::White).bg(Color::Black))
                    .borders(Borders::ALL),
            )
            .style_error(Style::default().fg(Color::Red))
            .style_debug(Style::default().fg(Color::Green))
            .style_warn(Style::default().fg(Color::Yellow))
            .style_trace(Style::default().fg(Color::Magenta))
            .style_info(Style::default().fg(Color::Cyan))
            .output_separator('|')
            // .output_timestamp(Some("%F %H:%M:%S%.3f".to_string()))
            .output_level(Some(TuiLoggerLevelOutput::Abbreviated))
            .output_target(false)
            .output_file(false)
            .output_line(false)
            .style(Style::default().fg(Color::White).bg(Color::Black))
            .state(&lws);

        f.render_widget(widget, logs_events[0]);
    }

and this is what I get in response.

error[E0277]: the trait bound `&mut TuiLoggerWidget<'_>: Widget` is not satisfied
   --> cli/src/ui/terminal.rs:129:25
    |
129 |         f.render_widget(widget, logs_events[0]);
    |           ------------- ^^^^^^ the trait `Widget` is not implemented for `&mut TuiLoggerWidget<'_>`
    |           |
    |           required by a bound introduced by this call
    |
    = help: the trait `Widget` is implemented for `TuiLoggerWidget<'b>`
note: required by a bound in `Frame::<'a, B>::render_widget`
   --> .../.cargo/registry/src/github.com-1ecc6299db9ec823/tui-0.19.0/src/terminal.rs:99:12
    |
99  |         W: Widget,
    |            ^^^^^^ required by this bound in `Frame::<'a, B>::render_widget`

I've tried getting to the widget like so

f.render_widget(*widget, logs_events[0]);

but that gives me

129 |         f.render_widget(*widget, logs_events[0]);
    |                         ^^^^^^^ move occurs because `*widget` has type `TuiLoggerWidget<'_>`, which does not implement the `Copy` trait

and I've tried a few other things. none of which have rewarded me 😢

It's probably a very simple thing I'm missing, but it just escapes me at present.

Appreciate the help.

gin66 added a commit that referenced this issue Mar 2, 2023
@gin66
Copy link
Owner

gin66 commented Mar 2, 2023

Thanks for trying and based on your feedback, I have checked the code. Unfortunately the function state() has returned a different value than Self. The fixed version is v0.8.2 and uploaded to crates.io, too

@pms1969
Copy link
Author

pms1969 commented Mar 2, 2023

You are a super ⭐

Thank you very much.

@pms1969 pms1969 closed this as completed Mar 2, 2023
@pms1969 pms1969 reopened this Mar 2, 2023
@pms1969
Copy link
Author

pms1969 commented Mar 2, 2023

Sorry @gin66 , I'm back. Although I can now use the state method, I think it's being ignored.
My widgets have the following states:

        let lws = TuiWidgetState::new()
            .set_level_for_target("task_log", log::LevelFilter::Debug)
            .set_default_display_level(log::LevelFilter::Off);

and

        let ews = TuiWidgetState::new()
            .set_level_for_target("event_log", log::LevelFilter::Debug)
            .set_default_display_level(log::LevelFilter::Off);

which is added appropriately to each widget.
However, regardless of what I do, the output is identical in both of them, and is governed more by init_logger(LevelFilter::Info)?; than anything I do to the WidgetState.

Any idea's what I might be doing wrong?

@gin66
Copy link
Owner

gin66 commented Mar 2, 2023

Perhaps a typo (lws/ews) in the .state() calls ?
I have extended the demo to demonstrate how/that it works.

@pms1969
Copy link
Author

pms1969 commented Mar 2, 2023

This patch will do it.

diff --git a/src/lib.rs b/src/lib.rs
index c99be58..23e558b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1054,6 +1054,8 @@ impl<'b> Widget for TuiLoggerWidget<'b> {
                     if *level < evt.level {
                         continue;
                     }
+                } else {
+                    continue;
                 }
                 if state.focus_selected {

@gin66
Copy link
Owner

gin66 commented Mar 2, 2023

hmmm. I think, that patch will ensure, that the widget does not display anything.

@pms1969
Copy link
Author

pms1969 commented Mar 2, 2023

I've observed it to work. The statement in question is

                if let Some(level) = state.config.get(&evt.target) {
                    if *level < evt.level {
                        continue;
                    }
                }

and becomes

                if let Some(level) = state.config.get(&evt.target) {
                    if *level < evt.level {
                        continue;
                    }
                } else {
                    continue;
                }

that first if is checking if there is a level set for the target, and if there is, and the level is set lower than the events level, it ignores it. It doesn't account for it not being set at all.

Now that I've typed that, and re-read your comment, I think it will behave badly for the case where the state is not explicitly set, and there is no target?

@pms1969
Copy link
Author

pms1969 commented Mar 2, 2023

The answer to that is a catagorical yes. It ignores all the logs.... Thinking...

@pms1969
Copy link
Author

pms1969 commented Mar 2, 2023

This one works better.

diff --git a/src/lib.rs b/src/lib.rs
index c99be58..854a78f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1054,6 +1054,12 @@ impl<'b> Widget for TuiLoggerWidget<'b> {
                     if *level < evt.level {
                         continue;
                     }
+                } else {
+                    if let Some(level) = state.config.default_display_level {
+                        if level < evt.level {
+                            continue;
+                        }
+                    }
                 }
                 if state.focus_selected {
                     if let Some(target) = state.opt_selected_target.as_ref() {

@gin66
Copy link
Owner

gin66 commented Mar 2, 2023

Thanks for the patch. I have applied it to v0.8.3

@pms1969 pms1969 closed this as completed Mar 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants