Skip to content

Commit bc632a8

Browse files
committed
add logging in ui WIP
1 parent fac8dd6 commit bc632a8

File tree

8 files changed

+145
-3
lines changed

8 files changed

+145
-3
lines changed

controller/thymis_controller/crud/logs.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import uuid
22
from datetime import datetime
33

4-
from sqlalchemy import nullslast
4+
from sqlalchemy import nullslast, or_
55
from sqlalchemy.dialects.sqlite import insert as sqlite_insert
66
from sqlalchemy.orm import Session
77
from thymis_controller import db_models
@@ -62,3 +62,29 @@ def create(
6262
)
6363
session.execute(stmt)
6464
session.commit()
65+
66+
67+
def get_logs(
68+
session: Session,
69+
deployment_info: db_models.DeploymentInfo,
70+
from_datetime: datetime = None,
71+
to_datetime: datetime = None,
72+
limit: int = 100,
73+
offset: int = 0,
74+
):
75+
# where ID equals or ssh public key equals
76+
77+
stmt = session.query(db_models.LogEntry)
78+
stmt = stmt.filter(
79+
or_(
80+
db_models.LogEntry.deployment_info_id == deployment_info.id,
81+
db_models.LogEntry.ssh_public_key == deployment_info.ssh_public_key,
82+
)
83+
)
84+
if from_datetime is not None:
85+
stmt = stmt.filter(db_models.LogEntry.timestamp >= from_datetime)
86+
if to_datetime is not None:
87+
stmt = stmt.filter(db_models.LogEntry.timestamp <= to_datetime)
88+
stmt = stmt.order_by(nullslast(db_models.LogEntry.timestamp.desc()))
89+
stmt = stmt.limit(limit).offset(offset)
90+
return stmt.all()

controller/thymis_controller/routers/api.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from thymis_controller.routers import (
66
api_action,
77
api_deployment_info,
8+
api_logging,
89
api_secrets,
910
api_state,
1011
api_statistics,
@@ -25,3 +26,5 @@
2526
router.include_router(api_deployment_info.router)
2627
router.include_router(api_ui_sockets.router)
2728
router.include_router(api_statistics.router)
29+
router.include_router(api_statistics.router, prefix="/statistics", tags=["statistics"])
30+
router.include_router(api_logging.router)
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import datetime
2+
import uuid
3+
4+
from fastapi import APIRouter, Response, WebSocket
5+
from thymis_controller import db_models
6+
from thymis_controller.crud.logs import get_logs
7+
from thymis_controller.dependencies import (
8+
DBSessionAD,
9+
EngineAD,
10+
NetworkRelayAD,
11+
ProjectAD,
12+
TaskControllerAD,
13+
)
14+
from thymis_controller.routers.frontend import is_running_in_playwright
15+
from thymis_controller.task.subscribe_ui import TaskWebsocketSubscriber
16+
17+
router = APIRouter()
18+
19+
20+
@router.get("/logs/{deployment_info_id}")
21+
def get_tasks(
22+
session: DBSessionAD,
23+
deployment_info_id: uuid.UUID,
24+
from_datetime: str = None,
25+
to_datetime: str = None,
26+
limit: int = 100,
27+
offset: int = 0,
28+
):
29+
deployment_info = (
30+
session.query(db_models.DeploymentInfo)
31+
.filter(db_models.DeploymentInfo.id == deployment_info_id)
32+
.first()
33+
)
34+
if deployment_info is None:
35+
return Response(status_code=404)
36+
37+
logs = get_logs(
38+
session,
39+
deployment_info=deployment_info,
40+
from_datetime=datetime.datetime.fromisoformat(from_datetime),
41+
to_datetime=datetime.datetime.fromisoformat(to_datetime),
42+
limit=limit,
43+
offset=offset,
44+
)
45+
return logs

frontend/src/lib/logs.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
export const getLogs = async (
2+
fetch: typeof window.fetch = window.fetch,
3+
deploymentId: string,
4+
to: Date,
5+
from: Date,
6+
limit: number,
7+
offset: number
8+
) => {
9+
const response = await fetch(
10+
`/api/logs/${deploymentId}?from_datetime=${from.toISOString()}&to_datetime=${to.toISOString()}&limit=${limit}&offset=${offset}`,
11+
{
12+
method: 'GET',
13+
headers: {
14+
'Content-Type': 'application/json'
15+
}
16+
}
17+
);
18+
if (!response.ok) {
19+
throw new Error(`Failed to fetch logs: ${response.statusText}`);
20+
}
21+
const logs = await response.json();
22+
return logs;
23+
};

frontend/src/locales/en.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@
8686
"last-seen": "Last seen",
8787
"no-hardware-ids": "No hardware ids detected",
8888
"unknown-device-type": "Unknown device type",
89-
"never-seen": "Never"
89+
"never-seen": "Never",
90+
"actions": "Actions"
9091
},
9192
"hardware-keys": {
9293
"pi-serial-number": "Pi Serial Number"
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<script lang="ts">
2+
import type { PageData } from './$types';
3+
4+
let { data }: { data: PageData } = $props();
5+
</script>
6+
7+
{JSON.stringify(data.logs)}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import type { PageLoad } from './$types';
2+
import { getLogs } from '$lib/logs';
3+
import { error } from '@sveltejs/kit';
4+
5+
export const load = (async ({ params, fetch, url }) => {
6+
try {
7+
const fromDateTime = url.searchParams.get('fromDateTime');
8+
const toDateTime = url.searchParams.get('toDateTime');
9+
// default to is now
10+
// default from is to-1h
11+
const to = toDateTime ? new Date(toDateTime) : new Date();
12+
const from = fromDateTime ? new Date(fromDateTime) : new Date(to.getTime() - 60 * 60 * 1000);
13+
// default to 1000
14+
const limit = url.searchParams.get('limit')
15+
? parseInt(url.searchParams.get('limit') ?? '1000')
16+
: 1000;
17+
// default to 0
18+
const offset = url.searchParams.get('offset')
19+
? parseInt(url.searchParams.get('offset') ?? '0')
20+
: 0;
21+
const logs = await getLogs(fetch, params.id, to, from, limit, offset);
22+
return {
23+
logs
24+
};
25+
} catch (e) {
26+
error(404, 'Task not found');
27+
}
28+
}) satisfies PageLoad;

frontend/src/routes/(authenticated)/devices/+page.svelte

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script lang="ts">
22
import { t } from 'svelte-i18n';
33
import type { PageData } from './$types';
4-
import { Table, TableBodyCell, TableHead, TableHeadCell, Toggle } from 'flowbite-svelte';
4+
import { Button, Table, TableBodyCell, TableHead, TableHeadCell, Toggle } from 'flowbite-svelte';
55
import { getDeviceTypesMap, getDeviceType } from '$lib/config/configUtils';
66
import PageHead from '$lib/components/layout/PageHead.svelte';
77
import RenderTimeAgo from '$lib/components/RenderTimeAgo.svelte';
@@ -53,6 +53,7 @@
5353
<TableHeadCell padding="p-2">{$t('hardware-devices.table.device-type')}</TableHeadCell>
5454
<TableHeadCell padding="p-2">{$t('hardware-devices.table.hardware-ids')}</TableHeadCell>
5555
<TableHeadCell padding="p-2">{$t('hardware-devices.table.connected')}</TableHeadCell>
56+
<TableHeadCell padding="p-2">{$t('hardware-devices.table.actions')}</TableHeadCell>
5657
</TableHead>
5758
<tbody>
5859
{#each data.deploymentInfos as deploymentInfo (deploymentInfo.id)}
@@ -118,6 +119,14 @@
118119
{/if}
119120
{/if}
120121
</TableBodyCell>
122+
<TableBodyCell tdClass="p-2">
123+
<!-- View Logs button -->
124+
<a href={`/deployment_info/${deploymentInfo.id}/logs`}>
125+
<Button>
126+
{$t('hardware-devices.table.view-logs')}
127+
</Button>
128+
</a>
129+
</TableBodyCell>
121130
</tr>
122131
{/if}
123132
{/each}

0 commit comments

Comments
 (0)