This topic describes how to import trace data from Golang applications to Simple Log Service by using OpenTelemetry SDK for Golang.
Prerequisites
A trace instance is created. For more information, see Create a trace instance.
A Golang development environment is set up. The Golang version is 1.13 or later.
Procedure
Initialize an OpenTelemetry provider.
Check whether the conditions for importing data in semi-automatic mode are met.
If the conditions are met, you can import trace data in semi-automatic mode.
If the semi-automatic mode does not meet your requirements, you must manually import the trace data.
If the conditions are not met, you can import trace data in manual mode.
Step 1: Initialize an OpenTelemetry provider
Simple Log Service offers a provider that allows you to build dependencies and upload the dependencies to Simple Log Service. This provider helps simplify the use of an OpenTelemetry provider. For more information, see opentelemetry-go-provider-sls.
You must initialize an OpenTelemetry provider before you create traces and register metrics.
You can run code or configure environment variables to initialize an OpenTelemetry provider.
Run code to initialize an OpenTelemetry provider.
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 )
Write initialization code.
Replace the variables in the following code with the actual values. For more information about the variables, see Variables.
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}")) // Invoke the panic() function. If the initialization fails, the OpenTelemetry provider exits. 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 business logic code. ... }
Table 1. Variables
Variable
Description
Example
${service}
The name of the service. Specify the value based on your business requirements.
payment
${service.namespace}
The namespace to which the service belongs.
order
${version}
The version of the service. We recommend that you specify a version in the va.b.c format.
v0.1.2
${endpoint}
The endpoint of the Simple Log Service project. Format: ${project}.${region-endpoint}:Port.
${project}: the name of the Simple Log Service project.
${region-endpoint}: the Simple Log Service endpoint for the region where the project resides. You can access Simple Log Service by using an internal or public endpoint. An internal endpoint can be accessed over the classic network or a virtual private cloud (VPC). A public endpoint can be accessed over the Internet. For more information, see Endpoints.
Port: the port number. The value is fixed as 10010.
NoteIf you set the variable to stdout, data is printed to standard output. In this case, the code line is provider.WithTraceExporterEndpoint("stdout").
If you leave the variable 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.
We recommend that you use the AccessKey pair of a RAM user that has only the write permissions on the Simple Log Service project. An AccessKey pair consists of an AccessKey ID and an AccessKey secret. For more information about how to grant the write permissions on a specified project to a RAM user, see Use custom policies to grant permissions to a RAM user. 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.
We recommend that you use the AccessKey pair of a RAM user that has only the write permissions on the Simple Log Service project.
None
Configure environment variables to initialize an OpenTelemetry provider.
Configuration method
Environment variable
Required
Description
Default value
WithServiceName
SLS_OTEL_SERVICE_NAME
Yes
The name of the service. Specify the value based on your business requirements.
None
WithServiceNamespace
SLS_OTEL_SERVICE-NAMESPACE
No
The namespace to which the service belongs.
order
WithServiceVersion
SLS_OTEL_SERVICE_VERSION
Yes
The version of the service. We recommend that you specify a version in the va.b.c format.
v0.1.0
WithSLSConfig
SLS_OTEL_PROJECT, SLS_OTEL_INSTANCE_ID, SLS_OTEL_ACCESS_KEY_ID, and SLS_OTEL_ACCESS_KEY_SECRET
No
The information about Simple Log Service resources. The information includes the name of a project, name of a trace instance, AccessKey ID of an account that has only the write-permissions on the project, and AccessKey secret of the account. For more information about how to grant the write permissions on a specified project to a RAM user, see Use custom policies to grant permissions to a RAM user. 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. Format: ${project}.${region-endpoint}:Port.
${project}: the name of the Simple Log Service project.
${region-endpoint}: the Simple Log Service endpoint for the region where the project resides. You can access Simple Log Service by using an internal or public endpoint. An internal endpoint can be accessed over the classic network or a VPC. A public endpoint can be accessed over the Internet. For more information, see Endpoints.
Port: the port number. The value is fixed as 10010.
NoteIf you set the variable to stdout, data is printed to standard output.
If you leave the variable empty, trace data is not uploaded to Simple Log Service.
stdout
WithTraceExporterInsecure
SLS_OTEL_TRACE_INSECURE
No
Specifies whether to transfer data by using a method that is not secure. Valid values:
true
false
NoteIf you want to directly transfer data to Simple Log Service, you must set the variable to false.
false
WithMetricExporterEndpoint
SLS_OTEL_METRIC_ENDPOINT
No
The endpoint of the Simple Log Service project. Format: ${project}.${region-endpoint}:Port.
${project}: the name of the Simple Log Service project.
${region-endpoint}: the Simple Log Service endpoint for the region where the project resides. You can access Simple Log Service by using an internal or public endpoint. An internal endpoint can be accessed over the classic network or a VPC. A public endpoint can be accessed over the Internet. For more information, see Endpoints.
Port: the port number. The value is fixed as 10010.
NoteIf you set the variable to stdout, data is printed to standard output.
If you leave the variable empty, metric data is not uploaded to Simple Log Service.
stdout
WithMetricExporterInsecure
SLS_OTEL_METRIC_INSECURE
No
Specifies whether to transfer data by using a method that is not secure. Valid values:
true
false
NoteIf you want to directly transfer data to Simple Log Service, you must set the variable to false.
false
WithResourceAttributes
None
No
The additional tag information, such as the environment and zone.
None
WithResource
OTEL_RESOURCE_ATTRIBUTES
No
The additional tag information, such as the environment and zone. Format: key1=value1,key2=value2.
None
WithMetricReportingPeriod
SLS_OTEL_METRIC_EXPORT_PERIOD
No
The interval of reporting metric data. We recommend that you set the interval to a value from 15s to 60s.
30s
WithErrorHandler
None
No
The error handling function. If an internal SDK error occurs, the system calls this function. This function is equivalent to the WithErrorHandlerFunc function.
None
WithErrorHandlerFunc
None
No
The error handling function.
None
None
SLS_OTEL_ATTRIBUTES_ENV_KEYS
No
The additional tag information, such as the environment and zone. This variable is similar to OTEL_RESOURCE_ATTRIBUTES. However, the values of attribute keys that are defined in the SLS_OTEL_ATTRIBUTES_ENV_KEYS variable are read from other environment variables.
SLS_OTEL_ATTRIBUTES_ENV_KEYS is commonly used in Kubernetes clusters to pad some template values to specified environment variables. Format: env-key-1|env-key-2|env-key-3.
None
Step 2: Import data
Semi-automatic mode: recommended
OpenTelemetry provides automatic instrumentation solutions for various basic libraries. If your business rely on these libraries, you can use the automatic instrumentation solutions to import data. For more information about basic libraries, see Instrumentation.
Use the .NET or HTTP framework to import data
The following sample code is created 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 with the actual values. For more information about the variables, see Variables.
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}")) // Invoke the panic() function. If the initialization fails, the OpenTelemetry provider exits. 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) // If you want to analyze metric data in the application, you can register the 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 { // If you want to record some events, you can obtain the span in the context and add events. 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 instrumentation solution for otel net/http, you need to only 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) } }
Use the Gorilla Mux framework to import data
The following sample code is created based on go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.37.0. The interface may change in later versions. For more information about the latest sample code, see otel-mux-example.
Replace the variables in the following code with the actual values. For more information about the variables, see Variables.
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}")) // Invoke the panic() function. If the initialization fails, the OpenTelemetry provider exits. 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) // If you want to analyze metric data in the application, you can register the 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" } // If you want to record some events, you can obtain the span in the context and add events. 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 mode
Replace the variables in the following code with the actual values. For more information about the variables, see Variables.
// 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")) // Invoke the panic() function. If the initialization fails, the OpenTelemetry provider exits. 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 the label 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 // Obtain 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 value 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 result of the span and the error information. 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) }