Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions cmd/loki/health.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package main

import (
"fmt"
"net/http"
"os"
"regexp"
"time"
)

const (
healthFlag = "health"
defaultHealthURL = "http://localhost:3100/ready"
healthTimeout = 5 * time.Second
)

// CheckHealth checks if args contain the -health flag
func CheckHealth(args []string) bool {
pattern := regexp.MustCompile(`^-+` + healthFlag + `$`)
for _, a := range args {
if pattern.MatchString(a) {
return true
}
}
return false
}

// RunHealthCheck performs a health check against the /ready endpoint
// Returns exit code 0 if healthy, 1 if unhealthy
func RunHealthCheck(args []string) int {

url := getHealthURL(args)

client := &http.Client{
Timeout: healthTimeout,
}

resp, err := client.Get(url)
if err != nil {
fmt.Fprintf(os.Stderr, "Health check failed: %v\n", err)
return 1
}
defer resp.Body.Close()

if resp.StatusCode == http.StatusOK {
fmt.Println("Loki is healthy")
return 0
}

fmt.Fprintf(os.Stderr, "Loki is unhealthy: status code %d\n", resp.StatusCode)
return 1
}

// getHealthURL extracts the URL from args or returns default
// Looks for -health.url=<url> or -health.url <url>
func getHealthURL(args []string) string {
urlPattern := regexp.MustCompile(`^-+health\.url[=:]?(.*)$`)
healthArgPattern := regexp.MustCompile(`^-`)

for i, a := range args {
if matches := urlPattern.FindStringSubmatch(a); matches != nil {
if matches[1] != "" {
return matches[1]
}
// Check next argument for the URL value
if i+1 < len(args) && !healthArgPattern.MatchString(args[i+1]) {
return args[i+1]
}
}
}
return defaultHealthURL
}
6 changes: 6 additions & 0 deletions cmd/loki/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ func main() {

var config loki.ConfigWrapper

// Health check command - runs before any config parsing
// Usage: loki -health [-health.url=http://localhost:3100/ready]
if CheckHealth(os.Args[1:]) {
os.Exit(RunHealthCheck(os.Args[1:]))
}

if loki.PrintVersion(os.Args[1:]) {
fmt.Println(version.Print("loki"))
os.Exit(0)
Expand Down
26 changes: 22 additions & 4 deletions production/docker/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,6 @@ services:
init:
image: &lokiImage grafana/loki:latest
user: root
entrypoint:
- "chown"
- "10001:10001"
- "/loki"
volumes:
- ./loki:/loki
networks:
Expand Down Expand Up @@ -57,6 +53,7 @@ services:
# for testing purposes only, disable in production
log-generator:
image: mingrammer/flog
platform: linux/amd64
command:
- --loop
- --format=json
Expand Down Expand Up @@ -132,6 +129,13 @@ services:
deploy:
mode: replicated
replicas: 3
# Native healthcheck using loki -health command (no shell required)
healthcheck:
test: ["CMD", "/usr/bin/loki", "-health"]
start_period: 30s
interval: 30s
timeout: 10s
retries: 3

loki-write:
image: *lokiImage
Expand All @@ -154,6 +158,13 @@ services:
deploy:
mode: replicated
replicas: 3
# Native healthcheck using loki -health command (no shell required)
healthcheck:
test: ["CMD", "/usr/bin/loki", "-health"]
start_period: 30s
interval: 30s
timeout: 10s
retries: 3

loki-backend:
image: *lokiImage
Expand All @@ -177,6 +188,13 @@ services:
deploy:
mode: replicated
replicas: 3
# # Native healthcheck using loki -health command (no shell required)
healthcheck:
test: ["CMD", "/usr/bin/loki", "-health"]
start_period: 30s
interval: 30s
timeout: 10s
retries: 3

# alertmanager to enable receiving alerts
alertmanager:
Expand Down