[refactor](decoders): extract decoder logging into trx-decode-log crate
Move DecoderLoggers and DecodeLogsConfig out of trx-server into a dedicated src/decoders/trx-decode-log crate, giving file logging the same standalone crate treatment as the four decoder crates. - src/decoders/trx-decode-log/ (new — DecodeLogsConfig + DecoderLoggers) - trx-server/config.rs: re-exports DecodeLogsConfig from trx-decode-log so ServerConfig field references and all tests compile unchanged - trx-server: drop decode_logs module, use trx_decode_log directly Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Generated
+13
@@ -2343,6 +2343,18 @@ dependencies = [
|
|||||||
"trx-core",
|
"trx-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "trx-decode-log"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
|
"dirs",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"tracing",
|
||||||
|
"trx-core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "trx-frontend"
|
name = "trx-frontend"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -2433,6 +2445,7 @@ dependencies = [
|
|||||||
"trx-backend",
|
"trx-backend",
|
||||||
"trx-core",
|
"trx-core",
|
||||||
"trx-cw",
|
"trx-cw",
|
||||||
|
"trx-decode-log",
|
||||||
"trx-ft8",
|
"trx-ft8",
|
||||||
"trx-protocol",
|
"trx-protocol",
|
||||||
"trx-wspr",
|
"trx-wspr",
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
members = [
|
members = [
|
||||||
"src/decoders/trx-aprs",
|
"src/decoders/trx-aprs",
|
||||||
"src/decoders/trx-cw",
|
"src/decoders/trx-cw",
|
||||||
|
"src/decoders/trx-decode-log",
|
||||||
"src/decoders/trx-ft8",
|
"src/decoders/trx-ft8",
|
||||||
"src/decoders/trx-wspr",
|
"src/decoders/trx-wspr",
|
||||||
"src/trx-core",
|
"src/trx-core",
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2026 Stanislaw Grams <stanislawgrams@gmail.com>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
|
||||||
|
[package]
|
||||||
|
name = "trx-decode-log"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
trx-core = { path = "../../trx-core" }
|
||||||
|
chrono = { version = "0.4", default-features = false, features = ["clock"] }
|
||||||
|
dirs = "6"
|
||||||
|
serde = { workspace = true, features = ["derive"] }
|
||||||
|
serde_json = { workspace = true }
|
||||||
|
tracing = { workspace = true }
|
||||||
@@ -1,7 +1,12 @@
|
|||||||
// SPDX-FileCopyrightText: 2025 Stanislaw Grams <stanislawgrams@gmail.com>
|
// SPDX-FileCopyrightText: 2026 Stanislaw Grams <stanislawgrams@gmail.com>
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: BSD-2-Clause
|
// SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
|
||||||
|
//! Server-side decoder file logging (APRS / CW / FT8 / WSPR).
|
||||||
|
//!
|
||||||
|
//! Provides [`DecodeLogsConfig`] for TOML configuration and [`DecoderLoggers`]
|
||||||
|
//! for writing JSON-Lines log files with automatic daily rotation.
|
||||||
|
|
||||||
use std::fs::{create_dir_all, File, OpenOptions};
|
use std::fs::{create_dir_all, File, OpenOptions};
|
||||||
use std::io::{BufWriter, Write};
|
use std::io::{BufWriter, Write};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
@@ -9,12 +14,62 @@ use std::sync::{Arc, Mutex};
|
|||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
use crate::config::DecodeLogsConfig;
|
|
||||||
use trx_core::decode::{AprsPacket, CwEvent, Ft8Message, WsprMessage};
|
use trx_core::decode::{AprsPacket, CwEvent, Ft8Message, WsprMessage};
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Configuration
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
fn default_decode_logs_dir() -> String {
|
||||||
|
if let Some(data_dir) = dirs::data_dir() {
|
||||||
|
return data_dir
|
||||||
|
.join("trx-rs")
|
||||||
|
.join("decoders")
|
||||||
|
.to_string_lossy()
|
||||||
|
.to_string();
|
||||||
|
}
|
||||||
|
"logs/decoders".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Server-side decoder file logging configuration.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
#[serde(default)]
|
||||||
|
pub struct DecodeLogsConfig {
|
||||||
|
/// Whether decoder file logging is enabled
|
||||||
|
pub enabled: bool,
|
||||||
|
/// Base directory for log files
|
||||||
|
pub dir: String,
|
||||||
|
/// APRS decoder log filename
|
||||||
|
pub aprs_file: String,
|
||||||
|
/// CW decoder log filename
|
||||||
|
pub cw_file: String,
|
||||||
|
/// FT8 decoder log filename
|
||||||
|
pub ft8_file: String,
|
||||||
|
/// WSPR decoder log filename
|
||||||
|
pub wspr_file: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for DecodeLogsConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
enabled: false,
|
||||||
|
dir: default_decode_logs_dir(),
|
||||||
|
aprs_file: "TRXRS-APRS-%YYYY%-%MM%-%DD%.log".to_string(),
|
||||||
|
cw_file: "TRXRS-CW-%YYYY%-%MM%-%DD%.log".to_string(),
|
||||||
|
ft8_file: "TRXRS-FT8-%YYYY%-%MM%-%DD%.log".to_string(),
|
||||||
|
wspr_file: "TRXRS-WSPR-%YYYY%-%MM%-%DD%.log".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// File logger (private)
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
struct DecoderFileLogger {
|
struct DecoderFileLogger {
|
||||||
base_dir: PathBuf,
|
base_dir: PathBuf,
|
||||||
file_template: String,
|
file_template: String,
|
||||||
@@ -64,7 +119,7 @@ impl DecoderFileLogger {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_payload<T: serde::Serialize>(&self, payload: &T) {
|
fn write_payload<T: Serialize>(&self, payload: &T) {
|
||||||
let ts_ms = match SystemTime::now().duration_since(UNIX_EPOCH) {
|
let ts_ms = match SystemTime::now().duration_since(UNIX_EPOCH) {
|
||||||
Ok(d) => d.as_millis() as u64,
|
Ok(d) => d.as_millis() as u64,
|
||||||
Err(_) => 0,
|
Err(_) => 0,
|
||||||
@@ -106,6 +161,11 @@ impl DecoderFileLogger {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Public API
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// Aggregate logger for all four server-side decoders.
|
||||||
pub struct DecoderLoggers {
|
pub struct DecoderLoggers {
|
||||||
aprs: DecoderFileLogger,
|
aprs: DecoderFileLogger,
|
||||||
cw: DecoderFileLogger,
|
cw: DecoderFileLogger,
|
||||||
@@ -114,6 +174,7 @@ pub struct DecoderLoggers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DecoderLoggers {
|
impl DecoderLoggers {
|
||||||
|
/// Create loggers from config, or return `None` when logging is disabled.
|
||||||
pub fn from_config(cfg: &DecodeLogsConfig) -> Result<Option<Arc<Self>>, String> {
|
pub fn from_config(cfg: &DecodeLogsConfig) -> Result<Option<Arc<Self>>, String> {
|
||||||
if !cfg.enabled {
|
if !cfg.enabled {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
@@ -26,6 +26,7 @@ trx-backend = { path = "trx-backend" }
|
|||||||
trx-core = { path = "../trx-core" }
|
trx-core = { path = "../trx-core" }
|
||||||
trx-aprs = { path = "../decoders/trx-aprs" }
|
trx-aprs = { path = "../decoders/trx-aprs" }
|
||||||
trx-cw = { path = "../decoders/trx-cw" }
|
trx-cw = { path = "../decoders/trx-cw" }
|
||||||
|
trx-decode-log = { path = "../decoders/trx-decode-log" }
|
||||||
trx-ft8 = { path = "../decoders/trx-ft8" }
|
trx-ft8 = { path = "../decoders/trx-ft8" }
|
||||||
trx-wspr = { path = "../decoders/trx-wspr" }
|
trx-wspr = { path = "../decoders/trx-wspr" }
|
||||||
trx-protocol = { path = "../trx-protocol" }
|
trx-protocol = { path = "../trx-protocol" }
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ use trx_ft8::Ft8Decoder;
|
|||||||
use trx_wspr::WsprDecoder;
|
use trx_wspr::WsprDecoder;
|
||||||
|
|
||||||
use crate::config::AudioConfig;
|
use crate::config::AudioConfig;
|
||||||
use crate::decode_logs::DecoderLoggers;
|
use trx_decode_log::DecoderLoggers;
|
||||||
|
|
||||||
const APRS_HISTORY_RETENTION: Duration = Duration::from_secs(24 * 60 * 60);
|
const APRS_HISTORY_RETENTION: Duration = Duration::from_secs(24 * 60 * 60);
|
||||||
const FT8_HISTORY_RETENTION: Duration = Duration::from_secs(24 * 60 * 60);
|
const FT8_HISTORY_RETENTION: Duration = Duration::from_secs(24 * 60 * 60);
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ use std::path::{Path, PathBuf};
|
|||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use trx_app::{ConfigError, ConfigFile};
|
use trx_app::{ConfigError, ConfigFile};
|
||||||
|
pub use trx_decode_log::DecodeLogsConfig;
|
||||||
|
|
||||||
use trx_core::rig::state::RigMode;
|
use trx_core::rig::state::RigMode;
|
||||||
|
|
||||||
@@ -260,48 +261,6 @@ impl Default for AprsFiConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_decode_logs_dir() -> String {
|
|
||||||
if let Some(data_dir) = dirs::data_dir() {
|
|
||||||
return data_dir
|
|
||||||
.join("trx-rs")
|
|
||||||
.join("decoders")
|
|
||||||
.to_string_lossy()
|
|
||||||
.to_string();
|
|
||||||
}
|
|
||||||
"logs/decoders".to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Server-side decoder file logging configuration.
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
||||||
#[serde(default)]
|
|
||||||
pub struct DecodeLogsConfig {
|
|
||||||
/// Whether decoder file logging is enabled
|
|
||||||
pub enabled: bool,
|
|
||||||
/// Base directory for log files
|
|
||||||
pub dir: String,
|
|
||||||
/// APRS decoder log filename
|
|
||||||
pub aprs_file: String,
|
|
||||||
/// CW decoder log filename
|
|
||||||
pub cw_file: String,
|
|
||||||
/// FT8 decoder log filename
|
|
||||||
pub ft8_file: String,
|
|
||||||
/// WSPR decoder log filename
|
|
||||||
pub wspr_file: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for DecodeLogsConfig {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
enabled: false,
|
|
||||||
dir: default_decode_logs_dir(),
|
|
||||||
aprs_file: "TRXRS-APRS-%YYYY%-%MM%-%DD%.log".to_string(),
|
|
||||||
cw_file: "TRXRS-CW-%YYYY%-%MM%-%DD%.log".to_string(),
|
|
||||||
ft8_file: "TRXRS-FT8-%YYYY%-%MM%-%DD%.log".to_string(),
|
|
||||||
wspr_file: "TRXRS-WSPR-%YYYY%-%MM%-%DD%.log".to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ServerConfig {
|
impl ServerConfig {
|
||||||
pub fn validate(&self) -> Result<(), String> {
|
pub fn validate(&self) -> Result<(), String> {
|
||||||
validate_log_level(self.general.log_level.as_deref())?;
|
validate_log_level(self.general.log_level.as_deref())?;
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
mod aprsfi;
|
mod aprsfi;
|
||||||
mod audio;
|
mod audio;
|
||||||
mod config;
|
mod config;
|
||||||
mod decode_logs;
|
|
||||||
mod error;
|
mod error;
|
||||||
mod listener;
|
mod listener;
|
||||||
mod pskreporter;
|
mod pskreporter;
|
||||||
@@ -34,7 +33,7 @@ use trx_core::rig::state::RigState;
|
|||||||
use trx_core::DynResult;
|
use trx_core::DynResult;
|
||||||
|
|
||||||
use config::ServerConfig;
|
use config::ServerConfig;
|
||||||
use decode_logs::DecoderLoggers;
|
use trx_decode_log::DecoderLoggers;
|
||||||
|
|
||||||
const PKG_DESCRIPTION: &str = concat!(env!("CARGO_PKG_NAME"), " - rig server daemon");
|
const PKG_DESCRIPTION: &str = concat!(env!("CARGO_PKG_NAME"), " - rig server daemon");
|
||||||
const RIG_TASK_CHANNEL_BUFFER: usize = 32;
|
const RIG_TASK_CHANNEL_BUFFER: usize = 32;
|
||||||
|
|||||||
Reference in New Issue
Block a user