Skip to main content

Command Palette

Search for a command to run...

Opentelemetry in Oracle APEX (part-1)

Published
7 min read
Opentelemetry in Oracle APEX (part-1)
R

Richard Martens has been involved in information technology for more than 20 years. He started as a web developer using the Oracle database as no more than data storage. Richard has been responsible for major European multilingual websites and has been working with the Oracle database since 2000. During those years, he developed himself using a multitude of technologies and specialized in PL/SQL and Oracle APEX. Richard has been working primarily with PLSQL and APEX since 2005. With APEX, he combines the things he loves most: the Oracle database and web technologies.

When preparing for my sessions this year on OpenTelemtrey within Oracle APEX I came across a couple of contributions:

The blog is available in a gazillion languages through Google Translate, that helps a lot. Nevertheless I thought, let me rephrase Yuji in English and add English screenshots as well.

Support for OpenTelemetry in Oracle APEX 24.2

Yuji checked the settings for OpenTelemetry, a new feature added in Oracle APEX 24.2. As far as he can tell from 2.31 Java Script Library Upgrades in the Oracle APEX 24.2 Release Notes , the following libraries are built into the front end of Oracle APEX as the OpenTelemetry SDK:

2.31 JavaScript Library Upgrades

  • @opentelemetry/api 1.9.0

  • @opentelemetry/core 1.26.0

  • @opentelemetry/instrumentation 0.53.0

  • @opentelemetry/instrumentation-document-load 0.39.0

  • @opentelemetry/instrumentation-fetch 0.53.0

  • @opentelemetry/instrumentation-xml-http-request 0.53.0

  • @opentelemetry/sdk-trace-base 1.26.0

  • @opentelemetry/sdk-trace-web 1.26.0

In Oracle APEX 24.2, there is little configuration required to enable sending client-side telemetry data through OpenTelemetry.

Oracle APEX 24.2 introduces OpenTelemetry to Workspace Utilities .

Configure OpenTelemetry by setting the client logging service URL and token relay URL .

The APEX application's application definition user interface now includes a section for OpenTelemetry, where you set the product family .

It was unclear to Yuji what OpenTelemetry settings settings such as Client Logging Service URL , Token Relay URL , and Product Family corresponded to, but it appears that Oracle APEX 24.2's OpenTelemetry support is intended to send data to the observability framework provided by Oracle's SaaS products .

Since connecting to Oracle SaaS is difficult, we will implement the client logging service URL and token relay URL processing in Oracle REST Data Services to verify the operation of OpenTelemetry in Oracle APEX 24.2.

Creating your own collector

An OpenTelemetry-enabled APEX application sends trace data from the browser in HTTP/JSON format, compressed with gzip. The Client Logging Service URL is the destination for this data. This data format is likely to be received directly by an OpenTelemetry Collector receiver.

Create a table to store the received traces. Name the table OTEL_TRACES .

create table otel_traces (
    id         number generated by default on null as identity
               constraint otel_traces_id_pk primary key,
    trace      clob,
    created    date default on null sysdate
);

You can do this in the SQL-workshop or any other (more appropriate tool)

Now we have the table, we can create a rest-service on it:

I'll provide the scripts that create all services below as well.

The module name is OpenTelemetry and the module base path is /otel/

Create traces as a URI template in the created module OpenTelemetry.

Register the POST handler in template traces.
Write the following as a source . The received data is decompressed with gzip and the extracted JSON document is saved in the TRACE column of the EBAJ_OTEL_TRACES table.

declare
   l_uncompressed_blob blob;
   l_json              clob;
begin
   l_uncompressed_blob := utl_compress.lz_uncompress(:body);
   l_json := apex_util.blob_to_clob(l_uncompressed_blob);
   insert into otel_traces(trace) values(l_json);
   dbms_lob.freeTemporary(l_uncompressed_blob);
end;

The client logging service is now complete. Copy and record the full URL of the resource handler as you will set it as the client logging service URL.

Creating the Token Relay Service

The whole process however is a bit different than we are used to as APEX / ORDS service developers. Instead of providing a client-id / secret to the application, remember we only gave APEX a "Token Relay URL". This will be a public service that APEX will call to retrieve a token.

It will then use that token to call the Client Logging Service

The process below falls apart in these steps:

  1. create rest client (Client-ID and Secret)

  2. store these client-credentials within APEX builder

  3. create the Token Relay service that will turn the client-credentials into a token

The OpenTelemetry implementation in Oracle APEX expects the client logging service URL to be protected with OAuth2 authentication, and the token relay URL is called to obtain the access token to be placed in the Authorization header.

Configure the module that implements the client logging service to require OAuth2 authentication when accessing OpenTelemetry .

Run the following script. It will secure the module OpenTelemetry with OAuth2 and print the client_id and client_secret needed to access it.

declare
  C_MODULE_NAME constant varchar2(40) := 'OpenTelemetry';
  C_ROLE_NAME   constant varchar2(40) := 'opentelemetry_client';
  C_PRIV_NAME   constant varchar2(40) := 'opentelemetry.priv';
  C_OAUTH_NAME  constant varchar2(40) := 'opentelemetry_client';
  l_priv_roles    owa.vc_arr;
  l_priv_patterns owa.vc_arr;
  l_priv_modules  owa.vc_arr;
  l_exist number;
  l_client_id     user_ords_clients.client_id%type;
  l_client_secret user_ords_clients.client_secret%type;
begin
  -- check if role C_ROLE_NAME is exist
  select count(*) 
  into l_exist 
  from user_ords_roles 
  where name = C_ROLE_NAME;
  if l_exist = 0 then
    -- create role
    ords.create_role( p_role_name => C_ROLE_NAME );
    dbms_output.put_line('Role ' || C_ROLE_NAME || ' is created.');
  else
    dbms_output.put_line('Role ' || C_ROLE_NAME || ' not created.');
  end if;
  -- check if privilege C_PRIV_NAME is exist
  select count(*) 
  into l_exist 
  from user_ords_privileges
  where name = C_PRIV_NAME;
  if l_exist = 0 then
    -- create priv
    l_priv_roles(1)   := C_ROLE_NAME;
    l_priv_modules(1) := C_MODULE_NAME;
    ords.define_privilege(
      p_privilege_name => C_PRIV_NAME
     ,p_roles    => l_priv_roles
     ,p_patterns => l_priv_patterns
     ,p_modules  => l_priv_modules
     ,p_label    => 'opentelemetry'
     ,p_description => 'priv for opentelemetry traces url'
     ,p_comments => ''
     );
    dbms_output.put_line('Privilege ' || C_PRIV_NAME || ' created.');
  else
    dbms_output.put_line('Privilege ' || C_PRIV_NAME || ' not created.');
  end if;
  -- check if oauth client is exist
  select count(*) 
  into l_exist
  from user_ords_clients
  where name = C_OAUTH_NAME;
  if l_exist = 0 then
    -- create oauth client
    oauth.create_client(
      p_name => C_OAUTH_NAME
     ,p_grant_type => 'client_credentials'
     ,p_description => 'access OpenTelemetry receivers'
     ,p_support_email   => 'your.email@example-domain.com'
     ,p_privilege_names => C_PRIV_NAME
     );
    dbms_output.put_line('OAuth client ' || C_OAUTH_NAME || ' created.');
  else
    dbms_output.put_line('OAuth client ' || C_OAUTH_NAME || ' not created.');
  end if;
  -- grant role to oauth client
  oauth.grant_client_role(
      p_client_name => C_OAUTH_NAME
      ,p_role_name  => C_ROLE_NAME
  );
  dbms_output.put_line('Role ' || C_ROLE_NAME || ' has is granted to OAuth user ' || C_OAUTH_NAME || '.');
  -- print client_id and client_secret
  select client_id
        ,client_secret
  into l_client_id
      ,l_client_secret
  from user_ords_clients
  where name = C_OAUTH_NAME;
  dbms_output.put_line('grant_type: client_credentials');
  dbms_output.put_line('client_id: ' || l_client_id);
  dbms_output.put_line('client_secret: ' || l_client_secret);
end;
/

Copy and record the client_id and client_secret that are printed as you will need them when creating web credentials for APEX .

declare
  C_ROLE_NAME  constant varchar2(40) := 'opentelemetry_client';
  C_PRIV_NAME  constant varchar2(40) := 'opentelemetry.priv';
  C_OAUTH_NAME constant varchar2(40) := 'opentelemetry_client';
  l_exist number;
begin
  -- delete OAuth client.
  select count(*) 
  into l_exist
  from user_ords_clients 
  where name = C_OAUTH_NAME;
  if l_exist = 1 then
    oauth.delete_client( p_name => C_OAUTH_NAME );
    dbms_output.put_line('OAuth client deleted.');
  end if;
  -- delete privilege.
  select count(*) 
  into l_exist
  from user_ords_privileges 
  where name = C_PRIV_NAME;
  if l_exist = 1 then
    ords.delete_privilege( p_name => C_PRIV_NAME );
    dbms_output.put_line('Privilege deleted.');
  end if;
  -- delete role.
  select count(*)
  into l_exist
  from user_ords_roles
  where name = C_ROLE_NAME;
  if l_exist = 1 then
    ords.delete_role( p_role_name => C_ROLE_NAME );
    dbms_output.put_line('Role is deleted.');
  end if;
end;
/

Open the Workspace Utilities Web Credentials and create a new web credential.

Create a web credential named OpenTelemetry with a static ID of OPENTELEMETRY . Select OAuth2 Client Credentials as the authentication type.

Enter the client_id value printed as a result of running the script above as the client ID or username , and the client_secret value as the client secret or password.

That's it.

A web credential , OpenTelemetry (Static ID: OPENTELEMETRY ), has been created.

Create a service in Oracle REST Data Services that uses these web credentials to obtain an access token - this service will be your token relay .

Open the RESTful service and create a module, name it OpenTelemetry-Auth and use the base path / otel-auth/.