Skip to content

Commit fe214e9

Browse files
authored
feat(api): Add endpoint to retrieve response time (#1070)
Add in the API the ability to get Response Times Co-authored-by: Adrian Almenar <[email protected]>
1 parent 493c716 commit fe214e9

File tree

4 files changed

+79
-0
lines changed

4 files changed

+79
-0
lines changed

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,10 +118,12 @@ Have any feedback or questions? [Create a discussion](https://github.com/TwiN/ga
118118
- [Health](#health)
119119
- [Health (Shields.io)](#health-shieldsio)
120120
- [Response time](#response-time)
121+
- [Response time (chart)](#response-time-chart)
121122
- [How to change the color thresholds of the response time badge](#how-to-change-the-color-thresholds-of-the-response-time-badge)
122123
- [API](#api)
123124
- [Raw Data](#raw-data)
124125
- [Uptime](#uptime-1)
126+
- [Response Time](#response-time-1)
125127
- [Installing as binary](#installing-as-binary)
126128
- [High level design overview](#high-level-design-overview)
127129

@@ -2511,6 +2513,20 @@ For instance, if you want the raw uptime data for the last 24 hours from the end
25112513
https://example.com/api/v1/endpoints/core_frontend/uptimes/24h
25122514
```
25132515

2516+
##### Response Time
2517+
The path to get raw response time data for an endpoint is:
2518+
```
2519+
/api/v1/endpoints/{key}/response-times/{duration}
2520+
```
2521+
Where:
2522+
- `{duration}` is `30d`, `7d`, `24h` or `1h`
2523+
- `{key}` has the pattern `<GROUP_NAME>_<ENDPOINT_NAME>` in which both variables have ` `, `/`, `_`, `,` and `.` replaced by `-`.
2524+
2525+
For instance, if you want the raw response time data for the last 24 hours from the endpoint `frontend` in the group `core`, the URL would look like this:
2526+
```
2527+
https://example.com/api/v1/endpoints/core_frontend/response-times/24h
2528+
```
2529+
25142530
### Installing as binary
25152531
You can download Gatus as a binary using the following command:
25162532
```

api/api.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ func (a *API) createRouter(cfg *config.Config) *fiber.App {
8080
unprotectedAPIRouter.Get("/v1/endpoints/:key/health/badge.shields", HealthBadgeShields)
8181
unprotectedAPIRouter.Get("/v1/endpoints/:key/uptimes/:duration", UptimeRaw)
8282
unprotectedAPIRouter.Get("/v1/endpoints/:key/uptimes/:duration/badge.svg", UptimeBadge)
83+
unprotectedAPIRouter.Get("/v1/endpoints/:key/response-times/:duration", ResponseTimeRaw)
8384
unprotectedAPIRouter.Get("/v1/endpoints/:key/response-times/:duration/badge.svg", ResponseTimeBadge(cfg))
8485
unprotectedAPIRouter.Get("/v1/endpoints/:key/response-times/:duration/chart.svg", ResponseTimeChart)
8586
// This endpoint requires authz with bearer token, so technically it is protected

api/raw.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,35 @@ func UptimeRaw(c *fiber.Ctx) error {
4141
c.Set("Expires", "0")
4242
return c.Status(200).Send([]byte(fmt.Sprintf("%f", uptime)))
4343
}
44+
45+
func ResponseTimeRaw(c *fiber.Ctx) error {
46+
duration := c.Params("duration")
47+
var from time.Time
48+
switch duration {
49+
case "30d":
50+
from = time.Now().Add(-30 * 24 * time.Hour)
51+
case "7d":
52+
from = time.Now().Add(-7 * 24 * time.Hour)
53+
case "24h":
54+
from = time.Now().Add(-24 * time.Hour)
55+
case "1h":
56+
from = time.Now().Add(-2 * time.Hour) // Because uptime metrics are stored by hour, we have to cheat a little
57+
default:
58+
return c.Status(400).SendString("Durations supported: 30d, 7d, 24h, 1h")
59+
}
60+
key := c.Params("key")
61+
responseTime, err := store.Get().GetAverageResponseTimeByKey(key, from, time.Now())
62+
if err != nil {
63+
if errors.Is(err, common.ErrEndpointNotFound) {
64+
return c.Status(404).SendString(err.Error())
65+
} else if errors.Is(err, common.ErrInvalidTimeRange) {
66+
return c.Status(400).SendString(err.Error())
67+
}
68+
return c.Status(500).SendString(err.Error())
69+
}
70+
71+
c.Set("Content-Type", "text/plain")
72+
c.Set("Cache-Control", "no-cache, no-store, must-revalidate")
73+
c.Set("Expires", "0")
74+
return c.Status(200).Send([]byte(fmt.Sprintf("%d", responseTime)))
75+
}

api/raw_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,36 @@ func TestRawDataEndpoint(t *testing.T) {
7474
Path: "/api/v1/endpoints/invalid_key/uptimes/7d",
7575
ExpectedCode: http.StatusNotFound,
7676
},
77+
{
78+
Name: "raw-response-times-1h",
79+
Path: "/api/v1/endpoints/core_frontend/response-times/1h",
80+
ExpectedCode: http.StatusOK,
81+
},
82+
{
83+
Name: "raw-response-times-24h",
84+
Path: "/api/v1/endpoints/core_backend/response-times/24h",
85+
ExpectedCode: http.StatusOK,
86+
},
87+
{
88+
Name: "raw-response-times-7d",
89+
Path: "/api/v1/endpoints/core_frontend/response-times/7d",
90+
ExpectedCode: http.StatusOK,
91+
},
92+
{
93+
Name: "raw-response-times-30d",
94+
Path: "/api/v1/endpoints/core_frontend/response-times/30d",
95+
ExpectedCode: http.StatusOK,
96+
},
97+
{
98+
Name: "raw-response-times-with-invalid-duration",
99+
Path: "/api/v1/endpoints/core_backend/response-times/3d",
100+
ExpectedCode: http.StatusBadRequest,
101+
},
102+
{
103+
Name: "raw-response-times-for-invalid-key",
104+
Path: "/api/v1/endpoints/invalid_key/response-times/7d",
105+
ExpectedCode: http.StatusNotFound,
106+
},
77107
}
78108
for _, scenario := range scenarios {
79109
t.Run(scenario.Name, func(t *testing.T) {

0 commit comments

Comments
 (0)