Denodo Observability with OpenTelemetry

You can translate the document:

Introduction

This document aims to demonstrate a sample implementation of OpenTelemetry observability for the Denodo Platform using the OpenTelemetry Java Agent.

You can see the architecture this document will follow below:

The OpenTelemetry Java Agent automatically adds monitoring code to your running Java application by modifying its bytecode at startup, acting like an invisible sensor system that tracks performance, errors, and behavior without you having to change any source code. We will use this Agent because Denodo is built with Java.

NOTE: you can set up the Java Agent with any of the Denodo Platform components like the Scheduler Server, Solution Manager Server, and so on. In this article we will set up the Java Agent for the Virtual DataPort Server.

The OpenTelemetry Collector is a central service that receives, processes, and forwards monitoring data (traces, metrics, and logs) from your applications to your monitoring systems like Prometheus, Loki, and Tempo, acting as a reliable middleman that can handle data transformation and buffering.

There are 3 main signal types in OpenTelemetry:

Traces

Record how a request moves through your system step by step, measuring how long each part takes and showing which services and operations were involved in handling it.

In this document we will connect the OpenTelemetry Collector to Grafana Tempo in order to store and query the traces.

IMPORTANT NOTE: Support for trace collection will be added in Denodo 9.2.

Metrics

Numbers that show how your system is running right now, like how many requests per second it handles, how much memory it uses, and how high your CPU usage is. We will use Prometheus as our metrics database.

Logs

Detailed text records that show exactly what your application is doing at each moment, including what went wrong during errors, with extra data attached to help find and fix problems. We will use Loki to store and help us search through these logs.

Finally, we will set up Grafana as our visualization frontend.

Alternative Backends

While this guide demonstrates the setup of Tempo (traces), Prometheus (metrics), and Loki (logs) as backend systems, OpenTelemetry allows for various alternative backends. The OpenTelemetry Collector can be configured to export data to many different observability systems, such as:

  • Traces: Jaeger, Zipkin, AWS X-Ray…
  • Metrics: Victoria Metrics, InfluxDB, Datadog…
  • Logs: Promtail, Fluentd, Elastic Logstash…

The configuration provided in this guide can be adapted to work with these alternatives by modifying the collector's exporter configuration.

Setting up the OpenTelemetry Java Agent

To configure the Java OpenTelemetry agent to run along with our VDP server, we will include the following Java properties in the java.env.DENODO_OPTS_START property in the <DENODO_HOME>/conf/VDBConfiguration.properties file:

  • -Dotel.service.name\=<arbitrary_service_name>
  • Defines the name of your service in the observability system

  • -Dotel.service.version\=<arbitrary_version_value>
  • Specifies the version of your service, e.g. 1.0

  • -Dotel.deployment.environment\=<deployment_environment>
  • Indicates the deployment environment (development, production, etc.) which helps in filtering telemetry data by environment

  • -javaagent:<path/to/opentelemetry-javaagent.jar>
  • Specifies the path to the OpenTelemetry Java agent

  • -Dotel.exporter.otlp.endpoint\=<endpoint_for_sending_data>

  • -Dotel.logs.exporter\=otlp
  • Configures the logs to be exported using the OTLP (OpenTelemetry Protocol) format, that is the standardized data format/protocol OpenTelemetry uses
  • Note that you do not have to explicitly set the exporter for metrics and traces when using OTLP

  • -Dotel.exporter.otlp.logs.endpoint\=<endpoint_for_sending_data>
  • Defines the endpoint where the OpenTelemetry logs will be sent

  • -Dotel.jmx.config\=<path/to/JMX/config/file/DenodoRules.yaml>
  • contains rules for collecting specific JMX metrics from your Denodo VDP instance. Continue reading to see an example.

Sample configuration of the Java agent:

-Dotel.service.name\=DockerVDP

-Dotel.service.version\=1.0 -Dotel.deployment.environment\=development -javaagent:/opt/tmp/observability/javaagent/opentelemetry-javaagent.jar -Dotel.exporter.otlp.endpoint\=http://collector:5555

-Dotel.logs.exporter\=otlp

-Dotel.exporter.otlp.logs.endpoint\=http://collector:5555 -Dotel.jmx.config\=/opt/tmp/observability/javaagent/DenodoRules.yaml

JMX is  a Java technology that provides tools for monitoring and managing Java applications. In Denodo it is used to expose key metrics such as the number of connections or the memory usage that you can collect and monitor.

This YAML configuration file maps Denodo's JMX metrics (exposed through MBeans) to OpenTelemetry metrics, specifying which server attributes to monitor and how they should be represented in your observability system.

MBeans (Management Beans) are Java objects that provide a standardized way to monitor Java applications by exposing their metrics and operational data through a common interface.

Each mapping defines how a specific JMX attribute (like active connections or memory usage) should be collected and labeled in your monitoring platform.

Below you will find an example YAML file and an explanation of its content:

rules:

  - bean: com.denodo.vdb.management.mbeans:type=DataSourceManagementInfo,name=GlobalDataSourceManagementInfo

    mapping:

      TotalActiveRequestsCount:

        metric: globaldatasourcemanagementinfo.totalactiverequestscount

        type: updowncounter

        desc: The total number of active requests

        unit: "1"

bean: The full name of the MBean in Denodo

  • Format: package:type=Name,name=SpecificName
  • Example: com.denodo.vdb.management.mbeans:type=DataSourceManagementInfo,name=GlobalDataSourceManagementInfo
  • This MBean monitors global data source activity across Denodo

mapping: Under this, you list the attributes of the MBean you want to collect. Each name must match the exact MBean attribute name, for instance:

  • TotalActiveRequestsCount: Tracks all active requests.
  • DFActiveRequestsCount: Tracks DataFrame requests.
  • JDBCActiveRequestsCount: Tracks JDBC data source connection requests.
  • JSONActiveRequestsCount: Tracks JSON data source requests.

For each attribute, you define:

  • metric: The name given in your monitoring system (e.g., globaldatasourcemanagementinfo.totalactiverequestscount).
  • type: Set to updowncounter because request counts can increase or decrease.
  • Other types:
  • gauge: A value that can go up and down (like current connections).
  • counter: A value that only increases (like total failures).
  • desc: A description of what is being measured.
  • unit: Set to "1" because we are counting individual requests.

For more details see

Quickstart Docker Compose

In this section we will provide and explain a quickstart Docker Compose template you can use to implement the architecture above.

  • OpenTelemtetry Javaagent Setup
  • Prepare the javaagent folder structure:

```

/path/to/OpenTelemetry/javaagent/folder/

├── opentelemetry-javaagent.jar

└── DenodoRules.yaml                        # Add this folder to the javaagent folder

 ```

  • Docker-compose.yaml
  • Setup the following file structure:

                project_root/

├── docker-compose.yml

├── collector/

│ └── collector-config-local.yaml           # OTLP config & routing

├── prometheus/

│ └── prometheus.yaml                  # Metrics scraping config

├── tempo/

│ ├── tempo.yaml                 # Traces storage config

│ └── tempo-data/                # Volume for trace data

├── loki/

│ ├── loki-config.yml                 # Log aggregation config

│ └── loki-data/                         # Volume for log data

  • This Docker Compose sets up Denodo with a complete observability pipeline using OpenTelemetry, where the Collector receives and routes Denodo's telemetry data to Tempo (traces), Prometheus (metrics), and Loki (logs) for monitoring and troubleshooting.

  • Denodo Server:
  • Runs Denodo with OpenTelemetry Java Agent instrumentation.
  • Specifies ports and port mapping.
  • Requires a license file mount and a Denodo container image.

  • OpenTelemetry Collector:
  • Ports: 5555 (OTLP in), 6666 (Prometheus metrics).
  • Depends on Tempo, Prometheus and Loki being healthy.

  • Temp:
  • Ports: 3200 (HTTP API), 4317 (OTLP) .
  • Local storage in ./tempo/tempo-data.
  • Includes health check configuration.

  • Prometheus:
  • Port: 9091 (remapped from 9090 since 9090 is in use by Denodo).
  • Includes health check configuration.

  • Loki:
  • Port: 3100 for log ingestion.
  • Local storage in ./loki/loki-data.
  • Includes health check configuration.

        

services:

  vdpserver_opentelemetry:

    image: <denodo_container_image>

   

    container_name: vdpserver_telemetry

    hostname: vdpserver_telemetry

    networks:

      - denodo_opentelemetry

    ports:

      - "9995-9999:9995-9999"

      - "9090:9090"

      - "9443:9443"

    volumes:

      - "<path/to/dendo/license.lic>:/opt/denodo/conf/denodo.lic"

      - "<path/to/OpenTelemetry/javaagent/folder>:/opt/tmp/observability/javaagent"

    environment:

      DENODO_REGISTRY_HOST: "vdpserver"

      DENODO_WEBCONTAINER_STARTUP: "true"

    # Required for OpenTelemetry Agent to work

      DENODO_JVM_OPTS: "-Dotel.service.name=DockerVDP -Dotel.service.version=1.0 -Dotel.deployment.environment=development -javaagent:/opt/tmp/observability/javaagent/opentelemetry-javaagent.jar -Dotel.exporter.otlp.endpoint=http://collector:5555 -Dotel.logs.exporter=otlp -Dotel.exporter.otlp.logs.endpoint=http://collector:5555 -Dotel.jmx.config=/opt/tmp/observability/javaagent/DenodoRules.yaml       DENODO_DEBUG: true

    command: ["--vqlserver","--designstudio", "--datacatalog"]

 

  collector:

    #image: otel/opentelemetry-collector:latest

    image: otel/opentelemetry-collector-contrib:0.97.0

    container_name: collector

    hostname: collector

    networks:

      - denodo_opentelemetry

    restart: always

    depends_on:

      tempo:

        condition: service_healthy

      prometheus:

        condition: service_healthy

      loki:

        condition: service_healthy

    command: ["--config=/etc/collector-config.yaml"]

    volumes:

      - ./collector/collector-config-local.yaml:/etc/collector-config.yaml

    ports:

      - "5555:5555"

      - "6666:6666"

  tempo:

    image: grafana/tempo:latest

    command: ["-config.file=/etc/tempo.yaml" ]

    container_name: tempo

    hostname: tempo

    networks:

      - denodo_opentelemetry

    restart: always

    volumes:

      - ./tempo/tempo.yaml:/etc/tempo.yaml

      - ./tempo/tempo-data:/tmp2

    environment:

      TMPDIR: /tmp2

    ports:

      - "3200:3200"

      - "4317:4317"

    healthcheck:

      interval: 5s

      retries: 10

      test: wget --no-verbose --tries=1 --spider http://localhost:3200/status || exit 1

  prometheus:

    image: prom/prometheus:v2.39.2

    container_name: prometheus

    hostname: prometheus

    networks:

      - denodo_opentelemetry 

    restart: always

    command:

      - --config.file=/etc/prometheus.yaml

      - --web.enable-remote-write-receiver

      - --enable-feature=exemplar-storage

    volumes:

      - ./prometheus/prometheus.yaml:/etc/prometheus.yaml

    ports:

      - "9091:9090"    #frorwarding 9091 to 9090, since 9090 is already used be denodo

    healthcheck:

      interval: 5s

      retries: 10

      test: wget --no-verbose --tries=1 --spider http://localhost:9090/status || exit 1

#

##################################################################################################################

  loki:

    image: grafana/loki:latest

    container_name: loki

    hostname: loki

    networks:

      - denodo_opentelemetry

    restart: always

    #restart: unless-stopped

    volumes:

       - ./loki/loki-config.yml:/mnt/config/loki-config.yml

       - ./loki/loki-data:/loki

    ports:

       - 3100:3100

    command: 

       - '-config.file=/mnt/config/loki-config.yml'

    healthcheck: test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:3100/ready || exit 1"]

      interval: 10s

      timeout: 5s

      retries: 5

  grafana:

    container_name: grafana-demo

    hostname: grafana-demo

    networks:

      - denodo_opentelemetry 

    image: grafana/grafana

    volumes:

      - ./grafana/grafana-datasources.yml:/etc/grafana/provisioning/datasources/datasources.yml

    restart: always

    ports:

      - "3000:3000"

networks:

    denodo_opentelemetry:

        name: denodo_opentelemetry

        driver: bridge

Configuration Files Setup

  • Collector Configuration (./collector/collector-config-local.yaml):
  • OTLP receiver on port 5555 for incoming telemetry.
  • Routes data to appropriate backends:
  • Traces → Tempo
  • Metrics → Prometheus
  • Logs → Loki
  • Uses batch processing with 1 second timeout and 1024 batch size.
  • Separate pipelines for metrics, traces, and logs.

receivers:

  otlp:

    protocols:

      grpc:

        endpoint: 0.0.0.0:5555

processors:

  batch:

    timeout: 1s

    send_batch_size: 1024

exporters:

  prometheus:

    endpoint: collector:6666

    namespace: denodo

  otlp/tempo:

    endpoint: tempo:4317

    tls:

      insecure: true

  loki:

    endpoint: http://loki:3100/loki/api/v1/push

  otlp/jaeger:

    endpoint: "http://jaeger:4317"

    tls:

      insecure: true

service:

  pipelines:

    metrics:

      receivers: [otlp]

      processors: [batch]

      exporters: [prometheus]

    traces:

      receivers: [otlp]

      processors: [batch]

      exporters: [otlp/jaeger,otlp/tempo]

    logs:

      receivers: [otlp]

      exporters: [loki]

  •  Tempo Configuration (./tempo/tempo.yaml):
  • Configures Tempo trace storage.
  • Defines retention settings.
  • Sets up receivers and search capabilities.

stream_over_http_enabled: true

server:

  http_listen_port: 3200

  log_level: info

query_frontend:

  search:

    duration_slo: 5s

    throughput_bytes_slo: 1.073741824e+09

  trace_by_id:

    duration_slo: 5s

distributor:

  receivers:                           # this configuration will listen on all ports and protocols that tempo is capable of.

    jaeger:                            # the receives all come from the OpenTelemetry collector.  more configuration information can

      protocols:                       # be found there: https://github.com/open-telemetry/opentelemetry-collector/tree/main/receiver

        thrift_http:                   #

        grpc:                          # for a production deployment you should only enable the receivers you need!

        thrift_binary:

        thrift_compact:

    zipkin:

    otlp:

      protocols:

        http:

        grpc:

    opencensus:

ingester:

  max_block_duration: 5m               # cut the headblock when this much time passes. this is being set for demo purposes and should probably be left alone normally

compactor:

  compaction:

    block_retention: 1h                # overall Tempo trace retention. set for demo purposes

metrics_generator:

  registry:

    external_labels:

      source: tempo

      cluster: docker-compose

  storage:

    path: /tmp/tempo/generator/wal

    remote_write:

      - url: http://prometheus:9090/api/v1/write

        send_exemplars: true

storage:

  trace:

    backend: local                     # backend configuration to use

    wal:

      path: /tmp/tempo/wal             # where to store the the wal locally

    local:

      path: /tmp/tempo/blocks

overrides:

  defaults:

    metrics_generator:

      processors: [service-graphs, span-metrics] # enables metrics generator

  • Prometheus Configuration (./prometheus/prometheus.yaml):
  • Scrapes metrics from collector:6666 every 5 seconds.
  • Matches the collector's Prometheus exporter endpoint.
  • Global evaluation interval: 15 seconds.

global:

  scrape_interval: 15s

  evaluation_interval: 15s

scrape_configs:

  - job_name: 'collector'

    scrape_interval: 5s

    static_configs:

      - targets: [ 'collector:6666' ]

  • Loki Configuration (./loki/loki-config.yml):
  • Receives logs on port 3100 via /loki/api/v1/push.
  • Uses filesystem storage with TSDB schema.
  • 100MB embedded cache.
  • Auth disabled for development setup.

auth_enabled: false

server:

  http_listen_port: 3100

  grpc_listen_port: 9096

common:

  instance_addr: 127.0.0.1

  path_prefix: /tmp/loki

  storage:

    filesystem:

      chunks_directory: /tmp/loki/chunks

      rules_directory: /tmp/loki/rules

  replication_factor: 1

  ring:

    kvstore:

      store: inmemory

query_range:

  results_cache:

    cache:

      embedded_cache:

        enabled: true

        max_size_mb: 100

schema_config:

  configs:

    - from: 2020-10-24

      store: tsdb

      #changed from boltdb-shipper to the newer tsdb

      object_store: filesystem

      schema: v13

      #changed from v11 to v13

      index:

        prefix: index_

        period: 24h

ruler:

  alertmanager_url: http://localhost:9093

# By default, Loki will send anonymous, but uniquely-identifiable usage and configuration

# analytics to Grafana Labs. These statistics are sent to https://stats.grafana.org/

#

# Statistics help us better understand how Loki is used, and they show us performance

# levels for most users. This helps us prioritize features and documentation.

# For more information on what's sent, look at

# https://github.com/grafana/loki/blob/main/pkg/usagestats/stats.go

# Refer to the buildReport method to see what goes into a report.

#

# If you would like to disable reporting, uncomment the following lines:

#analytics:

#  reporting_enabled: false

  • Grafana (./grafana/):
  • Contains the data source configuration to connect to the observability backends.
  • Note: You can also manually adjust the data source configuration in the Grafana web tool.

apiVersion: 1

datasources:

  - name: Prometheus

    type: prometheus

    uid: prometheus

    access: proxy

    orgId: 1

    url: http://prometheus:9090

    basicAuth: false

    isDefault: false

    version: 1

    editable: true

    jsonData:

      httpMethod: GET

      exemplarTraceIdDestinations:

        - datasourceUid: tempo

          name: trace_id

  - name: Loki

    type: loki

    uid: loki

    access: proxy

    orgId: 1

    url: http://loki:3100

    basicAuth: false

    isDefault: false

    version: 1

    editable: true

  - name: Tempo

    type: tempo

    access: proxy

    orgId: 1

    url: http://tempo:3200

    basicAuth: false

    isDefault: true

    version: 1

    editable: true

    apiVersion: 1

    uid: tempo

    jsonData:

      httpMethod: GET

      serviceMap:

        datasourceUid: prometheus

      tracesToLogsV2:

        datasourceUid: loki

        spanStartTimeShift: '-1h'

        spanEndTimeShift: '1h'

        filterByTraceID: true

        filterBySpanID: true

        tags: [ { key: 'service.name', value: 'job' } ]

Starting the services

From your project root directory you can now use the docker compose up command to start all the previously described services.

Connecting to Grafana

You can now connect to Grafana using your Grafana host address and the (default) port 3000 e.g. localhost:3000 where you will be prompted to login. You can use the default admin:admin credentials initially and you will be prompted to change the password.

Under Home > Connections > Data sources you can see our data sources:

NOTE: In order for the Denodo server to be monitored by tools using the JMX protocol, the VDP host name has to be set Set the Host Name of Denodo Servers.

References

Monitoring Denodo servers with JMX

JMX Metric Insight

Disclaimer

The information provided in the Denodo Knowledge Base is intended to assist our users in advanced uses of Denodo. Please note that the results from the application of processes and configurations detailed in these documents may vary depending on your specific environment. Use them at your own discretion.

For an official guide of supported features, please refer to the User Manuals. For questions on critical systems or complex environments we recommend you to contact your Denodo Customer Success Manager.
Recommendation

Recommended resources Recommendations generated by AI

Denodo Lakehouse Accelerator Observability with OpenTelemetry

Building an Observability Framework for Denodo with Grafana and OpenTelemetry

Monitoring the Denodo Lakehouse Accelerator with Prometheus and Grafana

Monitoring Denodo with Prometheus

Monitoring Denodo with Prometheus in OpenShift

Questions

Ask a question

You must sign in to ask a question. If you do not have an account, you can register here