Updated on 2025-03-01 GMT+08:00

Connecting to DataDog with an Extension

This section describes how to use extension APIs to report monitoring data to the DataDog platform.

Preparations

  • Register an account at the DataDog official website. Obtain necessary configuration information including the target domain name, API key, and app key for data reporting.
  • For functions that need to be extended, enable public access for them. For details, see Configuring Public Access for a Function.

Connecting a Function to DataDog

  1. Compile the extension package code.

    The following code example shows how to use an extension API to report the functiongraph.invoke.count metric to the DataDog platform to record the number of function calls. You can refer to this example to develop an extension package.
    package main
     
    import (
        "context"
        "encoding/json"
        "errors"
        "fmt"
        "io"
        "net/http"
        "os"
        "time"
     
        "github.com/DataDog/datadog-api-client-go/v2/api/datadog"
        "github.com/DataDog/datadog-api-client-go/v2/api/datadogV2"
    )
     
    const (
        registerURL    = "/extension/register"
        invokeURL      = "/extension/invoke"
        requestTimeout = 5 * time.Second
        extensionName  = "FunctionGraph-Extension-Name"
        extensionAddr  = "FunctionGraph-Extension-Address"
     
        currentExtensionAddr = "127.0.0.1:8081"
        currentExtensionName = "extension_1.sh"
    )
     
    var (
        extensionApiAddr = os.Getenv("EXTENSION_API_ADDR")
    )
     
    type ExtensionRequest struct {
        TraceID string `json:"traceId" valid:"optional"`
        URN     string `json:"invokedFunctionUrn" valid:"optional"`
    }
     
    func init() {
        // Set your DataDog site address
        os.Setenv("DD_SITE", "your_datadog_site")
        // Set your DataDog api key
        os.Setenv("DD_API_KEY", "your_datadog_api_key")
        // Set your DataDog app key
        os.Setenv("DD_APP_KEY", "your datadog_app_key")
    }
     
    func main() {
        err := registerExtension(currentExtensionName, currentExtensionAddr)
        if err != nil {
            fmt.Printf("failed to register extension, error: %s \n", err.Error())
            return
        }
     
        http.HandleFunc(invokeURL, ServeHTTP)
        err = http.ListenAndServe(currentExtensionAddr, nil)
        if err != nil {
            fmt.Printf("http server error, error: %s \n", err.Error())
            return
        }
     
        return
     
    }
     
    func getHTTPClient() *http.Client {
        httpClient := &http.Client{
            Timeout: requestTimeout,
        }
        return httpClient
    }
     
    func registerExtension(name, address string) error {
        if len(extensionApiAddr) == 0 {
            return errors.New("failed to get extension api addr")
        }
        baseURLPath := "http://" + extensionApiAddr + registerURL
        req, err := http.NewRequest("POST", baseURLPath, nil)
        if err != nil {
            fmt.Printf("failed to build register request, error: %s\n", err.Error())
            return err
        }
        req.Header.Set("Content-Type", "application/json")
        req.Header.Set(extensionName, currentExtensionName)
        req.Header.Set(extensionAddr, currentExtensionAddr)
        _, err = getHTTPClient().Do(req)
        if err != nil {
            fmt.Printf("failed to send register request, error: %s\n", err.Error())
            return err
        }
        return nil
    }
     
    func ServeHTTP(writer http.ResponseWriter, request *http.Request) {
        req := ExtensionRequest{}
        defer request.Body.Close()
        b, err := io.ReadAll(request.Body)
        if err != nil {
            fmt.Printf("failed to parse invoke request body with error: %s \n", err.Error())
            writeHeaders(writer, 500, "failed to parse invoke request body")
            return
        }
        if err := json.Unmarshal(b, &req); err != nil {
            fmt.Printf("failed to unmarshal the json, error: %s", err.Error())
            writeHeaders(writer, 500, "failed to unmarshal the json")
            return
        }
     
        fmt.Println(" invoke request traceID -- ", req.TraceID)
        fmt.Println(" invoke request URN -- ", req.URN)
        sendMetricToMyDataDog(req.URN, req.TraceID)
        writeHeaders(writer, 200, "succeed to get invoke request")
        return
    }
     
    func writeHeaders(w http.ResponseWriter, status int, msg string) error {
        w.WriteHeader(status)
        _, err := w.Write([]byte(msg))
        return err
    }
     
    func sendMetricToMyDataDog(urn string, traceId string) {
        // Assemble your metric data
        body := datadogV2.MetricPayload{
            Series: []datadogV2.MetricSeries{
                {
                    Metric: "functiongraph.invoke.count",
                    Type:   datadogV2.METRICINTAKETYPE_UNSPECIFIED.Ptr(),
                    Points: []datadogV2.MetricPoint{
                        {
                            Timestamp: datadog.PtrInt64(time.Now().Unix()),
                            Value:     datadog.PtrFloat64(1),
                        },
                    },
                    Resources: []datadogV2.MetricResource{
                        {
                            Name: datadog.PtrString(urn),
                            Type: datadog.PtrString("function_urn"),
                        },
                        {
                            Name: datadog.PtrString(traceId),
                            Type: datadog.PtrString("trace_id"),
                        },
                    },
                },
            },
        }
        ctx := datadog.NewDefaultContext(context.Background())
        configuration := datadog.NewConfiguration()
        apiClient := datadog.NewAPIClient(configuration)
        api := datadogV2.NewMetricsApi(apiClient)
        resp, r, err := api.SubmitMetrics(ctx, body, *datadogV2.NewSubmitMetricsOptionalParameters())
     
        if err != nil {
            fmt.Fprintf(os.Stderr, "Error when calling `MetricsApi.SubmitMetrics`: %v\n", err)
            fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r)
        }
     
        responseContent, _ := json.MarshalIndent(resp, "", "  ")
        fmt.Fprintf(os.Stdout, "Response from `MetricsApi.SubmitMetrics`:\n%s\n", responseContent)
    }

  2. Create an extension dependency.

    For details, see How Do I Create Function Dependencies? datadog-extension is an example dependency, which can be downloaded and used.

  3. Configure the function extension dependency.

    Configure the dependency for the function. For details, see Configuring Dependency Packages.

  4. Trigger the function and log in to the DataDog to view the reported metrics.

    Figure 1 Logging in to the DataDog and viewing the reported metrics