feat(tui): improve logging (#83)

* chore(deps): add logging dependencies

* refactor(cli): use log functions for logging

* refactor(core): use log functions for logging

* feat(tui): add logging for tui

* feat(args): add an option for setting the log file for TUI logs

* chore(core): add trace logging

* fix(cli): make log level configurable via RUST_LOG

* chore(log): improve logging

* chore(tui): add logs to specific events

* style(tui): update the colors for the logger widget

* feat(log): allow setting the log level for tui

* chore(config)!: remove the verbose option

* fix(test): update render tui test

* chore(tui): update MSRV for tui

* chore(audit): add configuration file for cargo-audit

* chore: Bump the Rust version in Dockerfile

* feat(log): add target to logs

* docs(readme): update documentation about logging
This commit is contained in:
Orhun Parmaksız 2023-04-24 22:08:43 +02:00 committed by GitHub
parent 80be6c5053
commit 3ace96419c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 860 additions and 86 deletions

8
.cargo/audit.toml Normal file
View file

@ -0,0 +1,8 @@
[advisories]
ignore = [
"RUSTSEC-2020-0071", # `chrono` - Potential segfault in the time crate
]
[output]
quiet = false
deny = ["warnings"]

536
Cargo.lock generated
View file

@ -28,6 +28,15 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "atty" name = "atty"
version = "0.2.14" version = "0.2.14"
@ -67,6 +76,12 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "bumpalo"
version = "3.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
[[package]] [[package]]
name = "byteorder" name = "byteorder"
version = "1.4.3" version = "1.4.3"
@ -79,12 +94,33 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
[[package]]
name = "cc"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.0" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b"
dependencies = [
"iana-time-zone",
"js-sys",
"num-integer",
"num-traits",
"time",
"wasm-bindgen",
"winapi",
]
[[package]] [[package]]
name = "clipboard-win" name = "clipboard-win"
version = "3.1.1" version = "3.1.1"
@ -95,6 +131,16 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "codespan-reporting"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
dependencies = [
"termcolor",
"unicode-width",
]
[[package]] [[package]]
name = "colored" name = "colored"
version = "2.0.0" version = "2.0.0"
@ -138,6 +184,12 @@ dependencies = [
"x11-clipboard", "x11-clipboard",
] ]
[[package]]
name = "core-foundation-sys"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
[[package]] [[package]]
name = "crc32fast" name = "crc32fast"
version = "1.3.2" version = "1.3.2"
@ -190,6 +242,50 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "cxx"
version = "1.0.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93"
dependencies = [
"cc",
"cxxbridge-flags",
"cxxbridge-macro",
"link-cplusplus",
]
[[package]]
name = "cxx-build"
version = "1.0.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b"
dependencies = [
"cc",
"codespan-reporting",
"once_cell",
"proc-macro2",
"quote",
"scratch",
"syn 2.0.15",
]
[[package]]
name = "cxxbridge-flags"
version = "1.0.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb"
[[package]]
name = "cxxbridge-macro"
version = "1.0.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.15",
]
[[package]] [[package]]
name = "dirs-next" name = "dirs-next"
version = "2.0.0" version = "2.0.0"
@ -250,6 +346,40 @@ dependencies = [
"syn 1.0.109", "syn 1.0.109",
] ]
[[package]]
name = "env_logger"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"
dependencies = [
"humantime",
"is-terminal",
"log",
"regex",
"termcolor",
]
[[package]]
name = "errno"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
dependencies = [
"errno-dragonfly",
"libc",
"windows-sys 0.48.0",
]
[[package]]
name = "errno-dragonfly"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
dependencies = [
"cc",
"libc",
]
[[package]] [[package]]
name = "flate2" name = "flate2"
version = "1.0.25" version = "1.0.25"
@ -266,6 +396,15 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "fxhash"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
dependencies = [
"byteorder",
]
[[package]] [[package]]
name = "gethostname" name = "gethostname"
version = "0.2.3" version = "0.2.3"
@ -293,7 +432,7 @@ checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"wasi", "wasi 0.11.0+wasi-snapshot-preview1",
] ]
[[package]] [[package]]
@ -353,6 +492,42 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "hermit-abi"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "iana-time-zone"
version = "0.1.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"windows",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca"
dependencies = [
"cxx",
"cxx-build",
]
[[package]] [[package]]
name = "ignore" name = "ignore"
version = "0.4.20" version = "0.4.20"
@ -370,12 +545,44 @@ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]]
name = "io-lifetimes"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220"
dependencies = [
"hermit-abi 0.3.1",
"libc",
"windows-sys 0.48.0",
]
[[package]]
name = "is-terminal"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f"
dependencies = [
"hermit-abi 0.3.1",
"io-lifetimes",
"rustix",
"windows-sys 0.48.0",
]
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.6" version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
[[package]]
name = "js-sys"
version = "0.3.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730"
dependencies = [
"wasm-bindgen",
]
[[package]] [[package]]
name = "lazy-bytes-cast" name = "lazy-bytes-cast"
version = "5.0.1" version = "5.0.1"
@ -404,6 +611,31 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "link-cplusplus"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5"
dependencies = [
"cc",
]
[[package]]
name = "linux-raw-sys"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b085a4f2cde5781fc4b1717f2e86c62f5cda49de7ba99a7c2eae02b61c9064c"
[[package]]
name = "lock_api"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.17" version = "0.4.17"
@ -492,6 +724,25 @@ dependencies = [
"minimal-lexical", "minimal-lexical",
] ]
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
]
[[package]] [[package]]
name = "num_cpus" name = "num_cpus"
version = "1.15.0" version = "1.15.0"
@ -553,6 +804,29 @@ dependencies = [
"hashbrown", "hashbrown",
] ]
[[package]]
name = "parking_lot"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-sys 0.45.0",
]
[[package]] [[package]]
name = "parseit" name = "parseit"
version = "0.1.2" version = "0.1.2"
@ -680,6 +954,20 @@ dependencies = [
"ordered-multimap", "ordered-multimap",
] ]
[[package]]
name = "rustix"
version = "0.37.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f79bef90eb6d984c72722595b5b1348ab39275a5e5123faca6863bf07d75a4e0"
dependencies = [
"bitflags",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys",
"windows-sys 0.48.0",
]
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.13" version = "1.0.13"
@ -707,6 +995,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "scratch"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.160" version = "1.0.160"
@ -812,7 +1106,9 @@ dependencies = [
name = "systeroid" name = "systeroid"
version = "0.3.2" version = "0.3.2"
dependencies = [ dependencies = [
"env_logger",
"getopts", "getopts",
"log",
"parseit", "parseit",
"systeroid-core", "systeroid-core",
] ]
@ -824,6 +1120,7 @@ dependencies = [
"colored", "colored",
"dirs-next", "dirs-next",
"lazy_static", "lazy_static",
"log",
"parseit", "parseit",
"rayon", "rayon",
"rust-ini", "rust-ini",
@ -840,13 +1137,24 @@ dependencies = [
"colorsys", "colorsys",
"copypasta-ext", "copypasta-ext",
"getopts", "getopts",
"log",
"ratatui", "ratatui",
"systeroid-core", "systeroid-core",
"termion", "termion",
"thiserror", "thiserror",
"tui-logger",
"unicode-width", "unicode-width",
] ]
[[package]]
name = "termcolor"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
dependencies = [
"winapi-util",
]
[[package]] [[package]]
name = "termion" name = "termion"
version = "2.0.1" version = "2.0.1"
@ -889,6 +1197,31 @@ dependencies = [
"once_cell", "once_cell",
] ]
[[package]]
name = "time"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
dependencies = [
"libc",
"wasi 0.10.0+wasi-snapshot-preview1",
"winapi",
]
[[package]]
name = "tui-logger"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b9e708d73f97e548b2d6e75f9931aff915a5b8bbad8fc7c7a63273994af6b5b"
dependencies = [
"chrono",
"fxhash",
"lazy_static",
"log",
"parking_lot",
"ratatui",
]
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.8" version = "1.0.8"
@ -923,12 +1256,72 @@ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.11.0+wasi-snapshot-preview1" version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn 1.0.109",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
[[package]] [[package]]
name = "wayland-client" name = "wayland-client"
version = "0.29.5" version = "0.29.5"
@ -1053,6 +1446,147 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
dependencies = [
"windows-targets 0.48.0",
]
[[package]]
name = "windows-sys"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
"windows-targets 0.42.2",
]
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets 0.48.0",
]
[[package]]
name = "windows-targets"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
dependencies = [
"windows_aarch64_gnullvm 0.42.2",
"windows_aarch64_msvc 0.42.2",
"windows_i686_gnu 0.42.2",
"windows_i686_msvc 0.42.2",
"windows_x86_64_gnu 0.42.2",
"windows_x86_64_gnullvm 0.42.2",
"windows_x86_64_msvc 0.42.2",
]
[[package]]
name = "windows-targets"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
dependencies = [
"windows_aarch64_gnullvm 0.48.0",
"windows_aarch64_msvc 0.48.0",
"windows_i686_gnu 0.48.0",
"windows_i686_msvc 0.48.0",
"windows_x86_64_gnu 0.48.0",
"windows_x86_64_gnullvm 0.48.0",
"windows_x86_64_msvc 0.48.0",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_i686_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
[[package]] [[package]]
name = "x11-clipboard" name = "x11-clipboard"
version = "0.7.1" version = "0.7.1"

View file

@ -3,6 +3,7 @@ members = ["systeroid-core", "systeroid-tui", "systeroid"]
[workspace.dependencies] [workspace.dependencies]
parseit = { version = "0.1.2", features = ["gzip"] } parseit = { version = "0.1.2", features = ["gzip"] }
log = { version = "0.4.17", features = ["std"] }
[profile.dev] [profile.dev]
opt-level = 0 opt-level = 0

View file

@ -1,4 +1,4 @@
FROM rust:1.66.1-alpine3.17 as builder FROM rust:1.69.0-alpine3.17 as builder
WORKDIR /app WORKDIR /app
RUN apk update RUN apk update
RUN apk add --no-cache musl-dev bash git RUN apk add --no-cache musl-dev bash git

View file

@ -77,6 +77,7 @@ Although **systeroid** does not need the parameter section to be specified expli
- [Loading values from the system directories](#loading-values-from-the-system-directories) - [Loading values from the system directories](#loading-values-from-the-system-directories)
- [Searching parameters](#searching-parameters) - [Searching parameters](#searching-parameters)
- [Showing information about parameters](#showing-information-about-parameters) - [Showing information about parameters](#showing-information-about-parameters)
- [Verbose logging](#verbose-logging)
- [TUI](#tui) - [TUI](#tui)
- [Usage](#usage-2) - [Usage](#usage-2)
- [Key Bindings](#key-bindings) - [Key Bindings](#key-bindings)
@ -93,6 +94,7 @@ Although **systeroid** does not need the parameter section to be specified expli
- [Changing the colors](#changing-the-colors) - [Changing the colors](#changing-the-colors)
- [Viewing the parameter documentation](#viewing-the-parameter-documentation) - [Viewing the parameter documentation](#viewing-the-parameter-documentation)
- [Setting the refresh rate](#setting-the-refresh-rate) - [Setting the refresh rate](#setting-the-refresh-rate)
- [Logging](#logging)
- [Configuration](#configuration) - [Configuration](#configuration)
- [Resources](#resources) - [Resources](#resources)
- [References](#references) - [References](#references)
@ -398,6 +400,20 @@ It is also possible to retrieve information about multiple parameters:
systeroid -E --pattern '.*ipv4.*' --no-pager systeroid -E --pattern '.*ipv4.*' --no-pager
``` ```
#### Verbose logging
`--verbose` flag can be used to enable verbose logging:
```sh
systeroid --verbose
```
Also, `RUST_LOG` environment variable can be set accordingly to filter based on different log levels.
```sh
RUST_LOG=trace systeroid
```
## TUI ## TUI
### Usage ### Usage
@ -440,6 +456,7 @@ systeroid-tui [options]
| <kbd>enter</kbd> | select / set parameter value | | <kbd>enter</kbd> | select / set parameter value |
| <kbd>s</kbd> | save parameter value | | <kbd>s</kbd> | save parameter value |
| <kbd>c</kbd> | copy to clipboard | | <kbd>c</kbd> | copy to clipboard |
| <kbd>ctrl-l</kbd>, <kbd>f2</kbd> | show logs |
| <kbd>r</kbd>, <kbd>f5</kbd> | refresh | | <kbd>r</kbd>, <kbd>f5</kbd> | refresh |
| <kbd>esc</kbd> | cancel / exit | | <kbd>esc</kbd> | cancel / exit |
| <kbd>q</kbd>, <kbd>ctrl-c/ctrl-d</kbd> | exit | | <kbd>q</kbd>, <kbd>ctrl-c/ctrl-d</kbd> | exit |
@ -524,15 +541,16 @@ Press <kbd>:</kbd> to open the command prompt for running a command. Available c
| Command | Description | | Command | Description |
| ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | | ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `:help` | Show help | | `:help` | show help |
| `:search` | Enable search | | `:search` | enable search |
| `:select` | Select the current parameter in the list | | `:select` | select the current parameter in the list |
| `:set <name> <value>` | Set parameter value | | `:set <name> <value>` | set parameter value |
| `:save <name> <value>` | Save parameter value to file | | `:save <name> <value>` | save parameter value to file |
| `:scroll [area] [direction] <amount>` | Scroll the list or text<br>- areas: `list`, `docs`, `section`<br>- directions: `up`, `down`, `top`, `bottom`, `right`, `left` | | `:scroll [area] [direction] <amount>` | scroll the list or text<br>- areas: `list`, `docs`, `section`<br>- directions: `up`, `down`, `top`, `bottom`, `right`, `left` |
| `:copy` | Copy to clipboard | | `:copy` | copy to clipboard |
| `:refresh` | Refresh values | | `:logs` | show logs |
| `:quit`, `:q` | Quit | | `:refresh` | refresh values |
| `:quit`, `:q` | quit |
#### Copying to clipboard #### Copying to clipboard
@ -570,6 +588,55 @@ It is possible to specify a value in milliseconds via `--tick-rate` argument for
systeroid-tui --tick-rate 500 systeroid-tui --tick-rate 500
``` ```
#### Logging
To view the log messages, press <kbd>ctrl-l</kbd>. It will bring up a pane in the TUI for analyzing the logs:
![Logs](assets/systeroid-tui-logs.gif)
This pane consists of two parts. Left is the target selector and on the right side the logging messages view scrolling up.
The target selector controls:
- Capturing of log messages by the logger.
- Selection of levels for display in the logging message view.
The two columns have the following meaning:
- Code `EWIDT`: `E` stands for Error, `W` for Warn, and similarly Info, Debug and Trace.
- Inverted characters (EWIDT) are enabled log levels in the view.
- Normal characters show enabled capturing of a log level per target.
- If any of EWIDT are not shown, then the respective log level is not captured.
This logger pane has the following key bindings and they are only activated while the logs are being shown:
| Key | Action |
| ------------------- | -------------------------------------------------------------- |
| <kbd>h</kbd> | toggles target selector widget hidden/visible |
| <kbd>f</kbd> | toggle focus on the selected target only |
| <kbd>up</kbd> | select previous target in target selector widget |
| <kbd>down</kbd> | select next target in target selector widget |
| <kbd>left</kbd> | reduce SHOWN (!) log messages by one level |
| <kbd>right</kbd> | increase SHOWN (!) log messages by one level |
| <kbd>-</kbd> | reduce CAPTURED (!) log messages by one level |
| <kbd>+</kbd> | increase CAPTURED (!) log messages by one level |
| <kbd>pageup</kbd> | enter Page Mode and scroll approx. half page up in log history |
| <kbd>pagedown</kbd> | only in page mode: scroll 10 events down in log history |
| <kbd>escape</kbd> | exit page mode and go back to scrolling mode |
| <kbd>space</kbd> | toggles hiding of targets, which have logfilter set to off |
For saving the logs to a file, you can use the `--log-file` argument:
```sh
systeroid-tui --log-file systeroid.log
```
`RUST_LOG` environment variable can be used to set the log level accordingly.
```sh
RUST_LOG=debug systeroid-tui
```
## Configuration ## Configuration
**systeroid** can be configured with a configuration file that uses the [INI format](https://en.wikipedia.org/wiki/INI_file). It can be specified via `--config` or `SYSTEROID_CONFIG` environment variable. It can also be placed in one of the following global locations: **systeroid** can be configured with a configuration file that uses the [INI format](https://en.wikipedia.org/wiki/INI_file). It can be specified via `--config` or `SYSTEROID_CONFIG` environment variable. It can also be placed in one of the following global locations:

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View file

@ -17,8 +17,6 @@ display_deprecated = false
kernel_docs = "/usr/share/doc/linux" kernel_docs = "/usr/share/doc/linux"
[cli] [cli]
; enable verbose logging
verbose = false
; ignore unknown variable errors ; ignore unknown variable errors
ignore_errors = true ignore_errors = true
; do not print variable after the value is set ; do not print variable after the value is set
@ -59,6 +57,8 @@ tick_rate = 250
no_docs = true no_docs = true
; path for saving the changed kernel parameters ; path for saving the changed kernel parameters
save_path = "/etc/sysctl.conf" save_path = "/etc/sysctl.conf"
; file to save the logs
;log_file = "systeroid.log"
[tui.colors] [tui.colors]
; available colors are defined in https://docs.rs/tui/latest/tui/style/enum.Color.html ; available colors are defined in https://docs.rs/tui/latest/tui/style/enum.Color.html

View file

@ -23,6 +23,9 @@ Use this option to set a custom path for the kernel documentation.
\fB\-\-save\-path\fR <path> \fB\-\-save\-path\fR <path>
Use this option to set the path for saving the changed parameters. Use this option to set the path for saving the changed parameters.
.TP .TP
\fB\-l\fR, \fB\-\-log\-file\fR <path>
Use this option to set the file to save the logs.
.TP
\fB\-s\fR, \fB\-\-section\fR <section> \fB\-s\fR, \fB\-\-section\fR <section>
Use this option to set the section to filter. Use this option to set the section to filter.
.HP .HP
@ -61,6 +64,8 @@ systeroid-tui \-D /usr/share/doc/linux
.br .br
systeroid-tui \-\-save-path 99-local.conf systeroid-tui \-\-save-path 99-local.conf
.br .br
systeroid-tui \-\-log-file systeroid.log
.br
systeroid-tui -n systeroid-tui -n
.SH KEY BINDINGS .SH KEY BINDINGS

View file

@ -12,6 +12,8 @@ edition = "2021"
rust-version = "1.64.0" rust-version = "1.64.0"
[dependencies] [dependencies]
parseit.workspace = true
log.workspace = true
sysctl = "0.5.4" sysctl = "0.5.4"
thiserror = "1.0.40" thiserror = "1.0.40"
lazy_static = "1.4.0" lazy_static = "1.4.0"
@ -20,5 +22,4 @@ colored = "2.0.0"
serde = { version = "1.0.160", features = ["derive"] } serde = { version = "1.0.160", features = ["derive"] }
serde_json = "1.0.96" serde_json = "1.0.96"
dirs-next = "2.0.0" dirs-next = "2.0.0"
parseit.workspace = true
rust-ini = "0.18.0" rust-ini = "0.18.0"

View file

@ -54,8 +54,6 @@ pub struct Config {
/// CLI configuration. /// CLI configuration.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct CliConfig { pub struct CliConfig {
/// Whether if the verbose logging is enabled.
pub verbose: bool,
/// Whether if the errors should be ignored. /// Whether if the errors should be ignored.
pub ignore_errors: bool, pub ignore_errors: bool,
/// Whether if the quiet mode is enabled. /// Whether if the quiet mode is enabled.
@ -88,6 +86,8 @@ pub struct TuiConfig {
pub no_docs: bool, pub no_docs: bool,
/// Path for saving the changed kernel parameters. /// Path for saving the changed kernel parameters.
pub save_path: Option<PathBuf>, pub save_path: Option<PathBuf>,
/// File to save the logs.
pub log_file: Option<String>,
/// Color configuration. /// Color configuration.
pub color: TuiColorConfig, pub color: TuiColorConfig,
} }
@ -116,6 +116,7 @@ impl Config {
} }
} }
if let Some(path) = config_path { if let Some(path) = config_path {
log::trace!(target: "config", "Parsing configuration from {:?}", path);
let ini = Ini::load_from_file(path)?; let ini = Ini::load_from_file(path)?;
if let Some(general_section) = ini.section(Some("general")) { if let Some(general_section) = ini.section(Some("general")) {
if let Some(display_deprecated) = general_section.get("display_deprecated") { if let Some(display_deprecated) = general_section.get("display_deprecated") {
@ -126,7 +127,6 @@ impl Config {
} }
} }
if let Some(section) = ini.section(Some("cli")) { if let Some(section) = ini.section(Some("cli")) {
parse_ini_flag!(self, cli, section, verbose);
parse_ini_flag!(self, cli, section, ignore_errors); parse_ini_flag!(self, cli, section, ignore_errors);
parse_ini_flag!(self, cli, section, quiet); parse_ini_flag!(self, cli, section, quiet);
parse_ini_flag!(self, cli, section, no_pager); parse_ini_flag!(self, cli, section, no_pager);
@ -162,6 +162,9 @@ impl Config {
if let Some(save_path) = section.get("save_path") { if let Some(save_path) = section.get("save_path") {
self.tui.save_path = Some(PathBuf::from(save_path)); self.tui.save_path = Some(PathBuf::from(save_path));
} }
if let Some(log_file) = section.get("log_file") {
self.tui.log_file = Some(log_file.to_string());
}
parse_ini_flag!(self, tui, section, no_docs); parse_ini_flag!(self, tui, section, no_docs);
} }
if let Some(section) = ini.section(Some("tui.colors")) { if let Some(section) = ini.section(Some("tui.colors")) {
@ -183,7 +186,6 @@ impl Default for Config {
display_deprecated: false, display_deprecated: false,
kernel_docs: None, kernel_docs: None,
cli: CliConfig { cli: CliConfig {
verbose: false,
ignore_errors: false, ignore_errors: false,
quiet: false, quiet: false,
no_pager: false, no_pager: false,
@ -207,6 +209,7 @@ impl Default for Config {
tick_rate: 250, tick_rate: 250,
no_docs: false, no_docs: false,
save_path: None, save_path: None,
log_file: None,
color: TuiColorConfig { color: TuiColorConfig {
fg_color: String::from("white"), fg_color: String::from("white"),
bg_color: String::from("black"), bg_color: String::from("black"),

View file

@ -51,9 +51,7 @@ impl Sysctl {
} }
} }
Err(e) => { Err(e) => {
if config.cli.verbose { log::trace!(target: "sysctl", "{} ({})", e, ctl.name()?);
eprintln!("{} ({})", e, ctl.name()?);
}
} }
} }
} }
@ -68,6 +66,7 @@ impl Sysctl {
/// Returns the parameters that matches the given query. /// Returns the parameters that matches the given query.
pub fn get_parameters(&self, query: &str) -> Vec<&Parameter> { pub fn get_parameters(&self, query: &str) -> Vec<&Parameter> {
log::trace!(target: "sysctl", "Querying parameters: {:?}", query);
let query = query.replace('/', "."); let query = query.replace('/', ".");
let parameters = self let parameters = self
.parameters .parameters
@ -79,7 +78,8 @@ impl Sysctl {
}) })
.collect::<Vec<&Parameter>>(); .collect::<Vec<&Parameter>>();
if parameters.is_empty() && !self.config.cli.ignore_errors { if parameters.is_empty() && !self.config.cli.ignore_errors {
eprintln!( log::error!(
target: "sysctl",
"{}: cannot stat {}{}: No such file or directory", "{}: cannot stat {}{}: No such file or directory",
env!("CARGO_PKG_NAME").split('-').collect::<Vec<_>>()[0], env!("CARGO_PKG_NAME").split('-').collect::<Vec<_>>()[0],
PROC_PATH, PROC_PATH,
@ -91,6 +91,7 @@ impl Sysctl {
/// Updates the descriptions of the kernel parameters using the given cached data. /// Updates the descriptions of the kernel parameters using the given cached data.
pub fn update_docs_from_cache(&mut self, cache: &Cache) -> Result<()> { pub fn update_docs_from_cache(&mut self, cache: &Cache) -> Result<()> {
log::trace!(target: "cache", "{:?}", cache);
let mut kernel_docs_path = if let Some(path) = &self.config.kernel_docs { let mut kernel_docs_path = if let Some(path) = &self.config.kernel_docs {
vec![path.to_path_buf()] vec![path.to_path_buf()]
} else { } else {
@ -109,6 +110,7 @@ impl Sysctl {
} }
if let Some(path) = kernel_docs_path.iter().find(|path| path.exists()) { if let Some(path) = kernel_docs_path.iter().find(|path| path.exists()) {
if cache.exists(PARAMETERS_CACHE_LABEL) { if cache.exists(PARAMETERS_CACHE_LABEL) {
log::trace!(target: "cache", "Cache hit for {:?}", path);
let cache_data = cache.read(PARAMETERS_CACHE_LABEL)?; let cache_data = cache.read(PARAMETERS_CACHE_LABEL)?;
if cache_data.timestamp == CacheData::<()>::get_timestamp(path)? { if cache_data.timestamp == CacheData::<()>::get_timestamp(path)? {
self.update_params(cache_data.data); self.update_params(cache_data.data);
@ -117,13 +119,14 @@ impl Sysctl {
} }
self.update_docs(path)?; self.update_docs(path)?;
if env::var(DISABLE_CACHE_ENV).is_err() { if env::var(DISABLE_CACHE_ENV).is_err() {
log::trace!(target: "cache", "Writing cache to {:?}", cache);
cache.write( cache.write(
CacheData::new(&self.parameters, path)?, CacheData::new(&self.parameters, path)?,
PARAMETERS_CACHE_LABEL, PARAMETERS_CACHE_LABEL,
)?; )?;
} }
} else { } else {
eprintln!("warning: `Linux kernel documentation cannot be found. Please specify a path via '-D' argument`"); log::error!(target: "sysctl", "warning: `Linux kernel documentation cannot be found. Please specify a path via '-D' argument`");
} }
Ok(()) Ok(())
} }
@ -146,6 +149,7 @@ impl Sysctl {
/// Updates the descriptions of the kernel parameters. /// Updates the descriptions of the kernel parameters.
fn update_docs(&mut self, kernel_docs: &Path) -> Result<()> { fn update_docs(&mut self, kernel_docs: &Path) -> Result<()> {
log::trace!(target: "sysctl", "Parsing the kernel documentation from {:?}", kernel_docs);
let documents = parse_kernel_docs(kernel_docs)?; let documents = parse_kernel_docs(kernel_docs)?;
self.parameters self.parameters
.par_iter_mut() .par_iter_mut()
@ -187,6 +191,13 @@ impl Sysctl {
let save_path = save_path let save_path = save_path
.clone() .clone()
.unwrap_or_else(|| PathBuf::from(DEFAULT_PRELOAD)); .unwrap_or_else(|| PathBuf::from(DEFAULT_PRELOAD));
log::trace!(
target: "param",
"Writing the new value ({:?}) of {:?} to {:?}",
new_value,
param_name,
save_path
);
let data = format!("{param_name} = {new_value}"); let data = format!("{param_name} = {new_value}");
if save_path.exists() { if save_path.exists() {
let contents = reader::read_to_string(&save_path)?; let contents = reader::read_to_string(&save_path)?;

View file

@ -188,6 +188,7 @@ impl Parameter {
config: &Config, config: &Config,
output: &mut Output, output: &mut Output,
) -> Result<()> { ) -> Result<()> {
log::trace!(target: "param", "Setting the value of {:?} to {:?}", self.name, new_value);
let ctl = Ctl::new(&self.name)?; let ctl = Ctl::new(&self.name)?;
let new_value = ctl.set_value_string(new_value)?; let new_value = ctl.set_value_string(new_value)?;
self.value = new_value; self.value = new_value;

View file

@ -10,7 +10,7 @@ repository = "https://github.com/orhun/systeroid"
keywords = ["linux", "kernel", "parameter", "sysctl", "tui"] keywords = ["linux", "kernel", "parameter", "sysctl", "tui"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2021" edition = "2021"
rust-version = "1.64.0" rust-version = "1.68.2"
[features] [features]
# clipboard support is enabled as default # clipboard support is enabled as default
@ -24,6 +24,7 @@ thiserror = "1.0.40"
getopts = "0.2.21" getopts = "0.2.21"
copypasta-ext = { version = "0.4.4", optional = true } copypasta-ext = { version = "0.4.4", optional = true }
colorsys = "0.6.7" colorsys = "0.6.7"
log.workspace = true
[dependencies.systeroid-core] [dependencies.systeroid-core]
version = "0.3.2" # managed by release.sh version = "0.3.2" # managed by release.sh
@ -35,6 +36,11 @@ version = "0.20.1"
default-features = false default-features = false
features = ["termion"] features = ["termion"]
[dependencies.tui-logger]
version = "0.9.0"
default-features = false
features = ["ratatui-support"]
# metadata for cargo-binstall to get the right artifacts # metadata for cargo-binstall to get the right artifacts
[package.metadata.binstall] [package.metadata.binstall]
pkg-url = "{ repo }/releases/download/v{ version }/systeroid-{ version }-{ target }.{ archive-format }" pkg-url = "{ repo }/releases/download/v{ version }/systeroid-{ version }-{ target }.{ archive-format }"

View file

@ -4,11 +4,13 @@ use crate::options::{CopyOption, Direction, ScrollArea};
use crate::widgets::SelectableList; use crate::widgets::SelectableList;
#[cfg(feature = "clipboard")] #[cfg(feature = "clipboard")]
use copypasta_ext::{display::DisplayServer, ClipboardProviderExt}; use copypasta_ext::{display::DisplayServer, ClipboardProviderExt};
use log::{Level, LevelFilter};
use std::str::FromStr; use std::str::FromStr;
use std::time::Instant; use std::time::Instant;
use systeroid_core::sysctl::controller::Sysctl; use systeroid_core::sysctl::controller::Sysctl;
use systeroid_core::sysctl::parameter::Parameter; use systeroid_core::sysctl::parameter::Parameter;
use systeroid_core::sysctl::section::Section; use systeroid_core::sysctl::section::Section;
use tui_logger::TuiWidgetState;
use unicode_width::UnicodeWidthStr; use unicode_width::UnicodeWidthStr;
/// Representation of a key binding. /// Representation of a key binding.
@ -87,6 +89,11 @@ pub const KEY_BINDINGS: &[&KeyBinding] = &[
action: "copy to clipboard", action: "copy to clipboard",
command: None, command: None,
}, },
&KeyBinding {
key: "ctrl-l, f2",
action: "show logs",
command: Some("logs"),
},
&KeyBinding { &KeyBinding {
key: "r, f5", key: "r, f5",
action: "refresh", action: "refresh",
@ -113,6 +120,10 @@ pub struct App<'a> {
pub running: bool, pub running: bool,
/// Whether if the help message is shown. /// Whether if the help message is shown.
pub show_help: bool, pub show_help: bool,
/// Whether if the logs are shown.
pub show_logs: bool,
/// Logger state.
pub logger_state: TuiWidgetState,
/// Input buffer. /// Input buffer.
pub input: Option<String>, pub input: Option<String>,
/// Time tracker for measuring the time for clearing the input. /// Time tracker for measuring the time for clearing the input.
@ -144,6 +155,8 @@ impl<'a> App<'a> {
let mut app = Self { let mut app = Self {
running: true, running: true,
show_help: false, show_help: false,
show_logs: false,
logger_state: TuiWidgetState::new().set_default_display_level(LevelFilter::Trace),
input: None, input: None,
input_time: None, input_time: None,
input_cursor: 0, input_cursor: 0,
@ -167,16 +180,16 @@ impl<'a> App<'a> {
app.parameter_list.items = app.sysctl.parameters.clone(); app.parameter_list.items = app.sysctl.parameters.clone();
#[cfg(feature = "clipboard")] #[cfg(feature = "clipboard")]
{ {
app.clipboard = match DisplayServer::select().try_context() { app.clipboard =
None => { match DisplayServer::select().try_context() {
app.input = Some(String::from( None => {
app.log(Level::Error, String::from(
"Failed to initialize clipboard, no suitable clipboard provider found", "Failed to initialize clipboard, no suitable clipboard provider found",
)); ));
app.input_time = Some(Instant::now()); None
None }
clipboard => clipboard,
} }
clipboard => clipboard,
}
} }
app app
} }
@ -186,6 +199,13 @@ impl<'a> App<'a> {
self.input.is_some() && self.input_time.is_none() self.input.is_some() && self.input_time.is_none()
} }
/// Sets the log message for the application.
pub fn log(&mut self, level: Level, message: String) {
log::log!(target: "tui", level, "{message}");
self.input = Some(message);
self.input_time = Some(Instant::now());
}
/// Performs a search operation in the kernel parameter list. /// Performs a search operation in the kernel parameter list.
pub fn search(&mut self) { pub fn search(&mut self) {
let section = self let section = self
@ -228,7 +248,8 @@ impl<'a> App<'a> {
/// Copies the selected entry to the clipboard. /// Copies the selected entry to the clipboard.
#[cfg(feature = "clipboard")] #[cfg(feature = "clipboard")]
fn copy_to_clipboard(&mut self, copy_option: CopyOption) -> Result<()> { fn copy_to_clipboard(&mut self, copy_option: CopyOption) -> Result<()> {
self.input = Some(if let Some(clipboard) = self.clipboard.as_mut() { log::debug!(target: "tui", "Copying to clipboard: {:?}", copy_option);
if let Some(clipboard) = self.clipboard.as_mut() {
if let Some(parameter) = self.parameter_list.selected() { if let Some(parameter) = self.parameter_list.selected() {
match copy_option { match copy_option {
CopyOption::Name => clipboard.set_contents(parameter.name.clone()), CopyOption::Name => clipboard.set_contents(parameter.name.clone()),
@ -238,22 +259,23 @@ impl<'a> App<'a> {
} }
} }
.map_err(|e| crate::error::Error::ClipboardError(e.to_string()))?; .map_err(|e| crate::error::Error::ClipboardError(e.to_string()))?;
String::from("Copied to clipboard!") self.log(Level::Info, String::from("Copied to clipboard!"));
} else { } else {
String::from("No parameter is selected") self.log(Level::Warn, String::from("No parameter is selected"));
} }
} else { } else {
String::from("Clipboard is not initialized") self.log(Level::Error, String::from("Clipboard is not initialized"));
}); }
self.input_time = Some(Instant::now());
Ok(()) Ok(())
} }
/// Shows a message about clipboard being not enabled. /// Shows a message about clipboard being not enabled.
#[cfg(not(feature = "clipboard"))] #[cfg(not(feature = "clipboard"))]
fn copy_to_clipboard(&mut self, _: CopyOption) -> Result<()> { fn copy_to_clipboard(&mut self, _: CopyOption) -> Result<()> {
self.input = Some(String::from("Clipboard support is not enabled")); self.log(
self.input_time = Some(Instant::now()); Level::Warn,
String::from("Clipboard support is not enabled"),
);
Ok(()) Ok(())
} }
@ -268,6 +290,12 @@ impl<'a> App<'a> {
self.show_help = true; self.show_help = true;
hide_popup = false; hide_popup = false;
} }
Command::Logs => {
self.show_logs = !self.show_logs;
}
Command::LoggerEvent(event) => {
self.logger_state.transition(&event.0);
}
Command::Select => { Command::Select => {
if let Some(copy_option) = self if let Some(copy_option) = self
.options .options
@ -312,8 +340,7 @@ impl<'a> App<'a> {
self.run_command(Command::Refresh)?; self.run_command(Command::Refresh)?;
} }
Err(e) => { Err(e) => {
self.input = Some(e.to_string()); self.log(Level::Error, e.to_string());
self.input_time = Some(Instant::now());
} }
} }
if save_to_file { if save_to_file {
@ -323,17 +350,15 @@ impl<'a> App<'a> {
&self.sysctl.config.tui.save_path, &self.sysctl.config.tui.save_path,
) { ) {
Ok(path) => { Ok(path) => {
self.input = Some(format!("Saved to file: {path:?}")); self.log(Level::Info, format!("Saved to file: {path:?}"));
} }
Err(e) => { Err(e) => {
self.input = Some(format!("Failed to save: {e}")); self.log(Level::Error, format!("Failed to save: {e}"));
} }
} }
self.input_time = Some(Instant::now());
} }
} else { } else {
self.input = Some(String::from("Unknown parameter")); self.log(Level::Warn, String::from("Unknown parameter"));
self.input_time = Some(Instant::now());
} }
} }
Command::Scroll(ScrollArea::List, Direction::Up, amount) => { Command::Scroll(ScrollArea::List, Direction::Up, amount) => {
@ -442,8 +467,7 @@ impl<'a> App<'a> {
self.run_command(command)?; self.run_command(command)?;
hide_popup = false; hide_popup = false;
} else { } else {
self.input = Some(String::from("Unknown command")); self.log(Level::Warn, String::from("Unknown command"));
self.input_time = Some(Instant::now());
} }
} }
} }
@ -516,8 +540,7 @@ impl<'a> App<'a> {
hide_popup = false; hide_popup = false;
self.show_help = false; self.show_help = false;
} else { } else {
self.input = Some(String::from("No parameter is selected")); self.log(Level::Warn, String::from("No parameter is selected"));
self.input_time = Some(Instant::now());
} }
} }
Command::Refresh => { Command::Refresh => {
@ -531,6 +554,7 @@ impl<'a> App<'a> {
parameter.value = param.value.to_string(); parameter.value = param.value.to_string();
} }
}); });
self.log(Level::Info, String::from("Refreshed!"));
} }
Command::Cancel => { Command::Cancel => {
if self.input.is_some() { if self.input.is_some() {

View file

@ -26,6 +26,8 @@ pub struct Args {
pub kernel_docs: Option<PathBuf>, pub kernel_docs: Option<PathBuf>,
/// Path for the changed parameters. /// Path for the changed parameters.
pub save_path: Option<PathBuf>, pub save_path: Option<PathBuf>,
/// File to save the logs.
pub log_file: Option<String>,
/// Sysctl section to filter. /// Sysctl section to filter.
pub section: Option<Section>, pub section: Option<Section>,
/// Query to search on startup. /// Query to search on startup.
@ -62,6 +64,7 @@ impl Args {
"set the path for saving the changed parameters", "set the path for saving the changed parameters",
"<path>", "<path>",
); );
opts.optopt("l", "log-file", "set the file to save the logs", "<path>");
opts.optopt("s", "section", "set the section to filter", "<section>"); opts.optopt("s", "section", "set the section to filter", "<section>");
opts.optopt("q", "query", "set the query to search", "<query>"); opts.optopt("q", "query", "set the query to search", "<query>");
opts.optopt( opts.optopt(
@ -123,6 +126,7 @@ impl Args {
.or_else(|| env::var(KERNEL_DOCS_ENV).ok()) .or_else(|| env::var(KERNEL_DOCS_ENV).ok())
.map(PathBuf::from), .map(PathBuf::from),
save_path: matches.opt_str("save-path").map(PathBuf::from), save_path: matches.opt_str("save-path").map(PathBuf::from),
log_file: matches.opt_str("l"),
section: matches.opt_str("s").map(Section::from), section: matches.opt_str("s").map(Section::from),
search_query: matches.opt_str("q"), search_query: matches.opt_str("q"),
fg_color: matches fg_color: matches

View file

@ -1,12 +1,44 @@
use crate::options::{Direction, ScrollArea}; use crate::options::{Direction, ScrollArea};
use std::str::FromStr; use std::str::FromStr;
use termion::event::Key; use termion::event::Key;
use tui_logger::TuiWidgetEvent;
/// Possible logger widget commands.
#[derive(Debug, PartialEq)]
pub struct LoggerCommand(pub TuiWidgetEvent);
impl Eq for LoggerCommand {}
impl LoggerCommand {
/// Parses a logger command from the given key.
pub fn parse(key: Key) -> Option<Self> {
match key {
Key::Char(' ') => Some(Self(TuiWidgetEvent::SpaceKey)),
Key::Esc => Some(Self(TuiWidgetEvent::EscapeKey)),
Key::PageUp => Some(Self(TuiWidgetEvent::PrevPageKey)),
Key::PageDown => Some(Self(TuiWidgetEvent::NextPageKey)),
Key::Up => Some(Self(TuiWidgetEvent::UpKey)),
Key::Down => Some(Self(TuiWidgetEvent::DownKey)),
Key::Left => Some(Self(TuiWidgetEvent::LeftKey)),
Key::Right => Some(Self(TuiWidgetEvent::RightKey)),
Key::Char('+') => Some(Self(TuiWidgetEvent::PlusKey)),
Key::Char('-') => Some(Self(TuiWidgetEvent::MinusKey)),
Key::Char('h') => Some(Self(TuiWidgetEvent::HideKey)),
Key::Char('f') => Some(Self(TuiWidgetEvent::FocusKey)),
_ => None,
}
}
}
/// Possible application commands. /// Possible application commands.
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub enum Command { pub enum Command {
/// Show help. /// Show help.
Help, Help,
/// Show logs.
Logs,
/// Logger event.
LoggerEvent(LoggerCommand),
/// Perform an action based on the selected entry. /// Perform an action based on the selected entry.
Select, Select,
/// Save the value of a parameter to a file. /// Save the value of a parameter to a file.
@ -42,6 +74,7 @@ impl FromStr for Command {
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
match s { match s {
"help" => Ok(Command::Help), "help" => Ok(Command::Help),
"logs" => Ok(Command::Logs),
"search" => Ok(Command::Search), "search" => Ok(Command::Search),
"select" => Ok(Command::Select), "select" => Ok(Command::Select),
"copy" => Ok(Command::Copy), "copy" => Ok(Command::Copy),
@ -91,6 +124,7 @@ impl Command {
} else { } else {
match key { match key {
Key::Char('?') | Key::F(1) => Command::Help, Key::Char('?') | Key::F(1) => Command::Help,
Key::Ctrl('l') | Key::F(2) => Command::Logs,
Key::Up | Key::Char('k') => Command::Scroll(ScrollArea::List, Direction::Up, 1), Key::Up | Key::Char('k') => Command::Scroll(ScrollArea::List, Direction::Up, 1),
Key::Down | Key::Char('j') => Command::Scroll(ScrollArea::List, Direction::Down, 1), Key::Down | Key::Char('j') => Command::Scroll(ScrollArea::List, Direction::Down, 1),
Key::PageUp => Command::Scroll(ScrollArea::List, Direction::Up, 4), Key::PageUp => Command::Scroll(ScrollArea::List, Direction::Up, 4),
@ -135,6 +169,7 @@ mod tests {
fn test_command() { fn test_command() {
for (command, value) in vec![ for (command, value) in vec![
(Command::Help, "help"), (Command::Help, "help"),
(Command::Logs, "logs"),
(Command::Search, "search"), (Command::Search, "search"),
(Command::Select, "select"), (Command::Select, "select"),
(Command::Copy, "copy"), (Command::Copy, "copy"),
@ -181,6 +216,7 @@ mod tests {
assert_command_parser! { assert_command_parser! {
input_mode: false, input_mode: false,
Key::Char('?') => Command::Help, Key::Char('?') => Command::Help,
Key::Ctrl('l') => Command::Logs,
Key::Up => Command::Scroll(ScrollArea::List, Direction::Up, 1), Key::Up => Command::Scroll(ScrollArea::List, Direction::Up, 1),
Key::Down => Command::Scroll(ScrollArea::List, Direction::Down, 1), Key::Down => Command::Scroll(ScrollArea::List, Direction::Down, 1),
Key::PageUp => Command::Scroll(ScrollArea::List, Direction::Up, 4), Key::PageUp => Command::Scroll(ScrollArea::List, Direction::Up, 4),

View file

@ -15,6 +15,12 @@ pub enum Error {
/// Error that may occur while parsing a color. /// Error that may occur while parsing a color.
#[error(transparent)] #[error(transparent)]
ColorParseError(#[from] colorsys::ParseError), ColorParseError(#[from] colorsys::ParseError),
/// Error that may occur if the logger is already set.
#[error(transparent)]
LoggerSetError(#[from] log::SetLoggerError),
/// Error that may occur when the string doesnt match any of the log levels.
#[error(transparent)]
LoggerParseError(#[from] log::ParseLevelError),
/// Error that may occur in the core library. /// Error that may occur in the core library.
#[error(transparent)] #[error(transparent)]
SysctlError(#[from] systeroid_core::error::Error), SysctlError(#[from] systeroid_core::error::Error),

View file

@ -27,6 +27,10 @@ use crate::command::Command;
use crate::error::Result; use crate::error::Result;
use crate::event::{Event, EventHandler}; use crate::event::{Event, EventHandler};
use crate::style::Colors; use crate::style::Colors;
use command::LoggerCommand;
use log::LevelFilter;
use std::env;
use std::str::FromStr;
use systeroid_core::cache::Cache; use systeroid_core::cache::Cache;
use systeroid_core::config::Config; use systeroid_core::config::Config;
use systeroid_core::sysctl::controller::Sysctl; use systeroid_core::sysctl::controller::Sysctl;
@ -42,11 +46,22 @@ pub fn run<B: Backend>(args: Args, backend: B) -> Result<()> {
}; };
config.tui.tick_rate = args.tick_rate; config.tui.tick_rate = args.tick_rate;
config.tui.save_path = args.save_path; config.tui.save_path = args.save_path;
config.tui.log_file = args.log_file;
config.tui.no_docs = args.no_docs; config.tui.no_docs = args.no_docs;
config.tui.color.fg_color = args.fg_color; config.tui.color.fg_color = args.fg_color;
config.tui.color.bg_color = args.bg_color; config.tui.color.bg_color = args.bg_color;
config.parse(args.config)?; config.parse(args.config)?;
let colors = Colors::new(&config.tui.color.bg_color, &config.tui.color.fg_color)?; let colors = Colors::new(&config.tui.color.bg_color, &config.tui.color.fg_color)?;
tui_logger::init_logger(if let Ok(log_level) = env::var("RUST_LOG") {
LevelFilter::from_str(&log_level)?
} else {
LevelFilter::Trace
})?;
tui_logger::set_default_level(LevelFilter::Trace);
if let Some(ref log_file) = config.tui.log_file {
tui_logger::set_log_file(log_file)?;
}
log::trace!(target: "config", "{:?}", config);
let mut sysctl = Sysctl::init(config)?; let mut sysctl = Sysctl::init(config)?;
if !sysctl.config.tui.no_docs { if !sysctl.config.tui.no_docs {
sysctl.update_docs_from_cache(&Cache::init()?)?; sysctl.update_docs_from_cache(&Cache::init()?)?;
@ -75,7 +90,12 @@ pub fn run<B: Backend>(args: Args, backend: B) -> Result<()> {
terminal.draw(|frame| ui::render(frame, &mut app, &colors))?; terminal.draw(|frame| ui::render(frame, &mut app, &colors))?;
match event_handler.next()? { match event_handler.next()? {
Event::KeyPress(key) => { Event::KeyPress(key) => {
let command = Command::parse(key, app.is_input_mode()); let mut command = Command::parse(key, app.is_input_mode());
if app.show_logs {
command = LoggerCommand::parse(key)
.map(Command::LoggerEvent)
.unwrap_or(command);
}
app.run_command(command)?; app.run_command(command)?;
} }
#[cfg(not(test))] #[cfg(not(test))]

View file

@ -3,9 +3,11 @@ use crate::style::Colors;
use crate::widgets::SelectableList; use crate::widgets::SelectableList;
use tui::backend::Backend; use tui::backend::Backend;
use tui::layout::{Alignment, Constraint, Direction, Layout, Rect}; use tui::layout::{Alignment, Constraint, Direction, Layout, Rect};
use tui::style::{Color as TuiColor, Style};
use tui::text::{Span, Text}; use tui::text::{Span, Text};
use tui::widgets::{Block, BorderType, Borders, Cell, Clear, Paragraph, Row, Table, Wrap}; use tui::widgets::{Block, BorderType, Borders, Cell, Clear, Paragraph, Row, Table, Wrap};
use tui::Frame; use tui::Frame;
use tui_logger::{TuiLoggerLevelOutput, TuiLoggerSmartWidget};
use unicode_width::UnicodeWidthStr; use unicode_width::UnicodeWidthStr;
/// Renders the user interface. /// Renders the user interface.
@ -16,35 +18,48 @@ pub fn render<B: Backend>(frame: &mut Frame<'_, B>, app: &mut App, colors: &Colo
.and_then(|parameter| parameter.get_documentation()); .and_then(|parameter| parameter.get_documentation());
let rect = frame.size(); let rect = frame.size();
let chunks = Layout::default() let chunks = Layout::default()
.direction(Direction::Horizontal) .direction(Direction::Vertical)
.constraints(if documentation.is_some() { .constraints(if app.show_logs {
[Constraint::Percentage(50), Constraint::Percentage(50)] [Constraint::Percentage(60), Constraint::Percentage(40)]
} else { } else {
[Constraint::Percentage(100), Constraint::Min(0)] [Constraint::Percentage(100), Constraint::Min(0)]
}) })
.split(rect); .split(rect);
{ {
let chunks = Layout::default() let chunks = Layout::default()
.direction(Direction::Vertical) .direction(Direction::Horizontal)
.constraints(if app.input.is_some() { .constraints(if documentation.is_some() {
[Constraint::Min(rect.height - 3), Constraint::Min(3)] [Constraint::Percentage(50), Constraint::Percentage(50)]
} else { } else {
[Constraint::Percentage(100), Constraint::Min(0)] [Constraint::Percentage(100), Constraint::Min(0)]
}) })
.split(chunks[0]); .split(chunks[0]);
render_parameter_list(frame, chunks[0], app, colors); {
if app.input.is_some() { let chunks = Layout::default()
render_input_prompt(frame, chunks[1], rect.height - 2, app, colors); .direction(Direction::Vertical)
.constraints(if app.input.is_some() {
[Constraint::Min(chunks[0].height - 3), Constraint::Min(3)]
} else {
[Constraint::Percentage(100), Constraint::Min(0)]
})
.split(chunks[0]);
render_parameter_list(frame, chunks[0], app, colors);
if app.input.is_some() {
render_input_prompt(frame, chunks[1], chunks[0].height + 1, app, colors);
}
}
if let Some(documentation) = documentation {
render_parameter_documentation(
frame,
chunks[1],
documentation,
&mut app.docs_scroll_amount,
colors,
);
} }
} }
if let Some(documentation) = documentation { if app.show_logs {
render_parameter_documentation( render_log_view(frame, chunks[1], app, colors);
frame,
chunks[1],
documentation,
&mut app.docs_scroll_amount,
colors,
);
} }
if app.show_help { if app.show_help {
render_help_text(frame, rect, &mut app.key_bindings, colors); render_help_text(frame, rect, &mut app.key_bindings, colors);
@ -463,3 +478,24 @@ fn render_input_prompt<B: Backend>(
rect, rect,
); );
} }
/// Renders the log view.
fn render_log_view<B: Backend>(frame: &mut Frame<'_, B>, rect: Rect, app: &App, colors: &Colors) {
let logger_widget = TuiLoggerSmartWidget::default()
.style_trace(Style::default().fg(TuiColor::DarkGray))
.style_debug(Style::default().fg(TuiColor::Blue))
.style_warn(Style::default().fg(TuiColor::Yellow))
.style_error(Style::default().fg(TuiColor::Red))
.style_info(Style::default().fg(TuiColor::Green))
.highlight_style(colors.get_fg_style())
.border_style(colors.get_fg_style())
.style(colors.get_bg_style())
.output_separator(':')
.output_timestamp(Some("%H:%M:%S".to_string()))
.output_level(Some(TuiLoggerLevelOutput::Long))
.output_target(true)
.output_file(true)
.output_line(true)
.state(&app.logger_state);
frame.render_widget(logger_widget, rect);
}

View file

@ -307,10 +307,10 @@ fn test_render_tui() -> Result<()> {
"│vm.stat_interval =││============= │", "│vm.stat_interval =││============= │",
"│ ││The time interval │", "│ ││The time interval │",
"│ ││between which vm │", "│ ││between which vm │",
" ││statistics are │", "2/2 ││statistics are │",
"│ ││updated │", "╰──────────────────╯│updated │",
"│ ││- │", "╭──────────────────╮│- │",
" 2/2 ││Parameter: │", "MSG: Refreshed! ││Parameter: │",
"╰──────────────────╯╰──────────────────╯", "╰──────────────────╯╰──────────────────╯",
]), ]),
terminal.backend(), terminal.backend(),

View file

@ -10,7 +10,7 @@ repository = "https://github.com/orhun/systeroid"
keywords = ["linux", "kernel", "parameter", "sysctl"] keywords = ["linux", "kernel", "parameter", "sysctl"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
edition = "2021" edition = "2021"
default-run="systeroid" default-run = "systeroid"
rust-version = "1.64.0" rust-version = "1.64.0"
[features] [features]
@ -18,8 +18,10 @@ rust-version = "1.64.0"
live-tests = [] live-tests = []
[dependencies] [dependencies]
getopts = "0.2.21"
parseit.workspace = true parseit.workspace = true
log.workspace = true
env_logger = "0.10.0"
getopts = "0.2.21"
[dependencies.systeroid-core] [dependencies.systeroid-core]
version = "0.3.2" # managed by release.sh version = "0.3.2" # managed by release.sh

View file

@ -95,7 +95,7 @@ impl<'a, Output: Write> App<'a, Output> {
} }
Err(e) => { Err(e) => {
if !pager.is_empty() { if !pager.is_empty() {
eprintln!("pager error: `{e}`"); log::error!("pager error: `{e}`");
} }
fallback_to_default = true; fallback_to_default = true;
} }
@ -130,7 +130,7 @@ impl<'a, Output: Write> App<'a, Output> {
if parameters.len() == 1 { if parameters.len() == 1 {
let param = parameters[0]; let param = parameters[0];
if DEPRECATED_PARAMS.contains(&param.get_absolute_name().unwrap_or_default()) { if DEPRECATED_PARAMS.contains(&param.get_absolute_name().unwrap_or_default()) {
eprintln!( log::error!(
"{}: {} is deprecated, value not set", "{}: {} is deprecated, value not set",
env!("CARGO_PKG_NAME"), env!("CARGO_PKG_NAME"),
parameter parameter
@ -145,14 +145,14 @@ impl<'a, Output: Write> App<'a, Output> {
param.update_value(&new_value, &config, self.output)?; param.update_value(&new_value, &config, self.output)?;
} }
} else { } else {
eprintln!( log::error!(
"{}: ambiguous parameter name: {}", "{}: ambiguous parameter name: {}",
env!("CARGO_PKG_NAME"), env!("CARGO_PKG_NAME"),
parameter parameter
); );
} }
} else if write_mode { } else if write_mode {
eprintln!( log::error!(
"{}: {:?} must be in the format: name=value", "{}: {:?} must be in the format: name=value",
env!("CARGO_PKG_NAME"), env!("CARGO_PKG_NAME"),
parameter parameter
@ -171,13 +171,13 @@ impl<'a, Output: Write> App<'a, Output> {
let lines = stdin.lock().lines(); let lines = stdin.lock().lines();
for line in lines { for line in lines {
if let Err(e) = self.process_parameter(line?, true, false) { if let Err(e) = self.process_parameter(line?, true, false) {
println!("{}: {}", env!("CARGO_PKG_NAME"), e); log::info!("{}: {}", env!("CARGO_PKG_NAME"), e);
} }
} }
return Ok(()); return Ok(());
} }
if !path.exists() { if !path.exists() {
eprintln!( log::error!(
"{}: cannot open {:?}: No such file or directory", "{}: cannot open {:?}: No such file or directory",
env!("CARGO_PKG_NAME"), env!("CARGO_PKG_NAME"),
path path
@ -194,7 +194,7 @@ impl<'a, Output: Write> App<'a, Output> {
if !parameter.starts_with('-') { if !parameter.starts_with('-') {
process_result?; process_result?;
} else if let Err(e) = process_result { } else if let Err(e) = process_result {
eprintln!("{}: {}", env!("CARGO_PKG_NAME"), e); log::error!("{}: {}", env!("CARGO_PKG_NAME"), e);
} }
} }
Ok(()) Ok(())
@ -208,7 +208,7 @@ impl<'a, Output: Write> App<'a, Output> {
{ {
if let Ok(glob_walker) = globwalk::glob(preload_path.to_string_lossy()) { if let Ok(glob_walker) = globwalk::glob(preload_path.to_string_lossy()) {
for file in glob_walker.filter_map(|v| v.ok()) { for file in glob_walker.filter_map(|v| v.ok()) {
println!("* Applying {} ...", file.path().display()); log::info!("* Applying {} ...", file.path().display());
self.preload_from_file(file.path().to_path_buf())?; self.preload_from_file(file.path().to_path_buf())?;
} }
} }

View file

@ -23,13 +23,13 @@ pub fn run<Output: Write>(args: Args, output: &mut Output) -> Result<()> {
kernel_docs: args.kernel_docs, kernel_docs: args.kernel_docs,
..Default::default() ..Default::default()
}; };
config.cli.verbose = args.verbose;
config.cli.ignore_errors = args.ignore_errors; config.cli.ignore_errors = args.ignore_errors;
config.cli.quiet = args.quiet; config.cli.quiet = args.quiet;
config.cli.no_pager = args.no_pager; config.cli.no_pager = args.no_pager;
config.cli.display_type = args.display_type; config.cli.display_type = args.display_type;
config.cli.output_type = args.output_type; config.cli.output_type = args.output_type;
config.parse(args.config)?; config.parse(args.config)?;
log::trace!("{:?}", config);
let mut sysctl = Sysctl::init(config)?; let mut sysctl = Sysctl::init(config)?;
if args.explain { if args.explain {
sysctl.update_docs_from_cache(&Cache::init()?)?; sysctl.update_docs_from_cache(&Cache::init()?)?;

View file

@ -1,10 +1,19 @@
use env_logger::Builder as LoggerBuilder;
use log::LevelFilter;
use std::env; use std::env;
use std::io; use std::io::{self, Write};
use std::process::{self, Command}; use std::process::{self, Command};
use systeroid::args::Args; use systeroid::args::Args;
fn main() { fn main() {
if let Some(args) = Args::parse(env::args().collect()) { if let Some(args) = Args::parse(env::args().collect()) {
let mut builder = LoggerBuilder::from_default_env();
if args.verbose {
builder.filter(None, LevelFilter::Trace);
}
builder
.format(|buf, record| writeln!(buf, "{}", record.args()))
.init();
if args.show_tui { if args.show_tui {
let bin = format!("{}-tui", env!("CARGO_PKG_NAME")); let bin = format!("{}-tui", env!("CARGO_PKG_NAME"));
let mut command = Command::new(&bin); let mut command = Command::new(&bin);
@ -17,7 +26,7 @@ fn main() {
match command.spawn().map(|mut child| child.wait()) { match command.spawn().map(|mut child| child.wait()) {
Ok(_) => process::exit(0), Ok(_) => process::exit(0),
Err(e) => { Err(e) => {
eprintln!("Cannot run `{bin}` ({e})"); log::error!("Cannot run `{bin}` ({e})");
process::exit(1) process::exit(1)
} }
} }
@ -26,7 +35,7 @@ fn main() {
match systeroid::run(args, &mut stdout) { match systeroid::run(args, &mut stdout) {
Ok(_) => process::exit(0), Ok(_) => process::exit(0),
Err(e) => { Err(e) => {
eprintln!("{e}"); log::error!("{e}");
process::exit(1) process::exit(1)
} }
} }