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 => "[31merror[39m", + Level::Warn => "[33mwarn[39m", + Level::Info => "[32minfo[39m", + Level::Debug => "[36mdebug[39m", + Level::Trace => "[36mtrace[39m", + }; + 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() {