feat: integrate tracing for structured logging

- Added `tracing` and `tracing-subscriber` for improved logging, replacing `println` statements with `info`, `debug`, `warn`, and `error`.
- Annotated key methods with `#[instrument]` for better tracing of function calls and arguments.
- Configured logging initialization in `main.rs` with `EnvFilter` to control log verbosity.
This commit is contained in:
Harald Hoyer 2025-03-20 14:59:22 +01:00
parent f11b83ddf4
commit 8f28cc1af2
5 changed files with 243 additions and 81 deletions

View file

@ -10,6 +10,7 @@ use axum::{
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use tokio::net::TcpListener;
use tracing::{info, error, debug, instrument};
use crate::document_service::{Document, DocumentService, SignatureVerification};
use crate::vault_setup::VaultClient;
@ -48,6 +49,7 @@ pub struct SignDocumentRequest {
// API response implementations
impl IntoResponse for ApiError {
fn into_response(self) -> Response {
error!("API error: {}", self.0);
(
StatusCode::INTERNAL_SERVER_ERROR,
format!("Error: {}", self.0),
@ -66,12 +68,13 @@ where
}
// Start the API server
#[instrument(skip(vault_addr, root_token))]
pub async fn start_api(
vault_addr: &str,
root_token: &str,
api_port: u16,
) -> Result<()> {
println!("Starting API server on port {}...", api_port);
info!("Starting API server on port {}...", api_port);
// Initialize Vault client
let vault_client = VaultClient::new(vault_addr, root_token);
@ -101,14 +104,17 @@ pub async fn start_api(
.route("/api/documents/:id/verify", get(verify_document))
.with_state(state);
info!("API routes configured");
// Start server
let listener = TcpListener::bind(format!("0.0.0.0:{}", api_port)).await?;
println!("API server started on port {}", api_port);
info!("API server started on port {}", api_port);
// Get the socket address
let addr = listener.local_addr()?;
// Bind and serve
info!("Serving API at {}", addr);
Server::bind(&addr)
.serve(app.into_make_service())
.await?;
@ -117,27 +123,36 @@ pub async fn start_api(
}
// Health check endpoint
#[instrument]
async fn health_check() -> &'static str {
debug!("Health check endpoint called");
"OK"
}
// Login endpoint
#[instrument(skip(state, request), fields(username = %request.username))]
async fn login(
State(state): State<ApiState>,
Json(request): Json<LoginRequest>,
) -> Result<Json<LoginResponse>, ApiError> {
info!("Login attempt for user: {}", request.username);
let token = state.vault_client
.login_user(&request.username, &request.password)
.await?;
info!("User {} successfully authenticated", request.username);
Ok(Json(LoginResponse { token }))
}
// Upload document endpoint
#[instrument(skip(state, multipart))]
async fn upload_document(
State(state): State<ApiState>,
mut multipart: Multipart,
) -> Result<Json<Document>, ApiError> {
info!("Document upload request received");
let mut document_name = String::new();
let mut document_content = Vec::new();
@ -147,12 +162,15 @@ async fn upload_document(
if name == "name" {
document_name = field.text().await?;
debug!("Received document name: {}", document_name);
} else if name == "file" {
document_content = field.bytes().await?.to_vec();
debug!("Received document content: {} bytes", document_content.len());
}
}
if document_name.is_empty() || document_content.is_empty() {
error!("Missing document name or content");
return Err(anyhow::anyhow!("Missing document name or content").into());
}
@ -166,27 +184,37 @@ async fn upload_document(
.get_document(&document_id)
.await?;
info!("Document uploaded successfully with ID: {}", document_id);
Ok(Json(document))
}
// Get document endpoint
#[instrument(skip(state))]
async fn get_document(
State(state): State<ApiState>,
Path(document_id): Path<String>,
) -> Result<Json<Document>, ApiError> {
info!("Fetching document: {}", document_id);
let document = state.document_service
.get_document(&document_id)
.await?;
debug!("Retrieved document {} with {} signatures",
document.id, document.signatures.len());
Ok(Json(document))
}
// Sign document endpoint
#[instrument(skip(state, request), fields(document_id = %document_id, username = %request.username))]
async fn sign_document(
State(state): State<ApiState>,
Path(document_id): Path<String>,
Json(request): Json<SignDocumentRequest>,
) -> Result<Json<Document>, ApiError> {
info!("Signing request for document {} by user {}", document_id, request.username);
state.document_service
.sign_document(&document_id, &request.username, &request.token)
.await?;
@ -195,17 +223,24 @@ async fn sign_document(
.get_document(&document_id)
.await?;
info!("Document {} successfully signed by {}", document_id, request.username);
Ok(Json(document))
}
// Verify document endpoint
#[instrument(skip(state))]
async fn verify_document(
State(state): State<ApiState>,
Path(document_id): Path<String>,
) -> Result<Json<SignatureVerification>, ApiError> {
info!("Verifying document signatures: {}", document_id);
let verification = state.document_service
.verify_document_signatures(&document_id)
.await?;
info!("Document {} verification result: {}",
document_id, if verification.is_verified { "VERIFIED" } else { "PENDING" });
Ok(Json(verification))
}