Skip to content

Commit 763f86a

Browse files
committed
Merge remote-tracking branch 'upstream/main' into feat/workspaces_subcommands
2 parents eed3d5e + cb02721 commit 763f86a

File tree

3 files changed

+69
-10
lines changed

3 files changed

+69
-10
lines changed

silverback/_cli.py

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import os
33
import shlex
44
import subprocess
5-
from datetime import timedelta
5+
from datetime import datetime, timedelta, timezone
66
from pathlib import Path
77

88
import click
@@ -31,7 +31,7 @@
3131
token_amount_callback,
3232
)
3333
from silverback.cluster.client import ClusterClient, PlatformClient
34-
from silverback.cluster.types import ClusterTier, ResourceStatus
34+
from silverback.cluster.types import ClusterTier, LogLevel, ResourceStatus
3535
from silverback.runner import PollingRunner, WebsocketRunner
3636
from silverback.worker import run_worker
3737

@@ -120,7 +120,8 @@ def run(cli_ctx, account, runner_class, recorder_class, max_exceptions, bot):
120120
runner_class = PollingRunner
121121
else:
122122
raise click.BadOptionUsage(
123-
option_name="network", message="Network choice cannot support running bot"
123+
option_name="network",
124+
message="Network choice cannot support running bot",
124125
)
125126

126127
runner = runner_class(
@@ -177,7 +178,11 @@ def build(generate, path):
177178
f"-t {file.name.split('.')[1]}:latest ."
178179
)
179180
result = subprocess.run(
180-
command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=True
181+
command,
182+
stdout=subprocess.PIPE,
183+
stderr=subprocess.PIPE,
184+
text=True,
185+
check=True,
181186
)
182187
click.echo(result.stdout)
183188
except subprocess.CalledProcessError as e:
@@ -1202,14 +1207,37 @@ def stop_bot(cluster: ClusterClient, name: str):
12021207

12031208
@bots.command(name="logs", section="Bot Operation Commands")
12041209
@click.argument("name", metavar="BOT")
1210+
@click.option(
1211+
"-l",
1212+
"--log-level",
1213+
"log_level",
1214+
help="Minimum log level to display.",
1215+
default="INFO",
1216+
)
1217+
@click.option(
1218+
"-s",
1219+
"--since",
1220+
"since",
1221+
help="Return logs since N ago.",
1222+
callback=timedelta_callback,
1223+
)
12051224
@cluster_client
1206-
def show_bot_logs(cluster: ClusterClient, name: str):
1225+
def show_bot_logs(cluster: ClusterClient, name: str, log_level: str, since: timedelta | None):
12071226
"""Show runtime logs for BOT in CLUSTER"""
12081227

1228+
start_time = None
1229+
if since:
1230+
start_time = datetime.now(tz=timezone.utc) - since
1231+
12091232
if not (bot := cluster.bots.get(name)):
12101233
raise click.UsageError(f"Unknown bot '{name}'.")
12111234

1212-
for log in bot.logs:
1235+
try:
1236+
level = LogLevel.__dict__[log_level.upper()]
1237+
except KeyError:
1238+
level = LogLevel.INFO
1239+
1240+
for log in bot.filter_logs(log_level=level, start_time=start_time):
12131241
click.echo(log)
12141242

12151243

silverback/cluster/client.py

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
from datetime import datetime
12
from functools import cache
23
from typing import ClassVar, Literal
34

45
import httpx
56
from ape import Contract
67
from ape.contracts import ContractInstance
8+
from ape.logging import LogLevel
79
from apepay import Stream, StreamManager
810
from pydantic import computed_field
911

@@ -12,6 +14,7 @@
1214
from .types import (
1315
BotHealth,
1416
BotInfo,
17+
BotLogEntry,
1518
ClusterHealth,
1619
ClusterInfo,
1720
ClusterState,
@@ -189,11 +192,27 @@ def errors(self) -> list[str]:
189192
handle_error_with_response(response)
190193
return response.json()
191194

192-
@property
193-
def logs(self) -> list[str]:
194-
response = self.cluster.get(f"/bots/{self.id}/logs")
195+
def filter_logs(
196+
self,
197+
log_level: LogLevel = LogLevel.INFO,
198+
start_time: datetime | None = None,
199+
end_time: datetime | None = None,
200+
) -> list[BotLogEntry]:
201+
query = {"log_level": log_level.name}
202+
203+
if start_time:
204+
query["start_time"] = start_time.isoformat()
205+
206+
if end_time:
207+
query["end_time"] = end_time.isoformat()
208+
209+
response = self.cluster.get(f"/bots/{self.id}/logs", params=query, timeout=120)
195210
handle_error_with_response(response)
196-
return response.json()
211+
return [BotLogEntry.model_validate(log) for log in response.json()]
212+
213+
@property
214+
def logs(self) -> list[BotLogEntry]:
215+
return self.filter_logs()
197216

198217
def remove(self):
199218
response = self.cluster.delete(f"/bots/{self.id}")

silverback/cluster/types.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1+
from __future__ import annotations
2+
13
import enum
24
import math
35
import uuid
46
from datetime import datetime
57
from typing import Annotated, Any
68

9+
from ape.logging import LogLevel
710
from ape.types import AddressType, HexBytes
811
from cryptography.exceptions import InvalidSignature
912
from cryptography.hazmat.primitives.hmac import HMAC, hashes
@@ -374,3 +377,12 @@ class BotInfo(BaseModel):
374377
registry_credentials_id: str | None
375378

376379
environment: list[EnvironmentVariable] = []
380+
381+
382+
class BotLogEntry(BaseModel):
383+
message: str
384+
timestamp: datetime | None
385+
level: LogLevel
386+
387+
def __str__(self) -> str:
388+
return f"{self.timestamp}: {self.message}"

0 commit comments

Comments
 (0)