This topic describes how to use the OpenTelemetry Golang software development kit (SDK) to ingest trace data from a Golang application into Simple Log Service.
Simple Log Service provides a commercial, proprietary probe for the Go language. It offers non-intrusive instrumentation, additional features, and greater stability. For more information, see Start monitoring Go applications.
Prerequisites
- A Trace instance has been created. For more information, see Create a Trace instance.
A Golang development environment, version 1.13 or later, has been installed.
Ingestion flow
Initialize the OpenTelemetry provider.
Determine whether the conditions for semi-automatic ingestion are met.
If the conditions are met, use the semi-automatic method to ingest trace data.
For scenarios not covered by the semi-automatic method, use the manual method.
If the conditions are not met, use the manual method to ingest trace data.
Step 1: Initialize the OpenTelemetry provider
To simplify using the OpenTelemetry provider, Simple Log Service provides the SLS Provider, which helps you quickly build the required dependencies and upload them to Simple Log Service.
Initialize the OpenTelemetry provider before you create traces or register metrics.
You can initialize the OpenTelemetry provider by running code or by configuring environment variables:
Initialize by running code.
Add dependencies.
module opentelemetry-golang-sample go 1.13 require ( github.com/aliyun-sls/opentelemetry-go-provider-sls v0.10.0 go.opentelemetry.io/otel v1.16.0 go.opentelemetry.io/otel/metric v1.16.0 go.opentelemetry.io/otel/trace v1.16.0 )Configure the initialization code.
Replace the variables in the following code to meet your requirements. For descriptions of the variables, see Variable description.
package main import ( "github.com/aliyun-sls/opentelemetry-go-provider-sls/provider" ) func main() { slsConfig, err := provider.NewConfig(provider.WithServiceName("${service}"), provider.WithServiceNamespace("${service.namespace}"), provider.WithServiceVersion("${version}"), provider.WithTraceExporterEndpoint("${endpoint}"), provider.WithMetricExporterEndpoint("${endpoint}"), provider.WithSLSConfig("${project}", "${instance}", "${access-key-id}", "${access-key-secret}")) // Use panic() to exit the program if initialization fails. You can also use other error handling methods. if err != nil { panic(err) } if err := provider.Start(slsConfig); err != nil { panic(err) } defer provider.Shutdown(slsConfig) // Add your business logic code. ... }Table 1. Variable description
Variable
Description
Example
${service}
The service name. Set this parameter based on your scenario.
payment
${service.namespace}
The namespace to which the service belongs.
order
${version}
The service version number. Use the va.b.c format.
v0.1.2
${endpoint}
The endpoint of the Simple Log Service project. The format is ${project}.${region-endpoint}:Port. The parameters are described as follows:
${project}: The name of the Simple Log Service project.
${region-endpoint}: The endpoint of the region where the Simple Log Service project resides. Public network, classic network, and VPC endpoints are supported. For more information, see Endpoints.
Port: The network port. The value is fixed at 10010.
NoteIf you set this parameter to stdout, for example, provider.WithTraceExporterEndpoint("stdout"), the data is printed to the standard output.
If you leave this parameter empty, trace data is not uploaded to Simple Log Service.
test-project.cn-hangzhou.log.aliyuncs.com:10010
${project}
The name of the Simple Log Service project.
test-project
${instance}
The ID of the Trace instance. For more information, see Create a Trace instance.
test-traces
${access-key-id}
The AccessKey ID of your Alibaba Cloud account.
Use the AccessKey pair (AccessKey ID and AccessKey secret) of a Resource Access Management (RAM) user who has only the write permissions on the Simple Log Service project. For more information about how to grant a RAM user the permissions to write data to a specified project, see Authorization. For more information about how to obtain an AccessKey pair, see AccessKey pair.
None
${access-key-secret}
The AccessKey secret of your Alibaba Cloud account.
Use the AccessKey pair of a RAM user who has only the write permissions on the Simple Log Service project.
None
Initialize by configuring environment variables.
Configuration method
Environment variable
Required
Description
Default value
WithServiceName
SLS_OTEL_SERVICE_NAME
Yes
The service name. Set this parameter based on your scenario.
None
WithServiceNamespace
SLS_OTEL_SERVICE-NAMESPACE
No
The namespace to which the service belongs.
order
WithServiceVersion
SLS_OTEL_SERVICE_VERSION
Yes
The service version number. Use the va.b.c format.
v0.1.0
WithSLSConfig
SLS_OTEL_PROJECT, SLS_OTEL_INSTANCE_ID, SLS_OTEL_ACCESS_KEY_ID, SLS_OTEL_ACCESS_KEY_SECRET
No
The Simple Log Service resource information, including the project name, Trace instance name, and the AccessKey ID and AccessKey secret of a user who has write-only permissions on the project. For more information about how to grant a RAM user the permissions to write data to a specified project, see Authorization. For more information about how to obtain an AccessKey pair, see AccessKey pair.
None
WithTraceExporterEndpoint
SLS_OTEL_TRACE_ENDPOINT
No
The endpoint of the Simple Log Service project. The format is ${project}.${region-endpoint}:Port. The parameters are described as follows:
${project}: The name of the Simple Log Service project.
${region-endpoint}: The endpoint of the region where the Simple Log Service project resides. Internal network and public network access are supported. For more information, see Endpoints.
Port: The network port. The value is fixed at 10010.
NoteIf you set this parameter to stdout, the data is printed to the standard output.
If you leave this parameter empty, trace data is not uploaded to Simple Log Service.
stdout
WithTraceExporterInsecure
SLS_OTEL_TRACE_INSECURE
No
Specifies whether to use an insecure method for transmission.
true: Use an insecure method for transmission.
false: Use a secure method for transmission.
NoteIf sending data directly to Simple Log Service, set this parameter to false.
false
WithMetricExporterEndpoint
SLS_OTEL_METRIC_ENDPOINT
No
The endpoint of the Simple Log Service project. The format is ${project}.${region-endpoint}:Port. The parameters are described as follows:
${project}: The name of the Simple Log Service project.
${region-endpoint}: The endpoint of the region where the Simple Log Service project resides. Internal network and public network access are supported. For more information, see Endpoints.
Port: The network port. The value is fixed at 10010.
NoteIf you set this parameter to stdout, the data is printed to the standard output.
If you leave this parameter empty, metric data is not uploaded to Simple Log Service.
stdout
WithMetricExporterInsecure
SLS_OTEL_METRIC_INSECURE
No
Specifies whether to use an insecure method for transmission.
true: Use an insecure method for transmission.
false: Use a secure method for transmission.
NoteIf sending data directly to Simple Log Service, set this parameter to false.
false
WithResourceAttributes
None
No
Configure additional tags, such as the environment and zone.
None
WithResource
OTEL_RESOURCE_ATTRIBUTES
No
Configure additional tags, such as the environment and zone. The format is key1=value1,key2=value2.
None
WithMetricReportingPeriod
SLS_OTEL_METRIC_EXPORT_PERIOD
No
The interval at which metrics are exported. Set a value from 15s to 60s.
30s
WithErrorHandler
None
No
The error handling function. This function is called when an internal error occurs in the SDK. It has the same effect as WithErrorHandlerFunc.
None
WithErrorHandlerFunc
None
No
The error handling function.
None
None
SLS_OTEL_ATTRIBUTES_ENV_KEYS
No
Configure additional tags, such as the environment and zone. This is similar to OTEL_RESOURCE_ATTRIBUTES. The difference is that for the attribute keys defined by SLS_OTEL_ATTRIBUTES_ENV_KEYS, their actual values are read from the corresponding environment variables.
This is often used in Kubernetes scenarios to fill template values into specific environment variables. The format is env-key-1|env-key-2|env-key-3.
None
Step 2: Ingest data
(Recommended) Semi-automatic ingestion
OpenTelemetry provides automatic instrumentation for many common libraries. If your application uses these libraries, you can use the corresponding automatic instrumentation solutions to ingest data. For a list of supported libraries, see Golang automatic instrumentation solutions.
net/http ingestion
The following example is based on go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.37.0. For more information, see otel-http-example.
Replace the variables in the following code to meet your requirements. For descriptions of the variables, see Variable description.
package main import ( "fmt" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric/global" "io" "net/http" "time" "github.com/aliyun-sls/opentelemetry-go-provider-sls/provider" "go.opentelemetry.io/otel/trace" ) func main() { slsConfig, err := provider.NewConfig(provider.WithServiceName("${service}"), provider.WithServiceNamespace("${service.namespace}"), provider.WithServiceVersion("${version}"), provider.WithTraceExporterEndpoint("${endpoint}"), provider.WithMetricExporterEndpoint("${endpoint}"), provider.WithSLSConfig("${project}", "${instance}", "${access-key-id}", "${access-key-secret}")) // Use panic() to exit the program if initialization fails. You can also use other error handling methods. if err != nil { panic(err) } if err := provider.Start(slsConfig); err != nil { panic(err) } defer provider.Shutdown(slsConfig) // To analyze metric data in your application, you can register the relevant metrics. labels := []attribute.KeyValue{ attribute.String("label1", "value1"), } meter := global.Meter("aliyun.sls") sayDavidCount, _ := meter.Int64Counter("say_david_count") helloHandler := func(w http.ResponseWriter, req *http.Request) { if time.Now().Unix()%10 == 0 { _, _ = io.WriteString(w, "Hello, world!\n") } else { // To record events, you can get the Span from the Context and add an Event. ctx := req.Context() span := trace.SpanFromContext(ctx) span.AddEvent("say : Hello, I am david", trace.WithAttributes(attribute.KeyValue{ Key: "label-key-1", Value: attribute.StringValue("label-value-1"), })) _, _ = io.WriteString(w, "Hello, I am david!\n") sayDavidCount.Add(req.Context(), 1, labels...) } } // To use the automatic injection of otel net/http, just wrap http.Handler with otelhttp.NewHandler. otelHandler := otelhttp.NewHandler(http.HandlerFunc(helloHandler), "Hello") http.Handle("/hello", otelHandler) fmt.Println("Now listen port 8080, you can visit 127.0.0.1:8080/hello .") err = http.ListenAndServe(":8080", nil) if err != nil { panic(err) } }mux ingestion
The following example is based on go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.37.0. The API may change in later versions. For the latest example, see otel-mux-example.
Replace the variables in the following code to meet your requirements. For descriptions of the variables, see Variable description.
package main import ( "context" "fmt" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric/global" "net/http" "github.com/aliyun-sls/opentelemetry-go-provider-sls/provider" "github.com/gorilla/mux" "go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux" "go.opentelemetry.io/otel/trace" ) func main() { slsConfig, err := provider.NewConfig(provider.WithServiceName("${service}"), provider.WithServiceNamespace("${service.namespace}"), provider.WithServiceVersion("${version}"), provider.WithTraceExporterEndpoint("${endpoint}"), provider.WithMetricExporterEndpoint("${endpoint}"), provider.WithSLSConfig("${project}", "${instance}", "${access-key-id}", "${access-key-secret}")) // Use panic() to exit the program if initialization fails. You can also use other error handling methods. if err != nil { panic(err) } if err := provider.Start(slsConfig); err != nil { panic(err) } defer provider.Shutdown(slsConfig) // To analyze metric data in your application, you can register the relevant metrics. labels := []attribute.KeyValue{ attribute.String("label1", "value1"), } meter := global.Meter("aliyun.sls") callUsersCount, _ := meter.Int64Counter("call_users_count") r := mux.NewRouter() r.Use(otelmux.Middleware("my-server")) r.HandleFunc("/users/{id:[0-9]+}", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) id := vars["id"] callUsersCount.Add(r.Context(), 1, labels...) name := getUser(r.Context(), id) reply := fmt.Sprintf("user %s (id %s)\n", name, id) _, _ = w.Write(([]byte)(reply)) })) http.Handle("/", r) fmt.Println("Now listen port 8080, you can visit 127.0.0.1:8080/users/xxx .") _ = http.ListenAndServe(":8080", nil) } func getUser(ctx context.Context, id string) string { if id == "123" { return "otelmux tester" } // To record events, you can get the Span from the Context and add an Event. span := trace.SpanFromContext(ctx) span.AddEvent("unknown user id : "+id, trace.WithAttributes(attribute.KeyValue{ Key: "label-key-1", Value: attribute.StringValue("label-value-1"), })) return "unknown" }
Manual ingestion
Replace the variables in the following code to meet your requirements. For descriptions of the variables, see Variable description.
// Copyright The AliyunSLS Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import ( "context" "errors" "fmt" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric/global" "go.opentelemetry.io/otel/metric/instrument" "math/rand" "time" "github.com/aliyun-sls/opentelemetry-go-provider-sls/provider" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/trace" ) func main() { slsConfig, err := provider.NewConfig(provider.WithServiceName("payment"), provider.WithServiceVersion("v0.1.0"), provider.WithTraceExporterEndpoint("stdout"), provider.WithMetricExporterEndpoint("stdout"), provider.WithSLSConfig("test-project", "test-otel", "access-key-id", "access-key-secret")) // Use panic() to exit the program if initialization fails. You can also use other error handling methods. if err != nil { panic(err) } if err := provider.Start(slsConfig); err != nil { panic(err) } defer provider.Shutdown(slsConfig) mockTrace() mockMetrics() } func mockMetrics() { // Add tag information. labels := []attribute.KeyValue{ attribute.String("label1", "value1"), } meter := global.Meter("ex.com/basic") meter.Float64ObservableCounter("randval", instrument.WithFloat64Callback(func(ctx context.Context, observer instrument.Float64Observer) error { observer.Observe(rand.Float64(), labels...) return nil })) temperature, _ := meter.Float64Counter("temperature") interrupts, _ := meter.Int64Counter("interrupts") ctx := context.Background() for { temperature.Add(ctx, 100+10*rand.NormFloat64(), labels...) interrupts.Add(ctx, int64(rand.Intn(100)), labels...) time.Sleep(time.Second * time.Duration(rand.Intn(10))) } } func mockTrace() { tracer := otel.Tracer("ex.com/basic") ctx0 := context.Background() ctx1, finish1 := tracer.Start(ctx0, "foo") defer finish1.End() ctx2, finish2 := tracer.Start(ctx1, "bar") defer finish2.End() ctx3, finish3 := tracer.Start(ctx2, "baz") defer finish3.End() ctx := ctx3 getSpan(ctx) addAttribute(ctx) addEvent(ctx) recordException(ctx) createChild(ctx, tracer) } // example of getting the current span // Get the current Span. func getSpan(ctx context.Context) { span := trace.SpanFromContext(ctx) fmt.Printf("current span: %v\n", span) } // example of adding an attribute to a span // Add an attribute to the Span. func addAttribute(ctx context.Context) { span := trace.SpanFromContext(ctx) span.SetAttributes(attribute.KeyValue{ Key: "label-key-1", Value: attribute.StringValue("label-value-1")}) } // example of adding an event to a span // Add an event to the Span. func addEvent(ctx context.Context) { span := trace.SpanFromContext(ctx) span.AddEvent("event1", trace.WithAttributes( attribute.String("event-attr1", "event-string1"), attribute.Int64("event-attr2", 10))) } // example of recording an exception // Record the Span result and error message. func recordException(ctx context.Context) { span := trace.SpanFromContext(ctx) span.RecordError(errors.New("exception has occurred")) span.SetStatus(codes.Error, "internal error") } // example of creating a child span // Create a child Span. func createChild(ctx context.Context, tracer trace.Tracer) { // span := trace.SpanFromContext(ctx) _, childSpan := tracer.Start(ctx, "child") defer childSpan.End() fmt.Printf("child span: %v\n", childSpan) }