

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 遷移至 OpenTelemetry Go
<a name="manual-instrumentation-go"></a>

從 X-Ray 遷移時，請使用下列程式碼範例，使用 OpenTelemetry SDK 手動檢測 Go 應用程式。

## 使用 SDK 手動檢測
<a name="tracing-setup"></a>

------
#### [ Tracing setup with X-Ray SDK ]

使用適用於 Go 的 X-Ray 開發套件時，在檢測程式碼之前，需要設定服務外掛程式或本機取樣規則。

```
func init() {
    if os.Getenv("ENVIRONMENT") == "production" {
        ec2.Init()
    }

    xray.Configure(xray.Config{
        DaemonAddr:       "127.0.0.1:2000", 
        ServiceVersion:   "1.2.3",
    })
}
```

------
#### [ Set up tracing with OpenTelemetry SDK ]

透過執行個體化 TracerProvider 並將其註冊為全域追蹤器供應商，來設定 OpenTelemetry SDK。我們建議您設定下列元件：
+ OTLP 追蹤匯出工具 – 將追蹤匯出至 CloudWatch Agent 或 OpenTelemetry Collector 時需要
+ X-Ray 傳播器 – 將追蹤內容傳播至與 X-Ray 整合 AWS 的服務時需要
+ X-Ray 遠端取樣器 – 使用 X-Ray 取樣規則取樣請求時需要
+ 資源偵測器 – 偵測執行您應用程式的主機中繼資料

```
import (
    "go.opentelemetry.io/contrib/detectors/aws/ec2"
    "go.opentelemetry.io/contrib/propagators/aws/xray"
    "go.opentelemetry.io/contrib/samplers/aws/xray"
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
    "go.opentelemetry.io/otel/sdk/trace"
)

func setupTracing() error {
    ctx := context.Background()

    exporterEndpoint := os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT")
    if exporterEndpoint == "" {
        exporterEndpoint = "localhost:4317"
    }

    traceExporter, err := otlptracegrpc.New(ctx,
        otlptracegrpc.WithInsecure(),
        otlptracegrpc.WithEndpoint(exporterEndpoint))
    if err != nil {
        return fmt.Errorf("failed to create OTLP trace exporter: %v", err)
    }

    remoteSampler, err := xray.NewRemoteSampler(ctx, "my-service-name", "ec2")
    if err != nil {
        return fmt.Errorf("failed to create X-Ray Remote Sampler: %v", err)
    }

    ec2Resource, err := ec2.NewResourceDetector().Detect(ctx)
    if err != nil {
        return fmt.Errorf("failed to detect EC2 resource: %v", err)
    }

    tp := trace.NewTracerProvider(
        trace.WithSampler(remoteSampler),
        trace.WithBatcher(traceExporter),
        trace.WithResource(ec2Resource),
    )

    otel.SetTracerProvider(tp)
    otel.SetTextMapPropagator(xray.Propagator{})

    return nil
}
```

------

## 追蹤傳入請求 (HTTP 處理常式檢測）
<a name="http-handler-instrumentation"></a>

------
#### [ With X-Ray SDK ]

為了使用 X-Ray 檢測 HTTP 處理常式，X-Ray 處理常式方法用於使用 NewFixedSegmentNamer 產生區段。

```
func main() {
    http.Handle("/", xray.Handler(xray.NewFixedSegmentNamer("myApp"), http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Hello!"))
    })))
    http.ListenAndServe(":8000", nil)
}
```

------
#### [ With OpenTelemetry SDK ]

若要使用 OpenTelemetry 檢測 HTTP 處理常式，請使用 OpenTelemetry 的 newHandler 方法來包裝原始處理常式程式碼。

```
import (
    "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)
    
helloHandler := func(w http.ResponseWriter, req *http.Request) {
    ctx := req.Context()
    span := trace.SpanFromContext(ctx)
    span.SetAttributes(attribute.Bool("isHelloHandlerSpan", true), attribute.String("attrKey", "attrValue"))

    _, _ = io.WriteString(w, "Hello World!\n")
}

otelHandler := otelhttp.NewHandler(http.HandlerFunc(helloHandler), "Hello")

http.Handle("/hello", otelHandler)
err = http.ListenAndServe(":8080", nil)
if err != nil {
    log.Fatal(err)
}
```

------

## AWS 適用於 Go v2 檢測的 SDK
<a name="aws-sdk-instrumentation"></a>

------
#### [ With X-Ray SDK ]

若要檢測來自 AWS SDK 的傳出 AWS 請求，您的用戶端會依下列方式進行檢測：

```
// Create a segment
ctx, root := xray.BeginSegment(context.TODO(), "AWSSDKV2_Dynamodb")
defer root.Close(nil)

cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion("us-west-2"))
if err != nil {
    log.Fatalf("unable to load SDK config, %v", err)
}
// Instrumenting AWS SDK v2
awsv2.AWSV2Instrumentor(&cfg.APIOptions)
// Using the Config value, create the DynamoDB client
svc := dynamodb.NewFromConfig(cfg)
// Build the request with its input parameters
_, err = svc.ListTables(ctx, &dynamodb.ListTablesInput{
    Limit: aws.Int32(5),
})
if err != nil {
    log.Fatalf("failed to list tables, %v", err)
}
```

------
#### [ With OpenTelemetry SDK ]

OpenTelemetry 的 AWS SDK for Go v2 Instrumentation 提供下游 AWS SDK 呼叫的追蹤支援。以下是追蹤 S3 用戶端呼叫的範例：

```
import (
    ...

    "github.com/aws/aws-sdk-go-v2/config"
    "github.com/aws/aws-sdk-go-v2/service/s3"

    "go.opentelemetry.io/otel"
    oteltrace "go.opentelemetry.io/otel/trace"
    awsConfig "github.com/aws/aws-sdk-go-v2/config"
    "go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws"
)

...

    
    // init aws config
    cfg, err := awsConfig.LoadDefaultConfig(ctx)
    if err != nil {
        panic("configuration error, " + err.Error())
    }
    
    // instrument all aws clients
    otelaws.AppendMiddlewares(&.APIOptions)
    
    
    // Call to S3
    s3Client := s3.NewFromConfig(cfg)
    input := &s3.ListBucketsInput{}
    result, err := s3Client.ListBuckets(ctx, input)
    if err != nil {
        fmt.Printf("Got an error retrieving buckets, %v", err)
        return
    }
```

------

## 檢測傳出的 HTTP 呼叫
<a name="http-client-instrumentation"></a>

------
#### [ With X-Ray SDK ]

為了使用 X-Ray 檢測傳出的 HTTP 呼叫，xray.Client 用於建立所提供 HTTP 用戶端的副本。

```
myClient := xray.Client(http-client)

resp, err := ctxhttp.Get(ctx, xray.Client(nil), url)
```

------
#### [ With OpenTelemetry SDK ]

若要使用 OpenTelemetry 檢測 HTTP 用戶端，請使用 OpenTelemetry 的 otelhttp.NewTransport 方法來包裝 http.DefaultTransport。

```
import (
    "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)

// Create an instrumented HTTP client.
httpClient := &http.Client{
    Transport: otelhttp.NewTransport(
        http.DefaultTransport,
    ),
}

req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.github.com/repos/aws-observability/aws-otel-go/releases/latest", nil)
if err != nil {
    fmt.Printf("failed to create http request, %v\n", err)
}
res, err := httpClient.Do(req)
if err != nil {
    fmt.Printf("failed to make http request, %v\n", err)
}
// Request body must be closed
defer func(Body io.ReadCloser) {
    err := Body.Close()
    if err != nil {
        fmt.Printf("failed to close http response body, %v\n", err)
    }
}(res.Body)
```

------

## 其他程式庫的檢測支援
<a name="other-libraries-go"></a>

您可以在檢測套件 下找到 OpenTelemetry Go 支援程式庫檢測的完整清單。 [https://github.com/open-telemetry/opentelemetry-go-contrib/tree/main/instrumentation#instrumentation-packages](https://github.com/open-telemetry/opentelemetry-go-contrib/tree/main/instrumentation#instrumentation-packages)

或者，您可以搜尋 OpenTelemetry 登錄檔，了解 OpenTelemetry 是否支援對[登錄檔](https://opentelemetry.io/ecosystem/registry/)下的程式庫進行檢測。

## 手動建立追蹤資料
<a name="manual-trace-creation"></a>

------
#### [ With X-Ray SDK ]

使用 X-Ray 開發套件時，需要 BeginSegment 和 BeginSubsegment 方法，才能手動建立 X-Ray 區段和子區段。

```
// Start a segment
ctx, seg := xray.BeginSegment(context.Background(), "service-name")
// Start a subsegment
subCtx, subSeg := xray.BeginSubsegment(ctx, "subsegment-name")

// Add metadata or annotation here if necessary
xray.AddAnnotation(subCtx, "annotationKey", "annotationValue")
xray.AddMetadata(subCtx, "metadataKey", "metadataValue")

subSeg.Close(nil)
// Close the segment
seg.Close(nil)
```

------
#### [ With OpenTelemetry SDK ]

使用自訂範圍來監控檢測程式庫未擷取的內部活動效能。請注意，只有種類的伺服器會轉換為 X-Ray 區段，所有其他範圍則會轉換為 X-Ray 子區段。

首先，您需要建立追蹤器來產生跨度，您可以透過 `otel.Tracer`方法取得。這將提供來自 TracerProvider 的 Tracer 執行個體，該執行個體已在追蹤設定範例中全域註冊。您可以視需要建立任意數量的 Tracer 執行個體，但整個應用程式通常會有一個 Tracer。

```
    tracer := otel.Tracer("application-tracer")
```

```
import (
    ...

    oteltrace "go.opentelemetry.io/otel/trace"
)

...

    var attributes = []attribute.KeyValue{
        attribute.KeyValue{Key: "metadataKey", Value: attribute.StringValue("metadataValue")},
        attribute.KeyValue{Key: "annotationKey", Value: attribute.StringValue("annotationValue")},
        attribute.KeyValue{Key: "aws.xray.annotations", Value: attribute.StringSliceValue([]string{"annotationKey"})},
    }
    
    ctx := context.Background()
    
    parentSpanContext, parentSpan := tracer.Start(ctx, "ParentSpan", oteltrace.WithSpanKind(oteltrace.SpanKindServer), oteltrace.WithAttributes(attributes...))
    _, childSpan := tracer.Start(parentSpanContext, "ChildSpan", oteltrace.WithSpanKind(oteltrace.SpanKindInternal))
    
    // ...
    
    childSpan.End()
    parentSpan.End()
```

**使用 OpenTelemetry SDK 將註釋和中繼資料新增至追蹤**

在上述範例中， `WithAttributes`方法用於將屬性新增至每個跨度。請注意，根據預設，所有跨度屬性都會轉換為 X-Ray 原始資料中的中繼資料。若要確保屬性轉換為註釋而非中繼資料，請將屬性的索引鍵新增至`aws.xray.annotations`屬性清單。如需詳細資訊，請參閱[啟用自訂 X-Ray 註釋。 ](https://aws-otel.github.io/docs/getting-started/x-ray#enable-the-customized-x-ray-annotations)

------

## Lambda 手動檢測
<a name="lambda-instrumentation"></a>

------
#### [ With X-Ray SDK ]

使用 X-Ray 開發套件時，在 Lambda *啟用主動追蹤*之後，不需要其他組態即可使用 X-Ray 開發套件。Lambda 建立了代表 Lambda 處理常式調用的區段，而且您使用 X-Ray SDK 建立子區段，而不需要任何其他組態。

------
#### [ With OpenTelemetry SDK ]

下列 Lambda 函數程式碼 （不含檢測） 會發出 Amazon S3 ListBuckets 呼叫和傳出 HTTP 請求。

```
package main

import (
    "context"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "os"

    "github.com/aws/aws-lambda-go/events"
    "github.com/aws/aws-lambda-go/lambda"
    awsconfig "github.com/aws/aws-sdk-go-v2/config"
    "github.com/aws/aws-sdk-go-v2/service/s3"
)

func lambdaHandler(ctx context.Context) (interface{}, error) {
    // Initialize AWS config.
    cfg, err := awsconfig.LoadDefaultConfig(ctx)
    if err != nil {
        panic("configuration error, " + err.Error())
    }

    s3Client := s3.NewFromConfig(cfg)

    // Create an HTTP client.
    httpClient := &http.Client{
        Transport: http.DefaultTransport,
    }

    input := &s3.ListBucketsInput{}
    result, err := s3Client.ListBuckets(ctx, input)
    if err != nil {
        fmt.Printf("Got an error retrieving buckets, %v", err)
    }

    fmt.Println("Buckets:")
    for _, bucket := range result.Buckets {
        fmt.Println(*bucket.Name + ": " + bucket.CreationDate.Format("2006-01-02 15:04:05 Monday"))
    }
    fmt.Println("End Buckets.")

    req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.github.com/repos/aws-observability/aws-otel-go/releases/latest", nil)
    if err != nil {
        fmt.Printf("failed to create http request, %v\n", err)
    }
    res, err := httpClient.Do(req)
    if err != nil {
        fmt.Printf("failed to make http request, %v\n", err)
    }
    defer func(Body io.ReadCloser) {
        err := Body.Close()
        if err != nil {
            fmt.Printf("failed to close http response body, %v\n", err)
        }
    }(res.Body)

    var data map[string]interface{}
    err = json.NewDecoder(res.Body).Decode(&data)
    if err != nil {
        fmt.Printf("failed to read http response body, %v\n", err)
    }
    fmt.Printf("Latest ADOT Go Release is '%s'\n", data["name"])

    return events.APIGatewayProxyResponse{
        StatusCode: http.StatusOK,
        Body:       os.Getenv("_X_AMZN_TRACE_ID"),
    }, nil
}

func main() {
    lambda.Start(lambdaHandler)
}
```

若要手動檢測 Lambda 處理常式和 Amazon S3 用戶端，請執行下列動作：

1. 在 *main()* 中，執行個體化 TracerProvider (tp)，並將其註冊為全域追蹤器提供者。建議將 TracerProvider 設定為：

   1. 具有 X-Ray UDP 跨度匯出程式的簡易跨度處理器，可將追蹤傳送至 Lambda 的 UDP X-Ray 端點

   1. 將 *service.name* 設定為 Lambda 函數名稱的資源

1. 將 的用量`lambda.Start(lambdaHandler)`變更為 `lambda.Start(otellambda.InstrumentHandler(lambdaHandler, xrayconfig.WithRecommendedOptions(tp)...))`。

1. 透過將 的 OpenTelemetry 中介軟體附加`aws-sdk-go-v2`至 Amazon S3 用戶端組態，使用 OpenTemetry SDK 檢測來檢測 Amazon S3 用戶端。 OpenTemetry AWS OpenTelemetry 

1. 使用 OpenTelemetry 的 `otelhttp.NewTransport`方法來包裝 ，以檢測 http 用戶端`http.DefaultTransport`。

下列程式碼是 Lambda 函數在變更後的外觀範例。除了自動提供的範圍之外，您還可以手動建立其他自訂範圍。

```
package main

import (
    "context"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "os"

    "github.com/aws-observability/aws-otel-go/exporters/xrayudp"
    "github.com/aws/aws-lambda-go/events"
    "github.com/aws/aws-lambda-go/lambda"
    awsconfig "github.com/aws/aws-sdk-go-v2/config"
    "github.com/aws/aws-sdk-go-v2/service/s3"

    lambdadetector "go.opentelemetry.io/contrib/detectors/aws/lambda"
    "go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-lambda-go/otellambda"
    "go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-lambda-go/otellambda/xrayconfig"
    "go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws"
    "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
    "go.opentelemetry.io/contrib/propagators/aws/xray"
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/attribute"
    "go.opentelemetry.io/otel/sdk/resource"
    "go.opentelemetry.io/otel/sdk/trace"
    semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
)

func lambdaHandler(ctx context.Context) (interface{}, error) {
    // Initialize AWS config.
    cfg, err := awsconfig.LoadDefaultConfig(ctx)
    if err != nil {
        panic("configuration error, " + err.Error())
    }

    // Instrument all AWS clients.
    otelaws.AppendMiddlewares(&cfg.APIOptions)
    // Create an instrumented S3 client from the config.
    s3Client := s3.NewFromConfig(cfg)

    // Create an instrumented HTTP client.
    httpClient := &http.Client{
        Transport: otelhttp.NewTransport(
            http.DefaultTransport,
        ),
    }

    // return func(ctx context.Context) (interface{}, error) {
    input := &s3.ListBucketsInput{}
    result, err := s3Client.ListBuckets(ctx, input)
    if err != nil {
        fmt.Printf("Got an error retrieving buckets, %v", err)
    }

    fmt.Println("Buckets:")
    for _, bucket := range result.Buckets {
        fmt.Println(*bucket.Name + ": " + bucket.CreationDate.Format("2006-01-02 15:04:05 Monday"))
    }
    fmt.Println("End Buckets.")

    req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.github.com/repos/aws-observability/aws-otel-go/releases/latest", nil)
    if err != nil {
        fmt.Printf("failed to create http request, %v\n", err)
    }
    res, err := httpClient.Do(req)
    if err != nil {
        fmt.Printf("failed to make http request, %v\n", err)
    }
    defer func(Body io.ReadCloser) {
        err := Body.Close()
        if err != nil {
            fmt.Printf("failed to close http response body, %v\n", err)
        }
    }(res.Body)

    var data map[string]interface{}
    err = json.NewDecoder(res.Body).Decode(&data)
    if err != nil {
        fmt.Printf("failed to read http response body, %v\n", err)
    }
    fmt.Printf("Latest ADOT Go Release is '%s'\n", data["name"])

    return events.APIGatewayProxyResponse{
        StatusCode: http.StatusOK,
        Body:       os.Getenv("_X_AMZN_TRACE_ID"),
    }, nil
}

func main() {
    ctx := context.Background()
    detector := lambdadetector.NewResourceDetector()
    lambdaResource, err := detector.Detect(context.Background())
    if err != nil {
        fmt.Printf("failed to detect lambda resources: %v\n", err)
    }

    var attributes = []attribute.KeyValue{
        attribute.KeyValue{Key: semconv.ServiceNameKey, Value: attribute.StringValue(os.Getenv("AWS_LAMBDA_FUNCTION_NAME"))},
    }
    customResource := resource.NewWithAttributes(semconv.SchemaURL, attributes...)
    mergedResource, _ := resource.Merge(lambdaResource, customResource)

    xrayUdpExporter, _ := xrayudp.NewSpanExporter(ctx)
    tp := trace.NewTracerProvider(
        trace.WithSpanProcessor(trace.NewSimpleSpanProcessor(xrayUdpExporter)),
        trace.WithResource(mergedResource),
    )

    defer func(ctx context.Context) {
        err := tp.Shutdown(ctx)
        if err != nil {
            fmt.Printf("error shutting down tracer provider: %v", err)
        }
    }(ctx)

    otel.SetTracerProvider(tp)
    otel.SetTextMapPropagator(xray.Propagator{})

    lambda.Start(otellambda.InstrumentHandler(lambdaHandler, xrayconfig.WithRecommendedOptions(tp)...))
}
```

------

叫用 Lambda 時，您會在 CloudWatch 主控台的 `Trace Map`中看到下列追蹤：

![\[適用於 Golang 的 CloudWatch 主控台中的追蹤映射。\]](http://docs.aws.amazon.com/zh_tw/xray/latest/devguide/images/deprecation_golang.png)
