diff --git a/Cargo.lock b/Cargo.lock
index cfbd52eca049b23a62694a0fc2040366b131b4c9..668bb14eb22dba4b05d1491a30172b3169e6677b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -65,6 +65,27 @@ dependencies = [
  "version_check",
 ]
 
+[[package]]
+name = "aho-corasick"
+version = "0.7.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.51"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b26702f315f53b6071259e15dd9d64528213b44d61de1ec926eca7715d62203"
+
+[[package]]
+name = "arc-swap"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dabe5a181f83789739c194cbe5a897dde195078fac08568d09221fd6137a7ba8"
+
 [[package]]
 name = "assign"
 version = "1.1.1"
@@ -199,6 +220,19 @@ version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "17cc5e6b5ab06331c33589842070416baa137e8b0eb912b008cfd4a78ada7919"
 
+[[package]]
+name = "chrono"
+version = "0.4.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
+dependencies = [
+ "libc",
+ "num-integer",
+ "num-traits",
+ "time",
+ "winapi",
+]
+
 [[package]]
 name = "cipher"
 version = "0.3.0"
@@ -368,6 +402,17 @@ dependencies = [
  "const-oid",
 ]
 
+[[package]]
+name = "derivative"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
+dependencies = [
+ "proc-macro2 1.0.33",
+ "quote 1.0.10",
+ "syn 1.0.82",
+]
+
 [[package]]
 name = "digest"
 version = "0.9.0"
@@ -386,6 +431,12 @@ dependencies = [
  "rand 0.8.4",
 ]
 
+[[package]]
+name = "dtoa"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0"
+
 [[package]]
 name = "ed25519"
 version = "1.3.0"
@@ -773,6 +824,12 @@ version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
 
+[[package]]
+name = "humantime"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
+
 [[package]]
 name = "hyper"
 version = "0.14.16"
@@ -932,6 +989,12 @@ version = "0.2.111"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8e167738f1866a7ec625567bae89ca0d44477232a4f7c52b1c7f2adc2c98804f"
 
+[[package]]
+name = "linked-hash-map"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
+
 [[package]]
 name = "lock_api"
 version = "0.4.5"
@@ -948,6 +1011,40 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
 dependencies = [
  "cfg-if",
+ "serde",
+]
+
+[[package]]
+name = "log-mdc"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a94d21414c1f4a51209ad204c1776a3d0765002c76c6abcb602a6f09f1e881c7"
+
+[[package]]
+name = "log4rs"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d1572a880d1115ff867396eee7ae2bc924554225e67a0d3c85c745b3e60ca211"
+dependencies = [
+ "anyhow",
+ "arc-swap",
+ "chrono",
+ "derivative",
+ "fnv",
+ "humantime",
+ "libc",
+ "log",
+ "log-mdc",
+ "parking_lot",
+ "regex",
+ "serde",
+ "serde-value",
+ "serde_json",
+ "serde_yaml",
+ "thiserror",
+ "thread-id",
+ "typemap",
+ "winapi",
 ]
 
 [[package]]
@@ -1105,6 +1202,8 @@ dependencies = [
 name = "mgb"
 version = "0.1.0"
 dependencies = [
+ "log",
+ "log4rs",
  "matrix-sdk",
  "rust-ini",
  "tokio",
@@ -1303,6 +1402,15 @@ dependencies = [
  "vcpkg",
 ]
 
+[[package]]
+name = "ordered-float"
+version = "2.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97c9d06878b3a851e8026ef94bf7fef9ba93062cd412601da4d9cf369b1cc62d"
+dependencies = [
+ "num-traits",
+]
+
 [[package]]
 name = "ordered-multimap"
 version = "0.3.1"
@@ -1333,7 +1441,7 @@ dependencies = [
  "cfg-if",
  "instant",
  "libc",
- "redox_syscall",
+ "redox_syscall 0.2.10",
  "smallvec",
  "winapi",
 ]
@@ -1611,6 +1719,12 @@ dependencies = [
  "num_cpus",
 ]
 
+[[package]]
+name = "redox_syscall"
+version = "0.1.57"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
+
 [[package]]
 name = "redox_syscall"
 version = "0.2.10"
@@ -1620,6 +1734,23 @@ dependencies = [
  "bitflags",
 ]
 
+[[package]]
+name = "regex"
+version = "1.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
+
 [[package]]
 name = "remove_dir_all"
 version = "0.5.3"
@@ -1975,6 +2106,16 @@ dependencies = [
  "serde_derive",
 ]
 
+[[package]]
+name = "serde-value"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c"
+dependencies = [
+ "ordered-float",
+ "serde",
+]
+
 [[package]]
 name = "serde_derive"
 version = "1.0.131"
@@ -2009,6 +2150,18 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "serde_yaml"
+version = "0.8.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8c608a35705a5d3cdc9fbe403147647ff34b921f8e833e49306df898f9b20af"
+dependencies = [
+ "dtoa",
+ "indexmap",
+ "serde",
+ "yaml-rust",
+]
+
 [[package]]
 name = "sha2"
 version = "0.9.8"
@@ -2124,7 +2277,7 @@ dependencies = [
  "cfg-if",
  "libc",
  "rand 0.8.4",
- "redox_syscall",
+ "redox_syscall 0.2.10",
  "remove_dir_all",
  "winapi",
 ]
@@ -2149,6 +2302,17 @@ dependencies = [
  "syn 1.0.82",
 ]
 
+[[package]]
+name = "thread-id"
+version = "3.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7fbf4c9d56b320106cd64fd024dadfa0be7cb4706725fc44a7d7ce952d820c1"
+dependencies = [
+ "libc",
+ "redox_syscall 0.1.57",
+ "winapi",
+]
+
 [[package]]
 name = "tiff"
 version = "0.6.1"
@@ -2160,6 +2324,16 @@ dependencies = [
  "weezl",
 ]
 
+[[package]]
+name = "time"
+version = "0.1.43"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
 [[package]]
 name = "tinyvec"
 version = "1.5.1"
@@ -2284,12 +2458,27 @@ dependencies = [
  "tracing",
 ]
 
+[[package]]
+name = "traitobject"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079"
+
 [[package]]
 name = "try-lock"
 version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
 
+[[package]]
+name = "typemap"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "653be63c80a3296da5551e1bfd2cca35227e13cdd08c6668903ae2f4f77aa1f6"
+dependencies = [
+ "unsafe-any",
+]
+
 [[package]]
 name = "typenum"
 version = "1.14.0"
@@ -2339,6 +2528,15 @@ dependencies = [
  "subtle",
 ]
 
+[[package]]
+name = "unsafe-any"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f30360d7979f5e9c6e6cea48af192ea8fab4afb3cf72597154b8f08935bc9c7f"
+dependencies = [
+ "traitobject",
+]
+
 [[package]]
 name = "url"
 version = "2.2.2"
@@ -2514,6 +2712,15 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "yaml-rust"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
+dependencies = [
+ "linked-hash-map",
+]
+
 [[package]]
 name = "zeroize"
 version = "1.4.3"
diff --git a/Cargo.toml b/Cargo.toml
index 5f453b906d907ae02dda3556af4e0c3885af01c3..8068eed19b16a102d18cab5c4c07d6250a95b568 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -14,3 +14,5 @@ matrix-sdk = "0.4.1"
 tokio      = { version = "1", features = ["rt", "rt-multi-thread", "sync", "macros"] }
 rust-ini   = "0.17"
 url        = "2.2.2"
+log        = "0.4"
+log4rs     = "1.0"
diff --git a/src/config.rs b/src/config.rs
index ee574cfb9be9648ae6f3eecfa6809704faf139c3..c98d967b8c6f09cda9df329c5afd33478c0c3f5b 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -1,7 +1,9 @@
 #![allow(dead_code)]
 
 use ini::{Error::Io as IniErrIo, Error::Parse as IniErrParse, Ini};
+use log::{error, info, warn, LevelFilter};
 use url::Url;
+use std::{ process::abort, path::Path, env };
 
 pub struct Config {
     pub display_name: String,  // The display name to set for the bot
@@ -10,6 +12,24 @@ pub struct Config {
     pub user_password: String, // The true user name
 }
 
+pub fn setup_logging() {
+    // https://docs.rs/log4rs/latest/log4rs/encode/pattern/index.html
+    let log_stdout = log4rs::append::console::ConsoleAppender::builder()
+        .encoder(Box::new(log4rs::encode::pattern::PatternEncoder::new(
+            "{d(%Y-%m-%d %H:%M:%S)} {h({l} - {m})}{n}",
+        )))
+        .build();
+    let log_config = log4rs::config::Config::builder()
+        .appender(log4rs::config::Appender::builder().build("stdout", Box::new(log_stdout)))
+        .build(
+            log4rs::config::Root::builder()
+                .appender("stdout")
+                .build(LevelFilter::Info),
+        )
+        .unwrap();
+    let _log_handle = log4rs::init_config(log_config).unwrap();
+}
+
 pub fn from_file(file_name: &String) -> Result<Config, String> {
     return match Ini::load_from_file(file_name) {
         Err(IniErrIo(e)) => Err(e.to_string()),
@@ -46,3 +66,40 @@ pub fn write_default(file_name: &String) -> Result<(), String> {
         Err(e) => Err(e.to_string()),
     };
 }
+
+pub fn check_argument_or_abort(should_write_default: bool) {
+    if env::args().len() != 2 {
+        let default_file = env::args()
+            .nth(0)
+            .unwrap()
+            .split(std::path::MAIN_SEPARATOR)
+            .last()
+            .unwrap()
+            .to_string()
+            + ".yml";
+        if Path::new(&default_file).exists() {
+            error!(
+                "Args count is {}, you need to only specify the config file for the bot",
+                env::args().len() - 1
+            );
+            abort();
+        } else if should_write_default {
+            match write_default(&default_file) {
+                Ok(()) => error!(
+                    "Args count is {}, you need to only specify the config file for the bot. \
+                 The default config file was written to '{}'",
+                    env::args().len() - 1,
+                    default_file
+                ),
+                Err(e) => error!(
+                    "Args count is {}, you need to only specify the config file for the bot. \
+                 The default config file '{}' could not be written: {}",
+                    env::args().len() - 1,
+                    default_file,
+                    e
+                ),
+            }
+            abort();
+        }
+    }
+}
diff --git a/src/main.rs b/src/main.rs
index 9d9907673012ee0fe753a75241729dac1509ec71..d3a9be77165dfb5ea64a340b8f96b06406e6e82b 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -4,53 +4,30 @@ mod config;
 mod matrix;
 use crate::config::Config;
 
-use matrix_sdk::Result;
+use log::{error, info, warn};
+use log4rs;
+// use log4rs::config::{Appender, Config, Logger, Root};
+
+use matrix_sdk;
 use std::convert::TryFrom;
 use std::path::Path;
-use std::{env, process::exit};
+use std::{env, process::exit, process::abort};
 
 #[tokio::main]
-async fn main() -> Result<()> {
-    if env::args().len() != 2 {
-        let default_file = env::args()
-            .nth(0)
-            .unwrap()
-            .split(std::path::MAIN_SEPARATOR)
-            .last()
-            .unwrap()
-            .to_string()
-            + ".yml";
-        if Path::new(&default_file).exists() {
-            panic!(
-                "Args count is {}, you need to only specify the config file for the bot",
-                env::args().len() - 1
-            );
-        } else {
-            match config::write_default(&default_file) {
-                Ok(()) => panic!(
-                    "Args count is {}, you need to only specify the config file for the bot. \
-                 The default config file was written to '{}'",
-                    env::args().len() - 1,
-                    default_file
-                ),
-                Err(e) => panic!(
-                    "Args count is {}, you need to only specify the config file for the bot. \
-                 The default config file '{}' could not be written: {}",
-                    env::args().len() - 1,
-                    default_file,
-                    e
-                ),
-            }
-        }
-    }
+async fn main() -> matrix_sdk::Result<()> {
+    config::setup_logging();
+    config::check_argument_or_abort(true);
 
     let config_file = env::args().last().unwrap();
-    println!("Using config file: {}", config_file);
+    info!("Using config file: {}", config_file);
     return match config::from_file(&config_file) {
         Ok(config) => matrix::connect_and_handle(config).await,
-        Err(e) => panic!(
-            "Failed to read config file '{}' with error: {}",
-            config_file, e
-        ),
+        Err(e) => {
+            error!(
+                "Failed to read config file '{}' with error: {}",
+                config_file, e
+            );
+            abort();
+        }
     };
 }
diff --git a/src/matrix.rs b/src/matrix.rs
index 6fbb1c7e38eb92ce33f996612a8ded7a2045e1c3..6ecd4c8b97050d341f5870a6647d956f23c2782d 100644
--- a/src/matrix.rs
+++ b/src/matrix.rs
@@ -1,6 +1,5 @@
 use crate::config::Config;
-use std::convert::TryFrom;
-
+use log::{error, info, warn};
 use matrix_sdk::{
     deserialized_responses::EncryptionInfo,
     room::Room,
@@ -17,6 +16,7 @@ use matrix_sdk::{
     },
     Client, LoopCtrl, Result, RoomInfo, SyncSettings,
 };
+use std::convert::TryFrom;
 
 async fn on_room_message(
     ev: SyncMessageEvent<MessageEventContent>,
@@ -25,27 +25,24 @@ async fn on_room_message(
 ) {
     let sender_id = ev.sender;
     let own_id = room.own_user_id();
-    if sender_id.as_str() == own_id.as_str() { return; }
+    if sender_id.as_str() == own_id.as_str() {
+        return;
+    }
 
     if let Room::Joined(room) = room {
         let room_name = match room.name() {
             Some(n) => "<".to_string() + &n + ">",
             None => "<--none-->".to_string(),
         };
-        let body = match ev.content.msgtype {
+        let _body = match ev.content.msgtype {
             MessageType::Text(TextMessageEventContent { body, .. }) => "Text: ".to_string() + &body,
             _ => "Unsupported type".to_string(),
         };
-        println!(
-            "===\n\
-             JOINED-ROOM: {}\n\
-             ROOM-NAME: {}\n\
-             FROM: {}\n\
-             MSG: {}",
+        info!(
+            "{} a.k.a. {}, from {}",
             room.room_id(),
             room_name,
-            sender_id,
-            body
+            sender_id
         );
         let content =
             AnyMessageEventContent::RoomMessage(MessageEventContent::text_plain("Hello world"));
@@ -55,9 +52,8 @@ async fn on_room_message(
 
 fn log_joined_rooms(client: &Client) {
     for room in client.joined_rooms() {
-        println!("Got joined room: <{}>", room.name().unwrap());
+        info!("Got joined room: <{}>", room.name().unwrap());
     }
-
 }
 
 pub async fn connect_and_handle(config: Config) -> Result<()> {
@@ -72,14 +68,14 @@ pub async fn connect_and_handle(config: Config) -> Result<()> {
             Some(config.display_name.as_str()),
         )
         .await?;
-    println!("Logged as: {}", alice);
+    info!("Logged as: {}", alice);
 
     client.sync_once(SyncSettings::default()).await?;
     log_joined_rooms(&client);
 
     client.register_event_handler(on_room_message).await;
 
-    println!("Entering sync loop");
+    info!("Entering sync loop");
     let token = client.sync_token().await.unwrap();
     let settings = SyncSettings::default().token(token);
     client.sync(settings).await;