plugin logging

This commit is contained in:
Bradley Nelson
2026-06-16 23:00:23 -06:00
parent 803150635c
commit 4427b1613b
17 changed files with 1928 additions and 12 deletions
+96 -2
View File
@@ -1,9 +1,9 @@
//! DTOs for the admin plugin-management API.
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
use utoipa::{IntoParams, ToSchema};
use crate::application::ports::plugin_ports::PluginInfo;
use crate::application::ports::plugin_ports::{LogEntry, LogPage, PluginInfo, RetentionSettings};
/// A single installed plugin as returned by `GET /api/admin/plugins`.
#[derive(Debug, Clone, Serialize, ToSchema)]
@@ -35,3 +35,97 @@ impl From<PluginInfo> for PluginInfoDto {
pub struct SetEnabledDto {
pub enabled: bool,
}
/// A single structured log entry as returned by the admin log viewer / stream.
#[derive(Debug, Clone, Serialize, ToSchema)]
pub struct PluginLogEntryDto {
/// RFC 3339 timestamp.
pub ts: String,
pub invocation_id: String,
/// `"plugin"` (plugin-emitted line) or `"outcome"` (host invocation result).
pub kind: String,
/// `debug` | `info` | `warn` | `error`.
pub level: String,
/// Stable outcome key for `kind = "outcome"`.
#[serde(skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
pub msg: String,
}
impl From<LogEntry> for PluginLogEntryDto {
fn from(e: LogEntry) -> Self {
Self {
ts: e.ts,
invocation_id: e.invocation_id,
kind: e.kind,
level: e.level,
reason: e.reason,
msg: e.msg,
}
}
}
/// One page of log entries, newest first.
#[derive(Debug, Clone, Serialize, ToSchema)]
pub struct PluginLogPageDto {
pub entries: Vec<PluginLogEntryDto>,
/// Total entries matching the filter (across all pages).
pub total: usize,
pub limit: usize,
pub offset: usize,
}
impl PluginLogPageDto {
pub fn from_page(page: LogPage, limit: usize, offset: usize) -> Self {
Self {
entries: page
.entries
.into_iter()
.map(PluginLogEntryDto::from)
.collect(),
total: page.total,
limit,
offset,
}
}
}
/// Query string for `GET /api/admin/plugins/{id}/logs`.
#[derive(Debug, Deserialize, IntoParams)]
pub struct PluginLogQueryDto {
/// Keep only entries at this level (`debug`/`info`/`warn`/`error`).
pub level: Option<String>,
/// Case-insensitive substring filter on the message.
pub search: Option<String>,
/// Max entries to return (clamped server-side).
pub limit: Option<usize>,
/// Newest-first entries to skip.
pub offset: Option<usize>,
}
/// Per-plugin retention policy (request + response body).
#[derive(Debug, Clone, Copy, Serialize, Deserialize, ToSchema)]
pub struct PluginRetentionDto {
/// Delete rotated segments older than this many days.
pub retention_days: u32,
/// Aggregate byte ceiling on kept segments for the plugin.
pub max_bytes: u64,
}
impl From<RetentionSettings> for PluginRetentionDto {
fn from(s: RetentionSettings) -> Self {
Self {
retention_days: s.retention_days,
max_bytes: s.max_bytes,
}
}
}
impl From<PluginRetentionDto> for RetentionSettings {
fn from(d: PluginRetentionDto) -> Self {
Self {
retention_days: d.retention_days,
max_bytes: d.max_bytes,
}
}
}