Reference implementation
This commit is contained in:
124
main.go
124
main.go
@ -6,68 +6,132 @@
|
||||
// endpoint.
|
||||
//
|
||||
// Configuration and logger stored in context
|
||||
// Reference implementation of the provided packages
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"gitea.libretechconsulting.com/rmcguire/go-http-server-with-otel/pkg/config"
|
||||
"gitea.libretechconsulting.com/rmcguire/go-http-server-with-otel/pkg/logging"
|
||||
"gitea.libretechconsulting.com/rmcguire/go-http-server-with-otel/pkg/observability"
|
||||
"gitea.libretechconsulting.com/rmcguire/go-http-server-with-otel/pkg/otel"
|
||||
"gitea.libretechconsulting.com/rmcguire/go-http-server-with-otel/pkg/srv"
|
||||
)
|
||||
|
||||
var (
|
||||
cfg *config.AppConfig
|
||||
l *zerolog.Logger
|
||||
tracer trace.Tracer
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx, cncl := signal.NotifyContext(context.Background(), os.Interrupt, unix.SIGTERM)
|
||||
defer cncl()
|
||||
|
||||
// Set up app config and logging
|
||||
ctx, err := config.LoadConfig(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
shutdownFuncs := make([]func(context.Context) error, 0)
|
||||
|
||||
cfg := config.MustFromCtx(ctx)
|
||||
ctx = logging.MustInitLogging(ctx)
|
||||
l := zerolog.Ctx(ctx)
|
||||
// Load configuration and setup logging
|
||||
ctx = setupConfigAndLogging(ctx)
|
||||
|
||||
// Set up OTEL
|
||||
opts := make([]observability.Option, 0)
|
||||
if cfg.Logging.Level == "trace" {
|
||||
opts = append(opts, observability.EnableStdoutExporter)
|
||||
ctx, otelShutdown := otel.Init(ctx)
|
||||
shutdownFuncs = append(shutdownFuncs, otelShutdown)
|
||||
tracer = otel.MustTracerFromCtx(ctx)
|
||||
|
||||
// Start App
|
||||
ctx, initSpan := tracer.Start(ctx, "init")
|
||||
|
||||
// Start HTTP Server
|
||||
dummyFuncs := []srv.HTTPFunc{
|
||||
{
|
||||
Path: "/dummy",
|
||||
HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("hello world"))
|
||||
},
|
||||
},
|
||||
}
|
||||
shutdown := observability.Init(ctx, opts...)
|
||||
defer func() {
|
||||
shutdownCtx, cncl := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cncl()
|
||||
shutdown(shutdownCtx)
|
||||
}()
|
||||
httpShutdown, httpDone := srv.MustInitHTTPServer(ctx, dummyFuncs...)
|
||||
shutdownFuncs = append(shutdownFuncs, httpShutdown)
|
||||
|
||||
// Begin Server init
|
||||
tracer := otel.Tracer(cfg.Name)
|
||||
_, initSpan := tracer.Start(ctx, "init")
|
||||
|
||||
// TODO: HTTP Server
|
||||
|
||||
// App Ready
|
||||
l.Trace().Any("config", *cfg).Send()
|
||||
// Startup Complete
|
||||
l.Info().
|
||||
Str("name", cfg.Name).
|
||||
Str("version", cfg.Version).
|
||||
Str("logLevel", cfg.Logging.Level).
|
||||
Msg("app initialized")
|
||||
|
||||
initSpan.SetStatus(codes.Ok, "")
|
||||
initSpan.End()
|
||||
|
||||
<-ctx.Done()
|
||||
// Wait for signal
|
||||
select {
|
||||
case <-httpDone:
|
||||
l.Warn().Msg("shutting down early on http server done")
|
||||
case <-ctx.Done():
|
||||
l.Warn().Str("reason", ctx.Err().Error()).
|
||||
Msg("shutting down on context done")
|
||||
}
|
||||
shutdown(shutdownFuncs...)
|
||||
}
|
||||
|
||||
func init() {}
|
||||
func shutdown(shutdownFuncs ...func(context.Context) error) {
|
||||
now := time.Now()
|
||||
|
||||
doneCtx, cncl := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer func() {
|
||||
if doneCtx.Err() == context.DeadlineExceeded {
|
||||
l.Err(doneCtx.Err()).
|
||||
Dur("shutdownTime", time.Since(now)).
|
||||
Msg("app shutdown aborted")
|
||||
} else {
|
||||
l.Info().
|
||||
Int("shutdownFuncsCalled", len(shutdownFuncs)).
|
||||
Dur("shutdownTime", time.Since(now)).
|
||||
Msg("app shutdown normally")
|
||||
}
|
||||
cncl()
|
||||
}()
|
||||
|
||||
doneCtx, span := tracer.Start(doneCtx, "shutdown")
|
||||
defer span.End()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(shutdownFuncs))
|
||||
|
||||
for _, f := range shutdownFuncs {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
err := f(doneCtx)
|
||||
if err != nil {
|
||||
span.SetStatus(codes.Error, "shutdown failed")
|
||||
span.RecordError(err)
|
||||
l.Err(err).Send()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func setupConfigAndLogging(ctx context.Context) context.Context {
|
||||
ctx, err := config.LoadConfig(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
cfg = config.MustFromCtx(ctx)
|
||||
ctx = logging.MustInitLogging(ctx)
|
||||
l = zerolog.Ctx(ctx)
|
||||
|
||||
l.Trace().Any("config", *cfg).Send()
|
||||
return ctx
|
||||
}
|
||||
|
Reference in New Issue
Block a user