Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Writing custom metrics based on the event in task #396

Open
wchmiela opened this issue Dec 1, 2021 · 6 comments
Open

Writing custom metrics based on the event in task #396

wchmiela opened this issue Dec 1, 2021 · 6 comments
Labels
enhancement New feature or request

Comments

@wchmiela
Copy link

wchmiela commented Dec 1, 2021

I'm using different HTTP/2 client (h2 instead of reqwest) because of push promise support, therefore I need to measure some events like pushed data.

In locust.io there was an option to fire certain event using events.request.fire(...) providing request type, response time etc.

In goose, I can see a pub fn set_success(&self, request: &mut GooseRequestMetric) -> GooseTaskResult, which manually marks a request as a success. What is the best approach to mark custom event (which has no origin in built-in HTTP/2 client) and make it visible in the metrics section?

@jeremyandrews
Copy link
Member

Are you referring to https://docs.rs/h2/0.3.7/h2/client/index.html?

At this time Goose is very tightly coupled with Reqwest -- the goal is to make it possible to swap it out for other clients in #32 but this hasn't been worked on recently. Request metrics are collected in GooseUser::request, and this is the function that would ultimately need to be made more generic.

In theory you should be able to do the same thing that we do in that function in your own code to generate metrics. You'd need to create a GooseRequestMetric as happens here:
https://github.com/tag1consulting/goose/blob/main/src/goose.rs#L1556

Record the response time:
https://github.com/tag1consulting/goose/blob/main/src/goose.rs#L1565

Record the response code:
https://github.com/tag1consulting/goose/blob/main/src/goose.rs#L1574

Record the final URL (to know if it redirected):
https://github.com/tag1consulting/goose/blob/main/src/goose.rs#L1575

Update success to true or false, setting error if there was one.
https://github.com/tag1consulting/goose/blob/main/src/goose.rs#L1581

And send the resulting object to the parent:
https://github.com/tag1consulting/goose/blob/main/src/goose.rs#L1628

@jeremyandrews
Copy link
Member

Note that none of the methods defined on GooseRequestMetric (used above) are public:
https://github.com/tag1consulting/goose/blob/main/src/metrics.rs#L293

But all the fields are public, so you can just set/update them directly.

@jeremyandrews
Copy link
Member

In writing an Example to demonstrate how this might work, I realized send_request_metric_to_parent() isn't public, so what I describe above isn't going to work. Simply making that function public does make the following possible -- would this be enough to be useful to you? Perhaps if a couple of helpers are added it could be useful enough.

use goose::prelude::*;
use goose::metrics::{GooseRawRequest, GooseRequestMetric};
use isahc::prelude::*;

async fn loadtest_index(user: &mut GooseUser) -> GooseTaskResult {
    let started = std::time::Instant::now();
    let url = "http://example.com/";
    let mut response = isahc::get(url).unwrap();

    let status = response.status();
    let mut headers: Vec<String> = Vec::new();
    for header in response.headers() {
        headers.push(format!("{:?}", header));
    }
    response.consume().unwrap();

    // Record information about the request.
    let raw_request = GooseRawRequest {
        method: GooseMethod::Get,
        url: url.to_string(),
        headers,
        body: "".to_string(),
    };
    let request_metric = GooseRequestMetric {
        elapsed: user.started.elapsed().as_millis() as u64,
        raw: raw_request,
        //@TODO
        name: url.to_string(),
        //@TODO
        final_url: url.to_string(),
        //@TODO
        redirected: false,
        response_time: started.elapsed().as_millis() as u64,
        status_code: status.as_u16(),
        success: status.is_success(),
        update: false,
        user: user.weighted_users_index,
        //@TODO:
        error: "".to_string(),
        //@TODO
        coordinated_omission_elapsed: 0,
        //@TODO
        user_cadence: 0,
    };

    user.send_request_metric_to_parent(request_metric)?;

    Ok(())
}

@wchmiela
Copy link
Author

wchmiela commented Dec 3, 2021

Thanks @jeremyandrews for a quick response. That's exactly what I've implemented. I guess it's a nice way to record any metric.

@jeremyandrews
Copy link
Member

There's a work in progress here to better demonstrate with a fully-functional example:
#397

My eventual goal is to make changes to Goose that simplify this example, but to begin my goal is simply for it to work without lost functionality.

@jeremyandrews
Copy link
Member

One issue I've noticed is that if you manage your own client, then each time a task runs you're essentially building a new client and things like cookies and headers aren't preserved across requests. This means you can't (easily) simulate logging into an application, etc. I do hope to come up with a generic solution in the linked work-in-progress PR.

@jeremyandrews jeremyandrews added the enhancement New feature or request label Mar 23, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants