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 ///
80 /// Also see the `metrics` example in the `examples` directory of the repository, which contains
81 /// further help for getting up to speed with this feature.
82 #[cfg(feature = "metrics-collection")]
83 pub metrics_config: MetricsConfig,
84
85 /// Allows you to differentiate between multiple clients with
86 /// [the `tracing` crate](https://docs.rs/tracing).
87 ///
88 /// This library logs a variety of trace, debug, info, warning and error messages using the
89 /// `tracing` crate. An example log line using the default `tracing_subscriber` output format
90 /// might look like this:
91 ///
92 /// ```text
93 /// 2022-02-07T10:44:23.297571Z INFO client_loop: twitch_irc::client::event_loop: Making a new pool connection, new ID is 0
94 /// ```
95 ///
96 /// You may optionally set this configuration variable to some string, which will then
97 /// modify all log messages by giving the `client_loop` span the `name` attribute:
98 ///
99 /// ```
100 /// use std::borrow::Cow;
101 /// use twitch_irc::ClientConfig;
102 ///
103 /// let mut config = ClientConfig::default();
104 /// config.tracing_identifier = Some(Cow::Borrowed("bot_one"));
105 /// ```
106 ///
107 /// All log output will then look like this (note the additional `{name=bot_one}`:
108 ///
109 /// ```text
110 /// 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
111 /// ```
112 ///
113 /// Essentially, this library makes use of `tracing` `Span`s to differentiate between
114 /// different async tasks and to also differentiate log messages coming from different
115 /// connections.
116 ///
117 /// Specifying this option will further allow you to differentiate between multiple
118 /// clients if your application is running multiple of them. It will add the `name=your_value`
119 /// attribute to the `client_loop` span, which is root for all further deeper spans in the
120 /// client. This means that all log output from a single client will all be under that span,
121 /// with that name.
122 pub tracing_identifier: Option<Cow<'static, str>>,
123}
124
125/// Used to configure the options around metrics collection using the `prometheus` crate.
126///
127/// For more details, see [`ClientConfig::metrics_config`].
128#[cfg(feature = "metrics-collection")]
129#[derive(Debug)]
130pub enum MetricsConfig {
131 /// Metrics are not collected. Metrics are not registered with any registry.
132 ///
133 /// Useful if an application only requires monitoring on some of its running `twitch-irc`
134 /// clients.
135 Disabled,
136 /// Metrics are collected. The metrics are immediately registered when
137 /// [`TwitchIRCClient::new`](crate::TwitchIRCClient::new) is called.
138 Enabled {
139 /// Add these "constant labels" to all metrics produced by this client. This allows you
140 /// to, for example, differentiate between multiple clients by naming them, or you
141 /// may wish to place other relevant metadata pertaining to the whole client on all the
142 /// metrics.
143 ///
144 /// This defaults to an empty map.
145 constant_labels: HashMap<String, String>,
146 /// Specifies what [`Registry`](prometheus::Registry) to register all metrics for this
147 /// client with.
148 ///
149 /// Defaults to `None`, in which case the metrics are registered with the
150 /// [global default registry of the `prometheus` crate](prometheus::default_registry).
151 metrics_registry: Option<prometheus::Registry>,
152 },
153}
154
155#[cfg(feature = "metrics-collection")]
156impl Default for MetricsConfig {
157 fn default() -> Self {
158 MetricsConfig::Enabled {
159 constant_labels: HashMap::new(),
160 metrics_registry: None,
161 }
162 }
163}
164
165impl<L: LoginCredentials> ClientConfig<L> {
166 /// Create a new configuration from the given login credentials, with all other configuration
167 /// options being default.
168 pub fn new_simple(login_credentials: L) -> ClientConfig<L> {
169 ClientConfig {
170 login_credentials,
171 max_channels_per_connection: 90,
172
173 max_waiting_messages_per_connection: 5,
174 time_per_message: Duration::from_millis(150),
175
176 // 1 connection every 2 seconds seems to work well
177 connection_rate_limiter: Arc::new(Semaphore::new(1)),
178 new_connection_every: Duration::from_secs(2),
179 connect_timeout: Duration::from_secs(20),
180
181 #[cfg(feature = "metrics-collection")]
182 metrics_config: MetricsConfig::default(),
183 tracing_identifier: None,
184 }
185 }
186}
187
188impl Default for ClientConfig<StaticLoginCredentials> {
189 fn default() -> ClientConfig<StaticLoginCredentials> {
190 ClientConfig::new_simple(StaticLoginCredentials::anonymous())
191 }
192}