ev/logger.go

257 lines
5.5 KiB
Go
Raw Normal View History

2022-08-12 15:53:16 -06:00
package main
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"log"
2022-08-12 16:27:54 -06:00
"net/http"
2022-08-12 15:53:16 -06:00
"os"
"runtime/debug"
"strings"
"time"
metricsExporter "github.com/logzio/go-metrics-sdk"
"github.com/logzio/logzio-go"
2022-08-12 16:27:54 -06:00
"go.uber.org/multierr"
2022-08-12 15:53:16 -06:00
"github.com/go-logr/stdr"
2022-08-12 16:27:54 -06:00
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
2022-08-12 15:53:16 -06:00
"go.opentelemetry.io/contrib/instrumentation/runtime"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/metric/global"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/sdk/metric/controller/basic"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
2022-08-12 16:27:54 -06:00
"go.opentelemetry.io/otel/trace"
2022-08-12 15:53:16 -06:00
)
2022-08-12 16:27:54 -06:00
var tracer trace.Tracer
func Init(ctx context.Context) func() error {
stop := []func() error{
2022-08-12 15:53:16 -06:00
initLogger(),
initMetrics(),
initTracing(ctx),
}
2022-08-12 16:27:54 -06:00
tracer = otel.Tracer(app_name)
2022-08-12 15:53:16 -06:00
reverse(stop)
2022-08-12 16:27:54 -06:00
return func() error {
log.Println("flushing logs...")
errs := make([]error, len(stop))
for i, fn := range stop {
errs[i] = fn()
2022-08-12 15:53:16 -06:00
}
2022-08-12 16:27:54 -06:00
log.Println("all stopped.")
return multierr.Combine(errs...)
}
2022-08-12 15:53:16 -06:00
}
type logzwriter struct {
pkg string
goversion string
hostname string
w io.Writer
}
func (l *logzwriter) Write(b []byte) (int, error) {
i := 0
for _, sp := range bytes.Split(b, []byte("\n")) {
msg := struct {
Message string `json:"message"`
Host string `json:"host"`
GoVersion string `json:"go_version"`
Package string `json:"pkg"`
App string `json:"app"`
}{
Message: strings.TrimSpace(string(sp)),
Host: l.hostname,
GoVersion: l.goversion,
Package: l.pkg,
App: app_name,
}
if msg.Message == "" || strings.HasPrefix(msg.Message, "#") {
continue
}
b, err := json.Marshal(msg)
if err != nil {
return 0, err
}
j, err := l.w.Write(b)
i += j
if err != nil {
return i, err
}
}
return i, nil
}
func initLogger() func() error {
log.SetPrefix("[" + app_name + "] ")
log.SetFlags(log.LstdFlags&^(log.Ldate|log.Ltime) | log.Lshortfile)
token := env("LOGZIO_LOG_TOKEN", "")
if token == "" {
return nil
}
l, err := logzio.New(
token,
// logzio.SetDebug(os.Stderr),
logzio.SetUrl(env("LOGZIO_LOG_URL", "https://listener.logz.io:8071")),
logzio.SetDrainDuration(time.Second*5),
logzio.SetTempDirectory(env("LOGZIO_DIR", os.TempDir())),
logzio.SetCheckDiskSpace(true),
logzio.SetDrainDiskThreshold(70),
)
if err != nil {
return nil
}
w := io.MultiWriter(os.Stderr, lzw(l))
log.SetOutput(w)
otel.SetLogger(stdr.New(log.Default()))
2022-08-12 16:27:54 -06:00
return func() error {
defer log.Println("logger stopped")
log.SetOutput(os.Stderr)
l.Stop()
return nil
}
2022-08-12 15:53:16 -06:00
}
func lzw(l io.Writer) io.Writer {
lz := &logzwriter{
w: l,
}
if info, ok := debug.ReadBuildInfo(); ok {
lz.goversion = info.GoVersion
lz.pkg = info.Path
}
if hostname, err := os.Hostname(); err == nil {
lz.hostname = hostname
}
return lz
}
func initMetrics() func() error {
goversion := ""
pkg := ""
host := ""
if info, ok := debug.ReadBuildInfo(); ok {
goversion = info.GoVersion
pkg = info.Path
}
if h, err := os.Hostname(); err == nil {
host = h
}
config := metricsExporter.Config{
LogzioMetricsListener: env("LOGZIO_METRIC_URL", "https://listener.logz.io:8053"),
LogzioMetricsToken: env("LOGZIO_METRIC_TOKEN", ""),
RemoteTimeout: 30 * time.Second,
PushInterval: 5 * time.Second,
}
if config.LogzioMetricsToken == "" {
return nil
}
// Use the `config` instance from last step.
cont, err := metricsExporter.InstallNewPipeline(
config,
basic.WithCollectPeriod(30*time.Second),
basic.WithResource(
resource.NewWithAttributes(
semconv.SchemaURL,
attribute.String("app", app_name),
attribute.String("host", host),
attribute.String("go_version", goversion),
attribute.String("pkg", pkg),
),
),
)
if err != nil {
return nil
}
global.SetMeterProvider(cont)
runtime.Start()
return func() error {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
2022-08-12 16:27:54 -06:00
defer log.Println("metrics stopped")
2022-08-12 15:53:16 -06:00
return cont.Stop(ctx)
}
}
func initTracing(ctx context.Context) func() error {
res, err := resource.New(ctx,
resource.WithAttributes(
semconv.ServiceNameKey.String("sour.is-ev"),
),
)
2022-08-12 16:27:54 -06:00
if err != nil {
log.Println(wrap(err, "failed to create trace resource"))
return nil
}
2022-08-12 15:53:16 -06:00
traceExporter, err := otlptracehttp.New(ctx,
otlptracehttp.WithInsecure(),
otlptracehttp.WithEndpoint("localhost:4318"),
)
2022-08-12 16:27:54 -06:00
if err != nil {
log.Println(wrap(err, "failed to create trace exporter"))
return nil
}
2022-08-12 15:53:16 -06:00
bsp := sdktrace.NewBatchSpanProcessor(traceExporter)
tracerProvider := sdktrace.NewTracerProvider(
sdktrace.WithSampler(sdktrace.AlwaysSample()),
sdktrace.WithResource(res),
sdktrace.WithSpanProcessor(bsp),
)
otel.SetTracerProvider(tracerProvider)
otel.SetTextMapPropagator(propagation.TraceContext{})
return func() error {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
2022-08-12 16:27:54 -06:00
defer log.Println("tracer stopped")
2022-08-12 15:53:16 -06:00
return wrap(tracerProvider.Shutdown(ctx), "failed to shutdown TracerProvider")
}
}
func wrap(err error, s string) error {
if err != nil {
return fmt.Errorf(s, err)
}
return nil
}
func reverse[T any](s []T) {
2022-08-12 16:27:54 -06:00
first, last := 0, len(s)-1
2022-08-12 15:53:16 -06:00
for first < last {
s[first], s[last] = s[last], s[first]
first++
last--
}
}
2022-08-12 16:27:54 -06:00
func htrace(h http.Handler, name string) http.Handler {
return otelhttp.NewHandler(h, name)
}