diff --git a/README.md b/README.md index 8274e8742..95afe4146 100644 --- a/README.md +++ b/README.md @@ -82,44 +82,48 @@ For more details, see our [quickstart guide](https://docs.odigos.io/intro). **For step-by-step instructions detailed for every destination, see these [docs](https://docs.odigos.io/backends).** -### Managed - - -| | Traces | Metrics | Logs | -| ------------------------- | -------- | --------- | ------ | -| New Relic | ✅ | ✅ | ✅ | -| Datadog | ✅ | ✅ | ✅ | -| Grafana Cloud | ✅ | ✅ | ✅ | -| Honeycomb | ✅ | ✅ | ✅ | -| Chronosphere | ✅ | ✅ | | -| Logz.io | ✅ | ✅ | ✅ | -| qryn.cloud | ✅ | ✅ | ✅ | -| OpsVerse | ✅ | ✅ | ✅ | -| Dynatrace | ✅ | ✅ | ✅ | -| AWS S3 | ✅ | ✅ | ✅ | -| Google Cloud Monitoring | ✅ | | ✅ | -| Google Cloud Storage | ✅ | | ✅ | -| Azure Blob Storage | ✅ | | ✅ | -| Splunk | ✅ | | | -| Lightstep | ✅ | | | -| Sentry | ✅ | | | -| Axiom | ✅ | | ✅ | -| Sumo Logic | ✅ | ✅ | ✅ | -| Coralogix | ✅ | ✅ | ✅ | - -### Open Source - - -| | Traces | Metrics | Logs | -| --------------- | -------- | --------- | ------ | -| Prometheus | | ✅ | | -| Tempo | ✅ | | | -| Loki | | | ✅ | -| Jaeger | ✅ | | | -| SigNoz | ✅ | ✅ | ✅ | -| qryn | ✅ | ✅ | ✅ | -| Elasticsearch | ✅ | | ✅ | -| Quickwit | ✅ | | ✅ | +### Managed Destinations + +| Destination | Traces | Metrics | Logs | +|-------------------------|:------:|:-------:|:----:| +| AppDynamics | ✅ | | | +| Axiom | ✅ | | ✅ | +| AWS S3 | ✅ | | ✅ | +| Azure Blob Storage | ✅ | | ✅ | +| Causely | ✅ | | | +| Chronosphere | ✅ | ✅ | | +| Coralogix | ✅ | ✅ | ✅ | +| Datadog | ✅ | ✅ | ✅ | +| Dynatrace | ✅ | ✅ | ✅ | +| Gigapipe | ✅ | | | +| Google Cloud Monitoring | ✅ | ✅ | | +| Google Cloud Storage | ✅ | | ✅ | +| Grafana Cloud | ✅ | ✅ | ✅ | +| Honeycomb | ✅ | ✅ | ✅ | +| Last9 | ✅ | ✅ | | +| Lightstep | ✅ | | | +| Logz.io | ✅ | ✅ | ✅ | +| New Relic | ✅ | ✅ | ✅ | +| OpsVerse | ✅ | ✅ | ✅ | +| Sentry | ✅ | | | +| Splunk | ✅ | | | +| Sumo Logic | ✅ | ✅ | ✅ | + +## Self-Hosted (Open Source) Destinations + +| Destination | Traces | Metrics | Logs | +|---------------|:------:|:-------:|:----:| +| ClickHouse | ✅ | ✅ | ✅ | +| Elasticsearch | ✅ | | ✅ | +| Jaeger | ✅ | | | +| Loki | | | ✅ | +| OTLP | ✅ | ✅ | ✅ | +| OTLP HTTP | ✅ | ✅ | ✅ | +| Prometheus | | ✅ | | +| Quickwit | ✅ | | | +| qryn | ✅ | ✅ | ✅ | +| SigNoz | ✅ | ✅ | ✅ | +| Tempo | ✅ | | | Can't find the destination you need? Help us by following our quick [add new destination](https://docs.odigos.io/adding-new-dest) guide and submitting a PR. diff --git a/cli/cmd/install.go b/cli/cmd/install.go index a4378c450..6aa9b43e3 100644 --- a/cli/cmd/install.go +++ b/cli/cmd/install.go @@ -12,16 +12,15 @@ import ( apierrors "k8s.io/apimachinery/pkg/api/errors" + "github.com/odigos-io/odigos/cli/cmd/resources" + cmdcontext "github.com/odigos-io/odigos/cli/pkg/cmd_context" + "github.com/odigos-io/odigos/cli/pkg/kube" + "github.com/odigos-io/odigos/cli/pkg/log" "github.com/odigos-io/odigos/common" "github.com/odigos-io/odigos/common/consts" "github.com/odigos-io/odigos/common/utils" k8sconsts "github.com/odigos-io/odigos/k8sutils/pkg/consts" - "github.com/odigos-io/odigos/cli/cmd/resources" - "github.com/odigos-io/odigos/cli/pkg/kube" - "github.com/odigos-io/odigos/cli/pkg/log" - cmdcontext "github.com/odigos-io/odigos/cli/pkg/cmd_context" - "github.com/spf13/cobra" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" diff --git a/cli/cmd/resources/odigosconfig.go b/cli/cmd/resources/odigosconfig.go index 2785e64a1..0adf019a4 100644 --- a/cli/cmd/resources/odigosconfig.go +++ b/cli/cmd/resources/odigosconfig.go @@ -2,7 +2,6 @@ package resources import ( "context" - "encoding/json" "github.com/odigos-io/odigos/cli/cmd/resources/resourcemanager" "github.com/odigos-io/odigos/cli/pkg/kube" @@ -10,10 +9,11 @@ import ( "github.com/odigos-io/odigos/common/consts" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/yaml" ) func NewOdigosConfiguration(ns string, config *common.OdigosConfiguration) (kube.Object, error) { - data, err := json.Marshal(config) + data, err := yaml.Marshal(config) if err != nil { return nil, err } @@ -47,6 +47,10 @@ func (a *odigosConfigResourceManager) Name() string { return "OdigosConfig" } func (a *odigosConfigResourceManager) InstallFromScratch(ctx context.Context) error { + sizingProfile := FilterSizeProfiles(a.config.Profiles) + collectorGatewayConfig := GetGatewayConfigBasedOnSize(sizingProfile) + a.config.CollectorGateway = collectorGatewayConfig + obj, err := NewOdigosConfiguration(a.ns, a.config) if err != nil { return err @@ -57,3 +61,38 @@ func (a *odigosConfigResourceManager) InstallFromScratch(ctx context.Context) er } return a.client.ApplyResources(ctx, a.config.ConfigVersion, resources) } + +func GetGatewayConfigBasedOnSize(profile common.ProfileName) *common.CollectorGatewayConfiguration { + aggregateProfiles := append([]common.ProfileName{profile}, profilesMap[profile].Dependencies...) + + for _, profile := range aggregateProfiles { + switch profile { + case sizeSProfile.ProfileName: + return &common.CollectorGatewayConfiguration{ + MinReplicas: 1, + MaxReplicas: 5, + RequestCPUm: 150, + LimitCPUm: 300, + RequestMemoryMiB: 300, + } + case sizeMProfile.ProfileName: + return &common.CollectorGatewayConfiguration{ + MinReplicas: 2, + MaxReplicas: 8, + RequestCPUm: 500, + LimitCPUm: 1000, + RequestMemoryMiB: 500, + } + case sizeLProfile.ProfileName: + return &common.CollectorGatewayConfiguration{ + MinReplicas: 3, + MaxReplicas: 12, + RequestCPUm: 750, + LimitCPUm: 1250, + RequestMemoryMiB: 750, + } + } + } + // Return nil if no matching profile is found. + return nil +} diff --git a/cli/cmd/resources/profiles.go b/cli/cmd/resources/profiles.go index 84aad0f9b..5e950f85b 100644 --- a/cli/cmd/resources/profiles.go +++ b/cli/cmd/resources/profiles.go @@ -4,7 +4,6 @@ import ( "context" "fmt" - actions "github.com/odigos-io/odigos/api/actions/v1alpha1" odigosv1alpha1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" "github.com/odigos-io/odigos/cli/cmd/resources/profiles" "github.com/odigos-io/odigos/cli/cmd/resources/resourcemanager" @@ -20,6 +19,19 @@ type Profile struct { } var ( + // sizing profiles for the collector gateway + sizeSProfile = Profile{ + ProfileName: common.ProfileName("size_s"), + ShortDescription: "Small size deployment profile", + } + sizeMProfile = Profile{ + ProfileName: common.ProfileName("size_m"), + ShortDescription: "Medium size deployment profile", + } + sizeLProfile = Profile{ + ProfileName: common.ProfileName("size_l"), + ShortDescription: "Large size deployment profile", + } fullPayloadCollectionProfile = Profile{ ProfileName: common.ProfileName("full-payload-collection"), ShortDescription: "Collect any payload from the cluster where supported with default settings", @@ -38,7 +50,7 @@ var ( semconvUpgraderProfile = Profile{ ProfileName: common.ProfileName("semconv"), ShortDescription: "Upgrade and align some attribute names to a newer version of the OpenTelemetry semantic conventions", - KubeObject: &actions.RenameAttribute{}, + KubeObject: &odigosv1alpha1.Processor{}, } categoryAttributesProfile = Profile{ ProfileName: common.ProfileName("category-attributes"), @@ -75,17 +87,37 @@ var ( } kratosProfile = Profile{ ProfileName: common.ProfileName("kratos"), - ShortDescription: "Bundle profile that includes db-payload-collection, semconv, category-attributes, copy-scope, hostname-as-podname, java-native-instrumentations, code-attributes, query-operation-detector", - Dependencies: []common.ProfileName{"db-payload-collection", "semconv", "category-attributes", "copy-scope", "hostname-as-podname", "java-native-instrumentations", "code-attributes", "query-operation-detector", "disableNameProcessorProfile", "small-batches"}, + ShortDescription: "Bundle profile that includes db-payload-collection, semconv, category-attributes, copy-scope, hostname-as-podname, java-native-instrumentations, code-attributes, query-operation-detector, disableNameProcessorProfile, small-batches, size_m", + Dependencies: []common.ProfileName{"db-payload-collection", "semconv", "category-attributes", "copy-scope", "hostname-as-podname", "java-native-instrumentations", "code-attributes", "query-operation-detector", "disableNameProcessorProfile", "small-batches", "size_m"}, + } + profilesMap = map[common.ProfileName]Profile{ + sizeSProfile.ProfileName: sizeSProfile, + sizeMProfile.ProfileName: sizeMProfile, + sizeLProfile.ProfileName: sizeLProfile, + fullPayloadCollectionProfile.ProfileName: fullPayloadCollectionProfile, + dbPayloadCollectionProfile.ProfileName: dbPayloadCollectionProfile, + queryOperationDetector.ProfileName: queryOperationDetector, + semconvUpgraderProfile.ProfileName: semconvUpgraderProfile, + categoryAttributesProfile.ProfileName: categoryAttributesProfile, + copyScopeProfile.ProfileName: copyScopeProfile, + hostnameAsPodNameProfile.ProfileName: hostnameAsPodNameProfile, + javaNativeInstrumentationsProfile.ProfileName: javaNativeInstrumentationsProfile, + codeAttributesProfile.ProfileName: codeAttributesProfile, + disableNameProcessorProfile.ProfileName: disableNameProcessorProfile, + smallBatchesProfile.ProfileName: smallBatchesProfile, + kratosProfile.ProfileName: kratosProfile, } ) func GetAvailableCommunityProfiles() []Profile { - return []Profile{semconvUpgraderProfile, copyScopeProfile, disableNameProcessorProfile} + return []Profile{semconvUpgraderProfile, copyScopeProfile, disableNameProcessorProfile, sizeSProfile, sizeMProfile, + sizeLProfile} } func GetAvailableOnPremProfiles() []Profile { - return append([]Profile{fullPayloadCollectionProfile, dbPayloadCollectionProfile, categoryAttributesProfile, hostnameAsPodNameProfile, javaNativeInstrumentationsProfile, kratosProfile, queryOperationDetector, smallBatchesProfile}, + return append([]Profile{fullPayloadCollectionProfile, dbPayloadCollectionProfile, categoryAttributesProfile, + hostnameAsPodNameProfile, javaNativeInstrumentationsProfile, kratosProfile, queryOperationDetector, + smallBatchesProfile}, GetAvailableCommunityProfiles()...) } @@ -153,3 +185,25 @@ func (a *profilesResourceManager) InstallFromScratch(ctx context.Context) error } return a.client.ApplyResources(ctx, a.config.ConfigVersion, allResources) } + +func FilterSizeProfiles(profiles []common.ProfileName) common.ProfileName { + // In case multiple size profiles are provided, the first one will be used. + + for _, profile := range profiles { + // Check if the profile is a size profile. + switch profile { + case sizeSProfile.ProfileName, sizeMProfile.ProfileName, sizeLProfile.ProfileName: + return profile + } + + // Check if the profile has a dependency which is a size profile. + profileDependencies := profilesMap[profile].Dependencies + for _, dependencyProfile := range profileDependencies { + switch dependencyProfile { + case sizeSProfile.ProfileName, sizeMProfile.ProfileName, sizeLProfile.ProfileName: + return dependencyProfile + } + } + } + return "" +} diff --git a/common/config/appdynamics.go b/common/config/appdynamics.go new file mode 100644 index 000000000..4459cb5ca --- /dev/null +++ b/common/config/appdynamics.go @@ -0,0 +1,127 @@ +package config + +import ( + "errors" + "strings" + + "github.com/odigos-io/odigos/common" +) + +const ( + APPDYNAMICS_APPLICATION_NAME = "APPDYNAMICS_APPLICATION_NAME" + APPDYNAMICS_ACCOUNT_NAME = "APPDYNAMICS_ACCOUNT_NAME" + APPDYNAMICS_ENDPOINT_URL = "APPDYNAMICS_ENDPOINT_URL" + APPDYNAMICS_API_KEY = "APPDYNAMICS_API_KEY" +) + +type AppDynamics struct{} + +func (m *AppDynamics) DestType() common.DestinationType { + // DestinationType defined in common/dests.go + return common.AppDynamicsDestinationType +} + +func (m *AppDynamics) ModifyConfig(dest ExporterConfigurer, currentConfig *Config) error { + config := dest.GetConfig() + uniqueUri := "appdynamics-" + dest.GetID() + + endpoint, endpointExists := config[APPDYNAMICS_ENDPOINT_URL] + if !endpointExists { + return errors.New("AppDynamics Endpoint URL (\"APPDYNAMICS_ENDPOINT_URL\") not specified, AppDynamics will not be configured") + } + + isHttpEndpoint := strings.HasPrefix(endpoint, "http://") + isHttpsEndpoint := strings.HasPrefix(endpoint, "https://") + + if !isHttpEndpoint && !isHttpsEndpoint { + return errors.New("AppDynamics Endpoint URL (\"APPDYNAMICS_ENDPOINT_URL\") malformed, HTTP prefix is required, AppDynamics will not be configured") + } + + accountName, accountNameExists := config[APPDYNAMICS_ACCOUNT_NAME] + if !accountNameExists { + return errors.New("AppDynamics Account Name (\"APPDYNAMICS_ACCOUNT_NAME\") not specified, AppDynamics will not be configured") + } + + applicationName, applicationNameExists := config[APPDYNAMICS_APPLICATION_NAME] + if !applicationNameExists { + applicationName = "odigos" + } + + endpointParts := strings.Split(endpoint, ".") + if len(endpointParts) > 0 { + // Replace the first part of the endpoint with the account name (instead of collecting another input from the user). + // Example: + // endpoint - "https://.saas.appdynamics.com" + // host - ".saas.appdynamics.com" + endpointParts[0] = accountName + } + host := strings.Join(endpointParts, ".") + + // Create config for exporter + + exporterName := "otlphttp/" + uniqueUri + currentConfig.Exporters[exporterName] = GenericMap{ + "endpoint": endpoint, + "headers": GenericMap{ + "x-api-key": "${APPDYNAMICS_API_KEY}", + }, + } + + // Create config for processor + + processorName := "resource/" + uniqueUri + currentConfig.Processors[processorName] = GenericMap{ + "attributes": []GenericMap{ + { + // This is required by AppDynamics, without it they will accept the data but not display it. + // This key will be used to identify the cluster in AppDynamics. + "key": "service.namespace", + "value": applicationName, + "action": "insert", + }, + { + "key": "appdynamics.controller.account", + "value": accountName, + "action": "insert", + }, + { + "key": "appdynamics.controller.host", + "value": host, + "action": "insert", + }, + { + "key": "appdynamics.controller.port", + "value": 443, + "action": "insert", + }, + }, + } + + // Apply configs to serivce + + if isTracingEnabled(dest) { + tracesPipelineName := "traces/" + uniqueUri + currentConfig.Service.Pipelines[tracesPipelineName] = Pipeline{ + Exporters: []string{exporterName}, + Processors: []string{processorName}, + } + } + + if isMetricsEnabled(dest) { + metricsPipelineName := "metrics/" + uniqueUri + currentConfig.Service.Pipelines[metricsPipelineName] = Pipeline{ + Exporters: []string{exporterName}, + Processors: []string{processorName}, + } + } + + if isLoggingEnabled(dest) { + logsPipelineName := "logs/" + uniqueUri + currentConfig.Service.Pipelines[logsPipelineName] = Pipeline{ + Exporters: []string{exporterName}, + Processors: []string{processorName}, + } + } + + return nil +} diff --git a/common/config/root.go b/common/config/root.go index 643cd55ec..43a0ef7d7 100644 --- a/common/config/root.go +++ b/common/config/root.go @@ -15,7 +15,7 @@ const ( ) var availableConfigers = []Configer{ - &Middleware{}, &Honeycomb{}, &GrafanaCloudPrometheus{}, &GrafanaCloudTempo{}, + &Middleware{}, &AppDynamics{}, &Honeycomb{}, &GrafanaCloudPrometheus{}, &GrafanaCloudTempo{}, &GrafanaCloudLoki{}, &Datadog{}, &NewRelic{}, &Logzio{}, &Last9{}, &Prometheus{}, &Tempo{}, &Loki{}, &Jaeger{}, &GenericOTLP{}, &OTLPHttp{}, &Elasticsearch{}, &Quickwit{}, &Signoz{}, &Qryn{}, &OpsVerse{}, &Splunk{}, &Lightstep{}, &GoogleCloud{}, &GoogleCloudStorage{}, &Sentry{}, &AzureBlobStorage{}, diff --git a/common/dests.go b/common/dests.go index 5c2f1dad5..99a017ab4 100644 --- a/common/dests.go +++ b/common/dests.go @@ -3,6 +3,7 @@ package common type DestinationType string const ( + AppDynamicsDestinationType DestinationType = "appdynamics" AWSS3DestinationType DestinationType = "s3" AxiomDestinationType DestinationType = "axiom" AzureBlobDestinationType DestinationType = "azureblob" diff --git a/destinations/data/appdynamics.yaml b/destinations/data/appdynamics.yaml new file mode 100644 index 000000000..0c59c6ec6 --- /dev/null +++ b/destinations/data/appdynamics.yaml @@ -0,0 +1,41 @@ +apiVersion: internal.odigos.io/v1beta1 +kind: Destination +metadata: + type: appdynamics + displayName: AppDynamics + category: managed +spec: + image: appdynamics.svg + signals: + traces: + supported: true + metrics: + supported: false + logs: + supported: false + fields: + - name: APPDYNAMICS_APPLICATION_NAME + displayName: Application Name + componentType: input + componentProps: + type: text + required: false + - name: APPDYNAMICS_ACCOUNT_NAME + displayName: Account Name + componentType: input + componentProps: + type: text + required: true + - name: APPDYNAMICS_ENDPOINT_URL + displayName: Endpoint URL + componentType: input + componentProps: + type: text + required: true + - name: APPDYNAMICS_API_KEY + displayName: API Key + componentType: input + secret: true + componentProps: + type: password + required: true diff --git a/destinations/logos/appdynamics.svg b/destinations/logos/appdynamics.svg new file mode 100644 index 000000000..9c72f133a --- /dev/null +++ b/destinations/logos/appdynamics.svg @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/docs/backends-overview.mdx b/docs/backends-overview.mdx index 9413fb928..6ae47d8c6 100644 --- a/docs/backends-overview.mdx +++ b/docs/backends-overview.mdx @@ -1,33 +1,43 @@ --- -title: "Overview" +title: 'Overview' --- Odigos has destinations for many observability backends. -| | Traces | Metrics | Logs | Open Source | -| ----------------------- | :---: | :-----: | :--: | :---------: | -| SigNoz | ✅ | ✅ | ✅ | ✅ | -| qryn | ✅ | ✅ | ✅ | ✅ | -| Uptrace | ✅ | ✅ | ✅ | ✅ | -| Elasticsearch | ✅ | | ✅ | ✅ | -| Jaeger | ✅ | | | ✅ | -| Tempo | ✅ | | | ✅ | -| Prometheus | | ✅ | | ✅ | -| Loki | | | ✅ | ✅ | -| Datadog | ✅ | ✅ | ✅ | | -| Grafana Cloud | ✅ | ✅ | ✅ | | -| Honeycomb | ✅ | ✅ | ✅ | | -| Logz.io | ✅ | ✅ | ✅ | | -| New Relic | ✅ | ✅ | ✅ | | -| OpsVerse | ✅ | ✅ | ✅ | | -| qryn.cloud | ✅ | ✅ | ✅ | | -| Causely | ✅ | ✅ | | | -| AWS S3 | ✅ | | ✅ | | -| Azure Blob Storage | ✅ | | ✅ | | -| Google Cloud Monitoring | ✅ | | ✅ | | -| Google Cloud Storage | ✅ | | ✅ | | -| Lightstep | ✅ | | | | -| Sentry | ✅ | | | | -| Splunk | ✅ | | | | +| Destination | Category | Traces | Metrics | Logs | +|-------------------------|------------------|:------:|:-------:|:----:| +| AppDynamics | Managed | ✅ | | | +| Axiom | Managed | ✅ | | ✅ | +| AWS S3 | Managed | ✅ | | ✅ | +| Azure Blob Storage | Managed | ✅ | | ✅ | +| Causely | Managed | ✅ | | | +| Chronosphere | Managed | ✅ | ✅ | | +| ClickHouse | Self-Hosted | ✅ | ✅ | ✅ | +| Coralogix | Managed | ✅ | ✅ | ✅ | +| Datadog | Managed | ✅ | ✅ | ✅ | +| Dynatrace | Managed | ✅ | ✅ | ✅ | +| Elasticsearch | Self-Hosted | ✅ | | ✅ | +| Gigapipe | Managed | ✅ | | | +| Google Cloud Monitoring | Managed | ✅ | ✅ | | +| Google Cloud Storage | Managed | ✅ | | ✅ | +| Grafana Cloud | Managed | ✅ | ✅ | ✅ | +| Honeycomb | Managed | ✅ | ✅ | ✅ | +| Jaeger | Self-Hosted | ✅ | | | +| Last9 | Managed | ✅ | ✅ | | +| Lightstep | Managed | ✅ | | | +| Loki | Self-Hosted | | | ✅ | +| Logz.io | Managed | ✅ | ✅ | ✅ | +| New Relic | Managed | ✅ | ✅ | ✅ | +| OpsVerse | Managed | ✅ | ✅ | ✅ | +| OTLP | Managed | ✅ | ✅ | ✅ | +| OTLP HTTP | Managed | ✅ | ✅ | ✅ | +| Prometheus | Self-Hosted | | ✅ | | +| Quickwit | Self-Hosted | ✅ | | | +| qryn | Self-Hosted | ✅ | ✅ | ✅ | +| Sentry | Managed | ✅ | | | +| SigNoz | Self-Hosted | ✅ | ✅ | ✅ | +| Splunk | Managed | ✅ | | | +| Sumo Logic | Managed | ✅ | ✅ | ✅ | +| Tempo | Self-Hosted | ✅ | | | Can't find the destination you need? Help us by following our quick [adding new destination](/adding-new-dest) guide and submit a PR. diff --git a/docs/backends/appdynamics.mdx b/docs/backends/appdynamics.mdx new file mode 100644 index 000000000..08b9576af --- /dev/null +++ b/docs/backends/appdynamics.mdx @@ -0,0 +1,71 @@ +--- +title: 'AppDynamics' +--- + +## Configuring AppDynamics Backend + +1. [Register](https://accounts.appdynamics.com/trial)/[Login](https://login.appdynamics.com/sso/authenticate) to AppDynamics. +2. Navigate to the OpenTelemetry Configuration page: + +```bash +https://{APPDYNAMICS_ACCOUNT_NAME}.saas.appdynamics.com/controller/#/apm/otel/keyManagement +``` + +3. Click on `Processors`, you'll find 3 attributes, locate `appdynamics.controller.account` and copy it's value, this is your `Account Name`. +4. Click on `Exporters`, copy the `Endpoint`, and generate an `API Key`. + +- **APPDYNAMICS_APPLICATION_NAME** - Application Name (will define a namespace in AppDynamics) +- **APPDYNAMICS_ACCOUNT_NAME** - Account Name from above (step 3). +- **APPDYNAMICS_ENDPOINT_URL** - OTLP HTTP Endpoint URL from above (step 4). +- **APPDYNAMICS_API_KEY** - API Key from above (step 4). + +## Adding a destination to Odigos + +Odigos makes it simple to add and configure destinations, allowing you to select the specific signals [traces/logs/metrics] that you want to send to each destination. There are two primary methods for configuring destinations in Odigos: + +1. **Using the UI** + +- Use the [Odigos CLI](https://docs.odigos.io/cli/odigos_ui) to access the UI: + +```bash +odigos ui +``` + +2. **Using kubernetes manifests** + +Save the YAML below to a file (e.g., `destination.yaml`) and apply it using `kubectl`: + +```bash +kubectl apply -f destination.yaml +``` + + +```yaml +apiVersion: odigos.io/v1alpha1 +kind: Destination +metadata: + name: appdynamics-example + namespace: odigos-system +spec: + data: + APPDYNAMICS_ACCOUNT_NAME: + # APPDYNAMICS_APPLICATION_NAME: + # Note: The commented fields above are optional. + APPDYNAMICS_ENDPOINT_URL: + destinationName: appdynamics + secretRef: + name: appdynamics-secret + signals: + - TRACES + type: appdynamics + +--- +apiVersion: v1 +data: + APPDYNAMICS_API_KEY: +kind: Secret +metadata: + name: appdynamics-secret + namespace: odigos-system +type: Opaque +``` \ No newline at end of file diff --git a/docs/mint.json b/docs/mint.json index 32718b0c8..c30d9ac29 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -200,6 +200,7 @@ { "group": "Supported Backends", "pages": [ + "backends/appdynamics", "backends/s3", "backends/azureblob", "backends/causely", @@ -217,8 +218,8 @@ "backends/grafanacloudtempo", "backends/honeycomb", "backends/jaeger", - "backends/lightstep", "backends/last9", + "backends/lightstep", "backends/logzio", "backends/loki", "backends/newrelic", diff --git a/frontend/webapp/hooks/common/useTransition.tsx b/frontend/webapp/hooks/common/useTransition.tsx index beae4cf62..cbc3ede01 100644 --- a/frontend/webapp/hooks/common/useTransition.tsx +++ b/frontend/webapp/hooks/common/useTransition.tsx @@ -29,6 +29,8 @@ export const useTransition = ({ container, animateIn, animateOut, duration = 300 return () => clearTimeout(t); }, [enter, duration]); + if (!enter && !mounted) return null; + return ( {children}