Skip to content

Improve observability components and documentation #1001

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,31 @@

import io.opentelemetry.api.trace.Span;

/**
* Helper class for OpenTelemetry operations.
* Provides utility methods for working with OpenTelemetry.
*/
public class OpenTelemetryHelper {

/**
* Records an exception on the specified span
*
* @param span the span to record the exception on
* @param exc the exception to record
*/
public static void recordException(Span span, Throwable exc) {
if (span != null && exc != null) {
span.recordException(exc);
}
}

/**
* Records an exception on the current active span
*
* @param exc the exception to record
* @throws NullPointerException if exc is null
*/
public static void recordException(Throwable exc) {
recordException(Span.current(), exc);
}
}
}
100 changes: 100 additions & 0 deletions gxobservability/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# GeneXus Observability

A Java library for integrating OpenTelemetry tracing capabilities into GeneXus applications.

## Overview

The GeneXus Observability module provides a wrapper around the OpenTelemetry Java SDK, allowing GeneXus applications to easily implement distributed tracing. It simplifies the process of creating spans, managing trace context, and propagating trace information across service boundaries.

## Features

- Creation and management of spans with different configurations
- Context propagation across service boundaries
- Ability to add and retrieve baggage items (metadata associated with a trace)
- Support for different span types (internal, server, client, producer, consumer)
- Ability to set span attributes, status, and record exceptions
- Automatic configuration from environment variables

## Dependencies

This module depends on the following OpenTelemetry components:

- opentelemetry-api
- opentelemetry-sdk-trace
- opentelemetry-exporter-otlp
- opentelemetry-sdk
- opentelemetry-semconv
- opentelemetry-extension-annotations
- opentelemetry-sdk-extension-autoconfigure

## Main Components

### OtelTracer

The `OtelTracer` class is the main entry point for creating spans. It provides methods to:

- Create spans with different configurations
- Specify parent context
- Link to other spans
- Define span types

### OtelSpan

The `OtelSpan` class wraps an OpenTelemetry Span and provides methods to:

- Set span attributes
- Record exceptions
- Set span status
- Add and retrieve baggage items
- End spans

### GXSpanContext and GXTraceContext

These classes provide wrappers around OpenTelemetry's SpanContext and Context objects, making it easier to handle trace propagation between services.

## Configuration

The module can be configured using environment variables:

- `OTEL_SERVICE_NAME`: Defines the service name for traces
- `OTEL_SERVICE_VERSION`: Defines the service version
- `OTEL_RESOURCE_ATTRIBUTES`: Defines additional resource attributes
- `JAVA_INSTRUMENTATION_SCOPE_NAME`: Override for the instrumentation scope name
- `JAVA_INSTRUMENTATION_SCOPE_VERSION`: Override for the instrumentation scope version

## Usage Examples

### Creating a Simple Span

```java
OtelTracer tracer = new OtelTracer();
OtelSpan span = tracer.createSpan("MyOperation");
try {
// Perform your operation here
span.setStringAttribute("attribute.key", "value");
} catch (Exception e) {
span.recordException(e.getMessage());
span.setStatus((byte)2, "Operation failed"); // Set status to ERROR
} finally {
span.endSpan();
}
```

### Creating a Span with Context Propagation

```java
OtelTracer tracer = new OtelTracer();
OtelSpan parentSpan = tracer.createSpan("ParentOperation");
GXTraceContext context = parentSpan.getGXTraceContext();

// Use the context in a child operation
OtelSpan childSpan = tracer.createSpan("ChildOperation", context, (byte)2); // CLIENT span
childSpan.setStringAttribute("request.id", "12345");
childSpan.endSpan();

parentSpan.endSpan();
```

## Integration with GeneXus

This module is designed to be easily integrated with GeneXus applications, providing tracing capabilities that can be used both in Java-based backend services and in code generated by GeneXus.
59 changes: 46 additions & 13 deletions gxobservability/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,29 @@
</parent>

<artifactId>gxobservability</artifactId>
<name>GeneXus Observability</name>
<name>GeneXus Observability</name>

<dependencies>
<!-- OpenTelemetry dependencies -->
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-api</artifactId>
<version>${io.opentelemetry.version}</version>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk-trace</artifactId>
<version>${io.opentelemetry.version}</version>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-otlp</artifactId>
<version>${io.opentelemetry.version}</version>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk</artifactId>
<version>${io.opentelemetry.version}</version>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
Expand All @@ -38,24 +43,52 @@
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-extension-annotations</artifactId>
<version>1.18.0</version>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk-extension-autoconfigure</artifactId>
<version>1.36.0</version>
<version>${io.opentelemetry.version}</version>
</dependency>

<!-- Test dependencies -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<version>1.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>4.5.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>4.5.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk-testing</artifactId>
<version>${io.opentelemetry.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-bom</artifactId>
<version>1.23.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<build>
<finalName>gxobservability</finalName>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.genexus.opentelemetry;

/**
* Helper class to provide a testable version of the extractAttributeValue method from OtelTracer.
* This is used to support unit testing of the private method.
*/
public class ExtractAttributeValueHelper {

/**
* Extracts an attribute value from a resource attributes string
*
* @param resourceAttributes the resource attributes string
* @param attributeName the name of the attribute to extract
* @return the attribute value, or null if not found
*/
public static String extractAttributeValue(String resourceAttributes, String attributeName) {
if (resourceAttributes == null || attributeName == null) {
return null;
}

// Simple parsing for key-value pairs
String[] pairs = resourceAttributes.split(",");
for (String pair : pairs) {
String[] keyValue = pair.split("=");
if (keyValue.length == 2 && keyValue[0].equals(attributeName)) {
return keyValue[1];
}
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -1,33 +1,55 @@
package com.genexus.opentelemetry;

import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.context.Context;
public class GXSpanContext
{
private io.opentelemetry.api.trace.SpanContext _spanContext;
import io.opentelemetry.api.trace.SpanContext;

public io.opentelemetry.api.trace.SpanContext getSpanContext()
{
return _spanContext;
/**
* Wrapper class for OpenTelemetry SpanContext.
* Provides access to trace and span identifiers.
*/
public class GXSpanContext {
private final SpanContext spanContext;

/**
* Returns the underlying OpenTelemetry SpanContext
*
* @return the underlying SpanContext instance
*/
public SpanContext getSpanContext() {
return spanContext;
}
public GXSpanContext(io.opentelemetry.api.trace.SpanContext spanContext)
{
this._spanContext = spanContext;

/**
* Creates a GXSpanContext from an existing SpanContext
*
* @param spanContext the OpenTelemetry SpanContext to wrap
*/
public GXSpanContext(SpanContext spanContext) {
this.spanContext = spanContext;
}
public GXSpanContext()
{
_spanContext = Span.current().getSpanContext();

/**
* Creates a GXSpanContext from the current active span
*/
public GXSpanContext() {
spanContext = Span.current().getSpanContext();
}
public String traceId()
{
return _spanContext.getTraceId();

/**
* Gets the trace ID of the current span context
*
* @return the trace ID as a hexadecimal string
*/
public String traceId() {
return spanContext.getTraceId();
}
public String spanId()
{
return _spanContext.getSpanId();

/**
* Gets the span ID of the current span context
*
* @return the span ID as a hexadecimal string
*/
public String spanId() {
return spanContext.getSpanId();
}
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,29 @@
package com.genexus.opentelemetry;

import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.context.Context;

public class GXTraceContext
{
private Context context;
public GXTraceContext(io.opentelemetry.context.Context context)
{
/**
* Wrapper class for OpenTelemetry Context.
* Provides access to trace context for propagation between processes.
*/
public class GXTraceContext {
private final Context context;

/**
* Creates a GXTraceContext from an existing OpenTelemetry Context
*
* @param context the OpenTelemetry Context to wrap
*/
public GXTraceContext(Context context) {
this.context = context;
}
public Context getTraceContext()
{

/**
* Returns the underlying OpenTelemetry Context
*
* @return the underlying Context instance
*/
public Context getTraceContext() {
return this.context;
}
}
Loading
Loading