-
Notifications
You must be signed in to change notification settings - Fork 591
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
End to end tracing support - Lambda #983
Changes from 61 commits
dcc01de
1dd54d9
03f6fb4
5919be5
ebd45b4
e1ff7d0
041d9b8
e12a4b1
e906d2a
86c04d1
047f5d0
21db8fa
5204d27
c9c1bca
c956d4b
12c6c74
e042a6f
1f6b745
76a52a1
f0ec58b
f048e4b
23638fb
439023e
f7ac5db
e932685
8c86bb4
debba6c
d481dcb
fedb803
38a9e91
6cc43b2
f0b4a7f
6df424c
5b70dfe
9de0d34
51144f5
8f8e491
d3d2c18
e572b7b
ac8b307
24c9108
7b4a00e
2c391fc
b64863e
e463802
804939b
0f51b45
8226dd3
986e366
af8a871
8e2c1de
8c53555
e2a18cc
ada0137
1a0457f
1754a23
8445736
c0c6f30
92b045b
4df95c7
93e9916
bd387fd
cbc1242
8ee0d8a
8dd0fe8
fa51016
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,67 @@ | ||||||||
# OpenTelemetry AWS Lambda Resource Detector for Golang | ||||||||
|
||||||||
[![Go Reference][goref-image]][goref-url] | ||||||||
[![Apache License][license-image]][license-url] | ||||||||
|
||||||||
This module detects resource attributes available in AWS Lambda. | ||||||||
|
||||||||
## Installation | ||||||||
|
||||||||
```bash | ||||||||
go get -u go.opentelemetry.io/contrib/detectors/aws/lambda | ||||||||
``` | ||||||||
|
||||||||
## Usage | ||||||||
|
||||||||
Create a sample Lambda Go application such as below. | ||||||||
|
||||||||
```go | ||||||||
package main | ||||||||
|
||||||||
import ( | ||||||||
"github.com/aws/aws-lambda-go/lambda" | ||||||||
sdktrace "go.opencensus.io/otel/sdk/trace" | ||||||||
lambdadetector "go.opentelemetry.io/contrib/detectors/aws/lambda" | ||||||||
) | ||||||||
|
||||||||
func main() { | ||||||||
detector := lambdadetector.NewResourceDetector() | ||||||||
res, err := detector.Detect(context.Background()) | ||||||||
if err != nil { | ||||||||
fmt.Printf("failed to detect lambda resources: %v\n", err) | ||||||||
} | ||||||||
|
||||||||
tp := sdktrace.NewTracerProvider( | ||||||||
sdktrace.WithResource(res), | ||||||||
) | ||||||||
lambda.Start(<some lambda handler>) | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
} | ||||||||
``` | ||||||||
|
||||||||
Now your `TracerProvider` will have the following resource attributes and attach them to new spans: | ||||||||
|
||||||||
| Resource Attribute | Example Value | | ||||||||
| --- | --- | | ||||||||
| `cloud.provider` | aws | ||||||||
|`cloud.region` | us-east-1 | ||||||||
|`faas.name` | MyLambdaFunction | ||||||||
|`faas.version` | $LATEST | ||||||||
|
||||||||
Of note, `faas.id` and `cloud.account.id` are not set by the Lambda resource detector because they are not available outside a Lambda invocation. For this reason, when using the AWS Lambda Instrumentation these attributes are set as additional span attributes. | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could this be solved through lazy initialization? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not without asking the user to add more code. The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why would the user have to do that lazy-initialization, rather than the framework? E.g., at this point it seems like you'd have access to those invocation-scoped variables. It's true that you'd have to do separately for each possible reflective type of the handler function. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the problem is that we will then couple the Lambda resource detector and the Lambda instrumentation, which ought to be two separate components. The Lambda instrumentation does indeed have access to those invocation-scoped attributes, and is able to initialize the tracer provider. But the Lambda resource detector must operate only with what is available in the static environment, so if a user wants to initialize a custom tracer provider outside their handler as seen in the example it will work the same. I agree that it's unfortunate the lambda resource detector doesn't capture these attributes, but I believe this is a limitation of Lambda itself more than any individual instrumentation, which is why the span attribute is called out in the spec and there's talks of not even marking faas.id as a resource attribute at all. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for explaining. I was commenting based on "what would an operator find most useful" vs "what is a clean way to factor the code." Given the ambiguity / ongoing discussion around faas.id, I think this code makes a reasonable choice. |
||||||||
|
||||||||
## Useful links | ||||||||
|
||||||||
- For more on FaaS attribute conventions, visit <https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/faas.md> | ||||||||
- For more information on OpenTelemetry, visit: <https://opentelemetry.io/> | ||||||||
- For more about OpenTelemetry Go: <https://github.com/open-telemetry/opentelemetry-go> | ||||||||
- For help or feedback on this project, join us in [GitHub Discussions][discussions-url] | ||||||||
|
||||||||
## License | ||||||||
|
||||||||
Apache 2.0 - See [LICENSE][license-url] for more information. | ||||||||
|
||||||||
[license-url]: https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/LICENSE | ||||||||
[license-image]: https://img.shields.io/badge/license-Apache_2.0-green.svg?style=flat | ||||||||
[goref-image]: https://pkg.go.dev/badge/go.opentelemetry.io/contrib/detectors/aws/lambda.svg | ||||||||
[goref-url]: https://pkg.go.dev/go.opentelemetry.io/contrib/detectors/aws/lambda | ||||||||
[discussions-url]: https://github.com/open-telemetry/opentelemetry-go/discussions |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
// Copyright The OpenTelemetry 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 lambda | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"os" | ||
|
||
"go.opentelemetry.io/otel/attribute" | ||
"go.opentelemetry.io/otel/sdk/resource" | ||
semconv "go.opentelemetry.io/otel/semconv/v1.4.0" | ||
) | ||
|
||
const ( | ||
Aneurysm9 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
lambdaFunctionNameEnvVar = "AWS_LAMBDA_FUNCTION_NAME" | ||
Aneurysm9 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
awsRegionEnvVar = "AWS_REGION" | ||
lambdaFunctionVersionEnvVar = "AWS_LAMBDA_FUNCTION_VERSION" | ||
) | ||
|
||
var ( | ||
empty = resource.Empty() | ||
errNotOnLambda = errors.New("process is not on Lambda, cannot detect environment variables from Lambda") | ||
) | ||
|
||
// resource detector collects resource information from Lambda environment | ||
type resourceDetector struct{} | ||
|
||
// compile time assertion that resource detector implements the resource.Detector interface. | ||
var _ resource.Detector = (*resourceDetector)(nil) | ||
|
||
// NewResourceDetector returns a resource detector that will detect AWS Lambda resources. | ||
func NewResourceDetector() resource.Detector { | ||
return &resourceDetector{} | ||
} | ||
|
||
// Detect collects resource attributes available when running on lambda | ||
func (detector *resourceDetector) Detect(context.Context) (*resource.Resource, error) { | ||
|
||
// Lambda resources come from ENV | ||
lambdaName := os.Getenv(lambdaFunctionNameEnvVar) | ||
if len(lambdaName) == 0 { | ||
return empty, errNotOnLambda | ||
} | ||
awsRegion := os.Getenv(awsRegionEnvVar) | ||
functionVersion := os.Getenv(lambdaFunctionVersionEnvVar) | ||
|
||
attrs := []attribute.KeyValue{ | ||
semconv.CloudProviderAWS, | ||
semconv.CloudRegionKey.String(awsRegion), | ||
semconv.FaaSNameKey.String(lambdaName), | ||
semconv.FaaSVersionKey.String(functionVersion), | ||
} | ||
|
||
return resource.NewWithAttributes(semconv.SchemaURL, attrs...), nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
// Copyright The OpenTelemetry 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 lambda | ||
|
||
import ( | ||
"context" | ||
"os" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
|
||
"go.opentelemetry.io/otel/attribute" | ||
"go.opentelemetry.io/otel/sdk/resource" | ||
semconv "go.opentelemetry.io/otel/semconv/v1.4.0" | ||
) | ||
|
||
// successfully return resource when process is running on Amazon Lambda environment | ||
func TestDetectSuccess(t *testing.T) { | ||
os.Clearenv() | ||
_ = os.Setenv(lambdaFunctionNameEnvVar, "testFunction") | ||
_ = os.Setenv(awsRegionEnvVar, "us-texas-1") | ||
_ = os.Setenv(lambdaFunctionVersionEnvVar, "$LATEST") | ||
|
||
attributes := []attribute.KeyValue{ | ||
semconv.CloudProviderAWS, | ||
semconv.CloudRegionKey.String("us-texas-1"), | ||
semconv.FaaSNameKey.String("testFunction"), | ||
semconv.FaaSVersionKey.String("$LATEST"), | ||
} | ||
expectedResource := resource.NewWithAttributes(semconv.SchemaURL, attributes...) | ||
detector := resourceDetector{} | ||
res, err := detector.Detect(context.Background()) | ||
|
||
assert.Nil(t, err, "Detector unexpectedly returned error") | ||
assert.Equal(t, expectedResource, res, "Resource returned is incorrect") | ||
} | ||
|
||
// return empty resource when not running on lambda | ||
func TestReturnsIfNoEnvVars(t *testing.T) { | ||
os.Clearenv() | ||
detector := resourceDetector{} | ||
res, err := detector.Detect(context.Background()) | ||
|
||
assert.Equal(t, errNotOnLambda, err) | ||
assert.Equal(t, 0, len(res.Attributes())) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
module go.opentelemetry.io/contrib/detectors/aws/lambda | ||
|
||
go 1.15 | ||
|
||
require ( | ||
github.com/davecgh/go-spew v1.1.1 // indirect | ||
github.com/stretchr/testify v1.7.0 | ||
go.opentelemetry.io/otel v1.0.1 | ||
go.opentelemetry.io/otel/sdk v1.0.1 | ||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= | ||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= | ||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||
go.opentelemetry.io/otel v1.0.1 h1:4XKyXmfqJLOQ7feyV5DB6gsBFZ0ltB8vLtp6pj4JIcc= | ||
go.opentelemetry.io/otel v1.0.1/go.mod h1:OPEOD4jIT2SlZPMmwT6FqZz2C0ZNdQqiWcoK6M0SNFU= | ||
go.opentelemetry.io/otel/sdk v1.0.1 h1:wXxFEWGo7XfXupPwVJvTBOaPBC9FEg0wB8hMNrKk+cA= | ||
go.opentelemetry.io/otel/sdk v1.0.1/go.mod h1:HrdXne+BiwsOHYYkBE5ysIcv2bvdZstxzmCQhxTcZkI= | ||
go.opentelemetry.io/otel/trace v1.0.1 h1:StTeIH6Q3G4r0Fiw34LTokUFESZgIDUr0qIJ7mKmAfw= | ||
go.opentelemetry.io/otel/trace v1.0.1/go.mod h1:5g4i4fKLaX2BQpSBsxw8YYcgKpMMSW3x7ZTuYBr3sUk= | ||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 h1:iGu644GcxtEcrInvDsQRCwJjtCIOlT2V7IRt6ah2Whw= | ||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= | ||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= | ||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the example need either
otel.SetTracerProvider(tp)
or passingtp
explicitly into the Lambda handler? Otherwise the variable is unused.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, made a suggestion