Codevault initial commit
This commit is contained in:
139
internal/observability/logging/configure.go
Normal file
139
internal/observability/logging/configure.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/contrib/bridges/otelzap"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp"
|
||||
"go.opentelemetry.io/otel/log/global"
|
||||
"go.opentelemetry.io/otel/sdk/log"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
|
||||
"github.com/kanootoko/codevault/internal"
|
||||
"github.com/kanootoko/codevault/internal/config"
|
||||
"github.com/kanootoko/codevault/internal/observability"
|
||||
)
|
||||
|
||||
func ConfigureLogger(ctx context.Context, cfg config.LoggingConfig, isProductionEnv bool) (logger *zap.Logger, closeFunc func(), err error) {
|
||||
stderrLevel, err := cfg.Stderr.ToZapLevel()
|
||||
if err != nil && !errors.Is(err, config.ErrLoggingDisabled) {
|
||||
return nil, nil, fmt.Errorf("error on getting stderr logging level: %w", err)
|
||||
}
|
||||
fileLoggers := make(map[string]zapcore.Level)
|
||||
for level, file := range cfg.Files {
|
||||
l, err := level.ToZapLevel()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error on getting file logging level: %w", err)
|
||||
}
|
||||
fileLoggers[file] = l
|
||||
}
|
||||
var stderrEncoder zapcore.Encoder
|
||||
var fileEncoder zapcore.Encoder
|
||||
{
|
||||
var fileLoggerCfg zapcore.EncoderConfig
|
||||
var stderrLoggerCfg zapcore.EncoderConfig
|
||||
if isProductionEnv {
|
||||
fileLoggerCfg = zap.NewProductionEncoderConfig()
|
||||
stderrLoggerCfg = zap.NewProductionEncoderConfig()
|
||||
} else {
|
||||
fileLoggerCfg = zap.NewDevelopmentEncoderConfig()
|
||||
stderrLoggerCfg = zap.NewDevelopmentEncoderConfig()
|
||||
}
|
||||
fileLoggerCfg.TimeKey = "timestamp"
|
||||
fileLoggerCfg.EncodeTime = zapcore.ISO8601TimeEncoder
|
||||
|
||||
stderrLoggerCfg.EncodeLevel = zapcore.CapitalColorLevelEncoder
|
||||
|
||||
stderrEncoder = zapcore.NewConsoleEncoder(stderrLoggerCfg)
|
||||
fileEncoder = zapcore.NewJSONEncoder(fileLoggerCfg)
|
||||
}
|
||||
|
||||
fileHandlers := make([]*os.File, 0, len(fileLoggers))
|
||||
zapWriters := make([]zapcore.Core, 0, len(fileLoggers)+1)
|
||||
for filename, level := range fileLoggers {
|
||||
fileHandler, err := os.Create(filename)
|
||||
if err != nil {
|
||||
for _, file := range fileHandlers[:len(fileHandlers)-1] {
|
||||
_ = file.Close()
|
||||
}
|
||||
return nil, nil, fmt.Errorf("error on log file creation: %w", err)
|
||||
}
|
||||
fileHandlers = append(fileHandlers, fileHandler)
|
||||
fileSync := zapcore.AddSync(fileHandler)
|
||||
zapWriters = append(zapWriters, zapcore.NewCore(fileEncoder, fileSync, zap.NewAtomicLevelAt(level)))
|
||||
}
|
||||
|
||||
stderrSync := zapcore.AddSync(os.Stderr)
|
||||
if stderrLevel != zapcore.InvalidLevel {
|
||||
zapWriters = append(zapWriters, zapcore.NewCore(stderrEncoder, stderrSync, zap.NewAtomicLevelAt(stderrLevel)))
|
||||
}
|
||||
|
||||
var otlpCloseFunc func()
|
||||
if cfg.Exporter.Level != "" {
|
||||
var otlpWriter zapcore.Core
|
||||
var err error
|
||||
otlpWriter, otlpCloseFunc, err = configureOtelLogging(ctx, cfg.Exporter)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("cannot configure OpenTelemetry log exporter: %w", err)
|
||||
}
|
||||
zapWriters = append(zapWriters, otlpWriter)
|
||||
}
|
||||
|
||||
core := zapcore.NewTee(
|
||||
zapWriters...,
|
||||
)
|
||||
|
||||
logger = zap.New(core)
|
||||
|
||||
closeFunc = func() {
|
||||
_ = logger.Sync()
|
||||
for _, file := range fileHandlers {
|
||||
_ = file.Close()
|
||||
}
|
||||
if otlpCloseFunc != nil {
|
||||
otlpCloseFunc()
|
||||
}
|
||||
}
|
||||
|
||||
logger.Info("configured logging", zap.Any("config", cfg))
|
||||
return
|
||||
}
|
||||
|
||||
func configureOtelLogging(ctx context.Context, cfg config.LogsExporterConfig) (zapcore.Core, func(), error) {
|
||||
var exporter log.Exporter
|
||||
var err error
|
||||
if cfg.Transport == config.HTTPTransportType {
|
||||
exporter, err = otlploghttp.New(ctx, otlploghttp.WithEndpointURL(cfg.Endpoint))
|
||||
} else {
|
||||
exporter, err = otlploggrpc.New(ctx, otlploggrpc.WithEndpointURL(cfg.Endpoint))
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("cannot create OpenTelemetry logs exporter: %w", err)
|
||||
}
|
||||
res, err := observability.GetOtelResource()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("cannot get OpenTelemetry resource: %w", err)
|
||||
}
|
||||
|
||||
provider := log.NewLoggerProvider(
|
||||
log.WithProcessor(log.NewBatchProcessor(exporter)),
|
||||
log.WithResource(res),
|
||||
)
|
||||
|
||||
closeFunc := func() {
|
||||
shutdownContext, cancel := context.WithTimeout(context.Background(), time.Millisecond*300)
|
||||
defer cancel()
|
||||
_ = provider.ForceFlush(shutdownContext)
|
||||
_ = provider.Shutdown(shutdownContext)
|
||||
<-shutdownContext.Done()
|
||||
}
|
||||
global.SetLoggerProvider(provider)
|
||||
|
||||
return otelzap.NewCore(internal.PackageName, otelzap.WithLoggerProvider(provider)), closeFunc, nil
|
||||
}
|
||||
Reference in New Issue
Block a user