Introducing Goctuator: a Go-Based Actuator Module
Tags: go, monitoring, webdev
Published: 2022-11-23 03:55:58

As a developer, I'm constantly deploying and updating production-ready web servers. Some of the most important features that I look for in a web server are the metrics and the server's health. Having developed a number of Java web servers, I've come to appreciate the [Spring Boot Actuator](https://www.baeldung.com/spring-boot-actuators) extensions for Spring Web applications.

In the context of a Spring application, an actuator is a server extension which will provide information about the server. These metrics could be items like the health and stability of connections that the application depends on (i.e. the database), or system resource utilization metrics. When I deploy applications onto a deployment environment such as a Kubernetes cluster or into the Cloud (EKS, ECS, Fargate), actuators are consumed by the cloud services to monitor the service health.

I wasn't able to find much to go on as far as modules to implement this feature in Golang. I decided to roll my own.

## What is Goctuator?

[Goctuator](https://gitlab.com/mikeyGlitz/gohealth) is short for Go-Actuator. This module provides spring-boot like actuator functionality to Go-based web servers. Goctuator provides the following endpoints

- `/env` - Displays the current runtime environment variables
- `/info` - Used to convey information about the package being published such as author, version number, go-lang runtime that the project was built with, CPU architecture that the application was built for, Git revision hash, and the repo URL
- `/health` - This endpoint displays the overall system health as well as the health of any dependencies (i.e. database connection status)
- `/metrics` - This endpoint displays system resource utilization
- `/treaddump` - This endpoint displays a snapshot of the current thread and leverages `pprof.lookup` to gather information about the goroutines in use

## Installation

Installing Goctuator is fairly simple. Use `go get` to retrieve the module and add it to your project

``` bash
go get gitlab.com/mikeyGlitz/gohealth
```

After the package is installed you could add it to your project using a HTTP handler

### HTTP package

```go
handler := actuator.GetHandler(&actuator.Config{})
http.Handle("/actuator", handler)
```

### Gin handler
```go
handler := actuator.GetHandler(&actuator.Config{})
r := gin.Default()
r.Get("/actuator", func(ctx *gin.Context) {
    handle(ctx.Writer, ctx.Request)
}
```

## Customization

The Goctuator module may be customized in one of two ways:
- You can disable an endpoint
- You can add custom health checkers
- You can tailor the `/info` endpoint

### Customizing available endpoints

The `actuator.Configuration` structure supports a slice `Endpoints` which is used to configure which endpoints are enabled as actuators:

```go
config := &actuator.Configuration {
  Endpoints: []actuator.Endpoint {
    actuator.ENV,
    actuator.INFO,
  }
}
```

With this configuration, only the `/info` and `/env` endpoints would be enabled

### Customizing Health Checkers

The health checkers may also be customized. By default the module provides endpoints which simulate a generic ping response as well as an endpoint which provides disk usage statistics for the application. These health checkers are located in `health.PingChecker` and `health.DiskStatusChecker`.

You may also write your own Health Checker by implementing the `HealthChecker` interface:

```go
type ComponentDetails struct {
  Status HealthStatus `json:status` // Can either be UP or DOWN
  // Details - a struct which can be converted into a JSON
  // object which will provide additional details about the
  // service which health is being checked
  Details interface{} `json:details`
}

type HealthCheckResult struct {
  ComponentDetails
  Service string
}

type HealthChecker interface {
  CheckHealth() HealthCheckResult
}
```

When the `/health` endpoint is requested, the response will resemble the following JSON object:

```json
{
  "status": "UP",
  "components": {
    "diskspace": {
      "status": "UP",
      "details": {
        "all": "",
        "used": "",
        "free": "",
        "available": ""
      }
    },
    "ping": {
      "status": "UP"
    }
  }
}
```

### Customizing information returned by `/info`

The information returned by the `/info` endpoint is set at compile-time using [ldflags](https://www.digitalocean.com/community/tutorials/using-ldflags-to-set-version-information-for-go-applications)

```bash
go build -ldflags="-X gitlab.com/mikeyGlitz/gohealth/pkg/info.AppName=${appName} <other flags>"
```

The `/info` endpoint utilizes the following variables:

| Variable                    | Description                                                |
| --------------------------- | ---------------------------------------------------------- |
| AppName                     | The name of your application                               |
| AppDescription              | A brief description of what your application does          |
| AppVersion                  | An application version i.e. v1.0.0                         |
| **Git Variables**           |                                                            |
| CommitID                    | The SHA1 of the commit                                     |
| CommitTime                  | A timestamp of when the commit took place                  |
| BuildTime                   | A timestamp of when the application build took place       |
| RepositoryUrl               | A URL of where the Git repository is located               |
| Branch                      | The branch name the build was executed on                  |
| **Application Environment** |                                                            |
| OS                          | Operating system the application runs - darwin, linux      |
| RuntimeVersion              | The version of go the application was built for            |
| Arch                        | The CPU architecture the application was built for - arm64 |

For more information about Goctuator and how to use it, please consult the [Readme](https://gitlab.com/mikeyGlitz/gohealth/-/blob/main/README.md).