File size: 4,746 Bytes
e0d6c83
 
 
 
 
8861b68
 
e0d6c83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8861b68
 
 
e0d6c83
 
 
8861b68
 
 
e0d6c83
 
 
8861b68
 
 
e0d6c83
 
 
8861b68
 
 
e0d6c83
 
9bdb607
e0d6c83
 
 
 
 
 
 
9bdb607
e0d6c83
 
9bdb607
ccb4b5f
 
 
8861b68
ccb4b5f
 
 
 
 
8861b68
ccb4b5f
 
e0d6c83
 
 
 
 
 
 
 
 
 
 
8861b68
 
 
e0d6c83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8861b68
 
 
e0d6c83
8861b68
e0d6c83
 
 
 
 
 
72cbf19
 
 
 
 
 
 
8861b68
 
 
72cbf19
9cc9603
9bdb607
e0d6c83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
import logging
import random
import time

import gradio as gr

from gradio_logsview import LogsView, LogsViewRunner


def random_values(failing: bool = False):
    for i in range(10):
        logging.log(
            random.choice(
                [  # Random levels
                    logging.INFO,
                    logging.DEBUG,
                    logging.WARNING,
                    logging.ERROR,
                    logging.CRITICAL,
                ]
            ),
            f"Value {i+1}",  # Random values
        )
        time.sleep(random.uniform(0, 1))
        if failing and i == 5:
            raise ValueError("Failing!!")


def fn_process_success():
    runner = LogsViewRunner()
    yield from runner.run_process(["python", "-u", "demo/script.py"])
    yield runner.log(f"Runner: {runner}")


def fn_process_failing():
    runner = LogsViewRunner()
    yield from runner.run_process(["python", "-u", "demo/script.py", "--failing"])
    yield runner.log(f"Runner: {runner}")


def fn_thread_success():
    runner = LogsViewRunner()
    yield from runner.run_thread(random_values, log_level=logging.INFO, failing=False)
    yield runner.log(f"Runner: {runner}")


def fn_thread_failing():
    runner = LogsViewRunner()
    yield from runner.run_thread(random_values, log_level=logging.INFO, failing=True)
    yield runner.log(f"Runner: {runner}")


markdown_top = """
# LogsView Demo

This demo shows how to use the `LogsView` component to display logs from a process or a thread in real-time.

Click on any button to launch a process or a thread and see the logs displayed in real-time.
In the thread example, logs are generated randomly with different log levels.
In the process example, logs are generated by a Python script but any command can be executed.
"""


markdown_bottom = """
## Installation

```
pip install https://huggingface.co/spaces/Wauplin/gradio_logsview/resolve/main/gradio_logsview-0.0.3-py3-none-any.whl
```

or add this line to your `requirements.txt`:

```
gradio_logsview@https://huggingface.co/spaces/Wauplin/gradio_logsview/resolve/main/gradio_logsview-0.0.3-py3-none-any.whl
```

## How to run in a thread?

With `LogsView.run_thread`, you can run a function in a separate thread and capture logs in real-time.
You can configure which logs to capture (log level and logger name).

```py
from gradio_logsview import LogsView

def fn_thread():
    # Run `my_function` in a separate thread
    # All logs above `INFO` level will be captured and displayed in real-time.
    runner = LogsViewRunner() # Initialize the runner
    yield from runner.run_thread(my_function, log_level=logging.INFO, arg1="value1")
    yield runner.log(f"Runner: {runner}") # Log any message

with gr.Blocks() as demo:
    logs = LogsView()
    btn = gr.Button("Run thread")
    btn.click(fn_thread, outputs=logs)
```

## How to run in a process?

With `LogsView.run_process`, you can run a command in a separate process and capture logs from the process in real-time.
    
```py
from gradio_logsview import LogsView

def fn_process():
    # Run a process and capture all logs from the process
    runner = LogsViewRunner() # Initialize the runner
    yield from runner.run_process(
        cmd=["mergekit-yaml", "config.yaml", "merge", "--copy-", "--cuda", "--low-cpu-memory"]
    )
    yield runner.log(f"Runner: {runner}") # Log any message

with gr.Blocks() as demo:
    logs = LogsView()
    btn = gr.Button("Run process")
    btn.click(fn_process, outputs=logs)
```

## TODO

- [ ] display logs with colors (front-end)
- [ ] format logs client-side (front-end)
- [ ] scrollable logs if more than N lines (front-end)
- [ ] format each log only once (front-end)
- [x] stop process if `run_process` gets cancelled (back-end)
- [x] correctly pass error stacktrace in `run_thread` (back-end)
- [ ] correctly catch print statements in `run_thread` (back-end)
- [ ] disable interactivity + remove all code editing logic (both?)
- [ ] how to handle progress bars? (i.e when logs are overwritten in terminal)
"""

with gr.Blocks() as demo:
    gr.Markdown(markdown_top)

    with gr.Row():
        btn_thread_success = gr.Button("Run thread (success)")
        btn_thread_failing = gr.Button("Run thread (failing)")
    with gr.Row():
        btn_process_success = gr.Button("Run process (success)")
        btn_process_failing = gr.Button("Run process (failing)")
    logs = LogsView()

    gr.Markdown(markdown_bottom)

    btn_thread_failing.click(fn_thread_failing, outputs=logs)
    btn_thread_success.click(fn_thread_success, outputs=logs)
    btn_process_failing.click(fn_process_failing, outputs=logs)
    btn_process_success.click(fn_process_success, outputs=logs)


if __name__ == "__main__":
    demo.launch()