From 8e3e72ceb6f3b1e0480de34f5d7a9f4e44cabc1d Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Tue, 24 Oct 2023 18:36:14 -0600 Subject: [PATCH 1/3] Add Python prototype Fixes #53 --- .gitignore | 3 + prototypes/python/LICENSE | 201 ++++ prototypes/python/README.rst | 40 + prototypes/python/noxfile.py | 12 + prototypes/python/pyproject.toml | 53 + prototypes/python/requirements.txt | 3 + .../opentelemetry/configuration/__init__.py | 35 + .../configuration/_internal/__init__.py | 371 ++++++ .../configuration/_internal/path_function.py | 1066 +++++++++++++++++ .../_internal/templates/template.jinja2 | 70 ++ .../src/opentelemetry/configuration/py.typed | 0 .../opentelemetry/configuration/version.py | 15 + prototypes/python/tests/__init__.py | 13 + prototypes/python/tests/data/common.json | 58 + .../python/tests/data/kitchen-sink.yaml | 380 ++++++ .../python/tests/data/logger_provider.json | 109 ++ .../python/tests/data/meter_provider.json | 264 ++++ .../data/opentelemetry_configuration.json | 50 + prototypes/python/tests/data/propagator.json | 17 + prototypes/python/tests/data/resource.json | 27 + .../python/tests/data/tracer_provider.json | 220 ++++ prototypes/python/tests/test_configuration.py | 112 ++ 22 files changed, 3119 insertions(+) create mode 100644 prototypes/python/LICENSE create mode 100644 prototypes/python/README.rst create mode 100644 prototypes/python/noxfile.py create mode 100644 prototypes/python/pyproject.toml create mode 100644 prototypes/python/requirements.txt create mode 100644 prototypes/python/src/opentelemetry/configuration/__init__.py create mode 100644 prototypes/python/src/opentelemetry/configuration/_internal/__init__.py create mode 100644 prototypes/python/src/opentelemetry/configuration/_internal/path_function.py create mode 100644 prototypes/python/src/opentelemetry/configuration/_internal/templates/template.jinja2 create mode 100644 prototypes/python/src/opentelemetry/configuration/py.typed create mode 100644 prototypes/python/src/opentelemetry/configuration/version.py create mode 100644 prototypes/python/tests/__init__.py create mode 100644 prototypes/python/tests/data/common.json create mode 100644 prototypes/python/tests/data/kitchen-sink.yaml create mode 100644 prototypes/python/tests/data/logger_provider.json create mode 100644 prototypes/python/tests/data/meter_provider.json create mode 100644 prototypes/python/tests/data/opentelemetry_configuration.json create mode 100644 prototypes/python/tests/data/propagator.json create mode 100644 prototypes/python/tests/data/resource.json create mode 100644 prototypes/python/tests/data/tracer_provider.json create mode 100644 prototypes/python/tests/test_configuration.py diff --git a/.gitignore b/.gitignore index 79390fd..1799ee9 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,6 @@ # Node.js files for tools (e.g. markdown-toc) node_modules/ package-lock.json + +# Python cache +__pycache__ diff --git a/prototypes/python/LICENSE b/prototypes/python/LICENSE new file mode 100644 index 0000000..1ef7dad --- /dev/null +++ b/prototypes/python/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright The OpenTelemetry Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/prototypes/python/README.rst b/prototypes/python/README.rst new file mode 100644 index 0000000..8a73327 --- /dev/null +++ b/prototypes/python/README.rst @@ -0,0 +1,40 @@ +OpenTelemetry Python Configuration Prototype +============================================ + +This prototype first needs the ``src/opentelemetry/configuration/_interna/path_function.py`` +to be generated with the ``opentelemetry.configuration.render_schema`` function. + +Once this file is generated, implement the functions defined there. + +To create any provider object first create a ``Resource`` object: + +.. code-block:: python + + from opentelemetry.configuration._internal.path_function import set_resource + from opentelemetry.configuration import ( + resolve_schema, + process_schema, + create_object, + validate_configuration, + ) + from pathlib import Path + + data_path = Path(__file__).parent.joinpath("data") + + configuration = validate_configuration( + data_path.joinpath("kitchen-sink.yaml") + ) + + processed_schema = process_schema( + resolve_schema( + data_path.joinpath("opentelemetry_configuration.json") + ) + ) + + set_resource( + create_object(configuration, processed_schema, "resource") + ) + + tracer_provider = create_object( + configuration, processed_schema, "tracer_provider" + ) diff --git a/prototypes/python/noxfile.py b/prototypes/python/noxfile.py new file mode 100644 index 0000000..614a99f --- /dev/null +++ b/prototypes/python/noxfile.py @@ -0,0 +1,12 @@ +from nox import session + + +@session(python=["3.11"], reuse_venv=True) +def test(session): + session.install(".") + session.install("-r", "requirements.txt") + + if session.posargs: + session.run("pytest", *session.posargs) + else: + session.run("pytest", "tests/test_configuration.py") diff --git a/prototypes/python/pyproject.toml b/prototypes/python/pyproject.toml new file mode 100644 index 0000000..3f4d97e --- /dev/null +++ b/prototypes/python/pyproject.toml @@ -0,0 +1,53 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "opentelemetry-configuration" +dynamic = ["version"] +description = "OpenTelemetry Python Configuration" +readme = "README.rst" +license = "Apache-2.0" +requires-python = ">=3.7" +authors = [ + { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" }, +] +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Typing :: Typed", +] +dependencies = [ + "opentelemetry-api", + "opentelemetry-sdk", + "opentelemetry-exporter-otlp", + "opentelemetry-exporter-jaeger", + "opentelemetry-exporter-zipkin", + "jsonschema", + "pyyaml", + "jsonref", + "jinja2" +] + +[project.urls] +Homepage = "https://github.com/open-telemetry/opentelemetry-python/tree/main/opentelemetry-configuration" + +[tool.hatch.version] +path = "src/opentelemetry/configuration/version.py" + +[tool.hatch.build.targets.sdist] +include = [ + "/src", + "/tests", +] + +[tool.hatch.build.targets.wheel] +packages = ["src/opentelemetry"] diff --git a/prototypes/python/requirements.txt b/prototypes/python/requirements.txt new file mode 100644 index 0000000..d0f5095 --- /dev/null +++ b/prototypes/python/requirements.txt @@ -0,0 +1,3 @@ +pytest +pdbpp +ipdb diff --git a/prototypes/python/src/opentelemetry/configuration/__init__.py b/prototypes/python/src/opentelemetry/configuration/__init__.py new file mode 100644 index 0000000..e10f9b5 --- /dev/null +++ b/prototypes/python/src/opentelemetry/configuration/__init__.py @@ -0,0 +1,35 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +The OpenTelemetry Configuration package is an implementation of the +OpenTelemetry Configuration Specification +""" + + +from opentelemetry.configuration._internal import ( + resolve_schema, + validate_configuration, + process_schema, + render_schema, + create_object, +) + +__all__ = [ + "resolve_schema", + "validate_configuration", + "process_schema", + "render_schema", + "create_object", +] diff --git a/prototypes/python/src/opentelemetry/configuration/_internal/__init__.py b/prototypes/python/src/opentelemetry/configuration/_internal/__init__.py new file mode 100644 index 0000000..9b7dded --- /dev/null +++ b/prototypes/python/src/opentelemetry/configuration/_internal/__init__.py @@ -0,0 +1,371 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from yaml import safe_load +from jsonref import JsonRef +from os.path import exists +from pathlib import Path +from os import getcwd +from collections import OrderedDict +from json import loads as json_loads +from jsonref import loads as jsonref_loads +from jsonschema.validators import Draft202012Validator +from referencing import Registry, Resource +from opentelemetry.configuration._internal.path_function import path_function +from jinja2 import Environment, FileSystemLoader + + +def resolve_schema(json_file_path) -> dict: + + root_path = json_file_path.absolute() + + with open(json_file_path, "r") as json_file: + dictionary = jsonref_loads( + json_file.read(), base_uri=root_path.as_uri() + ) + + return dictionary + + +def validate_configuration(configuration_file_path: str) -> dict: + + with open(configuration_file_path, "r") as configuration_file: + + configuration = safe_load(configuration_file) + + root_path = Path(getcwd()).parent.parent + + schema_path = str( + root_path. + joinpath("schema"). + joinpath("opentelemetry_configuration.json") + ) + + if not exists(schema_path): + raise Exception(f"{schema_path} does not exist") + + def retrieve_from_path(path: str): + return Resource.from_contents(json_loads(Path(path).read_text())) + + Draft202012Validator( + {"$ref": schema_path}, + registry=Registry(retrieve=retrieve_from_path) + ).validate(configuration) + + return configuration + + +def process_schema(schema: dict): + + type_type = { + "integer": "int", + "boolean": "bool", + "string": "str", + "array": "list", + "object": "object", + "number": "float" + } + + def traverse( + schema: dict, + schema_value_id_stack: list, + schema_key_stack: list, + recursive_path: list, + processed_schema: dict, + ): + schema_value_type = schema.get("type") + + if schema_value_type == "array": + traverse( + schema["items"], + schema_value_id_stack, + schema_key_stack, + recursive_path, + processed_schema, + ) + + elif schema_value_type == "object": + + schema_properties = schema.get("properties", {}) + + all_attributes = set(schema_properties.keys()) + + positional_attributes = set(schema.get("required", [])) + + optional_attributes = ( + all_attributes.difference(positional_attributes) + ) + + positional_attributes = sorted(list(positional_attributes)) + optional_attributes = sorted(list(optional_attributes)) + + result_positional_attributes = OrderedDict() + result_optional_attributes = OrderedDict() + + for positional_attribute in positional_attributes: + + result_positional_attributes[positional_attribute] = ( + type_type[ + schema_properties[positional_attribute]["type"] + ] + ) + + for optional_attribute in optional_attributes: + + result_optional_attributes[optional_attribute] = ( + type_type[ + schema_properties[optional_attribute]["type"] + ] + ) + + children = {} + + processed_schema[schema_key_stack[-1]] = { + "function_name": "_".join(schema_key_stack[1:]), + "positional_attributes": result_positional_attributes, + "optional_attributes": result_optional_attributes, + "additional_properties": ( + schema.get("additionalProperties", False) + or "patternProperties" in schema.keys() + ), + "recursive_path": recursive_path, + "children": children + } + + if recursive_path: + return + + for ( + schema_properties_key, + schema_properties_value + ) in schema_properties.items(): + + schema_properties_value_type = ( + schema_properties_value.get("type") + ) + + if ( + schema_properties_value_type != "object" + and schema_properties_value_type != "array" + ): + continue + + if isinstance(schema_properties_value, JsonRef): + schema_properties_value_id = ( + id(schema_properties_value.__subject__) + ) + + else: + schema_properties_value_id = id(schema_properties_value) + + is_recursive = ( + schema_properties_value_id in schema_value_id_stack + ) + + schema_value_id_stack.append(schema_properties_value_id) + schema_key_stack.append(schema_properties_key) + + recursive_path = [] + + if is_recursive: + + for ( + current_schema_key_stack, + current_schema_value_id + ) in zip( + schema_key_stack[1:], + schema_value_id_stack + ): + recursive_path.append(current_schema_key_stack) + if ( + schema_properties_value_id + == current_schema_value_id + ): + break + + traverse( + schema_properties_value, + schema_value_id_stack, + schema_key_stack, + recursive_path, + children, + ) + + schema_value_id_stack.pop() + schema_key_stack.pop() + + processed_schema = {} + + traverse(schema, [], [""], [], processed_schema) + + return processed_schema[""]["children"] + + +def render_schema(processed_schema: dict): + + def traverse( + processed_schema: dict, + schema_function: dict, + function_arguments: dict, + ): + + for ( + processed_schema_key, + processed_schema_value + ) in processed_schema.items(): + + function_arguments[processed_schema_value["function_name"]] = { + "optional_attributes": ( + processed_schema_value["optional_attributes"] + ), + "positional_attributes": ( + processed_schema_value["positional_attributes"] + ), + "additional_properties": ( + processed_schema_value["additional_properties"] + ), + } + + schema_function_children = {} + schema_function[processed_schema_key] = { + "function": processed_schema_value["function_name"], + "children": schema_function_children, + "recursive_path": processed_schema_value["recursive_path"] + } + + children = processed_schema_value["children"] + + if children: + traverse( + children, schema_function_children, function_arguments + ) + + schema_function = {} + function_arguments = {} + traverse(processed_schema, schema_function, function_arguments) + + current_path = Path(__file__).parent + + environment = Environment( + loader=FileSystemLoader(current_path.joinpath("templates")) + ) + + with open("path_function.py", "w") as result_py_file: + + result_py_file.write( + "\n".join( + [ + f"{line} # noqa" if len(line) > 80 else line + for line in environment.get_template("template.jinja2"). + render(locals()).split("\n") + ] + ) + ) + + +def create_object( + configuration: dict, processed_schema: dict, object_name: str +) -> object: + + def create_object( + configuration: dict, + processed_schema: dict, + path_function: dict, + original_processed_schema: dict, + original_path_function: dict, + ) -> object: + + positional_arguments = [] + optional_arguments = {} + + for configuration_key, configuration_value in ( + configuration.items() + ): + + if isinstance(configuration_value, dict): + + if processed_schema["recursive_path"]: + + new_processed_schema = original_processed_schema + new_path_function = original_path_function + + for path in processed_schema["recursive_path"]: + new_processed_schema = ( + new_processed_schema[path]["children"] + ) + new_path_function = ( + new_path_function[path]["children"] + ) + + new_processed_schema = ( + new_processed_schema[configuration_key] + ) + new_path_function = ( + new_path_function[configuration_key] + ) + else: + new_processed_schema = ( + processed_schema["children"][configuration_key] + ) + new_path_function = ( + path_function["children"][configuration_key] + ) + + object_ = create_object( + configuration_value, + new_processed_schema, + new_path_function, + original_processed_schema, + original_path_function, + ) + + elif isinstance(configuration_value, list): + + object_ = [] + + for element in configuration_value: + + object_.append( + create_object( + element, + processed_schema["children"][configuration_key], + path_function["children"][configuration_key], + original_processed_schema, + original_path_function, + ) + ) + + else: + + object_ = configuration_value + + if configuration_key in ( + processed_schema["positional_attributes"].keys() + ): + positional_arguments.append(object_) + + else: + optional_arguments[configuration_key] = object_ + + return path_function["function"]( + *positional_arguments, **optional_arguments + ) + + return create_object( + configuration[object_name], + processed_schema[object_name], + path_function[object_name], + processed_schema, + path_function, + ) diff --git a/prototypes/python/src/opentelemetry/configuration/_internal/path_function.py b/prototypes/python/src/opentelemetry/configuration/_internal/path_function.py new file mode 100644 index 0000000..3783098 --- /dev/null +++ b/prototypes/python/src/opentelemetry/configuration/_internal/path_function.py @@ -0,0 +1,1066 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from opentelemetry.sdk.resources import Resource +from opentelemetry.sdk.trace import ( + TracerProvider, SynchronousMultiSpanProcessor, SpanLimits +) +from opentelemetry.sdk.trace.export import ( + BatchSpanProcessor, ConsoleSpanExporter, SimpleSpanProcessor +) +from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import ( + OTLPSpanExporter as GRPCOTLPSpanExporter +) +from opentelemetry.exporter.otlp.proto.http.trace_exporter import ( + OTLPSpanExporter as HTTPOTLPSpanExporter +) +from opentelemetry.exporter.zipkin.proto.http import ZipkinExporter +from opentelemetry.sdk.trace.sampling import ( + ParentBasedTraceIdRatio, + ALWAYS_OFF, + ALWAYS_ON, +) +from urllib.parse import urlparse +from unittest.mock import Mock + +_resource = None + + +def set_resource(resource): + global _resource + _resource = resource + + +def attribute_limits( + attribute_count_limit: int = None, + attribute_value_length_limit: int = None, + **kwargs +): + pass + + +def logger_provider( + limits: object = None, + processors: list = None +): + pass + + +def logger_provider_processors( + batch: object = None, + simple: object = None, + **kwargs +): + pass + + +def logger_provider_processors_batch( + exporter: object, + export_timeout: int = None, + max_export_batch_size: int = None, + max_queue_size: int = None, + schedule_delay: int = None +): + pass + + +def logger_provider_processors_batch_exporter( + otlp: object = None, + **kwargs +): + pass + + +def logger_provider_processors_batch_exporter_otlp( + endpoint: str, + protocol: str, + certificate: str = None, + client_certificate: str = None, + client_key: str = None, + compression: str = None, + headers: object = None, + timeout: int = None +): + pass + + +def logger_provider_processors_batch_exporter_otlp_headers( + **kwargs +): + pass + + +def logger_provider_processors_simple( + exporter: object +): + pass + + +def logger_provider_processors_simple_exporter( + otlp: object = None, + **kwargs +): + pass + + +def logger_provider_processors_simple_exporter_otlp( + endpoint: str, + protocol: str, + certificate: str = None, + client_certificate: str = None, + client_key: str = None, + compression: str = None, + headers: object = None, + timeout: int = None +): + pass + + +def logger_provider_processors_simple_exporter_otlp_headers( + **kwargs +): + pass + + +def logger_provider_limits( + attribute_count_limit: int = None, + attribute_value_length_limit: int = None +): + pass + + +def meter_provider( + readers: list = None, + views: list = None +): + pass + + +def meter_provider_readers( + periodic: object = None, + pull: object = None +): + pass + + +def meter_provider_readers_periodic( + exporter: object, + interval: int = None, + timeout: int = None +): + pass + + +def meter_provider_readers_periodic_exporter( + console: object = None, + otlp: object = None, + prometheus: object = None, + **kwargs +): + pass + + +def meter_provider_readers_periodic_exporter_otlp( + endpoint: str, + protocol: str, + certificate: str = None, + client_certificate: str = None, + client_key: str = None, + compression: str = None, + default_histogram_aggregation: str = None, + headers: object = None, + temporality_preference: str = None, + timeout: int = None +): + pass + + +def meter_provider_readers_periodic_exporter_otlp_headers( + **kwargs +): + pass + + +def meter_provider_readers_periodic_exporter_console(): + pass + + +def meter_provider_readers_periodic_exporter_prometheus( + host: str = None, + port: int = None +): + pass + + +def meter_provider_readers_pull( + exporter: object +): + pass + + +def meter_provider_readers_pull_exporter( + console: object = None, + otlp: object = None, + prometheus: object = None, + **kwargs +): + pass + + +def meter_provider_readers_pull_exporter_otlp( + endpoint: str, + protocol: str, + certificate: str = None, + client_certificate: str = None, + client_key: str = None, + compression: str = None, + default_histogram_aggregation: str = None, + headers: object = None, + temporality_preference: str = None, + timeout: int = None +): + pass + + +def meter_provider_readers_pull_exporter_otlp_headers( + **kwargs +): + pass + + +def meter_provider_readers_pull_exporter_console(): + pass + + +def meter_provider_readers_pull_exporter_prometheus( + host: str = None, + port: int = None +): + pass + + +def meter_provider_views( + selector: object = None, + stream: object = None +): + pass + + +def meter_provider_views_selector( + instrument_name: str = None, + instrument_type: str = None, + meter_name: str = None, + meter_schema_url: str = None, + meter_version: str = None, + unit: str = None +): + pass + + +def meter_provider_views_stream( + aggregation: object = None, + attribute_keys: list = None, + description: str = None, + name: str = None +): + pass + + +def meter_provider_views_stream_aggregation( + base2_exponential_bucket_histogram: object = None, + default: object = None, + drop: object = None, + explicit_bucket_histogram: object = None, + last_value: object = None, + sum: object = None +): + pass + + +def meter_provider_views_stream_aggregation_default(): + pass + + +def meter_provider_views_stream_aggregation_drop(): + pass + + +def meter_provider_views_stream_aggregation_explicit_bucket_histogram( + boundaries: list = None, + record_min_max: bool = None +): + pass + + +def meter_provider_views_stream_aggregation_base2_exponential_bucket_histogram( + max_scale: int = None, + max_size: int = None, + record_min_max: bool = None +): + pass + + +def meter_provider_views_stream_aggregation_last_value(): + pass + + +def meter_provider_views_stream_aggregation_sum(): + pass + + +def propagator( + composite: list = None, + **kwargs +): + pass + + +def tracer_provider( + limits: object = None, + processors: list = None, + sampler: object = None +): + # FIXME how to define shutdown_on_exit? + # FIXME how to define id_generator? + # FIXME how to define if the span processors should be synchronous or not? + + synchronous_multi_span_processor = SynchronousMultiSpanProcessor() + + if processors is not None: + for processor in processors: + synchronous_multi_span_processor.add_span_processor(processor) + + return TracerProvider( + sampler=sampler, + resource=_resource, + active_span_processor=synchronous_multi_span_processor, + span_limits=limits + ) + + +def tracer_provider_processors( + batch: object = None, + simple: object = None, + **kwargs +): + return batch or simple + + +def tracer_provider_processors_batch( + exporter: object, + export_timeout: int = None, + max_export_batch_size: int = None, + max_queue_size: int = None, + schedule_delay: int = None +): + return BatchSpanProcessor( + exporter, + max_queue_size=max_queue_size, + schedule_delay_millis=schedule_delay, + max_export_batch_size=max_export_batch_size, + export_timeout_millis=export_timeout + ) + + +def tracer_provider_processors_batch_exporter( + console: object = None, + otlp: object = None, + zipkin: object = None, + **kwargs +): + return console or otlp or zipkin + + +def tracer_provider_processors_batch_exporter_otlp( + endpoint: str, + protocol: str, + certificate: str = None, + client_certificate: str = None, + client_key: str = None, + compression: str = None, + headers: object = None, + timeout: int = None +): + protocol = urlparse(protocol).scheme + + if protocol.startswith("http"): + exporter_class = HTTPOTLPSpanExporter + + else: + exporter_class = GRPCOTLPSpanExporter + + return exporter_class( + endpoint=endpoint, + # insecure=None, + # FIXME somehow create credentials here + # from grpc.credentials import create_credentials + # credentials=create_credentials() + headers=headers, + timeout=timeout, + # compression=compression + ) + + +def tracer_provider_processors_batch_exporter_otlp_headers( + **kwargs +): + return kwargs + + +def tracer_provider_processors_batch_exporter_console(): + return ConsoleSpanExporter + pass + + +def tracer_provider_processors_batch_exporter_zipkin( + endpoint: str, + timeout: int = None +): + return ZipkinExporter(endpoint, timeout=timeout) + + +def tracer_provider_processors_simple( + exporter: object +): + return SimpleSpanProcessor(exporter) + + +def tracer_provider_processors_simple_exporter( + console: object = None, + otlp: object = None, + zipkin: object = None, + **kwargs +): + return console or otlp or zipkin + + +def tracer_provider_processors_simple_exporter_otlp( + endpoint: str, + protocol: str, + certificate: str = None, + client_certificate: str = None, + client_key: str = None, + compression: str = None, + headers: object = None, + timeout: int = None +): + protocol = urlparse(protocol).scheme + + if protocol.startswith("http"): + exporter_class = HTTPOTLPSpanExporter + + else: + exporter_class = GRPCOTLPSpanExporter + + return exporter_class( + endpoint=endpoint, + # insecure=None, + # FIXME somehow create credentials here + # from grpc.credentials import create_credentials + # credentials=create_credentials() + headers=headers, + timeout=timeout, + # compression=compression + ) + + +def tracer_provider_processors_simple_exporter_otlp_headers( + **kwargs +): + return kwargs + + +def tracer_provider_processors_simple_exporter_console(): + return ConsoleSpanExporter() + + +def tracer_provider_processors_simple_exporter_zipkin( + endpoint: str, + timeout: int = None +): + return ZipkinExporter(endpoint, timeout=timeout) + + +def tracer_provider_limits( + attribute_count_limit: int = None, + attribute_value_length_limit: int = None, + event_attribute_count_limit: int = None, + event_count_limit: int = None, + link_attribute_count_limit: int = None, + link_count_limit: int = None +): + return SpanLimits( + max_span_attributes=attribute_count_limit, + max_span_attribute_length=attribute_value_length_limit, + max_event_attributes=event_count_limit, + max_events=event_count_limit, + max_link_attributes=link_attribute_count_limit, + max_links=link_count_limit, + ) + + +def tracer_provider_sampler( + always_off: object = None, + always_on: object = None, + jaeger_remote: object = None, + parent_based: object = None, + trace_id_ratio_based: object = None, + **kwargs +): + return Mock( + type="sampler", + always_off=always_off, + always_on=always_on, + jaeger_remote=jaeger_remote, + parent_based=parent_based, + trace_id_ratio_based=trace_id_ratio_based, + **kwargs + ) + + +def tracer_provider_sampler_always_off(): + return ALWAYS_OFF + + +def tracer_provider_sampler_always_on(): + return ALWAYS_ON + + +def tracer_provider_sampler_jaeger_remote( + endpoint: str = None, + initial_sampler: object = None, + interval: int = None +): + return Mock( + type="jaeger_remote", + endpoint=endpoint, + initial_sampler=initial_sampler, + interval=interval + ) + + +def tracer_provider_sampler_jaeger_remote_initial_sampler( + always_off: object = None, + always_on: object = None, + jaeger_remote: object = None, + parent_based: object = None, + trace_id_ratio_based: object = None, + **kwargs +): + return Mock( + type="initial_sampler", + always_off=always_off, + always_on=always_on, + jaeger_remote=jaeger_remote, + parent_based=parent_based, + trace_id_ratio_based=trace_id_ratio_based, + **kwargs + ) + + +def tracer_provider_sampler_parent_based( + local_parent_not_sampled: object = None, + local_parent_sampled: object = None, + remote_parent_not_sampled: object = None, + remote_parent_sampled: object = None, + root: object = None +): + return Mock( + type="parent_based", + local_parent_not_sampled=local_parent_not_sampled, + local_parent_sampled=local_parent_sampled, + remote_parent_not_sampled=remote_parent_not_sampled, + remote_parent_sampled=remote_parent_sampled, + root=root, + ) + + +def tracer_provider_sampler_parent_based_root( + always_off: object = None, + always_on: object = None, + jaeger_remote: object = None, + parent_based: object = None, + trace_id_ratio_based: object = None, + **kwargs +): + return Mock( + type="root", + always_off=always_off, + always_on=always_on, + jaeger_remote=jaeger_remote, + parent_based=parent_based, + trace_id_ratio_based=trace_id_ratio_based, + **kwargs + ) + + +def tracer_provider_sampler_parent_based_remote_parent_sampled( + always_off: object = None, + always_on: object = None, + jaeger_remote: object = None, + parent_based: object = None, + trace_id_ratio_based: object = None, + **kwargs +): + return Mock( + type="remote_parent_sampled", + always_off=always_off, + always_on=always_on, + jaeger_remote=jaeger_remote, + parent_based=parent_based, + trace_id_ratio_based=trace_id_ratio_based, + **kwargs + ) + + +def tracer_provider_sampler_parent_based_remote_parent_not_sampled( + always_off: object = None, + always_on: object = None, + jaeger_remote: object = None, + parent_based: object = None, + trace_id_ratio_based: object = None, + **kwargs +): + return Mock( + type="remote_parent_not_sampled", + always_off=always_off, + always_on=always_on, + jaeger_remote=jaeger_remote, + parent_based=parent_based, + trace_id_ratio_based=trace_id_ratio_based, + **kwargs + ) + + +def tracer_provider_sampler_parent_based_local_parent_sampled( + always_off: object = None, + always_on: object = None, + jaeger_remote: object = None, + parent_based: object = None, + trace_id_ratio_based: object = None, + **kwargs +): + return Mock( + type="local_parent_sampled", + always_off=always_off, + always_on=always_on, + jaeger_remote=jaeger_remote, + parent_based=parent_based, + trace_id_ratio_based=trace_id_ratio_based, + **kwargs + ) + + +def tracer_provider_sampler_parent_based_local_parent_not_sampled( + always_off: object = None, + always_on: object = None, + jaeger_remote: object = None, + parent_based: object = None, + trace_id_ratio_based: object = None, + **kwargs +): + return Mock( + type="local_parent_not_sampled", + always_off=always_off, + always_on=always_on, + jaeger_remote=jaeger_remote, + parent_based=parent_based, + trace_id_ratio_based=trace_id_ratio_based, + **kwargs + ) + + +def tracer_provider_sampler_trace_id_ratio_based( + ratio: float = None +): + return ParentBasedTraceIdRatio(ratio) + + +def resource( + attributes: object = None, + schema_url: str = None +): + return Resource.create(attributes=attributes, schema_url=schema_url) + + +def resource_attributes( + service_name: str = None, + **kwargs +): + return {"service.name": service_name, **kwargs} + + +path_function = { + "attribute_limits": { + "function": attribute_limits, + "children": {}, + "recursive_path": [], + }, + "logger_provider": { + "function": logger_provider, + "children": { + "processors": { + "function": logger_provider_processors, + "children": { + "batch": { + "function": logger_provider_processors_batch, + "children": { + "exporter": { + "function": logger_provider_processors_batch_exporter, # noqa + "children": { + "otlp": { + "function": logger_provider_processors_batch_exporter_otlp, # noqa + "children": { + "headers": { + "function": logger_provider_processors_batch_exporter_otlp_headers, # noqa + "children": {}, + "recursive_path": [], + }, + }, + "recursive_path": [], + }, + }, + "recursive_path": [], + }, + }, + "recursive_path": [], + }, + "simple": { + "function": logger_provider_processors_simple, + "children": { + "exporter": { + "function": logger_provider_processors_simple_exporter, # noqa + "children": { + "otlp": { + "function": logger_provider_processors_simple_exporter_otlp, # noqa + "children": { + "headers": { + "function": logger_provider_processors_simple_exporter_otlp_headers, # noqa + "children": {}, + "recursive_path": [], + }, + }, + "recursive_path": [], + }, + }, + "recursive_path": [], + }, + }, + "recursive_path": [], + }, + }, + "recursive_path": [], + }, + "limits": { + "function": logger_provider_limits, + "children": {}, + "recursive_path": [], + }, + }, + "recursive_path": [], + }, + "meter_provider": { + "function": meter_provider, + "children": { + "readers": { + "function": meter_provider_readers, + "children": { + "periodic": { + "function": meter_provider_readers_periodic, + "children": { + "exporter": { + "function": meter_provider_readers_periodic_exporter, # noqa + "children": { + "otlp": { + "function": meter_provider_readers_periodic_exporter_otlp, # noqa + "children": { + "headers": { + "function": meter_provider_readers_periodic_exporter_otlp_headers, # noqa + "children": {}, + "recursive_path": [], + }, + }, + "recursive_path": [], + }, + "console": { + "function": meter_provider_readers_periodic_exporter_console, # noqa + "children": {}, + "recursive_path": [], + }, + "prometheus": { + "function": meter_provider_readers_periodic_exporter_prometheus, # noqa + "children": {}, + "recursive_path": [], + }, + }, + "recursive_path": [], + }, + }, + "recursive_path": [], + }, + "pull": { + "function": meter_provider_readers_pull, + "children": { + "exporter": { + "function": meter_provider_readers_pull_exporter, # noqa + "children": { + "otlp": { + "function": meter_provider_readers_pull_exporter_otlp, # noqa + "children": { + "headers": { + "function": meter_provider_readers_pull_exporter_otlp_headers, # noqa + "children": {}, + "recursive_path": [], + }, + }, + "recursive_path": [], + }, + "console": { + "function": meter_provider_readers_pull_exporter_console, # noqa + "children": {}, + "recursive_path": [], + }, + "prometheus": { + "function": meter_provider_readers_pull_exporter_prometheus, # noqa + "children": {}, + "recursive_path": [], + }, + }, + "recursive_path": [], + }, + }, + "recursive_path": [], + }, + }, + "recursive_path": [], + }, + "views": { + "function": meter_provider_views, + "children": { + "selector": { + "function": meter_provider_views_selector, + "children": {}, + "recursive_path": [], + }, + "stream": { + "function": meter_provider_views_stream, + "children": { + "aggregation": { + "function": meter_provider_views_stream_aggregation, # noqa + "children": { + "default": { + "function": meter_provider_views_stream_aggregation_default, # noqa + "children": {}, + "recursive_path": [], + }, + "drop": { + "function": meter_provider_views_stream_aggregation_drop, # noqa + "children": {}, + "recursive_path": [], + }, + "explicit_bucket_histogram": { + "function": meter_provider_views_stream_aggregation_explicit_bucket_histogram, # noqa + "children": {}, + "recursive_path": [], + }, + "base2_exponential_bucket_histogram": { + "function": meter_provider_views_stream_aggregation_base2_exponential_bucket_histogram, # noqa + "children": {}, + "recursive_path": [], + }, + "last_value": { + "function": meter_provider_views_stream_aggregation_last_value, # noqa + "children": {}, + "recursive_path": [], + }, + "sum": { + "function": meter_provider_views_stream_aggregation_sum, # noqa + "children": {}, + "recursive_path": [], + }, + }, + "recursive_path": [], + }, + }, + "recursive_path": [], + }, + }, + "recursive_path": [], + }, + }, + "recursive_path": [], + }, + "propagator": { + "function": propagator, + "children": {}, + "recursive_path": [], + }, + "tracer_provider": { + "function": tracer_provider, + "children": { + "processors": { + "function": tracer_provider_processors, + "children": { + "batch": { + "function": tracer_provider_processors_batch, + "children": { + "exporter": { + "function": tracer_provider_processors_batch_exporter, # noqa + "children": { + "otlp": { + "function": tracer_provider_processors_batch_exporter_otlp, # noqa + "children": { + "headers": { + "function": tracer_provider_processors_batch_exporter_otlp_headers, # noqa + "children": {}, + "recursive_path": [], + }, + }, + "recursive_path": [], + }, + "console": { + "function": tracer_provider_processors_batch_exporter_console, # noqa + "children": {}, + "recursive_path": [], + }, + "zipkin": { + "function": tracer_provider_processors_batch_exporter_zipkin, # noqa + "children": {}, + "recursive_path": [], + }, + }, + "recursive_path": [], + }, + }, + "recursive_path": [], + }, + "simple": { + "function": tracer_provider_processors_simple, + "children": { + "exporter": { + "function": tracer_provider_processors_simple_exporter, # noqa + "children": { + "otlp": { + "function": tracer_provider_processors_simple_exporter_otlp, # noqa + "children": { + "headers": { + "function": tracer_provider_processors_simple_exporter_otlp_headers, # noqa + "children": {}, + "recursive_path": [], + }, + }, + "recursive_path": [], + }, + "console": { + "function": tracer_provider_processors_simple_exporter_console, # noqa + "children": {}, + "recursive_path": [], + }, + "zipkin": { + "function": tracer_provider_processors_simple_exporter_zipkin, # noqa + "children": {}, + "recursive_path": [], + }, + }, + "recursive_path": [], + }, + }, + "recursive_path": [], + }, + }, + "recursive_path": [], + }, + "limits": { + "function": tracer_provider_limits, + "children": {}, + "recursive_path": [], + }, + "sampler": { + "function": tracer_provider_sampler, + "children": { + "always_off": { + "function": tracer_provider_sampler_always_off, + "children": {}, + "recursive_path": [], + }, + "always_on": { + "function": tracer_provider_sampler_always_on, + "children": {}, + "recursive_path": [], + }, + "jaeger_remote": { + "function": tracer_provider_sampler_jaeger_remote, + "children": { + "initial_sampler": { + "function": tracer_provider_sampler_jaeger_remote_initial_sampler, # noqa + "children": {}, + "recursive_path": ['tracer_provider', 'sampler'], # noqa + }, + }, + "recursive_path": [], + }, + "parent_based": { + "function": tracer_provider_sampler_parent_based, + "children": { + "root": { + "function": tracer_provider_sampler_parent_based_root, # noqa + "children": {}, + "recursive_path": ['tracer_provider', 'sampler'], # noqa + }, + "remote_parent_sampled": { + "function": tracer_provider_sampler_parent_based_remote_parent_sampled, # noqa + "children": {}, + "recursive_path": ['tracer_provider', 'sampler'], # noqa + }, + "remote_parent_not_sampled": { + "function": tracer_provider_sampler_parent_based_remote_parent_not_sampled, # noqa + "children": {}, + "recursive_path": ['tracer_provider', 'sampler'], # noqa + }, + "local_parent_sampled": { + "function": tracer_provider_sampler_parent_based_local_parent_sampled, # noqa + "children": {}, + "recursive_path": ['tracer_provider', 'sampler'], # noqa + }, + "local_parent_not_sampled": { + "function": tracer_provider_sampler_parent_based_local_parent_not_sampled, # noqa + "children": {}, + "recursive_path": ['tracer_provider', 'sampler'], # noqa + }, + }, + "recursive_path": [], + }, + "trace_id_ratio_based": { + "function": tracer_provider_sampler_trace_id_ratio_based, # noqa + "children": {}, + "recursive_path": [], + }, + }, + "recursive_path": [], + }, + }, + "recursive_path": [], + }, + "resource": { + "function": resource, + "children": { + "attributes": { + "function": resource_attributes, + "children": {}, + "recursive_path": [], + }, + }, + "recursive_path": [], + }, +} diff --git a/prototypes/python/src/opentelemetry/configuration/_internal/templates/template.jinja2 b/prototypes/python/src/opentelemetry/configuration/_internal/templates/template.jinja2 new file mode 100644 index 0000000..7226d6e --- /dev/null +++ b/prototypes/python/src/opentelemetry/configuration/_internal/templates/template.jinja2 @@ -0,0 +1,70 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +_resource = None + + +def set_resource(resource): + global _resource + _resource = resource + + +{% for function_argument_key, function_argument_value in function_arguments.items() -%} + def {{ function_argument_key }}( + {%- if function_argument_value["positional_attributes"]|length == 0 and function_argument_value["optional_attributes"]|length == 0 and not function_argument_value["additional_properties"] -%} + ): + {%- else -%} + {{- "\n" -}} + {%- for positional_attribute_key, positional_attribute_value in function_argument_value["positional_attributes"].items() -%} + {{- " " }}{{ positional_attribute_key }}: {{ positional_attribute_value -}} + {%- if not loop.last or function_argument_value["optional_attributes"]|length > 0 or function_argument_value["additional_properties"]-%} + {{ ",\n" }} + {%- endif -%} + {%- endfor -%} + {%- for optional_attribute_key, optional_attribute_value in function_argument_value["optional_attributes"].items() -%} + {{- " " }}{{ optional_attribute_key }}: {{ optional_attribute_value }} = None + {%- if not loop.last or function_argument_value["additional_properties"]-%} + {{ ",\n" }} + {%- endif -%} + {%- endfor -%} + {%- if function_argument_value["additional_properties"] -%} + {{ " **kwargs" }} + {%- endif -%} + {{- "\n" -}} + ): + {%- endif -%} + {{- "\n pass\n\n\n" -}} +{%- endfor -%} + +{%- macro render_dict(schema_function, indentation) -%} + {%- for key, value in schema_function.items() -%} + {{- " " * indentation * 4 }}"{{ key }}":{{ " " -}} + {%- if value is mapping -%} + {%- if value|length == 0 -%} + {{- "{},\n" -}} + {%- else -%} + {{- "{\n" -}} + {{- render_dict(value, indentation + 1) -}} + {{- " " * indentation * 4 }}{{ "},\n" -}} + {%- endif -%} + {%- else -%} + {{- value }}{{ ",\n" -}} + {%- endif -%} + {%- endfor -%} +{%- endmacro -%} + +path_function = {{ "{\n" -}} +{{- render_dict(schema_function, 1) -}} +{{- "}" -}} diff --git a/prototypes/python/src/opentelemetry/configuration/py.typed b/prototypes/python/src/opentelemetry/configuration/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/prototypes/python/src/opentelemetry/configuration/version.py b/prototypes/python/src/opentelemetry/configuration/version.py new file mode 100644 index 0000000..87246ad --- /dev/null +++ b/prototypes/python/src/opentelemetry/configuration/version.py @@ -0,0 +1,15 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +__version__ = "1.21.0.dev" diff --git a/prototypes/python/tests/__init__.py b/prototypes/python/tests/__init__.py new file mode 100644 index 0000000..b0a6f42 --- /dev/null +++ b/prototypes/python/tests/__init__.py @@ -0,0 +1,13 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/prototypes/python/tests/data/common.json b/prototypes/python/tests/data/common.json new file mode 100644 index 0000000..55b14f8 --- /dev/null +++ b/prototypes/python/tests/data/common.json @@ -0,0 +1,58 @@ +{ + "$id": "https://opentelemetry.io/otelconfig/common.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Common", + "type": "object", + "$defs": { + "Headers": { + "type": "object", + "title": "Headers", + "patternProperties": { + ".*": { + "type": "string" + } + } + }, + "Otlp": { + "type": "object", + "additionalProperties": false, + "properties": { + "protocol": { + "type": "string", + "pattern": "^(http|grpc)\\/(protobuf|json)" + }, + "endpoint": { + "type": "string" + }, + "certificate": { + "type": "string" + }, + "client_key": { + "type": "string" + }, + "client_certificate": { + "type": "string" + }, + "headers": { + "$ref": "#/$defs/Headers" + }, + "compression": { + "type": "string" + }, + "timeout": { + "type": "integer", + "minimum": 0 + } + }, + "required": [ + "endpoint", + "protocol" + ], + "title": "Otlp" + }, + "Console": { + "type": "object", + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/prototypes/python/tests/data/kitchen-sink.yaml b/prototypes/python/tests/data/kitchen-sink.yaml new file mode 100644 index 0000000..11ea883 --- /dev/null +++ b/prototypes/python/tests/data/kitchen-sink.yaml @@ -0,0 +1,380 @@ +# kitchen-sink.yaml demonstrates all configurable surface area, including explanatory comments. +# +# It DOES NOT represent expected real world configuration, as it makes strange configuration +# choices in an effort to exercise the full surface area. +# +# Configuration values are set to their defaults when default values are defined. + +# The file format version +file_format: "0.1" + +# Configure if the SDK is disabled or not. This is not required to be provided +# to ensure the SDK isn't disabled, the default value when this is not provided +# is for the SDK to be enabled. +# +# Environment variable: OTEL_SDK_DISABLED +disabled: false + +# Configure general attribute limits. See also tracer_provider.limits, logger_provider.limits. +attribute_limits: + # Configure max attribute value size. + # + # Environment variable: OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT + attribute_value_length_limit: 4096 + # Configure max attribute count. + # + # Environment variable: OTEL_ATTRIBUTE_COUNT_LIMIT + attribute_count_limit: 128 + +# Configure logger provider. +logger_provider: + # Configure log record processors. + processors: + # Configure a batch log record processor. + - batch: + # Configure delay interval (in milliseconds) between two consecutive exports. + # + # Environment variable: OTEL_BLRP_SCHEDULE_DELAY + schedule_delay: 5000 + # Configure maximum allowed time (in milliseconds) to export data. + # + # Environment variable: OTEL_BLRP_EXPORT_TIMEOUT + export_timeout: 30000 + # Configure maximum queue size. + # + # Environment variable: OTEL_BLRP_MAX_QUEUE_SIZE + max_queue_size: 2048 + # Configure maximum batch size. + # + # Environment variable: OTEL_BLRP_MAX_EXPORT_BATCH_SIZE + max_export_batch_size: 512 + # Configure exporter. + # + # Environment variable: OTEL_LOGS_EXPORTER + exporter: + # Configure exporter to be OTLP. + otlp: + # Configure protocol. + # + # Environment variable: OTEL_EXPORTER_OTLP_PROTOCOL, OTEL_EXPORTER_OTLP_LOGS_PROTOCOL + protocol: http/protobuf + # Configure endpoint. + # + # Environment variable: OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_LOGS_ENDPOINT + endpoint: http://localhost:4318 + # Configure certificate. + # + # Environment variable: OTEL_EXPORTER_OTLP_CERTIFICATE, OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE + certificate: /app/cert.pem + # Configure mTLS private client key. + # + # Environment variable: OTEL_EXPORTER_OTLP_CLIENT_KEY, OTEL_EXPORTER_OTLP_LOGS_CLIENT_KEY + client_key: /app/cert.pem + # Configure mTLS client certificate. + # + # Environment variable: OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE, OTEL_EXPORTER_OTLP_LOGS_CLIENT_CERTIFICATE + client_certificate: /app/cert.pem + # Configure headers. + # + # Environment variable: OTEL_EXPORTER_OTLP_HEADERS, OTEL_EXPORTER_OTLP_LOGS_HEADERS + headers: + api-key: "1234" + # Configure compression. + # + # Environment variable: OTEL_EXPORTER_OTLP_COMPRESSION, OTEL_EXPORTER_OTLP_LOGS_COMPRESSION + compression: gzip + # Configure max time (in milliseconds) to wait for each export. + # + # Environment variable: OTEL_EXPORTER_OTLP_TIMEOUT, OTEL_EXPORTER_OTLP_LOGS_TIMEOUT + timeout: 10000 + # Configure log record limits. See also attribute_limits. + limits: + # Configure max log record attribute value size. Overrides attribute_limits.attribute_value_length_limit. + # + # Environment variable: OTEL_LOGRECORD_ATTRIBUTE_VALUE_LENGTH_LIMIT + attribute_value_length_limit: 4096 + # Configure max log record attribute count. Overrides attribute_limits.attribute_count_limit. + # + # Environment variable: OTEL_LOGRECORD_ATTRIBUTE_COUNT_LIMIT + attribute_count_limit: 128 + +# Configure meter provider. +meter_provider: + # Configure metric readers. + readers: + # Configure a pull-based metric reader. + - pull: + # Configure exporter. + # + # Environment variable: OTEL_METRICS_EXPORTER + exporter: + # Configure exporter to be prometheus. + prometheus: + # Configure host. + # + # Environment variable: OTEL_EXPORTER_PROMETHEUS_HOST + host: localhost + # Configure port. + # + # Environment variable: OTEL_EXPORTER_PROMETHEUS_PORT + port: 9464 + # Configure a periodic metric reader. + - periodic: + # Configure delay interval (in milliseconds) between start of two consecutive exports. + # + # Environment variable: OTEL_METRIC_EXPORT_INTERVAL + interval: 5000 + # Configure maximum allowed time (in milliseconds) to export data. + # + # Environment variable: OTEL_METRIC_EXPORT_TIMEOUT + timeout: 30000 + # Configure exporter. + # + # Environment variable: OTEL_METRICS_EXPORTER + exporter: + # Configure exporter to be OTLP. + otlp: + # Configure protocol. + # + # Environment variable: OTEL_EXPORTER_OTLP_PROTOCOL, OTEL_EXPORTER_OTLP_METRICS_PROTOCOL + protocol: http/protobuf + # Configure endpoint. + # + # Environment variable: OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_METRICS_ENDPOINT + endpoint: http://localhost:4318 + # Configure certificate. + # + # Environment variable: OTEL_EXPORTER_OTLP_CERTIFICATE, OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE + certificate: /app/cert.pem + # Configure mTLS private client key. + # + # Environment variable: OTEL_EXPORTER_OTLP_CLIENT_KEY, OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY + client_key: /app/cert.pem + # Configure mTLS client certificate. + # + # Environment variable: OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE, OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE + client_certificate: /app/cert.pem + # Configure headers. + # + # Environment variable: OTEL_EXPORTER_OTLP_HEADERS, OTEL_EXPORTER_OTLP_METRICS_HEADERS + headers: + api-key: !!str 1234 + # Configure compression. + # + # Environment variable: OTEL_EXPORTER_OTLP_COMPRESSION, OTEL_EXPORTER_OTLP_METRICS_COMPRESSION + compression: gzip + # Configure max time (in milliseconds) to wait for each export. + # + # Environment variable: OTEL_EXPORTER_OTLP_TIMEOUT, OTEL_EXPORTER_OTLP_METRICS_TIMEOUT + timeout: 10000 + # Configure temporality preference. + # + # Environment variable: OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE + temporality_preference: delta + # Configure default histogram aggregation. + # + # Environment variable: OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION + default_histogram_aggregation: base2_exponential_bucket_histogram + # Configure a periodic metric reader. + - periodic: + # Configure exporter. + exporter: + # Configure exporter to be console. + console: {} + # Configure views. Each view has a selector which determines the instrument(s) it applies to, and a configuration for the resulting stream(s). + views: + # Configure a view. + - selector: + # Configure instrument name selection criteria. + instrument_name: my-instrument + # Configure instrument type selection criteria. + instrument_type: histogram + # Configure the instrument unit selection criteria. + unit: ms + # Configure meter name selection criteria. + meter_name: my-meter + # Configure meter version selection criteria. + meter_version: 1.0.0 + # Configure meter schema url selection criteria. + meter_schema_url: https://opentelemetry.io/schemas/1.16.0 + # Configure stream. + stream: + # Configure metric name of the resulting stream(s). + name: new_instrument_name + # Configure metric description of the resulting stream(s). + description: new_description + # Configure aggregation of the resulting stream(s). Known values include: default, drop, explicit_bucket_histogram, base2_exponential_bucket_histogram, last_value, sum. + aggregation: + # Configure aggregation to be explicit_bucket_histogram. + explicit_bucket_histogram: + # Configure bucket boundaries. + boundaries: [ 0.0, 5.0, 10.0, 25.0, 50.0, 75.0, 100.0, 250.0, 500.0, 750.0, 1000.0, 2500.0, 5000.0, 7500.0, 10000.0 ] + # Configure record min and max. + record_min_max: true + # Configure attribute keys retained in the resulting stream(s). + attribute_keys: + - key1 + - key2 + +# Configure text map context propagators. +# +# Environment variable: OTEL_PROPAGATORS +propagator: + composite: [tracecontext, baggage, b3, b3multi, jaeger, xray, ottrace] + +# Configure tracer provider. +tracer_provider: + # Configure span processors. + processors: + # Configure a batch span processor. + - batch: + # Configure delay interval (in milliseconds) between two consecutive exports. + # + # Environment variable: OTEL_BSP_SCHEDULE_DELAY + schedule_delay: 5000 + # Configure maximum allowed time (in milliseconds) to export data. + # + # Environment variable: OTEL_BSP_EXPORT_TIMEOUT + export_timeout: 30000 + # Configure maximum queue size. + # + # Environment variable: OTEL_BSP_MAX_QUEUE_SIZE + max_queue_size: 2048 + # Configure maximum batch size. + # + # Environment variable: OTEL_BSP_MAX_EXPORT_BATCH_SIZE + max_export_batch_size: 512 + # Configure exporter. + # + # Environment variable: OTEL_TRACES_EXPORTER + exporter: + # Configure exporter to be OTLP. + otlp: + # Configure protocol. + # + # Environment variable: OTEL_EXPORTER_OTLP_PROTOCOL, OTEL_EXPORTER_OTLP_TRACES_PROTOCOL + protocol: http/protobuf + # Configure endpoint. + # + # Environment variable: OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_TRACES_ENDPOINT + endpoint: http://localhost:4318 + # Configure certificate. + # + # Environment variable: OTEL_EXPORTER_OTLP_CERTIFICATE, OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE + certificate: /app/cert.pem + # Configure mTLS private client key. + # + # Environment variable: OTEL_EXPORTER_OTLP_CLIENT_KEY, OTEL_EXPORTER_OTLP_TRACES_CLIENT_KEY + client_key: /app/cert.pem + # Configure mTLS client certificate. + # + # Environment variable: OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE, OTEL_EXPORTER_OTLP_TRACES_CLIENT_CERTIFICATE + client_certificate: /app/cert.pem + # Configure headers. + # + # Environment variable: OTEL_EXPORTER_OTLP_HEADERS, OTEL_EXPORTER_OTLP_TRACES_HEADERS + headers: + api-key: !!str 1234 + # Configure compression. + # + # Environment variable: OTEL_EXPORTER_OTLP_COMPRESSION, OTEL_EXPORTER_OTLP_TRACES_COMPRESSION + compression: gzip + # Configure max time (in milliseconds) to wait for each export. + # + # Environment variable: OTEL_EXPORTER_OTLP_TIMEOUT, OTEL_EXPORTER_OTLP_TRACES_TIMEOUT + timeout: 10000 + # Configure a batch span processor. + - batch: + # Configure exporter. + # + # Environment variable: OTEL_TRACES_EXPORTER + exporter: + # Configure exporter to be zipkin. + zipkin: + # Configure endpoint. + # + # Environment variable: OTEL_EXPORTER_ZIPKIN_ENDPOINT + endpoint: http://localhost:9411/api/v2/spans + # Configure max time (in milliseconds) to wait for each export. + # + # Environment variable: OTEL_EXPORTER_ZIPKIN_TIMEOUT + timeout: 10000 + # Configure a simple span processor. + - simple: + # Configure exporter. + exporter: + # Configure exporter to be console. + console: {} + # Configure span limits. See also attribute_limits. + limits: + # Configure max span attribute value size. Overrides attribute_limits.attribute_value_length_limit. + # + # Environment variable: OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT + attribute_value_length_limit: 4096 + # Configure max span attribute count. Overrides attribute_limits.attribute_count_limit. + # + # Environment variable: OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT + attribute_count_limit: 128 + # Configure max span event count. + # + # Environment variable: OTEL_SPAN_EVENT_COUNT_LIMIT + event_count_limit: 128 + # Configure max span link count. + # + # Environment variable: OTEL_SPAN_LINK_COUNT_LIMIT + link_count_limit: 128 + # Configure max attributes per span event. + # + # Environment variable: OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT + event_attribute_count_limit: 128 + # Configure max attributes per span link. + # + # Environment variable: OTEL_LINK_ATTRIBUTE_COUNT_LIMIT + link_attribute_count_limit: 128 + # Configure the sampler. + sampler: + # Configure sampler to be parent_based. Known values include: always_off, always_on, jaeger_remote, parent_based, trace_id_ratio_based. + # + # Environment variable: OTEL_TRACES_SAMPLER=parentbased_* + parent_based: + # Configure root sampler. + # + # Environment variable: OTEL_TRACES_SAMPLER=parentbased_traceidratio + root: + # Configure sampler to be trace_id_ratio_based. + trace_id_ratio_based: + # Configure trace_id_ratio. + # + # Environment variable: OTEL_TRACES_SAMPLER_ARG=traceidratio=0.0001 + ratio: 0.0001 + # Configure remote_parent_sampled sampler. + remote_parent_sampled: + # Configure sampler to be always_on. + always_on: {} + # Configure remote_parent_not_sampled sampler. + remote_parent_not_sampled: + # Configure sampler to be always_off. + always_off: {} + # Configure local_parent_sampled sampler. + local_parent_sampled: + # Configure sampler to be always_on. + always_on: {} + # Configure local_parent_not_sampled sampler. + local_parent_not_sampled: + parent_based: + remote_parent_not_sampled: + trace_id_ratio_based: + ratio: 0.0001 + +# Configure resource for all signals. +resource: + # Configure resource attributes. + # + # Environment variable: OTEL_RESOURCE_ATTRIBUTES + attributes: + # Configure `service.name` resource attribute + # + # Environment variable: OTEL_SERVICE_NAME + service.name: !!str "unknown_service" + # Configure the resource schema URL. + schema_url: https://opentelemetry.io/schemas/1.16.0 diff --git a/prototypes/python/tests/data/logger_provider.json b/prototypes/python/tests/data/logger_provider.json new file mode 100644 index 0000000..e41568a --- /dev/null +++ b/prototypes/python/tests/data/logger_provider.json @@ -0,0 +1,109 @@ +{ + "$id": "https://opentelemetry.io/otelconfig/logger_provider.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "LoggerProvider", + "type": "object", + "additionalProperties": false, + "properties": { + "processors": { + "type": "array", + "items": { + "$ref": "#/$defs/LogRecordProcessor" + } + }, + "limits": { + "$ref": "#/$defs/LogRecordLimits" + } + }, + "$defs": { + "SimpleLogRecordProcessor": { + "type": "object", + "additionalProperties": false, + "properties": { + "exporter": { + "$ref": "#/$defs/LogRecordExporter" + } + }, + "required": [ + "exporter" + ] + }, + "BatchLogRecordProcessor": { + "type": "object", + "additionalProperties": false, + "properties": { + "schedule_delay": { + "type": "integer", + "minimum": 0 + }, + "export_timeout": { + "type": "integer", + "minimum": 0 + }, + "max_queue_size": { + "type": "integer", + "minimum": 0 + }, + "max_export_batch_size": { + "type": "integer", + "minimum": 0 + }, + "exporter": { + "$ref": "#/$defs/LogRecordExporter" + } + }, + "required": [ + "exporter" + ] + }, + "LogRecordExporter": { + "type": "object", + "additionalProperties": true, + "minProperties": 1, + "maxProperties": 1, + "properties": { + "otlp": { + "$ref": "common.json#/$defs/Otlp" + } + }, + "patternProperties": { + ".*": { + "type": "object" + } + } + }, + "LogRecordLimits": { + "type": "object", + "additionalProperties": false, + "properties": { + "attribute_value_length_limit": { + "type": "integer", + "minimum": 0 + }, + "attribute_count_limit": { + "type": "integer", + "minimum": 0 + } + } + }, + "LogRecordProcessor": { + "type": "object", + "additionalProperties": true, + "minProperties": 1, + "maxProperties": 1, + "properties": { + "batch": { + "$ref": "#/$defs/BatchLogRecordProcessor" + }, + "simple": { + "$ref": "#/$defs/SimpleLogRecordProcessor" + } + }, + "patternProperties": { + ".*": { + "type": "object" + } + } + } + } +} diff --git a/prototypes/python/tests/data/meter_provider.json b/prototypes/python/tests/data/meter_provider.json new file mode 100644 index 0000000..9c6dc79 --- /dev/null +++ b/prototypes/python/tests/data/meter_provider.json @@ -0,0 +1,264 @@ +{ + "$id": "https://opentelemetry.io/otelconfig/meter_provider.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "MeterProvider", + "type": "object", + "additionalProperties": false, + "properties": { + "readers": { + "type": "array", + "items": { + "$ref": "#/$defs/MetricReader" + } + }, + "views": { + "type": "array", + "items": { + "$ref": "#/$defs/View" + } + } + }, + "$defs": { + "PeriodicMetricReader": { + "type": "object", + "additionalProperties": false, + "properties": { + "interval": { + "type": "integer", + "minimum": 0 + }, + "timeout": { + "type": "integer", + "minimum": 0 + }, + "exporter": { + "$ref": "#/$defs/MetricExporter" + } + }, + "required": [ + "exporter" + ], + "title": "PeriodicMetricReader" + }, + "PullMetricReader": { + "type": "object", + "additionalProperties": false, + "properties": { + "exporter": { + "$ref": "#/$defs/MetricExporter" + } + }, + "required": [ + "exporter" + ], + "title": "PullMetricReader" + }, + "MetricExporter": { + "type": "object", + "additionalProperties": true, + "minProperties": 1, + "maxProperties": 1, + "properties": { + "otlp": { + "$ref": "#/$defs/OtlpMetric" + }, + "console": { + "$ref": "common.json#/$defs/Console" + }, + "prometheus": { + "$ref": "#/$defs/Prometheus" + } + }, + "patternProperties": { + ".*": { + "type": "object" + } + } + }, + "Prometheus": { + "type": "object", + "additionalProperties": false, + "properties": { + "host": { + "type": "string" + }, + "port": { + "type": "integer" + } + } + }, + "MetricReader": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "maxProperties": 1, + "properties": { + "periodic": { + "$ref": "#/$defs/PeriodicMetricReader" + }, + "pull": { + "$ref": "#/$defs/PullMetricReader" + } + } + }, + "OtlpMetric": { + "type": "object", + "additionalProperties": false, + "properties": { + "protocol": { + "type": "string", + "pattern": "^(http|grpc)\\/(protobuf|json)" + }, + "endpoint": { + "type": "string" + }, + "certificate": { + "type": "string" + }, + "client_key": { + "type": "string" + }, + "client_certificate": { + "type": "string" + }, + "headers": { + "$ref": "common.json#/$defs/Headers" + }, + "compression": { + "type": "string" + }, + "timeout": { + "type": "integer", + "minimum": 0 + }, + "temporality_preference": { + "type": "string" + }, + "default_histogram_aggregation": { + "type": "string", + "enum": [ + "explicit_bucket_histogram", + "base2_exponential_bucket_histogram" + ] + } + }, + "required": [ + "endpoint", + "protocol" + ], + "title": "OtlpMetric" + }, + "View": { + "type": "object", + "additionalProperties": false, + "properties": { + "selector": { + "title": "Selector", + "type": "object", + "additionalProperties": false, + "properties": { + "instrument_name": { + "type": "string" + }, + "instrument_type": { + "type": "string", + "enum": [ + "counter", + "histogram", + "observable_counter", + "observable_gauge", + "observable_up_down_counter", + "up_down_counter" + ] + }, + "unit": { + "type": "string" + }, + "meter_name": { + "type": "string" + }, + "meter_version": { + "type": "string" + }, + "meter_schema_url": { + "type": "string" + } + } + }, + "stream": { + "title": "Stream", + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "aggregation": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "maxProperties": 1, + "properties": { + "default": { + "type": "object", + "additionalProperties": false + }, + "drop": { + "type": "object", + "additionalProperties": false + }, + "explicit_bucket_histogram": { + "type": "object", + "additionalProperties": false, + "properties": { + "boundaries": { + "type": "array", + "items": { + "type": "number" + } + }, + "record_min_max": { + "type": "boolean" + } + } + }, + "base2_exponential_bucket_histogram": { + "type": "object", + "additionalProperties": false, + "properties": { + "max_scale": { + "type": "integer" + }, + "max_size": { + "type": "integer" + }, + "record_min_max": { + "type": "boolean" + } + } + }, + "last_value": { + "type": "object", + "additionalProperties": false + }, + "sum": { + "type": "object", + "additionalProperties": false + } + } + }, + "attribute_keys": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + } +} diff --git a/prototypes/python/tests/data/opentelemetry_configuration.json b/prototypes/python/tests/data/opentelemetry_configuration.json new file mode 100644 index 0000000..d606299 --- /dev/null +++ b/prototypes/python/tests/data/opentelemetry_configuration.json @@ -0,0 +1,50 @@ +{ + "$id": "https://opentelemetry.io/otelconfig/opentelemetry_configuration.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "OpenTelemetryConfiguration", + "type": "object", + "additionalProperties": true, + "properties": { + "file_format": { + "type": "string" + }, + "disabled": { + "type": "boolean" + }, + "attribute_limits": { + "$ref": "#/$defs/AttributeLimits" + }, + "logger_provider": { + "$ref": "logger_provider.json" + }, + "meter_provider": { + "$ref": "meter_provider.json" + }, + "propagator": { + "$ref": "propagator.json" + }, + "tracer_provider": { + "$ref": "tracer_provider.json" + }, + "resource": { + "$ref": "resource.json" + } + }, + "required": [ + "file_format" + ], + "$defs": { + "AttributeLimits": { + "type": "object", + "additionalProperties": true, + "properties": { + "attribute_value_length_limit": { + "type": "integer" + }, + "attribute_count_limit": { + "type": "integer" + } + } + } + } +} diff --git a/prototypes/python/tests/data/propagator.json b/prototypes/python/tests/data/propagator.json new file mode 100644 index 0000000..1b29ec2 --- /dev/null +++ b/prototypes/python/tests/data/propagator.json @@ -0,0 +1,17 @@ +{ + "$id": "https://opentelemetry.io/otelconfig/propagator.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Propagator", + "type": "object", + "minProperties": 1, + "maxProperties": 1, + "additionalProperties": true, + "properties": { + "composite": { + "type": "array", + "items": { + "type": "string" + } + } + } +} diff --git a/prototypes/python/tests/data/resource.json b/prototypes/python/tests/data/resource.json new file mode 100644 index 0000000..d18267a --- /dev/null +++ b/prototypes/python/tests/data/resource.json @@ -0,0 +1,27 @@ +{ + "$id": "https://opentelemetry.io/otelconfig/resource.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Resource", + "type": "object", + "additionalProperties": false, + "properties": { + "attributes": { + "$ref": "#/$defs/Attributes" + }, + "schema_url": { + "type": "string" + } + }, + "$defs": { + "Attributes": { + "title": "Attributes", + "type": "object", + "additionalProperties": true, + "properties": { + "service.name": { + "type": "string" + } + } + } + } +} diff --git a/prototypes/python/tests/data/tracer_provider.json b/prototypes/python/tests/data/tracer_provider.json new file mode 100644 index 0000000..96dfa5c --- /dev/null +++ b/prototypes/python/tests/data/tracer_provider.json @@ -0,0 +1,220 @@ +{ + "$id": "https://opentelemetry.io/otelconfig/tracer_provider.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "TracerProvider", + "type": "object", + "additionalProperties": false, + "properties": { + "processors": { + "type": "array", + "items": { + "$ref": "#/$defs/SpanProcessor" + } + }, + "limits": { + "$ref": "#/$defs/SpanLimits" + }, + "sampler": { + "$ref": "#/$defs/Sampler" + } + }, + "$defs": { + "BatchSpanProcessor": { + "type": "object", + "additionalProperties": false, + "title": "BatchSpanProcessor", + "properties": { + "schedule_delay": { + "type": "integer", + "minimum": 0 + }, + "export_timeout": { + "type": "integer", + "minimum": 0 + }, + "max_queue_size": { + "type": "integer", + "minimum": 0 + }, + "max_export_batch_size": { + "type": "integer", + "minimum": 0 + }, + "exporter": { + "$ref": "#/$defs/SpanExporter" + } + }, + "required": [ + "exporter" + ] + }, + "Sampler": { + "type": "object", + "additionalProperties": true, + "minProperties": 1, + "maxProperties": 1, + "properties": { + "always_off": { + "type": "object", + "additionalProperties": false + }, + "always_on": { + "type": "object", + "additionalProperties": false + }, + "jaeger_remote": { + "type": "object", + "additionalProperties": false, + "properties": { + "endpoint": { + "type": "string" + }, + "interval": { + "type": "integer", + "minimum": 0 + }, + "initial_sampler": { + "$ref": "#/$defs/Sampler" + } + } + }, + "parent_based": { + "type": "object", + "additionalProperties": false, + "properties": { + "root": { + "$ref": "#/$defs/Sampler" + }, + "remote_parent_sampled": { + "$ref": "#/$defs/Sampler" + }, + "remote_parent_not_sampled": { + "$ref": "#/$defs/Sampler" + }, + "local_parent_sampled": { + "$ref": "#/$defs/Sampler" + }, + "local_parent_not_sampled": { + "$ref": "#/$defs/Sampler" + } + } + }, + "trace_id_ratio_based": { + "type": "object", + "additionalProperties": false, + "properties": { + "ratio": { + "type": "number" + } + } + } + }, + "patternProperties": { + ".*": { + "type": "object" + } + } + }, + "SimpleSpanProcessor": { + "type": "object", + "additionalProperties": false, + "title": "SimpleSpanProcessor", + "properties": { + "exporter": { + "$ref": "#/$defs/SpanExporter" + } + }, + "required": [ + "exporter" + ] + }, + "SpanExporter": { + "type": "object", + "additionalProperties": true, + "minProperties": 1, + "maxProperties": 1, + "properties": { + "otlp": { + "$ref": "common.json#/$defs/Otlp" + }, + "console": { + "$ref": "common.json#/$defs/Console" + }, + "zipkin": { + "$ref": "#/$defs/Zipkin" + } + }, + "patternProperties": { + ".*": { + "type": "object" + } + } + }, + "SpanLimits": { + "type": "object", + "additionalProperties": false, + "properties": { + "attribute_value_length_limit": { + "type": "integer", + "minimum": 0 + }, + "attribute_count_limit": { + "type": "integer", + "minimum": 0 + }, + "event_count_limit": { + "type": "integer", + "minimum": 0 + }, + "link_count_limit": { + "type": "integer", + "minimum": 0 + }, + "event_attribute_count_limit": { + "type": "integer", + "minimum": 0 + }, + "link_attribute_count_limit": { + "type": "integer", + "minimum": 0 + } + } + }, + "SpanProcessor": { + "type": "object", + "additionalProperties": true, + "minProperties": 1, + "maxProperties": 1, + "properties": { + "batch": { + "$ref": "#/$defs/BatchSpanProcessor" + }, + "simple": { + "$ref": "#/$defs/SimpleSpanProcessor" + } + }, + "patternProperties": { + ".*": { + "type": "object" + } + } + }, + "Zipkin": { + "type": "object", + "additionalProperties": false, + "properties": { + "endpoint": { + "type": "string" + }, + "timeout": { + "type": "integer", + "minimum": 0 + } + }, + "required": [ + "endpoint" + ], + "title": "Zipkin" + } + } +} diff --git a/prototypes/python/tests/test_configuration.py b/prototypes/python/tests/test_configuration.py new file mode 100644 index 0000000..295e000 --- /dev/null +++ b/prototypes/python/tests/test_configuration.py @@ -0,0 +1,112 @@ +# Copyright The OpenTelemetry Authors + +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from opentelemetry.configuration._internal.path_function import set_resource +from opentelemetry.configuration import ( + resolve_schema, + process_schema, + create_object, + validate_configuration, +) +from pathlib import Path + +data_path = Path(__file__).parent.joinpath("data") + + +def test_create_object(): + + configuration = validate_configuration( + data_path.joinpath("kitchen-sink.yaml") + ) + + processed_schema = process_schema( + resolve_schema( + data_path.joinpath("opentelemetry_configuration.json") + ) + ) + + set_resource( + create_object(configuration, processed_schema, "resource") + ) + + tracer_provider = create_object( + configuration, processed_schema, "tracer_provider" + ) + + assert ( + tracer_provider. + sampler. + parent_based. + root. + trace_id_ratio_based. + _root. + _rate + ) == 0.0001 + + assert ( + tracer_provider. + sampler. + parent_based. + local_parent_not_sampled. + parent_based. + remote_parent_not_sampled. + trace_id_ratio_based. + _root. + _rate + ) == 0.0001 + + assert ( + tracer_provider. + _span_limits. + max_events + ) == 128 + + assert ( + tracer_provider. + _active_span_processor. + _span_processors[0]. + max_queue_size + ) == 2048 + + assert ( + tracer_provider. + _active_span_processor. + _span_processors[0]. + span_exporter. + _headers["api-key"] + ) == "1234" + + assert ( + tracer_provider. + _active_span_processor. + _span_processors[1]. + span_exporter. + endpoint + ) == "http://localhost:9411/api/v2/spans" + + assert ( + tracer_provider. + _active_span_processor. + _span_processors[2]. + span_exporter. + __class__. + __name__ + ) == "ConsoleSpanExporter" + + assert ( + tracer_provider. + _resource. + _schema_url + ) == "https://opentelemetry.io/schemas/1.16.0" From cc396e78b5aeb87e4733f23472951584f9ffc38e Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Tue, 12 Dec 2023 16:01:02 -0600 Subject: [PATCH 2/3] Separate validation from loading --- .../src/opentelemetry/configuration/__init__.py | 2 ++ .../opentelemetry/configuration/_internal/__init__.py | 11 ++++++----- prototypes/python/tests/test_configuration.py | 5 ++++- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/prototypes/python/src/opentelemetry/configuration/__init__.py b/prototypes/python/src/opentelemetry/configuration/__init__.py index e10f9b5..afe051a 100644 --- a/prototypes/python/src/opentelemetry/configuration/__init__.py +++ b/prototypes/python/src/opentelemetry/configuration/__init__.py @@ -24,6 +24,7 @@ process_schema, render_schema, create_object, + load_configuration ) __all__ = [ @@ -32,4 +33,5 @@ "process_schema", "render_schema", "create_object", + "load_configuration" ] diff --git a/prototypes/python/src/opentelemetry/configuration/_internal/__init__.py b/prototypes/python/src/opentelemetry/configuration/_internal/__init__.py index 9b7dded..e93df4f 100644 --- a/prototypes/python/src/opentelemetry/configuration/_internal/__init__.py +++ b/prototypes/python/src/opentelemetry/configuration/_internal/__init__.py @@ -38,11 +38,14 @@ def resolve_schema(json_file_path) -> dict: return dictionary -def validate_configuration(configuration_file_path: str) -> dict: +def load_configuration(configuration_file_path: str) -> dict: with open(configuration_file_path, "r") as configuration_file: - configuration = safe_load(configuration_file) + return safe_load(configuration_file) + + +def validate_configuration(configuration: dict): root_path = Path(getcwd()).parent.parent @@ -63,10 +66,8 @@ def retrieve_from_path(path: str): registry=Registry(retrieve=retrieve_from_path) ).validate(configuration) - return configuration - -def process_schema(schema: dict): +def process_schema(schema: dict) -> dict: type_type = { "integer": "int", diff --git a/prototypes/python/tests/test_configuration.py b/prototypes/python/tests/test_configuration.py index 295e000..cbe1f38 100644 --- a/prototypes/python/tests/test_configuration.py +++ b/prototypes/python/tests/test_configuration.py @@ -19,6 +19,7 @@ process_schema, create_object, validate_configuration, + load_configuration, ) from pathlib import Path @@ -27,10 +28,12 @@ def test_create_object(): - configuration = validate_configuration( + configuration = load_configuration( data_path.joinpath("kitchen-sink.yaml") ) + validate_configuration(configuration) + processed_schema = process_schema( resolve_schema( data_path.joinpath("opentelemetry_configuration.json") From d70793ac359ac152a64e1a7d5b37c0242e3cd7cd Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Mon, 11 Dec 2023 18:11:54 -0600 Subject: [PATCH 3/3] Add support for environment variable substitution Fixes #60 --- .../opentelemetry/configuration/__init__.py | 6 +- .../configuration/_internal/__init__.py | 114 +++++- ...kitchen-sink.yaml => configuration_0.yaml} | 0 .../python/tests/data/configuration_1.yaml | 380 ++++++++++++++++++ prototypes/python/tests/test_configuration.py | 53 ++- 5 files changed, 532 insertions(+), 21 deletions(-) rename prototypes/python/tests/data/{kitchen-sink.yaml => configuration_0.yaml} (100%) create mode 100644 prototypes/python/tests/data/configuration_1.yaml diff --git a/prototypes/python/src/opentelemetry/configuration/__init__.py b/prototypes/python/src/opentelemetry/configuration/__init__.py index afe051a..e2dbadc 100644 --- a/prototypes/python/src/opentelemetry/configuration/__init__.py +++ b/prototypes/python/src/opentelemetry/configuration/__init__.py @@ -24,7 +24,8 @@ process_schema, render_schema, create_object, - load_configuration + load_configuration, + substitute_environment_variables, ) __all__ = [ @@ -33,5 +34,6 @@ "process_schema", "render_schema", "create_object", - "load_configuration" + "load_configuration", + "substitute_environment_variables", ] diff --git a/prototypes/python/src/opentelemetry/configuration/_internal/__init__.py b/prototypes/python/src/opentelemetry/configuration/_internal/__init__.py index e93df4f..b8903cb 100644 --- a/prototypes/python/src/opentelemetry/configuration/_internal/__init__.py +++ b/prototypes/python/src/opentelemetry/configuration/_internal/__init__.py @@ -12,7 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +from ipdb import set_trace +from os import environ from yaml import safe_load +from re import compile as re_compile from jsonref import JsonRef from os.path import exists from pathlib import Path @@ -25,6 +28,18 @@ from opentelemetry.configuration._internal.path_function import path_function from jinja2 import Environment, FileSystemLoader +set_trace + +_environment_variable_regex = re_compile(r"\$\{([a-zA-Z]\w*)\}") +_type_type = { + "integer": int, + "boolean": bool, + "string": str, + "array": list, + "object": object, + "number": float +} + def resolve_schema(json_file_path) -> dict: @@ -69,15 +84,6 @@ def retrieve_from_path(path: str): def process_schema(schema: dict) -> dict: - type_type = { - "integer": "int", - "boolean": "bool", - "string": "str", - "array": "list", - "object": "object", - "number": "float" - } - def traverse( schema: dict, schema_value_id_stack: list, @@ -117,21 +123,28 @@ def traverse( for positional_attribute in positional_attributes: result_positional_attributes[positional_attribute] = ( - type_type[ - schema_properties[positional_attribute]["type"] - ] + str( + _type_type[ + schema_properties[positional_attribute]["type"] + ].__name__ + ) ) for optional_attribute in optional_attributes: result_optional_attributes[optional_attribute] = ( - type_type[ - schema_properties[optional_attribute]["type"] - ] + str( + _type_type[ + schema_properties[optional_attribute]["type"] + ].__name__ + ) ) children = {} + children.update(result_positional_attributes) + children.update(result_optional_attributes) + processed_schema[schema_key_stack[-1]] = { "function_name": "_".join(schema_key_stack[1:]), "positional_attributes": result_positional_attributes, @@ -213,7 +226,7 @@ def traverse( return processed_schema[""]["children"] -def render_schema(processed_schema: dict): +def render_schema(processed_schema: dict, path_function_path: Path): def traverse( processed_schema: dict, @@ -226,6 +239,9 @@ def traverse( processed_schema_value ) in processed_schema.items(): + if not isinstance(processed_schema_value, dict): + continue + function_arguments[processed_schema_value["function_name"]] = { "optional_attributes": ( processed_schema_value["optional_attributes"] @@ -262,7 +278,7 @@ def traverse( loader=FileSystemLoader(current_path.joinpath("templates")) ) - with open("path_function.py", "w") as result_py_file: + with open(path_function_path, "w") as result_py_file: result_py_file.write( "\n".join( @@ -370,3 +386,67 @@ def create_object( processed_schema, path_function, ) + + +def substitute_environment_variables( + configuration: dict, + processed_schema: dict +) -> dict: + + def traverse( + configuration: dict, + processed_schema: dict, + original_processed_schema: dict + ): + + for configuration_key, configuration_value in configuration.items(): + + if configuration_key not in processed_schema.keys(): + continue + + if isinstance(configuration_value, dict): + + recursive_paths = ( + processed_schema[configuration_key]["recursive_path"] + ) + + if recursive_paths: + + children = original_processed_schema + + for recursive_path in recursive_paths: + children = children[recursive_path]["children"] + + else: + children = processed_schema[configuration_key]["children"] + + traverse( + configuration_value, + children, + original_processed_schema + ) + + elif isinstance(configuration_value, list): + + for element in configuration_value: + if isinstance(element, dict): + traverse( + element, + processed_schema[configuration_key]["children"], + original_processed_schema + ) + + elif isinstance(configuration_value, str): + + match = _environment_variable_regex.match(configuration_value) + + if match is not None: + + configuration[configuration_key] = ( + __builtins__[processed_schema[configuration_key]] + (environ.get(match.group(1))) + ) + + traverse(configuration, processed_schema, processed_schema) + + return configuration diff --git a/prototypes/python/tests/data/kitchen-sink.yaml b/prototypes/python/tests/data/configuration_0.yaml similarity index 100% rename from prototypes/python/tests/data/kitchen-sink.yaml rename to prototypes/python/tests/data/configuration_0.yaml diff --git a/prototypes/python/tests/data/configuration_1.yaml b/prototypes/python/tests/data/configuration_1.yaml new file mode 100644 index 0000000..83f4d97 --- /dev/null +++ b/prototypes/python/tests/data/configuration_1.yaml @@ -0,0 +1,380 @@ +# kitchen-sink.yaml demonstrates all configurable surface area, including explanatory comments. +# +# It DOES NOT represent expected real world configuration, as it makes strange configuration +# choices in an effort to exercise the full surface area. +# +# Configuration values are set to their defaults when default values are defined. + +# The file format version +file_format: "0.1" + +# Configure if the SDK is disabled or not. This is not required to be provided +# to ensure the SDK isn't disabled, the default value when this is not provided +# is for the SDK to be enabled. +# +# Environment variable: OTEL_SDK_DISABLED +disabled: false + +# Configure general attribute limits. See also tracer_provider.limits, logger_provider.limits. +attribute_limits: + # Configure max attribute value size. + # + # Environment variable: OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT + attribute_value_length_limit: 4096 + # Configure max attribute count. + # + # Environment variable: OTEL_ATTRIBUTE_COUNT_LIMIT + attribute_count_limit: 128 + +# Configure logger provider. +logger_provider: + # Configure log record processors. + processors: + # Configure a batch log record processor. + - batch: + # Configure delay interval (in milliseconds) between two consecutive exports. + # + # Environment variable: OTEL_BLRP_SCHEDULE_DELAY + schedule_delay: 5000 + # Configure maximum allowed time (in milliseconds) to export data. + # + # Environment variable: OTEL_BLRP_EXPORT_TIMEOUT + export_timeout: ${OTEL_BLRB_EXPORT_TIMEOUT} + # Configure maximum queue size. + # + # Environment variable: OTEL_BLRP_MAX_QUEUE_SIZE + max_queue_size: 2048 + # Configure maximum batch size. + # + # Environment variable: OTEL_BLRP_MAX_EXPORT_BATCH_SIZE + max_export_batch_size: 512 + # Configure exporter. + # + # Environment variable: OTEL_LOGS_EXPORTER + exporter: + # Configure exporter to be OTLP. + otlp: + # Configure protocol. + # + # Environment variable: OTEL_EXPORTER_OTLP_PROTOCOL, OTEL_EXPORTER_OTLP_LOGS_PROTOCOL + protocol: http/protobuf + # Configure endpoint. + # + # Environment variable: OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_LOGS_ENDPOINT + endpoint: http://localhost:4318 + # Configure certificate. + # + # Environment variable: OTEL_EXPORTER_OTLP_CERTIFICATE, OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE + certificate: /app/cert.pem + # Configure mTLS private client key. + # + # Environment variable: OTEL_EXPORTER_OTLP_CLIENT_KEY, OTEL_EXPORTER_OTLP_LOGS_CLIENT_KEY + client_key: /app/cert.pem + # Configure mTLS client certificate. + # + # Environment variable: OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE, OTEL_EXPORTER_OTLP_LOGS_CLIENT_CERTIFICATE + client_certificate: /app/cert.pem + # Configure headers. + # + # Environment variable: OTEL_EXPORTER_OTLP_HEADERS, OTEL_EXPORTER_OTLP_LOGS_HEADERS + headers: + api-key: "1234" + # Configure compression. + # + # Environment variable: OTEL_EXPORTER_OTLP_COMPRESSION, OTEL_EXPORTER_OTLP_LOGS_COMPRESSION + compression: gzip + # Configure max time (in milliseconds) to wait for each export. + # + # Environment variable: OTEL_EXPORTER_OTLP_TIMEOUT, OTEL_EXPORTER_OTLP_LOGS_TIMEOUT + timeout: 10000 + # Configure log record limits. See also attribute_limits. + limits: + # Configure max log record attribute value size. Overrides attribute_limits.attribute_value_length_limit. + # + # Environment variable: OTEL_LOGRECORD_ATTRIBUTE_VALUE_LENGTH_LIMIT + attribute_value_length_limit: 4096 + # Configure max log record attribute count. Overrides attribute_limits.attribute_count_limit. + # + # Environment variable: OTEL_LOGRECORD_ATTRIBUTE_COUNT_LIMIT + attribute_count_limit: 128 + +# Configure meter provider. +meter_provider: + # Configure metric readers. + readers: + # Configure a pull-based metric reader. + - pull: + # Configure exporter. + # + # Environment variable: OTEL_METRICS_EXPORTER + exporter: + # Configure exporter to be prometheus. + prometheus: + # Configure host. + # + # Environment variable: OTEL_EXPORTER_PROMETHEUS_HOST + host: localhost + # Configure port. + # + # Environment variable: OTEL_EXPORTER_PROMETHEUS_PORT + port: 9464 + # Configure a periodic metric reader. + - periodic: + # Configure delay interval (in milliseconds) between start of two consecutive exports. + # + # Environment variable: OTEL_METRIC_EXPORT_INTERVAL + interval: 5000 + # Configure maximum allowed time (in milliseconds) to export data. + # + # Environment variable: OTEL_METRIC_EXPORT_TIMEOUT + timeout: 30000 + # Configure exporter. + # + # Environment variable: OTEL_METRICS_EXPORTER + exporter: + # Configure exporter to be OTLP. + otlp: + # Configure protocol. + # + # Environment variable: OTEL_EXPORTER_OTLP_PROTOCOL, OTEL_EXPORTER_OTLP_METRICS_PROTOCOL + protocol: http/protobuf + # Configure endpoint. + # + # Environment variable: OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_METRICS_ENDPOINT + endpoint: http://localhost:4318 + # Configure certificate. + # + # Environment variable: OTEL_EXPORTER_OTLP_CERTIFICATE, OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE + certificate: /app/cert.pem + # Configure mTLS private client key. + # + # Environment variable: OTEL_EXPORTER_OTLP_CLIENT_KEY, OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY + client_key: /app/cert.pem + # Configure mTLS client certificate. + # + # Environment variable: OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE, OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE + client_certificate: /app/cert.pem + # Configure headers. + # + # Environment variable: OTEL_EXPORTER_OTLP_HEADERS, OTEL_EXPORTER_OTLP_METRICS_HEADERS + headers: + api-key: !!str 1234 + # Configure compression. + # + # Environment variable: OTEL_EXPORTER_OTLP_COMPRESSION, OTEL_EXPORTER_OTLP_METRICS_COMPRESSION + compression: gzip + # Configure max time (in milliseconds) to wait for each export. + # + # Environment variable: OTEL_EXPORTER_OTLP_TIMEOUT, OTEL_EXPORTER_OTLP_METRICS_TIMEOUT + timeout: 10000 + # Configure temporality preference. + # + # Environment variable: OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE + temporality_preference: delta + # Configure default histogram aggregation. + # + # Environment variable: OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION + default_histogram_aggregation: base2_exponential_bucket_histogram + # Configure a periodic metric reader. + - periodic: + # Configure exporter. + exporter: + # Configure exporter to be console. + console: {} + # Configure views. Each view has a selector which determines the instrument(s) it applies to, and a configuration for the resulting stream(s). + views: + # Configure a view. + - selector: + # Configure instrument name selection criteria. + instrument_name: my-instrument + # Configure instrument type selection criteria. + instrument_type: histogram + # Configure the instrument unit selection criteria. + unit: ms + # Configure meter name selection criteria. + meter_name: my-meter + # Configure meter version selection criteria. + meter_version: 1.0.0 + # Configure meter schema url selection criteria. + meter_schema_url: https://opentelemetry.io/schemas/1.16.0 + # Configure stream. + stream: + # Configure metric name of the resulting stream(s). + name: new_instrument_name + # Configure metric description of the resulting stream(s). + description: new_description + # Configure aggregation of the resulting stream(s). Known values include: default, drop, explicit_bucket_histogram, base2_exponential_bucket_histogram, last_value, sum. + aggregation: + # Configure aggregation to be explicit_bucket_histogram. + explicit_bucket_histogram: + # Configure bucket boundaries. + boundaries: [ 0.0, 5.0, 10.0, 25.0, 50.0, 75.0, 100.0, 250.0, 500.0, 750.0, 1000.0, 2500.0, 5000.0, 7500.0, 10000.0 ] + # Configure record min and max. + record_min_max: true + # Configure attribute keys retained in the resulting stream(s). + attribute_keys: + - key1 + - key2 + +# Configure text map context propagators. +# +# Environment variable: OTEL_PROPAGATORS +propagator: + composite: [tracecontext, baggage, b3, b3multi, jaeger, xray, ottrace] + +# Configure tracer provider. +tracer_provider: + # Configure span processors. + processors: + # Configure a batch span processor. + - batch: + # Configure delay interval (in milliseconds) between two consecutive exports. + # + # Environment variable: OTEL_BSP_SCHEDULE_DELAY + schedule_delay: 5000 + # Configure maximum allowed time (in milliseconds) to export data. + # + # Environment variable: OTEL_BSP_EXPORT_TIMEOUT + export_timeout: 30000 + # Configure maximum queue size. + # + # Environment variable: OTEL_BSP_MAX_QUEUE_SIZE + max_queue_size: 2048 + # Configure maximum batch size. + # + # Environment variable: OTEL_BSP_MAX_EXPORT_BATCH_SIZE + max_export_batch_size: 512 + # Configure exporter. + # + # Environment variable: OTEL_TRACES_EXPORTER + exporter: + # Configure exporter to be OTLP. + otlp: + # Configure protocol. + # + # Environment variable: OTEL_EXPORTER_OTLP_PROTOCOL, OTEL_EXPORTER_OTLP_TRACES_PROTOCOL + protocol: http/protobuf + # Configure endpoint. + # + # Environment variable: OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_TRACES_ENDPOINT + endpoint: http://localhost:4318 + # Configure certificate. + # + # Environment variable: OTEL_EXPORTER_OTLP_CERTIFICATE, OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE + certificate: /app/cert.pem + # Configure mTLS private client key. + # + # Environment variable: OTEL_EXPORTER_OTLP_CLIENT_KEY, OTEL_EXPORTER_OTLP_TRACES_CLIENT_KEY + client_key: /app/cert.pem + # Configure mTLS client certificate. + # + # Environment variable: OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE, OTEL_EXPORTER_OTLP_TRACES_CLIENT_CERTIFICATE + client_certificate: /app/cert.pem + # Configure headers. + # + # Environment variable: OTEL_EXPORTER_OTLP_HEADERS, OTEL_EXPORTER_OTLP_TRACES_HEADERS + headers: + api-key: !!str 1234 + # Configure compression. + # + # Environment variable: OTEL_EXPORTER_OTLP_COMPRESSION, OTEL_EXPORTER_OTLP_TRACES_COMPRESSION + compression: gzip + # Configure max time (in milliseconds) to wait for each export. + # + # Environment variable: OTEL_EXPORTER_OTLP_TIMEOUT, OTEL_EXPORTER_OTLP_TRACES_TIMEOUT + timeout: 10000 + # Configure a batch span processor. + - batch: + # Configure exporter. + # + # Environment variable: OTEL_TRACES_EXPORTER + exporter: + # Configure exporter to be zipkin. + zipkin: + # Configure endpoint. + # + # Environment variable: OTEL_EXPORTER_ZIPKIN_ENDPOINT + endpoint: http://localhost:9411/api/v2/spans + # Configure max time (in milliseconds) to wait for each export. + # + # Environment variable: OTEL_EXPORTER_ZIPKIN_TIMEOUT + timeout: 10000 + # Configure a simple span processor. + - simple: + # Configure exporter. + exporter: + # Configure exporter to be console. + console: {} + # Configure span limits. See also attribute_limits. + limits: + # Configure max span attribute value size. Overrides attribute_limits.attribute_value_length_limit. + # + # Environment variable: OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT + attribute_value_length_limit: 4096 + # Configure max span attribute count. Overrides attribute_limits.attribute_count_limit. + # + # Environment variable: OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT + attribute_count_limit: 128 + # Configure max span event count. + # + # Environment variable: OTEL_SPAN_EVENT_COUNT_LIMIT + event_count_limit: 128 + # Configure max span link count. + # + # Environment variable: OTEL_SPAN_LINK_COUNT_LIMIT + link_count_limit: 128 + # Configure max attributes per span event. + # + # Environment variable: OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT + event_attribute_count_limit: 128 + # Configure max attributes per span link. + # + # Environment variable: OTEL_LINK_ATTRIBUTE_COUNT_LIMIT + link_attribute_count_limit: 128 + # Configure the sampler. + sampler: + # Configure sampler to be parent_based. Known values include: always_off, always_on, jaeger_remote, parent_based, trace_id_ratio_based. + # + # Environment variable: OTEL_TRACES_SAMPLER=parentbased_* + parent_based: + # Configure root sampler. + # + # Environment variable: OTEL_TRACES_SAMPLER=parentbased_traceidratio + root: + # Configure sampler to be trace_id_ratio_based. + trace_id_ratio_based: + # Configure trace_id_ratio. + # + # Environment variable: OTEL_TRACES_SAMPLER_ARG=traceidratio=0.0001 + ratio: 0.0001 + # Configure remote_parent_sampled sampler. + remote_parent_sampled: + # Configure sampler to be always_on. + always_on: {} + # Configure remote_parent_not_sampled sampler. + remote_parent_not_sampled: + # Configure sampler to be always_off. + always_off: {} + # Configure local_parent_sampled sampler. + local_parent_sampled: + # Configure sampler to be always_on. + always_on: {} + # Configure local_parent_not_sampled sampler. + local_parent_not_sampled: + parent_based: + remote_parent_not_sampled: + trace_id_ratio_based: + ratio: 0.0001 + +# Configure resource for all signals. +resource: + # Configure resource attributes. + # + # Environment variable: OTEL_RESOURCE_ATTRIBUTES + attributes: + # Configure `service.name` resource attribute + # + # Environment variable: OTEL_SERVICE_NAME + service.name: !!str "unknown_service" + # Configure the resource schema URL. + schema_url: https://opentelemetry.io/schemas/1.16.0 diff --git a/prototypes/python/tests/test_configuration.py b/prototypes/python/tests/test_configuration.py index cbe1f38..dbbb390 100644 --- a/prototypes/python/tests/test_configuration.py +++ b/prototypes/python/tests/test_configuration.py @@ -20,8 +20,13 @@ create_object, validate_configuration, load_configuration, + substitute_environment_variables, + render_schema, ) +from unittest.mock import patch +from os import environ from pathlib import Path +from pytest import fail data_path = Path(__file__).parent.joinpath("data") @@ -29,10 +34,13 @@ def test_create_object(): configuration = load_configuration( - data_path.joinpath("kitchen-sink.yaml") + data_path.joinpath("configuration_0.yaml") ) - validate_configuration(configuration) + try: + validate_configuration(configuration) + except Exception as error: + fail(f"Unexpected exception raised: {error}") processed_schema = process_schema( resolve_schema( @@ -113,3 +121,44 @@ def test_create_object(): _resource. _schema_url ) == "https://opentelemetry.io/schemas/1.16.0" + + +@patch.dict(environ, {"OTEL_BLRB_EXPORT_TIMEOUT": "943"}, clear=True) +def test_substitute_environment_variables(): + configuration = load_configuration( + data_path.joinpath("configuration_1.yaml") + ) + + processed_schema = process_schema( + resolve_schema( + data_path.joinpath("opentelemetry_configuration.json") + ) + ) + configuration = substitute_environment_variables( + configuration, processed_schema + ) + + assert ( + configuration + ["logger_provider"] + ["processors"] + [0] + ["batch"] + ["export_timeout"] + ) == 943 + try: + validate_configuration(configuration) + except Exception as error: + fail(f"Unexpected exception raised: {error}") + + +def test_render(tmpdir): + + render_schema( + process_schema( + resolve_schema( + data_path.joinpath("opentelemetry_configuration.json") + ) + ), + tmpdir.join("path_function.py") + )