Skip to content

Commit 877aae4

Browse files
authored
add gracefully shutdown in otel_setup.go (#315)
add gracefully shutdown in otel_setup.go
1 parent e9372bc commit 877aae4

File tree

5 files changed

+107
-24
lines changed

5 files changed

+107
-24
lines changed

pkg/data/default.json

+6
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@
2929
"OnEnter": "defer func(){ retVal0.otel_trace_context = contextPropagate(callergp.otel_trace_context); retVal0.otel_baggage_container = contextPropagate(callergp.otel_baggage_container); }()",
3030
"UseRaw": true
3131
},
32+
{
33+
"ImportPath": "runtime",
34+
"Function": "runExitHooks",
35+
"OnEnter": "if ExitHook != nil { ExitHook(); }",
36+
"UseRaw": true
37+
},
3238
{
3339
"ImportPath": "runtime",
3440
"FileName": "runtime_linker.go",

pkg/data/test_runtime.json

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
[
2+
{
3+
"ImportPath": "runtime",
4+
"FileName": "reorder_init.go",
5+
"Path": "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/rules/runtime"
6+
},
7+
{
8+
"ImportPath": "runtime",
9+
"Function":"doInit",
10+
"OnEnter": "reorderInitTasks(ts)",
11+
"UseRaw": true,
12+
"GoVersion": "[1.21.0,)"
13+
},
14+
{
15+
"ImportPath": "runtime",
16+
"StructType": "g",
17+
"FieldName": "otel_trace_context",
18+
"FieldType": "interface{}"
19+
},
20+
{
21+
"ImportPath": "runtime",
22+
"StructType": "g",
23+
"FieldName": "otel_baggage_container",
24+
"FieldType": "interface{}"
25+
},
26+
{
27+
"ImportPath": "runtime",
28+
"Function": "newproc1",
29+
"OnEnter": "defer func(){ retVal0.otel_trace_context = contextPropagate(callergp.otel_trace_context); retVal0.otel_baggage_container = contextPropagate(callergp.otel_baggage_container); }()",
30+
"UseRaw": true
31+
},
32+
{
33+
"ImportPath": "runtime",
34+
"Function": "runExitHooks",
35+
"OnEnter": "if ExitHook != nil { ExitHook(); }",
36+
"UseRaw": true
37+
},
38+
{
39+
"ImportPath": "runtime",
40+
"FileName": "runtime_linker.go",
41+
"Path": "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/rules/runtime"
42+
}
43+
]

pkg/otel_setup.go

+52-22
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
//go:build ignore
2+
13
// Copyright (c) 2024 Alibaba Group Holding Ltd.
24
//
35
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -26,14 +28,14 @@ import (
2628
"log"
2729
http2 "net/http"
2830
"os"
31+
"runtime"
2932
"strings"
3033

3134
"github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/core/meter"
3235
"github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/inst-api-semconv/instrumenter/http"
3336
"github.com/alibaba/opentelemetry-go-auto-instrumentation/test/verifier"
34-
"go.opentelemetry.io/contrib/instrumentation/runtime"
37+
otelruntime "go.opentelemetry.io/contrib/instrumentation/runtime"
3538
"go.opentelemetry.io/otel"
36-
_ "go.opentelemetry.io/otel"
3739
_ "go.opentelemetry.io/otel/baggage"
3840
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
3941
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp"
@@ -43,7 +45,6 @@ import (
4345
"go.opentelemetry.io/otel/propagation"
4446
"go.opentelemetry.io/otel/sdk/metric"
4547
"go.opentelemetry.io/otel/sdk/trace"
46-
_ "go.opentelemetry.io/otel/sdk/trace"
4748
)
4849

4950
// set the following environment variables based on https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables
@@ -57,7 +58,19 @@ const metrics_exporter = "OTEL_METRICS_EXPORTER"
5758
const prometheus_exporter_port = "OTEL_EXPORTER_PROMETHEUS_PORT"
5859
const default_prometheus_exporter_port = "9464"
5960

61+
var (
62+
spanExporter trace.SpanExporter
63+
traceProvider *trace.TracerProvider
64+
metricsProvider *metric.MeterProvider
65+
batchSpanProcessor trace.SpanProcessor
66+
)
67+
6068
func init() {
69+
ctx := context.Background()
70+
// graceful shutdown
71+
runtime.ExitHook = func() {
72+
gracefullyShutdown(ctx)
73+
}
6174
path, err := os.Executable()
6275
if err != nil {
6376
panic(err)
@@ -66,7 +79,7 @@ func init() {
6679
if strings.HasSuffix(path, exec_name) {
6780
return
6881
}
69-
if err = initOpenTelemetry(); err != nil {
82+
if err = initOpenTelemetry(ctx); err != nil {
7083
log.Fatalf("%s: %v", "Failed to initialize opentelemetry resource", err)
7184
}
7285
}
@@ -78,32 +91,27 @@ func newSpanProcessor(ctx context.Context) trace.SpanProcessor {
7891
simpleProcessor := trace.NewSimpleSpanProcessor(traceExporter)
7992
return simpleProcessor
8093
} else {
81-
var traceExporter trace.SpanExporter
8294
var err error
8395
if os.Getenv(report_protocol) == "grpc" || os.Getenv(trace_report_protocol) == "grpc" {
84-
traceExporter, err = otlptrace.New(ctx, otlptracegrpc.NewClient())
96+
spanExporter, err = otlptrace.New(ctx, otlptracegrpc.NewClient())
8597
if err != nil {
8698
log.Fatalf("%s: %v", "Failed to create the OpenTelemetry trace exporter", err)
8799
}
88100
} else {
89-
traceExporter, err = otlptrace.New(ctx, otlptracehttp.NewClient())
101+
spanExporter, err = otlptrace.New(ctx, otlptracehttp.NewClient())
90102
if err != nil {
91103
log.Fatalf("%s: %v", "Failed to create the OpenTelemetry trace exporter", err)
92104
}
93105
}
94-
batchSpanProcessor := trace.NewBatchSpanProcessor(traceExporter)
106+
batchSpanProcessor = trace.NewBatchSpanProcessor(spanExporter)
95107
return batchSpanProcessor
96108
}
97109
}
98110

99-
func initOpenTelemetry() error {
100-
ctx := context.Background()
101-
102-
var batchSpanProcessor trace.SpanProcessor
111+
func initOpenTelemetry(ctx context.Context) error {
103112

104113
batchSpanProcessor = newSpanProcessor(ctx)
105114

106-
var traceProvider *trace.TracerProvider
107115
if batchSpanProcessor != nil {
108116
traceProvider = trace.NewTracerProvider(
109117
trace.WithSpanProcessor(batchSpanProcessor))
@@ -118,10 +126,9 @@ func initOpenTelemetry() error {
118126

119127
func initMetrics() error {
120128
ctx := context.Background()
121-
var mp *metric.MeterProvider
122129
// TODO: abstract the if-else
123130
if verifier.IsInTest() {
124-
mp = metric.NewMeterProvider(
131+
metricsProvider = metric.NewMeterProvider(
125132
metric.WithReader(verifier.ManualReader),
126133
)
127134
} else {
@@ -130,7 +137,7 @@ func initMetrics() error {
130137
if err != nil {
131138
log.Fatalf("new otlp metric prometheus exporter failed: %v", err)
132139
}
133-
mp = metric.NewMeterProvider(
140+
metricsProvider = metric.NewMeterProvider(
134141
metric.WithReader(exporter),
135142
)
136143
go serveMetrics()
@@ -139,24 +146,24 @@ func initMetrics() error {
139146
if err != nil {
140147
log.Fatalf("new otlp metric grpc exporter failed: %v", err)
141148
}
142-
mp = metric.NewMeterProvider(
149+
metricsProvider = metric.NewMeterProvider(
143150
metric.WithReader(metric.NewPeriodicReader(exporter)),
144151
)
145152
} else {
146153
exporter, err := otlpmetrichttp.New(ctx)
147154
if err != nil {
148155
log.Fatalf("new otlp metric http exporter failed: %v", err)
149156
}
150-
mp = metric.NewMeterProvider(
157+
metricsProvider = metric.NewMeterProvider(
151158
metric.WithReader(metric.NewPeriodicReader(exporter)),
152159
)
153160
}
154161
}
155-
if mp == nil {
162+
if metricsProvider == nil {
156163
return errors.New("No MeterProvider is provided")
157164
}
158-
otel.SetMeterProvider(mp)
159-
m := mp.Meter("opentelemetry-global-meter")
165+
otel.SetMeterProvider(metricsProvider)
166+
m := metricsProvider.Meter("opentelemetry-global-meter")
160167
meter.SetMeter(m)
161168
// init http metrics
162169
http.InitHttpMetrics(m)
@@ -167,7 +174,7 @@ func initMetrics() error {
167174
// nacos experimental metrics
168175
experimental.InitNacosExperimentalMetrics(m)
169176
// DefaultMinimumReadMemStatsInterval is 15 second
170-
return runtime.Start(runtime.WithMeterProvider(mp))
177+
return otelruntime.Start(otelruntime.WithMeterProvider(metricsProvider))
171178
}
172179

173180
func serveMetrics() {
@@ -183,3 +190,26 @@ func serveMetrics() {
183190
return
184191
}
185192
}
193+
194+
func gracefullyShutdown(ctx context.Context) {
195+
if metricsProvider != nil {
196+
if err := metricsProvider.Shutdown(ctx); err != nil {
197+
log.Printf("%s: %v", "Failed to shutdown the OpenTelemetry metric provider", err)
198+
}
199+
}
200+
if traceProvider != nil {
201+
if err := traceProvider.Shutdown(ctx); err != nil {
202+
log.Printf("%s: %v", "Failed to shutdown the OpenTelemetry trace provider", err)
203+
}
204+
}
205+
if spanExporter != nil {
206+
if err := spanExporter.Shutdown(ctx); err != nil {
207+
log.Printf("%s: %v", "Failed to shutdown the OpenTelemetry span exporter", err)
208+
}
209+
}
210+
if batchSpanProcessor != nil {
211+
if err := batchSpanProcessor.Shutdown(ctx); err != nil {
212+
log.Printf("%s: %v", "Failed to shutdown the OpenTelemetry batch span processor", err)
213+
}
214+
}
215+
}

pkg/rules/runtime/runtime_linker.go

+4
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ import (
1818
_ "unsafe"
1919
)
2020

21+
// When the program exit, we should call the exit hook to shutdown gracefully.
22+
// See https://github.com/alibaba/opentelemetry-go-auto-instrumentation/blob/main/pkg/otel_setup.go
23+
var ExitHook func()
24+
2125
//go:linkname otel_get_trace_context_from_gls otel_get_trace_context_from_gls
2226
var otel_get_trace_context_from_gls = _otel_gls_get_trace_context_impl
2327

test/build_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ func TestBuildProject4(t *testing.T) {
5858
RunGoBuildFallible(t, "go", "build", "m1")
5959
RunSet(t, "-rule=../../pkg/data/test_error.json,../../pkg/data/test_fmt.json")
6060
RunGoBuild(t, "go", "build", "m1")
61-
RunSet(t, "-disabledefault=true", "-rule=../../pkg/data/test_error.json,../../pkg/data/test_fmt.json")
61+
RunSet(t, "-disabledefault=true", "-rule=../../pkg/data/test_error.json,../../pkg/data/test_fmt.json,../../pkg/data/test_runtime.json")
6262
RunGoBuild(t, "go", "build", "m1")
6363
RunSet(t, "-disabledefault=true", "-rule=../../pkg/data/default.json,../../pkg/data/test_fmt.json")
6464
RunGoBuild(t, "go", "build", "m1")
@@ -80,7 +80,7 @@ func TestBuildProject6(t *testing.T) {
8080
const AppName = "build"
8181
UseApp(AppName)
8282

83-
RunSet(t, "-disabledefault=true", "-rule=../../pkg/data/test_fmt.json", "-verbose")
83+
RunSet(t, "-disabledefault=true", "-rule=../../pkg/data/test_fmt.json,../../pkg/data/test_runtime.json", "-verbose")
8484
RunGoBuild(t, "go", "build", "m1")
8585
// only test_fmt.json should be available because -disabledefault is set
8686
ExpectPreprocessContains(t, util.DebugLogFile, "fmt")

0 commit comments

Comments
 (0)