Master Your Logs and Status Codes: Introducing the Go Observability Sidekick

Nikhil IkharNikhil Ikhar
6 min read

Are you tired of constantly tweaking your application code just to test your observability alerts? Whether it's validating log patterns for your SIEM or ensuring HTTP status code triggers fire correctly in your monitoring system, the traditional approach often involves modifying and redeploying your core application. This process is不仅 time-consuming but also introduces unnecessary risk to your production-ready code.

Enter the Go Logger Tester, a lightweight yet powerful utility designed to simplify your observability testing workflow. This project provides a simple Go-based API that allows you to programmatically generate logs with specific levels and messages, and return custom HTTP response codes, all without touching your main application's codebase. It's the perfect sidecar for your testing and validation environments!

The Problem It Solves

Modern cloud-native applications rely heavily on robust observability. We set up alerts for error logs, specific log patterns, and HTTP 5xx responses to ensure system health and quick incident response. However, testing these alerts effectively can be a significant challenge:

  • Log Alert Testing: How do you simulate an ERROR log with a very specific message that your alert rule is designed to catch? Often, developers resort to adding temporary log.Error("TEST_ERROR_PATTERN_123") lines to their application, then removing them after the test. This is cumbersome and error-prone.

  • HTTP Alert Testing: Need to see if your system triggers an alert when a service returns a 500 Internal Server Error or a 404 Not Found under specific conditions? Similar to logging, you might temporarily introduce error-inducing code paths.

  • Deployment Overhead: Each change (even temporary ones) often means a new build, a new container image, and a redeployment cycle.

The Go Logger Tester directly addresses these pain points by providing a dedicated, external service that can be deployed alongside your application, acting as a "test harness" for your observability stack.

Core Features and How It Works

This project is built with simplicity and effectiveness in mind, leveraging Go's performance and the popular Gin web framework for its API.

1. Configurable Log Generation

The core feature is the ability to generate logs with a specified level (INFO, WARNING, ERROR) and custom content. This is crucial for testing log-based alerts. The application uses Go's standard log package, configured to output to os.Stdout, making it ideal for containerized environments where logs are typically collected from standard output streams.

Let's look at how the loggers are initialized:

// File: logger.go
package main

import (
    "log"
    "os"
)

var (
    WarningLogger *log.Logger
    InfoLogger    *log.Logger
    ErrorLogger   *log.Logger
)

func init() {
    InfoLogger = log.New(os.Stdout, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile)
    WarningLogger = log.New(os.Stdout, "WARNING: ", log.Ldate|log.Ltime|log.Lshortfile)
    ErrorLogger = log.New(os.Stdout, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile)
}

By defining separate loggers for each level, the output is clearly prefixed, aiding in parsing and filtering by log aggregation systems. The log.Lshortfile flag is particularly useful for debugging, as it shows the file and line number where the log was called.

2. Custom HTTP Status Code Responses

Beyond logging, the application allows you to dictate the HTTP status code it returns. This is invaluable for testing API Gateway rules, load balancer health checks, or monitoring alerts that trigger on specific HTTP response ranges (e.g., 5xx errors).

3. Simple JSON API Endpoint

The POST /log endpoint is the heart of the application, accepting a JSON payload that defines the desired log message, log level, and HTTP status code for the response.

Here's the data structure it expects:

// File: main.go
type logData struct {
    Data       string `json:"data"`
    StatusCode int    `json:"statusCode"`
    Level      string `json:"level"`
}

And the logic to process it:

// File: main.go
func postLog(c *gin.Context) {
    var newLog logData

    if err := c.BindJSON(&newLog); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request payload"})
        return
    }

    // Default to Bad Request if statusCode is invalid or level is unknown
    statusCode := newLog.StatusCode
    data := newLog.Data

    switch level := strings.ToUpper(newLog.Level); level {
    case "INFO":
        InfoLogger.Println(newLog.Data)
    case "WARNING":
        WarningLogger.Println(newLog.Data)
    case "ERROR":
        ErrorLogger.Println(newLog.Data)
    default:
        statusCode = http.StatusBadRequest // If level is invalid, return Bad Request
        data = "Incorrect Level. Must be INFO, WARNING, or ERROR."
    }

    // Validate the status code to ensure it's a known HTTP status
    if len(http.StatusText(newLog.StatusCode)) == 0 {
        data = "Incorrect status code. Please provide a valid HTTP status code."
        statusCode = http.StatusBadRequest 
    }

    c.JSON(statusCode, data) // Respond with the requested status code and data
}

This function demonstrates:

  • JSON Binding: Gin efficiently parses the incoming JSON request into the logData struct.

  • Log Level Handling: A switch statement dynamically dispatches the log message to the appropriate InfoLogger, WarningLogger, or ErrorLogger. Invalid levels result in a 400 Bad Request response.

  • Status Code Control: The c.JSON() call ensures the HTTP response code is set to the value provided in the payload, with basic validation to ensure it's a valid HTTP status code.

4. Docker-Ready Deployment

The project includes a multi-stage Dockerfile, making it incredibly easy to build a small, efficient Docker image. This is key for deploying it as a sidecar container in Kubernetes or any containerized environment.

# File: Dockerfile
FROM golang:${GO_VERSION}-alpine AS builder
# ... install build dependencies ...
WORKDIR /api
COPY go.mod .
COPY go.sum .
RUN go mod download
COPY *.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o /app  .

FROM alpine:latest
WORKDIR /api
COPY --from=builder /app .

ENV GIN_MODE release
EXPOSE 8080
ENTRYPOINT ["./app"]

This Dockerfile is optimized for production, building a lean image by copying only the compiled Go binary into a minimal alpine:latest base image.

How to Use It

Using the Go Logger Tester is straightforward. Once deployed (e.g., as a sidecar alongside your main application in a Kubernetes pod), you can interact with it using a simple curl command:

curl http://localhost:8080/log \
--header "Content-Type: application/json" \
--request "POST" \
--data  '{"data":"This is a test INFO log message for my cluster.","statusCode":201, "level":"INFO"}'

This command will:

  1. Print INFO: <timestamp> <filename>:... This is a test INFO log message for my cluster. to the standard output of the go.logger container.

  2. Return an HTTP 201 Created status code in its response.

You can change level to WARNING or ERROR, and statusCode to 500, 404, or any other valid HTTP code to test different scenarios. There's also a simple GET /ping endpoint for health checks.

Conclusion

The Go Logger Tester empowers developers and SREs to take control of their observability testing. By providing a dedicated, configurable service for generating specific logs and HTTP responses, it eliminates the need for intrusive code changes in your main applications. This "sidecar" pattern promotes cleaner code, faster testing cycles, and more reliable observability setups.

Future Enhancements could include:

  • Configurable Log Formats: Support for JSON, CSV, or other structured log formats.

  • Delayed Responses/Errors: Introduce artificial delays or intermittent errors to simulate real-world network conditions.

  • More Complex Payloads: Allow logging of more complex data structures.

  • Metrics Generation: Integrate with Prometheus or other metrics systems to generate custom metrics on demand.

Whether you're validating a new log aggregation pipeline, fine-tuning your alerting rules, or simply demonstrating observability capabilities, the Go Logger Tester is a valuable addition to your toolkit. Give it a try and streamline your observability testing today!

Github Repo

https://github.com/nik-hil/go-grpc-course

0
Subscribe to my newsletter

Read articles from Nikhil Ikhar directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Nikhil Ikhar
Nikhil Ikhar