Getting started
This guide builds a small HTTP service: it answers GET /hello/{name}, and produces traces, metrics and logs.
Go 1.26 or later is required.
Install
go mod init example.com/hellogo get github.com/go-oryn/oryn/core github.com/go-oryn/oryn/httpserverConfiguration
Configuration files live in a configs directory, embedded in the binary:
hello/├── configs/│ ├── app.yaml│ ├── embed.go│ └── dev/│ └── otel.yaml├── main.go└── main_test.goapp: name: hello version: 0.1.0log: console: enabled: true# configs/dev/otel.yaml: loaded when APP_ENV=dev, prints the spans on the consoleotel: traces: exporter: console metrics: exporter: none logs: exporter: nonepackage configs
import "embed"
//go:embed *.yaml */*.yamlvar FS embed.FSSee Configuration for the loading rules.
Application
package main
import ( "fmt" "net/http"
"github.com/go-oryn/oryn/core" "github.com/go-oryn/oryn/httpserver"
"example.com/hello/configs")
// Module holds the application handlers. Tests reuse it.var Module = httpserver.AsHandler("GET /hello/{name}", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "hello %s", r.PathValue("name"))})
func main() { core.New( core.WithConfigFS(configs.FS), httpserver.Module, Module, ).Run(httpserver.Serve())}core.Newcreates the application, with the core module: configuration, OpenTelemetry, logging and health checks.httpserver.Moduleprovides the HTTP server, andhttpserver.AsHandlerregisters a handler on it.Runstarts the application with the HTTP server, and stops it gracefully onSIGINTorSIGTERM.
Run it:
APP_ENV=dev go run .curl localhost:8080/hello/orynThe console shows the request log, and the GET /hello/{name} span. Without APP_ENV=dev, telemetry is sent with OTLP to localhost:4318, or wherever OTEL_EXPORTER_OTLP_ENDPOINT points.
Test
Tests run the application in the test environment, where telemetry is exported in memory, and serve requests without a network:
package main
import ( "net/http" "net/http/httptest" "testing"
"github.com/stretchr/testify/assert" "go.opentelemetry.io/otel/sdk/trace/tracetest" "go.uber.org/fx"
"github.com/go-oryn/oryn/core" "github.com/go-oryn/oryn/core/coretest" "github.com/go-oryn/oryn/core/otel/oteltest/spantest" "github.com/go-oryn/oryn/httpserver"
"example.com/hello/configs")
func TestHello(t *testing.T) { t.Parallel()
var ( server *http.Server spans *tracetest.InMemoryExporter )
app := core.New(core.WithConfigFS(configs.FS), httpserver.Module, Module) coretest.Start(t, app, fx.Populate(&server, &spans))
recorder := httptest.NewRecorder() server.Handler.ServeHTTP(recorder, httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/hello/oryn", nil))
assert.Equal(t, "hello oryn", recorder.Body.String()) spantest.HasSpan(t, spans, spantest.WithName("GET /hello/{name}"))}go test ./...Next steps
- Demo application: a complete application, serving one tasks module over HTTP, gRPC and MCP, with a database and its telemetry.
- Application: how an oryn application is assembled.
- HTTP server, gRPC server, MCP server: the servers of your APIs.
- OpenTelemetry: exporters, sampling and the
OTEL_*variables.
