mirror of
https://github.com/chylex/Apache-Prometheus-Exporter.git
synced 2025-04-08 05:15:46 +02:00
Refactor module structure and file names
This commit is contained in:
parent
f0e1447ae5
commit
bbc416b8d3
@ -178,7 +178,7 @@ impl LogFilePath {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::log_file_pattern::{LogFilePattern, parse_log_file_pattern_from_str};
|
||||
use super::{LogFilePattern, parse_log_file_pattern_from_str};
|
||||
|
||||
#[test]
|
||||
fn empty_path() {
|
@ -9,12 +9,12 @@ use tokio::io::{AsyncBufReadExt, BufReader, Lines};
|
||||
use tokio::sync::mpsc;
|
||||
use tokio::sync::mpsc::Receiver;
|
||||
|
||||
use crate::ApacheMetrics;
|
||||
use crate::fs_watcher::{FsEventCallbacks, FsWatcher};
|
||||
use crate::log_file_pattern::LogFilePath;
|
||||
use crate::logs::filesystem_watcher::{FsEventCallbacks, FsWatcher};
|
||||
use crate::logs::log_file_pattern::LogFilePath;
|
||||
use crate::metrics::Metrics;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
enum LogFileKind {
|
||||
pub enum LogFileKind {
|
||||
Access,
|
||||
Error,
|
||||
}
|
||||
@ -30,26 +30,12 @@ impl LogFileMetadata {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn start_log_watcher(access_log_files: Vec<LogFilePath>, error_log_files: Vec<LogFilePath>, metrics: ApacheMetrics) -> bool {
|
||||
let mut watcher = LogWatcherConfiguration::new();
|
||||
|
||||
for log_file in access_log_files.into_iter() {
|
||||
watcher.add_file(log_file, LogFileKind::Access);
|
||||
}
|
||||
|
||||
for log_file in error_log_files.into_iter() {
|
||||
watcher.add_file(log_file, LogFileKind::Error);
|
||||
}
|
||||
|
||||
watcher.start(&metrics).await
|
||||
}
|
||||
|
||||
struct LogWatcherConfiguration {
|
||||
pub struct LogWatcherConfiguration {
|
||||
files: Vec<(PathBuf, LogFileMetadata)>,
|
||||
}
|
||||
|
||||
impl LogWatcherConfiguration {
|
||||
fn new() -> LogWatcherConfiguration {
|
||||
pub fn new() -> LogWatcherConfiguration {
|
||||
LogWatcherConfiguration { files: Vec::new() }
|
||||
}
|
||||
|
||||
@ -57,14 +43,14 @@ impl LogWatcherConfiguration {
|
||||
return self.files.iter().filter(|(_, metadata)| metadata.kind == kind).count();
|
||||
}
|
||||
|
||||
fn add_file(&mut self, log_file: LogFilePath, kind: LogFileKind) {
|
||||
pub fn add_file(&mut self, log_file: LogFilePath, kind: LogFileKind) {
|
||||
let path = log_file.path;
|
||||
let label = log_file.label;
|
||||
let metadata = LogFileMetadata { kind, label };
|
||||
self.files.push((path, metadata));
|
||||
}
|
||||
|
||||
async fn start(self, metrics: &ApacheMetrics) -> bool {
|
||||
pub async fn start(self, metrics: &Metrics) -> bool {
|
||||
if self.files.is_empty() {
|
||||
println!("[LogWatcher] No log files provided.");
|
||||
return false;
|
||||
@ -141,7 +127,7 @@ struct LogWatcher {
|
||||
}
|
||||
|
||||
impl LogWatcher {
|
||||
async fn create(path: PathBuf, metadata: LogFileMetadata, metrics: ApacheMetrics, fs_watcher: Arc<FsWatcher>, fs_event_receiver: Receiver<Event>) -> Option<Self> {
|
||||
async fn create(path: PathBuf, metadata: LogFileMetadata, metrics: Metrics, fs_watcher: Arc<FsWatcher>, fs_event_receiver: Receiver<Event>) -> Option<Self> {
|
||||
let state = match LogWatchingState::initialize(path.clone(), fs_watcher).await {
|
||||
Some(state) => state,
|
||||
None => return None,
|
||||
@ -261,7 +247,7 @@ impl LogWatchingState {
|
||||
struct LogLineProcessor {
|
||||
path: PathBuf,
|
||||
metadata: LogFileMetadata,
|
||||
metrics: ApacheMetrics,
|
||||
metrics: Metrics,
|
||||
}
|
||||
|
||||
impl LogLineProcessor {
|
52
src/logs/mod.rs
Normal file
52
src/logs/mod.rs
Normal file
@ -0,0 +1,52 @@
|
||||
use log_file_watcher::{LogFileKind, LogWatcherConfiguration};
|
||||
|
||||
use crate::logs::log_file_pattern::{LogFilePath, parse_log_file_pattern_from_env};
|
||||
use crate::metrics::Metrics;
|
||||
|
||||
mod access_log_parser;
|
||||
mod filesystem_watcher;
|
||||
mod log_file_pattern;
|
||||
mod log_file_watcher;
|
||||
|
||||
pub fn find_log_files(environment_variable_name: &str, log_kind: &str) -> Option<Vec<LogFilePath>> {
|
||||
let log_file_pattern = match parse_log_file_pattern_from_env(environment_variable_name) {
|
||||
Ok(pattern) => pattern,
|
||||
Err(error) => {
|
||||
println!("Error: {}", error);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let log_files = match log_file_pattern.search() {
|
||||
Ok(files) => files,
|
||||
Err(error) => {
|
||||
println!("Error searching {} files: {}", log_kind, error);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
if log_files.is_empty() {
|
||||
println!("Found no matching {} files.", log_kind);
|
||||
return None;
|
||||
}
|
||||
|
||||
for log_file in &log_files {
|
||||
println!("Found {} file: {} (label \"{}\")", log_kind, log_file.path.display(), log_file.label);
|
||||
}
|
||||
|
||||
Some(log_files)
|
||||
}
|
||||
|
||||
pub async fn start_log_watcher(access_log_files: Vec<LogFilePath>, error_log_files: Vec<LogFilePath>, metrics: Metrics) -> bool {
|
||||
let mut watcher = LogWatcherConfiguration::new();
|
||||
|
||||
for log_file in access_log_files.into_iter() {
|
||||
watcher.add_file(log_file, LogFileKind::Access);
|
||||
}
|
||||
|
||||
for log_file in error_log_files.into_iter() {
|
||||
watcher.add_file(log_file, LogFileKind::Error);
|
||||
}
|
||||
|
||||
watcher.start(&metrics).await
|
||||
}
|
52
src/main.rs
52
src/main.rs
@ -6,50 +6,16 @@ use std::sync::Mutex;
|
||||
|
||||
use tokio::signal;
|
||||
|
||||
use crate::apache_metrics::ApacheMetrics;
|
||||
use crate::log_file_pattern::{LogFilePath, parse_log_file_pattern_from_env};
|
||||
use crate::log_watcher::start_log_watcher;
|
||||
use crate::web_server::WebServer;
|
||||
use crate::metrics::Metrics;
|
||||
use crate::web::WebServer;
|
||||
|
||||
mod apache_metrics;
|
||||
mod fs_watcher;
|
||||
mod log_file_pattern;
|
||||
mod log_parser;
|
||||
mod log_watcher;
|
||||
mod web_server;
|
||||
mod logs;
|
||||
mod metrics;
|
||||
mod web;
|
||||
|
||||
const ACCESS_LOG_FILE_PATTERN: &str = "ACCESS_LOG_FILE_PATTERN";
|
||||
const ERROR_LOG_FILE_PATTERN: &str = "ERROR_LOG_FILE_PATTERN";
|
||||
|
||||
fn find_log_files(environment_variable_name: &str, log_kind: &str) -> Option<Vec<LogFilePath>> {
|
||||
let log_file_pattern = match parse_log_file_pattern_from_env(environment_variable_name) {
|
||||
Ok(pattern) => pattern,
|
||||
Err(error) => {
|
||||
println!("Error: {}", error);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let log_files = match log_file_pattern.search() {
|
||||
Ok(files) => files,
|
||||
Err(error) => {
|
||||
println!("Error searching {} files: {}", log_kind, error);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
if log_files.is_empty() {
|
||||
println!("Found no matching {} files.", log_kind);
|
||||
return None;
|
||||
}
|
||||
|
||||
for log_file in &log_files {
|
||||
println!("Found {} file: {} (label \"{}\")", log_kind, log_file.path.display(), log_file.label);
|
||||
}
|
||||
|
||||
Some(log_files)
|
||||
}
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() -> ExitCode {
|
||||
let host = env::var("HTTP_HOST").unwrap_or(String::from("127.0.0.1"));
|
||||
@ -63,12 +29,12 @@ async fn main() -> ExitCode {
|
||||
|
||||
println!("Initializing exporter...");
|
||||
|
||||
let access_log_files = match find_log_files(ACCESS_LOG_FILE_PATTERN, "access log") {
|
||||
let access_log_files = match logs::find_log_files(ACCESS_LOG_FILE_PATTERN, "access log") {
|
||||
Some(files) => files,
|
||||
None => return ExitCode::FAILURE,
|
||||
};
|
||||
|
||||
let error_log_files = match find_log_files(ERROR_LOG_FILE_PATTERN, "error log") {
|
||||
let error_log_files = match logs::find_log_files(ERROR_LOG_FILE_PATTERN, "error log") {
|
||||
Some(files) => files,
|
||||
None => return ExitCode::FAILURE,
|
||||
};
|
||||
@ -78,9 +44,9 @@ async fn main() -> ExitCode {
|
||||
None => return ExitCode::FAILURE
|
||||
};
|
||||
|
||||
let (metrics_registry, metrics) = ApacheMetrics::new();
|
||||
let (metrics_registry, metrics) = Metrics::new();
|
||||
|
||||
if !start_log_watcher(access_log_files, error_log_files, metrics).await {
|
||||
if !logs::start_log_watcher(access_log_files, error_log_files, metrics).await {
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
|
||||
|
@ -4,20 +4,17 @@ use prometheus_client::registry::Registry;
|
||||
|
||||
type SingleLabel = [(&'static str, String); 1];
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ApacheMetrics {
|
||||
#[derive(Clone, Default)]
|
||||
pub struct Metrics {
|
||||
pub requests_total: Family<SingleLabel, Counter>,
|
||||
pub errors_total: Family<SingleLabel, Counter>
|
||||
}
|
||||
|
||||
impl ApacheMetrics {
|
||||
pub fn new() -> (Registry, ApacheMetrics) {
|
||||
impl Metrics {
|
||||
pub fn new() -> (Registry, Metrics) {
|
||||
let mut registry = <Registry>::default();
|
||||
|
||||
let metrics = ApacheMetrics {
|
||||
requests_total: Family::<SingleLabel, Counter>::default(),
|
||||
errors_total: Family::<SingleLabel, Counter>::default()
|
||||
};
|
||||
let metrics = Metrics::default();
|
||||
|
||||
registry.register("apache_requests", "Number of received requests", metrics.requests_total.clone());
|
||||
registry.register("apache_errors", "Number of logged errors", metrics.errors_total.clone());
|
42
src/web/metrics_endpoint.rs
Normal file
42
src/web/metrics_endpoint.rs
Normal file
@ -0,0 +1,42 @@
|
||||
use std::fmt;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use hyper::{Body, http, Response, StatusCode};
|
||||
use hyper::header::CONTENT_TYPE;
|
||||
use prometheus_client::encoding::text::encode;
|
||||
use prometheus_client::registry::Registry;
|
||||
|
||||
//noinspection SpellCheckingInspection
|
||||
const METRICS_CONTENT_TYPE: &str = "application/openmetrics-text; version=1.0.0; charset=utf-8";
|
||||
|
||||
pub async fn handle(metrics_registry: Arc<Mutex<Registry>>) -> http::Result<Response<Body>> {
|
||||
match try_encode(metrics_registry) {
|
||||
MetricsEncodeResult::Ok(buf) => {
|
||||
Response::builder().status(StatusCode::OK).header(CONTENT_TYPE, METRICS_CONTENT_TYPE).body(Body::from(buf))
|
||||
}
|
||||
MetricsEncodeResult::FailedAcquiringRegistryLock => {
|
||||
println!("[WebServer] Failed acquiring lock on registry.");
|
||||
Response::builder().status(StatusCode::INTERNAL_SERVER_ERROR).body(Body::empty())
|
||||
}
|
||||
MetricsEncodeResult::FailedEncodingMetrics(e) => {
|
||||
println!("[WebServer] Error encoding metrics: {}", e);
|
||||
Response::builder().status(StatusCode::INTERNAL_SERVER_ERROR).body(Body::empty())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum MetricsEncodeResult {
|
||||
Ok(String),
|
||||
FailedAcquiringRegistryLock,
|
||||
FailedEncodingMetrics(fmt::Error),
|
||||
}
|
||||
|
||||
fn try_encode(metrics_registry: Arc<Mutex<Registry>>) -> MetricsEncodeResult {
|
||||
let mut buf = String::new();
|
||||
|
||||
return if let Ok(metrics_registry) = metrics_registry.lock() {
|
||||
encode(&mut buf, &metrics_registry).map_or_else(MetricsEncodeResult::FailedEncodingMetrics, |_| MetricsEncodeResult::Ok(buf))
|
||||
} else {
|
||||
MetricsEncodeResult::FailedAcquiringRegistryLock
|
||||
};
|
||||
}
|
@ -1,16 +1,16 @@
|
||||
use std::fmt;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
|
||||
use hyper::{Body, Error, header, Method, Request, Response, Server, StatusCode};
|
||||
use hyper::{Body, Error, Method, Request, Response, Server, StatusCode};
|
||||
use hyper::http::Result;
|
||||
use hyper::server::Builder;
|
||||
use hyper::server::conn::AddrIncoming;
|
||||
use hyper::service::{make_service_fn, service_fn};
|
||||
use prometheus_client::encoding::text::encode;
|
||||
use prometheus_client::registry::Registry;
|
||||
|
||||
mod metrics_endpoint;
|
||||
|
||||
const MAX_BUFFER_SIZE: usize = 1024 * 32;
|
||||
|
||||
pub struct WebServer {
|
||||
@ -55,41 +55,8 @@ impl WebServer {
|
||||
|
||||
async fn handle_request(req: Request<Body>, metrics_registry: Arc<Mutex<Registry>>) -> Result<Response<Body>> {
|
||||
if req.method() == Method::GET && req.uri().path() == "/metrics" {
|
||||
metrics_handler(Arc::clone(&metrics_registry)).await
|
||||
metrics_endpoint::handle(Arc::clone(&metrics_registry)).await
|
||||
} else {
|
||||
Response::builder().status(StatusCode::NOT_FOUND).body(Body::empty())
|
||||
}
|
||||
}
|
||||
|
||||
//noinspection SpellCheckingInspection
|
||||
async fn metrics_handler(metrics_registry: Arc<Mutex<Registry>>) -> Result<Response<Body>> {
|
||||
match encode_metrics(metrics_registry) {
|
||||
MetricsEncodeResult::Ok(buf) => {
|
||||
Response::builder().status(StatusCode::OK).header(header::CONTENT_TYPE, "application/openmetrics-text; version=1.0.0; charset=utf-8").body(Body::from(buf))
|
||||
}
|
||||
MetricsEncodeResult::FailedAcquiringRegistryLock => {
|
||||
println!("[WebServer] Failed acquiring lock on registry.");
|
||||
Response::builder().status(StatusCode::INTERNAL_SERVER_ERROR).body(Body::empty())
|
||||
}
|
||||
MetricsEncodeResult::FailedEncodingMetrics(e) => {
|
||||
println!("[WebServer] Error encoding metrics: {}", e);
|
||||
Response::builder().status(StatusCode::INTERNAL_SERVER_ERROR).body(Body::empty())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum MetricsEncodeResult {
|
||||
Ok(String),
|
||||
FailedAcquiringRegistryLock,
|
||||
FailedEncodingMetrics(fmt::Error),
|
||||
}
|
||||
|
||||
fn encode_metrics(metrics_registry: Arc<Mutex<Registry>>) -> MetricsEncodeResult {
|
||||
let mut buf = String::new();
|
||||
|
||||
return if let Ok(metrics_registry) = metrics_registry.lock() {
|
||||
encode(&mut buf, &metrics_registry).map_or_else(MetricsEncodeResult::FailedEncodingMetrics, |_| MetricsEncodeResult::Ok(buf))
|
||||
} else {
|
||||
MetricsEncodeResult::FailedAcquiringRegistryLock
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue
Block a user