twitch_irc/
config.rs

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