diff --git a/Cargo.lock b/Cargo.lock
index 4f0b516942d1af627d6cad7267d827952c45423f..160ce908761120a4ab35ece5cfbda36cc2734207 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -119,6 +119,7 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
 name = "bitwarden_rs"
 version = "1.0.0"
 dependencies = [
+ "backtrace",
  "chashmap",
  "chrono",
  "data-encoding",
diff --git a/Cargo.toml b/Cargo.toml
index bdd9385203a746b5141fb3c4727f4db458b0ca30..e8187fa329c3adf1de50e3325e01109f49b75c22 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -118,6 +118,9 @@ idna = "0.2.0"
 # CLI argument parsing
 structopt = "0.3.11"
 
+# Logging panics to logfile instead stderr only
+backtrace = "0.3.45"
+
 [patch.crates-io]
 # Use newest ring
 rocket = { git = 'https://github.com/SergioBenitez/Rocket', rev = 'b95b6765e1cc8be7c1e7eaef8a9d9ad940b0ac13' }
diff --git a/src/main.rs b/src/main.rs
index aeda5ba2d641b7316e839ce7ab56ff886af0b648..a8efb1d71ff9a06c2ec21604633bdffb3f40dbab 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -20,12 +20,14 @@ extern crate derive_more;
 #[macro_use]
 extern crate num_derive;
 
+extern crate backtrace;
+
 use std::{
     fs::create_dir_all,
     path::Path,
     process::{exit, Command},
     str::FromStr,
-    panic,
+    panic, thread, fmt // For panic logging
 };
 
 #[macro_use]
@@ -43,6 +45,16 @@ pub use error::{Error, MapResult};
 
 use structopt::StructOpt;
 
+// Used for catching panics and log them to file instead of stderr
+use backtrace::Backtrace;
+struct Shim(Backtrace);
+
+impl fmt::Debug for Shim {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        write!(fmt, "\n{:?}", self.0)
+    }
+}
+
 #[derive(Debug, StructOpt)]
 #[structopt(name = "bitwarden_rs", about = "A Bitwarden API server written in Rust")]
 struct Opt {
@@ -145,7 +157,40 @@ fn init_logging(level: log::LevelFilter) -> Result<(), fern::InitError> {
 
     // Catch panics and log them instead of default output to StdErr
     panic::set_hook(Box::new(|info| {
-        warn!("[PANIC] {}", info);
+        let backtrace = Backtrace::new();
+
+        let thread = thread::current();
+        let thread = thread.name().unwrap_or("unnamed");
+
+        let msg = match info.payload().downcast_ref::<&'static str>() {
+            Some(s) => *s,
+            None => match info.payload().downcast_ref::<String>() {
+                Some(s) => &**s,
+                None => "Box<Any>",
+            },
+        };
+
+        match info.location() {
+            Some(location) => {
+                error!(
+                    target: "panic", "thread '{}' panicked at '{}': {}:{}{:?}",
+                    thread,
+                    msg,
+                    location.file(),
+                    location.line(),
+                    Shim(backtrace)
+                );
+            }
+            None => {
+                error!(
+                    target: "panic",
+                    "thread '{}' panicked at '{}'{:?}",
+                    thread,
+                    msg,
+                    Shim(backtrace)
+                )
+            }
+        }
     }));
 
     Ok(())