diff --git a/src/goose.rs b/src/goose.rs index 8665d1ff..f22a56f0 100644 --- a/src/goose.rs +++ b/src/goose.rs @@ -12,17 +12,18 @@ //! A [`GooseTaskSet`](./struct.GooseTaskSet.html) is created by passing in a `&str` name to the `new` function, for example: //! //! ```rust -//! use goose::prelude::*; +//! use goose::prelude::*; //! -//! let mut loadtest_tasks = taskset!("LoadtestTasks"); +//! let mut loadtest_tasks = taskset!("LoadtestTasks"); //! ``` //! //! ### Task Set Weight //! -//! A weight can be assigned to a task set, controlling how often it is assigned to user -//! threads. The larger the value of weight, the more it will be assigned to users. In the -//! following example, `FooTasks` will be assigned to users twice as often as `Bar` tasks. -//! We could have just added a weight of `2` to `FooTasks` and left the default weight of `1` +//! A weight can be applied to a task set, controlling how often it is assigned to +//! [`GooseUser`](../goose/struct.GooseUser.html) threads. The larger the integer value +//! of weight, the more the task set will be assigned to user threads. In the following +//! example, `FooTasks` will be assigned to users twice as often as `Bar` tasks. We could +//! have just added a weight of `2` to `FooTasks` and left the default weight of `1` //! assigned to `BarTasks` for the same weighting: //! //! ```rust @@ -45,10 +46,10 @@ //! hosts to different task sets if this is desirable: //! //! ```rust -//! use goose::prelude::*; +//! use goose::prelude::*; //! -//! let mut foo_tasks = taskset!("FooTasks").set_host("http://www.local"); -//! let mut bar_tasks = taskset!("BarTasks").set_host("http://www2.local"); +//! let mut foo_tasks = taskset!("FooTasks").set_host("http://www.local"); +//! let mut bar_tasks = taskset!("BarTasks").set_host("http://www2.local"); //! ``` //! //! ### Task Set Wait Time @@ -60,10 +61,10 @@ //! sleep 5 to 10 seconds after each task completes. //! //! ```rust -//! use goose::prelude::*; +//! use goose::prelude::*; //! -//! let mut foo_tasks = taskset!("FooTasks").set_wait_time(0, 3).unwrap(); -//! let mut bar_tasks = taskset!("BarTasks").set_wait_time(5, 10).unwrap(); +//! let mut foo_tasks = taskset!("FooTasks").set_wait_time(0, 3).unwrap(); +//! let mut bar_tasks = taskset!("BarTasks").set_wait_time(5, 10).unwrap(); //! ``` //! ## Creating Tasks //! @@ -71,16 +72,16 @@ //! will be executed each time the task is run. //! //! ```rust -//! use goose::prelude::*; +//! use goose::prelude::*; //! -//! let mut a_task = task!(task_function); +//! let mut a_task = task!(task_function); //! -//! /// A very simple task that loads the front page. -//! async fn task_function(user: &GooseUser) -> GooseTaskResult { -//! let _goose = user.get("/").await?; +//! /// A very simple task that loads the front page. +//! async fn task_function(user: &GooseUser) -> GooseTaskResult { +//! let _goose = user.get("/").await?; //! -//! Ok(()) -//! } +//! Ok(()) +//! } //! ``` //! //! ### Task Name @@ -89,16 +90,16 @@ //! made by the task. //! //! ```rust -//! use goose::prelude::*; +//! use goose::prelude::*; //! -//! let mut a_task = task!(task_function).set_name("a"); +//! let mut a_task = task!(task_function).set_name("a"); //! -//! /// A very simple task that loads the front page. -//! async fn task_function(user: &GooseUser) -> GooseTaskResult { -//! let _goose = user.get("/").await?; +//! /// A very simple task that loads the front page. +//! async fn task_function(user: &GooseUser) -> GooseTaskResult { +//! let _goose = user.get("/").await?; //! -//! Ok(()) -//! } +//! Ok(()) +//! } //! ``` //! //! ### Task Weight @@ -143,74 +144,74 @@ //! `a_task` runs before `b_task`, which runs before `c_task`: //! //! ```rust -//! use goose::prelude::*; +//! use goose::prelude::*; //! -//! let mut a_task = task!(a_task_function).set_sequence(1); -//! let mut b_task = task!(b_task_function).set_sequence(2); -//! let mut c_task = task!(c_task_function); +//! let mut a_task = task!(a_task_function).set_sequence(1); +//! let mut b_task = task!(b_task_function).set_sequence(2); +//! let mut c_task = task!(c_task_function); //! -//! /// A very simple task that loads the "a" page. -//! async fn a_task_function(user: &GooseUser) -> GooseTaskResult { -//! let _goose = user.get("/a/").await?; +//! /// A very simple task that loads the "a" page. +//! async fn a_task_function(user: &GooseUser) -> GooseTaskResult { +//! let _goose = user.get("/a/").await?; //! -//! Ok(()) -//! } +//! Ok(()) +//! } //! -//! /// Another very simple task that loads the "b" page. -//! async fn b_task_function(user: &GooseUser) -> GooseTaskResult { -//! let _goose = user.get("/b/").await?; +//! /// Another very simple task that loads the "b" page. +//! async fn b_task_function(user: &GooseUser) -> GooseTaskResult { +//! let _goose = user.get("/b/").await?; //! -//! Ok(()) -//! } +//! Ok(()) +//! } //! -//! /// Another very simple task that loads the "c" page. -//! async fn c_task_function(user: &GooseUser) -> GooseTaskResult { -//! let _goose = user.get("/c/").await?; +//! /// Another very simple task that loads the "c" page. +//! async fn c_task_function(user: &GooseUser) -> GooseTaskResult { +//! let _goose = user.get("/c/").await?; //! -//! Ok(()) -//! } +//! Ok(()) +//! } //! ``` //! //! ### Task On Start //! //! Tasks can be flagged to only run when a user first starts. This can be useful if you'd //! like your load test to use a logged-in user. It is possible to assign sequences and weights -//! to `on_start` functions if you want to have multiple tasks run in a specific order at start -//! time, and/or the tasks to run multiple times. A task can be flagged to run both on start -//! and on stop. +//! to [`on_start`](./struct.GooseTask.html#method.set_on_start) functions if you want to have +//! multiple tasks run in a specific order at start time, and/or the tasks to run multiple times. +//! A task can be flagged to run both on start and on stop. //! //! ```rust -//! use goose::prelude::*; +//! use goose::prelude::*; //! -//! let mut a_task = task!(a_task_function).set_sequence(1).set_on_start(); +//! let mut a_task = task!(a_task_function).set_sequence(1).set_on_start(); //! -//! /// A very simple task that loads the "a" page. -//! async fn a_task_function(user: &GooseUser) -> GooseTaskResult { -//! let _goose = user.get("/a/").await?; +//! /// A very simple task that loads the "a" page. +//! async fn a_task_function(user: &GooseUser) -> GooseTaskResult { +//! let _goose = user.get("/a/").await?; //! -//! Ok(()) -//! } +//! Ok(()) +//! } //! ``` //! //! ### Task On Stop //! //! Tasks can be flagged to only run when a user stops. This can be useful if you'd like your //! load test to simulate a user logging out when it finishes. It is possible to assign sequences -//! and weights to `on_stop` functions if you want to have multiple tasks run in a specific order -//! at stop time, and/or the tasks to run multiple times. A task can be flagged to run both on -//! start and on stop. +//! and weights to [`on_stop`](./struct.GooseTask.html#method.set_on_stop) functions if you want to +//! have multiple tasks run in a specific order at stop time, and/or the tasks to run multiple +//! times. A task can be flagged to run both on start and on stop. //! //! ```rust -//! use goose::prelude::*; +//! use goose::prelude::*; //! -//! let mut b_task = task!(b_task_function).set_sequence(2).set_on_stop(); +//! let mut b_task = task!(b_task_function).set_sequence(2).set_on_stop(); //! -//! /// Another very simple task that loads the "b" page. -//! async fn b_task_function(user: &GooseUser) -> GooseTaskResult { -//! let _goose = user.get("/b/").await?; +//! /// Another very simple task that loads the "b" page. +//! async fn b_task_function(user: &GooseUser) -> GooseTaskResult { +//! let _goose = user.get("/b/").await?; //! -//! Ok(()) -//! } +//! Ok(()) +//! } //! ``` //! //! ## Controlling User @@ -219,7 +220,8 @@ //! assigning a single [`GooseTaskSet`](./struct.GooseTaskSet.html) to each. This user is //! then used to generate load. Behind the scenes, Goose is leveraging the //! [`reqwest::client`](https://docs.rs/reqwest/*/reqwest/struct.Client.html) -//! to load web pages, and Goose can therefor do anything Reqwest can do. +//! to load web pages, and Goose can therefor do anything [`reqwest`](https://docs.rs/reqwest/) +//! can do. //! //! The most common request types are [`GET`](./struct.GooseUser.html#method.get) and //! [`POST`](./struct.GooseUser.html#method.post), but [`HEAD`](./struct.GooseUser.html#method.head), @@ -231,16 +233,16 @@ //! Automatically prepends the correct host. //! //! ```rust -//! use goose::prelude::*; +//! use goose::prelude::*; //! -//! let mut task = task!(get_function); +//! let mut task = task!(get_function); //! -//! /// A very simple task that makes a GET request. -//! async fn get_function(user: &GooseUser) -> GooseTaskResult { -//! let _goose = user.get("/path/to/foo/").await?; +//! /// A very simple task that makes a GET request. +//! async fn get_function(user: &GooseUser) -> GooseTaskResult { +//! let _goose = user.get("/path/to/foo/").await?; //! -//! Ok(()) -//! } +//! Ok(()) +//! } //! ``` //! //! The returned response is a [`reqwest::Response`](https://docs.rs/reqwest/*/reqwest/struct.Response.html) @@ -254,16 +256,16 @@ //! [`reqwest::Response`](https://docs.rs/reqwest/*/reqwest/struct.Response.html) //! //! ```rust -//! use goose::prelude::*; +//! use goose::prelude::*; //! -//! let mut task = task!(post_function); +//! let mut task = task!(post_function); //! -//! /// A very simple task that makes a POST request. -//! async fn post_function(user: &GooseUser) -> GooseTaskResult { -//! let _goose = user.post("/path/to/foo/", "string value to post").await?; +//! /// A very simple task that makes a POST request. +//! async fn post_function(user: &GooseUser) -> GooseTaskResult { +//! let _goose = user.post("/path/to/foo/", "string value to post").await?; //! -//! Ok(()) -//! } +//! Ok(()) +//! } //! ``` //! //! ## License @@ -274,7 +276,7 @@ //! you may not use this file except in compliance with the License. //! You may obtain a copy of the License at //! -//! http://www.apache.org/licenses/LICENSE-2.0 +//! [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) //! //! Unless required by applicable law or agreed to in writing, software //! distributed under the License is distributed on an "AS IS" BASIS, @@ -299,9 +301,10 @@ use url::Url; use crate::metrics::GooseMetric; use crate::{GooseConfiguration, GooseError, WeightedGooseTasks}; +/// By default Goose sets the following User-Agent header when making requests. static APP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION")); -/// task!(foo) expands to GooseTask::new(foo), but also does some boxing to work around a limitation in the compiler. +/// `task!(foo)` expands to `GooseTask::new(foo)`, but also does some boxing to work around a limitation in the compiler. #[macro_export] macro_rules! task { ($task_func:ident) => { @@ -311,7 +314,7 @@ macro_rules! task { }; } -/// taskset!("foo") expands to GooseTaskSet::new("foo"). +/// `taskset!("foo")` expands to `GooseTaskSet::new("foo")`. #[macro_export] macro_rules! taskset { ($name:tt) => { @@ -319,41 +322,48 @@ macro_rules! taskset { }; } -/// Goose tasks return a result, which is empty on success, or contains a GooseTaskError -/// on error. +/// Goose tasks return a result, which is empty on success, or contains a +/// [`GooseTaskError`](./enum.GooseTaskError.html) on error. pub type GooseTaskResult = Result<(), GooseTaskError>; -/// Definition of all errors Goose Tasks can return. +/// An enumeration of all errors a [`GooseTask`](./struct.GooseTask.html) can return. #[derive(Debug)] pub enum GooseTaskError { - /// Contains a reqwest::Error. + /// Wraps a [`reqwest::Error`](https://docs.rs/reqwest/*/reqwest/struct.Error.html). Reqwest(reqwest::Error), - /// Contains a url::ParseError. + /// Wraps a [`url::ParseError`](https://docs.rs/url/*/url/enum.ParseError.html). Url(url::ParseError), - /// The request failed. The `GooseRawRequest` that failed can be found in - /// `.raw_request`. - RequestFailed { raw_request: GooseRawRequest }, - /// The request was canceled (this happens when the throttle is enabled and - /// the load test finished). A `GooseRawRequest` has not yet been constructed, - // so is not available in this error. - RequestCanceled { source: flume::SendError }, + /// The request failed. + RequestFailed { + /// The [`GooseRawRequest`](./struct.GooseRawRequest.html) that failed. + raw_request: GooseRawRequest, + }, + /// The request was canceled. This happens when the throttle is enabled and the load + /// test finishes. + RequestCanceled { + /// Wraps a [`flume::SendError`](https://docs.rs/flume/*/flume/struct.SendError.html), + /// a [`GooseRawRequest`](./struct.GooseRawRequest.html) has not yet been constructed. + source: flume::SendError, + }, /// There was an error sending the metrics for a request to the parent thread. - /// The `GooseRawRequest` that was not recorded can be extracted from the error - /// chain, available inside `.source`. MetricsFailed { + /// Wraps a [`flume::SendError`](https://docs.rs/flume/*/flume/struct.SendError.html), + /// which contains the [`GooseMetric`](../metrics/enum.GooseMetric.html) that wasn't sent. source: flume::SendError, }, - /// Attempt to send debug detail to logger failed. - /// There was an error sending debug information to the logger thread. The - /// `GooseDebug` that was not logged can be extracted from the error chain, - /// available inside `.source`. + /// There was an error sending debug information to the logger thread. LoggerFailed { + /// Wraps a [`flume::SendError`](https://docs.rs/flume/*/flume/struct.SendError.html), + /// which contains the [`GooseDebug`](./struct.GooseDebug.html) that wasn't sent. source: flume::SendError>, }, - /// Attempted an unrecognized HTTP request method. The unrecognized method - /// is available in `.method`. - InvalidMethod { method: Method }, + /// Attempted an unrecognized HTTP request method. + InvalidMethod { + /// The unrecognized HTTP request method. + method: Method, + }, } +/// Implement a helper to provide a text description of all possible types of errors. impl GooseTaskError { fn describe(&self) -> &str { match *self { @@ -370,8 +380,9 @@ impl GooseTaskError { } } -// Define how to display errors. +/// Implement format trait to allow displaying errors. impl fmt::Display for GooseTaskError { + // Implement display of error with `{}` marker. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { GooseTaskError::Reqwest(ref source) => { @@ -423,8 +434,10 @@ impl From for GooseTaskError { } /// When the throttle is enabled and the load test ends, the throttle channel is -/// shut down. This causes a SendError, which gets automatically converted to -// `RequestCanceled`. +/// shut down. This causes a +/// [`flume::SendError`](https://docs.rs/flume/*/flume/struct.SendError.html), +/// which gets automatically converted to `RequestCanceled`. +/// [`RequestCanceled`](./enum.GooseTaskError.html#variant.RequestCanceled) impl From> for GooseTaskError { fn from(source: flume::SendError) -> GooseTaskError { GooseTaskError::RequestCanceled { source } @@ -450,7 +463,8 @@ impl From>> for GooseTaskError { pub struct GooseTaskSet { /// The name of the task set. pub name: String, - /// An integer reflecting where this task set lives in the internal `GooseTest.task_sets` vector. + /// An integer reflecting where this task set lives in the internal + /// [`GooseAttack`](../struct.GooseAttack.html)`.task_sets` vector. pub task_sets_index: usize, /// An integer value that controls the frequency that this task set will be assigned to a user. pub weight: usize, @@ -458,26 +472,34 @@ pub struct GooseTaskSet { pub min_wait: usize, /// An integer value indicating the maximum number of seconds a user will sleep after running a task. pub max_wait: usize, - /// A vector containing one copy of each GooseTask that will run by users running this task set. + /// A vector containing one copy of each [`GooseTask`](./struct.GooseTask.html) that will + /// run by users running this task set. pub tasks: Vec, - /// A fully scheduled and weighted vector of integers (pointing to GooseTasks) and GooseTask names. + /// A fully scheduled and weighted vector of integers (pointing to + /// [`GooseTask`](./struct.GooseTask.html)s and [`GooseTask`](./struct.GooseTask.html) names. pub weighted_tasks: WeightedGooseTasks, - /// A vector of vectors of integers, controlling the sequence and order on_start GooseTasks are run when the user first starts. + /// A vector of vectors of integers, controlling the sequence and order + /// [`on_start`](./struct.GooseTask.html#method.set_on_start) + /// [`GooseTask`](./struct.GooseTask.html)s are run when the user first starts. pub weighted_on_start_tasks: WeightedGooseTasks, - /// A vector of vectors of integers, controlling the sequence and order on_stop GooseTasks are run when the user stops. + /// A vector of vectors of integers, controlling the sequence and order + /// [`on_stop`](./struct.GooseTask.html#method.set_on_stop) + /// [`GooseTask`](./struct.GooseTask.html)s are run when the user first starts. pub weighted_on_stop_tasks: WeightedGooseTasks, - /// An optional default host to run this TaskSet against. + /// An optional default host to run this `GooseTaskSet` against. pub host: Option, } impl GooseTaskSet { - /// Creates a new GooseTaskSet. Once created, GooseTasks must be assigned to it, and finally it must be - /// registered with the GooseAttack object. The returned object must be stored in a mutable value. + /// Creates a new [`GooseTaskSet`](./struct.GooseTaskSet.html). Once created, a + /// [`GooseTask`](./struct.GooseTask.html) must be assigned to it, and finally it must + /// be registered with the [`GooseAttack`](../struct.GooseAttack.html) object. The + /// returned object must be stored in a mutable value. /// /// # Example /// ```rust - /// use goose::prelude::*; + /// use goose::prelude::*; /// - /// let mut example_tasks = taskset!("ExampleTasks"); + /// let mut example_tasks = taskset!("ExampleTasks"); /// ``` pub fn new(name: &str) -> Self { trace!("new taskset: name: {}", &name); @@ -495,22 +517,24 @@ impl GooseTaskSet { } } - /// Registers a GooseTask with a GooseTaskSet, where it is stored in the GooseTaskSet.tasks vector. The - /// function associated with the task will be run during the load test. + /// Registers a [`GooseTask`](./struct.GooseTask.html) with a + /// [`GooseTaskSet`](./struct.GooseTaskSet.html), where it is stored in the + /// [`GooseTaskSet`](./struct.GooseTaskSet.html)`.tasks` vector. The function + /// associated with the task will be run during the load test. /// /// # Example /// ```rust - /// use goose::prelude::*; + /// use goose::prelude::*; /// - /// let mut example_tasks = taskset!("ExampleTasks"); - /// example_tasks.register_task(task!(a_task_function)); + /// let mut example_tasks = taskset!("ExampleTasks"); + /// example_tasks.register_task(task!(a_task_function)); /// - /// /// A very simple task that loads the "a" page. - /// async fn a_task_function(user: &GooseUser) -> GooseTaskResult { - /// let _goose = user.get("/a/").await?; + /// /// A very simple task that loads the "a" page. + /// async fn a_task_function(user: &GooseUser) -> GooseTaskResult { + /// let _goose = user.get("/a/").await?; /// - /// Ok(()) - /// } + /// Ok(()) + /// } /// ``` pub fn register_task(mut self, mut task: GooseTask) -> Self { trace!("{} register_task: {}", self.name, task.name); @@ -554,9 +578,9 @@ impl GooseTaskSet { /// /// # Example /// ```rust - /// use goose::prelude::*; + /// use goose::prelude::*; /// - /// let mut example_tasks = taskset!("ExampleTasks").set_host("http://10.1.1.42"); + /// let mut example_tasks = taskset!("ExampleTasks").set_host("http://10.1.1.42"); /// ``` pub fn set_host(mut self, host: &str) -> Self { trace!("{} set_host: {}", self.name, host); @@ -602,7 +626,7 @@ impl GooseTaskSet { } } -/// Commands sent between the parent and user threads, and between manager and +/// Commands sent from the parent thread to the user threads, and from the manager to the /// worker processes. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum GooseUserCommand { @@ -610,7 +634,7 @@ pub enum GooseUserCommand { Wait, /// Tell worker process to start load test. Run, - /// Tell user thread to exit. + /// Tell user thread or worker process to exit. Exit, } @@ -626,6 +650,7 @@ pub enum GooseMethod { } /// Display method in upper case. impl fmt::Display for GooseMethod { + // Implement display of `GooseMethod` with `{}` marker. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { GooseMethod::Delete => write!(f, "DELETE"), @@ -638,7 +663,9 @@ impl fmt::Display for GooseMethod { } } -fn goose_method_from_method(method: Method) -> Result { +/// Convert [`http::method::Method`](https://docs.rs/http/0.2.4/http/method/struct.Method.html) +/// to [`GooseMethod`](./enum.GooseMethod.html). +pub fn goose_method_from_method(method: Method) -> Result { Ok(match method { Method::DELETE => GooseMethod::Delete, Method::GET => GooseMethod::Get, @@ -711,10 +738,12 @@ impl GooseRawRequest { } } + // Record how long the `response_time` took. fn set_response_time(&mut self, response_time: u128) { self.response_time = response_time as u64; } + // Record the returned `status_code`. fn set_status_code(&mut self, status_code: Option) { self.status_code = match status_code { Some(status_code) => status_code.as_u16(), @@ -844,11 +873,13 @@ impl GooseRequest { debug!("incremented {} counter: {}", status_code, counter); } } +/// Implement ordering for GooseRequest. impl Ord for GooseRequest { fn cmp(&self, other: &Self) -> Ordering { (&self.method, &self.path).cmp(&(&other.method, &other.path)) } } +/// Implement partial-ordering for GooseRequest. impl PartialOrd for GooseRequest { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) @@ -858,7 +889,9 @@ impl PartialOrd for GooseRequest { /// The response to a GooseRequest #[derive(Debug)] pub struct GooseResponse { + /// The request that this is a response to. pub request: GooseRawRequest, + /// The response. pub response: Result, } impl GooseResponse { @@ -867,7 +900,8 @@ impl GooseResponse { } } -/// Object created by log_debug() and written to log to assist in debugging. +/// Object created by [`log_debug()`](struct.GooseUser.html#method.log_debug) and written +/// to log to assist in debugging. #[derive(Debug, Serialize)] pub struct GooseDebug { /// String to identify the source of the log message. @@ -902,7 +936,8 @@ impl GooseDebug { /// The elements needed to build an individual user state on a Gaggle Worker. #[derive(Debug, Clone)] pub struct GaggleUser { - /// An index into the internal `GooseTest.task_sets` vector, indicating which GooseTaskSet is running. + /// An index into the internal [`GooseAttack`](../struct.GooseAttack.html)`.task_sets` + /// vector, indicating which [`GooseTaskSet`](./struct.GooseTaskSet.html) is running. pub task_sets_index: usize, /// The base URL to prepend to all relative paths. pub base_url: Arc>, @@ -937,12 +972,14 @@ impl GaggleUser { } } -/// An individual user state, repeatedly running all GooseTasks in a specific GooseTaskSet. +/// An individual user state, repeatedly running all [`GooseTask`](./struct.GooseTask.html)s +/// in a specific [`GooseTaskSet`](./struct.GooseTaskSet.html). #[derive(Debug, Clone)] pub struct GooseUser { - /// The Instant when this GooseUser client started. + /// The Instant when this `GooseUser` client started. pub started: Instant, - /// An index into the internal `GooseTest.task_sets` vector, indicating which GooseTaskSet is running. + /// An index into the internal [`GooseAttack`](../struct.GooseAttack.html)`.task_sets` + /// vector, indicating which [`GooseTaskSet`](./struct.GooseTaskSet.html) is running. pub task_sets_index: usize, /// Client used to make requests, managing sessions and cookies. pub client: Arc>, @@ -954,17 +991,20 @@ pub struct GooseUser { pub min_wait: usize, /// Maximum amount of time to sleep after running a task. pub max_wait: usize, - /// A local copy of the global GooseConfiguration. + /// A local copy of the global [`GooseConfiguration`](../struct.GooseConfiguration.html). pub config: GooseConfiguration, /// Channel to logger. pub debug_logger: Option>>, /// Channel to throttle. pub throttle: Option>, - /// Normal tasks are optionally throttled, test_start and test_stop tasks are not. + /// Normal tasks are optionally throttled, + /// [`test_start`](../struct.GooseAttack.html#method.test_start) and + /// [`test_stop`](../struct.GooseAttack.html#method.test_stop) tasks are not. pub is_throttled: bool, /// Channel to parent. pub channel_to_parent: Option>, - /// An index into the internal `GooseTest.weighted_users, indicating which weighted GooseTaskSet is running. + /// An index into the internal [`GooseAttack`](../struct.GooseAttack.html)`.weighted_users` + /// vector, indicating which weighted `GooseUser` is running. pub weighted_users_index: usize, /// A weighted list of all tasks that run when the user first starts. pub weighted_on_start_tasks: WeightedGooseTasks, @@ -1018,19 +1058,22 @@ impl GooseUser { let mut single_user = GooseUser::new(0, base_url, 0, 0, configuration, 0)?; // Only one user, so index is 0. single_user.weighted_users_index = 0; - // Do not throttle test_start (setup) and test_stop (teardown) tasks. + // Do not throttle [`test_start`](../struct.GooseAttack.html#method.test_start) (setup) and + // [`test_stop`](../struct.GooseAttack.html#method.test_stop) (teardown) tasks. single_user.is_throttled = false; Ok(single_user) } - /// A helper that prepends a base_url to all relative paths. + /// A helper that prepends a `base_url` to all relative paths. /// - /// A base_url is determined per user thread, using the following order + /// A `base_url` is determined per user thread, using the following order /// of precedence: /// 1. `--host` (host specified on the command line when running load test) - /// 2. `GooseTaskSet.host` (default host defined for the current task set) - /// 3. `GooseAttack.host` (default host defined for the current load test) + /// 2. [`GooseTaskSet`](./struct.GooseTaskSet.html)`.host` (default host defined for the + /// current task set) + /// 3. [`GooseDefault::Host`](../enum.GooseDefault.html#variant.Host) (default host + /// defined for the current load test) pub async fn build_url(&self, path: &str) -> Result { // If URL includes a host, simply use it. if let Ok(parsed_path) = Url::parse(path) { @@ -1039,7 +1082,7 @@ impl GooseUser { } } - // Otherwise use the base_url. + // Otherwise use the `base_url`. Ok(self.base_url.read().await.join(path)?.to_string()) } @@ -1048,13 +1091,15 @@ impl GooseUser { /// /// (If you need to set headers, change timeouts, or otherwise make use of the /// [`reqwest::RequestBuilder`](https://docs.rs/reqwest/*/reqwest/struct.RequestBuilder.html) - /// object, you can instead call `goose_get` which returns a RequestBuilder, then - /// call `goose_send` to invoke the request.) + /// object, you can instead call [`goose_get`](./struct.GooseUser.html#method.goose_get) + /// which returns a + /// [`RequestBuilder`](https://docs.rs/reqwest/*/reqwest/struct.RequestBuilder.html), + /// then call + /// [`goose_send`](./struct.GooseUser.html#method.goose_send) to invoke the request.) /// - /// Calls to `user.get` return a `GooseResponse` object which contains a copy of - /// the request you made - /// ([`goose.request`](https://docs.rs/goose/*/goose/goose/struct.GooseRawRequest)), and the response - /// ([`goose.response`](https://docs.rs/reqwest/*/reqwest/struct.Response.html)). + /// Calls to `get()` return a [`GooseResponse`](./struct.GooseResponse.html) object which + /// contains a copy of the request you made ([`GooseRawRequest`](./struct.GooseRawRequest.html)), + /// and the response ([`reqwest::Response`](https://docs.rs/reqwest/*/reqwest/struct.Response.html)). /// /// # Example /// ```rust @@ -1079,10 +1124,9 @@ impl GooseUser { /// Automatically prepends the correct host. Naming a request only affects collected /// metrics. /// - /// Calls to `user.get_named` return a `GooseResponse` object which contains a copy of - /// the request you made - /// ([`goose.request`](https://docs.rs/goose/*/goose/goose/struct.GooseRawRequest)), and the response - /// ([`goose.response`](https://docs.rs/reqwest/*/reqwest/struct.Response.html)). + /// Calls to `get_named()` return a [`GooseResponse`](./struct.GooseResponse.html) object which + /// contains a copy of the request you made ([`GooseRawRequest`](./struct.GooseRawRequest.html)), + /// and the response ([`reqwest::Response`](https://docs.rs/reqwest/*/reqwest/struct.Response.html)). /// /// # Example /// ```rust @@ -1112,13 +1156,13 @@ impl GooseUser { /// /// (If you need to set headers, change timeouts, or otherwise make use of the /// [`reqwest::RequestBuilder`](https://docs.rs/reqwest/*/reqwest/struct.RequestBuilder.html) - /// object, you can instead call `goose_post` which returns a RequestBuilder, then - /// call `goose_send` to invoke the request.) + /// object, you can instead call `goose_post` which returns a + /// [`RequestBuilder`](https://docs.rs/reqwest/*/reqwest/struct.RequestBuilder.html), + /// then call `goose_send` to invoke the request.) /// - /// Calls to `user.post` return a `GooseResponse` object which contains a copy of - /// the request you made - /// ([`goose.request`](https://docs.rs/goose/*/goose/goose/struct.GooseRawRequest)), and the response - /// ([`goose.response`](https://docs.rs/reqwest/*/reqwest/struct.Response.html)). + /// Calls to `post()` return a [`GooseResponse`](./struct.GooseResponse.html) object which + /// contains a copy of the request you made ([`GooseRawRequest`](./struct.GooseRawRequest.html)), + /// and the response ([`reqwest::Response`](https://docs.rs/reqwest/*/reqwest/struct.Response.html)). /// /// # Example /// ```rust @@ -1143,10 +1187,9 @@ impl GooseUser { /// Automatically prepends the correct host. Naming a request only affects collected /// metrics. /// - /// Calls to `user.post` return a `GooseResponse` object which contains a copy of - /// the request you made - /// ([`goose.request`](https://docs.rs/goose/*/goose/goose/struct.GooseRawRequest)), and the response - /// ([`goose.response`](https://docs.rs/reqwest/*/reqwest/struct.Response.html)). + /// Calls to `post_named()` return a [`GooseResponse`](./struct.GooseResponse.html) object which + /// contains a copy of the request you made ([`GooseRawRequest`](./struct.GooseRawRequest.html)), + /// and the response ([`reqwest::Response`](https://docs.rs/reqwest/*/reqwest/struct.Response.html)). /// /// # Example /// ```rust @@ -1177,13 +1220,13 @@ impl GooseUser { /// /// (If you need to set headers, change timeouts, or otherwise make use of the /// [`reqwest::RequestBuilder`](https://docs.rs/reqwest/*/reqwest/struct.RequestBuilder.html) - /// object, you can instead call `goose_head` which returns a RequestBuilder, then - /// call `goose_send` to invoke the request.) + /// object, you can instead call `goose_head` which returns a + /// [`RequestBuilder`](https://docs.rs/reqwest/*/reqwest/struct.RequestBuilder.html), + /// then call `goose_send` to invoke the request.) /// - /// Calls to `user.head` return a `GooseResponse` object which contains a copy of - /// the request you made - /// ([`goose.request`](https://docs.rs/goose/*/goose/goose/struct.GooseRawRequest)), and the response - /// ([`goose.response`](https://docs.rs/reqwest/*/reqwest/struct.Response.html)). + /// Calls to `head()` return a [`GooseResponse`](./struct.GooseResponse.html) object which + /// contains a copy of the request you made ([`GooseRawRequest`](./struct.GooseRawRequest.html)), + /// and the response ([`reqwest::Response`](https://docs.rs/reqwest/*/reqwest/struct.Response.html)). /// /// # Example /// ```rust @@ -1208,10 +1251,9 @@ impl GooseUser { /// Automatically prepends the correct host. Naming a request only affects collected /// metrics. /// - /// Calls to `user.head` return a `GooseResponse` object which contains a copy of - /// the request you made - /// ([`goose.request`](https://docs.rs/goose/*/goose/goose/struct.GooseRawRequest)), and the response - /// ([`goose.response`](https://docs.rs/reqwest/*/reqwest/struct.Response.html)). + /// Calls to `head_named()` return a [`GooseResponse`](./struct.GooseResponse.html) object which + /// contains a copy of the request you made ([`GooseRawRequest`](./struct.GooseRawRequest.html)), + /// and the response ([`reqwest::Response`](https://docs.rs/reqwest/*/reqwest/struct.Response.html)). /// /// # Example /// ```rust @@ -1241,13 +1283,13 @@ impl GooseUser { /// /// (If you need to set headers, change timeouts, or otherwise make use of the /// [`reqwest::RequestBuilder`](https://docs.rs/reqwest/*/reqwest/struct.RequestBuilder.html) - /// object, you can instead call `goose_delete` which returns a RequestBuilder, + /// object, you can instead call `goose_delete` which returns a + /// [`RequestBuilder`](https://docs.rs/reqwest/*/reqwest/struct.RequestBuilder.html), /// then call `goose_send` to invoke the request.) /// - /// Calls to `user.delete` return a `GooseResponse` object which contains a copy of - /// the request you made - /// ([`goose.request`](https://docs.rs/goose/*/goose/goose/struct.GooseRawRequest)), and the response - /// ([`goose.response`](https://docs.rs/reqwest/*/reqwest/struct.Response.html)). + /// Calls to `delete()` return a [`GooseResponse`](./struct.GooseResponse.html) object which + /// contains a copy of the request you made ([`GooseRawRequest`](./struct.GooseRawRequest.html)), + /// and the response ([`reqwest::Response`](https://docs.rs/reqwest/*/reqwest/struct.Response.html)). /// /// # Example /// ```rust @@ -1272,10 +1314,9 @@ impl GooseUser { /// Automatically prepends the correct host. Naming a request only affects collected /// metrics. /// - /// Calls to `user.delete` return a `GooseResponse` object which contains a copy of - /// the request you made - /// ([`goose.request`](https://docs.rs/goose/*/goose/goose/struct.GooseRawRequest)), and the response - /// ([`goose.response`](https://docs.rs/reqwest/*/reqwest/struct.Response.html)). + /// Calls to `delete_named()` return a [`GooseResponse`](./struct.GooseResponse.html) object which + /// contains a copy of the request you made ([`GooseRawRequest`](./struct.GooseRawRequest.html)), + /// and the response ([`reqwest::Response`](https://docs.rs/reqwest/*/reqwest/struct.Response.html)). /// /// # Example /// ```rust @@ -1304,7 +1345,8 @@ impl GooseUser { /// [`reqwest::RequestBuilder`](https://docs.rs/reqwest/*/reqwest/struct.RequestBuilder.html) /// object for making a `GET` request. /// - /// (You must then call `goose_send` on this object to actually execute the request.) + /// (You must then call [`goose_send`](./struct.GooseUser.html#method.goose_send) on this + /// object to actually execute the request.) /// /// # Example /// ```rust @@ -1331,7 +1373,8 @@ impl GooseUser { /// [`reqwest::RequestBuilder`](https://docs.rs/reqwest/*/reqwest/struct.RequestBuilder.html) /// object for making a `POST` request. /// - /// (You must then call `goose_send` on this object to actually execute the request.) + /// (You must then call [`goose_send`](./struct.GooseUser.html#method.goose_send) on this + /// object to actually execute the request.) /// /// # Example /// ```rust @@ -1358,7 +1401,8 @@ impl GooseUser { /// [`reqwest::RequestBuilder`](https://docs.rs/reqwest/*/reqwest/struct.RequestBuilder.html) /// object for making a `HEAD` request. /// - /// (You must then call `goose_send` on this object to actually execute the request.) + /// (You must then call [`goose_send`](./struct.GooseUser.html#method.goose_send) on this + /// object to actually execute the request.) /// /// # Example /// ```rust @@ -1385,7 +1429,8 @@ impl GooseUser { /// [`reqwest::RequestBuilder`](https://docs.rs/reqwest/*/reqwest/struct.RequestBuilder.html) /// object for making a `PUT` request. /// - /// (You must then call `goose_send` on this object to actually execute the request.) + /// (You must then call [`goose_send`](./struct.GooseUser.html#method.goose_send) on this + /// object to actually execute the request.) /// /// # Example /// ```rust @@ -1412,7 +1457,8 @@ impl GooseUser { /// [`reqwest::RequestBuilder`](https://docs.rs/reqwest/*/reqwest/struct.RequestBuilder.html) /// object for making a `PATCH` request. /// - /// (You must then call `goose_send` on this object to actually execute the request.) + /// (You must then call [`goose_send`](./struct.GooseUser.html#method.goose_send) on this + /// object to actually execute the request.) /// /// # Example /// ```rust @@ -1439,7 +1485,8 @@ impl GooseUser { /// [`reqwest::RequestBuilder`](https://docs.rs/reqwest/*/reqwest/struct.RequestBuilder.html) /// object for making a `DELETE` request. /// - /// (You must then call `goose_send` on this object to actually execute the request.) + /// (You must then call [`goose_send`](./struct.GooseUser.html#method.goose_send) on this + /// object to actually execute the request.) /// /// # Example /// ```rust @@ -1467,33 +1514,35 @@ impl GooseUser { /// object and then executes the response. If metrics are being displayed, it /// also captures request metrics. /// - /// It is possible to build and execute a `RequestBuilder` object directly with - /// Reqwest without using this helper function, but then Goose is unable to capture - /// metrics. + /// It is possible to build and execute a + /// [`reqwest::RequestBuilder`](https://docs.rs/reqwest/*/reqwest/struct.RequestBuilder.html) + /// object directly with [`reqwest`](https://docs.rs/reqwest/) without using this helper + /// function, but then Goose is unable to capture metrics. /// - /// Calls to `user.goose_send()` returns a `Result` containing a `GooseResponse` on success, - /// and a `flume::SendError` on failure. Failure only happens when `--throttle-requests` - /// is enabled and the load test completes. The `GooseResponse` object contains a copy of the - /// request made - /// ([`goose.request`](https://docs.rs/goose/*/goose/goose/struct.GooseRawRequest)), and the - /// Reqwest response ([`goose.response`](https://docs.rs/reqwest/*/reqwest/struct.Response.html)). + /// Calls to `goose_send()` returns a `Result` containing a + /// [`GooseResponse`](./struct.GooseResponse.html) on success, and a + /// [`flume::SendError`](https://docs.rs/flume/*/flume/struct.SendError.html)``, + /// on failure. Failure only happens when `--throttle-requests` is enabled and the load test + /// completes. The [`GooseResponse`](./struct.GooseResponse.html) object contains a copy of + /// the request you made ([`GooseRawRequest`](./struct.GooseRawRequest.html)), and the + /// response ([`reqwest::Response`](https://docs.rs/reqwest/*/reqwest/struct.Response.html)). /// /// # Example /// ```rust - /// use goose::prelude::*; + /// use goose::prelude::*; /// - /// let mut task = task!(get_function); + /// let mut task = task!(get_function); /// - /// /// A simple task that makes a GET request, exposing the Reqwest - /// /// request builder. - /// async fn get_function(user: &GooseUser) -> GooseTaskResult { - /// let request_builder = user.goose_get("/path/to/foo").await?; - /// let goose = user.goose_send(request_builder, None).await?; + /// /// A simple task that makes a GET request, exposing the Reqwest + /// /// request builder. + /// async fn get_function(user: &GooseUser) -> GooseTaskResult { + /// let request_builder = user.goose_get("/path/to/foo").await?; + /// let goose = user.goose_send(request_builder, None).await?; /// - /// // Do stuff with goose.request and/or goose.response here. + /// // Do stuff with goose.request and/or goose.response here. /// - /// Ok(()) - /// } + /// Ok(()) + /// } /// ``` pub async fn goose_send( &self, @@ -1584,8 +1633,9 @@ impl GooseUser { } fn send_to_parent(&self, metric: GooseMetric) -> GooseTaskResult { - // Parent is not defined when running test_start_task, test_stop_task, - // and during testing. + // Parent is not defined when running + // [`test_start`](../struct.GooseAttack.html#method.test_start), + // [`test_stop`](../struct.GooseAttack.html#method.test_stop), and during testing. if let Some(parent) = self.channel_to_parent.clone() { parent.send(metric)?; } @@ -1662,11 +1712,11 @@ impl GooseUser { /// /// Calls to `set_failure` must include four parameters. The first, `tag`, is an /// arbitrary string identifying the reason for the failure, used when logging. The - /// second, `request`, is a mutable reference to the `GooseRawRequest` object of the - /// request being identified as a failure (the contained `success` field will be set - /// to `false`, and the `update` field will be set to `true`). The last two - /// parameters, `header` and `body`, are optional and used to provide more detail in - /// logs. + /// second, `request`, is a mutable reference to the + /// ([`GooseRawRequest`](./struct.GooseRawRequest.html)) object of the request being + /// identified as a failure (the contained `success` field will be set to `false`, + /// and the `update` field will be set to `true`). The last two parameters, `header` + /// and `body`, are optional and used to provide more detail in logs. /// /// The value of `tag` will normally be collected into the errors summary table if /// metrics are being displayed. However, if `set_failure` is called multiple times, @@ -1678,35 +1728,35 @@ impl GooseUser { /// /// # Example /// ```rust - /// use goose::prelude::*; - /// - /// let mut task = task!(loadtest_index_page); - /// - /// async fn loadtest_index_page(user: &GooseUser) -> GooseTaskResult { - /// let mut goose = user.get_named("/", "index").await?; + /// use goose::prelude::*; /// - /// if let Ok(response) = goose.response { - /// // We only need to check pages that returned a success status code. - /// if response.status().is_success() { - /// match response.text().await { - /// Ok(text) => { - /// // If the expected string doesn't exist, this page load - /// // was a failure. - /// if !text.contains("this string must exist") { - /// // As this is a named request, pass in the name not the URL - /// return user.set_failure("string missing", &mut goose.request, None, None); - /// } - /// } - /// // Empty page, this is a failure. - /// Err(_) => { - /// return user.set_failure("empty page", &mut goose.request, None, None); + /// let mut task = task!(loadtest_index_page); + /// + /// async fn loadtest_index_page(user: &GooseUser) -> GooseTaskResult { + /// let mut goose = user.get_named("/", "index").await?; + /// + /// if let Ok(response) = goose.response { + /// // We only need to check pages that returned a success status code. + /// if response.status().is_success() { + /// match response.text().await { + /// Ok(text) => { + /// // If the expected string doesn't exist, this page load + /// // was a failure. + /// if !text.contains("this string must exist") { + /// // As this is a named request, pass in the name not the URL + /// return user.set_failure("string missing", &mut goose.request, None, None); /// } /// } + /// // Empty page, this is a failure. + /// Err(_) => { + /// return user.set_failure("empty page", &mut goose.request, None, None); + /// } /// } - /// }; + /// } + /// }; /// - /// Ok(()) - /// } + /// Ok(()) + /// } /// ```` pub fn set_failure( &self, @@ -1733,7 +1783,8 @@ impl GooseUser { }) } - /// Write to debug_file if enabled. + /// Write to [`debug_file`](../struct.GooseConfiguration.html#structfield.debug_file) + /// if enabled. /// /// This function provides a mechanism for optional debug logging when a load test /// is running. This can be especially helpful when writing a load test. Each entry @@ -1742,13 +1793,13 @@ impl GooseUser { /// returned by the server, and the response body returned by the server, /// /// As the response body can be large, the `--no-debug-body` option (or - /// `GooseDefault::NoDebugBody` default) can be set to prevent the debug log from - /// including the response body. When this option is enabled, the body will always - /// show up as `null` in the debug log. + /// [`GooseDefault::NoDebugBody`](../enum.GooseDefault.html#variant.NoDebugBody) default) + /// can be set to prevent the debug log from including the response body. When this option + /// is enabled, the body will always show up as `null` in the debug log. /// /// Calls to /// [`set_failure`](https://docs.rs/goose/*/goose/goose/struct.GooseUser.html#method.set_failure) - // automatically invoke `log_debug`. + /// automatically invoke `log_debug`. /// /// To enable the debug log, a load test must be run with the `--debug-log-file=foo` /// option set, where `foo` is either a relative or an absolute path of the log file @@ -1758,54 +1809,54 @@ impl GooseUser { /// /// # Example /// ```rust - /// use goose::prelude::*; - /// - /// let mut task = task!(loadtest_index_page); - /// - /// async fn loadtest_index_page(user: &GooseUser) -> GooseTaskResult { - /// let mut goose = user.get("/").await?; - /// - /// match goose.response { - /// Ok(response) => { - /// // Grab a copy of the headers so we can include them when logging errors. - /// let headers = &response.headers().clone(); - /// // We only need to check pages that returned a success status code. - /// if !response.status().is_success() { - /// match response.text().await { - /// Ok(html) => { - /// // Server returned an error code, log everything. - /// user.log_debug( - /// "error loading /", - /// Some(&goose.request), - /// Some(headers), - /// Some(&html), - /// ); - /// }, - /// Err(e) => { - /// // No body was returned, log everything else. - /// user.log_debug( - /// &format!("error loading /: {}", e), - /// Some(&goose.request), - /// Some(headers), - /// None, - /// ); - /// } + /// use goose::prelude::*; + /// + /// let mut task = task!(loadtest_index_page); + /// + /// async fn loadtest_index_page(user: &GooseUser) -> GooseTaskResult { + /// let mut goose = user.get("/").await?; + /// + /// match goose.response { + /// Ok(response) => { + /// // Grab a copy of the headers so we can include them when logging errors. + /// let headers = &response.headers().clone(); + /// // We only need to check pages that returned a success status code. + /// if !response.status().is_success() { + /// match response.text().await { + /// Ok(html) => { + /// // Server returned an error code, log everything. + /// user.log_debug( + /// "error loading /", + /// Some(&goose.request), + /// Some(headers), + /// Some(&html), + /// ); + /// }, + /// Err(e) => { + /// // No body was returned, log everything else. + /// user.log_debug( + /// &format!("error loading /: {}", e), + /// Some(&goose.request), + /// Some(headers), + /// None, + /// ); /// } /// } - /// }, - /// // No response from server. - /// Err(e) => { - /// user.log_debug( - /// "no response from server when loading /", - /// Some(&goose.request), - /// None, - /// None, - /// ); /// } + /// }, + /// // No response from server. + /// Err(e) => { + /// user.log_debug( + /// "no response from server when loading /", + /// Some(&goose.request), + /// None, + /// None, + /// ); /// } - /// - /// Ok(()) /// } + /// + /// Ok(()) + /// } /// ```` pub fn log_debug( &self, @@ -1815,8 +1866,9 @@ impl GooseUser { body: Option<&str>, ) -> GooseTaskResult { if !self.config.debug_file.is_empty() { - // Logger is not defined when running test_start_task, test_stop_task, - // and during testing. + // Logger is not defined when running + // [`test_start`](../struct.GooseAttack.html#method.test_start), + // [`test_stop`](../struct.GooseAttack.html#method.test_stop), and during testing. if let Some(debug_logger) = self.debug_logger.clone() { if self.config.no_debug_body { debug_logger.send(Some(GooseDebug::new(tag, request, headers, None)))?; @@ -1829,12 +1881,17 @@ impl GooseUser { Ok(()) } - /// Manually build a Reqwest client. + /// Manually build a + /// [`reqwest::Client`](https://docs.rs/reqwest/*/reqwest/struct.Client.html). /// - /// By default, Goose configures two options when building a Reqwest client. The first - /// configures Goose to report itself as the user agent requesting web pages (ie - /// `goose/0.11.0`). The second option configures Reqwest to store cookies, which is - /// generally necessary if you aim to simulate logged in users. + /// By default, Goose configures two options when building a + /// [`reqwest::Client`](https://docs.rs/reqwest/*/reqwest/struct.Client.html). The first + /// configures Goose to report itself as the + /// [`user_agent`](https://docs.rs/reqwest/*/reqwest/struct.ClientBuilder.html#method.user_agent) + /// requesting web pages (ie `goose/0.11.0`). The second option configures + /// [`reqwest`](https://docs.rs/reqwest/) to + /// [store cookies](https://docs.rs/reqwest/*/reqwest/struct.ClientBuilder.html#method.cookie_store), + /// which is generally necessary if you aim to simulate logged in users. /// /// # Default configuration: /// @@ -1848,16 +1905,19 @@ impl GooseUser { /// .cookie_store(true); /// ``` /// - /// Alternatively, you can use this function to manually build a Reqwest client with custom - /// configuration. Available options are found in the - /// [Reqwest `ClientBuilder`](https://docs.rs/reqwest/*/reqwest/struct.ClientBuilder.html) + /// Alternatively, you can use this function to manually build a + /// [`reqwest::Client`](https://docs.rs/reqwest/*/reqwest/struct.Client.html). + /// with custom configuration. Available options are found in the + /// [`reqwest::ClientBuilder`](https://docs.rs/reqwest/*/reqwest/struct.ClientBuilder.html) /// documentation. /// - /// When manually building a Reqwest client, there are a few things to be aware of: - /// - Manually building a client in `test_start` will only affect requests made during - /// test setup; - /// - Manually building a client in `test_stop` will only affect requests made during - /// test teardown; + /// When manually building a + /// [`reqwest::Client`](https://docs.rs/reqwest/*/reqwest/struct.Client.html), + /// there are a few things to be aware of: + /// - Manually building a client in [`test_start`](../struct.GooseAttack.html#method.test_start) + /// will only affect requests made during test setup; + /// - Manually building a client in [`test_stop`](../struct.GooseAttack.html#method.test_stop) + /// will only affect requests made during test teardown; /// - A manually built client is specific to a single Goose thread -- if you are /// generating a large load test with many users, each will need to manually build their /// own client (typically you'd do this in a Task that is registered with `set_on_start()` @@ -1867,7 +1927,8 @@ impl GooseUser { /// built client will be gone; /// - You must include all desired configuration, as you are completely replacing Goose /// defaults. For example, if you want Goose clients to store cookies, you will have to - /// include `.cookie_store(true)`. + /// include + /// [`.cookie_store(true)`](https://docs.rs/reqwest/*/reqwest/struct.ClientBuilder.html#method.cookie_store). /// /// In the following example, the Goose client is configured with a different user agent, /// sets a default header on every request, and stores cookies. @@ -1903,41 +1964,46 @@ impl GooseUser { /// Some websites use multiple domains to serve traffic, redirecting depending on /// the user's roll. For this reason, Goose needs to respect a redirect of the - /// base_url and subsequent paths should be built from the redirect domain. + /// `base_url` and subsequent paths should be built from the redirect domain. /// - /// For example, if the base_url (ie --host) is set to foo.example.com and the - /// load test requests /login, thereby loading http://foo.example.com/login and - /// this request gets redirected by the server to http://foo-secure.example.com/, + /// For example, if the `base_url` (ie `--host`) is set to `foo.example.com` and the + /// load test requests `/login`, thereby loading `http://foo.example.com/login` and + /// this request gets redirected by the server to `http://foo-secure.example.com/`, /// subsequent requests made by this user need to be against the new - /// foo-secure.example.com domain. (Further, if the base_url is again redirected, - /// such as when loading http://foo-secure.example.com/logout, the user should + /// `foo-secure.example.com domain`. (Further, if the `base_url` is again redirected, + /// such as when loading `http://foo-secure.example.com/logout`, the user should /// again follow for subsequent requests, perhaps in this case back to - /// foo.example.com.) + /// `foo.example.com`.) /// /// Load tests can also request absolute URLs, and if these URLs are redirected - /// it does not affect the base_url of the load test. For example, if - /// foo.example.com is the base url, and the load test requests - /// http://bar.example.com (a different domain) and this request gets redirected - /// to http://other.example.com, subsequent relative requests would still be made - /// against foo.example.com. + /// it does not affect the `base_url` of the load test. For example, if + /// `foo.example.com` is the base url, and the load test requests + /// `http://bar.example.com` (a different domain) and this request gets redirected + /// to `http://other.example.com`, subsequent relative requests would still be made + /// against `foo.example.com`. /// /// This functionality is used internally by Goose to follow redirects of the - /// base_url when `--sticky-follow` is specified at run time, or - /// `set_default(GooseDefault::StickyFollow, true)` is enabled. It is also + /// `base_url` when `--sticky-follow` is specified at run time, or + /// [`set_default`](../struct.GooseAttack.html#method.set_default) + /// `(`[`GooseDefault::StickyFollow`](../enum.GooseDefault.html#variant.StickyFollow) + /// `, true)` is enabled. It is also /// available to be manually invoked from a load test such as in the following /// example. /// /// # Example - /// ```rust,no_run + /// ```rust /// use goose::prelude::*; /// /// fn main() -> Result<(), GooseError> { /// let _goose_metrics = GooseAttack::initialize()? - /// .register_taskset(taskset!("LoadtestTasks").set_host("http//foo.example.com/") + /// .register_taskset(taskset!("LoadtestTasks") + /// .set_host("http://foo.example.com/") /// .set_wait_time(0, 3)? /// .register_task(task!(task_foo).set_weight(10)?) /// .register_task(task!(task_bar)) /// ) + /// // Set a default run time so this test runs to completion. + /// .set_default(GooseDefault::RunTime, 1)? /// .execute()?; /// /// Ok(()) @@ -1972,8 +2038,10 @@ impl GooseUser { /// /// The first of these defined will be returned as the prepended host: /// 1. `--host` (host specified on the command line when running load test) -/// 2. `GooseTaskSet.host` (default host defined for the current task set) -/// 3. `GooseAttack.host` (default host defined for the current load test) +/// 2. [`GooseTaskSet`](./struct.GooseTaskSet.html)`.host` (default host defined +/// for the current task set) +/// 3. [`GooseDefault::Host`](../enum.GooseDefault.html#variant.Host) (default +/// host defined for the current load test) pub fn get_base_url( config_host: Option, task_set_host: Option, @@ -2024,16 +2092,18 @@ pub type GooseTaskFunction = Arc< + Sync, >; -/// An individual task within a `GooseTaskSet`. +/// An individual task within a [`GooseTaskSet`](./struct.GooseTaskSet.html). #[derive(Clone)] pub struct GooseTask { - /// An index into GooseTaskSet.task, indicating which task this is. + /// An index into [`GooseTaskSet`](./struct.GooseTaskSet.html)`.task`, indicating which + /// task this is. pub tasks_index: usize, /// An optional name for the task, used when displaying metrics about requests made. pub name: String, /// An integer value that controls the frequency that this task will be run. pub weight: usize, - /// An integer value that controls when this task runs compared to other tasks in the same GooseTaskSet. + /// An integer value that controls when this task runs compared to other tasks in the same + /// [`GooseTaskSet`](./struct.GooseTaskSet.html). pub sequence: usize, /// A flag indicating that this task runs when the user starts. pub on_start: bool, @@ -2059,21 +2129,22 @@ impl GooseTask { /// Set an optional name for the task, used when displaying metrics about /// requests made by the task. /// - /// Individual requests can also be named withing your load test. See the - /// documentation for `GooseUser`. - /// [`set_request_name()`](./struct.GooseUser.html#method.set_request_name) + /// Individual requests can also be named withing your load test if you use the + /// `_named` version of each method, for example + /// [`get_named`](./struct.GooseUser.html#method.get_named) or + /// [`post_named`](./struct.GooseUser.html#method.post_named). /// /// # Example /// ```rust - /// use goose::prelude::*; + /// use goose::prelude::*; /// - /// task!(my_task_function).set_name("foo"); + /// task!(my_task_function).set_name("foo"); /// - /// async fn my_task_function(user: &GooseUser) -> GooseTaskResult { - /// let _goose = user.get("/").await?; + /// async fn my_task_function(user: &GooseUser) -> GooseTaskResult { + /// let _goose = user.get("/").await?; /// - /// Ok(()) - /// } + /// Ok(()) + /// } /// ``` pub fn set_name(mut self, name: &str) -> Self { trace!("[{}] set_name: {}", self.tasks_index, self.name); @@ -2093,15 +2164,15 @@ impl GooseTask { /// /// # Example /// ```rust - /// use goose::prelude::*; + /// use goose::prelude::*; /// - /// task!(my_on_start_function).set_on_start(); + /// task!(my_on_start_function).set_on_start(); /// - /// async fn my_on_start_function(user: &GooseUser) -> GooseTaskResult { - /// let _goose = user.get("/").await?; + /// async fn my_on_start_function(user: &GooseUser) -> GooseTaskResult { + /// let _goose = user.get("/").await?; /// - /// Ok(()) - /// } + /// Ok(()) + /// } /// ``` pub fn set_on_start(mut self) -> Self { trace!("{} [{}] set_on_start task", self.name, self.tasks_index); @@ -2121,15 +2192,15 @@ impl GooseTask { /// /// # Example /// ```rust - /// use goose::prelude::*; + /// use goose::prelude::*; /// - /// task!(my_on_stop_function).set_on_stop(); + /// task!(my_on_stop_function).set_on_stop(); /// - /// async fn my_on_stop_function(user: &GooseUser) -> GooseTaskResult { - /// let _goose = user.get("/").await?; + /// async fn my_on_stop_function(user: &GooseUser) -> GooseTaskResult { + /// let _goose = user.get("/").await?; /// - /// Ok(()) - /// } + /// Ok(()) + /// } /// ``` pub fn set_on_stop(mut self) -> Self { trace!("{} [{}] set_on_stop task", self.name, self.tasks_index); @@ -2186,29 +2257,29 @@ impl GooseTask { /// # Examples /// In this first example, the variable names indicate the order the tasks will be run in: /// ```rust - /// use goose::prelude::*; + /// use goose::prelude::*; /// - /// let runs_first = task!(first_task_function).set_sequence(3); - /// let runs_second = task!(second_task_function).set_sequence(5835); - /// let runs_last = task!(third_task_function); + /// let runs_first = task!(first_task_function).set_sequence(3); + /// let runs_second = task!(second_task_function).set_sequence(5835); + /// let runs_last = task!(third_task_function); /// - /// async fn first_task_function(user: &GooseUser) -> GooseTaskResult { - /// let _goose = user.get("/1").await?; + /// async fn first_task_function(user: &GooseUser) -> GooseTaskResult { + /// let _goose = user.get("/1").await?; /// - /// Ok(()) - /// } + /// Ok(()) + /// } /// - /// async fn second_task_function(user: &GooseUser) -> GooseTaskResult { - /// let _goose = user.get("/2").await?; + /// async fn second_task_function(user: &GooseUser) -> GooseTaskResult { + /// let _goose = user.get("/2").await?; /// - /// Ok(()) - /// } + /// Ok(()) + /// } /// - /// async fn third_task_function(user: &GooseUser) -> GooseTaskResult { - /// let _goose = user.get("/3").await?; + /// async fn third_task_function(user: &GooseUser) -> GooseTaskResult { + /// let _goose = user.get("/3").await?; /// - /// Ok(()) - /// } + /// Ok(()) + /// } /// ``` /// /// In the following example, the `runs_first` task runs two times, then one instance of `runs_second` diff --git a/src/lib.rs b/src/lib.rs index ba43d9a2..f39a728e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,7 @@ //! //! Have you ever been attacked by a goose? //! -//! Goose is a load testing tool inspired by [Locust](https://locust.io/). +//! Goose is a load testing framework inspired by [Locust](https://locust.io/). //! User behavior is defined with standard Rust code. //! //! Goose load tests, called Goose Attacks, are built by creating an application @@ -63,8 +63,8 @@ //! Below your `main` function (which currently is the default `Hello, world!`), add //! one or more load test functions. The names of these functions are arbitrary, but it is //! recommended you use self-documenting names. Load test functions must be async. Each load -//! test function must accept a reference to a `GooseUser` object and return a -//! `GooseTaskResult`. For example: +//! test function must accept a reference to a [`GooseUser`](./goose/struct.GooseUser.html) object +//! and return a [`GooseTaskResult`](./goose/type.GooseTaskResult.html). For example: //! //! ```rust //! use goose::prelude::*; @@ -76,11 +76,14 @@ //! } //! ``` //! -//! In the above example, we're using the GooseUser helper method `get` to load a path -//! on the website we are load testing. This helper creates a Reqwest request builder, and -//! uses it to build and execute a request for the above path. If you want access to the -//! request builder object, you can instead use the `goose_get` helper, for example to -//! set a timeout on this specific request: +//! In the above example, we're using the [`GooseUser`](./goose/struct.GooseUser.html) helper +//! [`get`](./goose/struct.GooseUser.html#method.get) to load a path on the website we are load +//! testing. This helper creates a +//! [`reqwest::RequestBuilder`](https://docs.rs/reqwest/*/reqwest/struct.RequestBuilder.html) +//! object and uses it to build and execute a request for the above path. If you want access +//! to the [`RequestBuilder`](https://docs.rs/reqwest/*/reqwest/struct.RequestBuilder.html) +//! object, you can instead use the [`goose_get`](./goose/struct.GooseUser.html#method.goose_get) +//! helper, for example to set a timeout on this specific request: //! //! ```rust //! use std::time; @@ -95,15 +98,17 @@ //! } //! ``` //! -//! We pass the `request_builder` object to `goose_send` which builds and executes it, also -//! collecting useful metrics. The `.await` at the end is necessary as `goose_send` is an -//! async function. +//! We pass the [`RequestBuilder`](https://docs.rs/reqwest/*/reqwest/struct.RequestBuilder.html) +//! object to [`goose_send`](./goose/struct.GooseUser.html#method.goose_send) which builds and +//! executes it, also collecting useful metrics. The +//! [`.await`](https://doc.rust-lang.org/std/keyword.await.html) at the end is necessary as +//! [`goose_send`](./goose/struct.GooseUser.html#method.goose_send) is an async function. //! //! Once all our tasks are created, we edit the main function to initialize goose and register //! the tasks. In this very simple example we only have two tasks to register, while in a real //! load test you can have any number of task sets with any number of individual tasks. //! -//! ```rust,no_run +//! ```rust //! use goose::prelude::*; //! //! fn main() -> Result<(), GooseError> { @@ -117,7 +122,9 @@ //! .register_task(task!(loadtest_bar).set_name("bar").set_weight(2)?) //! ) //! // You could also set a default host here, for example: -//! //.set_default(GooseDefault::Host, "http://dev.local/")? +//! .set_default(GooseDefault::Host, "http://dev.local/")? +//! // We set a default run time so this test runs to completion. +//! .set_default(GooseDefault::RunTime, 1)? //! .execute()?; //! //! Ok(()) @@ -139,9 +146,9 @@ //! ``` //! //! Goose now spins up a configurable number of users, each simulating a user on your -//! website. Thanks to Reqwest, each user maintains its own web client state, handling -//! cookies and more so your "users" can log in, fill out forms, and more, as real users -//! on your sites would do. +//! website. Thanks to [`reqwest`](https://docs.rs/reqwest/), each user maintains its own +//! web client state, handling cookies and more so your "users" can log in, fill out forms, +//! and more, as real users on your sites would do. //! //! ### Running the Goose load test //! @@ -255,11 +262,13 @@ //! All 8 users hatched, resetting metrics (disable with --no-reset-metrics). //! ``` //! -//! When printing metrics, by default Goose will display running values approximately -//! every 15 seconds. Running metrics are broken into several tables. First are the -//! per-task metrics which are further split into two sections. The first section shows -//! how many requests have been made, how many of them failed (non-2xx response), and -//! the corresponding per-second rates. +//! Goose can optionally display running metrics if started with `--running-metrics INT` +//! where INT is an integer value in seconds. For example, if Goose is started with +//! `--running-metrics 15` it will display running values approximately every 15 seconds. +//! Running metrics are broken into several tables. First are the per-task metrics which +//! are further split into two sections. The first section shows how many requests have +//! been made, how many of them failed (non-2xx response), and the corresponding per-second +//! rates. //! //! This table shows details for all Task Sets and all Tasks defined by your load test, //! regardless of if they actually run. This can be useful to ensure that you have set @@ -289,8 +298,8 @@ //! Aggregated | 21.24 | 8 | 156 | 19 //! ``` //! -//! The second table breaks down the same metrics by Request instead of by Task. For -//! our simple load test, each Task only makes a single Request, so the metrics are +//! The second table breaks down the same metrics by request instead of by Task. For +//! our simple load test, each Task only makes a single request, so the metrics are //! the same. There are two main differences. First, metrics are listed by request //! type and path or name. The first request shows up as `GET /path/to/foo` as the //! request was not named. The second request shows up as `GET bar` as the request @@ -315,8 +324,8 @@ //! Aggregated | 21.20 | 8 | 156 | 19 //! ``` //! -//! Note that Goose respected the per-task weights we set, and `foo` (with a weight of -//! 10) is being loaded five times as often as `bar` (with a weight of 2). On average +//! Note that Goose respected the per-task weights we set, and `foo` (with a weight of 10) +//! is being loaded five times as often as `bar` (with a weight of 2). On average //! each page is returning within `21.2` milliseconds. The quickest page response was //! for `foo` in `8` milliseconds. The slowest page response was for `bar` in `156` //! milliseconds. @@ -398,13 +407,13 @@ //! //! ## License //! -//! Copyright 2020 Jeremy Andrews +//! Copyright 2020-21 Jeremy Andrews //! //! Licensed under the Apache License, Version 2.0 (the "License"); //! you may not use this file except in compliance with the License. //! You may obtain a copy of the License at //! -//! http://www.apache.org/licenses/LICENSE-2.0 +//! [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) //! //! Unless required by applicable law or agreed to in writing, software //! distributed under the License is distributed on an "AS IS" BASIS, @@ -478,7 +487,9 @@ type UnsequencedGooseTasks = Vec; /// Internal representation of sequenced tasks. type SequencedGooseTasks = BTreeMap>; +/// Optional unbounded receiver for logger thread, if debug logger is enabled. type DebugLoggerHandle = Option>; +/// Optional unbounded sender from all GooseUsers to logger thread, if enabled. type DebugLoggerChannel = Option>>; /// Worker ID to aid in tracing logs when running a Gaggle. @@ -488,50 +499,64 @@ pub fn get_worker_id() -> usize { #[cfg(not(feature = "gaggle"))] #[derive(Debug, Clone)] -/// Socket used for coordinating a Gaggle, a distributed load test. +/// Socket used for coordinating a Gaggle distributed load test. pub struct Socket {} -/// Definition of all errors a GooseAttack can return. +/// An enumeration of all errors a [`GooseAttack`](./struct.GooseAttack.html) can return. #[derive(Debug)] pub enum GooseError { - /// Contains an io::Error. + /// Wraps a [`std::io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html). Io(io::Error), - /// Contains a reqwest::Error. + /// Wraps a [`reqwest::Error`](https://docs.rs/reqwest/*/reqwest/struct.Error.html). Reqwest(reqwest::Error), - /// Failed attempt to use code that requires a compile-time feature be enabled. The missing - /// feature is named in `.feature`. An optional explanation may be found in `.detail`. - FeatureNotEnabled { feature: String, detail: String }, - /// Failed to parse hostname. The invalid hostname that caused this error is found in - /// `.host`. An optional explanation may be found in `.detail`. The lower level - /// `url::ParseError` is contained in `.parse_error`. + /// Failed attempt to use code that requires a compile-time feature be enabled. + FeatureNotEnabled { + /// The missing compile-time feature. + feature: String, + /// An optional explanation of the error. + detail: String, + }, + /// Failed to parse a hostname. InvalidHost { + /// The invalid hostname that caused this error. host: String, + /// An optional explanation of the error. detail: String, + /// Wraps a [`url::ParseError`](https://docs.rs/url/*/url/enum.ParseError.html). parse_error: url::ParseError, }, - /// Invalid option or value specified, may only be invalid in context. The invalid option - /// is found in `.option`, while the invalid value is found in `.value`. An optional - /// explanation providing context may be found in `.detail`. + /// Invalid option or value specified, may only be invalid in context. InvalidOption { + /// The invalid option that caused this error, may be only invalid in context. option: String, + /// The invalid value that caused this error, may be only invalid in context. value: String, + /// An optional explanation of the error. detail: String, }, - /// Invalid wait time specified. The minimum wait time and maximum wait time are found in - /// `.min_wait` and `.max_wait` respectively. An optional explanation providing context may - /// be found in `.detail`. + /// Invalid wait time specified. InvalidWaitTime { + // The specified minimum wait time. min_wait: usize, + // The specified maximum wait time. max_wait: usize, + /// An optional explanation of the error. + detail: String, + }, + /// Invalid weight specified. + InvalidWeight { + // The specified weight. + weight: usize, + /// An optional explanation of the error. + detail: String, + }, + /// [`GooseAttack`](./struct.GooseAttack.html) has no [`GooseTaskSet`](./goose/struct.GooseTaskSet.html) defined. + NoTaskSets { + /// An optional explanation of the error. detail: String, }, - /// Invalid weight specified. The invalid weight value is found in `.weight`. An optional - // explanation providing context may be found in `.detail`. - InvalidWeight { weight: usize, detail: String }, - /// `GooseAttack` has no `GooseTaskSet` defined. An optional explanation may be found in - /// `.detail`. - NoTaskSets { detail: String }, } +/// Implement a helper to provide a text description of all possible types of errors. impl GooseError { fn describe(&self) -> &str { match *self { @@ -547,8 +572,9 @@ impl GooseError { } } -// Define how to display errors. +/// Implement format trait to allow displaying errors. impl fmt::Display for GooseError { + // Implement display of error with `{}` marker. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { GooseError::Io(ref source) => write!(f, "GooseError: {} ({})", self.describe(), source), @@ -592,9 +618,10 @@ impl From for GooseError { } #[derive(Clone, Debug, PartialEq)] -/// A GooseAttack load test can operate in only one mode. +/// A [`GooseAttack`](./struct.GooseAttack.html) load test operates in one (and only one) +/// of the following modes. pub enum AttackMode { - /// A mode has not yet been assigned. + /// During early startup before one of the following modes gets assigned. Undefined, /// A single standalone process performing a load test. StandAlone, @@ -605,20 +632,23 @@ pub enum AttackMode { } #[derive(Clone, Debug, PartialEq)] -/// A GooseAttack load test can operate in only one mode. +/// A [`GooseAttack`](./struct.GooseAttack.html) load test moves through each of the following +/// phases during a complete load test. pub enum AttackPhase { - /// Memory is being allocated for the GooseAttack. + /// Memory is being allocated for the [`GooseAttack`](./struct.GooseAttack.html). Initializing, - /// GooseUsers are starting and beginning to generate load. + /// [`GooseUser`](./goose/struct.GooseUser.html)s are starting and beginning to generate + /// load. Starting, - /// All GooseUsers are started and generating load. + /// All [`GooseUser`](./goose/struct.GooseUser.html)s have started and are generating load. Running, - /// GooseUsers are stopping. + /// [`GooseUser`](./goose/struct.GooseUser.html)s are stopping. Stopping, } #[derive(Clone, Debug, PartialEq)] -/// Used to define the order GooseTasksSets and GooseTasks are allocated. +/// Used to define the order [`GooseTaskSet`](./goose/struct.GooseTaskSet.html)s and +/// [`GooseTask`](./goose/struct.GooseTask.html)s are allocated. pub enum GooseScheduler { /// Allocate one of each available type at a time (default). RoundRobin, @@ -755,26 +785,33 @@ pub enum GooseDefault { } /// Internal global run state for load test. -pub struct GooseAttackRunState { - /// A timestamp tracking when the previous GooseUser was launched. +struct GooseAttackRunState { + /// A timestamp tracking when the previous [`GooseUser`](./goose/struct.GooseUser.html) + /// was launched. spawn_user_timer: std::time::Instant, - /// How many milliseconds until the next user should be spawned. + /// How many milliseconds until the next [`GooseUser`](./goose/struct.GooseUser.html) + /// should be spawned. spawn_user_in_ms: usize, - /// A counter tracking which GooseUser is being spawned. + /// A counter tracking which [`GooseUser`](./goose/struct.GooseUser.html) is being + /// spawned. spawn_user_counter: usize, /// This variable accounts for time spent doing things which is then subtracted from /// the time sleeping to avoid an unintentional drift in events that are supposed to /// happen regularly. drift_timer: tokio::time::Instant, - /// Unbounded sender used by all GooseUser threads to send metrics to parent. + /// Unbounded sender used by all [`GooseUser`](./goose/struct.GooseUser.html) + /// threads to send metrics to parent. all_threads_metrics_tx: flume::Sender, - /// Unbounded receiver used by Goose parent to receive metrics from GooseUsers. + /// Unbounded receiver used by Goose parent to receive metrics from + /// [`GooseUser`](./goose/struct.GooseUser.html)s. metrics_rx: flume::Receiver, /// Optional unbounded receiver for logger thread, if enabled. debug_logger: DebugLoggerHandle, - /// Optional unbounded sender from all GooseUsers to logger thread, if enabled. + /// Optional unbounded sender from all [`GooseUser`](./goose/struct.GooseUser.html)s + /// to logger thread, if enabled. all_threads_debug_logger_tx: DebugLoggerChannel, - /// Optional receiver for all GooseUsers from throttle thread, if enabled. + /// Optional receiver for all [`GooseUser`](./goose/struct.GooseUser.html)s from + /// throttle thread, if enabled. throttle_threads_tx: Option>, /// Optional sender for throttle thread, if enabled. parent_to_throttle_tx: Option>, @@ -785,46 +822,51 @@ pub struct GooseAttackRunState { /// A flag tracking whether or not the header has been written when the metrics /// log is enabled. metrics_header_displayed: bool, - /// Collection of all GooseUser threads so they can be stopped later. + /// Collection of all [`GooseUser`](./goose/struct.GooseUser.html) threads so they + /// can be stopped later. users: Vec>, - /// All unbounded senders to allow communication with GooseUser threads. + /// All unbounded senders to allow communication with + /// [`GooseUser`](./goose/struct.GooseUser.html) threads. user_channels: Vec>, /// Timer tracking when to display running metrics, if enabled. running_metrics_timer: std::time::Instant, /// Boolean flag indicating if running metrics should be displayed. display_running_metrics: bool, - /// Boolean flag indicating if all GooseUsers have been spawned. + /// Boolean flag indicating if all [`GooseUser`](./goose/struct.GooseUser.html)s + /// have been spawned. all_users_spawned: bool, - /// Thread-safe boolean flag indicating if the GooseAttack has been canceled. + /// Thread-safe boolean flag indicating if the [`GooseAttack`](./struct.GooseAttack.html) + /// has been canceled. canceled: Arc, /// Optional socket used to coordinate a distributed Gaggle. socket: Option, } -/// Internal global state for load test. +/// Global internal state for the load test. #[derive(Clone)] pub struct GooseAttack { - /// An optional task to run one time before starting users and running task sets. + /// An optional task that is run one time before starting GooseUsers and running GooseTaskSets. test_start_task: Option, - /// An optional task to run one time after users have finished running task sets. + /// An optional task that is run one time after all GooseUsers have finished. test_stop_task: Option, - /// A vector containing one copy of each GooseTaskSet that will run during this load test. + /// A vector containing one copy of each GooseTaskSet defined by this load test. task_sets: Vec, - /// A weighted vector containing a GooseUser object for each user that will run during this load test. + /// A weighted vector containing a GooseUser object for each GooseUser that will run during this load test. weighted_users: Vec, - /// A weighted vector containing a lightweight GaggleUser object that will get sent to Workers. + /// A weighted vector containing a lightweight GaggleUser object that is sent to all Workers if running in Gaggle mode. weighted_gaggle_users: Vec, - /// An optional default host to run this load test against. + /// Optional default values for Goose run-time options. defaults: GooseDefaults, - /// Configuration object managed by StructOpt. + /// Configuration object holding options set when launching the load test. configuration: GooseConfiguration, - /// Track how long the load test should run. + /// How long (in seconds) the load test should run. run_time: usize, - /// Which mode this GooseAttack is operating in. + /// The load test operates in only one of the following modes: StandAlone, Manager, or Worker. attack_mode: AttackMode, - /// Which mode this GooseAttack is operating in. + /// Which phase the load test is currently operating in. attack_phase: AttackPhase, - /// Defines the order GooseTaskSets and GooseTasks are allocated. + /// Defines the order [`GooseTaskSet`](./goose/struct.GooseTaskSet.html)s and + /// [`GooseTask`](./goose/struct.GooseTask.html)s are allocated. scheduler: GooseScheduler, /// When the load test started. started: Option, @@ -833,13 +875,13 @@ pub struct GooseAttack { } /// Goose's internal global state. impl GooseAttack { - /// Load configuration from command line and initialize a GooseAttack. + /// Load configuration and initialize a [`GooseAttack`](./struct.GooseAttack.html). /// /// # Example - /// ```rust,no_run - /// use goose::prelude::*; + /// ```rust + /// use goose::prelude::*; /// - /// let mut goose_attack = GooseAttack::initialize(); + /// let mut goose_attack = GooseAttack::initialize(); /// ``` pub fn initialize() -> Result { Ok(GooseAttack { @@ -859,16 +901,16 @@ impl GooseAttack { }) } - /// Initialize a GooseAttack with an already loaded configuration. - /// This should only be called by worker instances. + /// Initialize a [`GooseAttack`](./struct.GooseAttack.html) with an already loaded + /// configuration. This should only be called by Worker instances. /// /// # Example - /// ```rust,no_run - /// use goose::{GooseAttack, GooseConfiguration}; - /// use gumdrop::Options; + /// ```rust + /// use goose::{GooseAttack, GooseConfiguration}; + /// use gumdrop::Options; /// - /// let configuration = GooseConfiguration::parse_args_default_or_exit(); - /// let mut goose_attack = GooseAttack::initialize_with_config(configuration); + /// let configuration = GooseConfiguration::parse_args_default_or_exit(); + /// let mut goose_attack = GooseAttack::initialize_with_config(configuration); /// ``` pub fn initialize_with_config( configuration: GooseConfiguration, @@ -890,6 +932,11 @@ impl GooseAttack { }) } + /// Optionally initialize the logger which writes to standard out and/or to + /// a configurable log file. + /// + /// This method is invoked by + /// [`GooseAttack.execute()`](./struct.GooseAttack.html#method.execute). pub fn initialize_logger(&self) { // Allow optionally controlling debug output level let debug_level; @@ -957,12 +1004,14 @@ impl GooseAttack { info!("Logfile verbosity level: {}", log_level); } - /// Define the order `GooseTaskSet`s are allocated to new `GooseUser`s as they - /// are launched. + /// Define the order [`GooseTaskSet`](./goose/struct.GooseTaskSet.html)s are + /// allocated to new [`GooseUser`](./goose/struct.GooseUser.html)s as they are + /// launched. /// - /// By default, GooseTaskSets are allocated to new GooseUser's in a round robin - /// style. For example, if TaskSet A has a weight of 5, Task Set B has a weight - /// of 3, and you launch 20 users, they will be launched in the following order: + /// By default, [`GooseTaskSet`](./goose/struct.GooseTaskSet.html)s are allocated + /// to new [`GooseUser`](./goose/struct.GooseUser.html)s in a round robin style. + /// For example, if TaskSet A has a weight of 5, TaskSet B has a weight of 3, and + /// you launch 20 users, they will be launched in the following order: /// A, B, A, B, A, B, A, A, A, B, A, B, A, B, A, A, A, B, A, B /// /// Note that the following pattern is repeated: @@ -970,19 +1019,21 @@ impl GooseAttack { /// /// If reconfigured to schedule serially, then they will instead be allocated in /// the following order: - /// A, A, A, A, A, B, B, B, A, A, A, A, A, B, B, B, A, A, A, A + /// A, A, A, A, A, B, B, B, A, A, A, A, A, B, B, B, A, A, A, A /// /// In the serial case, the following pattern is repeated: - /// A, A, A, A, A, B, B, B + /// A, A, A, A, A, B, B, B /// - /// In the following example, GooseTaskSets are allocated to launching GooseUsers - /// in a random order. This means running the test multiple times can generate + /// In the following example, [`GooseTaskSet`](./goose/struct.GooseTaskSet.html)s + /// are allocated to launching [`GooseUser`](./goose/struct.GooseUser.html)s in a + /// random order. This means running the test multiple times can generate /// different amounts of load, as depending on your weighting rules you may - /// have a different number of GooseUsers running each GooseTaskSet each time. + /// have a different number of [`GooseUser`](./goose/struct.GooseUser.html)s + /// running each [`GooseTaskSet`](./goose/struct.GooseTaskSet.html) each time. /// /// # Example - /// ```rust,no_run - /// use goose::prelude::*; + /// ```rust + /// use goose::prelude::*; /// /// fn main() -> Result<(), GooseError> { /// GooseAttack::initialize()? @@ -1016,12 +1067,12 @@ impl GooseAttack { self } - /// A load test must contain one or more `GooseTaskSet`s. Each task set must + /// A load test must contain one or more [`GooseTaskSet`](./goose/struct.GooseTaskSet.html)s /// be registered into Goose's global state with this method for it to run. /// /// # Example - /// ```rust,no_run - /// use goose::prelude::*; + /// ```rust + /// use goose::prelude::*; /// /// fn main() -> Result<(), GooseError> { /// GooseAttack::initialize()? @@ -1057,17 +1108,19 @@ impl GooseAttack { /// start running. This is would generally be used to set up anything required /// for the load test. /// - /// The GooseUser used to run the `test_start` tasks is not preserved and does not - /// otherwise affect the subsequent GooseUsers that run the rest of the load test. - /// For example, if the GooseUser logs in during `test_start`, subsequent GooseUsers + /// The [`GooseUser`](./goose/struct.GooseUser.html) used to run the `test_start` + /// tasks is not preserved and does not otherwise affect the subsequent + /// [`GooseUser`](./goose/struct.GooseUser.html)s that run the rest of the load + /// test. For example, if the [`GooseUser`](./goose/struct.GooseUser.html) + /// logs in during `test_start`, subsequent [`GooseUser`](./goose/struct.GooseUser.html) /// do not retain this session and are therefor not already logged in. /// /// When running in a distributed Gaggle, this task is only run one time by the /// Manager. /// /// # Example - /// ```rust,no_run - /// use goose::prelude::*; + /// ```rust + /// use goose::prelude::*; /// /// fn main() -> Result<(), GooseError> { /// GooseAttack::initialize()? @@ -1095,8 +1148,8 @@ impl GooseAttack { /// Manager. /// /// # Example - /// ```rust,no_run - /// use goose::prelude::*; + /// ```rust + /// use goose::prelude::*; /// /// fn main() -> Result<(), GooseError> { /// GooseAttack::initialize()? @@ -1116,8 +1169,9 @@ impl GooseAttack { self } - /// Use configured GooseScheduler to build out a properly - /// weighted list of TaskSets to be assigned to GooseUsers. + /// Use configured GooseScheduler to build out a properly weighted list of + /// [`GooseTaskSet`](./goose/struct.GooseTaskSet.html)s to be assigned to + /// [`GooseUser`](./goose/struct.GooseUser.html)s fn allocate_task_sets(&mut self) -> Vec { trace!("allocate_task_sets"); @@ -1213,7 +1267,7 @@ impl GooseAttack { weighted_task_sets } - /// Allocate a vector of weighted GooseUser. + /// Allocate a vector of weighted [`GooseUser`](./goose/struct.GooseUser.html)s. fn weight_task_set_users(&mut self) -> Result, GooseError> { trace!("weight_task_set_users"); @@ -1253,7 +1307,7 @@ impl GooseAttack { } } - /// Allocate a vector of weighted GaggleUser. + /// Allocate a vector of weighted [`GaggleUser`](./goose/struct.GaggleUser.html). fn prepare_worker_task_set_users(&mut self) -> Result, GooseError> { trace!("prepare_worker_task_set_users"); @@ -1288,7 +1342,8 @@ impl GooseAttack { } } - // Configure which mode this GooseAttack will run in. + // Configure which mode this [`GooseAttack`](./struct.GooseAttack.html) + // will run in. fn set_attack_mode(&mut self) -> Result<(), GooseError> { // Determine if Manager is enabled by default. let manager_is_default = if let Some(value) = self.defaults.manager { @@ -1396,7 +1451,7 @@ impl GooseAttack { self.attack_phase = phase; } - // Determine how many workers to expect. + // Determine how many Workers to expect. fn set_expect_workers(&mut self) -> Result<(), GooseError> { // Track how value gets set so we can return a meaningful error if necessary. let mut key = "configuration.expect_workers"; @@ -1452,6 +1507,7 @@ impl GooseAttack { Ok(()) } + // Configure the host and port the Manager listens on. fn set_gaggle_host_and_port(&mut self) -> Result<(), GooseError> { // Configure manager_bind_host and manager_bind_port. if self.attack_mode == AttackMode::Manager { @@ -1537,7 +1593,7 @@ impl GooseAttack { Ok(()) } - // Configure how many Goose Users to hatch. + // Configure how many [`GooseUser`](./goose/struct.GooseUser.html)s to hatch. fn set_users(&mut self) -> Result<(), GooseError> { // Track how value gets set so we can return a meaningful error if necessary. let mut key = "configuration.users"; @@ -1640,7 +1696,7 @@ impl GooseAttack { Ok(()) } - // Configure how quickly to hatch Goose Users. + // Configure how quickly to hatch [`GooseUser`](./goose/struct.GooseUser.html)s. fn set_hatch_rate(&mut self) -> Result<(), GooseError> { // Track how value gets set so we can return a meaningful error if necessary. let mut key = "configuration.hatch_rate"; @@ -1758,7 +1814,7 @@ impl GooseAttack { Ok(()) } - // Determine if no_reset_statics is enabled. + // Determine if `no_reset_statics` is enabled. fn set_no_reset_metrics(&mut self) -> Result<(), GooseError> { // Track how value gets set so we can return a meaningful error if necessary. let mut key = "configuration.no_reset_metrics"; @@ -1790,7 +1846,7 @@ impl GooseAttack { Ok(()) } - // Determine if the status_codes flag is enabled. + // Determine if the `--status-codes` flag is enabled. fn set_status_codes(&mut self) -> Result<(), GooseError> { // Track how value gets set so we can return a meaningful error if necessary. let mut key = "configuration.status_codes"; @@ -1822,7 +1878,7 @@ impl GooseAttack { Ok(()) } - // Determine if the running_metrics flag is enabled. + // Determine if the `--running-metrics` flag is enabled. fn set_running_metrics(&mut self) -> Result<(), GooseError> { // Track how value gets set so we can return a meaningful error if necessary. let mut key = "configuration.running_metrics"; @@ -1860,7 +1916,7 @@ impl GooseAttack { Ok(()) } - // Determine if the no_task_metrics flag is enabled. + // Determine if the `--no-task-metrics` flag is enabled. fn set_no_task_metrics(&mut self) -> Result<(), GooseError> { // Track how value gets set so we can return a meaningful error if necessary. let mut key = "configuration.no_task_metrics"; @@ -1892,7 +1948,7 @@ impl GooseAttack { Ok(()) } - // Determine if the no_error_summary flag is enabled. + // Determine if the `--no-error-summary` flag is enabled. fn set_no_error_summary(&mut self) -> Result<(), GooseError> { // Track how value gets set so we can return a meaningful error if necessary. let mut key = "configuration.no_error_summary"; @@ -1924,7 +1980,7 @@ impl GooseAttack { Ok(()) } - // Determine if the no_metrics flag is enabled. + // Determine if the `--no-metrics` flag is enabled. fn set_no_metrics(&mut self) -> Result<(), GooseError> { // Track how value gets set so we can return a meaningful error if necessary. let mut key = "configuration.no_metrics"; @@ -1994,7 +2050,7 @@ impl GooseAttack { Ok(()) } - // Determine if the sticky_follow flag is enabled. + // Determine if the `--sticky-follow` flag is enabled. fn set_sticky_follow(&mut self) -> Result<(), GooseError> { // Track how value gets set so we can return a meaningful error if necessary. let mut key = "configuration.sticky_follow"; @@ -2026,7 +2082,7 @@ impl GooseAttack { } #[cfg(feature = "gaggle")] - // Determine if no_hash_check flag is enabled. + // Determine if `--no-hash-check` flag is enabled. fn set_no_hash_check(&mut self) -> Result<(), GooseError> { // Track how value gets set so we can return a meaningful error if necessary. let mut key = "configuration.no_hash_check"; @@ -2229,10 +2285,10 @@ impl GooseAttack { Ok(()) } - /// Execute the load test. + /// Execute the [`GooseAttack`](./struct.GooseAttack.html) load test. /// /// # Example - /// ```rust,no_run + /// ```rust /// use goose::prelude::*; /// /// fn main() -> Result<(), GooseError> { @@ -2240,7 +2296,11 @@ impl GooseAttack { /// .register_taskset(taskset!("ExampleTasks") /// .register_task(task!(example_task).set_weight(2)?) /// .register_task(task!(another_example_task).set_weight(3)?) + /// // Goose must run against a host, point to localhost so test starts. + /// .set_host("http://localhost") /// ) + /// // Exit after one second so test doesn't run forever. + /// .set_default(GooseDefault::RunTime, 1)? /// .execute()?; /// /// Ok(()) @@ -2450,7 +2510,7 @@ impl GooseAttack { Ok(self.metrics) } - /// Helper to wrap configured host in Option<> if set. + /// Helper to wrap configured host in `Option<>` if set. fn get_configuration_host(&self) -> Option { if self.configuration.host.is_empty() { None @@ -2529,11 +2589,13 @@ impl GooseAttack { (Some(logger_thread), Some(all_threads_debug_logger)) } - // Helper to spawn a throttle thread if configured. + // Helper to spawn a throttle thread if configured. The throttle thread opens + // a bounded channel to control how quickly [`GooseUser`](./goose/struct.GooseUser.html) + // threads can make requests. async fn setup_throttle( &self, ) -> ( - // A channel used by GooseClients to throttle requests. + // A channel used by [`GooseUser`](./goose/struct.GooseUser.html)s to throttle requests. Option>, // A channel used by parent to tell throttle the load test is complete. Option>, @@ -2544,7 +2606,7 @@ impl GooseAttack { } // Create a bounded channel allowing single-sender multi-receiver to throttle - // GooseUser threads. + // [`GooseUser`](./goose/struct.GooseUser.html) threads. let (all_threads_throttle, throttle_receiver): ( flume::Sender, flume::Receiver, @@ -2576,7 +2638,7 @@ impl GooseAttack { (Some(all_threads_throttle), Some(parent_to_throttle_tx)) } - // Prepare an asynchronous file writer for report_file (if enabled). + // Prepare an asynchronous file writer for `report_file` (if enabled). async fn prepare_report_file(&mut self) -> Result, GooseError> { if let Some(report_file_path) = self.get_report_file_path() { Ok(Some(File::create(&report_file_path).await?)) @@ -2585,7 +2647,7 @@ impl GooseAttack { } } - // Prepare an asynchronous buffered file writer for requests_file (if enabled). + // Prepare an asynchronous buffered file writer for `requests_file` (if enabled). async fn prepare_requests_file(&mut self) -> Result>, GooseError> { if let Some(requests_file_path) = self.get_requests_file_path() { Ok(Some(BufWriter::new( @@ -2596,7 +2658,7 @@ impl GooseAttack { } } - // Invoke test_start tasks if existing. + // Invoke `test_start` tasks if existing. async fn run_test_start(&self) -> Result<(), GooseError> { // Initialize per-user states. if self.attack_mode != AttackMode::Worker { @@ -2622,7 +2684,7 @@ impl GooseAttack { Ok(()) } - // Invoke test_stop tasks if existing. + // Invoke `test_stop` tasks if existing. async fn run_test_stop(&self) -> Result<(), GooseError> { // Initialize per-user states. if self.attack_mode != AttackMode::Worker { @@ -2649,7 +2711,7 @@ impl GooseAttack { } // Create a GooseAttackRunState object and do all initialization required - // to start a GooseAttack. + // to start a [`GooseAttack`](./struct.GooseAttack.html). async fn initialize_attack( &mut self, socket: Option, @@ -2747,7 +2809,8 @@ impl GooseAttack { Ok(goose_attack_run_state) } - // Spawn GooseUsers to generate a GooseAttack. + // Spawn [`GooseUser`](./goose/struct.GooseUser.html) threads to generate a + // [`GooseAttack`](./struct.GooseAttack.html). async fn spawn_attack( &mut self, goose_attack_run_state: &mut GooseAttackRunState, @@ -2914,8 +2977,8 @@ impl GooseAttack { Ok(()) } - // Let the GooseAttack run until the timer expires (or the test is canceled), and then - // trigger a shut down. + // Let the [`GooseAttack`](./struct.GooseAttack.html) run until the timer expires + // (or the test is canceled), and then trigger a shut down. async fn monitor_attack( &mut self, goose_attack_run_state: &mut GooseAttackRunState, @@ -3084,7 +3147,8 @@ impl GooseAttack { Ok(()) } - // When the Goose Attack starts, optionally flush metrics. + // When the [`GooseAttack`](./struct.GooseAttack.html) goes from the `Starting` + // phase to the `Running` phase, optionally flush metrics. async fn reset_metrics( &mut self, goose_attack_run_state: &mut GooseAttackRunState, @@ -3134,7 +3198,7 @@ impl GooseAttack { Ok(()) } - // Cleanly shut down the Goose Attack. + // Cleanly shut down the [`GooseAttack`](./struct.GooseAttack.html). async fn stop_attack( &mut self, goose_attack_run_state: &mut GooseAttackRunState, @@ -3160,6 +3224,7 @@ impl GooseAttack { Ok(()) } + // Write an HTML-formatted report, if enabled. async fn write_html_report( &mut self, goose_attack_run_state: &mut GooseAttackRunState, @@ -3482,7 +3547,7 @@ impl GooseAttack { Ok(()) } - /// Called internally in local-mode and gaggle-mode. + // Called internally in local-mode and gaggle-mode. async fn start_attack(mut self, socket: Option) -> Result { trace!("start_attack: socket({:?})", socket); @@ -3532,6 +3597,7 @@ impl GooseAttack { Ok(self) } + // Receive metrics from [`GooseUser`](./goose/struct.GooseUser.html) threads. async fn receive_metrics( &mut self, goose_attack_run_state: &mut GooseAttackRunState, @@ -3665,17 +3731,18 @@ impl GooseAttack { /// All run-time options can optionally be configured with custom defaults. For /// example, you can optionally configure a default host for the load test. This is -/// used if no per-GooseTaskSet host is defined, no `--host` CLI option is -/// configured, and if the GooseTask itself doesn't hard-code the host in the base -/// url of its request. In that case, this host is added to all requests. +/// used if no per-[`GooseTaskSet`](./goose/struct.GooseTaskSet.html) host is defined, +/// no `--host` CLI option is configured, and if the +/// [`GooseTask`](./goose/struct.GooseTask.html) itself doesn't hard-code the host in +/// the base url of its request. In that case, this host is added to all requests. /// /// For example, a load test could be configured to default to running against a local /// development container, and the `--host` option could be used to override the host /// value to run the load test against the production environment. /// /// # Example -/// ```rust,no_run -/// use goose::prelude::*; +/// ```rust +/// use goose::prelude::*; /// /// fn main() -> Result<(), GooseError> { /// GooseAttack::initialize()? @@ -3723,8 +3790,8 @@ impl GooseAttack { /// - GooseDefault::Worker /// /// # Another Example -/// ```rust,no_run -/// use goose::prelude::*; +/// ```rust +/// use goose::prelude::*; /// /// fn main() -> Result<(), GooseError> { /// GooseAttack::initialize()? @@ -4018,12 +4085,13 @@ pub struct GooseConfiguration { pub manager_port: u16, } -/// Use the configured GooseScheduler to allocate all GooseTasks within the -/// GooseTaskSet in the appropriate order. Returns three set of ordered tasks: -/// `on_start_tasks`, `tasks`, and `on_stop_tasks`. The `on_start_tasks` are -/// only run once when the GooseAttack first starts. Normal `tasks` are then -/// run for the duration of the GooseAttack. The `on_stop_tasks` finally are -/// only run once when the GooseAttack stops. +/// Use the configured GooseScheduler to allocate all [`GooseTask`](./goose/struct.GooseTask.html)s +/// within the [`GooseTaskSet`](./goose/struct.GooseTaskSet.html) in the appropriate order. Returns +/// three set of ordered tasks: /// `on_start_tasks`, `tasks`, and `on_stop_tasks`. The +/// `on_start_tasks` are only run once when the [`GooseAttack`](./struct.GooseAttack.html) first +/// starts. Normal `tasks` are then run for the duration of the +/// [`GooseAttack`](./struct.GooseAttack.html). The `on_stop_tasks` finally are only run once when +/// the [`GooseAttack`](./struct.GooseAttack.html) stops. fn allocate_tasks( task_set: &GooseTaskSet, scheduler: &GooseScheduler, @@ -4271,6 +4339,7 @@ fn schedule_unsequenced_tasks( weighted_tasks } +// Helper function to determine if a host can be parsed. fn is_valid_host(host: &str) -> Result { Url::parse(host).map_err(|parse_error| GooseError::InvalidHost { host: host.to_string(), diff --git a/src/logger.rs b/src/logger.rs index 19b61fe4..ff00d3d5 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -1,18 +1,21 @@ //! Optional debug logger thread. //! //! The Goose debug logger is enabled with the `--debug-file` command-line option, or the -//! `GooseDefault::DebugFile` default configuration option. When enabled, this thread is -//! launched and a channel is provided from all `GooseUser` threads to send debug information -//! for efficient logging to file. The debug logger thread uses Tokio's asynchronous BufWriter. +//! [`GooseDefault::DebugFile`](../enum.GooseDefault.html#variant.DebugFile) default +//! configuration option. When enabled, this thread is launched and a channel is provided +//! from all [`GooseUser`](../goose/struct.GooseUser.html) threads to send debug information +//! for efficient logging to file. The debug logger thread uses Tokio's asynchronous +//! [`BufWriter`](https://docs.rs/tokio/*/tokio/io/struct.BufWriter.html). //! //! ## Writing Debug Logs //! Logs can be sent to the logger thread by invoking -//! [`log_debug`](https://docs.rs/goose/*/goose/goose/struct.GooseUser.html#method.log_debug) +//! [`log_debug`](../goose/struct.GooseUser.html#method.log_debug) //! from load test task functions. //! //! Calls to -//! [`set_failure`](https://docs.rs/goose/*/goose/goose/struct.GooseUser.html#method.set_failure) -//! automatically invoke `log_debug`. +//! [`set_failure`](../goose/struct.GooseUser.html#method.set_failure) +//! automatically invoke +//! [`log_debug`](../goose/struct.GooseUser.html#method.log_debug). //! //! Most of the included examples showing how to use the debug logger include a copy of the //! request made, the response headers returned by the server, and the response body. It can @@ -56,31 +59,37 @@ //! } //! ``` //! -//! The first call to `log_debug` results in a debug log message similar to: +//! The first call to +//! [`log_debug`](../goose/struct.GooseUser.html#method.log_debug) +//! results in a debug log message similar to: //! ```json //! {"body":null,"header":null,"request":null,"tag":"POSTing [(\"field_1\", \"foo\"), (\"field_2\", \"bar\"), (\"op\", \"Save\")] on /path/to/form"} //! ``` //! -//! The second call to `log_debug` results in a debug log message similar to: +//! The second call to +//! [`log_debug`](../goose/struct.GooseUser.html#method.log_debug) +//! results in a debug log message similar to: //! ```json //! {"body":null,"header":null,"request":{"elapsed":1,"final_url":"http://local.dev/path/to/form","method":"POST","name":"(Anon) post to form","redirected":false,"response_time":22,"status_code":404,"success":false,"update":false,"url":"http://local.dev/path/to/form","user":0},"tag":"POSTing [(\"field_1\", \"foo\"), (\"field_2\", \"bar\"), (\"op\", \"Save\")] on /path/to/form"} //! ``` //! //! For a more complex debug logging example, refer to the -//! [`log_debug`](https://docs.rs/goose/*/goose/goose/struct.GooseUser.html#method.log_debug) -//! documentation. +//! [`log_debug`](../goose/struct.GooseUser.html#method.log_debug) documentation. //! //! ## Reducing File And Memory Usage //! //! The debug logger can result in a very large debug file, as by default it includes the //! entire body of any pages returned that result in an error. This also requires allocating -//! a bigger BufWriter, and can generate a lot of disk io. +//! a bigger [`BufWriter`](https://docs.rs/tokio/*/tokio/io/struct.BufWriter.html), and can +//! generate a lot of disk io. //! //! If you don't need to log response bodies, you can disable this functionality (and reduce -//! the amount of RAM required by the BufWriter) by setting the `--no-debug-body` command-line -//! option, or the `GooseDefault::NoDebugBody` default configuration option. The debug logger -//! will still record any custom messages, details about the request (when available), and all -//! server response headers (when available). +//! the amount of RAM required by the +//! [`BufWriter`](https://docs.rs/tokio/*/tokio/io/struct.BufWriter.html) by setting the +//! `--no-debug-body` command-line option, or the +//! [`GooseDefault::NoDebugBody`](../enum.GooseDefault.html#variant.NoDebugBody) default +//! configuration option. The debug logger will still record any custom messages, details +//! about the request (when available), and all server response headers (when available). use serde_json::json; use tokio::fs::File; @@ -91,7 +100,8 @@ use crate::goose::GooseDebug; use crate::GooseConfiguration; /// Logger thread, opens a log file (if configured) and waits for messages from -/// GooseUser threads. This function is not intended to be invoked manually. +/// [`GooseUser`](../goose/struct.GooseUser.html) threads. This function is not intended +/// to be invoked manually. pub async fn logger_main( configuration: GooseConfiguration, log_receiver: flume::Receiver>, diff --git a/src/metrics.rs b/src/metrics.rs index 0aa4c7a9..ade00709 100644 --- a/src/metrics.rs +++ b/src/metrics.rs @@ -14,7 +14,8 @@ use crate::goose::{GooseMethod, GooseRawRequest, GooseRequest, GooseTaskSet}; use crate::util; use crate::GooseConfiguration; -/// Each GooseUser thread pushes these metrics to the parent for aggregation. +/// Each [`GooseUser`](../goose/struct.GooseUser.html) thread pushes these metrics to +/// the parent for aggregation. #[derive(Debug, Clone, Serialize, Deserialize)] pub enum GooseMetric { Request(GooseRawRequest), @@ -79,11 +80,13 @@ impl GooseRawTask { /// Aggregated per-task metrics updated each time a task is invoked. #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] pub struct GooseTaskMetric { - /// An index into GooseAttack.task_sets, indicating which task set this is. + /// An index into [`GooseAttack`](../struct.GooseAttack.html)`.task_sets`, + /// indicating which task set this is. pub taskset_index: usize, /// The task set name. pub taskset_name: String, - /// An index into GooseTaskSet.task, indicating which task this is. + /// An index into [`GooseTaskSet`](../goose/struct.GooseTaskSet.html)`.task`, + /// indicating which task this is. pub task_index: usize, /// An optional name for the task. pub task_name: String, @@ -125,7 +128,7 @@ impl GooseTaskMetric { } } - /// Track task function elapsed time. + /// Track task function elapsed time in milliseconds. pub fn set_time(&mut self, time: u64, success: bool) { // Perform this conversion only once, then re-use throughout this function. let time_usize = time as usize; @@ -179,7 +182,7 @@ impl GooseTaskMetric { /// Metrics collected during a Goose load test. /// /// # Example -/// ```rust,no_run +/// ```rust /// use goose::prelude::*; /// /// fn main() -> Result<(), GooseError> { @@ -187,6 +190,10 @@ impl GooseTaskMetric { /// .register_taskset(taskset!("ExampleUsers") /// .register_task(task!(example_task)) /// ) +/// // Set a default host so the load test will start. +/// .set_default(GooseDefault::Host, "http://localhost/")? +/// // Set a default run time so this test runs to completion. +/// .set_default(GooseDefault::RunTime, 1)? /// .execute()?; /// /// // It is now possible to do something with the metrics collected by Goose. @@ -209,7 +216,7 @@ pub struct GooseMetrics { pub hash: u64, /// The system timestamp of when the load test started. pub started: Option>, - /// How many seconds the load test ran. + /// Total number of seconds the load test ran. pub duration: usize, /// Total number of users simulated during this load test. pub users: usize, @@ -256,7 +263,7 @@ impl GooseMetrics { /// Consumes and display all metrics from a completed load test. /// /// # Example - /// ```rust,no_run + /// ```rust /// use goose::prelude::*; /// /// fn main() -> Result<(), GooseError> { @@ -264,6 +271,10 @@ impl GooseMetrics { /// .register_taskset(taskset!("ExampleUsers") /// .register_task(task!(example_task)) /// ) + /// // Set a default host so the load test will start. + /// .set_default(GooseDefault::Host, "http://localhost/")? + /// // Set a default run time so this test runs to completion. + /// .set_default(GooseDefault::RunTime, 1)? /// .execute()? /// .print(); /// @@ -1019,7 +1030,9 @@ impl GooseMetrics { } } +/// Implement format trait to allow displaying metrics. impl fmt::Display for GooseMetrics { + // Implement display of metrics with `{}` marker. fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { // Formats from zero to six tables of data, depending on what data is contained // and which contained flags are set.