From ac8b4b914131b819f043fb164aeced2ddcef1794 Mon Sep 17 00:00:00 2001 From: dmitchsplunk Date: Tue, 17 Dec 2024 16:07:10 -0800 Subject: [PATCH] updated go azure function example --- instrumentation/go/azure-functions/README.md | 249 +++++++++++++++++- instrumentation/go/azure-functions/go.mod | 48 ++++ instrumentation/go/azure-functions/go.sum | 81 ++++++ instrumentation/go/azure-functions/handler.go | 95 ++++++- 4 files changed, 466 insertions(+), 7 deletions(-) create mode 100644 instrumentation/go/azure-functions/go.mod create mode 100644 instrumentation/go/azure-functions/go.sum diff --git a/instrumentation/go/azure-functions/README.md b/instrumentation/go/azure-functions/README.md index 80b4a2d..6660a14 100644 --- a/instrumentation/go/azure-functions/README.md +++ b/instrumentation/go/azure-functions/README.md @@ -1 +1,248 @@ -# splunk-opentelemetry-examples \ No newline at end of file +# Instrumenting a Go Azure Function with OpenTelemetry + +This example demonstrates how to instrument an serverless Azure function written in +Go using OpenTelemetry, and then export the data to Splunk Observability +Cloud. We'll use Go v1.21.4 for this example, but the steps for other Go versions are +similar. + +We'll use a custom handler for this example, which is based on +[Quickstart: Create a Go or Rust function in Azure using Visual Studio Code](https://learn.microsoft.com/en-us/azure/azure-functions/create-first-function-vs-code-other). + +## Prerequisites + +The following tools are required to deploy Go Azure functions: + +* An Azure account with permissions to create and execute Azure functions +* [Visual Studio Code](https://code.visualstudio.com/) +* An OpenTelemetry collector that's accessible to the Azure function +* Azure Functions extension for Visual Studio Code (installed using Visual Studio Code) + +## Splunk Distribution of the OpenTelemetry Collector + +For this example, we deployed the Splunk Distribution of the OpenTelemetry Collector onto a virtual machine +in Azure using Gateway mode, and ensured it's accessible to our Azure function. + +We configured it with the `SPLUNK_HEC_TOKEN` and `SPLUNK_HEC_URL` environment variables, so that it +exports logs to our Splunk Cloud instance. + +Please refer to [Install the Collector using packages and deployment tools](https://docs.splunk.com/observability/en/gdi/opentelemetry/install-the-collector.html#collector-package-install) +for collector installation instructions. + +## Application Overview + +If you just want to build and deploy the example, feel free to skip this section. + +The application used for this example is a simple Hello World application. + +We updated the [index.js](./src/index.js) file to include code that starts the instrumentation, +which adds the Azure function OpenTelemetry instrumentation: + +```` +const sdk = new NodeSDK({ + traceExporter: new OTLPTraceExporter(), + metricReader: new PeriodicExportingMetricReader({ + exporter: new OTLPMetricExporter(), + }), + logRecordProcessor: new BatchLogRecordProcessor( + new OTLPLogExporter() + ), + instrumentations: [getNodeAutoInstrumentations(), new AzureFunctionsInstrumentation()], +}); +sdk.start(); +```` + +We also modified the +[azure-function-Go-opentelemetry-example.js](./src/functions/azure-function-Go-opentelemetry-example.js) +file to start a custom span and add a span attribute to it: + +```` +const opentelemetry = require('@opentelemetry/api'); + +const tracer = opentelemetry.trace.getTracer('azure-function-Go-opentelemetry-example', '0.1.0'); + +app.http('azure-function-Go-opentelemetry-example', { + methods: ['GET', 'POST'], + authLevel: 'anonymous', + handler: async (request, context) => { + return tracer.startActiveSpan('Go-azure-http-trigger', (span) => { + + logger.info(`Http function processed request for url "${request.url}"`); + + const name = request.query.get('name') || 'world'; + + span.setAttribute('app.name', name); + span.end(); + + return { body: `Hello, ${name}!` }; + }); + } +}); +```` + +These code changes required the `@splunk/otel`, `@opentelemetry/api` and +`@azure/functions-opentelemetry-instrumentation` packages to be installed with npm, which we can see +in the [package.json](./package.json) file: + +```` + "dependencies": { + "@azure/functions": "^4.5.0", + "@azure/functions-opentelemetry-instrumentation": "^0.1.0", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/sdk-node": "^0.56.0", + "@opentelemetry/sdk-metrics": "^1.29.0", + "@opentelemetry/sdk-logs": "^0.56.0", + "@opentelemetry/exporter-trace-otlp-http": "^0.56.0", + "@opentelemetry/exporter-metrics-otlp-http": "^0.56.0", + "@opentelemetry/exporter-logs-otlp-http": "^0.56.0", + "@opentelemetry/auto-instrumentations-node": "^0.54.0", + "pino": "^9.5.0" + }, +```` + +For this example, we'll send metrics, traces, and logs to a collector running on another virtual machine in Azure. The [local.settings.json](./local.settings.json) file was updated as follows: + +```` + "Values": { + "AzureWebJobsStorage": "UseDevelopmentStorage=true", + "FUNCTIONS_WORKER_RUNTIME": "node", + "OTEL_EXPORTER_OTLP_ENDPOINT": "http://:4318", + "OTEL_SERVICE_NAME": "azure-function-Go-opentelemetry-example", + "OTEL_RESOURCE_ATTRIBUTES": "deployment.environment=test" + } +```` + +The [host.json](./host.json) file was also updated to set the `telemetryMode` to `openTelemetry`. This +enables OpenTelemetry output from the host where the function runs: + +```` +{ + "version": "2.0", + "logging": { + "applicationInsights": { + "samplingSettings": { + "isEnabled": true, + "excludedTypes": "Request" + } + } + }, + "telemetryMode": "OpenTelemetry", + "extensionBundle": { + "id": "Microsoft.Azure.Functions.ExtensionBundle", + "version": "[4.*, 5.0.0)" + } +```` + +Note: while the above setting should be optional, during testing it was observed that +application traces don't get captured either if this setting is not included. + +## Build and Deploy + +Open the following project using Visual Studio Code: + +```` +splunk-opentelemetry-examples/instrumentation/Go/azure-functions +```` + +Then run the following command in a Visual Studio Code terminal to build the application: + +```` +go build handler.go +```` + +To run the function locally, use: + +```` +func start +```` + +Before we deploy the function to Azure, we need to compile the custom handler by +running the following command using the integrated terminal in VS Code: + +```` +GOOS=linux GOARCH=amd64 go build handler.go +```` + +Note: the above command is for Mac OS. For other operating systems, please refer +to [Compile the custom handler for Azure](https://learn.microsoft.com/en-us/azure/azure-functions/create-first-function-vs-code-other?tabs=go%2Cmacos#compile-the-custom-handler-for-azure) + +### Create a Function App in Azure + +Create a Function App in Azure if you don't already have one. For my example, +I used `opentelemetry-Go-examples` as the function name, and used the region of “West US 2” +with Go v20 as the runtime. + +![Azure Function App](./images/azure-function-app.png) + +### Create a Deployment Slot (Optional) + +By default, Azure will use a deployment slot named "Production" for an Azure Function App. +In my example, I created a deployment slot named "test". + +![Deployment Slot](./images/deployment-slot.png) + +### Set Environment Variables + +To allow OpenTelemetry to send trace data to Splunk Observability Cloud, +we need to set the `OTEL_EXPORTER_OTLP_ENDPOINT`, `OTEL_SERVICE_NAME`, +and `OTEL_RESOURCE_ATTRIBUTES` environment variables +for our Azure Function App: + +![Environment Variables](./images/env-vars.png) + +### Build and Deploy the Azure Function + +In the Azure section of Visual Studio Code, right click on the deployment slot of interest +and select `Deploy to Slot`. + +Deploy + +It will ask you to confirm: + +Confirm Deploy + +### Test the Azure Function + +Copy the function URL from the Azure function: + +Function URL + +Then point your browser to that URL, it should return: + +```` +Hello, World! +```` + +### View Traces in Splunk Observability Cloud + +After a minute or so, you should start to see traces for the serverless function +appearing in Splunk Observability Cloud: + +![Trace](./images/trace.png) + +Note that the bottom-right of the trace includes a button that links to the related log entries. + +### Add Trace Context to Logs + +Logs generated by an Azure function get sent to Application Insights. +Various methods exist for ingesting logs into Splunk platform from Application Insights, +such as the +[Splunk Add-on for Microsoft Cloud Services](https://splunkbase.splunk.com/app/3110). + +Once the logs are in Splunk platform, they can be made available to +Splunk Observability Cloud using Log Observer Connect. + +In general, logs generated by an Azure function get sent to Application Insights. +Various methods exist for ingesting logs into Splunk platform from Application Insights, +such as the +[Splunk Add-on for Microsoft Cloud Services](https://splunkbase.splunk.com/app/3110). + +In this example, OpenTelemetry Go also exports logs +to our collector using OTLP. + +By following the link from the trace show above, we can see all of the log entries associated +with this trace: + +![Related Logs](./images/related-logs.png) + +We can see that the log entries include a trace_id and span_id, which allows us to correlate +logs with traces. \ No newline at end of file diff --git a/instrumentation/go/azure-functions/go.mod b/instrumentation/go/azure-functions/go.mod new file mode 100644 index 0000000..57ddc54 --- /dev/null +++ b/instrumentation/go/azure-functions/go.mod @@ -0,0 +1,48 @@ +module example.com/azure_function_go_opentelemetry_example + +go 1.22.7 + +toolchain go1.22.10 + +require ( + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-logr/zapr v1.3.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 // indirect + github.com/signalfx/splunk-otel-go/distro v1.23.0 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect + go.opentelemetry.io/contrib/instrumentation/runtime v0.58.0 // indirect + go.opentelemetry.io/contrib/propagators/autoprop v0.58.0 // indirect + go.opentelemetry.io/contrib/propagators/aws v1.33.0 // indirect + go.opentelemetry.io/contrib/propagators/b3 v1.33.0 // indirect + go.opentelemetry.io/contrib/propagators/jaeger v1.33.0 // indirect + go.opentelemetry.io/contrib/propagators/ot v1.33.0 // indirect + go.opentelemetry.io/otel v1.33.0 // indirect + go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.9.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.33.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.33.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 // indirect + go.opentelemetry.io/otel/log v0.9.0 // indirect + go.opentelemetry.io/otel/metric v1.33.0 // indirect + go.opentelemetry.io/otel/sdk v1.33.0 // indirect + go.opentelemetry.io/otel/sdk/log v0.9.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.33.0 // indirect + go.opentelemetry.io/otel/trace v1.33.0 // indirect + go.opentelemetry.io/proto/otlp v1.4.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/net v0.32.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect + google.golang.org/grpc v1.69.0 // indirect + google.golang.org/protobuf v1.35.2 // indirect +) diff --git a/instrumentation/go/azure-functions/go.sum b/instrumentation/go/azure-functions/go.sum new file mode 100644 index 0000000..5bba497 --- /dev/null +++ b/instrumentation/go/azure-functions/go.sum @@ -0,0 +1,81 @@ +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +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/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= +github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 h1:TmHmbvxPmaegwhDubVz0lICL0J5Ka2vwTzhoePEXsGE= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0/go.mod h1:qztMSjm835F2bXf+5HKAPIS5qsmQDqZna/PgVt4rWtI= +github.com/signalfx/splunk-otel-go/distro v1.23.0 h1:cNoVuPaaPOZbwjcyI7gG2M+bCQXZNTl9ev0SFU7Pbko= +github.com/signalfx/splunk-otel-go/distro v1.23.0/go.mod h1:yDKt8QeGUMaYvSX7fRQZ9iKo6ZveSlr+L1NRD88UKVw= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= +go.opentelemetry.io/contrib/instrumentation/runtime v0.58.0 h1:GrcF8ABgnBHQFgp4zu5/jTSqLkoJ9uiDz2e7eKkjq+w= +go.opentelemetry.io/contrib/instrumentation/runtime v0.58.0/go.mod h1:+kxR5prZLoFAJVXJWZKWO2e4PY2dYyXIRNklBuOyzpM= +go.opentelemetry.io/contrib/propagators/autoprop v0.58.0 h1:pL1MMoBcG/ol6fVsjE1bbOO9A8GMQiN+T73hnmaXDoU= +go.opentelemetry.io/contrib/propagators/autoprop v0.58.0/go.mod h1:EU5uMoCqafsagp4hzFqzu1Eyg/8L23JS5Y1hChoHf7s= +go.opentelemetry.io/contrib/propagators/aws v1.33.0 h1:MefPfPIut0IxEiQRK1qVv5AFADBOwizl189+m7QhpFg= +go.opentelemetry.io/contrib/propagators/aws v1.33.0/go.mod h1:VB6xPo12uW/PezOqtA/cY2/DiAGYshnhID606wC9NEY= +go.opentelemetry.io/contrib/propagators/b3 v1.33.0 h1:ig/IsHyyoQ1F1d6FUDIIW5oYpsuTVtN16AyGOgdjAHQ= +go.opentelemetry.io/contrib/propagators/b3 v1.33.0/go.mod h1:EsVYoNy+Eol5znb6wwN3XQTILyjl040gUpEnUSNZfsk= +go.opentelemetry.io/contrib/propagators/jaeger v1.33.0 h1:Jok/dG8kfp+yod29XKYV/blWgYPlMuRUoRHljrXMF5E= +go.opentelemetry.io/contrib/propagators/jaeger v1.33.0/go.mod h1:ku/EpGk44S5lyVMbtJRK2KFOnXEehxf6SDnhu1eZmjA= +go.opentelemetry.io/contrib/propagators/ot v1.33.0 h1:xj/pQFKo4ROsx0v129KpLgFwaYMgFTu3dAMEEih97cY= +go.opentelemetry.io/contrib/propagators/ot v1.33.0/go.mod h1:/xxHCLhTmaypEFwMViRGROj2qgrGiFrkxIlATt0rddc= +go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= +go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= +go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4= +go.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.9.0 h1:Za0Z/j9Gf3Z9DKQ1choU9xI2noCxlkcyFFP2Ob3miEQ= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.9.0/go.mod h1:jMRB8N75meTNjDFQyJBA/2Z9en21CsxwMctn08NHY6c= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.33.0 h1:7F29RDmnlqk6B5d+sUqemt8TBfDqxryYW5gX6L74RFA= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.33.0/go.mod h1:ZiGDq7xwDMKmWDrN1XsXAj0iC7hns+2DhxBFSncNHSE= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.33.0 h1:bSjzTvsXZbLSWU8hnZXcKmEVaJjjnandxD0PxThhVU8= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.33.0/go.mod h1:aj2rilHL8WjXY1I5V+ra+z8FELtk681deydgYT8ikxU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojmb1U1AogINhN3SurB+zm/nIcusopeBNp42f45QM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 h1:wpMfgF8E1rkrT1Z6meFh1NDtownE9Ii3n3X2GJYjsaU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0/go.mod h1:wAy0T/dUbs468uOlkT31xjvqQgEVXv58BRFWEgn5v/0= +go.opentelemetry.io/otel/log v0.9.0 h1:0OiWRefqJ2QszpCiqwGO0u9ajMPe17q6IscQvvp3czY= +go.opentelemetry.io/otel/log v0.9.0/go.mod h1:WPP4OJ+RBkQ416jrFCQFuFKtXKD6mOoYCQm6ykK8VaU= +go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ= +go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M= +go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM= +go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM= +go.opentelemetry.io/otel/sdk/log v0.9.0 h1:YPCi6W1Eg0vwT/XJWsv2/PaQ2nyAJYuF7UUjQSBe3bc= +go.opentelemetry.io/otel/sdk/log v0.9.0/go.mod h1:y0HdrOz7OkXQBuc2yjiqnEHc+CRKeVhRE3hx4RwTmV4= +go.opentelemetry.io/otel/sdk/metric v1.33.0 h1:Gs5VK9/WUJhNXZgn8MR6ITatvAmKeIuCtNbsP3JkNqU= +go.opentelemetry.io/otel/sdk/metric v1.33.0/go.mod h1:dL5ykHZmm1B1nVRk9dDjChwDmt81MjVp3gLkQRwKf/Q= +go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= +go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= +go.opentelemetry.io/proto/otlp v1.4.0 h1:TA9WRvW6zMwP+Ssb6fLoUIuirti1gGbP28GcKG1jgeg= +go.opentelemetry.io/proto/otlp v1.4.0/go.mod h1:PPBWZIP98o2ElSqI35IHfu7hIhSwvc5N38Jw8pXuGFY= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= +golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q= +google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 h1:8ZmaLZE4XWrtU3MyClkYqqtl6Oegr3235h7jxsDyqCY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= +google.golang.org/grpc v1.69.0 h1:quSiOM1GJPmPH5XtU+BCoVXcDVJJAzNcoyfC2cCjGkI= +google.golang.org/grpc v1.69.0/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= +google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= +google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= diff --git a/instrumentation/go/azure-functions/handler.go b/instrumentation/go/azure-functions/handler.go index a3506e1..f80dd60 100644 --- a/instrumentation/go/azure-functions/handler.go +++ b/instrumentation/go/azure-functions/handler.go @@ -1,27 +1,110 @@ package main import ( + "context" "fmt" - "log" "net/http" "os" + + "github.com/signalfx/splunk-otel-go/distro" + "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp" + "go.opentelemetry.io/otel/log/global" + "go.opentelemetry.io/otel/sdk/log" + "go.opentelemetry.io/otel/trace" + "go.uber.org/zap" ) +var logger *zap.Logger +var tracer trace.Tracer + func helloHandler(w http.ResponseWriter, r *http.Request) { - message := "This HTTP triggered function executed successfully. Pass a name in the query string for a personalized response.\n" + ctx := r.Context() + loggerWithTraceContext := withTraceMetadata(ctx, logger) + loggerWithTraceContext.Info("In helloHandler()") + + message := "Hello, World!\n" name := r.URL.Query().Get("name") if name != "" { - message = fmt.Sprintf("Hello, %s. This HTTP triggered function executed successfully.\n", name) + message = fmt.Sprintf("Hello, %s!\n", name) } fmt.Fprint(w, message) } +func withTraceMetadata(ctx context.Context, logger *zap.Logger) *zap.Logger { + spanContext := trace.SpanContextFromContext(ctx) + if !spanContext.IsValid() { + // ctx does not contain a valid span. + // There is no trace metadata to add. + return logger + } + return logger.With( + zap.String("trace_id", spanContext.TraceID().String()), + zap.String("span_id", spanContext.SpanID().String()), + zap.String("trace_flags", spanContext.TraceFlags().String()), + ) +} + +func newLoggerProvider(ctx context.Context) (*log.LoggerProvider, error) { + exporter, err := otlploghttp.New(ctx) + if err != nil { + return nil, err + } + processor := log.NewBatchProcessor(exporter) + provider := log.NewLoggerProvider( + log.WithProcessor(processor), + ) + return provider, nil +} + func main() { + + ctx := context.Background() + + sdk, err := distro.Run() + if err != nil { + panic(err) + } + // Flush all spans before the application exits + defer func() { + if err := sdk.Shutdown(context.Background()); err != nil { + panic(err) + } + }() + + logger, err = zap.NewProduction() + if err != nil { + panic(err) + } + defer logger.Sync() + + tracer = otel.Tracer("azure_function_go_opentelemetry_example") + + // Create a logger provider. + // You can pass this instance directly when creating bridges. + loggerProvider, err := newLoggerProvider(ctx) + if err != nil { + panic(err) + } + + defer func() { + if err := loggerProvider.Shutdown(ctx); err != nil { + fmt.Println(err) + } + }() + + global.SetLoggerProvider(loggerProvider) + listenAddr := ":8080" if val, ok := os.LookupEnv("FUNCTIONS_CUSTOMHANDLER_PORT"); ok { listenAddr = ":" + val } - http.HandleFunc("/api/azure_function_go_opentelemetry_example", helloHandler) - log.Printf("About to listen on %s. Go to https://127.0.0.1%s/", listenAddr, listenAddr) - log.Fatal(http.ListenAndServe(listenAddr, nil)) + + // Wrap the helloHandler function. + handler := http.HandlerFunc(helloHandler) + wrappedHandler := otelhttp.NewHandler(handler, "hello") + http.Handle("/api/azure_function_go_opentelemetry_example", wrappedHandler) + logger.Info(fmt.Sprintf("About to listen on %s. Go to https://127.0.0.1%s/", listenAddr, listenAddr)) + http.ListenAndServe(listenAddr, nil) }