diff --git a/Cargo.lock b/Cargo.lock
index 8ce9d9e..88c170e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -22,7 +22,6 @@ name = "apache_prometheus_exporter"
 version = "0.1.0"
 dependencies = [
  "hyper",
- "linemux",
  "path-slash",
  "prometheus-client",
  "tokio",
@@ -73,44 +72,12 @@ version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
-[[package]]
-name = "crossbeam-channel"
-version = "0.5.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
-dependencies = [
- "cfg-if",
- "crossbeam-utils",
-]
-
-[[package]]
-name = "crossbeam-utils"
-version = "0.8.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc"
-dependencies = [
- "cfg-if",
- "once_cell",
-]
-
 [[package]]
 name = "dtoa"
 version = "1.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c6053ff46b5639ceb91756a85a4c8914668393a03170efd79c8884a529d80656"
 
-[[package]]
-name = "filetime"
-version = "0.2.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e94a7bbaa59354bc20dd75b67f23e2797b4490e9d6928203fb105c79e448c86c"
-dependencies = [
- "cfg-if",
- "libc",
- "redox_syscall",
- "windows-sys 0.36.1",
-]
-
 [[package]]
 name = "fnv"
 version = "1.0.7"
@@ -148,7 +115,6 @@ dependencies = [
  "futures-task",
  "pin-project-lite",
  "pin-utils",
- "slab",
 ]
 
 [[package]]
@@ -214,70 +180,18 @@ dependencies = [
  "want",
 ]
 
-[[package]]
-name = "inotify"
-version = "0.9.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff"
-dependencies = [
- "bitflags",
- "inotify-sys",
- "libc",
-]
-
-[[package]]
-name = "inotify-sys"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb"
-dependencies = [
- "libc",
-]
-
 [[package]]
 name = "itoa"
 version = "1.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
 
-[[package]]
-name = "kqueue"
-version = "1.0.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4d6112e8f37b59803ac47a42d14f1f3a59bbf72fc6857ffc5be455e28a691f8e"
-dependencies = [
- "kqueue-sys",
- "libc",
-]
-
-[[package]]
-name = "kqueue-sys"
-version = "1.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8367585489f01bc55dd27404dcf56b95e6da061a256a666ab23be9ba96a2e587"
-dependencies = [
- "bitflags",
- "libc",
-]
-
 [[package]]
 name = "libc"
 version = "0.2.148"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b"
 
-[[package]]
-name = "linemux"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "feb035c7806bd7982a317d8d66815021e91f7ab14a5fbedee22b06f608f11b43"
-dependencies = [
- "futures-util",
- "notify",
- "pin-project-lite",
- "tokio",
-]
-
 [[package]]
 name = "lock_api"
 version = "0.4.8"
@@ -288,15 +202,6 @@ dependencies = [
  "scopeguard",
 ]
 
-[[package]]
-name = "log"
-version = "0.4.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
-dependencies = [
- "cfg-if",
-]
-
 [[package]]
 name = "memchr"
 version = "2.5.0"
@@ -319,28 +224,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2"
 dependencies = [
  "libc",
- "log",
  "wasi",
  "windows-sys 0.48.0",
 ]
 
-[[package]]
-name = "notify"
-version = "5.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "729f63e1ca555a43fe3efa4f3efdf4801c479da85b432242a7b726f353c88486"
-dependencies = [
- "bitflags",
- "crossbeam-channel",
- "filetime",
- "inotify",
- "kqueue",
- "libc",
- "mio",
- "walkdir",
- "windows-sys 0.45.0",
-]
-
 [[package]]
 name = "object"
 version = "0.32.1"
@@ -453,15 +340,6 @@ version = "0.1.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
 
-[[package]]
-name = "same-file"
-version = "1.0.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
-dependencies = [
- "winapi-util",
-]
-
 [[package]]
 name = "scopeguard"
 version = "1.1.0"
@@ -477,15 +355,6 @@ dependencies = [
  "libc",
 ]
 
-[[package]]
-name = "slab"
-version = "0.4.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef"
-dependencies = [
- "autocfg",
-]
-
 [[package]]
 name = "smallvec"
 version = "1.9.0"
@@ -589,17 +458,6 @@ version = "1.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
 
-[[package]]
-name = "walkdir"
-version = "2.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
-dependencies = [
- "same-file",
- "winapi",
- "winapi-util",
-]
-
 [[package]]
 name = "want"
 version = "0.3.1"
@@ -631,15 +489,6 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
 
-[[package]]
-name = "winapi-util"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
-dependencies = [
- "winapi",
-]
-
 [[package]]
 name = "winapi-x86_64-pc-windows-gnu"
 version = "0.4.0"
@@ -659,37 +508,13 @@ dependencies = [
  "windows_x86_64_msvc 0.36.1",
 ]
 
-[[package]]
-name = "windows-sys"
-version = "0.45.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
-dependencies = [
- "windows-targets 0.42.2",
-]
-
 [[package]]
 name = "windows-sys"
 version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
 dependencies = [
- "windows-targets 0.48.5",
-]
-
-[[package]]
-name = "windows-targets"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
-dependencies = [
- "windows_aarch64_gnullvm 0.42.2",
- "windows_aarch64_msvc 0.42.2",
- "windows_i686_gnu 0.42.2",
- "windows_i686_msvc 0.42.2",
- "windows_x86_64_gnu 0.42.2",
- "windows_x86_64_gnullvm 0.42.2",
- "windows_x86_64_msvc 0.42.2",
+ "windows-targets",
 ]
 
 [[package]]
@@ -698,21 +523,15 @@ version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
 dependencies = [
- "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_gnullvm",
  "windows_aarch64_msvc 0.48.5",
  "windows_i686_gnu 0.48.5",
  "windows_i686_msvc 0.48.5",
  "windows_x86_64_gnu 0.48.5",
- "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_gnullvm",
  "windows_x86_64_msvc 0.48.5",
 ]
 
-[[package]]
-name = "windows_aarch64_gnullvm"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
-
 [[package]]
 name = "windows_aarch64_gnullvm"
 version = "0.48.5"
@@ -725,12 +544,6 @@ version = "0.36.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
 
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
-
 [[package]]
 name = "windows_aarch64_msvc"
 version = "0.48.5"
@@ -743,12 +556,6 @@ version = "0.36.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
 
-[[package]]
-name = "windows_i686_gnu"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
-
 [[package]]
 name = "windows_i686_gnu"
 version = "0.48.5"
@@ -761,12 +568,6 @@ version = "0.36.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
 
-[[package]]
-name = "windows_i686_msvc"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
-
 [[package]]
 name = "windows_i686_msvc"
 version = "0.48.5"
@@ -779,24 +580,12 @@ version = "0.36.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
 
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
-
 [[package]]
 name = "windows_x86_64_gnu"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
 
-[[package]]
-name = "windows_x86_64_gnullvm"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
-
 [[package]]
 name = "windows_x86_64_gnullvm"
 version = "0.48.5"
@@ -809,12 +598,6 @@ version = "0.36.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
 
-[[package]]
-name = "windows_x86_64_msvc"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
-
 [[package]]
 name = "windows_x86_64_msvc"
 version = "0.48.5"
diff --git a/Cargo.toml b/Cargo.toml
index 40b7bcb..95389b9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -14,7 +14,6 @@ codegen-units = 1
 
 [dependencies]
 hyper = { version = "0.14.27", default-features = false, features = ["http1", "server", "runtime"] }
-linemux = "0.3.0"
 path-slash = "0.2.1"
 prometheus-client = "0.21.2"
-tokio = { version = "1.32.0", features = ["rt", "macros", "signal"] }
+tokio = { version = "1.32.0", features = ["io-util", "process", "rt", "macros", "signal"] }
diff --git a/src/log_watcher.rs b/src/log_watcher.rs
index eb7e3c3..90f509a 100644
--- a/src/log_watcher.rs
+++ b/src/log_watcher.rs
@@ -1,10 +1,8 @@
-use std::collections::HashMap;
-use std::io;
-use std::io::{Error, ErrorKind};
 use std::path::PathBuf;
+use std::process::Stdio;
 
-use linemux::{Line, MuxedLines};
-use tokio::sync::mpsc::UnboundedSender;
+use tokio::io::{AsyncBufReadExt, BufReader};
+use tokio::process::Command;
 
 use crate::ApacheMetrics;
 use crate::log_file_pattern::LogFilePath;
@@ -15,98 +13,126 @@ enum LogFileKind {
 	Error,
 }
 
-struct LogFileInfo<'a> {
-	pub kind: LogFileKind,
-	pub label: &'a String,
+struct LogFile {
+	pub path: PathBuf,
+	pub metadata: LogFileMetadata,
 }
 
-impl<'a> LogFileInfo<'a> {
+struct LogFileMetadata {
+	pub kind: LogFileKind,
+	pub label: String,
+}
+
+impl LogFileMetadata {
 	fn get_label_set(&self) -> [(&'static str, String); 1] {
 		[("file", self.label.clone())]
 	}
 }
 
-pub async fn watch_logs_task(access_log_files: Vec<LogFilePath>, error_log_files: Vec<LogFilePath>, metrics: ApacheMetrics, shutdown_send: UnboundedSender<()>) {
-	if let Err(error) = watch_logs(access_log_files, error_log_files, metrics).await {
-		println!("[LogWatcher] Error reading logs: {}", error);
-		shutdown_send.send(()).unwrap();
+pub async fn start_log_watcher(access_log_files: Vec<LogFilePath>, error_log_files: Vec<LogFilePath>, metrics: ApacheMetrics) -> bool {
+	let mut watcher = LogWatcher::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 LogWatcher<'a> {
-	reader: MuxedLines,
-	files: HashMap<PathBuf, LogFileInfo<'a>>,
+struct LogWatcher {
+	files: Vec<LogFile>,
 }
 
-impl<'a> LogWatcher<'a> {
-	fn new() -> io::Result<LogWatcher<'a>> {
-		Ok(LogWatcher {
-			reader: MuxedLines::new()?,
-			files: HashMap::new(),
-		})
+impl LogWatcher {
+	fn new() -> LogWatcher {
+		LogWatcher { files: Vec::new() }
 	}
 	
 	fn count_files_of_kind(&self, kind: LogFileKind) -> usize {
-		return self.files.values().filter(|info| info.kind == kind).count();
+		return self.files.iter().filter(|info| info.metadata.kind == kind).count();
 	}
 	
-	async fn add_file(&mut self, log_file: &'a LogFilePath, kind: LogFileKind) -> io::Result<()> {
-		let lookup_key = self.reader.add_file(&log_file.path).await?;
-		self.files.insert(lookup_key, LogFileInfo { kind, label: &log_file.label });
-		Ok(())
+	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(LogFile { path, metadata });
 	}
 	
-	async fn start_watching(&mut self, metrics: &ApacheMetrics) -> io::Result<()> {
+	async fn start(self, metrics: &ApacheMetrics) -> bool {
 		if self.files.is_empty() {
 			println!("[LogWatcher] No log files provided.");
-			return Err(Error::from(ErrorKind::Unsupported));
+			return false;
 		}
 		
 		println!("[LogWatcher] Watching {} access log file(s) and {} error log file(s).", self.count_files_of_kind(LogFileKind::Access), self.count_files_of_kind(LogFileKind::Error));
 		
-		for metadata in self.files.values() {
+		for file in self.files.into_iter() {
+			let metadata = file.metadata;
 			let label_set = metadata.get_label_set();
 			let _ = metrics.requests_total.get_or_create(&label_set);
 			let _ = metrics.errors_total.get_or_create(&label_set);
+			
+			let command = Command::new("tail")
+				.arg("-q") // Don't print file names.
+				.arg("-F") // Follow rotations.
+				.arg("-n").arg("0") // Start from end.
+				.arg(&file.path)
+				.env_clear()
+				.stdin(Stdio::null())
+				.stdout(Stdio::piped())
+				.stderr(Stdio::null())
+				.spawn();
+			
+			let mut process = match command {
+				Ok(process) => process,
+				Err(error) => {
+					println!("[LogWatcher] Error spawning tail process for file \"{}\": {}", file.path.to_string_lossy(), error);
+					return false;
+				}
+			};
+			
+			let stdout = match process.stdout.take() {
+				Some(stdout) => stdout,
+				None => {
+					println!("[LogWatcher] No output handle in tail process for file: {}", file.path.to_string_lossy());
+					return false;
+				}
+			};
+			
+			let mut output_reader = BufReader::new(stdout).lines();
+			let metrics = metrics.clone();
+			
+			tokio::spawn(async move {
+				loop {
+					match output_reader.next_line().await {
+						Ok(maybe_line) => match maybe_line {
+							Some(line) => handle_line(&metadata, line, &metrics),
+							None => break,
+						},
+						Err(e) => {
+							println!("[LogWatcher] Error reading from file \"{}\": {}", metadata.label, e);
+							break;
+						}
+					}
+				}
+			});
 		}
 		
-		loop {
-			if let Some(event) = self.reader.next_line().await? {
-				self.handle_line(event, metrics);
-			}
-		}
-	}
-	
-	fn handle_line(&mut self, event: Line, metrics: &ApacheMetrics) {
-		match self.files.get(event.source()) {
-			Some(metadata) => {
-				let label = metadata.label;
-				let (kind, family) = match metadata.kind {
-					LogFileKind::Access => ("access log", &metrics.requests_total),
-					LogFileKind::Error => ("error log", &metrics.errors_total),
-				};
-				
-				println!("[LogWatcher] Received {} line from \"{}\": {}", kind, label, event.line());
-				family.get_or_create(&metadata.get_label_set()).inc();
-			}
-			None => {
-				println!("[LogWatcher] Received line from unknown file: {}", event.source().display());
-			}
-		}
+		true
 	}
 }
 
-async fn watch_logs(access_log_files: Vec<LogFilePath>, error_log_files: Vec<LogFilePath>, metrics: ApacheMetrics) -> io::Result<()> {
-	let mut watcher = LogWatcher::new()?;
+fn handle_line(metadata: &LogFileMetadata, line: String, metrics: &ApacheMetrics) {
+	let (kind, family) = match metadata.kind {
+		LogFileKind::Access => ("access log", &metrics.requests_total),
+		LogFileKind::Error => ("error log", &metrics.errors_total),
+	};
 	
-	for log_file in &access_log_files {
-		watcher.add_file(log_file, LogFileKind::Access).await?;
-	}
-	
-	for log_file in &error_log_files {
-		watcher.add_file(log_file, LogFileKind::Error).await?;
-	}
-	
-	watcher.start_watching(&metrics).await?;
-	Ok(())
+	println!("[LogWatcher] Received {} line from \"{}\": {}", kind, metadata.label, line);
+	family.get_or_create(&metadata.get_label_set()).inc();
 }
diff --git a/src/main.rs b/src/main.rs
index 1c64274..a305442 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -5,11 +5,10 @@ use std::str::FromStr;
 use std::sync::Mutex;
 
 use tokio::signal;
-use tokio::sync::mpsc;
 
 use crate::apache_metrics::ApacheMetrics;
 use crate::log_file_pattern::{LogFilePath, parse_log_file_pattern_from_env};
-use crate::log_watcher::watch_logs_task;
+use crate::log_watcher::start_log_watcher;
 use crate::web_server::WebServer;
 
 mod apache_metrics;
@@ -79,22 +78,21 @@ async fn main() -> ExitCode {
 	};
 	
 	let (metrics_registry, metrics) = ApacheMetrics::new();
-	let (shutdown_send, mut shutdown_recv) = mpsc::unbounded_channel();
 	
-	tokio::spawn(watch_logs_task(access_log_files, error_log_files, metrics.clone(), shutdown_send.clone()));
-	tokio::spawn(server.serve(Mutex::new(metrics_registry)));
-	
-	drop(shutdown_send);
-	
-	tokio::select! {
-		_ = signal::ctrl_c() => {
-			println!("Received CTRL-C, shutting down...")
-		}
-		
-		_ = shutdown_recv.recv() => {
-			println!("Shutting down...");
-		}
+	if !start_log_watcher(access_log_files, error_log_files, metrics).await {
+		return ExitCode::FAILURE;
 	}
 	
-	ExitCode::SUCCESS
+	tokio::spawn(server.serve(Mutex::new(metrics_registry)));
+	
+	match signal::ctrl_c().await {
+		Ok(_) => {
+			println!("Received CTRL-C, shutting down...");
+			ExitCode::SUCCESS
+		}
+		Err(e) => {
+			println!("Error registering CTRL-C handler: {}", e);
+			ExitCode::FAILURE
+		}
+	}
 }