Skip to content

Commit 9b7584a

Browse files
committed
Move actions and handlers into preset fine_logs
1 parent 07ce7ff commit 9b7584a

12 files changed

Lines changed: 719 additions & 14 deletions
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from fine_logs.list_observability_services_action import ListObservabilityServicesAction
2+
from fine_logs.get_service_logs_action import GetServiceLogsAction
3+
from fine_logs.clean_service_logs_action import CleanServiceLogsAction
4+
from fine_logs.clean_services_logs_action import CleanServicesLogsAction
5+
from fine_logs.list_observability_services_handler import ListObservabilityServicesHandler
6+
from fine_logs.get_service_logs_handler import GetServiceLogsHandler
7+
from fine_logs.clean_service_logs_handler import CleanServiceLogsHandler
8+
from fine_logs.clean_services_logs_discovery_handler import CleanServicesLogsDiscoveryHandler
9+
from fine_logs.clean_services_logs_iterate_handler import CleanServicesLogsIterateHandler
10+
11+
__all__ = [
12+
"ListObservabilityServicesAction",
13+
"GetServiceLogsAction",
14+
"CleanServiceLogsAction",
15+
"CleanServicesLogsAction",
16+
"ListObservabilityServicesHandler",
17+
"GetServiceLogsHandler",
18+
"CleanServiceLogsHandler",
19+
"CleanServicesLogsDiscoveryHandler",
20+
"CleanServicesLogsIterateHandler",
21+
]
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# docs: docs/reference/actions.md
2+
import dataclasses
3+
4+
from finecode_extension_api import code_action, textstyler
5+
6+
7+
@dataclasses.dataclass
8+
class CleanServiceLogsRunPayload(code_action.RunActionPayload):
9+
service_id: str
10+
"""Service whose logs should be deleted, as returned by list_observability_services."""
11+
12+
13+
class CleanServiceLogsRunContext(
14+
code_action.RunActionContext[CleanServiceLogsRunPayload]
15+
): ...
16+
17+
18+
@dataclasses.dataclass
19+
class CleanServiceLogsRunResult(code_action.RunActionResult):
20+
errors: list[str] = dataclasses.field(default_factory=list)
21+
22+
def update(self, other: code_action.RunActionResult) -> None:
23+
if not isinstance(other, CleanServiceLogsRunResult):
24+
return
25+
self.errors += other.errors
26+
27+
def to_text(self) -> str | textstyler.StyledText:
28+
if self.errors:
29+
return "\n".join(self.errors)
30+
return "Logs cleaned."
31+
32+
@property
33+
def return_code(self) -> code_action.RunReturnCode:
34+
return (
35+
code_action.RunReturnCode.ERROR
36+
if self.errors
37+
else code_action.RunReturnCode.SUCCESS
38+
)
39+
40+
41+
class CleanServiceLogsAction(
42+
code_action.Action[
43+
CleanServiceLogsRunPayload,
44+
CleanServiceLogsRunContext,
45+
CleanServiceLogsRunResult,
46+
]
47+
):
48+
"""Delete all logs for a specific observability service."""
49+
50+
DESCRIPTION = "Delete all logs for a specific observability service."
51+
PAYLOAD_TYPE = CleanServiceLogsRunPayload
52+
RUN_CONTEXT_TYPE = CleanServiceLogsRunContext
53+
RESULT_TYPE = CleanServiceLogsRunResult
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import dataclasses
2+
3+
from finecode_extension_api import code_action
4+
from fine_logs.clean_service_logs_action import (
5+
CleanServiceLogsAction,
6+
CleanServiceLogsRunContext,
7+
CleanServiceLogsRunPayload,
8+
CleanServiceLogsRunResult,
9+
)
10+
from finecode_extension_api.interfaces import (
11+
iextensionrunnerinfoprovider,
12+
ilogger,
13+
)
14+
15+
from fine_logs.observability_log_utils import resolve_log_dir
16+
17+
18+
@dataclasses.dataclass
19+
class CleanServiceLogsHandlerConfig(code_action.ActionHandlerConfig): ...
20+
21+
22+
class CleanServiceLogsHandler(
23+
code_action.ActionHandler[
24+
CleanServiceLogsAction,
25+
CleanServiceLogsHandlerConfig,
26+
]
27+
):
28+
def __init__(
29+
self,
30+
runner_info_provider: iextensionrunnerinfoprovider.IExtensionRunnerInfoProvider,
31+
logger: ilogger.ILogger,
32+
) -> None:
33+
self.runner_info_provider = runner_info_provider
34+
self.logger = logger
35+
36+
async def run(
37+
self,
38+
payload: CleanServiceLogsRunPayload,
39+
run_context: CleanServiceLogsRunContext,
40+
) -> CleanServiceLogsRunResult:
41+
log_dir = resolve_log_dir(payload.service_id, self.runner_info_provider)
42+
if not log_dir.is_dir():
43+
return CleanServiceLogsRunResult()
44+
45+
errors: list[str] = []
46+
for log_file in log_dir.glob("*.log"):
47+
try:
48+
log_file.unlink()
49+
self.logger.info(f"Deleted {log_file}")
50+
except Exception as e:
51+
errors.append(str(e))
52+
53+
return CleanServiceLogsRunResult(errors=errors)
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# docs: docs/reference/actions.md
2+
import dataclasses
3+
4+
from finecode_extension_api import code_action, textstyler
5+
6+
7+
@dataclasses.dataclass
8+
class CleanServicesLogsRunPayload(code_action.RunActionPayload):
9+
service_ids: list[str] | None = None
10+
"""Services whose logs should be deleted.
11+
None = discover and clean all available services (requires a discovery handler).
12+
Empty list = explicit no-op."""
13+
14+
15+
class CleanServicesLogsRunContext(
16+
code_action.RunActionContext[CleanServicesLogsRunPayload]
17+
):
18+
def __init__(
19+
self,
20+
run_id: int,
21+
initial_payload: CleanServicesLogsRunPayload,
22+
meta: code_action.RunActionMeta,
23+
info_provider: code_action.RunContextInfoProvider,
24+
progress_sender: code_action.ProgressSender = code_action._NOOP_PROGRESS_SENDER,
25+
) -> None:
26+
super().__init__(
27+
run_id=run_id,
28+
initial_payload=initial_payload,
29+
meta=meta,
30+
info_provider=info_provider,
31+
progress_sender=progress_sender,
32+
)
33+
self.service_ids: list[str] | None = None
34+
"""None = discovery has not run. [] = explicit no-op."""
35+
36+
async def init(self) -> None:
37+
if self.initial_payload.service_ids is not None:
38+
self.service_ids = list(self.initial_payload.service_ids)
39+
40+
41+
@dataclasses.dataclass
42+
class CleanServicesLogsRunResult(code_action.RunActionResult):
43+
services_cleaned: list[str] = dataclasses.field(default_factory=list)
44+
errors: list[str] = dataclasses.field(default_factory=list)
45+
46+
def update(self, other: code_action.RunActionResult) -> None:
47+
if not isinstance(other, CleanServicesLogsRunResult):
48+
return
49+
self.services_cleaned += other.services_cleaned
50+
self.errors += other.errors
51+
52+
def to_text(self) -> str | textstyler.StyledText:
53+
if self.errors:
54+
return "\n".join(self.errors)
55+
return f"Cleaned {len(self.services_cleaned)} service(s)."
56+
57+
@property
58+
def return_code(self) -> code_action.RunReturnCode:
59+
return (
60+
code_action.RunReturnCode.ERROR
61+
if self.errors
62+
else code_action.RunReturnCode.SUCCESS
63+
)
64+
65+
66+
class CleanServicesLogsAction(
67+
code_action.Action[
68+
CleanServicesLogsRunPayload,
69+
CleanServicesLogsRunContext,
70+
CleanServicesLogsRunResult,
71+
]
72+
):
73+
"""Delete logs for multiple observability services.
74+
75+
When service_ids is None, a discovery handler must run first to populate
76+
run_context.service_ids from list_observability_services. No-op when
77+
service_ids is an empty list.
78+
"""
79+
80+
DESCRIPTION = "Delete logs for multiple observability services."
81+
PAYLOAD_TYPE = CleanServicesLogsRunPayload
82+
RUN_CONTEXT_TYPE = CleanServicesLogsRunContext
83+
RESULT_TYPE = CleanServicesLogsRunResult
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import dataclasses
2+
3+
from finecode_extension_api import code_action
4+
from fine_logs.clean_services_logs_action import (
5+
CleanServicesLogsAction,
6+
CleanServicesLogsRunContext,
7+
CleanServicesLogsRunPayload,
8+
CleanServicesLogsRunResult,
9+
)
10+
from fine_logs.list_observability_services_action import (
11+
ListObservabilityServicesAction,
12+
ListObservabilityServicesRunPayload,
13+
)
14+
from finecode_extension_api.interfaces import ilogger, iprojectactionrunner
15+
16+
17+
@dataclasses.dataclass
18+
class CleanServicesLogsDiscoveryHandlerConfig(code_action.ActionHandlerConfig): ...
19+
20+
21+
class CleanServicesLogsDiscoveryHandler(
22+
code_action.ActionHandler[
23+
CleanServicesLogsAction,
24+
CleanServicesLogsDiscoveryHandlerConfig,
25+
]
26+
):
27+
"""Populate run_context.service_ids by calling list_observability_services.
28+
29+
Must be registered before performing cleaning. No-op when
30+
run_context.service_ids is already set (i.e. explicit caller or repeated run).
31+
"""
32+
33+
def __init__(
34+
self,
35+
project_action_runner: iprojectactionrunner.IProjectActionRunner,
36+
logger: ilogger.ILogger,
37+
) -> None:
38+
self.project_action_runner = project_action_runner
39+
self.logger = logger
40+
41+
async def run(
42+
self,
43+
payload: CleanServicesLogsRunPayload,
44+
run_context: CleanServicesLogsRunContext,
45+
) -> CleanServicesLogsRunResult:
46+
if run_context.service_ids is not None:
47+
return CleanServicesLogsRunResult()
48+
49+
result = await self.project_action_runner.run_action(
50+
action_type=iprojectactionrunner.ActionRef.from_type(ListObservabilityServicesAction),
51+
payload=ListObservabilityServicesRunPayload(),
52+
meta=run_context.meta,
53+
)
54+
run_context.service_ids = [s.service_id for s in result.services]
55+
self.logger.debug(f"Discovered services to clean: {run_context.service_ids}")
56+
return CleanServicesLogsRunResult()
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import dataclasses
2+
3+
from finecode_extension_api import code_action
4+
from fine_logs.clean_service_logs_action import (
5+
CleanServiceLogsAction,
6+
CleanServiceLogsRunPayload,
7+
)
8+
from fine_logs.clean_services_logs_action import (
9+
CleanServicesLogsAction,
10+
CleanServicesLogsRunContext,
11+
CleanServicesLogsRunPayload,
12+
CleanServicesLogsRunResult,
13+
)
14+
from finecode_extension_api.interfaces import ilogger, iprojectactionrunner
15+
16+
17+
@dataclasses.dataclass
18+
class CleanServicesLogsIterateHandlerConfig(code_action.ActionHandlerConfig): ...
19+
20+
21+
class CleanServicesLogsIterateHandler(
22+
code_action.ActionHandler[
23+
CleanServicesLogsAction,
24+
CleanServicesLogsIterateHandlerConfig,
25+
]
26+
):
27+
"""Call clean_service_logs for each service_id in run_context.service_ids.
28+
29+
Must be registered after discovery handler.
30+
"""
31+
32+
def __init__(
33+
self,
34+
project_action_runner: iprojectactionrunner.IProjectActionRunner,
35+
logger: ilogger.ILogger,
36+
) -> None:
37+
self.project_action_runner = project_action_runner
38+
self.logger = logger
39+
40+
async def run(
41+
self,
42+
payload: CleanServicesLogsRunPayload,
43+
run_context: CleanServicesLogsRunContext,
44+
) -> CleanServicesLogsRunResult:
45+
if run_context.service_ids is None:
46+
raise code_action.ActionFailedException(
47+
"CleanServicesLogsDiscoveryHandler must be registered before"
48+
" CleanServicesLogsIterateHandler"
49+
)
50+
if not run_context.service_ids:
51+
return CleanServicesLogsRunResult()
52+
53+
services_cleaned: list[str] = []
54+
errors: list[str] = []
55+
56+
for service_id in run_context.service_ids:
57+
result = await self.project_action_runner.run_action(
58+
action_type=iprojectactionrunner.ActionRef.from_type(CleanServiceLogsAction),
59+
payload=CleanServiceLogsRunPayload(service_id=service_id),
60+
meta=run_context.meta,
61+
)
62+
if result.errors:
63+
errors.extend(result.errors)
64+
else:
65+
services_cleaned.append(service_id)
66+
67+
return CleanServicesLogsRunResult(
68+
services_cleaned=services_cleaned,
69+
errors=errors,
70+
)

0 commit comments

Comments
 (0)