Skip to content

Repository files navigation

echoserver

The echoserver is a HTTP / gRPC server written in Go, which was mainly written to dump HTTP requests. Nowadays it can also be used to test / showcase the instrumentation of a Go application with metrics, logs, traces and profiles. It can also be used to test the timeout / header size configuration of a reverse proxy.

Building and Running

To build and run the echoserver the following commands can be used:

make build
./bin/echoserver

Via Docker the following commands can be used to build the image and run the echoserver:

docker build -f ./cmd/echoserver/Dockerfile -t ghcr.io/ricoberger/echoserver:latest .
docker run -it --rm --name echoserver -p 8080:8080 -p 8081:8081 ghcr.io/ricoberger/echoserver:latest

The echoserver can also be deployed on Kubernetes via Helm:

helm upgrade --install echoserver oci://ghcr.io/ricoberger/charts/echoserver --version <VERSION>

Configuration

Usage: echoserver [flags]

Flags:
  -h, --help                           Show context-sensitive help.
      --http-server.address=":8080"    The address where the HTTP server should listen on ($HTTP_SERVER_ADDRESS).
      --grpc-server.address=":8081"    The address where the gRPC server should listen on ($GRPC_SERVER_ADDRESS).
# Configure an OTLP/gRPC or OTLP/HTTP endpoint for traces, metrics, and logs.
# To configure different endpoints for traces, metrics, and logs, use the
# OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, OTEL_EXPORTER_OTLP_METRICS_ENDPOINT,
# and OTEL_EXPORTER_OTLP_LOGS_ENDPOINT environment variables.
export OTEL_EXPORTER_OTLP_ENDPOINT="localhost:4317"
# Set the logs exporter which should be used. Valid values are "console",
# "otlp" and "none". The default value is "none".
export OTEL_LOGS_EXPORTER="console"
# Set the metrics exporter which should be used. Valid values are "console",
# "otlp", "prometheus" and "none". The default value is "none".
export OTEL_METRICS_EXPORTER="console"
# Set the traces exporter which should be used. Valid values are "console",
# "otlp" and "none". The default value is "none".
export OTEL_TRACES_EXPORTER="console"
# Set the profiles exporter which should be used. The only valid value is
# "pyroscope".
export OTEL_PROFILES_EXPORTER=""
# Key-value pairs to be used as resource attributes. This can be used to
# overwrite the default service name / version and to set additional attributes,
# like the Kubernetes Pod name, etc.
export OTEL_RESOURCE_ATTRIBUTES=service.name=my-service,service.version=1.0.0
# Enable resource detectors. Valid values are "container", "host", "os",
# "process", and "sdk".
export OTEL_RESOURCE_DETECTORS="container,host,os,process,sdk"

# When "OTEL_LOGS_EXPORTER" is set to "console", the following environment
# variables can be used to configure the logging output:
# - LOG_FORMAT: Set the output format of the logs. Must be "console" or "json".
# - LOG_LEVEL: Set the log level. Must be "DEBUG", "INFO", "WARN" or "ERROR".
# - LOG_RESOURCE_ATTRIBUTES: Define if the resource attributes should be logged.
#   Must be "true" or "false".
export LOG_FORMAT="console"
export LOG_LEVEL="INFO"
export LOG_RESOURCE_ATTRIBUTES="true"

# When "OTEL_METRICS_EXPORTER" is set to "prometheus", the following environment
# variables can be used to configure the Prometheus metrics:
export PROMETHEUS_RESOURCE_ATTRIBUTES="service.name,service.namespace"

# When "OTEL_PROFILES_EXPORTER" is set to "pyroscope", the following environment
# variables are required:
# - PYROSCOPE_SERVER_ADDRESS: The address of the Pyroscope server.

Usage

HTTP Endpoints

  • /: Dump the HTTP request.
  • /health: Returns a 200 status code.
  • /panic: Panics within the http handler and returns a status code 500.
  • /status: Returns a random status code, when the status parameter is empty or random. Return the status code specified in the status parameter, e.g. ?status=200.
  • /timeout: Wait the given amount of time (?timeout=1m) before returning a 200 status code. The optional flush parameter (?timeout=10s&flush=2s) writes and flushes a message in the given interval while waiting.
  • /headersize: Returns a 200 status code with a header X-Header-Size of the size defined via ?size=1024.
  • /request: Returns the response of the requested server. The request body should have the following structure: {"method": "POST", "url": "http://localhost:8080/", "body": "test", "headers": {"x-test": "test"}}
  • /fibonacci: Returns the Fibonacci number for the given n parameter, e.g. ?n=100. The intention behind this endpoint is to simulate a CPU-intensive task.
  • /simulate: Simulates some heavy work to exercise the collected profiles. The simulation is selected via the type parameter and runs for the given duration (e.g. ?duration=10s). Valid types are:
    • cpu: Keeps all CPUs busy (?type=cpu&duration=10s).
    • memory: Allocates and retains size bytes of memory (?type=memory&duration=10s&size=268435456).
    • goroutines: Spawns count goroutines (?type=goroutines&duration=10s&count=1000).
    • mutex: Runs workers goroutines contending on a mutex (?type=mutex&duration=10s&workers=100).
    • block: Runs workers goroutines blocking on a channel (?type=block&duration=10s&workers=100).
  • /websocket: Can be used to test WebSocket connections. It returns the message sent over the WebSocket connection.
  • /metrics: Returns the captured Prometheus metrics, when the OTEL_METRICS_EXPORTER environment variable is set to prometheus.
  • /debug/pprof/: Exposes the pprof profiling endpoints (e.g. /debug/pprof/profile, /debug/pprof/heap, /debug/pprof/goroutine).

gRPC Endpoints

  • Echoserver.Echo: Echoes the message sent in the request.
  • Echoserver.Status: Returns a gRPC error with the status specified in the status field of the request or a random gRPC error when the value of the status field is empty or random.
  • Echoserver.Request: Forwards the request to the specified gRPC endpoint and returns the response. The request message should have the following structure: {"uri": "localhost:8081", "method": "Echoserver.Echo", "message": "{ \"message\": \"Hello\" }"}
  • Echoserver.Simulate: Simulates some heavy work to exercise the collected profiles. The simulation is selected via the type field (cpu, memory, goroutines, mutex or block) and runs for the given duration. Depending on the type the size (memory), count (goroutines) or workers (mutex and block) field is required, e.g. {"type": "cpu", "duration": "10s"} or {"type": "memory", "duration": "10s", "size": 268435456}.

Examples

curl -vvv "http://localhost:8080/"
curl -vvv "http://localhost:8080/panic"
curl -vvv "http://localhost:8080/status"
curl -vvv "http://localhost:8080/status?status=400"
curl -vvv "http://localhost:8080/timeout?timeout=10s&flush=2s"
curl -vvv "http://localhost:8080/headersize?size=100"
curl -vvv -X POST -d '{"method": "POST", "url": "http://localhost:8080/", "body": "test", "headers": {"x-test": "test"}}' http://localhost:8080/request
curl -vvv "http://localhost:8080/fibonacci?n=100"
curl -vvv "http://localhost:8080/simulate?type=cpu&duration=10s"
curl -vvv "http://localhost:8080/simulate?type=memory&duration=10s&size=268435456"
curl -vvv "http://localhost:8080/simulate?type=goroutines&duration=10s&count=1000"
grpcurl -format-error -plaintext -d '{ "message": "Hello" }' 'localhost:8081' Echoserver.Echo
grpcurl -format-error -plaintext -d '{ "status": "random" }' 'localhost:8081' Echoserver.Status
grpcurl -format-error -plaintext -d '{ "uri": "localhost:8081", "method": "Echoserver.Status", "message": "{ \"status\": \"random\" }" }' 'localhost:8081' Echoserver.Request
grpcurl -format-error -plaintext -d '{ "uri": "localhost:8081", "method": "Echoserver.Echo", "message": "{ \"message\": \"Hello\" }" }' 'localhost:8081' Echoserver.Request
grpcurl -format-error -plaintext -d '{ "type": "cpu", "duration": "10s" }' 'localhost:8081' Echoserver.Simulate
grpcurl -format-error -plaintext -d '{ "type": "memory", "duration": "10s", "size": 268435456 }' 'localhost:8081' Echoserver.Simulate

# List endpoints, list methods for a specific endpoint and describe a method
grpcurl -plaintext 'localhost:8081' list
grpcurl -plaintext 'localhost:8081' list Echoserver
grpcurl -plaintext 'localhost:8081' describe Echoserver.Echo

Load Testing

The hack/loadtest.sh script generates load against a running echoserver by repeatedly calling all of its HTTP and gRPC endpoints. It is useful for producing telemetry (metrics, logs, traces and profiles) to test the instrumentation. It only requires curl and, for the gRPC endpoints, grpcurl.

# Run against a local echoserver for 60s with 4 parallel workers.
./hack/loadtest.sh

# Customize the run via environment variables.
DURATION=120 CONCURRENCY=8 HTTP_ADDRESS=localhost:8080 GRPC_ADDRESS=localhost:8081 ./hack/loadtest.sh

About

A simple HTTP and gRPC server that demonstrates the use of OpenTelemetry for metrics, logs, traces and profiles.

Topics

Resources

Stars

11 stars

Watchers

1 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages