Skip to content

Commit

Permalink
Merge pull request #657 from hashicorp/f-update-logging-fields
Browse files Browse the repository at this point in the history
Adds test for HTTP logging attributes
  • Loading branch information
gdavison authored Sep 19, 2023
2 parents 5bf0127 + 8a433d4 commit 3b0502f
Show file tree
Hide file tree
Showing 13 changed files with 266 additions and 48 deletions.
111 changes: 111 additions & 0 deletions aws_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ import (
"io"
"net"
"net/http"
"net/url"
"os"
"path/filepath"
"reflect"
"strconv"
"strings"
"testing"
"time"
Expand All @@ -39,6 +41,9 @@ import (
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/hashicorp/terraform-plugin-log/tflogtest"
"go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws"
semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
"golang.org/x/exp/maps"
)

const (
Expand Down Expand Up @@ -3513,6 +3518,112 @@ func TestLogger_TfLog(t *testing.T) {
t.Errorf("GetAwsConfig: line %d: expected module %q, got %q", i+1, e, a)
}
}
var requestLines []map[string]any
var responseLines []map[string]any
for _, line := range lines {
if line["@message"] == "HTTP Request Sent" {
requestLines = append(requestLines, line)
} else if line["@message"] == "HTTP Response Received" {
responseLines = append(responseLines, line)
}
}
if len(requestLines) != 1 {
t.Fatalf("expected 1 request line, got %d", len(requestLines))
}
requestLine := requestLines[0]
maps.DeleteFunc(requestLine, func(k string, _ any) bool {
return strings.HasPrefix(k, "@")
})

for _, k := range []string{
string(semconv.HTTPUserAgentKey),
string(logging.RequestHeaderAttributeKey("Amz-Sdk-Invocation-Id")),
string(logging.RequestHeaderAttributeKey("Authorization")),
string(logging.RequestHeaderAttributeKey("X-Amz-Date")),
} {
_, ok := requestLine[k]
if !ok {
t.Errorf("expected a value for request attribute %q", k)
}
delete(requestLine, k)
}

tsUrl, _ := url.Parse(ts.URL)
if tsUrl.Path == "" {
tsUrl.Path = "/"
}

port, err := strconv.ParseFloat(tsUrl.Port(), 32)
if err != nil {
t.Errorf("error parsing URL port %q: %s", tsUrl.Port(), err)
}

requestBody := "Action=GetCallerIdentity&Version=2011-06-15"

expectedRequest := map[string]any{
// AWS attributes
string(semconv.RPCSystemKey): otelaws.AWSSystemVal,
string(semconv.RPCServiceKey): sts.ServiceID,
string(otelaws.RegionKey): "us-east-1",
string(semconv.RPCMethodKey): "GetCallerIdentity",
// Custom attributes
string(logging.AwsSdkKey): awsSdkGoV2Val,
string(logging.CustomEndpointKey): true,
"http.request.body": requestBody + "\n",
// HTTP attributes
string(semconv.HTTPMethodKey): "POST",
string(logging.RequestHeaderAttributeKey("Amz-Sdk-Request")): "attempt=1; max=3",
string(logging.RequestHeaderAttributeKey("Content-Type")): "application/x-www-form-urlencoded",
string(semconv.HTTPRequestContentLengthKey): float64(len(requestBody)),
string(semconv.HTTPURLKey): tsUrl.String(),
// Net attributes
string(semconv.NetPeerNameKey): tsUrl.Hostname(),
string(semconv.NetPeerPortKey): port,
}

if diff := cmp.Diff(requestLine, expectedRequest); diff != "" {
t.Fatalf("unexpected request attributes: (- got, + expected)\n%s", diff)
}

if len(responseLines) != 1 {
t.Fatalf("expected 1 response line, got %d", len(responseLines))
}
responseLine := responseLines[0]
maps.DeleteFunc(responseLine, func(k string, _ any) bool {
return strings.HasPrefix(k, "@")
})

for _, k := range []string{
string("http.duration"),
string("http.response.body"),
string(logging.ResponseHeaderAttributeKey("Date")),
string(semconv.HTTPResponseContentLengthKey),
string(logging.ResponseHeaderAttributeKey("X-Amzn-Requestid")),
} {
_, ok := responseLine[k]
if !ok {
t.Errorf("expected a value for response attribute %q", k)
}
delete(responseLine, k)
}

expectedResponse := map[string]any{
// AWS attributes
string(semconv.RPCSystemKey): otelaws.AWSSystemVal,
string(semconv.RPCServiceKey): sts.ServiceID,
string(otelaws.RegionKey): "us-east-1",
string(semconv.RPCMethodKey): "GetCallerIdentity",
// Custom attributes
string(logging.AwsSdkKey): awsSdkGoV2Val,
string(logging.CustomEndpointKey): true,
// HTTP attributes
string(semconv.HTTPStatusCodeKey): float64(http.StatusOK),
string(logging.ResponseHeaderAttributeKey("Content-Type")): "text/xml",
}

if diff := cmp.Diff(responseLine, expectedResponse); diff != "" {
t.Fatalf("unexpected response attributes: (- got, + expected)\n%s", diff)
}

_, _, diags = GetAwsAccountIDAndPartition(ctx, awsConfig, config)
if diags.HasError() {
Expand Down
14 changes: 12 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,32 @@ require (
github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/terraform-plugin-log v0.9.0
github.com/mitchellh/go-homedir v1.1.0
go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws v0.44.0
go.opentelemetry.io/otel v1.18.0
golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea
golang.org/x/exp v0.0.0-20230905200255-921286631fa9
)

require (
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.42 // indirect
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.21.5 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.14 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.7.35 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35 // indirect
github.com/aws/aws-sdk-go-v2/service/sqs v1.24.5 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.13.6 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.6 // indirect
github.com/fatih/color v1.15.0 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
go.opentelemetry.io/otel/metric v1.18.0 // indirect
go.opentelemetry.io/otel/trace v1.18.0 // indirect
golang.org/x/sys v0.11.0 // indirect
golang.org/x/sys v0.12.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
29 changes: 25 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,18 @@ github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35 h1:SijA0mgjV8E+8G45lt
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35/go.mod h1:SJC1nEVVva1g3pHAIdCp7QsRIkMmLAgoDquQ9Rr8kYw=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.42 h1:GPUcE/Yq7Ur8YSUk6lVkoIMWnJNO0HT18GUzCWCgCI0=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.42/go.mod h1:rzfdUlfA+jdgLDmPKjd3Chq9V7LVLYo1Nz++Wb91aRo=
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.21.5 h1:EeNQ3bDA6hlx3vifHf7LT/l9dh9w7D2XgCdaD11TRU4=
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.21.5/go.mod h1:X3ThW5RPV19hi7bnQ0RMAiBjZbzxj4rZlj+qdctbMWY=
github.com/aws/aws-sdk-go-v2/service/iam v1.22.5 h1:qGv+oW4uV1T3kbE9uSYEfdZbo38OqxgRxxfStfDr4BU=
github.com/aws/aws-sdk-go-v2/service/iam v1.22.5/go.mod h1:8lyPrjQczmx72ac9s82zTjf9xLqs7uuFMG9TVEZ07XU=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.14 h1:m0QTSI6pZYJTk5WSKx3fm5cNW/DCicVzULBgU/6IyD0=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.14/go.mod h1:dDilntgHy9WnHXsh7dDtUPgHKEfTJIBUTHM8OWm0f/0=
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.7.35 h1:UKjpIDLVF90RfV88XurdduMoTxPqtGHZMIDYZQM7RO4=
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.7.35/go.mod h1:B3dUg0V6eJesUTi+m27NUkj7n8hdDKYUpxj8f4+TqaQ=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35 h1:CdzPW9kKitgIiLV1+MHobfR5Xg25iYnyzWZhyQuSlDI=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35/go.mod h1:QGF2Rs33W5MaN9gYdEQOBBFPLwTZkEhRwI33f7KIG0o=
github.com/aws/aws-sdk-go-v2/service/sqs v1.24.5 h1:RyDpTOMEJO6ycxw1vU/6s0KLFaH3M0z/z9gXHSndPTk=
github.com/aws/aws-sdk-go-v2/service/sqs v1.24.5/go.mod h1:RZBu4jmYz3Nikzpu/VuVvRnTEJ5a+kf36WT2fcl5Q+Q=
github.com/aws/aws-sdk-go-v2/service/sso v1.13.6 h1:2PylFCfKCEDv6PeSN09pC/VUiRd10wi1VfHG5FrW0/g=
github.com/aws/aws-sdk-go-v2/service/sso v1.13.6/go.mod h1:fIAwKQKBFu90pBxx07BFOMJLpRUGu8VOzLJakeY+0K4=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.6 h1:pSB560BbVj9ZlJZF4WYj5zsytWHWKxg+NgyGV4B2L58=
Expand All @@ -30,6 +38,11 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
Expand All @@ -42,7 +55,9 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0=
github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
Expand All @@ -62,21 +77,27 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws v0.44.0 h1:u2wxpWcQ6px9ACaIUX27ttNDx7B2OtTGRaIzvZOBsCQ=
go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws v0.44.0/go.mod h1:BmbXHiVZH22QIi98PXQtfD8YEA3lmnaEotGBn1vJ/X4=
go.opentelemetry.io/otel v1.18.0 h1:TgVozPGZ01nHyDZxK5WGPFB9QexeTMXEH7+tIClWfzs=
go.opentelemetry.io/otel v1.18.0/go.mod h1:9lWqYO0Db579XzVuCKFNPDl4s73Voa+zEck3wHaAYQI=
go.opentelemetry.io/otel/metric v1.18.0 h1:JwVzw94UYmbx3ej++CwLUQZxEODDj/pOuTCvzhtRrSQ=
go.opentelemetry.io/otel/metric v1.18.0/go.mod h1:nNSpsVDjWGfb7chbRLUNW+PBNdcSTHD4Uu5pfFMOI0k=
go.opentelemetry.io/otel/trace v1.18.0 h1:NY+czwbHbmndxojTEKiSMHkG2ClNH2PwmcHrdo0JY10=
go.opentelemetry.io/otel/trace v1.18.0/go.mod h1:T2+SGJGuYZY3bjj5rgh/hN7KIrlpWC5nS8Mjvzckz+0=
golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea h1:vLCWI/yYrdEHyN2JzIzPO3aaQJHQdp89IZBA/+azVC4=
golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
2 changes: 1 addition & 1 deletion go.work
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
go 1.19
go 1.20

use (
.
Expand Down
30 changes: 21 additions & 9 deletions logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/aws/smithy-go/middleware"
smithyhttp "github.com/aws/smithy-go/transport/http"
"github.com/hashicorp/aws-sdk-go-base/v2/logging"
"go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/semconv/v1.17.0/httpconv"
)
Expand All @@ -41,7 +42,7 @@ func (l debugLogger) Logf(classification smithylogging.Classification, format st
}
} else {
s = strings.ReplaceAll(s, "\r", "") // Works around https://github.com/jen20/teamcity-go-test/pull/2
log.Printf("[%s] missing_context: %s aws.sdk=aws-sdk-go-v2", classification, s)
log.Printf("[%s] missing_context: %s "+string(logging.AwsSdkKey)+"="+awsSdkGoV2Val, classification, s)
}
}

Expand All @@ -51,6 +52,12 @@ func (l debugLogger) WithContext(ctx context.Context) smithylogging.Logger {
}
}

const awsSdkGoV2Val = "aws-sdk-go-v2"

func awsSDKv2Attr() attribute.KeyValue {
return logging.AwsSdkKey.String(awsSdkGoV2Val)
}

// Replaces the built-in logging middleware from https://github.com/aws/smithy-go/blob/main/transport/http/middleware_http_logging.go
// We want access to the request and response structs, and cannot get it from the built-in.
// The typical route of adding logging to the http.RoundTripper doesn't work for the AWS SDK for Go v2 without forcing us to manually implement
Expand All @@ -69,19 +76,24 @@ func (r *requestResponseLogger) HandleDeserialize(ctx context.Context, in middle
) {
logger := logging.RetrieveLogger(ctx)

ctx = logger.SetField(ctx, "aws.sdk", "aws-sdk-go-v2")
ctx = logger.SetField(ctx, "aws.service", awsmiddleware.GetServiceID(ctx))
ctx = logger.SetField(ctx, "aws.operation", awsmiddleware.GetOperationName(ctx))

region := awsmiddleware.GetRegion(ctx)
ctx = logger.SetField(ctx, "aws.region", region)

attributes := []attribute.KeyValue{
otelaws.SystemAttr(),
otelaws.ServiceAttr(awsmiddleware.GetServiceID(ctx)),
otelaws.RegionAttr(region),
otelaws.OperationAttr(awsmiddleware.GetOperationName(ctx)),
awsSDKv2Attr(),
}
if signingRegion := awsmiddleware.GetSigningRegion(ctx); signingRegion != region {
ctx = logger.SetField(ctx, "aws.signing_region", signingRegion)
attributes = append(attributes, logging.SigningRegion(signingRegion))
}

if awsmiddleware.GetEndpointSource(ctx) == aws.EndpointSourceCustom {
ctx = logger.SetField(ctx, "aws.custom_endpoint_source", true)
attributes = append(attributes, logging.CustomEndpoint(true))
}

for _, attribute := range attributes {
ctx = logger.SetField(ctx, string(attribute.Key), attribute.Value.AsInterface())
}

smithyRequest, ok := in.Request.(*smithyhttp.Request)
Expand Down
20 changes: 20 additions & 0 deletions logging/attributes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package logging

import "go.opentelemetry.io/otel/attribute"

const (
AwsSdkKey attribute.Key = "tf_aws.sdk"
SigningRegionKey attribute.Key = "tf_aws.signing_region"
CustomEndpointKey attribute.Key = "tf_aws.custom_endpoint"
)

func SigningRegion(region string) attribute.KeyValue {
return SigningRegionKey.String(region)
}

func CustomEndpoint(custom bool) attribute.KeyValue {
return CustomEndpointKey.Bool(custom)
}
14 changes: 11 additions & 3 deletions logging/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func decomposeRequestHeaders(req *http.Request) []attribute.KeyValue {

securityToken := header.Values("X-Amz-Security-Token")
if len(securityToken) > 0 {
results = append(results, requestHeaderAttribute("X-Amz-Security-Token").String("*****"))
results = append(results, RequestHeaderAttributeKey("X-Amz-Security-Token").String("*****"))
}
header.Del("X-Amz-Security-Token")

Expand Down Expand Up @@ -114,7 +114,7 @@ func decomposeRequestBody(req *http.Request) (kv attribute.KeyValue, err error)
return attribute.String("http.request.body", body), nil
}

func requestHeaderAttribute(k string) attribute.Key {
func RequestHeaderAttributeKey(k string) attribute.Key {
return attribute.Key(requestHeaderAttributeName(k))
}

Expand Down Expand Up @@ -142,7 +142,7 @@ func authorizationHeaderAttribute(v string) (attribute.KeyValue, bool) {
return attribute.KeyValue{}, false
}

key := requestHeaderAttribute("Authorization")
key := RequestHeaderAttributeKey("Authorization")
if strings.HasPrefix(scheme, "AWS4-") {
components := regexp.MustCompile(`,\s+`).Split(params, -1)
var builder strings.Builder
Expand Down Expand Up @@ -201,6 +201,14 @@ func DecomposeResponseHeaders(resp *http.Response) []attribute.KeyValue {
return results
}

func ResponseHeaderAttributeKey(k string) attribute.Key {
return attribute.Key(responseHeaderAttributeName(k))
}

func responseHeaderAttributeName(k string) string {
return fmt.Sprintf("http.response.header.%s", normalizeHeaderName(k))
}

// cleanUpHeaderAttributes converts header attributes with a single element to a string
func cleanUpHeaderAttributes(attrs []attribute.KeyValue) []attribute.KeyValue {
return slices.ApplyToAll(attrs, func(attr attribute.KeyValue) attribute.KeyValue {
Expand Down
2 changes: 1 addition & 1 deletion servicemocks/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -418,10 +418,10 @@ func MockAwsApiServer(svcName string, endpoints []*MockEndpoint) *httptest.Serve
log.Printf("[DEBUG] Mocked %s API responding with %d: %s",
svcName, e.Response.StatusCode, e.Response.Body)

w.WriteHeader(e.Response.StatusCode)
w.Header().Set("Content-Type", e.Response.ContentType)
w.Header().Set("X-Amzn-Requestid", "1b206dd1-f9a8-11e5-becf-051c60f11c4a")
w.Header().Set("Date", time.Now().Format(time.RFC1123))
w.WriteHeader(e.Response.StatusCode)

fmt.Fprintln(w, e.Response.Body)
return
Expand Down
Loading

0 comments on commit 3b0502f

Please sign in to comment.