> ## Documentation Index
> Fetch the complete documentation index at: https://docs.odigos.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Adding New Observability Destination

> There are tens if not hundreds of different observability destinations. Odigos's goal is to provide a seamless and easy way to ship observability data to any one of them.

In this guide, you will learn how to contribute a new destination to Odigos. We will create a new dummy destination called `mydest`.

Creating a new destination involves these steps:

1. Extending the UI for the new destination
2. Adding the collector configuration for the new destination
3. Generating the documentation for the new destination

### User Interface

For our new destination to be visible in the UI, we need to define the destination metadata and fields in YAML format:

<Steps>
  <Step title="Add Logo">
    Go to [`destinations/logos/`](https://github.com/odigos-io/odigos/tree/main/destinations/logos) directory and add your logo.

    * Must be in SVG format
    * Example: `mydest.svg`

    ```svg mydest.svg theme={null}
    <svg fill="#F5F5F5" width="16" height="12" viewBox="0 0 16 12" xmlns="http://www.w3.org/2000/svg">
      <title>My Destination</title>
      <g>
        <path d="M5.17215 9.01884L2.18377 7.69813C1.48159 7.38732 1.0448 6.73677 1.0448 5.99948C1.0448 5.2622 1.48056 4.61165 2.18377 4.30083L6.36172 2.45349L6.45156 2.41425V0H1.66127C1.0417 0 0.538818 0.4781 0.538818 1.06772V10.9312C0.538818 11.5209 1.0417 11.999 1.66127 11.999H5.83303H6.45259V9.5682L5.17319 9.01781H5.17215V9.01884Z"/>
        <path d="M14.3397 0H9.5484V2.4411L10.8041 2.98115L10.801 2.9698L13.8162 4.3029C14.5184 4.61372 14.9552 5.26426 14.9552 6.00155C14.9552 6.73883 14.5194 7.38938 13.8162 7.7002L10.1649 9.31417L9.54736 9.57646V12H14.3387C14.9583 12 15.4611 11.5219 15.4611 10.9323V1.06772C15.4611 0.4781 14.9583 0 14.3387 0H14.3397Z"/>
        <path d="M8.00048 8.47972C9.36972 8.47972 10.4808 7.36966 10.4808 5.99938C10.4808 4.6291 9.37076 3.51904 8.00048 3.51904C6.6302 3.51904 5.52014 4.6291 5.52014 5.99938C5.52014 7.36966 6.6302 8.47972 8.00048 8.47972Z"/>
      </g>
    </svg>
    ```
  </Step>

  <Step title="Add Destination Metadata">
    Go to [`destinations/data/`](https://github.com/odigos-io/odigos/tree/main/destinations/data) directory and create a new YAML file.

    * Example: `mydest.yaml`

    <AccordionGroup>
      <Accordion icon="circle-info" title="Allowed properties">
        | Key                            | Example                                                               | Required | Description                                                                                                                                      | Docs |
        | ------------------------------ | --------------------------------------------------------------------- | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------ | :--: |
        | metadata.type                  | `mydest`                                                              |     ✅    | unique identifier for the destination                                                                                                            |   ✅  |
        | metadata.displayName           | `My Destination`                                                      |     ✅    | display name of the destination                                                                                                                  |   ✅  |
        | metadata.category              | `managed`                                                             |     ✅    | one-of: `managed` `self hosted`                                                                                                                  |   ✅  |
        | spec.image                     | `mydest.svg`                                                          |     ✅    | logo of the destination                                                                                                                          |   ✅  |
        | spec.signals.traces.supported  | `true`                                                                |     ✅    | is tracing supported?                                                                                                                            |   ✅  |
        | spec.signals.metrics.supported | `true`                                                                |     ✅    | is metrics supported?                                                                                                                            |   ✅  |
        | spec.signals.logs.supported    | `false`                                                               |     ✅    | is logging supported?                                                                                                                            |   ✅  |
        | spec.note.type                 | `Note`                                                                |     ❌    | type of [callout box](https://mintlify.com/docs/content/components/callouts) to display<br /><br />one-of: `Note` `Info` `Warning` `Tip` `Check` |   ✅  |
        | spec.note.content              | `We handle the endpoint internally, so you don't need to provide it.` |     ❌    | content of the callout box                                                                                                                       |   ✅  |
        | spec.fields                    | -                                                                     |     ✅    | list of fields to be configured, [see specification here](#allowed-properties-for-spec-fields)                                                   |   ✅  |
      </Accordion>

      <Accordion icon="circle-info" title="Allowed properties for spec.fields[]">
        | Key                        | Example                                | Required | Description                                                                                                                                                                                                                      | Docs |
        | -------------------------- | -------------------------------------- | :------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--: |
        | name                       | `MYDEST_API_KEY`                       |     ✅    | key name of the field                                                                                                                                                                                                            |   ✅  |
        | displayName                | `API Key`                              |     ✅    | display name of the field                                                                                                                                                                                                        |   ✅  |
        | secret                     | `true`                                 |     ❌    | is the field a secret? (e.g. password, token, etc.)                                                                                                                                                                              |   ❌  |
        | initialValue               | -                                      |     ❌    | default value for the field                                                                                                                                                                                                      |   ✅  |
        | componentType              | `input`                                |     ✅    | one-of: `input` `textarea` `dropdown` `checkbox` `multiInput` `keyValuePairs`                                                                                                                                                    |   ✅  |
        | componentProps.type        | `password`                             |     ❌    | the type of value (e.g. string, number, password, etc.)                                                                                                                                                                          |   ✅  |
        | componentProps.required    | `true`                                 |     ❌    | is the field required?                                                                                                                                                                                                           |   ✅  |
        | componentProps.values      | -                                      |     ❌    | list of options for dropdown                                                                                                                                                                                                     |   ❌  |
        | componentProps.placeholder | `glc_eyJvIj...`                        |     ❌    | placeholder text for input                                                                                                                                                                                                       |   ✅  |
        | componentProps.tooltip     | `Obtained from your "My Dest" account` |     ❌    | tooltip text for input                                                                                                                                                                                                           |   ✅  |
        | renderCondition            | `["MYDEST_URL", "!=", ""]`             |     ❌    | hide/show the field in the UI (form only)<br /><br />one-of: `[boolean]`<br />`[string, operator, any]`<br /><br />where `string` is the key name of the dependency field, and `operator` is one-of: `!=` `==` `>=` `<=` `>` `<` |   ❌  |
        | hideFromReadData           | `["MYDEST_URL", "==", ""]`             |     ❌    | hide/show the field in the UI (read only)<br /><br />one-of: `[boolean]`<br />`[string, operator, any]`<br /><br />where `string` is the key name of the dependency field, and `operator` is one-of: `!=` `==` `>=` `<=` `>` `<` |   ❌  |
        | customReadDataLabels       | -                                      |     ❌    | custom labels for the UI (read only)                                                                                                                                                                                             |   ❌  |
      </Accordion>
    </AccordionGroup>

    ```yaml mydest.yaml theme={null}
    apiVersion: internal.odigos.io/v1beta1
    kind: Destination
    metadata:
      type: mydest
      displayName: My Destination
      category: managed
    spec:
      image: mydest.svg
      signals:
        traces:
          supported: true
        metrics:
          supported: true
        logs:
          supported: false
      fields:
        - name: MYDEST_URL
          displayName: URL
          componentType: input
          componentProps:
            required: true
        - name: MYDEST_REGION
          displayName: Region
          componentType: input
          componentProps:
            required: true
        - name: MYDEST_API_KEY
          displayName: API Key
          componentType: input
          componentProps:
            type: password
            required: true
          secret: true
    ```
  </Step>
</Steps>

### Collector Configuration

Now that our new vendor can be persisted/loaded in the Kubernetes data store, we need to implement the collector configuration.

<Steps>
  <Step title="Add Destination Type">
    Go to [`common/dests.go`](https://github.com/odigos-io/odigos/tree/main/common/dests.go) and add your new destination to the `DestinationType` enum.

    * Make sure the value is the same as the `type` property in `mydest.yaml`
  </Step>

  <Step title="Add Collector Config">
    Go to [`common/config`](https://github.com/odigos-io/odigos/tree/main/common/config) directory and create a new GO file.

    * Example: `mydest.go`

    <AccordionGroup>
      <Accordion icon="circle-info" title="Explain Config">
        - **DestType** - Returns the enum value of the destination added to [`common/dests.go`](https://github.com/odigos-io/odigos/tree/main/common/dests.go) in step 1.
        - **ModifyConfig** - Is called with the `dest` object which holds the data received from the UI and the `currentConfig` object. The `currentConfig` object contains the current configuration of the gateway collector. Modify this object to include the [OpenTelemetry configuration](https://opentelemetry.io/docs/collector/configuration/) needed by your destination. You can assume a basic configuration is already provided in the `currentConfig` object, for details see `getBasicConfig` method in [`common/config/root.go`](https://github.com/odigos-io/odigos/tree/main/common/config/root.go) file.
          * Make sure to give any exporter or pipeline a unique name in order to avoid conflicts, use the conventions:
            * `otlp/<dest-name>-<dest.GetID()>` for OTel gRPC exporters
            * `otlphttp/<dest-name>-<dest.GetID()>` for OTel HTTP exporters
            * `traces/<dest-name>-<dest.GetID()>` for traces pipelines
            * `metrics/<dest-name>-<dest.GetID()>` for metrics pipelines
            * `logs/<dest-name>-<dest.GetID()>` for logs pipelines
          * You can use the utility methods `isTracingEnabled`, `isMetricsEnabled` and `isLoggingEnabled` to determine which signals are selected by the user for the destination, and configure the collector accordingly.
      </Accordion>
    </AccordionGroup>

    ```go mydest.go theme={null}
    package config

    import (
      "fmt"

      "github.com/odigos-io/odigos/common"
    )

    const (
      regionKey = "MYDEST_REGION"
      urlKey    = "MYDEST_URL"
      apiKeyKey = "MYDEST_API_KEY"
    )

    type MyDest struct{}

    func (m *MyDest) DestType() common.DestinationType {
      // DestinationType defined in common/dests.go
      return common.MyDestDestinationType
    }

    func (m *MyDest) ModifyConfig(dest ExporterConfigurer, currentConfig *Config) ([]string, error) {
      config := dest.GetConfig()
      // To make sure that the exporter and pipeline names are unique, we'll need to define a unique ID
      uniqueUri := "mydest-" + dest.GetID()

      region, exists := config[regionKey]
      if !exists {
        region = "us"
        return errorMissingKey(regionKey)
      }

      endpoint, exists := config[urlKey]
      if !exists {
        endpoint = fmt.Sprintf("https://%s.mydest.com:4317", region)
      }

      // Modify the exporter here
      exporterName := "otlp/" + uniqueUri
      exporterConfig := GenericMap{
        "endpoint": endpoint,
        "headers": GenericMap{
          // The API Key is a secret, so we can't expose it in the config
          "x-mydest-header-apikey": "${MYDEST_API_KEY}",
        },
      }

      currentConfig.Exporters[exporterName] = exporterConfig

      // Modify the pipelines here
      var pipelineNames []string

      if isTracingEnabled(dest) {
        pipeName := "traces/" + uniqueUri
        currentConfig.Service.Pipelines[pipeName] = Pipeline{
          Exporters: []string{exporterName},
        }
        pipelineNames = append(pipelineNames, pipeName)
      }

      if isMetricsEnabled(dest) {
        pipeName := "metrics/" + uniqueUri
        currentConfig.Service.Pipelines[pipeName] = Pipeline{
          Exporters: []string{exporterName},
        }
        pipelineNames = append(pipelineNames, pipeName)
      }

      if isLoggingEnabled(dest) {
        pipeName := "logs/" + uniqueUri
        currentConfig.Service.Pipelines[pipeName] = Pipeline{
          Exporters: []string{exporterName},
        }
        pipelineNames = append(pipelineNames, pipeName)
      }

      return pipelineNames, nil
    }
    ```
  </Step>

  <Step title="Add Available Config">
    Go to [`common/config/root.go`](https://github.com/odigos-io/odigos/tree/main/common/config/root.go) and add the new destination to the `availableConfigers` list.

    * Example: `&MyDest{}`

    ```go common/config/root.go theme={null}
    var availableConfigers = []Configer{
      &MyDest{},
      /* List of existing destinations...  */
    }
    ```
  </Step>
</Steps>

### Kubernetes-Specific Collector Configuration

If your destination configures Kubernetes settings, for example you have a config field that accepts a value that should be mounted
in the Collector Pod, create a `K8sConfiger` object in [`destinations.k8sconfig`](https://github.com/odigos-io/odigos/tree/main/autoscaler/k8sconfig).

This interface provides an additional function, `ModifyGatewayCollectorDeployment`, which accepts a Destination and Collector Deployment.
Your implementation can use this function to modify the Deployment as necessary based on the Destination config.

Note: It is the responsibility of your implementation to check for the validity of this config, such as checking for conflicts or values
that already exist.

The `K8sConfiger` interface also requires a `DestType()` function, similar to common Destinations. This function returns the type
of common Destination that this Kubernetes implementation relies on. This is used by the Autoscaler to conditionally apply the `K8sConfiger`
only for the matching Destination.

When it is implemented, add your `K8sConfiger` implementation to the `availableConfigers` list in
[`destinations/k8sconfig/root.go`](https://github.com/odigos-io/odigos/tree/main/autoscaler/k8sconfig/root.go)

### Generating Documentation

<Steps>
  <Step title="Install Python">
    Make sure you have [Python installed](https://www.python.org/downloads/) on your system.
  </Step>

  <Step title="Generate Documentation">
    Go to [`docs`](https://github.com/odigos-io/odigos/tree/main/docs) directory, and run:

    ```bash theme={null}
    python sync-dest-doc.py
    # OR
    python3 sync-dest-doc.py
    ```
  </Step>

  <Step title="Edit Documentation">
    The documentation has been generated in the [`docs/backends`](https://github.com/odigos-io/odigos/tree/main/docs/backends) directory. Feel free to add any additional content to the generated file.

    * Edit only between the `!! START CUSTOM EDIT !!` and `!! END CUSTOM EDIT !!` comments
    * Example: `mydest.mdx`

    ```mdx mydest.mdx theme={null}
    {/*
        !! Do not remove this comment, this acts as a key indicator in `docs/sync-dest-doc.py` !!
        !! START CUSTOM EDIT !!
    */}

    **Creating Account**<br />
    Go to the **[🔗 website](https://odigos.io) > Account** and click **Sign Up**

    **Obtaining Access Token**<br />
    Go to **⚙️ > Access Tokens** and click **Create New**

    {/*
        !! Do not remove this comment, this acts as a key indicator in `docs/sync-dest-doc.py` !!
        !! END CUSTOM EDIT !!
    */}
    ```
  </Step>

  <Step title="Update README.md">
    Go to [`README.md`](https://github.com/odigos-io/odigos/tree/main/README.md) and add your new destination to the list of destinations.
  </Step>
</Steps>

#### That's it! Now you can use your new destination in the UI and send data to it.

**Please submit a PR to the [odigos git repository](https://github.com/odigos-io/odigos).**
