diff --git a/Cargo.lock b/Cargo.lock
index 850a71c53174102dfcd5684dcebda283c89c1b3a..04af08ed5c72eb3e9c25a3be8d5af517ce097c6a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -184,6 +184,19 @@ version = "0.1.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
 
+[[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 = "ci_info"
 version = "0.10.2"
@@ -440,6 +453,8 @@ dependencies = [
  "dubp-wallet",
  "dubp-wot",
  "flate2",
+ "flexi_logger",
+ "log",
  "neon",
  "neon-build",
  "neon-serde",
@@ -523,6 +538,19 @@ dependencies = [
  "miniz_oxide",
 ]
 
+[[package]]
+name = "flexi_logger"
+version = "0.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85227d9628d00dc712da528ecf261a81a39bab0c061fb9d76811cc63d8ae8f05"
+dependencies = [
+ "chrono",
+ "flate2",
+ "glob",
+ "log",
+ "thiserror",
+]
+
 [[package]]
 name = "fsio"
 version = "0.1.3"
@@ -555,7 +583,7 @@ checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6"
 dependencies = [
  "cfg-if",
  "libc",
- "wasi",
+ "wasi 0.9.0+wasi-snapshot-preview1",
  "wasm-bindgen",
 ]
 
@@ -565,6 +593,12 @@ version = "0.22.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724"
 
+[[package]]
+name = "glob"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
+
 [[package]]
 name = "hashbrown"
 version = "0.9.1"
@@ -1241,6 +1275,17 @@ dependencies = [
  "lazy_static",
 ]
 
+[[package]]
+name = "time"
+version = "0.1.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
+dependencies = [
+ "libc",
+ "wasi 0.10.0+wasi-snapshot-preview1",
+ "winapi",
+]
+
 [[package]]
 name = "toml"
 version = "0.5.6"
@@ -1316,6 +1361,12 @@ version = "0.9.0+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
 
+[[package]]
+name = "wasi"
+version = "0.10.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
+
 [[package]]
 name = "wasm-bindgen"
 version = "0.2.60"
diff --git a/app/lib/logger.ts b/app/lib/logger.ts
index 7b8a99d31c57e93a9c6c8f8f369904d9a177edc2..781b7f41d8f3e21f7188261b538529d426f556c1 100644
--- a/app/lib/logger.ts
+++ b/app/lib/logger.ts
@@ -11,157 +11,58 @@
 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 // GNU Affero General Public License for more details.
 
-import * as moment from "moment";
-import { Directory } from "./system/directory";
+import { format } from "util";
+import { RustLogger } from "../../neon/native";
 
-const path = require("path");
-const winston = require("winston");
+export class Logger {
+  logger: RustLogger | null = null;
 
-/***************
- * CALLBACK LOGGER
- ***************/
+  constructor() {}
 
-const util = require("util");
-
-const CallbackLogger: any = (winston.transports.CallbackLogger = function (
-  options: any
-) {
-  this.name = "customLogger";
-  this.level = options.level || "info";
-  this.callback = options.callback;
-  this.timestamp = options.timestamp;
-});
-
-util.inherits(CallbackLogger, winston.Transport);
-
-CallbackLogger.prototype.log = function (
-  level: string,
-  msg: string,
-  meta: any,
-  callback: any
-) {
-  this.callback(level, msg, this.timestamp());
-  callback(null, true);
-};
-
-/***************
- * NORMAL LOGGER
- ***************/
-
-const customLevels = {
-  levels: {
-    error: 0,
-    warn: 1,
-    info: 2,
-    debug: 3,
-    trace: 4,
-    query: 5,
-  },
-  colors: {
-    error: "red",
-    warn: "yellow",
-    info: "green",
-    debug: "cyan",
-    trace: "cyan",
-    query: "grey",
-  },
-};
-
-// create the logger
-const logger = new winston.Logger({
-  level: "trace",
-  levels: customLevels.levels,
-  handleExceptions: false,
-  colors: customLevels.colors,
-  transports: [
-    // setup console logging
-    new winston.transports.Console({
-      level: "trace",
-      levels: customLevels.levels,
-      handleExceptions: false,
-      colorize: true,
-      timestamp: function () {
-        return moment().format();
-      },
-    }),
-  ],
-});
-
-// Singletons
-let loggerAttached = false;
-logger.addCallbackLogs = (callbackForLog: any) => {
-  if (!loggerAttached) {
-    loggerAttached = true;
-    logger.add(CallbackLogger, {
-      callback: callbackForLog,
-      level: "trace",
-      levels: customLevels.levels,
-      handleExceptions: false,
-      colorize: true,
-      timestamp: function () {
-        return moment().format();
-      },
-    });
+  initLogger(home: string, level: string | undefined) {
+    if (this.logger == null) {
+      this.logger = new RustLogger(home, level || "info");
+    }
   }
-};
 
-// Singletons
-let loggerHomeAttached = false;
-logger.addHomeLogs = (home: string, level: string) => {
-  if (!muted) {
-    if (loggerHomeAttached) {
-      logger.remove(winston.transports.File);
+  changeLevel(level: string) {
+    if (this.logger != null) {
+      this.logger.changeLevel(level || "info");
     }
-    loggerHomeAttached = true;
-    logger.add(winston.transports.File, {
-      level: level || "info",
-      levels: customLevels.levels,
-      handleExceptions: false,
-      colorize: true,
-      tailable: true,
-      maxsize: 50 * 1024 * 1024, // 50 MB
-      maxFiles: 3,
-      //zippedArchive: true,
-      json: false,
-      filename: path.join(home, "duniter.log"),
-      timestamp: function () {
-        return moment().format();
-      },
-    });
   }
-};
 
-let muted = false;
-logger.mute = () => {
-  if (!muted) {
-    logger.remove(winston.transports.Console);
-    muted = true;
+  error(format_: any, ...param: any[]) {
+    if (this.logger != null) {
+      this.logger.error(format(format_, ...param));
+    }
   }
-};
-
-logger.unmute = () => {
-  if (muted) {
-    muted = false;
-    logger.add(winston.transports.Console, {
-      level: "trace",
-      levels: customLevels.levels,
-      handleExceptions: false,
-      colorize: true,
-      timestamp: function () {
-        return moment().format();
-      },
-    });
+  warn(format_: any, ...param: any[]) {
+    if (this.logger != null) {
+      this.logger.warn(format(format_, ...param));
+    }
+  }
+  info(format_: any, ...param: any[]) {
+    if (this.logger != null) {
+      this.logger.info(format(format_, ...param));
+    }
+  }
+  debug(format_: any, ...param: any[]) {
+    if (this.logger != null) {
+      this.logger.debug(format(format_, ...param));
+    }
   }
-};
+  trace(format_: any, ...param: any[]) {
+    if (this.logger != null) {
+      this.logger.trace(format(format_, ...param));
+    }
+  }
+}
 
-/**
- * Default logging path
- */
-logger.addHomeLogs(Directory.INSTANCE_HOME);
+const logger = new Logger();
 
 /**
  * Convenience function to get logger directly
  */
-export function NewLogger(name?: string) {
+export function NewLogger(name?: string): Logger {
   return logger;
 }
diff --git a/app/modules/crawler/index.ts b/app/modules/crawler/index.ts
index 77a3f2d7b9af186fbc024b78a100210732e9fb1a..d6c26eb090c485b78014ddb7888af3c82c447878 100644
--- a/app/modules/crawler/index.ts
+++ b/app/modules/crawler/index.ts
@@ -417,6 +417,7 @@ export const CrawlerDependency = {
                 server.conf.currency,
                 res,
                 server,
+                false,
                 logger
               );
             } catch (e) {
diff --git a/app/modules/crawler/lib/sync.ts b/app/modules/crawler/lib/sync.ts
index 69d073a23e107ee909aad9db96dbb52f4246cae0..907cce5ceeb32274642d5e8b4377316c760967ce 100644
--- a/app/modules/crawler/lib/sync.ts
+++ b/app/modules/crawler/lib/sync.ts
@@ -94,7 +94,7 @@ export class Synchroniser extends stream.Duplex {
     this.syncStrategy.setWatcher(this.watcher);
 
     if (interactive) {
-      this.logger.mute();
+      this.logger.changeLevel("OFF");
     }
   }
 
diff --git a/index.ts b/index.ts
index 3d7418c598c1755745fd9e173948afd80568f221..c0a4b0623db978716e9971ed4a6126297a067129 100644
--- a/index.ts
+++ b/index.ts
@@ -29,6 +29,7 @@ import {Underscore} from "./app/lib/common-libs/underscore"
 import {CliCommand, DuniterDependency, DuniterModule} from "./app/modules/DuniterModule"
 import {ProgramOptions} from "./app/lib/common-libs/programOptions"
 import {ExitCodes} from "./app/lib/common-libs/exit-codes"
+import { NewLogger } from "./app/lib/logger"
 
 const path = require('path');
 const constants = require('./app/lib/constants');
@@ -299,13 +300,9 @@ export class Stack {
     const dbHome = program.home;
     const home = Directory.getHome(dbName, dbHome);
 
-    if (command.logs === false) {
-      logger.mute();
-    }
-
     // Add log files for this instance (non-memory instances only)
-    if (!program.memory) {
-      logger.addHomeLogs(home, program.loglevel);
+    if (!program.memory && command.logs !== false) {
+      NewLogger().initLogger(home, program.loglevel);
     }
 
     const server = new Server(home, program.memory === true, commandLineConf(program));
@@ -370,7 +367,7 @@ export class Stack {
       // Eventually change the log level
       // Add log files for this instance (non-memory instances only)
       if (!program.memory) {
-        logger.addHomeLogs(home, conf.loglevel);
+        logger.changeLevel(conf.loglevel);
       }
 
       // Auto-configuration default
diff --git a/neon/native/Cargo.toml b/neon/native/Cargo.toml
index 487c7d8222800808c0570a79803766b66df9dcb1..60629f759324e0e7af77d0268ac258c3ba46dfdb 100644
--- a/neon/native/Cargo.toml
+++ b/neon/native/Cargo.toml
@@ -23,6 +23,8 @@ dubp-documents-parser = { version = "0.25.0" }
 dubp-wallet = { version = "0.25.0" }
 dubp-wot = { path = "../../rust-libs/dubp-wot" }
 flate2 = "1.0.16"
+flexi_logger = { version = "=0.16.0", default-features = false, features = ["compress"] }
+log = "0.4.11"
 neon = "0.4.0"
 neon-serde = "0.4.0"
 
diff --git a/neon/native/index.d.ts b/neon/native/index.d.ts
index abec083830de1861fac0071c57ba576254c20ad0..3493b0eab5b9cd4005f1ba227528f4ea2a00173f 100644
--- a/neon/native/index.d.ts
+++ b/neon/native/index.d.ts
@@ -1,6 +1,7 @@
 /* tslint:disable */
 
 import * as _crypto from './crypto';
+import * as _logger from './logger';
 import * as _transactions from './transaction';
 import * as _wot from './wot';
 
@@ -10,6 +11,8 @@ export import seedToSecretKey = _crypto.seedToSecretKey;
 export import sha256 = _crypto.sha256;
 export import verify = _crypto.verify;
 
+export import RustLogger = _logger.RustLogger;
+
 export import TransactionDTOV10 = _transactions.TransactionDTOV10;
 export import rawTxParseAndVerify = _transactions.rawTxParseAndVerify;
 export import sourceIsUnlockable = _transactions.sourceIsUnlockable;
diff --git a/neon/native/logger.d.ts b/neon/native/logger.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d0e0a4226a0adb8622dc6210bb5fd8bf3ad86f0a
--- /dev/null
+++ b/neon/native/logger.d.ts
@@ -0,0 +1,15 @@
+/* tslint:disable */
+
+export class RustLogger {
+
+    constructor(home: string, level: string);
+
+    changeLevel(level: string): void;
+    error(s: string): void;
+    warn(s: string): void;
+    info(s: string): void;
+    debug(s: string): void;
+    trace(s: string): void;
+}
+
+
diff --git a/neon/native/src/lib.rs b/neon/native/src/lib.rs
index bc28e4cd396352312503b23608aa1cfe6f597188..0fde2b572783bb433627466d337bf2dc1e4828a2 100644
--- a/neon/native/src/lib.rs
+++ b/neon/native/src/lib.rs
@@ -26,6 +26,7 @@
 )]
 
 mod crypto;
+mod logger;
 mod transaction;
 mod wot;
 
@@ -50,6 +51,7 @@ register_module!(mut cx, {
     cx.export_function("sha256", crate::crypto::sha256)?;
     cx.export_function("verify", crate::crypto::verify)?;
     cx.export_class::<crate::crypto::JsKeyPair>("Ed25519Signator")?;
+    cx.export_class::<crate::logger::JsLogger>("RustLogger")?;
     cx.export_function(
         "rawTxParseAndVerify",
         crate::transaction::raw_tx_parse_and_verify,
diff --git a/neon/native/src/logger.rs b/neon/native/src/logger.rs
new file mode 100644
index 0000000000000000000000000000000000000000..02960e2193b0778a11ee85145c4edf4aedb2b14f
--- /dev/null
+++ b/neon/native/src/logger.rs
@@ -0,0 +1,125 @@
+//  Copyright (C) 2020 Éloïs SANCHEZ.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+use std::{
+    io::{Error, Write},
+    path::PathBuf,
+    str::FromStr,
+};
+
+use flexi_logger::{
+    Cleanup, Criterion, DeferredNow, Duplicate, Level, LogSpecification, Logger, Naming,
+    ReconfigurationHandle, Record,
+};
+use log::{debug, error, info, trace, warn, LevelFilter};
+use neon::{declare_types, prelude::*};
+
+pub struct RustLogger(ReconfigurationHandle);
+
+impl Drop for RustLogger {
+    fn drop(&mut self) {
+        self.0.shutdown();
+    }
+}
+
+declare_types! {
+    pub class JsLogger for RustLogger {
+        init(mut cx) {
+            let home = cx.argument::<JsString>(0)?.value();
+            let level = LevelFilter::from_str(&cx.argument::<JsString>(1)?.value()).unwrap_or(LevelFilter::Info);
+
+            let logger = Logger::with(
+                LogSpecification::default(level)
+                    .finalize(),
+            )
+            .format(log_format)
+            .log_to_file()
+            .append()
+            .directory(&home)
+            .discriminant("duniter")
+            .rotate(
+                Criterion::Size(10_000_000),
+                Naming::Numbers,
+                Cleanup::KeepLogAndCompressedFiles(3, 7),
+            )
+            .create_symlink(PathBuf::from(home).join("duniter.log"));
+
+            let res = if std::env::var_os("DUNITER_LOG_STDOUT") == Some("no".into()) {
+                logger.start()
+            } else {
+                logger.duplicate_to_stdout(Duplicate::All).start()
+            };
+
+            match res {
+                Ok(logger_handle) => Ok(RustLogger(logger_handle)),
+                Err(e) => cx.throw_error(format!("Fail to init logger: {}", e)),
+            }
+        }
+        method changeLevel(mut cx) {
+            let level = LevelFilter::from_str(&cx.argument::<JsString>(0)?.value()).unwrap_or(LevelFilter::Info);
+            let mut this = cx.this();
+            {
+                let guard = cx.lock();
+                let mut logger_handle = this.borrow_mut(&guard);
+                logger_handle.0.set_new_spec(LogSpecification::default(level).finalize())
+            }
+
+            Ok(cx.undefined().upcast())
+        }
+        method error(mut cx) {
+            let string = cx.argument::<JsString>(0)?.value();
+            error!("{}", string);
+            Ok(cx.undefined().upcast())
+        }
+        method warn(mut cx) {
+            let string = cx.argument::<JsString>(0)?.value();
+            warn!("{}", string);
+            Ok(cx.undefined().upcast())
+        }
+        method info(mut cx) {
+            let string = cx.argument::<JsString>(0)?.value();
+            info!("{}", string);
+            Ok(cx.undefined().upcast())
+        }
+        method debug(mut cx) {
+            let string = cx.argument::<JsString>(0)?.value();
+            debug!("{}", string);
+            Ok(cx.undefined().upcast())
+        }
+        method trace(mut cx) {
+            let string = cx.argument::<JsString>(0)?.value();
+            trace!("{}", string);
+            Ok(cx.undefined().upcast())
+        }
+    }
+}
+
+fn log_format(w: &mut dyn Write, now: &mut DeferredNow, record: &Record<'_>) -> Result<(), Error> {
+    // 2020-10-04T18:14:11+02:00 - info: text
+    let level = match record.level() {
+        Level::Error => "error",
+        Level::Warn => "warn",
+        Level::Info => "info",
+        Level::Debug => "debug",
+        Level::Trace => "trace",
+    };
+    write!(
+        w,
+        "{} - {}: {}",
+        now.now().format("%Y-%m-%dT%H:%M:%S%:z"),
+        level,
+        &record.args()
+    )
+}
diff --git a/package.json b/package.json
index 31b742349335208a5e70728f4af08d4f74a47a27..c3c21d630d3a880397b029fb5e95a77f37e91fab 100644
--- a/package.json
+++ b/package.json
@@ -28,7 +28,7 @@
     "tsc": "tsc",
     "tscw": "tsc -w",
     "doc": "typedoc --out typedoc/ index.ts app/ --mode file --readme README.md --includeDeclarations --sourcefile-url-prefix \"https://git.duniter.org/nodes/typescript/duniter/blob/loki/\"",
-    "test": "nyc --reporter html mocha",
+    "test": "DUNITER_LOG_STDOUT=no nyc --reporter html mocha",
     "start": "cargo run -- start",
     "build": "./neon/build.sh && cd.. && tsc && cd \"../node_modules/duniter-ui\" && npm install && npm run build",
     "install": "./neon/build.sh",
diff --git a/rust-bins/duniter-launcher/src/daemon.rs b/rust-bins/duniter-launcher/src/daemon.rs
index 2eae921c4ba079010de43bc4735c1f5e7a1b9984..10288641ba5a863ea36580b8ffb5dedc320a87fa 100644
--- a/rust-bins/duniter-launcher/src/daemon.rs
+++ b/rust-bins/duniter-launcher/src/daemon.rs
@@ -16,17 +16,13 @@
 use crate::*;
 
 pub fn start(prod: bool, profile_path: &Path, duniter_js_args: &[String]) -> Result<()> {
-    let stdout = OpenOptions::new()
-        .append(true)
-        .create(true)
-        .open(profile_path.join(LOG_FILE))?;
     let mut duniter_js_command = Command::new(get_node_path()?);
     if prod {
         duniter_js_command.current_dir(DUNITER_JS_CURRENT_DIR);
     }
     let mut child = duniter_js_command
         .args(duniter_js_args)
-        .stdout(stdout)
+        .stdout(Stdio::null())
         .stderr(Stdio::null())
         .spawn()?;
 
diff --git a/rust-bins/duniter-launcher/src/main.rs b/rust-bins/duniter-launcher/src/main.rs
index 7cfe68857dbd3b3f47bd4c8de8294c79e3f45b73..95e7bf2611cbc3141a385cddba3cc4f8e5aeef4b 100644
--- a/rust-bins/duniter-launcher/src/main.rs
+++ b/rust-bins/duniter-launcher/src/main.rs
@@ -39,12 +39,7 @@ use nix::{
     Error,
 };
 use std::{
-    fs::{File, OpenOptions},
-    io::prelude::*,
-    path::Path,
-    path::PathBuf,
-    process::Command,
-    process::Output,
+    fs::File, io::prelude::*, path::Path, path::PathBuf, process::Command, process::Output,
     process::Stdio,
 };
 use structopt::{clap::Shell, StructOpt};
@@ -69,8 +64,8 @@ struct DuniterArgs {
     #[structopt(short, long, parse(from_os_str))]
     home: Option<PathBuf>,
     /// Logs level (If not specified, use the logs level defined in the configuration or INFO by default).
-    #[structopt(short, long, alias("loglevel"), case_insensitive(true), possible_values = &["ERROR", "WARN", "INFO", "DEBUG", "TRACE"])]
-    log: Option<log::Level>,
+    #[structopt(short, long, alias("loglevel"), case_insensitive(true), possible_values = &["OFF", "ERROR", "WARN", "INFO", "DEBUG", "TRACE"])]
+    log: Option<log::LevelFilter>,
     /// Profile name (defauld "duniter_default")
     #[structopt(short, long, alias("mdb"))]
     profile: Option<String>,
diff --git a/test/integration/branches/branches2.ts b/test/integration/branches/branches2.ts
index 2da15a3470efa627260578d81a969b92a7b6e203..ff7a31ccc831a779a3cbeb5ae0713318fb082b48 100644
--- a/test/integration/branches/branches2.ts
+++ b/test/integration/branches/branches2.ts
@@ -26,7 +26,7 @@ import {expectHttpCode, expectJSON} from "../tools/http-expect"
 const rp        = require('request-promise');
 
 if (OtherConstants.MUTE_LOGS_DURING_UNIT_TESTS) {
-  NewLogger().mute();
+  NewLogger().changeLevel("OFF")
 }
 
 // Trace these errors
diff --git a/test/integration/forwarding.ts b/test/integration/forwarding.ts
index 353670519fc0660a891789d4ff54a50d61e1a7db..88b95048f62d11e1dfa553ba30cbcd73c1e8561d 100644
--- a/test/integration/forwarding.ts
+++ b/test/integration/forwarding.ts
@@ -25,7 +25,7 @@ const constants = require('../../app/lib/constants');
 BmaDependency.duniter.methods.noLimit(); // Disables the HTTP limiter
 
 if (constants.MUTE_LOGS_DURING_UNIT_TESTS) {
-  NewLogger().mute()
+  NewLogger().changeLevel("OFF")
 }
 
 describe("Forwarding", function() {