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",
]
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "atty"
version = "0.2.14"
@ -67,6 +76,12 @@ dependencies = [
"serde",
]
[[package]]
name = "bumpalo"
version = "3.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
[[package]]
name = "byteorder"
version = "1.4.3"
@ -79,12 +94,33 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
[[package]]
name = "cc"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "clipboard-win"
version = "3.1.1"
@ -95,6 +131,16 @@ dependencies = [
"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]]
name = "colored"
version = "2.0.0"
@ -138,6 +184,12 @@ dependencies = [
"x11-clipboard",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
[[package]]
name = "crc32fast"
version = "1.3.2"
@ -190,6 +242,50 @@ dependencies = [
"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]]
name = "dirs-next"
version = "2.0.0"
@ -250,6 +346,40 @@ dependencies = [
"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]]
name = "flate2"
version = "1.0.25"
@ -266,6 +396,15 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "fxhash"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
dependencies = [
"byteorder",
]
[[package]]
name = "gethostname"
version = "0.2.3"
@ -293,7 +432,7 @@ checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
dependencies = [
"cfg-if",
"libc",
"wasi",
"wasi 0.11.0+wasi-snapshot-preview1",
]
[[package]]
@ -353,6 +492,42 @@ dependencies = [
"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]]
name = "ignore"
version = "0.4.20"
@ -370,12 +545,44 @@ dependencies = [
"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]]
name = "itoa"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "lazy-bytes-cast"
version = "5.0.1"
@ -404,6 +611,31 @@ dependencies = [
"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]]
name = "log"
version = "0.4.17"
@ -492,6 +724,25 @@ dependencies = [
"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]]
name = "num_cpus"
version = "1.15.0"
@ -553,6 +804,29 @@ dependencies = [
"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]]
name = "parseit"
version = "0.1.2"
@ -680,6 +954,20 @@ dependencies = [
"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]]
name = "ryu"
version = "1.0.13"
@ -707,6 +995,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "scratch"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1"
[[package]]
name = "serde"
version = "1.0.160"
@ -812,7 +1106,9 @@ dependencies = [
name = "systeroid"
version = "0.3.2"
dependencies = [
"env_logger",
"getopts",
"log",
"parseit",
"systeroid-core",
]
@ -824,6 +1120,7 @@ dependencies = [
"colored",
"dirs-next",
"lazy_static",
"log",
"parseit",
"rayon",
"rust-ini",
@ -840,13 +1137,24 @@ dependencies = [
"colorsys",
"copypasta-ext",
"getopts",
"log",
"ratatui",
"systeroid-core",
"termion",
"thiserror",
"tui-logger",
"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]]
name = "termion"
version = "2.0.1"
@ -889,6 +1197,31 @@ dependencies = [
"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]]
name = "unicode-ident"
version = "1.0.8"
@ -923,12 +1256,72 @@ dependencies = [
"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]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "wayland-client"
version = "0.29.5"
@ -1053,6 +1446,147 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "x11-clipboard"
version = "0.7.1"

View File

@ -3,6 +3,7 @@ members = ["systeroid-core", "systeroid-tui", "systeroid"]
[workspace.dependencies]
parseit = { version = "0.1.2", features = ["gzip"] }
log = { version = "0.4.17", features = ["std"] }
[profile.dev]
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
RUN apk update
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)
- [Searching parameters](#searching-parameters)
- [Showing information about parameters](#showing-information-about-parameters)
- [Verbose logging](#verbose-logging)
- [TUI](#tui)
- [Usage](#usage-2)
- [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)
- [Viewing the parameter documentation](#viewing-the-parameter-documentation)
- [Setting the refresh rate](#setting-the-refresh-rate)
- [Logging](#logging)
- [Configuration](#configuration)
- [Resources](#resources)
- [References](#references)
@ -398,6 +400,20 @@ It is also possible to retrieve information about multiple parameters:
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
### Usage
@ -440,6 +456,7 @@ systeroid-tui [options]
| <kbd>enter</kbd> | select / set parameter value |
| <kbd>s</kbd> | save parameter value |
| <kbd>c</kbd> | copy to clipboard |
| <kbd>ctrl-l</kbd>, <kbd>f2</kbd> | show logs |
| <kbd>r</kbd>, <kbd>f5</kbd> | refresh |
| <kbd>esc</kbd> | cancel / 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 |
| ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `:help` | Show help |
| `:search` | Enable search |
| `:select` | Select the current parameter in the list |
| `:set <name> <value>` | Set parameter value |
| `: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` |
| `:copy` | Copy to clipboard |
| `:refresh` | Refresh values |
| `:quit`, `:q` | Quit |
| `:help` | show help |
| `:search` | enable search |
| `:select` | select the current parameter in the list |
| `:set <name> <value>` | set parameter value |
| `: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` |
| `:copy` | copy to clipboard |
| `:logs` | show logs |
| `:refresh` | refresh values |
| `:quit`, `:q` | quit |
#### 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
```
#### 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
**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"
[cli]
; enable verbose logging
verbose = false
; ignore unknown variable errors
ignore_errors = true
; do not print variable after the value is set
@ -59,6 +57,8 @@ tick_rate = 250
no_docs = true
; path for saving the changed kernel parameters
save_path = "/etc/sysctl.conf"
; file to save the logs
;log_file = "systeroid.log"
[tui.colors]
; 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>
Use this option to set the path for saving the changed parameters.
.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>
Use this option to set the section to filter.
.HP
@ -61,6 +64,8 @@ systeroid-tui \-D /usr/share/doc/linux
.br
systeroid-tui \-\-save-path 99-local.conf
.br
systeroid-tui \-\-log-file systeroid.log
.br
systeroid-tui -n
.SH KEY BINDINGS

View File

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

View File

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

View File

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

View File

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

View File

@ -10,7 +10,7 @@ repository = "https://github.com/orhun/systeroid"
keywords = ["linux", "kernel", "parameter", "sysctl", "tui"]
categories = ["command-line-utilities"]
edition = "2021"
rust-version = "1.64.0"
rust-version = "1.68.2"
[features]
# clipboard support is enabled as default
@ -24,6 +24,7 @@ thiserror = "1.0.40"
getopts = "0.2.21"
copypasta-ext = { version = "0.4.4", optional = true }
colorsys = "0.6.7"
log.workspace = true
[dependencies.systeroid-core]
version = "0.3.2" # managed by release.sh
@ -35,6 +36,11 @@ version = "0.20.1"
default-features = false
features = ["termion"]
[dependencies.tui-logger]
version = "0.9.0"
default-features = false
features = ["ratatui-support"]
# metadata for cargo-binstall to get the right artifacts
[package.metadata.binstall]
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;
#[cfg(feature = "clipboard")]
use copypasta_ext::{display::DisplayServer, ClipboardProviderExt};
use log::{Level, LevelFilter};
use std::str::FromStr;
use std::time::Instant;
use systeroid_core::sysctl::controller::Sysctl;
use systeroid_core::sysctl::parameter::Parameter;
use systeroid_core::sysctl::section::Section;
use tui_logger::TuiWidgetState;
use unicode_width::UnicodeWidthStr;
/// Representation of a key binding.
@ -87,6 +89,11 @@ pub const KEY_BINDINGS: &[&KeyBinding] = &[
action: "copy to clipboard",
command: None,
},
&KeyBinding {
key: "ctrl-l, f2",
action: "show logs",
command: Some("logs"),
},
&KeyBinding {
key: "r, f5",
action: "refresh",
@ -113,6 +120,10 @@ pub struct App<'a> {
pub running: bool,
/// Whether if the help message is shown.
pub show_help: bool,
/// Whether if the logs are shown.
pub show_logs: bool,
/// Logger state.
pub logger_state: TuiWidgetState,
/// Input buffer.
pub input: Option<String>,
/// Time tracker for measuring the time for clearing the input.
@ -144,6 +155,8 @@ impl<'a> App<'a> {
let mut app = Self {
running: true,
show_help: false,
show_logs: false,
logger_state: TuiWidgetState::new().set_default_display_level(LevelFilter::Trace),
input: None,
input_time: None,
input_cursor: 0,
@ -167,16 +180,16 @@ impl<'a> App<'a> {
app.parameter_list.items = app.sysctl.parameters.clone();
#[cfg(feature = "clipboard")]
{
app.clipboard = match DisplayServer::select().try_context() {
None => {
app.input = Some(String::from(
app.clipboard =
match DisplayServer::select().try_context() {
None => {
app.log(Level::Error, String::from(
"Failed to initialize clipboard, no suitable clipboard provider found",
));
app.input_time = Some(Instant::now());
None
None
}
clipboard => clipboard,
}
clipboard => clipboard,
}
}
app
}
@ -186,6 +199,13 @@ impl<'a> App<'a> {
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.
pub fn search(&mut self) {
let section = self
@ -228,7 +248,8 @@ impl<'a> App<'a> {
/// Copies the selected entry to the clipboard.
#[cfg(feature = "clipboard")]
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() {
match copy_option {
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()))?;
String::from("Copied to clipboard!")
self.log(Level::Info, String::from("Copied to clipboard!"));
} else {
String::from("No parameter is selected")
self.log(Level::Warn, String::from("No parameter is selected"));
}
} else {
String::from("Clipboard is not initialized")
});
self.input_time = Some(Instant::now());
self.log(Level::Error, String::from("Clipboard is not initialized"));
}
Ok(())
}
/// Shows a message about clipboard being not enabled.
#[cfg(not(feature = "clipboard"))]
fn copy_to_clipboard(&mut self, _: CopyOption) -> Result<()> {
self.input = Some(String::from("Clipboard support is not enabled"));
self.input_time = Some(Instant::now());
self.log(
Level::Warn,
String::from("Clipboard support is not enabled"),
);
Ok(())
}
@ -268,6 +290,12 @@ impl<'a> App<'a> {
self.show_help = true;
hide_popup = false;
}
Command::Logs => {
self.show_logs = !self.show_logs;
}
Command::LoggerEvent(event) => {
self.logger_state.transition(&event.0);
}
Command::Select => {
if let Some(copy_option) = self
.options
@ -312,8 +340,7 @@ impl<'a> App<'a> {
self.run_command(Command::Refresh)?;
}
Err(e) => {
self.input = Some(e.to_string());
self.input_time = Some(Instant::now());
self.log(Level::Error, e.to_string());
}
}
if save_to_file {
@ -323,17 +350,15 @@ impl<'a> App<'a> {
&self.sysctl.config.tui.save_path,
) {
Ok(path) => {
self.input = Some(format!("Saved to file: {path:?}"));
self.log(Level::Info, format!("Saved to file: {path:?}"));
}
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 {
self.input = Some(String::from("Unknown parameter"));
self.input_time = Some(Instant::now());
self.log(Level::Warn, String::from("Unknown parameter"));
}
}
Command::Scroll(ScrollArea::List, Direction::Up, amount) => {
@ -442,8 +467,7 @@ impl<'a> App<'a> {
self.run_command(command)?;
hide_popup = false;
} else {
self.input = Some(String::from("Unknown command"));
self.input_time = Some(Instant::now());
self.log(Level::Warn, String::from("Unknown command"));
}
}
}
@ -516,8 +540,7 @@ impl<'a> App<'a> {
hide_popup = false;
self.show_help = false;
} else {
self.input = Some(String::from("No parameter is selected"));
self.input_time = Some(Instant::now());
self.log(Level::Warn, String::from("No parameter is selected"));
}
}
Command::Refresh => {
@ -531,6 +554,7 @@ impl<'a> App<'a> {
parameter.value = param.value.to_string();
}
});
self.log(Level::Info, String::from("Refreshed!"));
}
Command::Cancel => {
if self.input.is_some() {

View File

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

View File

@ -1,12 +1,44 @@
use crate::options::{Direction, ScrollArea};
use std::str::FromStr;
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.
#[derive(Debug, PartialEq, Eq)]
pub enum Command {
/// Show help.
Help,
/// Show logs.
Logs,
/// Logger event.
LoggerEvent(LoggerCommand),
/// Perform an action based on the selected entry.
Select,
/// 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> {
match s {
"help" => Ok(Command::Help),
"logs" => Ok(Command::Logs),
"search" => Ok(Command::Search),
"select" => Ok(Command::Select),
"copy" => Ok(Command::Copy),
@ -91,6 +124,7 @@ impl Command {
} else {
match key {
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::Down | Key::Char('j') => Command::Scroll(ScrollArea::List, Direction::Down, 1),
Key::PageUp => Command::Scroll(ScrollArea::List, Direction::Up, 4),
@ -135,6 +169,7 @@ mod tests {
fn test_command() {
for (command, value) in vec![
(Command::Help, "help"),
(Command::Logs, "logs"),
(Command::Search, "search"),
(Command::Select, "select"),
(Command::Copy, "copy"),
@ -181,6 +216,7 @@ mod tests {
assert_command_parser! {
input_mode: false,
Key::Char('?') => Command::Help,
Key::Ctrl('l') => Command::Logs,
Key::Up => Command::Scroll(ScrollArea::List, Direction::Up, 1),
Key::Down => Command::Scroll(ScrollArea::List, Direction::Down, 1),
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(transparent)]
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(transparent)]
SysctlError(#[from] systeroid_core::error::Error),

View File

@ -27,6 +27,10 @@ use crate::command::Command;
use crate::error::Result;
use crate::event::{Event, EventHandler};
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::config::Config;
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.save_path = args.save_path;
config.tui.log_file = args.log_file;
config.tui.no_docs = args.no_docs;
config.tui.color.fg_color = args.fg_color;
config.tui.color.bg_color = args.bg_color;
config.parse(args.config)?;
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)?;
if !sysctl.config.tui.no_docs {
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))?;
match event_handler.next()? {
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)?;
}
#[cfg(not(test))]

View File

@ -3,9 +3,11 @@ use crate::style::Colors;
use crate::widgets::SelectableList;
use tui::backend::Backend;
use tui::layout::{Alignment, Constraint, Direction, Layout, Rect};
use tui::style::{Color as TuiColor, Style};
use tui::text::{Span, Text};
use tui::widgets::{Block, BorderType, Borders, Cell, Clear, Paragraph, Row, Table, Wrap};
use tui::Frame;
use tui_logger::{TuiLoggerLevelOutput, TuiLoggerSmartWidget};
use unicode_width::UnicodeWidthStr;
/// 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());
let rect = frame.size();
let chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints(if documentation.is_some() {
[Constraint::Percentage(50), Constraint::Percentage(50)]
.direction(Direction::Vertical)
.constraints(if app.show_logs {
[Constraint::Percentage(60), Constraint::Percentage(40)]
} else {
[Constraint::Percentage(100), Constraint::Min(0)]
})
.split(rect);
{
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints(if app.input.is_some() {
[Constraint::Min(rect.height - 3), Constraint::Min(3)]
.direction(Direction::Horizontal)
.constraints(if documentation.is_some() {
[Constraint::Percentage(50), Constraint::Percentage(50)]
} 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], rect.height - 2, app, colors);
{
let chunks = Layout::default()
.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 {
render_parameter_documentation(
frame,
chunks[1],
documentation,
&mut app.docs_scroll_amount,
colors,
);
if app.show_logs {
render_log_view(frame, chunks[1], app, colors);
}
if app.show_help {
render_help_text(frame, rect, &mut app.key_bindings, colors);
@ -463,3 +478,24 @@ fn render_input_prompt<B: Backend>(
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 =││============= │",
"│ ││The time interval │",
"│ ││between which vm │",
" ││statistics are │",
"│ ││updated │",
"│ ││- │",
" 2/2 ││Parameter: │",
"2/2 ││statistics are │",
"╰──────────────────╯│updated │",
"╭──────────────────╮│- │",
"MSG: Refreshed! ││Parameter: │",
"╰──────────────────╯╰──────────────────╯",
]),
terminal.backend(),

View File

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

View File

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

View File

@ -23,13 +23,13 @@ pub fn run<Output: Write>(args: Args, output: &mut Output) -> Result<()> {
kernel_docs: args.kernel_docs,
..Default::default()
};
config.cli.verbose = args.verbose;
config.cli.ignore_errors = args.ignore_errors;
config.cli.quiet = args.quiet;
config.cli.no_pager = args.no_pager;
config.cli.display_type = args.display_type;
config.cli.output_type = args.output_type;
config.parse(args.config)?;
log::trace!("{:?}", config);
let mut sysctl = Sysctl::init(config)?;
if args.explain {
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::io;
use std::io::{self, Write};
use std::process::{self, Command};
use systeroid::args::Args;
fn main() {
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 {
let bin = format!("{}-tui", env!("CARGO_PKG_NAME"));
let mut command = Command::new(&bin);
@ -17,7 +26,7 @@ fn main() {
match command.spawn().map(|mut child| child.wait()) {
Ok(_) => process::exit(0),
Err(e) => {
eprintln!("Cannot run `{bin}` ({e})");
log::error!("Cannot run `{bin}` ({e})");
process::exit(1)
}
}
@ -26,7 +35,7 @@ fn main() {
match systeroid::run(args, &mut stdout) {
Ok(_) => process::exit(0),
Err(e) => {
eprintln!("{e}");
log::error!("{e}");
process::exit(1)
}
}