1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
use crate::login::{LoginCredentials, StaticLoginCredentials};
use std::borrow::Cow;
#[cfg(feature = "metrics-collection")]
use std::collections::HashMap;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::Semaphore;
/// Configures settings for a `TwitchIRCClient`.
#[derive(Debug)]
pub struct ClientConfig<L: LoginCredentials> {
/// Gets a set of credentials every time the client needs to log in on a new connection.
/// See [`LoginCredentials`] for details.
pub login_credentials: L,
/// A new connection will automatically be created if a channel is joined and all
/// currently established connections have joined at least this many channels.
pub max_channels_per_connection: usize,
/// A new connection will automatically be created if any message is to be sent
/// and all currently established connections have recently sent more than this many
/// messages (time interval is defined by `max_waiting_messages_duration_window`)
pub max_waiting_messages_per_connection: usize,
/// We assume messages to be "waiting" for this amount of time after sending them out, e.g.
/// typically 100 or 150 milliseconds (purely a value that has been measured/observed,
/// not documented or fixed in any way)
pub time_per_message: Duration,
/// rate-limits the opening of new connections. By default this is constructed with 1 permit
/// only, which means connections cannot be opened in parallel. If this is set to more than 1
/// permit, then that many connections can be opened in parallel.
///
/// This is designed to be wrapped in an Arc to allow it to be shared between multiple
/// TwitchIRCClient instances.
pub connection_rate_limiter: Arc<Semaphore>,
/// Allow a new connection to be made after this period has elapsed. By default this is set
/// to 2 seconds, and combined with the permits=1 of the semaphore, allows one connection
/// to be made every 2 seconds.
///
/// More specifically, after taking the permit from the semaphore, the permit will be put
/// back after this period has elapsed.
pub new_connection_every: Duration,
/// Imposes a general timeout for new connections. This is in place in addition to possible
/// operating system timeouts (E.g. for new TCP connections), since additional "connect" work
/// takes place after the TCP connection is opened, e.g. to set up TLS or perform a WebSocket
/// handshake. Default value: 20 seconds.
pub connect_timeout: Duration,
/// Disable or enable and configure the collection of metrics on this `TwitchIRCClient`
/// using the `prometheus` crate. See more information about the possible options on the
/// [`MetricsConfig`] enum.
///
/// This crate is currently capable of exporting the following prometheus metrics:
/// * `twitchirc_messages_received` with label `command` counts all incoming messages. (Counter)
///
/// * `twitchirc_messages_sent` counts messages sent out, with a `command` label. (Counter)
///
/// * `twitchirc_channels` with `type=allocated/confirmed` counts how many channels
/// you are joined to (Gauge). Allocated channels are joins that passed through the `TwitchIRCClient`
/// but may be waiting e.g. for the connection to finish connecting. Once a
/// confirmation response is received by Twitch that the channel was joined successfully,
/// that channel is additionally `confirmed`.
///
/// * `twitchirc_connections` counts how many connections this client has in use (Gauge).
/// The label `state=initializing/open` identifies how many connections are
/// in the process of connecting (`initializing`) vs how many connections are already established (`open`).
///
/// * `twitchirc_connections_failed` counts every time a connection fails (Counter). Note however, depending
/// on conditions e.g. how many channels were joined on that channel, there can be cases where
/// a connection failing would not mandate the creation of a new connection (e.g. if
/// you have parted channels on other connections, making it so all the channels the failed
/// connection was joined to can be re-joined on those already existing connections).
///
/// * `twitchirc_connections_created` on the other hand tracks how many times, since
/// the creation of the client, a new connection has been made.
#[cfg(feature = "metrics-collection")]
pub metrics_config: MetricsConfig,
/// Allows you to differentiate between multiple clients with
/// [the `tracing` crate](https://docs.rs/tracing).
///
/// This library logs a variety of trace, debug, info, warning and error messages using the
/// `tracing` crate. An example log line using the default `tracing_subscriber` output format
/// might look like this:
///
/// ```text
/// 2022-02-07T10:44:23.297571Z INFO client_loop: twitch_irc::client::event_loop: Making a new pool connection, new ID is 0
/// ```
///
/// You may optionally set this configuration variable to some string, which will then
/// modify all log messages by giving the `client_loop` span the `name` attribute:
///
/// ```
/// use std::borrow::Cow;
/// use twitch_irc::ClientConfig;
///
/// let mut config = ClientConfig::default();
/// config.tracing_identifier = Some(Cow::Borrowed("bot_one"));
/// ```
///
/// All log output will then look like this (note the additional `{name=bot_one}`:
///
/// ```text
/// 2022-02-07T10:48:34.769272Z INFO client_loop{name=bot_one}: twitch_irc::client::event_loop: Making a new pool connection, new ID is 0
/// ```
///
/// Essentially, this library makes use of `tracing` `Span`s to differentiate between
/// different async tasks and to also differentiate log messages coming from different
/// connections.
///
/// Specifying this option will further allow you to differentiate between multiple
/// clients if your application is running multiple of them. It will add the `name=your_value`
/// attribute to the `client_loop` span, which is root for all further deeper spans in the
/// client. This means that all log output from a single client will all be under that span,
/// with that name.
pub tracing_identifier: Option<Cow<'static, str>>,
}
/// Used to configure the options around metrics collection using the `prometheus` crate.
#[cfg(feature = "metrics-collection")]
#[derive(Debug)]
pub enum MetricsConfig {
/// Metrics are not collected. Metrics are not registered with any registry.
///
/// Useful if an application only requires monitoring on some of its running `twitch-irc`
/// clients.
Disabled,
/// Metrics are collected. The metrics are immediately registered when
/// [`TwitchIRCClient::new`](crate::TwitchIRCClient::new) is called.
Enabled {
/// Add these "constant labels" to all metrics produced by this client. This allows you
/// to, for example, differentiate between multiple clients by naming them, or you
/// may wish to place other relevant metadata pertaining to the whole client on all the
/// metrics.
///
/// This defaults to an empty map.
constant_labels: HashMap<String, String>,
/// Specifies what [`Registry`](prometheus::Registry) to register all metrics for this
/// client with.
///
/// Defaults to `None`, in which case the metrics are registered with the
/// [global default registry of the `prometheus` crate](prometheus::default_registry).
metrics_registry: Option<prometheus::Registry>,
},
}
#[cfg(feature = "metrics-collection")]
impl Default for MetricsConfig {
fn default() -> Self {
MetricsConfig::Enabled {
constant_labels: HashMap::new(),
metrics_registry: None,
}
}
}
impl<L: LoginCredentials> ClientConfig<L> {
/// Create a new configuration from the given login credentials, with all other configuration
/// options being default.
pub fn new_simple(login_credentials: L) -> ClientConfig<L> {
ClientConfig {
login_credentials,
max_channels_per_connection: 90,
max_waiting_messages_per_connection: 5,
time_per_message: Duration::from_millis(150),
// 1 connection every 2 seconds seems to work well
connection_rate_limiter: Arc::new(Semaphore::new(1)),
new_connection_every: Duration::from_secs(2),
connect_timeout: Duration::from_secs(20),
#[cfg(feature = "metrics-collection")]
metrics_config: MetricsConfig::default(),
tracing_identifier: None,
}
}
}
impl Default for ClientConfig<StaticLoginCredentials> {
fn default() -> ClientConfig<StaticLoginCredentials> {
ClientConfig::new_simple(StaticLoginCredentials::anonymous())
}
}