Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,13 @@ Once approved, the author of the pull request,
or reviewer if the author does not have commit access,
should "Squash and merge".

### Bumping version of EDOT instrumentations

When new EDOT instrumentations are released we need to update:

- `operator/requirements.txt`, in order to have them available in the Docker image used for the Kubernetes Operator auto-instrumentation
- `elasticotel/instrumentation/bootstrap.py`, in order to make them available to `edot-bootstrap`

### Releasing

Releases tags are signed so you need to have a PGP key set up, you can follow Github documentation on [creating a key](https://docs.github.com/en/authentication/managing-commit-signature-verification/generating-a-new-gpg-key) and
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@ pip install elastic-opentelemetry
## Usage

Our distribution does not install any instrumentation package by default, instead it relies on the
`opentelemetry-bootstrap` command to scan the installed packages and install the available instrumentation.
`edot-bootstrap` command to scan the installed packages and install the available instrumentation, preferring EDOT variants when available.
The following command will install all the instrumentations available for libraries found installed
in your environment:

```bash
opentelemetry-bootstrap --action=install
edot-bootstrap --action=install
```

It will be useful to add this command every time you need to deploy an updated version of your application,
Expand Down
4 changes: 2 additions & 2 deletions docs/get-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,12 @@ pip install elastic-opentelemetry
### Install the available instrumentation

EDOT Python does not install any instrumentation package by default, instead it relies on the
`opentelemetry-bootstrap` command to scan the installed packages and install the available instrumentation.
`edot-bootstrap` command to scan the installed packages and install the available instrumentation.
The following command will install all the instrumentations available for libraries found installed
in your environment:

```bash
opentelemetry-bootstrap --action=install
edot-bootstrap --action=install
```

> [!NOTE]
Expand Down
2 changes: 1 addition & 1 deletion docs/manual-instrumentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ We need to make sure to have Flask and the Flask OpenTelemetry instrumentation i

```bash
pip install flask
opentelemetry-bootstrap --action=install
edot-bootstrap --action=install
```

And then we can run this application with the following command:
Expand Down
2 changes: 1 addition & 1 deletion examples/flask/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ COPY . /app
RUN pip install flask elastic-opentelemetry

# Install all the instrumentations available for the installed packages
RUN opentelemetry-bootstrap -a install
RUN edot-bootstrap -a install

# default flask run port
EXPOSE 5000
Expand Down
4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ dependencies = [
"opentelemetry-instrumentation-system-metrics == 0.49b2",
"opentelemetry-semantic-conventions == 0.49b2",
"opentelemetry-sdk == 1.28.2",
"packaging",
]

[project.optional-dependencies]
Expand All @@ -48,6 +49,9 @@ distro = "elasticotel.distro:ElasticOpenTelemetryDistro"
process_runtime = "elasticotel.sdk.resources:ProcessRuntimeResourceDetector"
telemetry_distro = "elasticotel.sdk.resources:TelemetryDistroResourceDetector"

[project.scripts]
edot-bootstrap = "elasticotel.instrumentation.bootstrap:run"

[project.readme]
file = "README.md"
content-type = "text/markdown"
Expand Down
59 changes: 59 additions & 0 deletions scripts/build_edot_bootstrap_instrumentations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright
# ownership. Elasticsearch B.V. licenses this file to you 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.

import ast

# this requires python 3.11
import tomllib
from pathlib import Path

root_dir = Path(__file__).parent.parent
instrumentations_repo_dir = root_dir.parent / "elastic-otel-python-instrumentations"
instrumentations_dir = instrumentations_repo_dir / "instrumentation"

pyprojects = instrumentations_dir.glob("*/pyproject.toml")

instrumentations = []

for pyproject in pyprojects:
with pyproject.open("rb") as f:
data = tomllib.load(f)

instrumentation_name = data["project"]["name"]
instruments = data["project"]["optional-dependencies"]["instruments"]

version = None
for version_module in pyproject.parent.glob("src/opentelemetry/instrumentation/*/version.py"):
with version_module.open("rb") as vf:
for line in vf:
if line.startswith(b"__version__"):
tree = ast.parse(line)
assignment_value = tree.body[0].value
version = assignment_value.value
break
break

# not a fan of creating multiple entries is we require more than one library but that's the status
# see https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2409
for instrument in instruments:
instrumentations.append(
{
"library": instrument,
"instrumentation": f"{instrumentation_name}=={version}",
}
)

print(instrumentations)
Empty file.
51 changes: 51 additions & 0 deletions src/elasticotel/instrumentation/bootstrap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright
# ownership. Elasticsearch B.V. licenses this file to you 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.instrumentation.bootstrap import run as orig_run
from opentelemetry.instrumentation.bootstrap_gen import (
default_instrumentations as gen_default_instrumentations,
)
from opentelemetry.instrumentation.bootstrap_gen import (
libraries as gen_libraries,
)
from packaging.requirements import Requirement


# the instrumentations available in opentelemetry-bootstrap we want to skip
_EXCLUDED_INSTRUMENTATIONS = {"opentelemetry-instrumentation-openai-v2"}

# update with:
# $ python3.12 scripts/build_edot_bootstrap_instrumentations.py | ruff format -
_EDOT_INSTRUMENTATIONS = [
{
"library": "openai >= 1.2.0",
"instrumentation": "elastic-opentelemetry-instrumentation-openai==0.4.0",
}
]


def _get_instrumentation_name(library_entry):
instrumentation = library_entry["instrumentation"]
instrumentation_name = Requirement(instrumentation)
return instrumentation_name.name


def run() -> None:
"""This is a tiny wrapper around the upstream opentelemetry-boostrap implementation that let us decide which instrumentation to use"""
libraries = [
lib for lib in gen_libraries if _get_instrumentation_name(lib) not in _EXCLUDED_INSTRUMENTATIONS
] + _EDOT_INSTRUMENTATIONS
return orig_run(default_instrumentations=gen_default_instrumentations, libraries=libraries)
Empty file.
34 changes: 34 additions & 0 deletions tests/instrumentation/test_bootstrap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright
# ownership. Elasticsearch B.V. licenses this file to you 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.

import sys

from opentelemetry.instrumentation import (
bootstrap as otel_bootstrap,
)

from elasticotel.instrumentation import bootstrap


def test_overriden_instrumentations(monkeypatch, capfd):
monkeypatch.setattr(sys, "argv", ["edot-bootstrap", "-a", "requirements"])

monkeypatch.setattr(otel_bootstrap, "_is_installed", lambda lib: True)

bootstrap.run()
captured = capfd.readouterr()
assert "opentelemetry-instrumentation-openai-v2" not in captured.out
assert "elastic-opentelemetry-instrumentation-openai" in captured.out