-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
176 lines (151 loc) · 5.11 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
package main
import (
"context"
"fmt"
"os"
"os/signal"
"strconv"
"syscall"
"time"
r "github.com/otaviokr/twitch-bot-config/redis"
"github.com/fsnotify/fsnotify"
log "github.com/sirupsen/logrus"
"github.com/spf13/viper"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/jaeger"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
"go.opentelemetry.io/otel/trace"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
log.Warn("reading configuration file")
readConfig(ctx)
tp, err := jaegerTracerProvider(viper.GetString("jaeger.uri"))
if err != nil {
log.Fatalf("failed to initialize a tracer provider for Jaeger: %v", err)
}
defer func(ctx context.Context) {
ctx, cancel = context.WithTimeout(ctx, 5 * time.Second)
defer cancel()
if err := tp.Shutdown(ctx); err != nil {
log.Fatalf("failed to shutdown: %v", err)
}
}(ctx)
// Set Global Tracer Provider and a Global Meter Provider
otel.SetTracerProvider(tp)
propagator := propagation.NewCompositeTextMapPropagator(propagation.Baggage{}, propagation.TraceContext{})
otel.SetTextMapPropagator(propagator)
rawLevel := viper.GetString("log.level")
logLevel, err := log.ParseLevel(rawLevel)
if err != nil {
log.WithFields(
log.Fields{
"err": err.Error(),
"raw_level": rawLevel,
}).Fatal("failed to read configuration file for LOG.LEVEL")
}
log.SetLevel(logLevel)
log.SetFormatter(&log.JSONFormatter{})
log.SetOutput(os.Stdout)
SetupCloseHandler(ctx)
// Infinite loop until program is terminated.
for {
time.Sleep(2 * time.Second)
}
}
// ReadConfig will parse the properties file.
func readConfig(ctx context.Context) {
tracer := otel.Tracer("main/readConfig")
var span trace.Span
newCtx, span := tracer.Start(ctx, "readConfig", trace.WithAttributes(attribute.String("hello", "world")))
defer span.End()
span.AddEvent("Defining config file(s)")
viper.AddConfigPath("./config")
viper.AddConfigPath(".")
viper.SetConfigName("twitch-bot")
viper.SetConfigType("yaml")
span.AddEvent("Setting up live reconfig")
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
innerCtx, onChangeSpan := tracer.Start(newCtx, "onChangeConfig")
defer onChangeSpan.End()
sendToRedis(innerCtx)
log.WithFields(
log.Fields{
"file": e.Name,
"event": e.Op.String(),
}).Info("configuration file changed and settings have been refreshed")
})
span.AddEvent("Read configuration from file(s)")
err := viper.ReadInConfig()
if err != nil {
log.WithFields(
log.Fields{
"error": err.Error(),
}).Fatal("failed to process config file")
}
sendToRedis(newCtx)
span.AddEvent("First upload of configuration values to Redis")
}
// SendToRedis will load the contents of the configuration file into the redis instance.
func sendToRedis(ctx context.Context) {
tracer := otel.Tracer("main/readConfig")
var span trace.Span
newCtx, span := tracer.Start(ctx, "sendToRedis")
defer span.End()
port, err := strconv.Atoi(os.Getenv("REDIS_PORT"))
if err != nil {
fmt.Printf("Failed to convert port number: %s . Setting default %d\n", os.Getenv("REDIS_PORT"), r.RedisDefaultPort)
port = r.RedisDefaultPort
}
db, err := strconv.Atoi(os.Getenv("REDIS_DATABASE"))
if err != nil {
fmt.Printf("Failed to convert database ID: %s . Setting default %d\n", os.Getenv("REDIS_DATABASE"), r.RedisDefaultDatabase)
db = r.RedisDefaultPort
}
client := r.NewClient(os.Getenv("REDIS_URI"), port, os.Getenv("REDIS_PASSWORD"), db)
client.LoadFromFile(newCtx)
}
// tracerProvider returns an OpenTelemetry TracerProvider configured to use
// the Jaeger exporter that will send spans to the provided url. The returned
// TracerProvider will also use a Resource configured with all the information
// about the application.
func jaegerTracerProvider(url string) (*sdktrace.TracerProvider, error) {
// Create the Jaeger exporter
exp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(url)))
if err != nil {
return nil, err
}
tp := sdktrace.NewTracerProvider(
// Always be sure to batch in production.
sdktrace.WithBatcher(exp),
// Record information about this application in an Resource.
sdktrace.WithResource(resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceNameKey.String(viper.GetString("jaeger.service")),
attribute.String("environment", viper.GetString("jaeger.environment")),
attribute.Int64("ID", viper.GetInt64("jaeger.id")),
)),
)
return tp, nil
}
// SetupCloseHandler creates a listener on a new goroutine which will notify the program if it receives an interrupt from the OS.
// We then handle this by calling our clean up procedure and exiting the program.
func SetupCloseHandler(ctx context.Context) {
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
<-c
tracer := otel.Tracer("main/readConfig")
var span trace.Span
_, span = tracer.Start(ctx, "setupCloseHandler")
defer span.End()
span.AddEvent("SIGTERM received... closing program")
os.Exit(0)
}()
}