mirror of
https://github.com/mikedilger/chorus.git
synced 2026-05-03 06:51:42 +00:00
Reload config on HUP signal; change and synchronize default config
This commit is contained in:
parent
a701da6dda
commit
54bb5450f6
@ -6,9 +6,10 @@ use std::sync::atomic::AtomicUsize;
|
|||||||
use std::sync::OnceLock;
|
use std::sync::OnceLock;
|
||||||
use tokio::sync::broadcast::Sender as BroadcastSender;
|
use tokio::sync::broadcast::Sender as BroadcastSender;
|
||||||
use tokio::sync::watch::Sender as WatchSender;
|
use tokio::sync::watch::Sender as WatchSender;
|
||||||
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
pub struct Globals {
|
pub struct Globals {
|
||||||
pub config: OnceLock<Config>,
|
pub config: RwLock<Config>,
|
||||||
pub store: OnceLock<Store>,
|
pub store: OnceLock<Store>,
|
||||||
pub http_server: Http,
|
pub http_server: Http,
|
||||||
pub rid: OnceLock<String>,
|
pub rid: OnceLock<String>,
|
||||||
@ -33,7 +34,7 @@ lazy_static! {
|
|||||||
let (shutting_down, _) = tokio::sync::watch::channel(false);
|
let (shutting_down, _) = tokio::sync::watch::channel(false);
|
||||||
|
|
||||||
Globals {
|
Globals {
|
||||||
config: OnceLock::new(),
|
config: RwLock::new(Default::default()),
|
||||||
store: OnceLock::new(),
|
store: OnceLock::new(),
|
||||||
http_server,
|
http_server,
|
||||||
rid: OnceLock::new(),
|
rid: OnceLock::new(),
|
||||||
|
|||||||
@ -45,7 +45,7 @@ async fn main() -> Result<(), Error> {
|
|||||||
let config_path = args.next().unwrap();
|
let config_path = args.next().unwrap();
|
||||||
|
|
||||||
// Read config file
|
// Read config file
|
||||||
let mut file = OpenOptions::new().read(true).open(config_path)?;
|
let mut file = OpenOptions::new().read(true).open(config_path.clone())?;
|
||||||
let mut contents = String::new();
|
let mut contents = String::new();
|
||||||
file.read_to_string(&mut contents)?;
|
file.read_to_string(&mut contents)?;
|
||||||
let friendly_config: FriendlyConfig = toml::from_str(&contents)?;
|
let friendly_config: FriendlyConfig = toml::from_str(&contents)?;
|
||||||
@ -80,11 +80,12 @@ async fn main() -> Result<(), Error> {
|
|||||||
log::info!(target: "Server", "Running on {}:{}", config.ip_address, config.port);
|
log::info!(target: "Server", "Running on {}:{}", config.ip_address, config.port);
|
||||||
|
|
||||||
// Store config into GLOBALS
|
// Store config into GLOBALS
|
||||||
let _ = GLOBALS.config.set(config);
|
*GLOBALS.config.write().await = config;
|
||||||
|
|
||||||
let mut interrupt_signal = signal(SignalKind::interrupt())?;
|
let mut interrupt_signal = signal(SignalKind::interrupt())?;
|
||||||
let mut quit_signal = signal(SignalKind::quit())?;
|
let mut quit_signal = signal(SignalKind::quit())?;
|
||||||
let mut terminate_signal = signal(SignalKind::terminate())?;
|
let mut terminate_signal = signal(SignalKind::terminate())?;
|
||||||
|
let mut hup_signal = signal(SignalKind::hangup())?;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
@ -102,6 +103,20 @@ async fn main() -> Result<(), Error> {
|
|||||||
break;
|
break;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Reload config on HUP
|
||||||
|
v = hup_signal.recv() => if v.is_some() {
|
||||||
|
log::info!(target: "Server", "SIGHUP: Reloading configuration");
|
||||||
|
|
||||||
|
// Reload the config file
|
||||||
|
let mut file = OpenOptions::new().read(true).open(config_path.clone())?;
|
||||||
|
let mut contents = String::new();
|
||||||
|
file.read_to_string(&mut contents)?;
|
||||||
|
let friendly_config: FriendlyConfig = toml::from_str(&contents)?;
|
||||||
|
let config: Config = friendly_config.into_config()?;
|
||||||
|
|
||||||
|
*GLOBALS.config.write().await = config;
|
||||||
|
},
|
||||||
|
|
||||||
// Accepts network connections and spawn a task to serve each one
|
// Accepts network connections and spawn a task to serve each one
|
||||||
v = listener.accept() => {
|
v = listener.accept() => {
|
||||||
let (tcp_stream, peer_addr) = v?;
|
let (tcp_stream, peer_addr) = v?;
|
||||||
|
|||||||
@ -82,7 +82,7 @@ impl WebSocketService {
|
|||||||
if let Err(e) = self.req_inner(&subid, filters).await {
|
if let Err(e) = self.req_inner(&subid, filters).await {
|
||||||
let reply = match e.inner {
|
let reply = match e.inner {
|
||||||
ChorusError::TooManySubscriptions => {
|
ChorusError::TooManySubscriptions => {
|
||||||
let max_subscriptions = GLOBALS.config.get().unwrap().max_subscriptions;
|
let max_subscriptions = GLOBALS.config.read().await.max_subscriptions;
|
||||||
NostrReply::Closed(
|
NostrReply::Closed(
|
||||||
&subid,
|
&subid,
|
||||||
NostrReplyPrefix::Blocked,
|
NostrReplyPrefix::Blocked,
|
||||||
@ -105,7 +105,7 @@ impl WebSocketService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn req_inner(&mut self, subid: &String, filters: Vec<OwnedFilter>) -> Result<(), Error> {
|
async fn req_inner(&mut self, subid: &String, filters: Vec<OwnedFilter>) -> Result<(), Error> {
|
||||||
let max_subscriptions = GLOBALS.config.get().unwrap().max_subscriptions;
|
let max_subscriptions = GLOBALS.config.read().await.max_subscriptions;
|
||||||
if self.subscriptions.len() >= max_subscriptions {
|
if self.subscriptions.len() >= max_subscriptions {
|
||||||
return Err(ChorusError::TooManySubscriptions.into());
|
return Err(ChorusError::TooManySubscriptions.into());
|
||||||
}
|
}
|
||||||
@ -233,8 +233,7 @@ impl WebSocketService {
|
|||||||
|
|
||||||
let event_flags = event_flags(&event, &user);
|
let event_flags = event_flags(&event, &user);
|
||||||
|
|
||||||
if !event_flags.author_is_an_authorized_user || GLOBALS.config.get().unwrap().verify_events
|
if !event_flags.author_is_an_authorized_user || GLOBALS.config.read().await.verify_events {
|
||||||
{
|
|
||||||
// Verify the event is valid (id is hash, signature is valid)
|
// Verify the event is valid (id is hash, signature is valid)
|
||||||
if let Err(e) = event.verify() {
|
if let Err(e) = event.verify() {
|
||||||
return Err(ChorusError::EventIsInvalid(format!("{}", e)).into());
|
return Err(ChorusError::EventIsInvalid(format!("{}", e)).into());
|
||||||
@ -345,7 +344,7 @@ impl WebSocketService {
|
|||||||
};
|
};
|
||||||
if let Some(h) = url.host() {
|
if let Some(h) = url.host() {
|
||||||
let theirhost = h.to_owned();
|
let theirhost = h.to_owned();
|
||||||
if theirhost == GLOBALS.config.get().unwrap().hostname {
|
if theirhost == GLOBALS.config.read().await.hostname {
|
||||||
relay_ok = true;
|
relay_ok = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -402,7 +401,7 @@ async fn screen_incoming_event(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Accept if an open relay
|
// Accept if an open relay
|
||||||
if GLOBALS.config.get().unwrap().open_relay {
|
if GLOBALS.config.read().await.open_relay {
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -412,20 +411,20 @@ async fn screen_incoming_event(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Accept relay lists from anybody
|
// Accept relay lists from anybody
|
||||||
if event.kind() == Kind(10002) && GLOBALS.config.get().unwrap().serve_relay_lists {
|
if event.kind() == Kind(10002) && GLOBALS.config.read().await.serve_relay_lists {
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow if event kind ephemeral
|
// Allow if event kind ephemeral
|
||||||
if event.kind().is_ephemeral() && GLOBALS.config.get().unwrap().serve_ephemeral {
|
if event.kind().is_ephemeral() && GLOBALS.config.read().await.serve_ephemeral {
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the author is one of our users, always accept it
|
// If the author is one of our users, always accept it
|
||||||
if GLOBALS
|
if GLOBALS
|
||||||
.config
|
.config
|
||||||
.get()
|
.read()
|
||||||
.unwrap()
|
.await
|
||||||
.user_keys
|
.user_keys
|
||||||
.contains(&event.pubkey())
|
.contains(&event.pubkey())
|
||||||
{
|
{
|
||||||
@ -436,7 +435,7 @@ async fn screen_incoming_event(
|
|||||||
for mut tag in event.tags()?.iter() {
|
for mut tag in event.tags()?.iter() {
|
||||||
if tag.next() == Some(b"p") {
|
if tag.next() == Some(b"p") {
|
||||||
if let Some(value) = tag.next() {
|
if let Some(value) = tag.next() {
|
||||||
for ukhex in &GLOBALS.config.get().unwrap().user_hex_keys {
|
for ukhex in &GLOBALS.config.read().await.user_hex_keys {
|
||||||
if value == ukhex.as_bytes() {
|
if value == ukhex.as_bytes() {
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
@ -460,17 +459,17 @@ pub fn screen_outgoing_event(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Allow if an open relay
|
// Allow if an open relay
|
||||||
if GLOBALS.config.get().unwrap().open_relay {
|
if GLOBALS.config.blocking_read().open_relay {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow Relay Lists
|
// Allow Relay Lists
|
||||||
if event.kind() == Kind(10002) && GLOBALS.config.get().unwrap().serve_relay_lists {
|
if event.kind() == Kind(10002) && GLOBALS.config.blocking_read().serve_relay_lists {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow if event kind ephemeral
|
// Allow if event kind ephemeral
|
||||||
if event.kind().is_ephemeral() && GLOBALS.config.get().unwrap().serve_ephemeral {
|
if event.kind().is_ephemeral() && GLOBALS.config.blocking_read().serve_ephemeral {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -491,7 +490,7 @@ pub fn screen_outgoing_event(
|
|||||||
pub async fn authorized_user(user: &Option<Pubkey>) -> bool {
|
pub async fn authorized_user(user: &Option<Pubkey>) -> bool {
|
||||||
match user {
|
match user {
|
||||||
None => false,
|
None => false,
|
||||||
Some(pk) => GLOBALS.config.get().unwrap().user_keys.contains(pk),
|
Some(pk) => GLOBALS.config.read().await.user_keys.contains(pk),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -505,8 +504,7 @@ pub struct EventFlags {
|
|||||||
pub fn event_flags(event: &Event<'_>, user: &Option<Pubkey>) -> EventFlags {
|
pub fn event_flags(event: &Event<'_>, user: &Option<Pubkey>) -> EventFlags {
|
||||||
let author_is_an_authorized_user = GLOBALS
|
let author_is_an_authorized_user = GLOBALS
|
||||||
.config
|
.config
|
||||||
.get()
|
.blocking_read()
|
||||||
.unwrap()
|
|
||||||
.user_keys
|
.user_keys
|
||||||
.contains(&event.pubkey());
|
.contains(&event.pubkey());
|
||||||
|
|
||||||
@ -529,7 +527,12 @@ pub fn event_flags(event: &Event<'_>, user: &Option<Pubkey>) -> EventFlags {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if GLOBALS.config.get().unwrap().user_keys.contains(&tagged_pk) {
|
if GLOBALS
|
||||||
|
.config
|
||||||
|
.blocking_read()
|
||||||
|
.user_keys
|
||||||
|
.contains(&tagged_pk)
|
||||||
|
{
|
||||||
tags_an_authorized_user = true;
|
tags_an_authorized_user = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,7 +18,7 @@ pub async fn serve_http(peer: SocketAddr, request: Request<Body>) -> Result<Resp
|
|||||||
pub async fn serve_nip11(peer: SocketAddr) -> Result<Response<Body>, Error> {
|
pub async fn serve_nip11(peer: SocketAddr) -> Result<Response<Body>, Error> {
|
||||||
log::debug!(target: "Client", "{}: sent NIP-11", peer);
|
log::debug!(target: "Client", "{}: sent NIP-11", peer);
|
||||||
let rid = {
|
let rid = {
|
||||||
let config = GLOBALS.config.get().unwrap();
|
let config = &*GLOBALS.config.read().await;
|
||||||
GLOBALS.rid.get_or_init(|| build_rid(config))
|
GLOBALS.rid.get_or_init(|| build_rid(config))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -35,15 +35,15 @@ pub struct FriendlyConfig {
|
|||||||
impl Default for FriendlyConfig {
|
impl Default for FriendlyConfig {
|
||||||
fn default() -> FriendlyConfig {
|
fn default() -> FriendlyConfig {
|
||||||
FriendlyConfig {
|
FriendlyConfig {
|
||||||
data_directory: "/tmp".to_string(),
|
data_directory: "/opt/chorus/var/chorus".to_string(),
|
||||||
ip_address: "127.0.0.1".to_string(),
|
ip_address: "127.0.0.1".to_string(),
|
||||||
port: 80,
|
port: 443,
|
||||||
hostname: "localhost".to_string(),
|
hostname: "localhost".to_string(),
|
||||||
use_tls: false,
|
use_tls: true,
|
||||||
certchain_pem_path: "./tls/fullchain.pem".to_string(),
|
certchain_pem_path: "/opt/chorus/etc/tls/fullchain.pem".to_string(),
|
||||||
key_pem_path: "./tls/privkey.pem".to_string(),
|
key_pem_path: "/opt/chorus/etc/tls/privkey.pem".to_string(),
|
||||||
name: None,
|
name: Some("Chorus Default".to_string()),
|
||||||
description: None,
|
description: Some("A default config of the Chorus relay".to_string()),
|
||||||
contact: None,
|
contact: None,
|
||||||
public_key_hex: None,
|
public_key_hex: None,
|
||||||
open_relay: false,
|
open_relay: false,
|
||||||
@ -56,8 +56,8 @@ impl Default for FriendlyConfig {
|
|||||||
serve_ephemeral: true,
|
serve_ephemeral: true,
|
||||||
serve_relay_lists: true,
|
serve_relay_lists: true,
|
||||||
server_log_level: "Info".to_string(),
|
server_log_level: "Info".to_string(),
|
||||||
library_log_level: "Warn".to_string(),
|
library_log_level: "Info".to_string(),
|
||||||
client_log_level: "Error".to_string(),
|
client_log_level: "Info".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -103,9 +103,9 @@ impl FriendlyConfig {
|
|||||||
let hostname = Host::parse(&hostname)?;
|
let hostname = Host::parse(&hostname)?;
|
||||||
|
|
||||||
let server_log_level =
|
let server_log_level =
|
||||||
log::LevelFilter::from_str(&server_log_level).unwrap_or(log::LevelFilter::Error);
|
log::LevelFilter::from_str(&server_log_level).unwrap_or(log::LevelFilter::Info);
|
||||||
let library_log_level =
|
let library_log_level =
|
||||||
log::LevelFilter::from_str(&library_log_level).unwrap_or(log::LevelFilter::Warn);
|
log::LevelFilter::from_str(&library_log_level).unwrap_or(log::LevelFilter::Info);
|
||||||
let client_log_level =
|
let client_log_level =
|
||||||
log::LevelFilter::from_str(&client_log_level).unwrap_or(log::LevelFilter::Info);
|
log::LevelFilter::from_str(&client_log_level).unwrap_or(log::LevelFilter::Info);
|
||||||
|
|
||||||
@ -165,3 +165,12 @@ pub struct Config {
|
|||||||
pub library_log_level: log::LevelFilter,
|
pub library_log_level: log::LevelFilter,
|
||||||
pub client_log_level: log::LevelFilter,
|
pub client_log_level: log::LevelFilter,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
fn default() -> Config {
|
||||||
|
let friendly = FriendlyConfig::default();
|
||||||
|
|
||||||
|
// We know the default config passes into_config without error:
|
||||||
|
friendly.into_config().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -67,16 +67,16 @@ certchain_pem_path = "/opt/chorus/etc/tls/fullchain.pem"
|
|||||||
key_pem_path = "/opt/chorus/etc/tls/privkey.pem"
|
key_pem_path = "/opt/chorus/etc/tls/privkey.pem"
|
||||||
|
|
||||||
|
|
||||||
# This is an optional name for your relay, displayed in the NIP-11 response.
|
# This is a name for your relay, displayed in the NIP-11 response.
|
||||||
#
|
#
|
||||||
# Default is not set
|
# Default is "Chorus Default"
|
||||||
#
|
#
|
||||||
# name = "Chorus Default"
|
# name = "Chorus Default"
|
||||||
|
|
||||||
|
|
||||||
# This is an optional description for your relay, displayed in the NIP-11 response.
|
# This is a description for your relay, displayed in the NIP-11 response.
|
||||||
#
|
#
|
||||||
# Default is not set
|
# Default is "A default config of the Chorus relay"
|
||||||
#
|
#
|
||||||
# description = "A default config of the Chorus relay"
|
# description = "A default config of the Chorus relay"
|
||||||
|
|
||||||
@ -203,15 +203,15 @@ server_log_level = "Info"
|
|||||||
#
|
#
|
||||||
# Possible values are: Trace, Debug, Info, Warn, Error
|
# Possible values are: Trace, Debug, Info, Warn, Error
|
||||||
#
|
#
|
||||||
# Default is Warn
|
# Default is Info
|
||||||
#
|
#
|
||||||
library_log_level = "Warn"
|
library_log_level = "Info"
|
||||||
|
|
||||||
|
|
||||||
# How verbose to log issues with client requests
|
# How verbose to log issues with client requests
|
||||||
#
|
#
|
||||||
# Possible values are: Trace, Debug, Info, Warn, Error
|
# Possible values are: Trace, Debug, Info, Warn, Error
|
||||||
#
|
#
|
||||||
# Default is Error
|
# Default is Info
|
||||||
#
|
#
|
||||||
client_log_level = "Error"
|
client_log_level = "Info"
|
||||||
|
|||||||
@ -10,7 +10,7 @@ The config file must be in TOML format. See the [TOML documentation](https://git
|
|||||||
|
|
||||||
This is the directory where chorus stores data.
|
This is the directory where chorus stores data.
|
||||||
|
|
||||||
Default is "/tmp".
|
Default is "/opt/chorus/var/chorus".
|
||||||
|
|
||||||
If deployed according to [docs/DEPLOYING.md](docs/DEPLOYING.md), is "/opt/chorus/var/chorus".
|
If deployed according to [docs/DEPLOYING.md](docs/DEPLOYING.md), is "/opt/chorus/var/chorus".
|
||||||
|
|
||||||
@ -34,6 +34,8 @@ Default is 443.
|
|||||||
This is the DNS hostname of your relay. This is used for verifying AUTH events, which specify
|
This is the DNS hostname of your relay. This is used for verifying AUTH events, which specify
|
||||||
your relay host name.
|
your relay host name.
|
||||||
|
|
||||||
|
Default is localhost
|
||||||
|
|
||||||
### use_tls
|
### use_tls
|
||||||
|
|
||||||
If true, chorus will handle TLS, running over HTTPS. If false, chorus run over HTTP.
|
If true, chorus will handle TLS, running over HTTPS. If false, chorus run over HTTP.
|
||||||
@ -41,17 +43,18 @@ If true, chorus will handle TLS, running over HTTPS. If false, chorus run over
|
|||||||
If you are proxying via nginx, normally you will set this to false and allow nginx to handle
|
If you are proxying via nginx, normally you will set this to false and allow nginx to handle
|
||||||
TLS.
|
TLS.
|
||||||
|
|
||||||
|
Default is true
|
||||||
|
|
||||||
### certchain_pem_path
|
### certchain_pem_path
|
||||||
|
|
||||||
This is the path to your TLS certificate chain file.
|
This is the path to your TLS certificate chain file.
|
||||||
|
|
||||||
If `use_tls` is false, this value is irrelevant.
|
If `use_tls` is false, this value is irrelevant.
|
||||||
|
|
||||||
Default is "./tls/fullchain.pem".
|
Default is "/opt/chorus/etc/tls/fullchain.pem"
|
||||||
|
|
||||||
If deployed according to [docs/DEPLOYING.md](docs/DEPLOYING.md) using the direct method,
|
If deployed according to [docs/DEPLOYING.md](docs/DEPLOYING.md) using the direct method,
|
||||||
this is set to "/opt/chorus/etc/tls/fullchain.pem" and the systemd service copies letsencrypt
|
systemd service copies letsencrypt TLS certificates into this position on start.
|
||||||
TLS certificates into this position on start.
|
|
||||||
|
|
||||||
### key_pem_path
|
### key_pem_path
|
||||||
|
|
||||||
@ -59,23 +62,22 @@ This is the path to yoru TLS private key file.
|
|||||||
|
|
||||||
If `use_tls` is false, this value is irrelevant.
|
If `use_tls` is false, this value is irrelevant.
|
||||||
|
|
||||||
Default is "./tls/privkey.pem".
|
Default is "/opt/chorus/etc/tls/privkey.pem"
|
||||||
|
|
||||||
If deployed according to [docs/DEPLOYING.md](docs/DEPLOYING.md) using the direct method,
|
If deployed according to [docs/DEPLOYING.md](docs/DEPLOYING.md) using the direct method,
|
||||||
this is set to "/opt/chorus/etc/tls/privkey.pem" and the systemd service copies letsencrypt
|
systemd service copies letsencrypt TLS certificates into this position on start.
|
||||||
TLS certificates into this position on start.
|
|
||||||
|
|
||||||
### name
|
### name
|
||||||
|
|
||||||
This is an optional name for your relay, displayed in the NIP-11 response.
|
This is an optional name for your relay, displayed in the NIP-11 response.
|
||||||
|
|
||||||
Default is None.
|
Default is "Chorus Default"
|
||||||
|
|
||||||
### description
|
### description
|
||||||
|
|
||||||
This is an optional description for your relay, displayed in the NIP-11 response.
|
This is an optional description for your relay, displayed in the NIP-11 response.
|
||||||
|
|
||||||
Default is None.
|
Default is "A default config of the Chorus relay".
|
||||||
|
|
||||||
### contact
|
### contact
|
||||||
|
|
||||||
@ -177,7 +179,7 @@ How verbose to log library issues and other general issues
|
|||||||
|
|
||||||
Possible values are: Trace, Debug, Info, Warn, Error
|
Possible values are: Trace, Debug, Info, Warn, Error
|
||||||
|
|
||||||
Default is Warn
|
Default is Info
|
||||||
|
|
||||||
### client_log_level
|
### client_log_level
|
||||||
|
|
||||||
@ -185,5 +187,5 @@ How verbose to log issues with client requests
|
|||||||
|
|
||||||
Possible values are: Trace, Debug, Info, Warn, Error
|
Possible values are: Trace, Debug, Info, Warn, Error
|
||||||
|
|
||||||
Default is Error
|
Default is Info
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user