allow changing the log level while the server is running
This commit is contained in:
Родитель
de18fd6d0e
Коммит
31db8adf3f
|
@ -635,19 +635,6 @@ dependencies = [
|
|||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"humantime",
|
||||
"log",
|
||||
"regex",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "event-listener"
|
||||
version = "2.5.1"
|
||||
|
@ -679,6 +666,22 @@ dependencies = [
|
|||
"instant",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flexi_logger"
|
||||
version = "0.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33ab94b6ac8eb69f1496a6993f26f785b5fd6d99b7416023eb2a6175c0b242b1"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"chrono",
|
||||
"glob",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"regex",
|
||||
"thiserror",
|
||||
"yansi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
|
@ -893,6 +896,12 @@ version = "0.23.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce"
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||
|
||||
[[package]]
|
||||
name = "gloo-timers"
|
||||
version = "0.2.1"
|
||||
|
@ -1033,15 +1042,6 @@ version = "0.3.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47"
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
|
||||
dependencies = [
|
||||
"quick-error",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.13.9"
|
||||
|
@ -1513,6 +1513,7 @@ dependencies = [
|
|||
"ctrlc",
|
||||
"dashmap",
|
||||
"dotenv",
|
||||
"flexi_logger",
|
||||
"futures",
|
||||
"http-auth-basic",
|
||||
"log",
|
||||
|
@ -1521,7 +1522,6 @@ dependencies = [
|
|||
"parse-display",
|
||||
"percent-encoding",
|
||||
"php-literal-parser",
|
||||
"pretty_env_logger",
|
||||
"rand 0.7.3",
|
||||
"redis",
|
||||
"reqwest",
|
||||
|
@ -1821,16 +1821,6 @@ version = "0.2.10"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
|
||||
|
||||
[[package]]
|
||||
name = "pretty_env_logger"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d"
|
||||
dependencies = [
|
||||
"env_logger",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
|
@ -2654,15 +2644,6 @@ dependencies = [
|
|||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termion"
|
||||
version = "1.5.5"
|
||||
|
@ -3305,15 +3286,6 @@ version = "0.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
|
@ -3345,6 +3317,12 @@ version = "0.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214"
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fc79f4a1e39857fc00c3f662cbf2651c771f00e9c15fe2abc341806bd46bd71"
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.1.1"
|
||||
|
|
|
@ -12,7 +12,6 @@ thiserror = "1.0.22"
|
|||
warp = "0.2.5"
|
||||
tokio = { version = "0.2", features = ["macros", "rt-threaded"] }
|
||||
futures = "0.3"
|
||||
pretty_env_logger = "0.4.0"
|
||||
log = "0.4.11"
|
||||
sqlx = { version = "0.4.1", features = ["runtime-tokio-rustls", "any", "macros", "mysql", "sqlite", "postgres"] }
|
||||
dotenv = "0.15.0"
|
||||
|
@ -28,6 +27,7 @@ percent-encoding = "2.1.0"
|
|||
ctrlc = { version = "3.1.7", features = ["termination"] }
|
||||
rand = "0.7.3"
|
||||
ahash = "0.7.0"
|
||||
flexi_logger = { version = "0.17", features = ["colors"] }
|
||||
|
||||
[dev-dependencies]
|
||||
mini-redis = "0.2.0"
|
||||
|
|
23
README.md
23
README.md
|
@ -144,6 +144,29 @@ the push server is listening.
|
|||
|
||||
The app will automatically run some tests to verify that the push server is configured correctly.
|
||||
|
||||
### Logging
|
||||
|
||||
By default, the push server only logs warnings, you can temporarily change the log level with an occ command
|
||||
|
||||
```bash
|
||||
occ notify_push:log <level>
|
||||
```
|
||||
|
||||
Where level is `error`, `warn`, `info`, debug` or `trace`, or restore the log level to the previous value using
|
||||
|
||||
```bash
|
||||
occ notify_push:log --restore
|
||||
```
|
||||
|
||||
Alternatively you can set the log level of the push server in the `LOG` environment variable.
|
||||
|
||||
### Metrics
|
||||
|
||||
The push server can expose some basic metrics about the number of connected clients and the traffic flowing trough the server
|
||||
by setting the `METRICS_PORT` environment variable.
|
||||
|
||||
Once set the metrics are available in a prometheus compatible format at `/metrics` on the configured port.
|
||||
|
||||
## Developing
|
||||
|
||||
As developer of a Nextcloud app or client you can use the `notify_push` app to receive real time notifications from the
|
||||
|
|
|
@ -23,5 +23,6 @@
|
|||
<commands>
|
||||
<command>OCA\NotifyPush\Command\Setup</command>
|
||||
<command>OCA\NotifyPush\Command\SelfTest</command>
|
||||
<command>OCA\NotifyPush\Command\Log</command>
|
||||
</commands>
|
||||
</info>
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2021 Robin Appelman <robin@icewind.nl>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\NotifyPush\Command;
|
||||
|
||||
use OC\Core\Command\Base;
|
||||
use OCA\NotifyPush\Queue\IQueue;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class Log extends Base {
|
||||
private $queue;
|
||||
|
||||
public function __construct(
|
||||
IQueue $queue,
|
||||
) {
|
||||
parent::__construct();
|
||||
$this->queue = $queue;
|
||||
}
|
||||
|
||||
|
||||
protected function configure() {
|
||||
$this
|
||||
->setName('notify_push:log')
|
||||
->setDescription('Temporarily set the log level of the push server')
|
||||
->addOption("restore", "r", InputOption::VALUE_NONE, "restore the log level to the previous value")
|
||||
->addArgument("level", InputArgument::OPTIONAL, "the new log level to set");
|
||||
parent::configure();
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
$level = $input->getArgument("level");
|
||||
if ($input->getOption("restore")) {
|
||||
$output->writeln("restoring log level");
|
||||
$this->queue->push("notify_config", "log_restore");
|
||||
} elseif ($level) {
|
||||
// by default dont touch the log level of the libraries
|
||||
if (!strpos($level, "=") and $level !== "trace") {
|
||||
$level = "notify_push=$level";
|
||||
}
|
||||
$this->queue->push("notify_config", ["log_spec" => $level]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -97,7 +97,13 @@ pub async fn handle_user_socket(mut ws: WebSocket, app: Arc<App>, forwarded_for:
|
|||
let _msg = match result {
|
||||
Ok(msg) => msg,
|
||||
Err(e) => {
|
||||
log::warn!("websocket error: {}", e);
|
||||
let formatted = e.to_string();
|
||||
// hack while warp only has opaque error types
|
||||
if formatted
|
||||
!= "WebSocket protocol error: Connection reset without closing handshake"
|
||||
{
|
||||
log::warn!("websocket error: {}", e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
|
13
src/event.rs
13
src/event.rs
|
@ -41,6 +41,13 @@ pub struct PreAuth {
|
|||
pub token: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum Config {
|
||||
LogSpec(String),
|
||||
LogRestore,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Custom {
|
||||
pub user: UserId,
|
||||
|
@ -65,6 +72,8 @@ pub enum Event {
|
|||
PreAuth(PreAuth),
|
||||
#[display("custom notification {0.message} for user {0.user}")]
|
||||
Custom(Custom),
|
||||
#[display("config update")]
|
||||
Config(Config),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
|
@ -104,6 +113,9 @@ impl TryFrom<Msg> for Event {
|
|||
"notify_custom" => Ok(Event::Custom(serde_json::from_slice(
|
||||
msg.get_payload_bytes(),
|
||||
)?)),
|
||||
"notify_config" => Ok(Event::Config(serde_json::from_slice(
|
||||
msg.get_payload_bytes(),
|
||||
)?)),
|
||||
_ => Err(MessageDecodeError::UnsupportedEventType),
|
||||
}
|
||||
}
|
||||
|
@ -126,6 +138,7 @@ pub async fn subscribe(
|
|||
"notify_notification",
|
||||
"notify_pre_auth",
|
||||
"notify_custom",
|
||||
"notify_config",
|
||||
];
|
||||
for channel in channels.iter() {
|
||||
pubsub
|
||||
|
|
21
src/lib.rs
21
src/lib.rs
|
@ -9,6 +9,7 @@ pub use crate::user::UserId;
|
|||
use ahash::RandomState;
|
||||
use color_eyre::{eyre::WrapErr, Result};
|
||||
use dashmap::DashMap;
|
||||
use flexi_logger::LoggerHandle;
|
||||
use futures::StreamExt;
|
||||
use smallvec::alloc::sync::Arc;
|
||||
use sqlx::AnyPool;
|
||||
|
@ -16,6 +17,7 @@ use std::convert::Infallible;
|
|||
use std::net::{IpAddr, SocketAddr};
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
use std::time::Instant;
|
||||
use tokio::sync::Mutex;
|
||||
use warp::filters::addr::remote;
|
||||
use warp::Filter;
|
||||
use warp_real_ip::get_forwarded_for;
|
||||
|
@ -36,10 +38,11 @@ pub struct App {
|
|||
pre_auth: DashMap<String, (Instant, UserId), RandomState>,
|
||||
test_cookie: AtomicU32,
|
||||
redis_url: String,
|
||||
log_handle: Mutex<LoggerHandle>,
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub async fn new(config: Config) -> Result<Self> {
|
||||
pub async fn new(config: Config, log_handle: LoggerHandle) -> Result<Self> {
|
||||
let connections = ActiveConnections::default();
|
||||
let nc_client = nc::Client::new(&config.nextcloud_url)?;
|
||||
let test_cookie = AtomicU32::new(0);
|
||||
|
@ -57,10 +60,15 @@ impl App {
|
|||
pre_auth,
|
||||
storage_mapping,
|
||||
redis_url,
|
||||
log_handle: Mutex::new(log_handle),
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn with_connection(connection: AnyPool, config: Config) -> Result<Self> {
|
||||
pub async fn with_connection(
|
||||
connection: AnyPool,
|
||||
config: Config,
|
||||
log_handle: LoggerHandle,
|
||||
) -> Result<Self> {
|
||||
let connections = ActiveConnections::default();
|
||||
let nc_client = nc::Client::new(&config.nextcloud_url)?;
|
||||
let test_cookie = AtomicU32::new(0);
|
||||
|
@ -78,6 +86,7 @@ impl App {
|
|||
pre_auth,
|
||||
storage_mapping,
|
||||
redis_url,
|
||||
log_handle: Mutex::new(log_handle),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -139,6 +148,14 @@ impl App {
|
|||
.send_to_user(&user, MessageType::Custom(message))
|
||||
.await;
|
||||
}
|
||||
Event::Config(event::Config::LogSpec(spec)) => {
|
||||
self.log_handle.lock().await.parse_and_push_temp_spec(&spec);
|
||||
log::info!("Set log level to {}", spec);
|
||||
}
|
||||
Event::Config(event::Config::LogRestore) => {
|
||||
self.log_handle.lock().await.pop_temp_spec();
|
||||
log::info!("Restored log level");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
10
src/main.rs
10
src/main.rs
|
@ -1,4 +1,5 @@
|
|||
use color_eyre::{eyre::WrapErr, Result};
|
||||
use flexi_logger::{colored_detailed_format, LogTarget, Logger};
|
||||
use notify_push::config::Config;
|
||||
use notify_push::message::DEBOUNCE_ENABLE;
|
||||
use notify_push::metrics::serve_metrics;
|
||||
|
@ -10,7 +11,12 @@ use tokio::time::Duration;
|
|||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
color_eyre::install()?;
|
||||
pretty_env_logger::init();
|
||||
let level = dotenv::var("LOG").ok();
|
||||
let level = level.as_ref().map(String::as_str).unwrap_or("warn");
|
||||
let log_handle = Logger::with_str(level)
|
||||
.log_target(LogTarget::StdOut)
|
||||
.format(colored_detailed_format)
|
||||
.start()?;
|
||||
let _ = dotenv::dotenv();
|
||||
|
||||
ctrlc::set_handler(move || {
|
||||
|
@ -41,7 +47,7 @@ async fn main() -> Result<()> {
|
|||
|
||||
log::trace!("Running with config: {:?} on port {}", config, port);
|
||||
|
||||
let app = Arc::new(App::new(config).await?);
|
||||
let app = Arc::new(App::new(config, log_handle).await?);
|
||||
app.self_test().await?;
|
||||
|
||||
tokio::task::spawn(serve(app.clone(), port));
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
use dashmap::DashMap;
|
||||
use flexi_logger::{Logger, LoggerHandle};
|
||||
use futures::future::select;
|
||||
use futures::{pin_mut, FutureExt};
|
||||
use futures::{SinkExt, StreamExt};
|
||||
use http_auth_basic::Credentials;
|
||||
use notify_push::config::Config;
|
||||
use notify_push::{listen, serve, App};
|
||||
use once_cell::sync::Lazy;
|
||||
use redis::AsyncCommands;
|
||||
use smallvec::alloc::sync::Arc;
|
||||
use sqlx::AnyPool;
|
||||
|
@ -44,9 +46,10 @@ struct Services {
|
|||
db: AnyPool,
|
||||
}
|
||||
|
||||
static LOG_HANDLE: Lazy<LoggerHandle> = Lazy::new(|| Logger::with_str("").start().unwrap());
|
||||
|
||||
impl Services {
|
||||
pub async fn new() -> Self {
|
||||
let _ = pretty_env_logger::try_init();
|
||||
let redis_tcp = listen_available_port()
|
||||
.await
|
||||
.expect("Can't find open port for redis");
|
||||
|
@ -145,7 +148,9 @@ impl Services {
|
|||
|
||||
async fn app(&self) -> App {
|
||||
let config = self.config();
|
||||
App::with_connection(self.db.clone(), config).await.unwrap()
|
||||
App::with_connection(self.db.clone(), config, LOG_HANDLE.clone())
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
async fn spawn_server(&self) -> ServerHandle {
|
||||
|
|
Загрузка…
Ссылка в новой задаче