Commit e7501e19 authored by Éloïs's avatar Éloïs
Browse files

[ref] oxyde logger

parent b2718886
......@@ -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"
......
......@@ -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;
}
......@@ -417,6 +417,7 @@ export const CrawlerDependency = {
server.conf.currency,
res,
server,
false,
logger
);
} catch (e) {
......
......@@ -94,7 +94,7 @@ export class Synchroniser extends stream.Duplex {
this.syncStrategy.setWatcher(this.watcher);
if (interactive) {
this.logger.mute();
this.logger.changeLevel("OFF");
}
}
......
......@@ -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
......
......@@ -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"
......
/* 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;
......
/* 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;
}
......@@ -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,
......
// 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()
)
}
......@@ -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",
......
......@@ -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()?;
......
......@@ -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>,
......
......@@ -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
......
......@@ -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() {
......