Create a special logging formatter for release builds
This commit is contained in:
parent
8c6ffbfb5c
commit
f83757702a
11 changed files with 163 additions and 75 deletions
9
api/.env
9
api/.env
|
@ -1,7 +1,8 @@
|
|||
HOSTNAME=localhost
|
||||
PORT=42069
|
||||
TOKEN_KEY=k4.local.hWoS2ZulK9xPEATtXH1Dvj_iynzqfUv5ER5_IFTg5-Q
|
||||
DATABASE_URL=postgres://debt_pirate:HRURqlUmtjIy@192.168.122.251/debt_pirate
|
||||
ASSETS_DIR=/home/zcdziura/Documents/Projects/debt-pirate/api/assets
|
||||
DATABASE_URL=postgres://debt_pirate:HRURqlUmtjIy@192.168.122.251/debt_pirate
|
||||
HOSTNAME=localhost
|
||||
MAINTENANCE_USER_ACCOUNT=debt_pirate:HRURqlUmtjIy
|
||||
PORT=42069
|
||||
RUST_LOG=debt_pirate=trace
|
||||
SEND_VERIFICATION_EMAIL=false
|
||||
TOKEN_KEY=k4.local.hWoS2ZulK9xPEATtXH1Dvj_iynzqfUv5ER5_IFTg5-Q
|
||||
|
|
16
api/.vscode/launch.json
vendored
16
api/.vscode/launch.json
vendored
|
@ -7,15 +7,15 @@
|
|||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "Debug executable 'auth-test'",
|
||||
"name": "Debug executable 'debt-pirate'",
|
||||
"cargo": {
|
||||
"args": [
|
||||
"build",
|
||||
"--bin=auth-test",
|
||||
"--package=auth-test"
|
||||
"--bin=debt-pirate",
|
||||
"--package=debt-pirate"
|
||||
],
|
||||
"filter": {
|
||||
"name": "auth-test",
|
||||
"name": "debt-pirate",
|
||||
"kind": "bin"
|
||||
}
|
||||
},
|
||||
|
@ -25,16 +25,16 @@
|
|||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "Debug unit tests in executable 'auth-test'",
|
||||
"name": "Debug unit tests in executable 'debt-pirate'",
|
||||
"cargo": {
|
||||
"args": [
|
||||
"test",
|
||||
"--no-run",
|
||||
"--bin=auth-test",
|
||||
"--package=auth-test"
|
||||
"--bin=debt-pirate",
|
||||
"--package=debt-pirate"
|
||||
],
|
||||
"filter": {
|
||||
"name": "auth-test",
|
||||
"name": "debt-pirate",
|
||||
"kind": "bin"
|
||||
}
|
||||
},
|
||||
|
|
|
@ -25,9 +25,7 @@ lettre = { version = "0.11", default-features = false, features = [
|
|||
"tokio1",
|
||||
"tokio1-rustls-tls",
|
||||
] }
|
||||
log = "0.4"
|
||||
num_cpus = "1.16"
|
||||
once_cell = "1.19"
|
||||
pasetors = "0.7"
|
||||
serde = { version = "1.0", features = ["derive", "rc", "std"] }
|
||||
serde_json = "1.0"
|
||||
|
|
|
@ -123,7 +123,7 @@
|
|||
<!-- start copy -->
|
||||
<tr>
|
||||
<td align="left" bgcolor="#ffffff" style="padding: 24px; font-family: sans-serif; font-size: 16px; line-height: 24px;">
|
||||
<p style="margin: 0;">Click or tap the button below to confirm your email address. If you didn't create an account with <a href="#">Auth-Test</a>, you can safely delete this email.</p>
|
||||
<p style="margin: 0;">Click or tap the button below to confirm your email address. If you didn't create an account with <a href="#">Debt Pirate</a>, you can safely delete this email.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- end copy -->
|
||||
|
@ -160,7 +160,7 @@
|
|||
<!-- start copy -->
|
||||
<tr>
|
||||
<td align="left" bgcolor="#ffffff" style="padding: 24px; font-family: sans-serif; font-size: 16px; line-height: 24px; border-bottom: 3px solid #d4dadf">
|
||||
<p style="margin: 0;">Thank you,<br> The Auth-Test Team</p>
|
||||
<p style="margin: 0;">Thank you,<br> The Debt Pirate Team</p>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- end copy -->
|
||||
|
|
|
@ -1,38 +1,24 @@
|
|||
use std::{
|
||||
collections::HashSet,
|
||||
path::{Path, PathBuf},
|
||||
sync::mpsc::Sender,
|
||||
};
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use pasetors::{keys::SymmetricKey, version4::V4};
|
||||
|
||||
use crate::services::UserConfirmationMessage;
|
||||
|
||||
use super::AppError;
|
||||
|
||||
static REQUIRED_ENV_VARS: Lazy<HashSet<&'static str>> = Lazy::new(|| {
|
||||
[
|
||||
"HOSTNAME",
|
||||
"PORT",
|
||||
"TOKEN_KEY",
|
||||
"DATABASE_URL",
|
||||
"ASSETS_DIR",
|
||||
"RUST_LOG",
|
||||
]
|
||||
.into_iter()
|
||||
.collect()
|
||||
});
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Environment {
|
||||
hostname: String,
|
||||
port: u32,
|
||||
token_key: SymmetricKey<V4>,
|
||||
assets_dir: PathBuf,
|
||||
database_url: String,
|
||||
email_sender: Sender<UserConfirmationMessage>,
|
||||
assets_dir: PathBuf,
|
||||
hostname: String,
|
||||
port: u32,
|
||||
rust_log: String,
|
||||
send_verification_email: bool,
|
||||
token_key: SymmetricKey<V4>,
|
||||
}
|
||||
|
||||
impl Environment {
|
||||
|
@ -41,14 +27,14 @@ impl Environment {
|
|||
dotenvy::dotenv_iter()
|
||||
.expect("Missing .env file")
|
||||
.filter_map(|item| item.ok())
|
||||
.filter(|(key, _)| REQUIRED_ENV_VARS.contains(key.as_str()))
|
||||
.for_each(|(key, value)| match key.as_str() {
|
||||
"ASSETS_DIR" => builder.with_assets_dir(value),
|
||||
"DATABASE_URL" => builder.with_database_url(value),
|
||||
"HOSTNAME" => builder.with_hostname(value),
|
||||
"PORT" => builder.with_port(value),
|
||||
"TOKEN_KEY" => builder.with_token_key(value),
|
||||
"DATABASE_URL" => builder.with_database_url(value),
|
||||
"ASSETS_DIR" => builder.with_assets_dir(value),
|
||||
"RUST_LOG" => builder.with_rust_log(value),
|
||||
"SEND_VERIFICATION_EMAIL" => builder.with_send_verification_email(value),
|
||||
"TOKEN_KEY" => builder.with_token_key(value),
|
||||
_ => {}
|
||||
});
|
||||
|
||||
|
@ -87,41 +73,48 @@ impl Environment {
|
|||
pub fn rust_log(&self) -> &str {
|
||||
self.rust_log.as_str()
|
||||
}
|
||||
|
||||
pub fn send_verification_email(&self) -> bool {
|
||||
self.send_verification_email
|
||||
}
|
||||
}
|
||||
|
||||
impl From<EnvironmentObjectBuilder> for Environment {
|
||||
fn from(builder: EnvironmentObjectBuilder) -> Self {
|
||||
let EnvironmentObjectBuilder {
|
||||
hostname,
|
||||
port,
|
||||
token_key,
|
||||
assets_dir,
|
||||
database_url,
|
||||
email_sender,
|
||||
assets_dir,
|
||||
hostname,
|
||||
port,
|
||||
rust_log,
|
||||
send_verification,
|
||||
token_key,
|
||||
} = builder;
|
||||
|
||||
Self {
|
||||
hostname: hostname.unwrap(),
|
||||
port: port.unwrap(),
|
||||
token_key: token_key.unwrap(),
|
||||
assets_dir: assets_dir.unwrap(),
|
||||
database_url: database_url.unwrap(),
|
||||
email_sender: email_sender.unwrap(),
|
||||
assets_dir: assets_dir.unwrap(),
|
||||
hostname: hostname.unwrap(),
|
||||
port: port.unwrap(),
|
||||
rust_log: rust_log.unwrap(),
|
||||
send_verification_email: send_verification.unwrap_or(true),
|
||||
token_key: token_key.unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct EnvironmentObjectBuilder {
|
||||
pub hostname: Option<String>,
|
||||
pub port: Option<u32>,
|
||||
pub token_key: Option<SymmetricKey<V4>>,
|
||||
pub assets_dir: Option<PathBuf>,
|
||||
pub database_url: Option<String>,
|
||||
pub email_sender: Option<Sender<UserConfirmationMessage>>,
|
||||
pub assets_dir: Option<PathBuf>,
|
||||
pub hostname: Option<String>,
|
||||
pub port: Option<u32>,
|
||||
pub rust_log: Option<String>,
|
||||
pub send_verification: Option<bool>,
|
||||
pub token_key: Option<SymmetricKey<V4>>,
|
||||
}
|
||||
|
||||
impl EnvironmentObjectBuilder {
|
||||
|
@ -199,4 +192,9 @@ impl EnvironmentObjectBuilder {
|
|||
pub fn with_rust_log(&mut self, rust_log: String) {
|
||||
self.rust_log = Some(rust_log);
|
||||
}
|
||||
|
||||
pub fn with_send_verification_email(&mut self, send_verification_email: String) {
|
||||
let send_verification_email = send_verification_email.to_lowercase() == "true";
|
||||
self.send_verification = Some(send_verification_email);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,10 +116,7 @@ enum ErrorKind {
|
|||
|
||||
impl IntoResponse for AppError {
|
||||
fn into_response(self) -> axum::response::Response {
|
||||
match &self.kind {
|
||||
ErrorKind::DuplicateRecord => (),
|
||||
_ => (),
|
||||
}
|
||||
if let ErrorKind::DuplicateRecord = &self.kind { }
|
||||
|
||||
StatusCode::INTERNAL_SERVER_ERROR.into_response()
|
||||
}
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
mod user;
|
||||
|
||||
use axum::Router;
|
||||
use std::time::Duration;
|
||||
|
||||
use axum::{
|
||||
extract::{MatchedPath, Request},
|
||||
response::Response,
|
||||
Router,
|
||||
};
|
||||
use tokio::net::TcpListener;
|
||||
use tracing::info;
|
||||
use tower_http::trace::TraceLayer;
|
||||
use tracing::{error, info, info_span, warn, Span};
|
||||
|
||||
use crate::{
|
||||
db::DbPool,
|
||||
|
@ -38,8 +45,31 @@ pub async fn start_app(pool: DbPool, env: Environment) -> Result<(), AppError> {
|
|||
.await
|
||||
.unwrap();
|
||||
|
||||
let logging_layer = TraceLayer::new_for_http()
|
||||
.make_span_with(|request: &Request| {
|
||||
let request_path = request
|
||||
.extensions()
|
||||
.get::<MatchedPath>()
|
||||
.map(MatchedPath::as_str).unwrap();
|
||||
|
||||
info_span!("http_request", method = %request.method(), path = %request_path, status = tracing::field::Empty)
|
||||
})
|
||||
.on_response(|response: &Response, _duration: Duration, _span: &Span| {
|
||||
let status = response.status();
|
||||
|
||||
_span.record("status", status.to_string());
|
||||
|
||||
match status.as_u16() {
|
||||
w if (300..400).contains(&w) => warn!(""),
|
||||
e if e >= 400 => error!(""),
|
||||
_ => info!("")
|
||||
}
|
||||
});
|
||||
|
||||
let app_state = AppState::new(pool, env);
|
||||
let app = Router::new().merge(user::requests(app_state.clone()));
|
||||
let app = Router::new()
|
||||
.merge(user::requests(app_state.clone()))
|
||||
.layer(logging_layer);
|
||||
|
||||
info!("API started successfully.");
|
||||
axum::serve(listener, app)
|
||||
|
|
|
@ -51,13 +51,15 @@ pub async fn user_registration_post_handler(
|
|||
auth_token: auth_token.clone(),
|
||||
};
|
||||
|
||||
let _ = app_state
|
||||
.env()
|
||||
.email_sender()
|
||||
.send(new_user_confirmation_message)
|
||||
.inspect_err(|err| {
|
||||
eprintln!("Got the rollowing error while sending across the channel: {err}");
|
||||
});
|
||||
if app_state.env().send_verification_email() {
|
||||
let _ = app_state
|
||||
.env()
|
||||
.email_sender()
|
||||
.send(new_user_confirmation_message)
|
||||
.inspect_err(|err| {
|
||||
eprintln!("Got the rollowing error while sending across the channel: {err}");
|
||||
});
|
||||
}
|
||||
|
||||
let response = (
|
||||
StatusCode::CREATED,
|
||||
|
|
|
@ -104,7 +104,7 @@ fn generate_token(
|
|||
};
|
||||
|
||||
local::encrypt(
|
||||
&key,
|
||||
key,
|
||||
&claims,
|
||||
Some(&footer),
|
||||
Some("TODO_ENV_NAME_HERE".as_bytes()),
|
||||
|
|
|
@ -1,18 +1,75 @@
|
|||
use tracing::level_filters::LevelFilter;
|
||||
use tracing_subscriber::{fmt::time::UtcTime, EnvFilter};
|
||||
#![allow(unused)]
|
||||
use std::time::SystemTime;
|
||||
|
||||
use humantime::format_rfc3339_millis;
|
||||
use tracing::{level_filters::LevelFilter, Event, Subscriber};
|
||||
use tracing_subscriber::{
|
||||
fmt::{
|
||||
format::{DefaultFields, Writer},
|
||||
FmtContext, FormatEvent, FormatFields, FormattedFields,
|
||||
},
|
||||
registry::{LookupSpan, Scope},
|
||||
EnvFilter,
|
||||
};
|
||||
|
||||
use crate::models::Environment;
|
||||
|
||||
pub fn initialize_logger(env: &Environment) {
|
||||
let log_level = env.rust_log();
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
tracing_subscriber::fmt()
|
||||
.with_env_filter(
|
||||
EnvFilter::builder()
|
||||
.with_default_directive(LevelFilter::TRACE.into())
|
||||
.parse_lossy(log_level),
|
||||
)
|
||||
.event_format(tracing_subscriber::fmt::format().compact())
|
||||
.with_target(false)
|
||||
.with_file(true)
|
||||
.with_line_number(true)
|
||||
.init();
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
tracing_subscriber::fmt()
|
||||
.with_env_filter(
|
||||
EnvFilter::builder()
|
||||
.with_default_directive(LevelFilter::INFO.into())
|
||||
.parse_lossy(log_level),
|
||||
)
|
||||
.with_timer(UtcTime::rfc_3339())
|
||||
.with_target(false)
|
||||
.event_format(EventFormatter)
|
||||
.fmt_fields(DefaultFields::new())
|
||||
.init();
|
||||
}
|
||||
|
||||
struct EventFormatter;
|
||||
impl<S, N> FormatEvent<S, N> for EventFormatter
|
||||
where
|
||||
S: Subscriber + for<'a> LookupSpan<'a>,
|
||||
N: for<'a> FormatFields<'a> + 'static,
|
||||
{
|
||||
fn format_event(
|
||||
&self,
|
||||
ctx: &FmtContext<'_, S, N>,
|
||||
mut writer: Writer<'_>,
|
||||
event: &Event<'_>,
|
||||
) -> std::fmt::Result {
|
||||
let timestamp = format_rfc3339_millis(SystemTime::now());
|
||||
let level = event.metadata().level();
|
||||
|
||||
write!(writer, "{timestamp} {level:<5} ")?;
|
||||
|
||||
ctx.field_format().format_fields(writer.by_ref(), event)?;
|
||||
|
||||
for span in ctx.event_scope().into_iter().flat_map(Scope::from_root) {
|
||||
let exts = span.extensions();
|
||||
if let Some(fields) = exts.get::<FormattedFields<N>>() {
|
||||
if !fields.is_empty() {
|
||||
write!(writer, "{}", fields.fields)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
writeln!(writer)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
use std::{fs::File, io::Read, path::Path, sync::mpsc::Receiver, thread};
|
||||
use std::{
|
||||
fs::File,
|
||||
io::Read,
|
||||
path::Path,
|
||||
sync::{mpsc::Receiver, LazyLock},
|
||||
thread,
|
||||
};
|
||||
|
||||
use lettre::{
|
||||
message::{header::ContentType, Mailbox},
|
||||
|
@ -8,21 +14,20 @@ use lettre::{
|
|||
},
|
||||
AsyncSmtpTransport, AsyncTransport, Message, Tokio1Executor,
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
use tokio::runtime::Handle;
|
||||
|
||||
use super::UserConfirmationMessage;
|
||||
|
||||
static CREDENTIALS: Lazy<Credentials> = Lazy::new(|| {
|
||||
static CREDENTIALS: LazyLock<Credentials> = LazyLock::new(|| {
|
||||
Credentials::new(
|
||||
"donotreply@mail.dziura.cloud".to_owned(),
|
||||
"hunter2".to_owned(),
|
||||
)
|
||||
});
|
||||
|
||||
static NUM_CPUS: Lazy<u32> = Lazy::new(|| num_cpus::get() as u32);
|
||||
static NUM_CPUS: LazyLock<u32> = LazyLock::new(|| num_cpus::get() as u32);
|
||||
|
||||
static FROM_MAILBOX: Lazy<Mailbox> = Lazy::new(|| {
|
||||
static FROM_MAILBOX: LazyLock<Mailbox> = LazyLock::new(|| {
|
||||
"No Not Reply Auth-Test <donotreply@mail.dziura.cloud>"
|
||||
.parse()
|
||||
.unwrap()
|
||||
|
|
Loading…
Add table
Reference in a new issue