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}