Skip to content

Commit

Permalink
Merge pull request #215 from LerianStudio/feature/MZ-624
Browse files Browse the repository at this point in the history
Feature/MZ-624
  • Loading branch information
MartinezAvellan authored Nov 8, 2024
2 parents 4569a6c + c06141f commit 50e8e59
Show file tree
Hide file tree
Showing 37 changed files with 1,133 additions and 129 deletions.
215 changes: 215 additions & 0 deletions common/mopentelemetry/otel.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
package mopentelemetry

import (
"context"
"github.com/LerianStudio/midaz/common"
"github.com/LerianStudio/midaz/common/mlog"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/propagation"
sdklog "go.opentelemetry.io/otel/sdk/log"
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
sdkresource "go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
"go.opentelemetry.io/otel/trace"
"os"
"time"
)

type Telemetry struct {
LibraryName string
ServiceName string
ServiceVersion string
DeploymentEnv string
CollectorExporterEndpoint string
TracerProvider *sdktrace.TracerProvider
MetricProvider *sdkmetric.MeterProvider
Logger mlog.Logger
shutdown func()
}

// NewResource creates a new resource with default attributes.
func (tl *Telemetry) NewResource() (*sdkresource.Resource, error) {
r, err := sdkresource.Merge(
sdkresource.Default(),
sdkresource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceName(tl.ServiceName),
semconv.ServiceVersion(tl.ServiceVersion),
semconv.DeploymentEnvironment(tl.DeploymentEnv)),
)
if err != nil {
return nil, err
}

return r, nil
}

// NewLoggerExporter creates a new logger exporter that writes to stdout.
func (tl *Telemetry) NewLoggerExporter(ctx context.Context) (*otlploggrpc.Exporter, error) {
exporter, err := otlploggrpc.New(ctx, otlploggrpc.WithEndpoint(os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT")), otlploggrpc.WithInsecure())
if err != nil {
return nil, err
}

return exporter, nil
}

// newMetricExporter creates a new metric exporter that writes to stdout.
func (tl *Telemetry) newMetricExporter(ctx context.Context) (*otlpmetricgrpc.Exporter, error) {
exp, err := otlpmetricgrpc.New(ctx, otlpmetricgrpc.WithEndpoint(tl.CollectorExporterEndpoint), otlpmetricgrpc.WithInsecure())
if err != nil {
return nil, err
}

return exp, nil
}

// newTracerExporter creates a new tracer exporter that writes to stdout.
func (tl *Telemetry) newTracerExporter(ctx context.Context) (*otlptrace.Exporter, error) {
exporter, err := otlptracegrpc.New(ctx, otlptracegrpc.WithEndpoint(tl.CollectorExporterEndpoint), otlptracegrpc.WithInsecure())
if err != nil {
return nil, err
}

return exporter, nil
}

// NewLoggerProvider creates a new logger provider with stdout exporter and default resource.
func (tl *Telemetry) NewLoggerProvider(rsc *sdkresource.Resource, exp *otlploggrpc.Exporter) *sdklog.LoggerProvider {
bp := sdklog.NewBatchProcessor(exp)
lp := sdklog.NewLoggerProvider(sdklog.WithResource(rsc), sdklog.WithProcessor(bp))

return lp
}

// newMeterProvider creates a new meter provider with stdout exporter and default resource.
func (tl *Telemetry) newMeterProvider(res *sdkresource.Resource, exp *otlpmetricgrpc.Exporter) *sdkmetric.MeterProvider {
mp := sdkmetric.NewMeterProvider(
sdkmetric.WithResource(res),
sdkmetric.WithReader(sdkmetric.NewPeriodicReader(exp,
// TODO: (REMOVE THIS) Default is 1m. Set to 5s for development purposes.
sdkmetric.WithInterval(5*time.Second))),
)

return mp
}

// newTracerProvider creates a new tracer provider with stdout exporter and default resource.
func (tl *Telemetry) newTracerProvider(rsc *sdkresource.Resource, exp *otlptrace.Exporter) *sdktrace.TracerProvider {
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exp),
sdktrace.WithResource(rsc),
)

return tp
}

func (tl *Telemetry) ShutdownTelemetry() {
tl.shutdown()
}

// InitializeTelemetry initializes the telemetry providers and sets them globally.
func (tl *Telemetry) InitializeTelemetry() *Telemetry {
ctx := context.Background()

r, err := tl.NewResource()
if err != nil {
tl.Logger.Fatalf("can't initialize resource: %v", err)
}

tExp, err := tl.newTracerExporter(ctx)
if err != nil {
tl.Logger.Fatalf("can't initialize tracer exporter: %v", err)
}

mExp, err := tl.newMetricExporter(ctx)
if err != nil {
tl.Logger.Fatalf("can't initialize metric exporter: %v", err)
}

mp := tl.newMeterProvider(r, mExp)
otel.SetMeterProvider(mp)
tl.MetricProvider = mp

tp := tl.newTracerProvider(r, tExp)
otel.SetTracerProvider(tp)
tl.TracerProvider = tp

tl.shutdown = func() {
err := tExp.Shutdown(ctx)
if err != nil {
tl.Logger.Fatalf("can't shutdown tracer exporter: %v", err)
}

err = mExp.Shutdown(ctx)
if err != nil {
tl.Logger.Fatalf("can't shutdown metric exporter: %v", err)
}

err = mp.Shutdown(ctx)
if err != nil {
tl.Logger.Fatalf("can't shutdown metric provider: %v", err)
}

err = tp.Shutdown(ctx)
if err != nil {
tl.Logger.Fatalf("can't shutdown tracer provider: %v", err)
}
}

otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))

return &Telemetry{
LibraryName: tl.LibraryName,
TracerProvider: tp,
MetricProvider: mp,
shutdown: tl.shutdown,
}
}

// NewTracerFromContext returns a new tracer from the context.
//
//nolint:ireturn
func NewTracerFromContext(ctx context.Context) trace.Tracer {
if tracer := ctx.Value(tracerContextKey("tracer")); tracer != nil {
if l, ok := tracer.(trace.Tracer); ok {
return l
}
}

return otel.Tracer("default")
}

type tracerContextKey string

// ContextWithTracer returns a context within a trace.Tracer in "tracer" value.
func ContextWithTracer(ctx context.Context, tracer trace.Tracer) context.Context {
return context.WithValue(ctx, tracerContextKey("tracer"), tracer)
}

// SetSpanAttributesFromStruct converts a struct to a JSON string and sets it as an attribute on the span.
func SetSpanAttributesFromStruct(span *trace.Span, key string, valueStruct any) error {
vStr, err := common.StructToJSONString(valueStruct)
if err != nil {
return err
}

(*span).SetAttributes(attribute.KeyValue{
Key: attribute.Key(key),
Value: attribute.StringValue(vStr),
})

return nil
}

func HandleSpanError(span *trace.Span, message string, err error) {
(*span).SetStatus(codes.Error, message+": "+err.Error())
(*span).RecordError(err)
}
44 changes: 38 additions & 6 deletions common/mzap/injector.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package mzap

import (
"github.com/uptrace/opentelemetry-go-extra/otelzap"
"context"
"github.com/LerianStudio/midaz/common/mopentelemetry"
"go.opentelemetry.io/contrib/bridges/otelzap"
"go.opentelemetry.io/otel/log/global"
"log"
"os"

Expand All @@ -17,6 +20,8 @@ import (
func InitializeLogger() mlog.Logger {
var zapCfg zap.Config

ctx := context.Background()

if os.Getenv("ENV_NAME") == "production" {
zapCfg = zap.NewProductionConfig()
zapCfg.EncoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
Expand All @@ -38,19 +43,46 @@ func InitializeLogger() mlog.Logger {

zapCfg.DisableStacktrace = true

// AddCallerSkip(1) is used to skip the stack frame of the zap logger wrapper code and get the actual caller
logger, err := zapCfg.Build(zap.AddCallerSkip(1))
t := mopentelemetry.Telemetry{}

lExp, err := t.NewLoggerExporter(ctx)
if err != nil {
log.Fatalf("can't initialize logger exporter: %v", err)
}

r, err := t.NewResource()
if err != nil {
log.Fatalf("can't initialize logger resource: %v", err)
}

lp := t.NewLoggerProvider(r, lExp)
global.SetLoggerProvider(lp)

logger, err := zapCfg.Build(zap.AddCallerSkip(1), zap.WrapCore(func(core zapcore.Core) zapcore.Core {
return zapcore.NewTee(core, otelzap.NewCore(os.Getenv("OTEL_LIBRARY_NAME")))
}))
if err != nil {
log.Fatalf("can't initialize zap logger: %v", err)
}

tracingLogger := otelzap.New(logger)
sugarLogger := tracingLogger.Sugar()
sugarLogger := logger.Sugar()

sugarLogger.Infof("Log level is (%v)", zapCfg.Level)
sugarLogger.Infof("Logger is (%T) \n", sugarLogger)

return &ZapWithTraceLogger{
Logger: sugarLogger,
Logger: sugarLogger,
LoggerProvider: lp,
shutdown: func() {
err := lExp.Shutdown(ctx)
if err != nil {
sugarLogger.Fatalf("can't shutdown logger exporter: %v", err)
}

err = lp.Shutdown(ctx)
if err != nil {
sugarLogger.Fatalf("can't shutdown logger provider: %v", err)
}
},
}
}
Loading

0 comments on commit 50e8e59

Please sign in to comment.