Skip to content

Commit

Permalink
Merge pull request #41 from satyaog/feature/logs
Browse files Browse the repository at this point in the history
Add systemd journal viewer
  • Loading branch information
satyaog authored Oct 16, 2023
2 parents 7506412 + c0f58ef commit b73cf3c
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 0 deletions.
17 changes: 17 additions & 0 deletions paperoni/webapp/admin/logs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from starbear import bear

from ..common import LogsViewer, config, mila_template


@bear
@mila_template(help="/help#logs")
async def app(page, box):
"""Monitor systemd logs."""
try:
services = config().services
except AttributeError:
services = []
await LogsViewer(services).run(box)


ROUTES = app
13 changes: 13 additions & 0 deletions paperoni/webapp/app-style.css
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,13 @@ input[type="text"], input[type="date"] {
background: lightsalmon;
}

.validation-button input[type="radio"][class="log-button"] {
height: 3.5em;
}
.validation-button input[type="radio"][class="log-button"]:checked {
background: lightgreen;
}

/* Papers */

.paper {
Expand Down Expand Up @@ -446,3 +453,9 @@ form.update-file textarea {
width: 100%;
height: 500px;
}

div.log-stream textarea {
display: block;
width: 100%;
height: 500px;
}
74 changes: 74 additions & 0 deletions paperoni/webapp/common.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import asyncio
import html
import os
import subprocess
import traceback
from functools import wraps
from pathlib import Path
Expand Down Expand Up @@ -74,6 +76,78 @@ async def run(self, page):
page[actionarea].set(H.button("Update", name="update"))


class LogsViewer:
def __init__(self, services):
self.services = services

async def run(self, page):
q = Queue()
debounced = ClientWrap(q, debounce=0.3, form=True)
active_service = next(iter(self.services), None)

page.print(
H.form["logs-radio"](
*(
H.label["validation-button"](
H.input(
type="radio",
name=f"v-logs-radio",
value=s,
checked=active_service == s,
onchange=debounced,
**{"class":"log-button"},
),
s,
)
for s in self.services
)
)
)

page.print(
H.div["log-stream"](
log_stream := H.textarea(
"".join(
self.__class__.journal(active_service, 10000)
if active_service else []
),
name="log-stream",
readonly=True,
).autoid(),
),
)
page[log_stream].do("this.scrollTop = this.scrollTopMax")

async for event in q:
for k, v in event.items():
if k.startswith("v-"):
page[log_stream].clear()
blocks = []
for i, l in enumerate(self.__class__.journal(v, 10000)):
blocks.append(html.escape(l))
if i % 2000 == 0:
page[log_stream].print_html("".join(blocks))
page[log_stream].do("this.scrollTop = this.scrollTopMax")
blocks = ["\n"]
if blocks[1:]:
page[log_stream].print_html("".join(blocks))
page[log_stream].do("this.scrollTop = this.scrollTopMax")


@staticmethod
def journal(service, tail=-1):
with subprocess.Popen([
"journalctl",
*(("-n", str(tail)) if tail > 0 else tuple()),
"-qu", service,
# TODO: make viewer follow the journal logs
# "--follow",
# "--no-tail",
], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) as process:
for l in process.stdout:
yield l.decode("utf-8")


class GUI:
def __init__(self, page, db, queue, params, defaults):
self.page = page
Expand Down

0 comments on commit b73cf3c

Please sign in to comment.