Oracle APEX 24.2の新機能として追加されたOpenTelemetryの設定について確認してみました。Oracle APEX 24.2のRelease Notesの2. 31 Java Script Library Upgradesから確認できる範囲では、OpenTelemetry SDKとして、以下のライブラリがOracle APEXのフロントエンドに組み込まれています。
Oracle APEX 24.2で、OpenTelemetryによるクライアント側のテレメトリー・データの送信を有効にするために必要な設定は、それほど多くはありません。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
Oracle APEX 24.2では、ワークスペース・ユーティリティにOpenTelemetryが新設されました。
OpenTelemetryの構成としてクライアント・ロギング・サービスURLとトークン・リレーURLを設定します。
APEXアプリケーションのアプリケーション定義のユーザー・インターフェースに、OpenTelemetryのセクションが追加されています。そのセクションで製品ファミリを設定します。
クライアント・ロギング・サービスURL、トークン・リレーURLおよび製品ファミリといった設定がOpenTelemetryの何の設定に対応しているのか不明だったのですが、Oracle APEX 24.2のOpenTelemetryの対応は、OracleのSaaS製品が提供しているオブザーバビリティのフレームワークに送信することを想定しているようです。
OracleのSaaSに接続するのは難しいため、クライアント・ロギング・サービスURLとトークン・リレーURLの処理を、Oracle REST Data Servicesで実装してOracle APEX 24.2のOpenTelemetryの動作を確認します。
最初にクライアント・ロギング・サービスを実装します。
OpenTelemetryが有効化されたAPEXアプリケーションは、ブラウザよりトレース・データをHTTP/JSON形式でgzip圧縮して送信します。クライアント・ロギング・サービスURLは、このデータの送信先になります。この形式のデータは、OpenTelemetry Collectorのreceiverでも、そのまま受信可能と思われます。
受信したトレースを保存する表を作成します。表名はEBAJ_OTEL_TRACESとします。
create table ebaj_otel_traces (
id number generated by default on null as identity
constraint ebaj_otel_traces_id_pk primary key,
trace clob check (trace is json),
created date default on null sysdate
);
以下は、SQLワークショップのSQLコマンドで実行した例です。
SQLワークショップのRESTfulサービスより、モジュールOpenTelemetryを作成します。
モジュール名はOpenTelemetry、モジュール・ベース・パスは/otel/とします。
作成したモジュールOpenTelemetryに、URIテンプレートとしてtracesを作成します。
テンプレートtracesに、POSTハンドラを登録します。
ソースとして以下を記述します。受信したデータをgzipで解凍し、取り出したJSONドキュメントを表EBAJ_OTEL_TRACESの列TRACEに保存します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 ebaj_otel_traces(trace) values(l_json); | |
dbms_lob.freeTemporary(l_uncompressed_blob); | |
end; |
以上で、クライアント・ロギング・サービスは完成です。リソース・ハンドラの完全なURLはクライアント・ロギング・サービスURLとして設定するため、コピーして記録しておきます。
Oracle APEXのOpenTelemetryの実装では、クライアント・ロギング・サービスURLはOAuth2による認証で保護されていることが想定されています。トークン・リレーURLは、Authorizationヘッダーに設定するアクセス・トークンを取得する際に呼び出します。
クライアント・ロギング・サービスを実装したモジュールOpenTelemetryにアクセスする際に、OAuth2による認証を要求するように構成します。
以下のスクリプトを実行します。モジュールOpenTelemetryをOAuth2で保護し、そのアクセスに必要なclient_idとclient_secretを印刷します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 || ' has already 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 || ' is created.'); | |
else | |
dbms_output.put_line('Privilege ' || C_PRIV_NAME || ' has already 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 || ' is created.'); | |
else | |
dbms_output.put_line('OAuth client ' || C_OAUTH_NAME || ' has already 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; | |
/ |
印刷されたclient_idとclient_secretは、APEXのWeb資格証明を作成する際に使用するため、コピーして記録しておきます。
作業の参考になるように、上記で作成したOAuth2のクライアント、権限およびロールを削除するスクリプトを掲載しておきます。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 ' || C_OAUTH_NAME || ' is 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 ' || C_PRIV_NAME || ' is 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 ' || C_ROLE_NAME || ' is deleted.'); | |
end if; | |
end; | |
/ |
ワークスペース・ユーティリティのWeb資格証明を開き、新たにWeb資格証明を作成します。
作成するWeb資格証明の名前はOpenTelemetry、静的IDはOPENTELEMETRYとします。認証タイプはOAuth2クライアント資格証明を選択します。
クライアントIDまたはユーザー名に、先ほどのスクリプトの実行結果として印刷されたclient_idの値、クライアント・シークレットまたはパスワードに、client_secretの値を入力します。
以上で作成します。
Web資格証明としてOpenTelemetry(静的ID: OPENTELEMETRY)が作成されました。
このWeb資格証明を使ってアクセス・トークンを取得するサービスを、Oracle REST Data Servicesで作成します。このサービスがトークン・リレーになります。
RESTfulサービスを開き、モジュールを作成します。モジュール名はOpenTelemetry-Auth、ベース・パスは/otel-auth/とします。
テンプレートとしてtokenを作成します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
declare | |
l_response clob; | |
l_token varchar2(80); | |
l_expires_in integer; | |
begin | |
-- ワークスペース名は、Web資格証明OPENTELEMETRYを作成したワークスペース名に変更すること。 | |
apex_util.set_workspace(p_workspace => 'APEXDEV'); | |
apex_web_service.oauth_authenticate_credential( | |
/* | |
* トークンURLはワークスペース毎に決まっている。 | |
* proto://hostname/ords/ワークスペース名 + '/oauth/token' | |
*/ | |
p_token_url => 'http://localhost:8181/ords/apexdev/oauth/token' | |
,p_credential_static_id => 'OPENTELEMETRY' | |
); | |
l_token := apex_web_service.oauth_get_last_token; | |
l_expires_in := ((apex_web_service.g_oauth_token.expires - sysdate) * 84600); | |
l_response := apex_string.format('{ "access_token": "%s", "token_type": "bearer", "expires_in": %s }', l_token, l_expires_in); | |
owa_util.mime_header('application/json', true); | |
htp.p(l_response); | |
end; |
このトークン・リレーURLはAPEXのセッションで保護すべきですが、難しいため保護はかけていません。
以上でクライアント・ロギング・サービスURLとトークン・リレーURLの準備ができました。
ワークスペース・ユーティリティのOpenTelemetryを開き、それぞれの値を設定します。
テストに使用するAPEXアプリケーションを作成します。アプリケーション自体は何でもよいのですが、サンプル・データセットのEMP/DEPTをインストールしたときに作成できるアプリケーションを使うことにします。
サンプル・データセットを開き、EMP/DEPTをインストールまたは更新します。
インストールまたは更新が完了した後に表示される、アプリケーションの作成ボタンをクリックします。
アプリケーション作成ウィザードが開きます。
アプリケーションの名前をTest App for OpenTelemetryに変更し、アプリケーションの作成を実行します。
アプリケーションが作成されたら、アプリケーション定義のユーザー・インターフェースを開き、OpenTelemetryの製品ファミリにtest-appを設定します。
以上でテストの準備は完了です。
APEXアプリケーションを実行し、適当にアプリケーションを操作します。デバッグを有効にし、JavaScriptコンソールを開いてトークン・リレーURLやクライアント・ロギング・サービスURLの呼び出しが失敗していないことを確認します。失敗するとtelemetry.jsでエラーが発生し、JavaScriptコンソールに出力されます。
SQLワークショップのSQLコマンドを開き、表EBAJ_OTEL_TRACESにデータが書き込まれていることを確認します。
select count(*) from ebaj_otel_traces;
表EBAJ_OTEL_TRACESに保存されたデータを一覧するAPEXアプリケーションを作成します。Oracle APEX 24.2で追加されたJSONソースの機能を使います。
空のAPEXアプリケーションを作成します。名前はOpenTelemetry Trace Reportとします。
アプリケーションが作成されます。共有コンポーネントを開きます。
JSONソースを開きます。APEX 24.2の新機能です。
作成済みのJSONソースが一覧されます。作成をクリックします。
作成するJSONソースの名前はOpenTelemetry Tracesとします。JSONソース・タイプはJSON列のある表、JSON列のある表としてEBAJ_OTEL_TRACESを選択します。
次へ進みます。
JSON列1にTRACE(Clob)を選択します。JSONスキーマは無いため設定せず、すでに保存済みのデータより、データ・プロファイルを生成します。
次へ進みます。
列TRACEに保存済みのデータから、データ・プロファイルが作成されます。
列IDは表EBAJ_OTEL_TRACESの主キー列なので、デフォルトで主キーにチェックが入ります。列TRACEがJSONドキュメントとして認識されます。
列TRACEに保存されているJSONドキュメントの属性spansに、spanオブジェクトの配列が含まれます。spans配列中のspanオブジェクトを一意に識別する属性はspan_idであるため、SPAN_IDの主キーにチェックを入れます。
以上の設定を行い、作成をクリックします。
JSONソースとしてOpenTelemetry Tracesが作成されました。
作成したJSONソースOpenTelemetry Tracesをソースとした、対話モード・レポートとフォームのページを作成します。
ページの作成をクリックします。
対話モード・レポートを選択します。
ページの名前をSpansとし、フォーム・ページを含めるをオンにします。フォーム・ページ名はSpan Detailとします。
データ・ソースにJSONソースを選択し、JSONソースに先ほど作成したOpenTelemetry Tracesを選択します。ネストした行に列TRACEに含まれているJSON配列に当たる1.SPANSを選択します。
次へ進みます。
主キー列1にID (Number)、主キー列2にSPAN_ID (Varchar2)が選択されます。
確認してページの作成をクリックします。
OpenTelemetryのトレースと、トレースに含まれるspanを表示する対話モード・レポートとフォームのページが作成されました。
以上でアプリケーションは完成です。
アプリケーションを実行すると、受信したスパンの一覧が表示されます。
フォームを作成しているため、編集アイコンをクリックすると詳細情報が表示されます。
これでOracle APEX 24.2のOpenTelemetryについて、ある程度の評価ができそうです。
今回作成した、OpenTelemetryのスキャンを一覧するAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/opentelemetry_trace_report.zip
今回の記事は以上になります。
Oracle APEXのアプリケーション作成の参考になれば幸いです。
完