Compare commits
No commits in common. "main" and "0.0.3" have entirely different histories.
|
@ -12,10 +12,5 @@ trim_trailing_whitespace = true
|
|||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.yaml]
|
||||
indent_size = 2
|
||||
[*.yml]
|
||||
indent_size = 2
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
PHP_USER=php
|
||||
XDEBUG_MODE=debug
|
||||
PHP_CS_FIXER_IGNORE_ENV=true
|
||||
#docker-compose or "docker compose" for v1/v2 respectively, note that v1 support ends 06/2023
|
||||
DOCKER_COMPOSE=docker compose
|
|
@ -17,5 +17,3 @@ What did you see instead?
|
|||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
|
||||
<sub>**Tip**: [React](https://github.blog/news-insights/product-news/add-reactions-to-pull-requests-issues-and-comments/) with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding `+1` or `me too`, to help us triage it. Learn more [here](https://opentelemetry.io/community/end-user/issue-participation/).</sub>
|
||||
|
|
|
@ -17,5 +17,3 @@ Which alternative solutions or features have you considered?
|
|||
|
||||
**Additional context**
|
||||
Add any other context about the feature request here.
|
||||
|
||||
<sub>**Tip**: [React](https://github.blog/news-insights/product-news/add-reactions-to-pull-requests-issues-and-comments/) with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding `+1` or `me too`, to help us triage it. Learn more [here](https://opentelemetry.io/community/end-user/issue-participation/).</sub>
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
# configuration for new-issue-welcome bot https://github.com/behaviorbot/new-issue-welcome
|
||||
newPRWelcomeComment: >
|
||||
Thanks for opening your first pull request! If you haven't yet signed our Contributor License Agreement (CLA),
|
||||
then please do so that we can accept your contribution.
|
||||
A link should appear shortly in this PR if you have not already signed one.
|
|
@ -1,22 +0,0 @@
|
|||
---
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "composer"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/docker"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
|
||||
- package-ecosystem: "docker-compose"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
|
@ -1,21 +0,0 @@
|
|||
---
|
||||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 30
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 7
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- 'pinned'
|
||||
- 'security'
|
||||
- 'help wanted'
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: 'stale'
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
closeComment: >
|
||||
This issue has been automatically closed because it has not had
|
||||
recent activity, but it can be reopened.
|
||||
Thank you for your contributions.
|
|
@ -0,0 +1,35 @@
|
|||
name: Update OpenTelemetry Website Docs
|
||||
|
||||
on:
|
||||
# triggers only on a manual dispatch
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
update-docs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: make-pr
|
||||
env:
|
||||
API_TOKEN_GITHUB: ${{secrets.GITHUB_PAT_TOKEN}}
|
||||
# Destination repo should always be 'open-telemetry/opentelemetry.io'
|
||||
DESTINATION_REPO: open-telemetry/opentelemetry.io
|
||||
# Destination path should be the absolute path to your language's friendly name in the docs tree (i.e, 'content/en/docs/java')
|
||||
DESTINATION_PATH: content/en/docs/php
|
||||
# Source path should be 'website_docs', all files and folders are copied from here to dest
|
||||
SOURCE_PATH: website_docs
|
||||
run: |
|
||||
TARGET_DIR=$(mktemp -d)
|
||||
export GITHUB_TOKEN=$API_TOKEN_GITHUB
|
||||
git config user.name github-actions
|
||||
git config user.email github-actions@github.com
|
||||
git clone "https://$API_TOKEN_GITHUB@github.com/$DESTINATION_REPO.git" "$TARGET_DIR"
|
||||
cp -r $SOURCE_PATH/* "$TARGET_DIR/$DESTINATION_PATH"
|
||||
cd "$TARGET_DIR"
|
||||
git checkout -b docs-$GITHUB_REPOSITORY-$GITHUB_SHA
|
||||
git add .
|
||||
git commit -m "Docs update from $GITHUB_REPOSITORY"
|
||||
git push -u origin HEAD:docs-$GITHUB_REPOSITORY-$GITHUB_SHA
|
||||
gh pr create -t "Docs Update from $GITHUB_REPOSITORY" -b "This is an automated pull request." -B main -H docs-$GITHUB_REPOSITORY-$GITHUB_SHA
|
||||
echo "done"
|
|
@ -1,20 +0,0 @@
|
|||
name: FOSSA scanning
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
fossa:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- uses: fossas/fossa-action@3ebcea1862c6ffbd5cf1b4d0bd6b3fe7bd6f2cac # v1.7.0
|
||||
with:
|
||||
api-key: ${{secrets.FOSSA_API_KEY}}
|
||||
team: OpenTelemetry
|
|
@ -1,50 +0,0 @@
|
|||
name: OSSF Scorecard
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
schedule:
|
||||
- cron: "17 0 * * 1" # once a week
|
||||
workflow_dispatch:
|
||||
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
analysis:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
# Needed for Code scanning upload
|
||||
security-events: write
|
||||
# Needed for GitHub OIDC token if publish_results is true
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2
|
||||
with:
|
||||
results_file: results.sarif
|
||||
results_format: sarif
|
||||
publish_results: true
|
||||
# file_mode is needed in this repo because .gitattributes excludes the .github directory
|
||||
# (see https://github.com/ossf/scorecard/issues/4679#issuecomment-3013550752)
|
||||
file_mode: git
|
||||
|
||||
# Upload the results as artifacts (optional). Commenting out will disable
|
||||
# uploads of run results in SARIF format to the repository Actions tab.
|
||||
# https://docs.github.com/en/actions/advanced-guides/storing-workflow-data-as-artifacts
|
||||
- name: "Upload artifact"
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: SARIF file
|
||||
path: results.sarif
|
||||
retention-days: 5
|
||||
|
||||
# Upload the results to GitHub's code scanning dashboard (optional).
|
||||
# Commenting out will disable upload of results to your repo's Code Scanning dashboard
|
||||
- name: "Upload to code-scanning"
|
||||
uses: github/codeql-action/upload-sarif@76621b61decf072c1cee8dd1ce2d2a82d33c17ed # v3.29.5
|
||||
with:
|
||||
sarif_file: results.sarif
|
|
@ -1,4 +1,5 @@
|
|||
name: PHP QA
|
||||
|
||||
name: PHP Composer
|
||||
|
||||
on:
|
||||
push:
|
||||
|
@ -6,170 +7,61 @@ on:
|
|||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
php:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: ${{ matrix.experimental }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
php-version: ['8.1', '8.2', '8.3']
|
||||
experimental: [false]
|
||||
composer_args: [""]
|
||||
include:
|
||||
- php-version: 8.4
|
||||
experimental: false
|
||||
composer_args: "--ignore-platform-reqs"
|
||||
- php-version: 8.5
|
||||
experimental: true
|
||||
composer_args: "--ignore-platform-reqs"
|
||||
env:
|
||||
extensions: ast, grpc, opentelemetry, protobuf
|
||||
operating-system: [ubuntu-latest]
|
||||
php-versions: ['7.3', '7.4', '8.0']
|
||||
|
||||
steps:
|
||||
- name: Set cache key
|
||||
id: key
|
||||
run: |
|
||||
echo "key=$(date +'%Y-%U')" >> $GITHUB_ENV
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: gacts/run-and-post-run@v1
|
||||
id: post-run-command
|
||||
with:
|
||||
post: |
|
||||
echo "::group::Steps"
|
||||
echo "composer=${{steps.composer.outcome}}"
|
||||
echo "style=${{steps.style.outcome}}"
|
||||
echo "deps=${{steps.deps.outcome}}"
|
||||
echo "phan=${{steps.phan.outcome}}"
|
||||
echo "psalm=${{steps.psalm.outcome}}"
|
||||
echo "phpstan=${{steps.phpstan.outcome}}"
|
||||
echo "unit=${{steps.unit.outcome}}"
|
||||
echo "integration=${{steps.integration.outcome}}"
|
||||
echo "::endgroup::"
|
||||
|
||||
if [ ${{ steps.composer.outcome == 'failure' || steps.style.outcome == 'failure' || steps.deps.outcome == 'failure' || steps.phan.outcome == 'failure' || steps.psalm.outcome == 'failure' || steps.phpstan.outcome == 'failure' || steps.unit.outcome == 'failure' || steps.integration.outcome == 'failure' }} == true ]; then \
|
||||
echo "::error::One or more steps failed"; \
|
||||
fi
|
||||
|
||||
- name: Setup cache environment
|
||||
id: extcache
|
||||
uses: shivammathur/cache-extensions@v1
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
extensions: ${{ env.extensions }}
|
||||
key: ${{ env.key }}
|
||||
|
||||
- name: Cache extensions
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ steps.extcache.outputs.dir }}
|
||||
key: ${{ steps.extcache.outputs.key }}
|
||||
restore-keys: ${{ steps.extcache.outputs.key }}
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
php-version: ${{ matrix.php-versions }}
|
||||
coverage: xdebug
|
||||
tools: php-cs-fixer
|
||||
extensions: ${{ env.extensions }}
|
||||
extensions: ast, grpc
|
||||
|
||||
- name: Validate composer.json
|
||||
- name: Validate composer.json and composer.lock
|
||||
run: composer validate
|
||||
|
||||
- name: Cache Composer packages
|
||||
id: composer-cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: vendor
|
||||
key: ${{ runner.os }}-${{ matrix.php-version }}-vendor-${{ hashFiles('composer.json') }}
|
||||
key: ${{ runner.os }}-php-${{ hashFiles('**/composer.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ matrix.php-version }}-vendor-
|
||||
- name: Cache test tools
|
||||
id: test-tools-cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: vendor-bin
|
||||
key: ${{ runner.os }}-${{ matrix.php-version }}-vendor-bin-${{ hashFiles('vendor-bin/*/composer.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ matrix.php-version }}-vendor-bin-
|
||||
${{ runner.os }}-php-
|
||||
|
||||
- name: Install dependencies
|
||||
id: composer
|
||||
if: steps.composer-cache.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
composer --version
|
||||
composer install --prefer-dist --no-progress ${{ matrix.composer_args }}
|
||||
run: composer install --prefer-dist --no-progress --no-suggest
|
||||
|
||||
- name: Update Composer
|
||||
run: composer update
|
||||
|
||||
- name: Check Style
|
||||
id: style
|
||||
continue-on-error: ${{ matrix.experimental }}
|
||||
env:
|
||||
PHP_CS_FIXER_IGNORE_ENV: 1
|
||||
run: |
|
||||
vendor-bin/php-cs-fixer/vendor/bin/php-cs-fixer --version
|
||||
vendor-bin/php-cs-fixer/vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.php --dry-run --stop-on-violation --using-cache=no -vvv
|
||||
|
||||
- name: Check Dependencies
|
||||
id: deps
|
||||
continue-on-error: ${{ matrix.experimental }}
|
||||
run: |
|
||||
vendor-bin/deptrac/vendor/bin/deptrac --version
|
||||
vendor-bin/deptrac/vendor/bin/deptrac --formatter=github-actions --report-uncovered
|
||||
run: vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.php --dry-run --stop-on-violation --using-cache=no -vvv
|
||||
|
||||
- name: Run Phan
|
||||
id: phan
|
||||
continue-on-error: ${{ matrix.experimental }}
|
||||
env:
|
||||
XDEBUG_MODE: off
|
||||
PHAN_DISABLE_XDEBUG_WARN: 1
|
||||
run: |
|
||||
vendor-bin/phan/vendor/bin/phan --version
|
||||
vendor-bin/phan/vendor/bin/phan
|
||||
run: vendor/bin/phan
|
||||
|
||||
- name: Run Psalm
|
||||
id: psalm
|
||||
continue-on-error: ${{ matrix.experimental }}
|
||||
run: |
|
||||
vendor-bin/psalm/vendor/bin/psalm --version
|
||||
vendor-bin/psalm/vendor/bin/psalm --output-format=github
|
||||
run: vendor/bin/psalm --output-format=github
|
||||
|
||||
- name: Run Phpstan
|
||||
id: phpstan
|
||||
continue-on-error: ${{ matrix.experimental }}
|
||||
run: |
|
||||
vendor/bin/phpstan --version
|
||||
vendor/bin/phpstan analyse --error-format=github
|
||||
run: vendor/bin/phpstan analyse --error-format=github
|
||||
|
||||
- name: Run PHPUnit (unit tests)
|
||||
id: unit
|
||||
continue-on-error: ${{ matrix.experimental }}
|
||||
run: |
|
||||
vendor/bin/phpunit --version
|
||||
php -dzend.assertions=1 vendor/bin/phpunit --coverage-text --coverage-clover=coverage.clover --testsuite unit
|
||||
|
||||
- name: Run PHPUnit (integration tests)
|
||||
id: integration
|
||||
continue-on-error: ${{ matrix.experimental }}
|
||||
run: vendor/bin/phpunit --testsuite integration
|
||||
- name: Run PHPUnit
|
||||
run: vendor/bin/phpunit --coverage-text --coverage-clover=coverage.clover
|
||||
|
||||
- name: Code Coverage
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: ./coverage.clover
|
||||
flags: ${{ matrix.php-version }}
|
||||
verbose: false
|
||||
|
||||
packages:
|
||||
uses: opentelemetry-php/gh-workflows/.github/workflows/validate-packages.yml@main
|
||||
needs: php
|
||||
with:
|
||||
matrix_extension: '["ast, json, grpc"]'
|
||||
matrix_php_version: '["8.1", "8.2", "8.3"]'
|
||||
install_directory: '~/.test/.packages'
|
||||
run: bash <(curl -s https://codecov.io/bash)
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
name: "Generate API Documentation"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "main"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
documentation:
|
||||
permissions:
|
||||
pages: write # required for GitHub Pages deployment
|
||||
id-token: write # required for GitHub Pages deployment
|
||||
name: "Documentation"
|
||||
runs-on: "ubuntu-latest"
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v4"
|
||||
- name: "Build"
|
||||
uses: "phpDocumentor/phpDocumentor@v3.8.1"
|
||||
with:
|
||||
target: "docs/build"
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v5
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
path: 'docs/build'
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
|
@ -0,0 +1,24 @@
|
|||
name: Psalm Security Analysis
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
psalm:
|
||||
name: Psalm
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Psalm
|
||||
uses: docker://vimeo/psalm-github-actions
|
||||
with:
|
||||
composer_require_dev: true
|
||||
composer_ignore_platform_reqs: true
|
||||
security_analysis: true
|
||||
report_file: results.sarif
|
||||
|
||||
- name: Upload Security Analysis results to GitHub
|
||||
uses: github/codeql-action/upload-sarif@v1
|
||||
with:
|
||||
sarif_file: results.sarif
|
|
@ -1,59 +0,0 @@
|
|||
name: publish-otel-php-base-docker-image
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * 0"
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- docker/Dockerfile
|
||||
- .github/workflows/publish-otel-php-base-docker-image.yml
|
||||
pull_request:
|
||||
paths:
|
||||
- docker/Dockerfile
|
||||
- .github/workflows/publish-otel-php-base-docker-image.yml
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
push_to_registry:
|
||||
name: OpenTelemetry PHP base docker image creation
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.1', '8.2', '8.3', '8.4']
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
timeout-minutes: 500
|
||||
steps:
|
||||
|
||||
- name: check out the repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push ${{ matrix.php-version }} to ghcr.io
|
||||
uses: docker/build-push-action@v6
|
||||
if: github.ref != 'refs/heads/main'
|
||||
with:
|
||||
push: false
|
||||
file: docker/Dockerfile
|
||||
build-args: PHP_VERSION=${{ matrix.php-version }}
|
||||
platforms: linux/amd64,linux/arm/v8,linux/arm64
|
||||
|
||||
- name: Build and push ${{ matrix.php-version }} to ghcr.io
|
||||
uses: docker/build-push-action@v6
|
||||
if: github.ref == 'refs/heads/main'
|
||||
with:
|
||||
push: true
|
||||
file: docker/Dockerfile
|
||||
build-args: PHP_VERSION=${{ matrix.php-version }}
|
||||
platforms: linux/amd64,linux/arm/v8,linux/arm64
|
||||
tags: ghcr.io/open-telemetry/opentelemetry-php/opentelemetry-php-base:${{ matrix.php-version }}
|
|
@ -1,22 +0,0 @@
|
|||
name: Shellcheck
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
shellcheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install shellcheck
|
||||
run: sudo apt update && sudo apt install --assume-yes shellcheck
|
||||
|
||||
- name: Run shellcheck
|
||||
run: find . -name \*.sh | xargs shellcheck --severity=warning
|
|
@ -1,26 +0,0 @@
|
|||
name: gitsplit
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- split
|
||||
release:
|
||||
types: [published]
|
||||
create:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
gitsplit:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
run: git clone "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY" "$GITHUB_WORKSPACE" && cd "$GITHUB_WORKSPACE" && git checkout $GITHUB_SHA
|
||||
- name: Split repositories
|
||||
uses: docker://jderusse/gitsplit:latest
|
||||
with:
|
||||
args: gitsplit
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITSPLIT_TOKEN }}
|
|
@ -1,38 +1,12 @@
|
|||
composer.phar
|
||||
composer.lock
|
||||
var/
|
||||
vendor/
|
||||
vendor-bin/**/vendor/
|
||||
vendor-bin/**/composer.lock
|
||||
var
|
||||
vendor
|
||||
.idea/
|
||||
coverage.clover
|
||||
tests/coverage
|
||||
.php-cs-fixer.cache
|
||||
.phpbench
|
||||
!tests/coverage/html/.gitkeep
|
||||
var/metrics/
|
||||
!var/metrics/.gitkeep
|
||||
junit.xml
|
||||
.env
|
||||
|
||||
# IntelliJ IDEA
|
||||
.idea
|
||||
*.iml
|
||||
|
||||
# VS Code
|
||||
.vscode
|
||||
|
||||
# OS X
|
||||
.DS_Store
|
||||
.php_cs.cache
|
||||
|
||||
# W3C Test Service build artifacts
|
||||
tests/TraceContext/W3CTestService/test_app
|
||||
tests/TraceContext/W3CTestService/trace-context
|
||||
|
||||
# deptrac cache
|
||||
/.deptrac.cache
|
||||
|
||||
# output from phpdoc
|
||||
docs/build
|
||||
|
||||
# cache from phpdoc
|
||||
.phpdoc
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
# Path to a cache directory Used to speed up the split over time by reusing git's objects
|
||||
cache_url: "/cache/gitsplit"
|
||||
|
||||
# Path to the repository to split (default = current path)
|
||||
project_url: "https://github.com/open-telemetry/opentelemetry-php.git"
|
||||
|
||||
# List of splits.
|
||||
splits:
|
||||
- prefix: "proto/otel"
|
||||
target: "https://${GH_TOKEN}@github.com/opentelemetry-php/gen-otlp-protobuf.git"
|
||||
- prefix: "src/Context"
|
||||
target: "https://${GH_TOKEN}@github.com/opentelemetry-php/context.git"
|
||||
- prefix: "src/SemConv"
|
||||
target: "https://${GH_TOKEN}@github.com/opentelemetry-php/sem-conv.git"
|
||||
- prefix: "src/API"
|
||||
target: "https://${GH_TOKEN}@github.com/opentelemetry-php/api.git"
|
||||
- prefix: "src/SDK"
|
||||
target: "https://${GH_TOKEN}@github.com/opentelemetry-php/sdk.git"
|
||||
- prefix: "src/Config/SDK"
|
||||
target: "https://${GH_TOKEN}@github.com/opentelemetry-php/config-sdk.git"
|
||||
- prefix: "src/Contrib/Otlp"
|
||||
target: "https://${GH_TOKEN}@github.com/opentelemetry-php/exporter-otlp.git"
|
||||
- prefix: "src/Contrib/Grpc"
|
||||
target: "https://${GH_TOKEN}@github.com/opentelemetry-php/transport-grpc.git"
|
||||
- prefix: "src/Contrib/Zipkin"
|
||||
target: "https://${GH_TOKEN}@github.com/opentelemetry-php/exporter-zipkin.git"
|
||||
- prefix: "src/Extension/Propagator/B3"
|
||||
target: "https://${GH_TOKEN}@github.com/opentelemetry-php/extension-propagator-b3.git"
|
||||
- prefix: "src/Extension/Propagator/CloudTrace"
|
||||
target: "https://${GH_TOKEN}@github.com/opentelemetry-php/extension-propagator-cloudtrace.git"
|
||||
- prefix: "src/Extension/Propagator/Jaeger"
|
||||
target: "https://${GH_TOKEN}@github.com/opentelemetry-php/extension-propagator-jaeger.git"
|
||||
|
||||
# List of references to split (defined as regexp)
|
||||
origins:
|
||||
- ^main$
|
||||
- ^split$
|
|
@ -1,7 +1,5 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Phan\Issue;
|
||||
|
||||
/**
|
||||
|
@ -44,7 +42,7 @@ return [
|
|||
//
|
||||
// Note that the **only** effect of choosing `'5.6'` is to infer that functions removed in php 7.0 exist.
|
||||
// (See `backward_compatibility_checks` for additional options)
|
||||
'target_php_version' => '8.1',
|
||||
'target_php_version' => '7.3',
|
||||
|
||||
// If enabled, missing properties will be created when
|
||||
// they are first seen. If false, we'll report an
|
||||
|
@ -280,14 +278,7 @@ return [
|
|||
|
||||
// Add any issue types (such as `'PhanUndeclaredMethod'`)
|
||||
// to this deny-list to inhibit them from being reported.
|
||||
'suppress_issue_types' => [
|
||||
'PhanAccessClassInternal',
|
||||
'PhanAccessMethodInternal',
|
||||
'PhanAccessPropertyInternal',
|
||||
'PhanTypeMismatchPropertyReal',
|
||||
'PhanTemplateTypeNotUsedInFunctionReturn',
|
||||
'PhanUndeclaredClassAttribute',
|
||||
],
|
||||
'suppress_issue_types' => [],
|
||||
|
||||
// A regular expression to match files to be excluded
|
||||
// from parsing and analysis and will not be read at all.
|
||||
|
@ -303,9 +294,7 @@ return [
|
|||
//
|
||||
// This is useful for excluding hopelessly unanalyzable
|
||||
// files that can't be removed for whatever reason.
|
||||
'exclude_file_list' => [
|
||||
'vendor/composer/composer/src/Composer/InstalledVersions.php',
|
||||
],
|
||||
'exclude_file_list' => [],
|
||||
|
||||
// A directory list that defines files that will be excluded
|
||||
// from static analysis, but whose class and method
|
||||
|
@ -321,7 +310,6 @@ return [
|
|||
'exclude_analysis_directory_list' => [
|
||||
'vendor/',
|
||||
'proto/',
|
||||
'src/Config/SDK',
|
||||
],
|
||||
|
||||
// Enable this to enable checks of require/include statements referring to valid paths.
|
||||
|
@ -369,20 +357,19 @@ return [
|
|||
// Thus, both first-party and third-party code being used by
|
||||
// your application should be included in this list.
|
||||
'directory_list' => [
|
||||
'src',
|
||||
'proto/otel/GPBMetadata',
|
||||
'proto/otel/Opentelemetry',
|
||||
'vendor/composer',
|
||||
'vendor/grpc/grpc/src/lib',
|
||||
'api',
|
||||
'Context',
|
||||
'sdk',
|
||||
'contrib',
|
||||
'proto',
|
||||
'vendor/composer/xdebug-handler/src',
|
||||
'vendor/guzzlehttp',
|
||||
'vendor/tbachert/spi/src',
|
||||
'vendor/psr',
|
||||
'vendor/php-http',
|
||||
'vendor/phan/phan/src/Phan',
|
||||
'vendor/phpunit/phpunit/src',
|
||||
'vendor/google/protobuf/src',
|
||||
'vendor/ramsey/uuid/src',
|
||||
'vendor/nyholm/psr7-server/src',
|
||||
'vendor/symfony/config',
|
||||
'vendor/promphp/prometheus_client_php/src',
|
||||
'vendor/google/protobuf/src'
|
||||
],
|
||||
|
||||
// A list of individual files to include in analysis
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
$finder = PhpCsFixer\Finder::create()
|
||||
->in('examples/')
|
||||
->in('tests/')
|
||||
->in('src/');
|
||||
->exclude('vendor')
|
||||
->exclude('var/cache')
|
||||
->exclude('proto')
|
||||
->in(__DIR__);
|
||||
|
||||
$config = new PhpCsFixer\Config();
|
||||
|
||||
return $config->setRules([
|
||||
'concat_space' => ['spacing' => 'one'],
|
||||
'declare_equal_normalize' => ['space' => 'none'],
|
||||
|
@ -16,7 +13,6 @@ return $config->setRules([
|
|||
'modernize_types_casting' => true,
|
||||
'ordered_imports' => true,
|
||||
'php_unit_construct' => true,
|
||||
'php_unit_method_casing' => ['case' => 'snake_case'],
|
||||
'single_line_comment_style' => true,
|
||||
'yoda_style' => false,
|
||||
'@PSR2' => true,
|
||||
|
@ -25,15 +21,12 @@ return $config->setRules([
|
|||
'blank_line_before_statement' => true,
|
||||
'cast_spaces' => true,
|
||||
'declare_strict_types' => true,
|
||||
'type_declaration_spaces' => true,
|
||||
'function_typehint_space' => true,
|
||||
'include' => true,
|
||||
'lowercase_cast' => true,
|
||||
'new_with_parentheses' => true,
|
||||
'no_blank_lines_after_class_opening' => true,
|
||||
'new_with_braces' => true,
|
||||
'no_extra_blank_lines' => true,
|
||||
'no_leading_import_slash' => true,
|
||||
'no_trailing_whitespace' => true,
|
||||
'no_whitespace_in_blank_line' => true,
|
||||
'echo_tag_syntax' => true,
|
||||
'no_unused_imports' => true,
|
||||
'no_useless_else' => true,
|
||||
|
@ -42,9 +35,9 @@ return $config->setRules([
|
|||
'phpdoc_scalar' => true,
|
||||
'phpdoc_types' => true,
|
||||
'short_scalar_cast' => true,
|
||||
'blank_lines_before_namespace' => true,
|
||||
'single_blank_line_before_namespace' => true,
|
||||
'single_quote' => true,
|
||||
'trailing_comma_in_multiline' => ['elements' => ['arrays', 'parameters', 'match']],
|
||||
'trailing_comma_in_multiline' => true,
|
||||
])
|
||||
->setRiskyAllowed(true)
|
||||
->setFinder($finder);
|
||||
|
|
|
@ -12,4 +12,4 @@
|
|||
# https://help.github.com/en/articles/about-code-owners
|
||||
#
|
||||
|
||||
* @open-telemetry/php-approvers
|
||||
* @bobstrecansky @beniamin @zsistla
|
||||
|
|
322
CONTRIBUTING.md
322
CONTRIBUTING.md
|
@ -1,319 +1,19 @@
|
|||
# Contributing Guide
|
||||
Maintainers ([@open-telemetry/php-maintainers](https://github.com/orgs/open-telemetry/teams/php-maintainers)):
|
||||
|
||||
## Introduction
|
||||
- [Bob Strecansky](https://github.com/bobstrecansky), Mailchimp
|
||||
|
||||
Welcome to the OpenTelemetry PHP repository! We appreciate your interest in contributing and helping improve this project. No contribution is too small—whether you're fixing a typo, improving documentation, or implementing a major feature, we value your help.
|
||||
Find more about the maintainer role in [community repository](https://github.com/open-telemetry/community/blob/master/community-membership.md#maintainer)
|
||||
|
||||
This repository is part of the larger OpenTelemetry ecosystem, aimed at providing observability solutions for PHP applications. If you have any questions, feel free to ask in our community channels (See further help below). Your contributions make a difference!
|
||||
Approvers ([@open-telemetry/php-approvers](https://github.com/orgs/open-telemetry/teams/php-approvers)):
|
||||
|
||||
## Pre-requisites
|
||||
- [Levi Morrison](https://github.com/morrisonlevi), Datadog
|
||||
- [Austin Schoen](https://github.com/AustinSchoen), Mailchimp
|
||||
- [Beniamin Calota](https://github.com/beniamin), eMag
|
||||
|
||||
To contribute effectively, ensure you have the following tools installed:
|
||||
Find more information about the approver role in the [community repository](https://github.com/open-telemetry/community/blob/master/community-membership.md#approver)
|
||||
|
||||
* PHP 8.1 or higher (Check supported PHP versions)
|
||||
Triagers ([@open-telemetry/php-triagers](https://github.com/orgs/open-telemetry/teams/php-triagers)):
|
||||
|
||||
We aim to support officially supported PHP versions, according to https://www.php.net/supported-versions.php. The
|
||||
developer image `ghcr.io/open-telemetry/opentelemetry-php/opentelemetry-php-base` is tagged as `8.1`, `8.2` and `8.3`
|
||||
respectively, with `8.1` being the default. You can execute the test suite against other PHP versions by running the
|
||||
following command:
|
||||
- [Jodee Varney](https://github.com/jodeev), Splunk
|
||||
|
||||
```bash
|
||||
PHP_VERSION=8.1 make all
|
||||
#or
|
||||
PHP_VERSION=8.3 make all
|
||||
```
|
||||
For repeatability and consistency across different operating systems, we use the [3 Musketeers pattern](https://3musketeers.pages.dev/). If you're on Windows, it might be a good idea to use Git bash for following the steps below.
|
||||
|
||||
**Note: After cloning the repository, copy `.env.dist` to `.env`.**
|
||||
|
||||
Skipping the step above would result in a "`The "PHP_USER" variable is not set. Defaulting to a blank string`" warning
|
||||
|
||||
We use `docker` and `docker compose` to perform a lot of our static analysis and testing. If you're planning to develop for this library, it'll help to install
|
||||
[docker engine](https://docs.docker.com/engine/install/) and the [compose plugin](https://docs.docker.com/compose/install/).
|
||||
|
||||
Development tasks are generally run through a `Makefile`. Running `make` or `make help` will list available targets.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Pull Requests
|
||||
|
||||
To propose changes to the codebase, you need
|
||||
to [open a pull request](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request)
|
||||
to the opentelemetry-php project.
|
||||
|
||||
After you open the pull request, the CI will run all the
|
||||
associated [github actions](https://github.com/open-telemetry/opentelemetry-php/actions/workflows/php.yml).
|
||||
|
||||
To ensure your PR doesn't emit a failure with GitHub actions, it's recommended that you run the important CI tests locally with the following command:
|
||||
|
||||
```bash
|
||||
make all # composer update, then run all checks
|
||||
make all-lowest # composer update to lowest dependencies, then run all checks
|
||||
```
|
||||
|
||||
This does the following things:
|
||||
|
||||
* Installs/updates all the required dependencies for the project
|
||||
* Uses [Rector](https://github.com/rectorphp/rector) to refactor your code according to our standards.
|
||||
* Uses [php-cs-fixer](https://github.com/FriendsOfPHP/PHP-CS-Fixer) to style your code using our style preferences.
|
||||
* Uses [Deptrac](https://github.com/qossmic/deptrac) to check for dependency violations inside our code base
|
||||
* Makes sure the composer files for the different components are valid
|
||||
* Runs all of our [phpunit](https://phpunit.de/) unit tests.
|
||||
* Performs static analysis with [Phan](https://github.com/phan/phan), [Psalm](https://psalm.dev/)
|
||||
and [PHPStan](https://phpstan.org/user-guide/getting-started)
|
||||
|
||||
## Local Run/Build
|
||||
|
||||
To ensure you have all the correct packages installed locally in your dev environment, you can run
|
||||
|
||||
```bash
|
||||
make install
|
||||
```
|
||||
|
||||
This will install all the library dependencies to
|
||||
the `/vendor` directory.
|
||||
|
||||
To update these dependencies, you can run
|
||||
|
||||
```bash
|
||||
make update
|
||||
```
|
||||
|
||||
To downgrade to the lowest dependencies, you can run
|
||||
|
||||
```shell
|
||||
make update-lowest
|
||||
```
|
||||
|
||||
To run all checks without doing a composer update:
|
||||
|
||||
```shell
|
||||
make all-checks
|
||||
```
|
||||
## Testing
|
||||
|
||||
To make sure the tests in this repo work as you expect, you can use the included docker test wrapper.
|
||||
To run the test suite, execute
|
||||
|
||||
```bash
|
||||
make test
|
||||
```
|
||||
|
||||
This will output the test output as well as a test coverage analysis (text + html - see `tests/coverage/html`). Code
|
||||
that doesn't pass our currently defined tests will emit a failure in CI
|
||||
|
||||
## Contributing Rules
|
||||
|
||||
Even though it may not be reflected everywhere in the codebase yet, we aim to provide software which is easy to read and change.
|
||||
The methods described in Clean Code book(s) by Robert C. Martin (Uncle Bob) are a de facto industry standards nowadays.
|
||||
Reading those books is highly recommended, however you can take a look at the examples given at [Clean Code PHP](https://github.com/jupeter/clean-code-php).
|
||||
While we have no rule to strictly follow said methods and patterns, they are highly recommended as an orientation for
|
||||
your pull requests and to be referenced in reviews.
|
||||
|
||||
We might add additional guidelines regarding for example testing in the future.
|
||||
|
||||
## Additional Information
|
||||
|
||||
### Automatic Refactoring and Upgrading
|
||||
|
||||
We use [Rector](https://github.com/rectorphp/rector) to automatically refactor our code according to given standards
|
||||
and upgrade the code to supported PHP versions.
|
||||
The associated configuration can be found [here](./.rector.php)
|
||||
|
||||
If you want to check what changes would be applied by rector, you can run:
|
||||
|
||||
```bash
|
||||
make rector
|
||||
```
|
||||
This command will simply print out the changes `rector` would make without actually changing any code.
|
||||
|
||||
To refactor your code following our given standards, you can run:
|
||||
|
||||
```bash
|
||||
make rector-write
|
||||
```
|
||||
|
||||
This command applies the changes to the code base.
|
||||
Make sure to run `make style` (see below) after running the `rector`command as the changes might not follow our coding standard.
|
||||
|
||||
### Styling
|
||||
|
||||
We use [PHP-CS-Fixer](https://github.com/FriendsOfPHP/PHP-CS-Fixer) for our code linting and standards fixer. The
|
||||
associated configuration can be found [here](./.php-cs-fixer.php)
|
||||
|
||||
To ensure that your code follows our coding standards, you can run:
|
||||
|
||||
```bash
|
||||
make style
|
||||
```
|
||||
|
||||
This command executes a required check that also runs during CI. This process performs the required fixes and prints them
|
||||
out. Code that doesn't meet the style pattern will emit a failure with GitHub actions.
|
||||
|
||||
### Static Analysis
|
||||
|
||||
We use [Phan](https://github.com/phan/phan/) for static analysis. Currently, our phan configuration is just a standard
|
||||
default analysis configuration. You can use our phan docker wrapper to easily perform static analysis on your changes.
|
||||
|
||||
To run Phan, one can run the following command:
|
||||
|
||||
```bash
|
||||
make phan
|
||||
```
|
||||
|
||||
This process will return 0 on success. Usually this process is performed as part of a code checkin. This process runs
|
||||
during CI and is a required check. Code that doesn't match the standards that we have defined in
|
||||
our [phan config](https://github.com/open-telemetry/opentelemetry-php/blob/master/.phan/config.php) will emit a failure
|
||||
in CI.
|
||||
|
||||
We also use [Psalm](https://psalm.dev/) as a second static analysis tool.
|
||||
You can use our psalm docker wrapper to easily perform static analysis on your changes.
|
||||
|
||||
To run Psalm, one can run the following command:
|
||||
|
||||
```bash
|
||||
make psalm
|
||||
```
|
||||
|
||||
This process will return 0 on success. Usually this process is performed as part of a code checkin. This process runs
|
||||
during CI and is a required check. Code that doesn't match the standards that we have defined in
|
||||
our [psalm config](https://github.com/open-telemetry/opentelemetry-php/blob/main/psalm.xml.dist) will emit a failure in
|
||||
CI.
|
||||
|
||||
We use [PHPStan](https://github.com/phpstan/phpstan) as our third tool for static analysis. You can use our PHPStan
|
||||
docker wrapper to easily perform static analysis on your changes.
|
||||
|
||||
To perform static analysis with PHPStan run:
|
||||
|
||||
```bash
|
||||
make phpstan
|
||||
```
|
||||
|
||||
This process will return 0 on success. Usually this process is performed as part of a code checkin. This process runs
|
||||
during CI and is a required check. Code that doesn't match the standards that we have defined in
|
||||
our [PHPStan config](https://github.com/open-telemetry/opentelemetry-php/blob/main/phpstan.neon.dist) will emit a
|
||||
failure in CI.
|
||||
|
||||
### Code Coverage
|
||||
We use [codecov.io](https://about.codecov.io/) to track code coverage for this repo. This is configured in the [php.yaml github action](https://github.com/open-telemetry/opentelemetry-php/blob/main/.github/workflows/php.yml#L71-L72). We don't require a specific level of code coverage for PRs to pass - we just use this tool in order to understand how a PR will potentially change the amount of code coverage we have across the code base. This tool isn't perfect - sometimes we'll see small deltas in code coverage where there shouldn't be any - this is nothing to fret about.
|
||||
|
||||
If code coverage does decrease on a pull request, you will see a red X in the CI for the repo, but that's ok - the reviewer will use their judgement to determine whether or not we have sufficient code coverage for the change.
|
||||
|
||||
### Dependency Validation
|
||||
|
||||
To make sure the different components of the library are distributable as separate packages, we have to check
|
||||
for dependency violations inside the code base. Dependencies must create a [DAC](https://en.wikipedia.org/wiki/Directed_acyclic_graph) in order to not create recursive dependencies.
|
||||
For this purpose we use [Deptrac](https://github.com/qossmic/deptrac) and the respective configuration can be found
|
||||
[here](./deptrac.yaml)
|
||||
|
||||
To validate the dependencies inside the code base, you can run:
|
||||
|
||||
```bash
|
||||
make deptrac
|
||||
```
|
||||
This command will create an error for any violation of the defined dependencies. If you add new dependencies to the code base,
|
||||
please configure them in the rector configuration.
|
||||
|
||||
## PhpMetrics
|
||||
|
||||
To generate a report showing a variety of metrics for the library and its classes, you can run:
|
||||
|
||||
```bash
|
||||
make phpmetrics
|
||||
```
|
||||
|
||||
This will generate a HTML PhpMetrics report in the `var/metrics` directory. Make sure to run `make test` before to
|
||||
create the test log-file, used by the metrics report.
|
||||
|
||||
### Proto Generation
|
||||
|
||||
Our protobuf files are committed to the repository into the `/proto` folder. These are used in gRPC connections to the
|
||||
upstream. These get updated when the [opentelemetry-proto](https://github.com/open-telemetry/opentelemetry-proto)
|
||||
repo has a meaningful update. The maintainer SIG is discussing a way to make this more automatic in the future.
|
||||
|
||||
To generate protobuf files for use with this repository, you can run the following command:
|
||||
|
||||
```bash
|
||||
make protobuf
|
||||
```
|
||||
|
||||
This will replace `proto/otel/Opentelemetry` and `proto/otel/GPBMetadata` with freshly generated code based on the
|
||||
latest tag from `opentelemetry-proto`, which can then be committed.
|
||||
|
||||
### Semantic Conventions Generation
|
||||
|
||||
Autogenerated semantic convention files are committed to the repository in the `/src/SemConv` directory. These files are
|
||||
updated manually when a new version of [semantic-conventions](https://github.com/open-telemetry/semantic-conventions) is
|
||||
released.
|
||||
|
||||
```bash
|
||||
SEMCONV_VERSION=1.8.0 make semconv
|
||||
```
|
||||
|
||||
Run this command in the root of this repository.
|
||||
|
||||
### API Documentation
|
||||
|
||||
We use [phpDocumentor](https://phpdoc.org/) to automatically generate API documentation from DocBlocks in the code.
|
||||
|
||||
To generate a recent version of the API documentation, you can run:
|
||||
|
||||
```bash
|
||||
make phpdoc
|
||||
```
|
||||
|
||||
To preview the documentation and changes you might expect, you can run:
|
||||
|
||||
```bash
|
||||
make phpdoc-preview
|
||||
```
|
||||
|
||||
This will start a HTTP server running at <http://localhost:8080> serving the updated documentation files.
|
||||
|
||||
## Maintainers
|
||||
|
||||
- [Bob Strecansky](https://github.com/bobstrecansky), Intuit
|
||||
- [Brett McBride](https://github.com/brettmc/), Deakin University
|
||||
|
||||
For more information about the maintainer role, see the [community repository](https://github.com/open-telemetry/community/blob/main/community-membership.md#maintainer).
|
||||
|
||||
## Approvers
|
||||
|
||||
- [Ago Allikmaa](https://github.com/agoallikmaa)
|
||||
- [Cedriz Ziel](https://github.com/cedricziel)
|
||||
- [Chris Lightfoot-Wild](https://github.com/ChrisLightfootWild)
|
||||
|
||||
For more information about the approver role, see the [community repository](https://github.com/open-telemetry/community/blob/main/community-membership.md#approver).
|
||||
|
||||
## Triagers
|
||||
|
||||
For more information about the triager role, see the [community repository](https://github.com/open-telemetry/community/blob/main/community-membership.md#triagers).
|
||||
|
||||
## Emeritus maintainers/approvers/triagers
|
||||
|
||||
- [Amber Zsistla](https://github.com/zsistla)
|
||||
- [Beniamin Calota](https://github.com/beniamin)
|
||||
- [Fahmy Mohammed](https://github.com/Fahmy-Mohammed)
|
||||
- [Jodee Varney](https://github.com/jodeev)
|
||||
- [Levi Morrison](https://github.com/morrisonlevi)
|
||||
- [Przemek Delewski](https://github.com/pdelewski)
|
||||
- [Timo Michna](https://github.com/tidal/)
|
||||
|
||||
For more information about the emeritus role, see the [community repository](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#emeritus-maintainerapprovertriager).
|
||||
|
||||
## Further Help
|
||||
|
||||
Most of our communication is done on CNCF Slack in the channel [otel-php](https://cloud-native.slack.com/archives/C01NFPCV44V).
|
||||
To sign up, create a CNCF Slack account [here](http://slack.cncf.io/)
|
||||
|
||||
Our meetings are held weekly on zoom on Wednesdays at 10:30am PST / 1:30pm EST.
|
||||
A Google calendar invite with the included zoom link can be found [here](https://calendar.google.com/event?action=TEMPLATE&tmeid=N2VtZXZmYnVmbzZkYjZkbTYxdjZvYTdxN21fMjAyMDA5MTZUMTczMDAwWiBrYXJlbnlyeHVAbQ&tmsrc=google.com_b79e3e90j7bbsa2n2p5an5lf60%40group.calendar.google.com&scp=ALL)
|
||||
|
||||
Our open issues can all be found in the [GitHub issues tab](https://github.com/open-telemetry/opentelemetry-php/issues). Feel free to reach out on Slack if you have any additional questions about these issues; we are always happy to talk through implementation details.
|
||||
|
||||
|
||||
#### Thanks to all the people who already contributed!
|
||||
|
||||
<a href="https://github.com/open-telemetry/opentelemetry-php/graphs/contributors">
|
||||
<img src="https://contributors-img.web.app/image?repo=open-telemetry/opentelemetry-php" />
|
||||
</a>
|
||||
Find more information about the triager role in the [community repository](https://github.com/open-telemetry/community/blob/master/community-membership.md#triager)
|
||||
|
|
|
@ -0,0 +1,180 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Context;
|
||||
|
||||
/**
|
||||
* @template TContext of Context
|
||||
*/
|
||||
class Context
|
||||
{
|
||||
/**
|
||||
* @var ContextKey|null
|
||||
*/
|
||||
protected $key;
|
||||
|
||||
/**
|
||||
* @var mixed|null
|
||||
*/
|
||||
protected $value;
|
||||
|
||||
/**
|
||||
* @var TContext|null
|
||||
*/
|
||||
protected $parent;
|
||||
|
||||
protected static $current_context = null;
|
||||
|
||||
/**
|
||||
* This is a general purpose read-only key-value store. Read-only in the sense that adding a new value does not
|
||||
* mutate the existing context, but returns a new Context which has the new value added.
|
||||
*
|
||||
* In practical terms, this is implemented as a linked list of Context instances, with each one holding a reference
|
||||
* to the key object, the value that corresponds to the key, and an optional reference to the parent Context
|
||||
* (i.e. the next link in the linked list chain)
|
||||
*
|
||||
* If you inherit from this class, you should "shadow" $parent into your subclass so that all operations give
|
||||
* you back an instance of the same type that you are interacting with and different subclasses should NOT be
|
||||
* treated as interoperable. i.e. you should NOT have a Context object chain with both Context instances interleaved
|
||||
* with Baggage instances.
|
||||
*
|
||||
* @param ContextKey|null $key The key object. Should only be null when creating an "empty" context
|
||||
* @param mixed|null $value
|
||||
* @param TContext|null $parent Reference to the parent object
|
||||
*/
|
||||
final public function __construct(?ContextKey $key=null, $value=null, $parent=null)
|
||||
{
|
||||
$this->key = $key;
|
||||
$this->value = $value;
|
||||
$this->parent = $parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* This adds a k/v pair to this Context. We do this by instantiating a new Context instance with the k/v and pass
|
||||
* a reference to $this as the "parent" creating the linked list chain.
|
||||
*
|
||||
* @param ContextKey $key
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return Context a new Context containing the k/v
|
||||
*/
|
||||
public function set(ContextKey $key, $value)
|
||||
{
|
||||
return new static($key, $value, $this);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a static version of set().
|
||||
* This is primarily useful when the caller doesn't already have a reference to a Context that they want to mutate.
|
||||
*
|
||||
* There are two ways to call this function.
|
||||
* 1) With a $parent parameter:
|
||||
* Context::setValue($key, $value, $ctx) is functionally equivalent to $ctx->set($key, $value)
|
||||
* 2) Without a $parent parameter:
|
||||
* In this scenario, setValue() will use the `$current_context` reference as supplied by `getCurrent()`
|
||||
* `getCurrent()` will always return a valid Context. If one does not exist at the global scope,
|
||||
* an "empty" context will be created.
|
||||
*
|
||||
* @param ContextKey $key
|
||||
* @param mixed $value
|
||||
* @param Context|null $parent
|
||||
*
|
||||
* @return Context a new Context containing the k/v
|
||||
*/
|
||||
public static function setValue(ContextKey $key, $value, $parent=null)
|
||||
{
|
||||
if (null === $parent) {
|
||||
return static::$current_context = new static($key, $value, static::getCurrent());
|
||||
}
|
||||
|
||||
return new static($key, $value, $parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a value from the Context given a key value.
|
||||
*
|
||||
* @param ContextKey $key
|
||||
*
|
||||
* @throws ContextValueNotFoundException
|
||||
* @return mixed
|
||||
* @suppress PhanUndeclaredClassMethod
|
||||
*/
|
||||
public function get(ContextKey $key)
|
||||
{
|
||||
if ($this->key === $key) {
|
||||
return $this->value;
|
||||
}
|
||||
if (null === $this->parent) {
|
||||
throw new ContextValueNotFoundException();
|
||||
}
|
||||
|
||||
return $this->parent->get($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Static version of get()
|
||||
* This is primarily useful when the caller doesn't already have a reference to the Context that they want to mutate.
|
||||
* This will operate on the "current" global context in that scenario.
|
||||
*
|
||||
* There are two ways to call this function:
|
||||
* 1) With a $ctx value:
|
||||
* Context::getValue($key, $ctx) is functionally equivalent to $ctx->get($key)
|
||||
* 2) Without a $ctx value:
|
||||
* This will fetch the "current" Context if one exists or create one if not, then attempt to get the value from it.
|
||||
*
|
||||
* @param ContextKey $key
|
||||
* @param Context|null $ctx
|
||||
*
|
||||
* @throws ContextValueNotFoundException
|
||||
* @return mixed
|
||||
*/
|
||||
public static function getValue(ContextKey $key, $ctx=null)
|
||||
{
|
||||
$ctx = $ctx ?? static::getCurrent();
|
||||
|
||||
return $ctx->get($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Context
|
||||
*/
|
||||
public static function getCurrent()
|
||||
{
|
||||
if (null === static::$current_context) {
|
||||
static::$current_context = new static();
|
||||
}
|
||||
|
||||
return static::$current_context;
|
||||
}
|
||||
|
||||
/**
|
||||
* This will set the given Context to be the "current" one. We return a token which can be passed to `detach()` to
|
||||
* reset the Current Context back to the previous one.
|
||||
*
|
||||
* @param Context $ctx
|
||||
*
|
||||
* @return callable token for resetting the $current_context back
|
||||
*/
|
||||
public static function attach($ctx): callable
|
||||
{
|
||||
$former_ctx = static::$current_context;
|
||||
static::$current_context = $ctx;
|
||||
|
||||
return function () use ($former_ctx) {
|
||||
return $former_ctx;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a token, the current context will be set back to the one prior to the token being generated.
|
||||
*
|
||||
* @param callable $token
|
||||
*
|
||||
* @return Context
|
||||
*/
|
||||
public static function detach(callable $token)
|
||||
{
|
||||
return static::$current_context = call_user_func($token);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Context;
|
||||
|
||||
class ContextKey
|
||||
{
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $name;
|
||||
|
||||
public function __construct(?string $name=null)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function name(): ?string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
}
|
|
@ -4,9 +4,6 @@ declare(strict_types=1);
|
|||
|
||||
namespace OpenTelemetry\Context;
|
||||
|
||||
/**
|
||||
* @template-covariant T
|
||||
*/
|
||||
interface ContextKeyInterface
|
||||
class ContextValueNotFoundException extends \UnexpectedValueException
|
||||
{
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Context;
|
||||
|
||||
trait ContextValueTrait
|
||||
{
|
||||
/**
|
||||
* @param Context $context
|
||||
* @return mixed|null
|
||||
* @phan-suppress PhanAbstractStaticMethodCallInTrait
|
||||
*/
|
||||
public static function extract(Context $context)
|
||||
{
|
||||
try {
|
||||
return $context->get(static::getContextKey());
|
||||
} catch (ContextValueNotFoundException $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param Context $context
|
||||
* @return Context
|
||||
* @phan-suppress PhanAbstractStaticMethodCallInTrait
|
||||
*/
|
||||
public static function insert($value, Context $context): Context
|
||||
{
|
||||
return $context->set(static::getContextKey(), $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @return Scope
|
||||
* @phan-suppress PhanAbstractStaticMethodCallInTrait
|
||||
*/
|
||||
public static function setCurrent($value): Scope
|
||||
{
|
||||
$context = Context::getCurrent()->set(static::getContextKey(), $value);
|
||||
|
||||
return new Scope(Context::attach($context));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed|null
|
||||
* @phan-suppress PhanAbstractStaticMethodCallInTrait
|
||||
*/
|
||||
public static function getCurrent()
|
||||
{
|
||||
try {
|
||||
return Context::getCurrent()->get(static::getContextKey());
|
||||
} catch (ContextValueNotFoundException $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
abstract protected static function getContextKey(): ContextKey;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Context;
|
||||
|
||||
class Scope
|
||||
{
|
||||
/** @var callable Context token, result of Context::attach() */
|
||||
private $contextToken;
|
||||
|
||||
public function __construct(callable $contextToken)
|
||||
{
|
||||
$this->contextToken = $contextToken;
|
||||
}
|
||||
|
||||
public function close(): void
|
||||
{
|
||||
Context::detach($this->contextToken);
|
||||
}
|
||||
}
|
144
Makefile
144
Makefile
|
@ -1,114 +1,44 @@
|
|||
include .env
|
||||
DC_RUN_PHP = docker-compose run --rm php
|
||||
|
||||
PHP_VERSION ?= 8.1
|
||||
DOCKER_COMPOSE ?= docker compose
|
||||
DC_RUN_PHP = $(DOCKER_COMPOSE) run --rm php
|
||||
install:
|
||||
$(DC_RUN_PHP) composer install
|
||||
update:
|
||||
$(DC_RUN_PHP) composer update
|
||||
test:
|
||||
$(DC_RUN_PHP) php ./vendor/bin/phpunit --colors=always --coverage-text --testdox --coverage-clover coverage.clover
|
||||
phan:
|
||||
$(DC_RUN_PHP) env PHAN_DISABLE_XDEBUG_WARN=1 php ./vendor/bin/phan
|
||||
psalm:
|
||||
$(DC_RUN_PHP) php ./vendor/bin/psalm
|
||||
psalm-info:
|
||||
$(DC_RUN_PHP) php ./vendor/bin/psalm --show-info=true
|
||||
phpstan:
|
||||
$(DC_RUN_PHP) php ./vendor/bin/phpstan analyse
|
||||
trace examples: FORCE
|
||||
docker-compose up -d --remove-orphans
|
||||
$(DC_RUN_PHP) php ./examples/AlwaysOnZipkinExample.php
|
||||
$(DC_RUN_PHP) php ./examples/AlwaysOffTraceExample.php
|
||||
$(DC_RUN_PHP) php ./examples/AlwaysOnJaegerExample.php
|
||||
# The following examples do not use the DC_RUN_PHP global because they need environment variables.
|
||||
docker-compose run -e NEW_RELIC_ENDPOINT -e NEW_RELIC_INSERT_KEY --rm php php ./examples/AlwaysOnNewrelicExample.php
|
||||
docker-compose run -e NEW_RELIC_ENDPOINT -e NEW_RELIC_INSERT_KEY --rm php php ./examples/AlwaysOnZipkinToNewrelicExample.php
|
||||
docker-compose stop
|
||||
collector:
|
||||
docker-compose -f docker-compose-collector.yaml up -d --remove-orphans
|
||||
docker-compose -f docker-compose-collector.yaml run -e OTEL_EXPORTER_OTLP_ENDPOINT=otel-collector:4317 --rm php php ./examples/AlwaysOnOTLPGrpcExample2.php
|
||||
docker-compose -f docker-compose-collector.yaml stop
|
||||
|
||||
.DEFAULT_GOAL : help
|
||||
.PHONY: deptrac
|
||||
|
||||
help: ## Show this help
|
||||
@printf "\033[33m%s:\033[0m\n" 'Available commands'
|
||||
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z0-9_-]+:.*?## / {printf " \033[32m%-18s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
|
||||
all: update all-checks ## Update to latest and run all checks
|
||||
all-lowest: update-lowest all-checks ## Update to lowest dependencies and run all checks
|
||||
all-checks: rector style deptrac packages-composer phan psalm phpstan test ## Run all checks
|
||||
pull: ## Pull latest developer image
|
||||
$(DOCKER_COMPOSE) pull php
|
||||
build: ## Build developer image locally
|
||||
docker build docker/ --build-arg PHP_VERSION=${PHP_VERSION} -t ghcr.io/open-telemetry/opentelemetry-php/opentelemetry-php-base:${PHP_VERSION}
|
||||
install: ## Install dependencies
|
||||
$(DC_RUN_PHP) env XDEBUG_MODE=off composer install
|
||||
update: ## Update dependencies
|
||||
$(DC_RUN_PHP) env XDEBUG_MODE=off composer update
|
||||
update-lowest: ## Update dependencies to lowest supported versions
|
||||
$(DC_RUN_PHP) env XDEBUG_MODE=off composer update --prefer-lowest
|
||||
test: test-unit test-integration ## Run unit and integration tests
|
||||
test-unit: ## Run unit tests
|
||||
$(DC_RUN_PHP) env XDEBUG_MODE=coverage vendor/bin/phpunit --testsuite unit --colors=always
|
||||
test-integration: ## Run integration tests
|
||||
$(DC_RUN_PHP) env XDEBUG_MODE=off vendor/bin/phpunit --testsuite integration --colors=always
|
||||
test-verbose: ## Run unit tests with verbose (testdox) output
|
||||
$(DC_RUN_PHP) env XDEBUG_MODE=coverage vendor/bin/phpunit --testsuite unit --colors=always --testdox --coverage-clover coverage.clover --coverage-html=tests/coverage/html --log-junit=junit.xml
|
||||
test-coverage: ## Run units tests and generate code coverage
|
||||
$(DC_RUN_PHP) env XDEBUG_MODE=coverage vendor/bin/phpunit --testsuite unit --coverage-html=tests/coverage/html
|
||||
test-compliance: ## Run compliance tests
|
||||
$(DC_RUN_PHP) env XDEBUG_MODE=coverage vendor/bin/phpunit --group compliance
|
||||
test-trace-compliance: ## Run trace compliance tests
|
||||
$(DC_RUN_PHP) env XDEBUG_MODE=coverage vendor/bin/phpunit --group trace-compliance
|
||||
phan: ## Run phan
|
||||
$(DC_RUN_PHP) env XDEBUG_MODE=off env PHAN_DISABLE_XDEBUG_WARN=1 vendor-bin/phan/vendor/bin/phan
|
||||
psalm: ## Run psalm
|
||||
$(DC_RUN_PHP) env XDEBUG_MODE=off vendor-bin/psalm/vendor/bin/psalm --threads=1 --no-cache
|
||||
psalm-info: ## Run psalm and show info
|
||||
$(DC_RUN_PHP) env XDEBUG_MODE=off vendor-bin/psalm/vendor/bin/psalm --show-info=true --threads=1
|
||||
phpdoc: ## Run phpdoc
|
||||
$(DOCKER_COMPOSE) -f docker-compose.phpDocumentor.yaml run --rm phpdoc
|
||||
phpdoc-preview:
|
||||
$(DOCKER_COMPOSE) -f docker-compose.phpDocumentor.yaml run --service-ports --rm preview
|
||||
phpstan: ## Run phpstan
|
||||
$(DC_RUN_PHP) env XDEBUG_MODE=off vendor/bin/phpstan analyse --memory-limit=256M
|
||||
infection: ## Run infection (mutation testing)
|
||||
$(DC_RUN_PHP) env XDEBUG_MODE=coverage php -d memory_limit=1024M vendor-bin/infection/vendor/bin/infection --threads=max
|
||||
packages-composer: ## Validate composer packages
|
||||
$(DC_RUN_PHP) env XDEBUG_MODE=off vendor/bin/otel packages:composer:validate
|
||||
benchmark: ## Run phpbench
|
||||
$(DC_RUN_PHP) env XDEBUG_MODE=off vendor-bin/phpbench/vendor/bin/phpbench run --report=default
|
||||
phpmetrics: ## Run php metrics
|
||||
$(DC_RUN_PHP) env XDEBUG_MODE=off vendor-bin/phpmetrics/vendor/bin/phpmetrics --config=./phpmetrics.json --junit=junit.xml
|
||||
smoke-test-examples: smoke-test-isolated-examples smoke-test-exporter-examples smoke-test-collector-integration smoke-test-prometheus-example ## Run smoke test examples
|
||||
smoke-test-isolated-examples: ## Run smoke test isolated examples
|
||||
$(DC_RUN_PHP) php ./examples/traces/getting_started.php
|
||||
$(DC_RUN_PHP) php ./examples/traces/features/batch_exporting.php
|
||||
$(DC_RUN_PHP) php ./examples/traces/features/concurrent_spans.php
|
||||
$(DC_RUN_PHP) php ./examples/traces/features/configuration_from_environment.php
|
||||
$(DC_RUN_PHP) php ./examples/traces/features/creating_a_new_trace_in_the_same_process.php
|
||||
$(DC_RUN_PHP) php ./examples/traces/features/resource_detectors.php
|
||||
$(DC_RUN_PHP) php ./examples/traces/features/span_resources.php
|
||||
$(DC_RUN_PHP) php ./examples/traces/troubleshooting/air_gapped_trace_debugging.php
|
||||
$(DC_RUN_PHP) php ./examples/traces/troubleshooting/logging_of_span_data.php
|
||||
$(DC_RUN_PHP) php ./examples/traces/troubleshooting/setting_up_logging.php
|
||||
smoke-test-exporter-examples: FORCE ## Run (some) exporter smoke test examples
|
||||
# Note this does not include every exporter at the moment
|
||||
$(DOCKER_COMPOSE) up -d --remove-orphans
|
||||
$(DC_RUN_PHP) php ./examples/traces/exporters/zipkin.php
|
||||
$(DC_RUN_PHP) php ./examples/traces/features/parent_span_example.php
|
||||
smoke-test-collector-integration: ## Run smoke test collector integration
|
||||
$(DOCKER_COMPOSE) -f docker-compose.collector.yaml up -d --remove-orphans
|
||||
sleep 5
|
||||
$(DOCKER_COMPOSE) -f docker-compose.collector.yaml run --rm php php ./examples/traces/exporters/otlp_grpc.php
|
||||
$(DOCKER_COMPOSE) -f docker-compose.collector.yaml run --rm php php ./examples/traces/exporters/otlp_http.php
|
||||
$(DOCKER_COMPOSE) -f docker-compose.collector.yaml stop
|
||||
smoke-test-collector-metrics-integration:
|
||||
$(DOCKER_COMPOSE) -f docker-compose.collector.yaml up -d --force-recreate collector
|
||||
COMPOSE_IGNORE_ORPHANS=TRUE $(DOCKER_COMPOSE) -f docker-compose.yaml run --rm php php ./examples/metrics/features/exporters/otlp_http.php
|
||||
$(DOCKER_COMPOSE) -f docker-compose.collector.yaml logs collector
|
||||
$(DOCKER_COMPOSE) -f docker-compose.collector.yaml stop collector
|
||||
smoke-test-prometheus-example: metrics-prometheus-example stop-prometheus
|
||||
metrics-prometheus-example:
|
||||
@$(DOCKER_COMPOSE) -f docker-compose.prometheus.yaml -p opentelemetry-php_metrics-prometheus-example up -d web
|
||||
# This is slow because it's building the image from scratch (and parts of that, like installing the gRPC extension, are slow)
|
||||
@$(DOCKER_COMPOSE) -f docker-compose.prometheus.yaml -p opentelemetry-php_metrics-prometheus-example run --rm php php examples/metrics/prometheus/prometheus_metrics_example.php
|
||||
@docker-compose -f docker-compose.prometheus.yaml up -d web
|
||||
@docker-compose -f docker-compose.prometheus.yaml run php-prometheus php /var/www/public/examples/prometheus/PrometheusMetricsExample.php
|
||||
stop-prometheus:
|
||||
@$(DOCKER_COMPOSE) -f docker-compose.prometheus.yaml -p opentelemetry-php_metrics-prometheus-example stop
|
||||
fiber-ffi-example:
|
||||
@$(DOCKER_COMPOSE) -f docker-compose.fiber-ffi.yaml -p opentelemetry-php_fiber-ffi-example up -d web
|
||||
protobuf: ## Generate protobuf files
|
||||
./script/proto_gen.sh
|
||||
bash: ## bash shell into container
|
||||
@docker-compose -f docker-compose.prometheus.yaml stop
|
||||
proto:
|
||||
@docker-compose -f docker-compose.proto.yaml up proto
|
||||
bash:
|
||||
$(DC_RUN_PHP) bash
|
||||
style: ## Run style check/fix
|
||||
$(DC_RUN_PHP) env XDEBUG_MODE=off env PHP_CS_FIXER_IGNORE_ENV=1 vendor-bin/php-cs-fixer/vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.php --using-cache=no -vvv
|
||||
rector-write: ## Run rector
|
||||
$(DC_RUN_PHP) env XDEBUG_MODE=off vendor-bin/rector/vendor/bin/rector process
|
||||
rector: ## Run rector (dry-run)
|
||||
$(DC_RUN_PHP) env XDEBUG_MODE=off vendor-bin/rector/vendor/bin/rector process --dry-run
|
||||
deptrac: ## Run deptrac
|
||||
$(DC_RUN_PHP) env XDEBUG_MODE=off vendor-bin/deptrac/vendor/bin/deptrac --formatter=table --report-uncovered --no-cache
|
||||
style:
|
||||
$(DC_RUN_PHP) php ./vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.php --stop-on-violation --using-cache=no -vvv
|
||||
w3c-test-service:
|
||||
@$(DOCKER_COMPOSE) -f docker-compose.w3cTraceContext.yaml run --rm php ./tests/TraceContext/W3CTestService/trace-context-test.sh
|
||||
semconv: ## Generate semconv files
|
||||
./script/semantic-conventions/semconv.sh
|
||||
split: ## Run git split
|
||||
$(DOCKER_COMPOSE) -f docker/gitsplit/docker-compose.yaml --env-file ./.env up
|
||||
@docker-compose -f docker-compose.w3cTraceContext.yaml run --rm php ./tests/TraceContext/W3CTestService/symfony-setup
|
||||
FORCE:
|
||||
|
|
201
README.md
201
README.md
|
@ -1,56 +1,189 @@
|
|||
# OpenTelemetry for PHP
|
||||
# OpenTelemetry php library
|
||||
|
||||

|
||||

|
||||
[](https://codecov.io/gh/open-telemetry/opentelemetry-php)
|
||||
[](https://cloud-native.slack.com/archives/D03FAB6GN0K)
|
||||
|
||||
This is the **[monorepo](https://en.wikipedia.org/wiki/Monorepo)** for the **main** components of [OpenTelemetry](https://opentelemetry.io/) for PHP.
|
||||
## Current Project Status
|
||||

|
||||
|
||||
## Documentation
|
||||
This project currently lives in a pre-alpha status. Our current release is not production ready; it has been created in order to receive feedback from the community.
|
||||
|
||||
Please read the official documentation: https://opentelemetry.io/docs/instrumentation/php/
|
||||
We attempt to keep the [OpenTelemetry Specification Matrix](https://github.com/open-telemetry/opentelemetry-specification/blob/master/spec-compliance-matrix.md) up to date in order to show which features are available and which have not yet been implemented.
|
||||
|
||||
API Documentation is available here: https://open-telemetry.github.io/opentelemetry-php/
|
||||
If you find an inconsistency in the data in the matrix vs. the data in this repository, please let us know in our slack channel and we'll get it rectified.
|
||||
|
||||
## Communication
|
||||
Most of our communication is done on CNCF Slack, in the [otel-php](https://cloud-native.slack.com/archives/C01NFPCV44V) channel. To sign up, create a CNCF slack account here http://slack.cncf.io/
|
||||
|
||||
## Packages and versions
|
||||
Our meetings are held weekly on zoom on Wednesdays at 10:30am PST / 1:30pm EST.
|
||||
A Google calendar invite with the included zoom link can be found [here](https://calendar.google.com/event?action=TEMPLATE&tmeid=N2VtZXZmYnVmbzZkYjZkbTYxdjZvYTdxN21fMjAyMDA5MTZUMTczMDAwWiBrYXJlbnlyeHVAbQ&tmsrc=google.com_b79e3e90j7bbsa2n2p5an5lf60%40group.calendar.google.com&scp=ALL)
|
||||
|
||||
| Package | Latest |
|
||||
|----------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| API | [](https://packagist.org/packages/open-telemetry/api/) [](https://packagist.org/packages/open-telemetry/api/) |
|
||||
| SDK | [](https://packagist.org/packages/open-telemetry/sdk/) [](https://packagist.org/packages/open-telemetry/sdk/) |
|
||||
| Context | [](https://packagist.org/packages/open-telemetry/context/) [](https://packagist.org/packages/open-telemetry/context/) |
|
||||
| Semantic Conventions | [](https://packagist.org/packages/open-telemetry/sem-conv/) [](https://packagist.org/packages/open-telemetry/sem-conv/) |
|
||||
| OTLP Exporter | [](https://packagist.org/packages/open-telemetry/exporter-otlp/) [](https://packagist.org/packages/open-telemetry/exporter-otlp/) |
|
||||
| gRPC Transport | [](https://packagist.org/packages/open-telemetry/transport-grpc/) [](https://packagist.org/packages/open-telemetry/transport-grpc/) |
|
||||
| OTLP Protobuf Files | [](https://packagist.org/packages/open-telemetry/gen-otlp-protobuf/) [](https://packagist.org/packages/open-telemetry/gen-otlp-protobuf/) |
|
||||
| B3 Propagator | [](https://packagist.org/packages/open-telemetry/extension-propagator-b3/) [](https://packagist.org/packages/open-telemetry/extension-propagator-b3/) |
|
||||
Our open issues can all be found in the [github issues tab](https://github.com/open-telemetry/opentelemetry-php/issues). Feel free to reach out on Slack if you have any additional questions about these issues; we are always happy to talk through implementation details.
|
||||
|
||||
Releases for both this repository and [contrib](https://github.com/open-telemetry/opentelemetry-php-contrib) are
|
||||
based on read-only [git subtree splits](https://github.com/splitsh/lite) from our monorepo. You should refer to
|
||||
[packagist.org](https://packagist.org/packages/open-telemetry/) for all packages, their versions and details.
|
||||
## Installation
|
||||
The recommended way to install the library is through [Composer](http://getcomposer.org):
|
||||
|
||||
You can also look at the read-only repositories, which live in the
|
||||
[opentelemetry-php](https://github.com/opentelemetry-php) organization.
|
||||
1.) Install the composer package using [Composer's installation instructions](https://getcomposer.org/doc/00-intromd#installation-linux-unix-macos).
|
||||
|
||||
## Contributing
|
||||
2.) Add
|
||||
```bash
|
||||
"minimum-stability": "dev"
|
||||
```
|
||||
|
||||
[](https://github.com/open-telemetry/opentelemetry-php/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) [](https://github.com/open-telemetry/opentelemetry-php/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) [](https://github.com/open-telemetry/opentelemetry-php/pulls?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) [](https://github.com/open-telemetry/opentelemetry-php/issues?q=is%3Aopen)
|
||||
To your project's `composer.json` file, as this utility has not reached a stable release status yet.
|
||||
|
||||
We would love to have you on board, please see our [Contributing README](./CONTRIBUTING.md)
|
||||
2.) Install the dependency with composer:
|
||||
|
||||
## Specification conformance
|
||||
```bash
|
||||
$ composer require open-telemetry/opentelemetry
|
||||
```
|
||||
|
||||
We attempt to keep the [OpenTelemetry Specification Matrix](https://github.com/open-telemetry/opentelemetry-specification/blob/master/spec-compliance-matrix.md) up to date in order to show which features are available and which have not yet been implemented.
|
||||
## Development
|
||||
We use `docker` and `docker-compose` to perform a lot of our static analysis and testing.
|
||||
|
||||
If you find an inconsistency in the data in the matrix, please let us know in our slack channel and we'll get it rectified.
|
||||
If you're planning to develop for this library, it'll help to install `docker engine` and `docker-compose`.
|
||||
|
||||
## Backwards compatibility
|
||||
You can find installation instructions for these packages can be found [here](https://docs.docker.com/install/), under the `Docker Engine` and `Docker Compose` submenus respectively.
|
||||
|
||||
See [compatibility readme](src/SDK/Common/Dev/Compatibility/README.md).
|
||||
To ensure you have all the correct packages installed locally in your dev environment, you can run
|
||||
|
||||
```bash
|
||||
make install
|
||||
```
|
||||
|
||||
From your bash compatible shell. This will install all of the necessary vendored libraries that the project uses to
|
||||
the
|
||||
`/vendor` directory.
|
||||
|
||||
To update these dependencies, you can run
|
||||
|
||||
```bash
|
||||
make update
|
||||
```
|
||||
|
||||
In order to update all the vendored libraries in the `/vendor` directory.
|
||||
|
||||
|
||||
## Pull Requests
|
||||
|
||||
Once you've made the update to the codebase that you'd like to submit, you may [create a pull request](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request) to the opentelemetry-php project.
|
||||
|
||||
After you open the pull request, the CI/CD pipeline will run all of the associated [github actions](https://github.com/open-telemetry/opentelemetry-php/actions/workflows/php.yml).
|
||||
|
||||
You can simulate the important github actions locally before you submit your PR by running the following command:
|
||||
|
||||
```bash
|
||||
make install && make update && make style && make test && make phan && make psalm && make phpstan
|
||||
```
|
||||
|
||||
from your bash compatible shell. This does the following things:
|
||||
|
||||
* Installs all the required dependencies for the project and ensures they are up to date
|
||||
* Uses [php-cs-fixer](https://github.com/FriendsOfPHP/PHP-CS-Fixer) to style your code using our style preferences.
|
||||
* Runs all of our [phpunit](https://phpunit.de/) unit tests.
|
||||
* Performs static analysis with [Phan](https://github.com/phan/phan), [Psalm](https://psalm.dev/) and [PHPStan](https://phpstan.org/user-guide/getting-started)
|
||||
|
||||
|
||||
## Proto Generation
|
||||
Our proto files are committed to the repository into the `/proto` folder. These are used in gRPC connections to the
|
||||
upstream. These get updated when the [opentelemetry-proto](https://github.com/open-telemetry/opentelemetry-proto)
|
||||
repo has a meaningful update. The maintainer SIG is discussing a way to make this more automatic in the future.
|
||||
|
||||
If you'd like to generate proto files for use with this repository, one can run the following command:
|
||||
|
||||
```bash
|
||||
make proto
|
||||
```
|
||||
|
||||
From your bash compatible shell in the root of this directory. This wil create a `/proto` folder in the root
|
||||
directory of the
|
||||
repository.
|
||||
|
||||
## Styling
|
||||
We use [PHP-CS-Fixer](https://github.com/FriendsOfPHP/PHP-CS-Fixer) for our code linting and standards fixer. The associated configuration for this standards fixer can be found in the root of the repository [here](https://github.com/open-telemetry/opentelemetry-php/blob/master/.php_cs)
|
||||
|
||||
To ensure that your code is stylish, you can run the follwing command:
|
||||
```bash
|
||||
make style
|
||||
```
|
||||
|
||||
from your bash compatible shell. This process will print out the fixes that it is making to your
|
||||
associated files. Usually this process is performed as part of a code checkin. This process runs during CI and is a required check. Code that doesn't follow this style pattern will emit a failure in CI.
|
||||
|
||||
## Static Analysis
|
||||
We use [Phan](https://github.com/phan/phan/) for static analysis. Currently our phan configuration is just a standard default analysis configuration. You can use our phan docker wrapper to easily perform static analysis on your changes.
|
||||
|
||||
To run Phan, one can run the following command:
|
||||
```bash
|
||||
make phan
|
||||
```
|
||||
from your bash compatible shell.
|
||||
This process will return 0 on success.
|
||||
Usually this process is performed as part of a code checkin. This process runs during CI and is a required check. Code that doesn't match the standards that we have defined in our [phan config](https://github.com/open-telemetry/opentelemetry-php/blob/master/.phan/config.php) will emit a failure in CI.
|
||||
|
||||
We also use [Psalm](https://psalm.dev/) as a second static analysis tool.
|
||||
You can use our psalm docker wrapper to easily perform static analysis on your changes.
|
||||
|
||||
To run Psalm, one can run the following command:
|
||||
```bash
|
||||
make psalm
|
||||
```
|
||||
from your bash compatible shell. This process will return 0 on success. Usually this process is performed as part of a code checkin. This process runs during CI and is a required check. Code that doesn't match the standards that we have defined in our [psalm config](https://github.com/open-telemetry/opentelemetry-php/blob/main/psalm.xml.dist) will emit a failure in CI.
|
||||
|
||||
We use [PHPStan](https://github.com/phpstan/phpstan) as our third tool for static analysis.
|
||||
You can use our PHPStan docker wrapper to easily perform static analysis on your changes.
|
||||
|
||||
Execute `make phpstan` from your bash compatible shell. This process will return 0 on success. Usually this process is
|
||||
performed as part of a code checkin. This process runs during CI and is a required check. Code that doesn't match the
|
||||
standards that we have defined in
|
||||
our [PHPStan config](https://github.com/open-telemetry/opentelemetry-php/blob/main/phpstan.neon.dist) will emit a failure
|
||||
in CI.
|
||||
|
||||
## Testing
|
||||
To make sure the tests in this repo work as you expect, you can use the included docker test wrapper.
|
||||
To run the test suite, execute
|
||||
```bash
|
||||
make test
|
||||
```
|
||||
from your bash compatible shell. This will output the test output as well
|
||||
as a test coverage analysis. Code that doesn't pass our currently defined tests will emit a failure in CI
|
||||
|
||||
## Examples
|
||||
|
||||
### Trace
|
||||
You can use the [examples/AlwaysOnZipkinExample.php](/examples/AlwaysOnZipkinExample.php) file to test out the reference implementation we have for zipkin. This example perfoms a sample trace with a grouping of 5 spans and POSTs the result to a local zipkin instance.
|
||||
|
||||
You can also use the [examples/AlwaysOnJaegerExample.php](/examples/AlwaysOnJaegerExample.php) file to test out the reference implementation we have for jaegar. This example perfoms a sample trace with a grouping of 5 spans and POSTs the result to a local jaegar instance.
|
||||
|
||||
You can use the [examples/AlwaysOnZipkinToNewrelicExample.php](/examples/AlwaysOnZipkinToNewrelicExample.php) file to test out the reference implementation we have for zipkin to Newrelic. This example perfoms a sample trace with spans and POSTs the result to a Newrelic endpoint. This requires a license key (free accounts available) to be set in the environment (NEW_RELIC_INSERT_KEY) to authenticate to the backend.
|
||||
|
||||
You can use the [examples/AlwaysOnNewrelicExample.php](/examples/AlwaysOnNewrelicExample.php) file to test out the reference implementation we have for Newrelic. This example perfoms a sample trace with spans and POSTs the result to a Newrelic endpoint. This requires a license key (free accounts available) set in the environment (NEW_RELIC_INSERT_KEY) to authenticate to the backend.
|
||||
|
||||
The PHP for all examples should execute by itself (if you have a zipkin or jaegar instance running on localhost), but if you'd like a no-fuss way to test this out with docker and docker-compose, you can perform the following simple steps:
|
||||
|
||||
1.) Install the necessary dependencies by running `make install`. This will install the composer dependencies and store them in `/vendor`
|
||||
2.) Execute the example trace using `make trace examples`.
|
||||
|
||||
Exported spans can be seen in zipkin at [http://127.0.0.1:9411](http://127.0.0.1:9411)
|
||||
|
||||
Exported spans can also be seen in jaeger at [http://127.0.0.1:16686](http://127.0.0.1:16686)
|
||||
|
||||
### Metrics
|
||||
You can use the [examples/prometheus/PrometheusMetricsExample.php](/examples/prometheus/PrometheusMetricsExample.php) file to test out the reference implementation we have. This example will create a counter that will be scraped by local Prometheus instance.
|
||||
|
||||
The easy way to test the example out with docker and docker-compose is:
|
||||
|
||||
1.) Run `make metrics-prometheus-example`. Make sure that local ports 8080, 6379 and 9090 are available.
|
||||
|
||||
2.) Open local Prometheus instance: http://localhost:9090
|
||||
|
||||
3.) Go to Graph section, type "opentelemetry_prometheus_counter" in the search field or select it in the dropdown menu. You will see the counter value. Every other time you run `make metrics-prometheus-example` will increment the counter but remember that Prometheus scrapes values once in 10 seconds.
|
||||
|
||||
4.) In order to stop docker containers for this example just run `make stop-prometheus`
|
||||
|
||||
## User Integration Guides
|
||||
* [Integrating OpenTelemetry PHP into Laravel Applications](./docs/laravel-integration.md)
|
||||
* [Integrating OpenTelemetry PHP into Symfony Applications](./docs/symfony-integration.md)
|
||||
## Versioning
|
||||
|
||||
OpenTelemetry for PHP aims to support all officially supported PHP versions according to https://www.php.net/supported-versions.php, and
|
||||
support will be dropped for PHP versions within 12 months of that version going _End of life_.
|
||||
|
||||
Versioning rationale can be found in the [Versioning Documentation](/docs/versioning.md)
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry;
|
||||
|
||||
use OpenTelemetry\Context\ContextKey;
|
||||
|
||||
interface Baggage
|
||||
{
|
||||
public function getCorrelations(); // TODO
|
||||
|
||||
public function get(ContextKey $key);
|
||||
|
||||
public static function getValue(ContextKey $key, ?Baggage $ctx);
|
||||
|
||||
public function set(ContextKey $key, $value): Baggage;
|
||||
|
||||
public static function setValue(ContextKey $key, $value, ?Baggage $parent = null): Baggage;
|
||||
|
||||
public function removeCorrelation(): Baggage;
|
||||
|
||||
public function clearCorrelations(); // TODO
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Metrics;
|
||||
|
||||
interface Counter extends Metric
|
||||
{
|
||||
/**
|
||||
* Adds value to the counter
|
||||
*
|
||||
* @access public
|
||||
* @param int $value
|
||||
* @return self
|
||||
*/
|
||||
public function add(int $value): Counter;
|
||||
|
||||
/**
|
||||
* Increments value
|
||||
*
|
||||
* @access public
|
||||
* @return self
|
||||
*/
|
||||
public function increment(): Counter;
|
||||
|
||||
/**
|
||||
* Gets the value
|
||||
*
|
||||
* @access public
|
||||
* @return int
|
||||
*/
|
||||
public function getValue(): int;
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Metrics;
|
||||
|
||||
interface Exporter
|
||||
{
|
||||
/**
|
||||
* Possible return values as outlined in the OpenTelemetry spec
|
||||
*/
|
||||
const SUCCESS = 0;
|
||||
const FAILED_NOT_RETRYABLE = 1;
|
||||
const FAILED_RETRYABLE = 2;
|
||||
|
||||
/**
|
||||
* export.
|
||||
*
|
||||
* @access public
|
||||
* @param iterable<Metric> $metrics
|
||||
* @return int
|
||||
*
|
||||
* todo Should we pass a result callback in the 2nd parameter like in JavaScript implementation?
|
||||
*/
|
||||
public function export(iterable $metrics): int;
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Metrics;
|
||||
|
||||
interface LabelableMetric extends Metric
|
||||
{
|
||||
/**
|
||||
* Get $labels
|
||||
* todo: we will probably need a class Label and a typed collection for labels
|
||||
*
|
||||
* @return array<string>
|
||||
*/
|
||||
public function getLabels(): array;
|
||||
|
||||
/**
|
||||
* Set $labels
|
||||
*
|
||||
* @param array<string> $labels
|
||||
* @return self
|
||||
*/
|
||||
public function setLabels(array $labels);
|
||||
|
||||
/**
|
||||
* Set $labels
|
||||
*
|
||||
* @param string $label
|
||||
* @return self
|
||||
*/
|
||||
public function addLabel(string $label);
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Metrics;
|
||||
|
||||
interface Meter
|
||||
{
|
||||
/**
|
||||
* Returns the meter name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string;
|
||||
|
||||
/**
|
||||
* Returns the meter version.
|
||||
*
|
||||
* @return string Metric version
|
||||
*/
|
||||
public function getVersion(): string;
|
||||
|
||||
/**
|
||||
* Creates a Counter metric instrument.
|
||||
*
|
||||
* @param string $name (required) - Counter name
|
||||
* @param string $description (optional) - Counter description
|
||||
*
|
||||
* @return Counter
|
||||
*/
|
||||
public function newCounter(string $name, string $description): Counter;
|
||||
|
||||
/**
|
||||
* Creates an UpDownCounter metric instrument.
|
||||
*
|
||||
* @param string $name (required) - UpDownCounter name
|
||||
* @param string $description (optional) - UpDownCounter description
|
||||
*
|
||||
* @return UpDownCounter
|
||||
*/
|
||||
public function newUpDownCounter(string $name, string $description): UpDownCounter;
|
||||
|
||||
/**
|
||||
* Creates a ValueRecorder metric instrument.
|
||||
*
|
||||
* @param string $name (required) - ValueRecorder name
|
||||
* @param string $description (optional) - ValueRecorder description
|
||||
*
|
||||
* @return ValueRecorder
|
||||
*/
|
||||
public function newValueRecorder(string $name, string $description): ValueRecorder;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Metrics;
|
||||
|
||||
interface MeterProvider
|
||||
{
|
||||
/**
|
||||
* @access public
|
||||
* @param string $name - (required) - This name must identify the instrumentation library
|
||||
* (e.g. io.opentelemetry.contrib.mongodb) and not the instrumented library.
|
||||
* In case an invalid name (null or empty string) is specified, a working default Meter implementation is returned
|
||||
* as a fallback rather than returning null or throwing an exception.
|
||||
* A MeterProvider could also return a no-op Meter here if application owners configure the SDK to suppress
|
||||
* telemetry produced by this library.
|
||||
* @param ?string $version - (optional) - Specifies the version of the instrumentation library (e.g. semver:1.0.0)
|
||||
* @return Meter
|
||||
*/
|
||||
public function getMeter(string $name, ?string $version = null): Meter;
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Metrics;
|
||||
|
||||
interface Metric
|
||||
{
|
||||
/**
|
||||
* Returns metric's name
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string;
|
||||
|
||||
/**
|
||||
* Returns metric's description
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription(): string;
|
||||
|
||||
/**
|
||||
* Returns metric's type
|
||||
*
|
||||
* @access public
|
||||
* @return int
|
||||
*/
|
||||
public function getType(): int;
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Metrics;
|
||||
|
||||
interface MetricKind
|
||||
{
|
||||
public const COUNTER = 1;
|
||||
|
||||
public const UP_DOWN_COUNTER = 2;
|
||||
|
||||
public const VALUE_RECORDER = 3;
|
||||
|
||||
public const SUM_OBSERVER = 4;
|
||||
|
||||
public const UP_DOWN_SUM_OBSERVER = 4;
|
||||
|
||||
public const VALUE_OBSERVER = 5;
|
||||
|
||||
public const TYPES = [
|
||||
self::COUNTER,
|
||||
self::UP_DOWN_COUNTER,
|
||||
self::VALUE_RECORDER,
|
||||
self::SUM_OBSERVER,
|
||||
self::UP_DOWN_SUM_OBSERVER,
|
||||
self::VALUE_OBSERVER,
|
||||
];
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Metrics;
|
||||
|
||||
/*
|
||||
* Name: UpDownCounter
|
||||
* Instrument kind : Synchronous additive
|
||||
* Function(argument) : Add(increment) where increment is a numeric value
|
||||
* Default aggregation : Sum
|
||||
* Notes : Per-request, part of a non-monotonic sum
|
||||
*
|
||||
* UpDownCounter supports negative increments. This makes UpDownCounter
|
||||
* not useful for computing a rate aggregation. It aggregates a Sum,
|
||||
* only the sum is non-monotonic. It is generally useful for capturing changes
|
||||
* in an amount of resources used, or any quantity that rises and falls during
|
||||
* a request.
|
||||
*
|
||||
*/
|
||||
interface UpDownCounter
|
||||
{
|
||||
/**
|
||||
* Updates counter value with the positive or negative int that is passed in.
|
||||
*
|
||||
* @access public
|
||||
* @param int|float $increment
|
||||
* @return int returns the non-monotonic sum
|
||||
*/
|
||||
public function add($increment) : int;
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Metrics;
|
||||
|
||||
/*
|
||||
* Name: UpDownSumObserver
|
||||
* Instrument kind : Asynchronous additive
|
||||
* Function(argument) : Observe(sum) where sum is a numeric value
|
||||
* Default aggregation : Sum
|
||||
* Notes : Captures only one value per Measurement Interval, part of a non-monotonic sum
|
||||
*
|
||||
* UpDownSumObserver is the asynchronous instrument corresponding to UpDownCounter,
|
||||
* used to capture a non-monotonic count with Observe(sum).
|
||||
* "Sum" appears in the name to remind users that it is used to capture sums directly.
|
||||
* Use a UpDownSumObserver to capture any value that starts at zero and rises
|
||||
* or falls throughout the process lifetime.
|
||||
*
|
||||
*/
|
||||
|
||||
interface UpDownSumObserver
|
||||
{
|
||||
/**
|
||||
* Updates sum value with the positive or negative int that is passed in.
|
||||
*
|
||||
* @access public
|
||||
* @param int|float $value
|
||||
* @return int returns the non-monotonic sum
|
||||
*/
|
||||
public function observe($value) : int;
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Metrics;
|
||||
|
||||
interface ValueRecorder extends Metric
|
||||
{
|
||||
/**
|
||||
* records the given value to this ValueRecorder.
|
||||
*
|
||||
* @access public
|
||||
* @param int|float $value
|
||||
* @return void
|
||||
*/
|
||||
public function record($value) : void;
|
||||
|
||||
/**
|
||||
* Returns the sum of the values
|
||||
*
|
||||
* @access public
|
||||
* @return float
|
||||
*/
|
||||
public function getSum(): float;
|
||||
|
||||
/**
|
||||
* Returns the min of the values
|
||||
*
|
||||
* @access public
|
||||
* @return float
|
||||
*/
|
||||
public function getMin(): float;
|
||||
|
||||
/**
|
||||
* Returns the max of the values
|
||||
*
|
||||
* @access public
|
||||
* @return float
|
||||
*/
|
||||
public function getMax(): float;
|
||||
|
||||
/**
|
||||
* Returns the count of the values
|
||||
*
|
||||
* @access public
|
||||
* @return int
|
||||
*/
|
||||
public function getCount(): int;
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Trace;
|
||||
|
||||
interface Attribute
|
||||
{
|
||||
public function getKey(): string;
|
||||
|
||||
/**
|
||||
*
|
||||
* @return bool|int|float|string|list<bool>|list<int>|list<float>|list<string>
|
||||
*
|
||||
*/
|
||||
public function getValue();
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Trace;
|
||||
|
||||
interface Attributes extends \IteratorAggregate, \Countable
|
||||
{
|
||||
/**
|
||||
* Setting event should not invalidate nor change any existing iterators.
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
* @return Attributes
|
||||
*/
|
||||
public function setAttribute(string $name, $value): Attributes;
|
||||
|
||||
public function count(): int;
|
||||
public function getIterator(): AttributesIterator;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Trace;
|
||||
|
||||
interface AttributesIterator extends \Iterator
|
||||
{
|
||||
public function key(): string;
|
||||
public function current(): Attribute;
|
||||
|
||||
/**
|
||||
* Should be valid to call rewind as many times as desired UNTIL next() has been called; then it is implementation
|
||||
* defined whether it is valid or not. The implementation should throw if it cannot be rewound.
|
||||
*/
|
||||
public function rewind(): void;
|
||||
public function valid(): bool;
|
||||
public function next(): void;
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Trace;
|
||||
|
||||
interface Clock
|
||||
{
|
||||
/**
|
||||
* A combination of the Monotonic and Realtime Clocks
|
||||
* Monotonic time value in the first slot, as it'll get accessed more frequently in duration calculations.
|
||||
* @return array
|
||||
*/
|
||||
public function moment(): array;
|
||||
public function now(): int;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Trace;
|
||||
|
||||
interface Event
|
||||
{
|
||||
public function getName(): string;
|
||||
public function getAttributes(): Attributes;
|
||||
public function getTimestamp(): int;
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Trace;
|
||||
|
||||
interface Events extends \IteratorAggregate, \Countable
|
||||
{
|
||||
/**
|
||||
* Adding an event should not invalidate nor change any existing iterators.
|
||||
* @param string $name
|
||||
* @param Attributes|null $attributes
|
||||
* @param int|null $timestamp
|
||||
* @return Events Must return $this to allow setting multiple attributes at once in a chain.
|
||||
*/
|
||||
public function addEvent(string $name, ?Attributes $attributes = null, ?int $timestamp = null): Events;
|
||||
|
||||
public function count(): int;
|
||||
public function getIterator(): EventsIterator;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Trace;
|
||||
|
||||
interface EventsIterator extends \Iterator
|
||||
{
|
||||
public function key(): int;
|
||||
public function current(): Event;
|
||||
|
||||
/**
|
||||
* Should be valid to call rewind as many times as desired UNTIL next() has been called; then it is implementation
|
||||
* defined whether it is valid or not. The implementation should throw if it cannot be rewound.
|
||||
*/
|
||||
public function rewind(): void;
|
||||
public function valid(): bool;
|
||||
public function next(): void;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Trace;
|
||||
|
||||
interface Link
|
||||
{
|
||||
public function getSpanContext(): SpanContext;
|
||||
public function getAttributes(): Attributes;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Trace;
|
||||
|
||||
interface Links extends \IteratorAggregate, \Countable
|
||||
{
|
||||
public function count(): int;
|
||||
public function getIterator(): LinksIterator;
|
||||
|
||||
/**
|
||||
* Adding a link should not invalidate nor change any existing iterators.
|
||||
* @param SpanContext $context
|
||||
* @param Attributes|null $attributes
|
||||
* @return Links Return $this to allow setting multiple links at once in a chain.
|
||||
*/
|
||||
public function addLink(SpanContext $context, ?Attributes $attributes = null): Links;
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Trace;
|
||||
|
||||
interface LinksIterator extends \Iterator
|
||||
{
|
||||
|
||||
/**
|
||||
* Should be valid to call rewind as many times as desired UNTIL next() has been called; then it is implementation
|
||||
* defined whether it is valid or not. The implementation should throw if it cannot be rewound.
|
||||
*/
|
||||
public function rewind(): void;
|
||||
|
||||
public function valid(): bool;
|
||||
|
||||
/**
|
||||
* @return int The order the link was added.
|
||||
*/
|
||||
public function key(): int;
|
||||
|
||||
/**
|
||||
* @return Link Should throw if the iterator is !valid().
|
||||
*/
|
||||
public function current(): Link;
|
||||
|
||||
public function next(): void;
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Trace;
|
||||
|
||||
/**
|
||||
* The API Specification says:
|
||||
* OpenTelemetry can operate on time values up to nanosecond (ns) precision.
|
||||
* The representation of those values is language specific.
|
||||
* A duration is the elapsed time between two events.
|
||||
* - The minimal precision is milliseconds.
|
||||
* - The maximal precision is nanoseconds.
|
||||
*
|
||||
* In the PHP standard library, the best suited function for measuring elapsed
|
||||
* time is `hrtime`, available since PHP 7.3. In other words, callers can
|
||||
* reasonably expect to have nanosecond resolution (nsec) on PHP 7.3 and newer.
|
||||
*/
|
||||
interface MonotonicClock
|
||||
{
|
||||
/**
|
||||
* Represents the amount of time in nanoseconds since an unspecified point
|
||||
* in the past (for example, system start-up time, or the Epoch). This point
|
||||
* does not change after system start-up time.
|
||||
* @return int
|
||||
*/
|
||||
public function now(): int;
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Trace;
|
||||
|
||||
interface PropagationGetter
|
||||
{
|
||||
/**
|
||||
* Gets the value of a given key from a carrier.
|
||||
*
|
||||
* @param mixed $carrier
|
||||
* @param string $key
|
||||
* @return string|null
|
||||
*/
|
||||
public function get($carrier, string $key) : ?string;
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Trace;
|
||||
|
||||
interface PropagationSetter
|
||||
{
|
||||
/**
|
||||
* Set the value for a given key on the associated carrier.
|
||||
*
|
||||
* @param mixed $carrier
|
||||
* @param string $key
|
||||
* @param string $value
|
||||
* @return void
|
||||
*/
|
||||
public function set(&$carrier, string $key, string $value) : void;
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Trace;
|
||||
|
||||
/**
|
||||
* The API specification says:
|
||||
* OpenTelemetry can operate on time values up to nanosecond (ns) precision.
|
||||
* The representation of those values is language specific.
|
||||
* A timestamp is the time elapsed since the Unix epoch.
|
||||
* - The minimal precision is milliseconds.
|
||||
* - The maximal precision is nanoseconds.
|
||||
*
|
||||
* In the PHP standard library, the best suited function for time since the
|
||||
* Unix epoch is `microtime`, which uses microseconds or usecs. This interface
|
||||
* uses nanosecond resolution so it can represent nanoseconds as per
|
||||
* OpenTelemetry specification, but keep this mind as most implementations will
|
||||
* probably use `microtime` so don't expect nanosecond accuracy for a
|
||||
* RealtimeClock.
|
||||
*/
|
||||
interface RealtimeClock
|
||||
{
|
||||
/**
|
||||
* @return int Number of nanoseconds since the Unix epoch
|
||||
*/
|
||||
public function now(): int;
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Trace;
|
||||
|
||||
use Throwable;
|
||||
|
||||
interface Span extends SpanStatus, SpanKind
|
||||
{
|
||||
/**
|
||||
* Attributes SHOULD preserve the order in which they're set. Setting an attribute with the same key as an existing
|
||||
* attribute SHOULD overwrite the existing attribute's value.
|
||||
* @param string $key
|
||||
* @param bool|int|float|string|array $value Note: the array MUST be homogeneous, i.e. it MUST NOT contain values
|
||||
* of different types.
|
||||
* @return Span Must return $this to allow setting multiple attributes at once in a chain
|
||||
*/
|
||||
public function setAttribute(string $key, $value): Span;
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param int $timestamp
|
||||
* @param Attributes|null $attributes
|
||||
* @return Span Must return $this to allow setting multiple attributes at once in a chain.
|
||||
*/
|
||||
public function addEvent(string $name, int $timestamp, ?Attributes $attributes = null): Span;
|
||||
|
||||
/**
|
||||
* @param SpanContext $context
|
||||
* @param Attributes|null $attributes
|
||||
* @return Span Must return $this to allow setting multiple links at once in a chain.
|
||||
*/
|
||||
public function addLink(SpanContext $context, ?Attributes $attributes = null): Span;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param Throwable $exception
|
||||
* @return Span Must return $this to allow setting multiple attributes at once in a chain.
|
||||
*/
|
||||
public function recordException(Throwable $exception, ?Attributes $attributes = null): Span;
|
||||
|
||||
/**
|
||||
* Calling this method is highly discouraged; the name should be set on creation and left alone.
|
||||
* @param string $name
|
||||
* @return Span Must return $this
|
||||
*/
|
||||
public function updateName(string $name): Span;
|
||||
|
||||
/**
|
||||
* Sets the Status of the Span. If used, this will override the default Span status, which is OK.
|
||||
* Only the value of the last call will be recorded, and implementations are free to ignore previous calls.
|
||||
* @param string $code
|
||||
* @param string|null $description
|
||||
* @return Span Must return $this
|
||||
*/
|
||||
public function setSpanStatus(string $code, ?string $description = null): Span;
|
||||
|
||||
/**
|
||||
* @param int|null $timestamp
|
||||
* @return Span Must return $this
|
||||
*/
|
||||
public function end(int $timestamp = null): Span;
|
||||
|
||||
public function isRecording(): bool;
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Trace;
|
||||
|
||||
interface SpanContext
|
||||
{
|
||||
const TRACE_FLAG_SAMPLED = 1;
|
||||
|
||||
public function getTraceId(): string;
|
||||
public function getSpanId(): string;
|
||||
public function getTraceFlags(): int;
|
||||
public function getTraceState(): ?TraceState;
|
||||
public function isValid(): bool;
|
||||
public function isRemote(): bool;
|
||||
public function isSampled(): bool;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Trace;
|
||||
|
||||
interface SpanKind
|
||||
{
|
||||
const KIND_INTERNAL = 0;
|
||||
const KIND_CLIENT = 1;
|
||||
const KIND_SERVER = 2;
|
||||
const KIND_PRODUCER = 3;
|
||||
const KIND_CONSUMER = 4;
|
||||
|
||||
const TYPES = [
|
||||
self::KIND_INTERNAL,
|
||||
self::KIND_CLIENT,
|
||||
self::KIND_SERVER,
|
||||
self::KIND_PRODUCER,
|
||||
self::KIND_CONSUMER,
|
||||
];
|
||||
|
||||
public function getSpanKind(): int;
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Trace;
|
||||
|
||||
use OpenTelemetry\Context\Context;
|
||||
|
||||
/**
|
||||
* The SpanOptions implementation is intended to be tightly coupled with the Span and Tracer implementations.
|
||||
* Hopefully the Span object can implement SpanOptions so that the toSpan() call is almost a no-op, but prevents
|
||||
* the rest of the SpanOptions API from being used after toSpan() is called.
|
||||
*/
|
||||
interface SpanOptions
|
||||
{
|
||||
public function setSpanName(string $name): SpanOptions;
|
||||
/** should default to INTERNAL if not called */
|
||||
public function setSpanKind(int $spanKind): SpanOptions;
|
||||
public function setParent(Context $parentContext): SpanOptions;
|
||||
public function addAttributes(Attributes $attributes): SpanOptions;
|
||||
public function addLinks(Links $links): SpanOptions;
|
||||
|
||||
/**
|
||||
* This should only be used if the creation time has already passed; will set timestamp to current time by default
|
||||
* @param int $timestamp
|
||||
* @return SpanOptions
|
||||
*/
|
||||
public function addStartTimestamp(int $timestamp): SpanOptions;
|
||||
/**
|
||||
* This should only be used if the creation time has already passed; will set to current monotonic clock
|
||||
* value by default
|
||||
* @param int $now
|
||||
* @return SpanOptions
|
||||
*/
|
||||
public function addStart(int $now): SpanOptions;
|
||||
|
||||
public function toSpan(): Span;
|
||||
// todo: how do we want to optionally let people make these spans active? bool arg, setActive, or toActiveSpan?
|
||||
public function toActiveSpan(): Span;
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Trace;
|
||||
|
||||
interface SpanStatus
|
||||
{
|
||||
const UNSET = 'Unset';
|
||||
const OK = 'Ok';
|
||||
const ERROR = 'Error';
|
||||
|
||||
const DESCRIPTION = [
|
||||
self::UNSET => 'The default unset status.',
|
||||
self::OK => 'Not an error; returned on success.',
|
||||
self::ERROR => 'The operation contains an error.',
|
||||
];
|
||||
|
||||
public function getCanonicalStatusCode(): string;
|
||||
public function getStatusDescription(): string;
|
||||
public function isStatusOk(): bool;
|
||||
// public function setStatus(string $code = self::UNSET, string $description = null): void;
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Trace;
|
||||
|
||||
interface TextMapFormatPropagator
|
||||
{
|
||||
/**
|
||||
* Returns list of fields that will be used by this formatter.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function fields() : array;
|
||||
|
||||
/**
|
||||
* Encodes the given SpanContext into propagator specific format and injects
|
||||
* the encoded SpanContext using Setter into it's associated carrier.
|
||||
*
|
||||
* @param SpanContext $context
|
||||
* @param mixed $carrier
|
||||
* @param PropagationSetter $setter
|
||||
* @return void
|
||||
*/
|
||||
public static function inject(SpanContext $context, &$carrier, PropagationSetter $setter) : void;
|
||||
|
||||
/**
|
||||
* Retrieves encoded SpanContext using Getter from the associated carrier.
|
||||
*
|
||||
* @param mixed $carrier
|
||||
* @param PropagationGetter $getter
|
||||
* @return SpanContext
|
||||
*/
|
||||
public static function extract($carrier, PropagationGetter $getter): SpanContext;
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\API\Trace;
|
||||
namespace OpenTelemetry\Trace;
|
||||
|
||||
/**
|
||||
* TraceState parses and stores the tracestate header as an immutable list of string
|
||||
|
@ -18,27 +18,31 @@ namespace OpenTelemetry\API\Trace;
|
|||
* @see https://www.w3.org/TR/trace-context/#tracestate-header
|
||||
* @see https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/api.md#tracestate
|
||||
*/
|
||||
interface TraceStateInterface
|
||||
interface TraceState
|
||||
{
|
||||
/**
|
||||
* Return a new TraceState object that inherits from this TraceState
|
||||
* and contains the given key value pair.
|
||||
*
|
||||
* @return TraceStateInterface
|
||||
* @param string $key
|
||||
* @param string $value
|
||||
* @return TraceState
|
||||
*/
|
||||
public function with(string $key, string $value): TraceStateInterface;
|
||||
public function with(string $key, string $value): TraceState;
|
||||
|
||||
/**
|
||||
* Return a new TraceState object that inherits from this TraceState
|
||||
* without the given key value pair.
|
||||
*
|
||||
* @return TraceStateInterface
|
||||
* @param string $key
|
||||
* @return TraceState
|
||||
*/
|
||||
public function without(string $key): TraceStateInterface;
|
||||
public function without(string $key): TraceState;
|
||||
|
||||
/**
|
||||
* Return the value of a given key from this TraceState if it exists
|
||||
*
|
||||
* @param string $key
|
||||
* @return string|null
|
||||
*/
|
||||
public function get(string $key): ?string;
|
||||
|
@ -51,17 +55,9 @@ interface TraceStateInterface
|
|||
public function getListMemberCount(): int;
|
||||
|
||||
/**
|
||||
* Returns the concatenated string representation.
|
||||
* Build the TraceState header
|
||||
*
|
||||
* @param int|null $limit maximum length of the returned representation
|
||||
* @return string the string representation
|
||||
*
|
||||
* @see https://www.w3.org/TR/trace-context/#tracestate-limits
|
||||
* @return string|null
|
||||
*/
|
||||
public function toString(?int $limit = null): string;
|
||||
|
||||
/**
|
||||
* Returns a string representation of this TraceSate
|
||||
*/
|
||||
public function __toString(): string;
|
||||
public function build(): ?string;
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Trace;
|
||||
|
||||
use OpenTelemetry\Context\Context;
|
||||
|
||||
interface Tracer
|
||||
{
|
||||
public function startSpan(
|
||||
string $name,
|
||||
?Context $parentContext = null,
|
||||
int $spanKind = SpanKind::KIND_INTERNAL,
|
||||
?Attributes $attributes = null,
|
||||
?Links $links = null,
|
||||
?int $startTimestamp = null
|
||||
): Span;
|
||||
|
||||
public function getActiveSpan(): Span;
|
||||
|
||||
public function startActiveSpan(string $name, SpanContext $parentContext, bool $isRemote = false, int $spanKind = SpanKind::KIND_INTERNAL): Span;
|
||||
public function startAndActivateSpan(string $name, int $spanKind = SpanKind::KIND_INTERNAL): Span;
|
||||
public function startSpanWithOptions(string $name): SpanOptions;
|
||||
|
||||
public function setActiveSpan(Span $span): void;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Trace;
|
||||
|
||||
interface TracerProvider
|
||||
{
|
||||
public function getTracer(string $name, ?string $version = null): Tracer;
|
||||
}
|
227
composer.json
227
composer.json
|
@ -1,208 +1,65 @@
|
|||
{
|
||||
"name": "open-telemetry/opentelemetry",
|
||||
"description": "OpenTelemetry makes robust, portable telemetry a built-in feature of cloud-native software.",
|
||||
"keywords": ["opentelemetry", "otel", "open-telemetry", "tracing", "logging", "metrics"],
|
||||
"type": "library",
|
||||
"homepage": "https://opentelemetry.io/docs/php",
|
||||
"readme": "./README.md",
|
||||
"license": "Apache-2.0",
|
||||
"require": {
|
||||
"php": "^8.1",
|
||||
"google/protobuf": "^3.22 || ^4.0",
|
||||
"nyholm/psr7-server": "^1.1",
|
||||
"php-http/discovery": "^1.14",
|
||||
"psr/http-client": "^1.0",
|
||||
"php": "^7.3 || ^8.0",
|
||||
"ext-json": "*",
|
||||
"promphp/prometheus_client_php": "^2.2.1",
|
||||
"grpc/grpc": "^1.30",
|
||||
"google/protobuf": "^v3.3.0",
|
||||
"psr/http-client-implementation": "^1.0",
|
||||
"psr/http-factory-implementation": "^1.0",
|
||||
"psr/http-message": "^1.0.1|^2.0",
|
||||
"psr/log": "^1.1|^2.0|^3.0",
|
||||
"ramsey/uuid": "^3.0 || ^4.0",
|
||||
"symfony/config": "^5.4 || ^6.4 || ^7.0",
|
||||
"symfony/polyfill-mbstring": "^1.23",
|
||||
"symfony/polyfill-php82": "^1.26",
|
||||
"symfony/polyfill-php83": "^1.32",
|
||||
"symfony/polyfill-php84": "^1.32",
|
||||
"tbachert/spi": "^1.0.1"
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true,
|
||||
"allow-plugins": {
|
||||
"bamarni/composer-bin-plugin": true,
|
||||
"composer/package-versions-deprecated": true,
|
||||
"php-http/discovery": true,
|
||||
"symfony/runtime": true,
|
||||
"tbachert/spi": true
|
||||
}
|
||||
"nyholm/dsn": "^2.0.0"
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "opentelemetry-php contributors",
|
||||
"homepage": "https://github.com/open-telemetry/opentelemetry-php/graphs/contributors"
|
||||
"name": "Bob Strecansky",
|
||||
"email": "bob.strecansky@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Dmitry Krokhin",
|
||||
"email": "nekufa@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Levi Morrison",
|
||||
"email": "levim@php.net"
|
||||
}
|
||||
],
|
||||
"replace": {
|
||||
"open-telemetry/api": "1.0.x-dev",
|
||||
"open-telemetry/context": "1.0.x-dev",
|
||||
"open-telemetry/exporter-otlp": "1.0.x-dev",
|
||||
"open-telemetry/exporter-zipkin": "1.0.x-dev",
|
||||
"open-telemetry/extension-propagator-b3": "1.0.x-dev",
|
||||
"open-telemetry/extension-propagator-cloudtrace": "1.0.x-dev",
|
||||
"open-telemetry/extension-propagator-jaeger": "0.0.2",
|
||||
"open-telemetry/gen-otlp-protobuf": "1.0.x-dev",
|
||||
"open-telemetry/sdk": "1.0.x-dev",
|
||||
"open-telemetry/sdk-configuration": "0.1.x-dev",
|
||||
"open-telemetry/sdk-contrib": "1.0.x-dev",
|
||||
"open-telemetry/sem-conv": "1.0.x-dev",
|
||||
"open-telemetry/transport-grpc": "1.0.x-dev"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"OpenTelemetry\\": "src/",
|
||||
"Opentelemetry\\Proto\\": "proto/otel/Opentelemetry/Proto/",
|
||||
"GPBMetadata\\Opentelemetry\\": "proto/otel/GPBMetadata/Opentelemetry/"
|
||||
},
|
||||
"files": [
|
||||
"src/Context/fiber/initialize_fiber_handler.php",
|
||||
"src/API/Trace/functions.php",
|
||||
"src/Contrib/Otlp/_register.php",
|
||||
"src/Contrib/Grpc/_register.php",
|
||||
"src/Contrib/Zipkin/_register.php",
|
||||
"src/Extension/Propagator/B3/_register.php",
|
||||
"src/Extension/Propagator/CloudTrace/_register.php",
|
||||
"src/Extension/Propagator/Jaeger/_register.php",
|
||||
"src/SDK/Logs/Exporter/_register.php",
|
||||
"src/SDK/Metrics/MetricExporter/_register.php",
|
||||
"src/SDK/Propagation/_register.php",
|
||||
"src/SDK/Trace/SpanExporter/_register.php",
|
||||
"src/SDK/Common/Dev/Compatibility/_load.php",
|
||||
"src/SDK/Common/Util/functions.php",
|
||||
"src/SDK/_autoload.php"
|
||||
]
|
||||
"OpenTelemetry\\Context\\": "Context",
|
||||
"OpenTelemetry\\": "api",
|
||||
"OpenTelemetry\\Sdk\\": "sdk",
|
||||
"OpenTelemetry\\Contrib\\": "contrib",
|
||||
"Opentelemetry\\Proto\\Collector\\Trace\\V1\\": "proto/Opentelemetry/Proto/Collector/Trace/V1",
|
||||
"Opentelemetry\\Proto\\Trace\\V1\\":"proto/Opentelemetry/Proto/Trace/V1",
|
||||
"Opentelemetry\\Proto\\Common\\V1\\":"proto/Opentelemetry/Proto/Common/V1",
|
||||
"Opentelemetry\\Proto\\Resource\\V1\\":"proto/Opentelemetry/Proto/Resource/V1",
|
||||
"GPBMetadata\\Opentelemetry\\Proto\\Collector\\Trace\\V1\\": "proto/GPBMetadata/Opentelemetry/Proto/Collector/Trace/V1",
|
||||
"GPBMetadata\\Opentelemetry\\Proto\\Trace\\V1\\":"proto/GPBMetadata/Opentelemetry/Proto/Trace/V1",
|
||||
"GPBMetadata\\Opentelemetry\\Proto\\Common\\V1\\":"proto/GPBMetadata/Opentelemetry/Proto/Common/V1",
|
||||
"GPBMetadata\\Opentelemetry\\Proto\\Resource\\V1\\":"proto/GPBMetadata/Opentelemetry/Proto/Resource/V1"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"OpenTelemetry\\Tests\\": "tests/",
|
||||
"OpenTelemetry\\Example\\": "examples/src/",
|
||||
"ExampleSDK\\ComponentProvider\\": "tests/Unit/Config/SDK/Configuration/ExampleSdk/"
|
||||
"OpenTelemetry\\Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-grpc": "*",
|
||||
"bamarni/composer-bin-plugin": "^1.8",
|
||||
"dg/bypass-finals": "^1.4",
|
||||
"grpc/grpc": "^1.30",
|
||||
"guzzlehttp/guzzle": "^7.4",
|
||||
"guzzlehttp/psr7": "^2.1",
|
||||
"mikey179/vfsstream": "^1.6.11",
|
||||
"mockery/mockery": "^1.5.1",
|
||||
"monolog/monolog": "^3.0",
|
||||
"nyholm/psr7": "^1.4",
|
||||
"open-telemetry/dev-tools": "dev-main",
|
||||
"php-http/mock-client": "^1.5",
|
||||
"phpdocumentor/reflection-docblock": "^5.3",
|
||||
"phpspec/prophecy": "^1.22",
|
||||
"phpspec/prophecy-phpunit": "^2",
|
||||
"phpstan/phpstan": "^1.10.13",
|
||||
"phpstan/phpstan-mockery": "^1.1",
|
||||
"phpstan/phpstan-phpunit": "^1.3",
|
||||
"phpunit/phpunit": "^10 || ^11",
|
||||
"sebastian/exporter": "<= 6.0.1 || >= 6.1.3",
|
||||
"phpunit/phpunit": "^9.3",
|
||||
"composer/xdebug-handler": "^2.0",
|
||||
"phan/phan": "^4.1",
|
||||
"friendsofphp/php-cs-fixer": "^3.0",
|
||||
"vimeo/psalm": "^4.0",
|
||||
"phpstan/phpstan": "^0.12.50",
|
||||
"phpstan/phpstan-phpunit": "^0.12.16",
|
||||
"psalm/plugin-phpunit": "^0.13.0",
|
||||
"guzzlehttp/guzzle": "^7.3",
|
||||
"guzzlehttp/psr7": "^2.0@RC",
|
||||
"symfony/http-client": "^5.2",
|
||||
"symfony/var-exporter": "^5.4 || ^6.4 || ^7.0",
|
||||
"symfony/yaml": "^5.4 || ^6.4 || ^7.0"
|
||||
},
|
||||
"conflict": {
|
||||
"mockery/mockery": "1.6.12"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-gmp": "To support unlimited number of synchronous metric readers",
|
||||
"ext-grpc": "To use the OTLP GRPC Exporter",
|
||||
"ext-protobuf": "For more performant protobuf/grpc exporting",
|
||||
"ext-yaml": "Allows loading config from yaml files",
|
||||
"symfony/yaml": "Allows loading config from yaml files"
|
||||
},
|
||||
"extra": {
|
||||
"bamarni-bin": {
|
||||
"bin-links": false,
|
||||
"target-directory": "vendor-bin",
|
||||
"forward-command": true
|
||||
},
|
||||
"spi": {
|
||||
"OpenTelemetry\\API\\Configuration\\Config\\ComponentProvider": [
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Propagator\\TextMapPropagatorB3",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Propagator\\TextMapPropagatorB3Multi",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Propagator\\TextMapPropagatorBaggage",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Propagator\\TextMapPropagatorComposite",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Propagator\\TextMapPropagatorJaeger",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Propagator\\TextMapPropagatorTraceContext",
|
||||
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SamplerAlwaysOff",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SamplerAlwaysOn",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SamplerParentBased",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SamplerTraceIdRatioBased",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SpanExporterConsole",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SpanExporterMemory",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SpanExporterOtlpFile",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SpanExporterOtlpGrpc",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SpanExporterOtlpHttp",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SpanExporterZipkin",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SpanProcessorBatch",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SpanProcessorSimple",
|
||||
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Metrics\\AggregationResolverDefault",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Metrics\\MetricExporterConsole",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Metrics\\MetricExporterMemory",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Metrics\\MetricExporterOtlpFile",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Metrics\\MetricExporterOtlpGrpc",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Metrics\\MetricExporterOtlpHttp",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Metrics\\MetricReaderPeriodic",
|
||||
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordExporterConsole",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordExporterMemory",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordExporterOtlpFile",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordExporterOtlpGrpc",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordExporterOtlpHttp",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordProcessorBatch",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordProcessorSimple",
|
||||
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Detector\\Composer",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Detector\\Host",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Detector\\Process",
|
||||
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Instrumentation\\General\\HttpConfigProvider",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Instrumentation\\General\\PeerConfigProvider",
|
||||
|
||||
"OpenTelemetry\\Example\\ExampleConfigProvider",
|
||||
|
||||
"OpenTelemetry\\Tests\\Integration\\Config\\ComponentProvider\\Detector\\Container",
|
||||
"OpenTelemetry\\Tests\\Integration\\Config\\ComponentProvider\\Detector\\Os",
|
||||
"OpenTelemetry\\Tests\\Integration\\Config\\ComponentProvider\\Metrics\\AggregationResolverExplicitBucketHistogram",
|
||||
"OpenTelemetry\\Tests\\Integration\\Config\\ComponentProvider\\Metrics\\MetricExporterPrometheus",
|
||||
"OpenTelemetry\\Tests\\Integration\\Config\\ComponentProvider\\Metrics\\MetricReaderPull",
|
||||
"OpenTelemetry\\Tests\\Integration\\Config\\ComponentProvider\\Propagator\\TextMapPropagatorXray",
|
||||
"OpenTelemetry\\Tests\\Integration\\Config\\ComponentProvider\\Propagator\\TextMapPropagatorOtTrace"
|
||||
],
|
||||
"OpenTelemetry\\API\\Configuration\\ConfigEnv\\EnvComponentLoader": [
|
||||
"OpenTelemetry\\API\\Instrumentation\\Configuration\\General\\ConfigEnv\\EnvComponentLoaderHttpConfig",
|
||||
"OpenTelemetry\\API\\Instrumentation\\Configuration\\General\\ConfigEnv\\EnvComponentLoaderPeerConfig",
|
||||
|
||||
"OpenTelemetry\\Example\\ExampleConfigLoader"
|
||||
],
|
||||
"OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\HookManagerInterface": [
|
||||
"OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\ExtensionHookManager"
|
||||
],
|
||||
"OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\Instrumentation": [
|
||||
"OpenTelemetry\\Example\\ExampleInstrumentation"
|
||||
],
|
||||
"OpenTelemetry\\SDK\\Common\\Configuration\\Resolver\\ResolverInterface": [
|
||||
"OpenTelemetry\\SDK\\Common\\Configuration\\Resolver\\SdkConfigurationResolver"
|
||||
],
|
||||
"OpenTelemetry\\Config\\SDK\\Configuration\\Environment\\EnvSourceProvider": [
|
||||
"OpenTelemetry\\Config\\SDK\\Configuration\\Environment\\Adapter\\SymfonyDotenvProvider",
|
||||
"OpenTelemetry\\Config\\SDK\\Configuration\\Environment\\Adapter\\VlucasPhpdotenvProvider",
|
||||
|
||||
"OpenTelemetry\\Tests\\Integration\\SDK\\Common\\Configuration\\TestEnvSourceProvider"
|
||||
]
|
||||
}
|
||||
"nyholm/psr7": "^1.4"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Contrib\Jaeger;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Psr7\HttpFactory;
|
||||
use OpenTelemetry\Contrib\Zipkin;
|
||||
use OpenTelemetry\Sdk\Trace;
|
||||
|
||||
class Exporter extends Zipkin\Exporter implements Trace\Exporter
|
||||
{
|
||||
public static function fromConnectionString(string $endpointUrl, string $name, $args = null)
|
||||
{
|
||||
$factory = new HttpFactory();
|
||||
$exporter = new Exporter(
|
||||
$name,
|
||||
$endpointUrl,
|
||||
new Client(),
|
||||
$factory,
|
||||
$factory
|
||||
);
|
||||
|
||||
return $exporter;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,182 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Contrib\Newrelic;
|
||||
|
||||
use Exception;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Psr7\HttpFactory;
|
||||
use InvalidArgumentException;
|
||||
use OpenTelemetry\Sdk\Trace;
|
||||
use Psr\Http\Client\ClientExceptionInterface;
|
||||
use Psr\Http\Client\ClientInterface;
|
||||
use Psr\Http\Client\NetworkExceptionInterface;
|
||||
use Psr\Http\Client\RequestExceptionInterface;
|
||||
use Psr\Http\Message\RequestFactoryInterface;
|
||||
use Psr\Http\Message\StreamFactoryInterface;
|
||||
|
||||
/**
|
||||
* Class NewrelicExporter - implements the export interface for data transfer via Newrelic protocol
|
||||
* @package OpenTelemetry\Exporter
|
||||
*
|
||||
* This is an experimental, non-supported exporter.
|
||||
* It will send PHP Otel trace data end to end across the internet to a functional backend.
|
||||
* Needs a license key to connect. For a free account/key, go to: https://newrelic.com/signup/
|
||||
*/
|
||||
class Exporter implements Trace\Exporter
|
||||
{
|
||||
private const DATA_FORMAT = 'newrelic';
|
||||
private const DATA_FORMAT_VERSION_DEFAULT = '1';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $endpointUrl;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $licenseKey;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* @var SpanConverter
|
||||
*/
|
||||
private $spanConverter;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $running = true;
|
||||
|
||||
/**
|
||||
* @var ClientInterface
|
||||
*/
|
||||
private $client;
|
||||
|
||||
/**
|
||||
* @var RequestFactoryInterface
|
||||
*/
|
||||
private $requestFactory;
|
||||
|
||||
/**
|
||||
* @var StreamFactoryInterface
|
||||
*/
|
||||
private $streamFactory;
|
||||
|
||||
private $dataFormatVersion;
|
||||
|
||||
public function __construct(
|
||||
$name,
|
||||
string $endpointUrl,
|
||||
string $licenseKey,
|
||||
ClientInterface $client,
|
||||
RequestFactoryInterface $requestFactory,
|
||||
StreamFactoryInterface $streamFactory,
|
||||
SpanConverter $spanConverter = null,
|
||||
string $dataFormatVersion = Exporter::DATA_FORMAT_VERSION_DEFAULT
|
||||
) {
|
||||
$parsedDsn = parse_url($endpointUrl);
|
||||
|
||||
if (!is_array($parsedDsn)) {
|
||||
throw new InvalidArgumentException('Unable to parse provided DSN');
|
||||
}
|
||||
if (
|
||||
!isset($parsedDsn['scheme'])
|
||||
|| !isset($parsedDsn['host'])
|
||||
|| !isset($parsedDsn['path'])
|
||||
) {
|
||||
throw new InvalidArgumentException('Endpoint should have scheme, host, port and path');
|
||||
}
|
||||
|
||||
$this->name = $name;
|
||||
$this->endpointUrl = $endpointUrl;
|
||||
$this->licenseKey = $licenseKey;
|
||||
$this->client = $client;
|
||||
$this->requestFactory = $requestFactory;
|
||||
$this->streamFactory = $streamFactory;
|
||||
$this->spanConverter = $spanConverter ?? new SpanConverter($name);
|
||||
$this->dataFormatVersion = $dataFormatVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports the provided Span data via the Newrelic protocol
|
||||
*
|
||||
* @param iterable<Trace\ReadableSpan> $spans Array of Spans
|
||||
* @return int return code, defined on the Exporter interface
|
||||
*/
|
||||
public function export(iterable $spans): int
|
||||
{
|
||||
if (!$this->running) {
|
||||
return Exporter::FAILED_NOT_RETRYABLE;
|
||||
}
|
||||
|
||||
if (empty($spans)) {
|
||||
return Trace\Exporter::SUCCESS;
|
||||
}
|
||||
|
||||
$convertedSpans = [];
|
||||
foreach ($spans as $span) {
|
||||
array_push($convertedSpans, $this->spanConverter->convert($span));
|
||||
}
|
||||
$commonAttributes = ['attributes' => [ 'service.name' => $this->name,
|
||||
'host' => $this->endpointUrl, ]];
|
||||
$payload = [[ 'common' => $commonAttributes,
|
||||
'spans' => $convertedSpans, ]];
|
||||
|
||||
try {
|
||||
$body = $this->streamFactory->createStream(json_encode($payload));
|
||||
$request = $this->requestFactory
|
||||
->createRequest('POST', $this->endpointUrl)
|
||||
->withBody($body)
|
||||
->withHeader('content-type', 'application/json')
|
||||
->withAddedHeader('Api-Key', $this->licenseKey)
|
||||
->withAddedHeader('Data-Format', Exporter::DATA_FORMAT)
|
||||
->withAddedHeader('Data-Format-Version', $this->dataFormatVersion);
|
||||
|
||||
$response = $this->client->sendRequest($request);
|
||||
} catch (RequestExceptionInterface $e) {
|
||||
return Trace\Exporter::FAILED_NOT_RETRYABLE;
|
||||
} catch (NetworkExceptionInterface | ClientExceptionInterface $e) {
|
||||
return Trace\Exporter::FAILED_RETRYABLE;
|
||||
}
|
||||
|
||||
if ($response->getStatusCode() >= 400 && $response->getStatusCode() < 500) {
|
||||
return Trace\Exporter::FAILED_NOT_RETRYABLE;
|
||||
}
|
||||
|
||||
if ($response->getStatusCode() >= 500 && $response->getStatusCode() < 600) {
|
||||
return Trace\Exporter::FAILED_RETRYABLE;
|
||||
}
|
||||
|
||||
return Trace\Exporter::SUCCESS;
|
||||
}
|
||||
|
||||
public function shutdown(): void
|
||||
{
|
||||
$this->running = false;
|
||||
}
|
||||
|
||||
public static function fromConnectionString(string $endpointUrl, string $name, $args)
|
||||
{
|
||||
if ($args == false) {
|
||||
throw new Exception('Invalid license key.');
|
||||
}
|
||||
$factory = new HttpFactory();
|
||||
$exporter = new Exporter(
|
||||
$name,
|
||||
$endpointUrl,
|
||||
$args,
|
||||
new Client(),
|
||||
$factory,
|
||||
$factory
|
||||
);
|
||||
|
||||
return $exporter;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Contrib\Newrelic;
|
||||
|
||||
use OpenTelemetry\Sdk\Trace\ReadableSpan;
|
||||
|
||||
class SpanConverter
|
||||
{
|
||||
const STATUS_CODE_TAG_KEY = 'otel.status_code';
|
||||
const STATUS_DESCRIPTION_TAG_KEY = 'otel.status_description';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $serviceName;
|
||||
|
||||
public function __construct(string $serviceName)
|
||||
{
|
||||
$this->serviceName = $serviceName;
|
||||
}
|
||||
|
||||
public function convert(ReadableSpan $span)
|
||||
{
|
||||
$spanParent = $span->getParent();
|
||||
$row = [
|
||||
'id' => $span->getContext()->getSpanId(),
|
||||
'trace.id' => $span->getContext()->getTraceId(),
|
||||
'attributes' => [
|
||||
'name' => $span->getSpanName(),
|
||||
'service.name' => $this->serviceName,
|
||||
'parent.id' => $spanParent ? $spanParent->getSpanId() : null,
|
||||
'timestamp' => ($span->getStartEpochTimestamp() / 1e6), // RealtimeClock in milliseconds
|
||||
'duration.ms' => (($span->getEnd() - $span->getStart()) / 1e6), // Diff in milliseconds
|
||||
self::STATUS_CODE_TAG_KEY => $span->getStatus()->getCanonicalStatusCode(),
|
||||
self::STATUS_DESCRIPTION_TAG_KEY => $span->getStatus()->getStatusDescription(),
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($span->getAttributes() as $k => $v) {
|
||||
$row['attributes'][$k] = $v->getValue();
|
||||
}
|
||||
|
||||
/*
|
||||
foreach ($span->getEvents() as $event) {
|
||||
if (!array_key_exists('annotations', $row)) {
|
||||
$row['annotations'] = [];
|
||||
}
|
||||
$row['annotations'][] = [
|
||||
'timestamp' => (int) ($event->getTimestamp() / 1e6), // RealtimeClock in milliseconds
|
||||
'value' => $event->getName(),
|
||||
];
|
||||
}
|
||||
*/
|
||||
return $row;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,181 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Contrib\Otlp;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Psr7\HttpFactory;
|
||||
use OpenTelemetry\Contrib\OtlpGrpc\Exporter as OtlpGrpcExporter;
|
||||
use OpenTelemetry\Contrib\OtlpHttp\Exporter as OtlpHttpExporter;
|
||||
use OpenTelemetry\Sdk\Trace;
|
||||
use Psr\Http\Client\ClientExceptionInterface;
|
||||
use Psr\Http\Client\ClientInterface;
|
||||
use Psr\Http\Client\NetworkExceptionInterface;
|
||||
use Psr\Http\Client\RequestExceptionInterface;
|
||||
use Psr\Http\Message\RequestFactoryInterface;
|
||||
use Psr\Http\Message\StreamFactoryInterface;
|
||||
|
||||
class Exporter implements Trace\Exporter
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $endpointUrl;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $protocol;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $insecure;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $certificateFile;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $headers;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $compression;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $timeout;
|
||||
/**
|
||||
* @var SpanConverter
|
||||
*/
|
||||
private $spanConverter;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $running = true;
|
||||
|
||||
/**
|
||||
* @var ClientInterface
|
||||
*/
|
||||
private $client;
|
||||
|
||||
/**
|
||||
* @var RequestFactoryInterface
|
||||
*/
|
||||
private $requestFactory;
|
||||
|
||||
/**
|
||||
* @var StreamFactoryInterface
|
||||
*/
|
||||
private $streamFactory;
|
||||
|
||||
/**
|
||||
* Exporter constructor.
|
||||
* @param string $serviceName
|
||||
*/
|
||||
public function __construct(
|
||||
$serviceName,
|
||||
ClientInterface $client,
|
||||
RequestFactoryInterface $requestFactory,
|
||||
StreamFactoryInterface $streamFactory,
|
||||
SpanConverter $spanConverter = null
|
||||
) {
|
||||
|
||||
// Set default values based on presence of env variable
|
||||
$this->endpointUrl = getenv('OTEL_EXPORTER_OTLP_ENDPOINT') ?: 'localhost:55681';
|
||||
$this->protocol = getenv('OTEL_EXPORTER_OTLP_PROTOCOL') ?: 'json';
|
||||
$this->insecure = getenv('OTEL_EXPORTER_OTLP_INSECURE') ?: 'false';
|
||||
$this->certificateFile = getenv('OTEL_EXPORTER_OTLP_CERTIFICATE') ?: 'none';
|
||||
$this->headers[] = getenv('OTEL_EXPORTER_OTLP_HEADERS') ?: 'none';
|
||||
$this->compression = getenv('OTEL_EXPORTER_OTLP_COMPRESSION') ?: 'none';
|
||||
$this->timeout =(int) getenv('OTEL_EXPORTER_OTLP_TIMEOUT') ?: 10;
|
||||
|
||||
$this->client = $client;
|
||||
$this->requestFactory = $requestFactory;
|
||||
$this->streamFactory = $streamFactory;
|
||||
$this->spanConverter = $spanConverter ?? new SpanConverter($serviceName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports the provided Span data via the OTLP protocol
|
||||
*
|
||||
* @param iterable<Trace\ReadableSpan> $spans Array of Spans
|
||||
* @return int return code, defined on the Exporter interface
|
||||
*/
|
||||
public function export(iterable $spans): int
|
||||
{
|
||||
if (!$this->running) {
|
||||
return Exporter::FAILED_NOT_RETRYABLE;
|
||||
}
|
||||
|
||||
if (empty($spans)) {
|
||||
return Trace\Exporter::SUCCESS;
|
||||
}
|
||||
|
||||
$convertedSpans = [];
|
||||
foreach ($spans as $span) {
|
||||
array_push($convertedSpans, $this->spanConverter->convert($span));
|
||||
}
|
||||
|
||||
try {
|
||||
$body = $this->streamFactory->createStream(json_encode($convertedSpans));
|
||||
$request = $this->requestFactory
|
||||
->createRequest('POST', $this->endpointUrl)
|
||||
->withBody($body);
|
||||
|
||||
if ($this->protocol == 'json') {
|
||||
$request->withHeader('content-type', 'application/json');
|
||||
}
|
||||
$response = $this->client->sendRequest($request);
|
||||
} catch (RequestExceptionInterface $e) {
|
||||
return Trace\Exporter::FAILED_NOT_RETRYABLE;
|
||||
} catch (NetworkExceptionInterface | ClientExceptionInterface $e) {
|
||||
return Trace\Exporter::FAILED_RETRYABLE;
|
||||
}
|
||||
|
||||
if ($response->getStatusCode() >= 400 && $response->getStatusCode() < 500) {
|
||||
return Trace\Exporter::FAILED_NOT_RETRYABLE;
|
||||
}
|
||||
|
||||
if ($response->getStatusCode() >= 500 && $response->getStatusCode() < 600) {
|
||||
return Trace\Exporter::FAILED_RETRYABLE;
|
||||
}
|
||||
|
||||
return Trace\Exporter::SUCCESS;
|
||||
}
|
||||
|
||||
public function shutdown(): void
|
||||
{
|
||||
$this->running = false;
|
||||
}
|
||||
|
||||
public static function fromConnectionString(string $endpointUrl, string $name, $args)
|
||||
{
|
||||
// @phan-suppress-next-line PhanUndeclaredFunction
|
||||
if ($args !== '' && str_contains($args, 'grpc')) {
|
||||
return OtlpGrpcExporter::fromConnectionString();
|
||||
}
|
||||
// @phan-suppress-next-line PhanUndeclaredFunction
|
||||
if ($args !== '' && str_contains($args, 'http')) {
|
||||
return OtlpHttpExporter::fromConnectionString();
|
||||
}
|
||||
|
||||
$factory = new HttpFactory();
|
||||
$exporter = new Exporter(
|
||||
$name,
|
||||
new Client(),
|
||||
$factory,
|
||||
$factory
|
||||
);
|
||||
|
||||
return $exporter;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Contrib\Otlp;
|
||||
|
||||
use OpenTelemetry\Sdk\Trace\ReadableSpan;
|
||||
|
||||
class SpanConverter
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $serviceName;
|
||||
|
||||
public function __construct(string $serviceName)
|
||||
{
|
||||
$this->serviceName = $serviceName;
|
||||
}
|
||||
|
||||
private function sanitiseTagValue($value)
|
||||
{
|
||||
// Casting false to string makes an empty string
|
||||
if (is_bool($value)) {
|
||||
return $value ? 'true' : 'false';
|
||||
}
|
||||
|
||||
// OTLP tags must be strings, but opentelemetry
|
||||
// accepts strings, booleans, numbers, and lists of each.
|
||||
if (is_array($value)) {
|
||||
return join(',', array_map([$this, 'sanitiseTagValue'], $value));
|
||||
}
|
||||
|
||||
// Floats will lose precision if their string representation
|
||||
// is >=14 or >=17 digits, depending on PHP settings.
|
||||
// Can also throw E_RECOVERABLE_ERROR if $value is an object
|
||||
// without a __toString() method.
|
||||
// This is possible because OpenTelemetry\Trace\Span does not verify
|
||||
// setAttribute() $value input.
|
||||
return (string) $value;
|
||||
}
|
||||
|
||||
public function convert(ReadableSpan $span)
|
||||
{
|
||||
$spanParent = $span->getParent();
|
||||
$row = [
|
||||
'id' => $span->getContext()->getSpanId(),
|
||||
'traceId' => $span->getContext()->getTraceId(),
|
||||
'parentId' => $spanParent ? $spanParent->getSpanId() : null,
|
||||
'localEndpoint' => [
|
||||
'serviceName' => $this->serviceName,
|
||||
],
|
||||
'name' => $span->getSpanName(),
|
||||
'timestamp' => (int) ($span->getStartEpochTimestamp() / 1e3), // RealtimeClock in microseconds
|
||||
'duration' => (int) (($span->getEnd() - $span->getStart()) / 1e3), // Diff in microseconds
|
||||
];
|
||||
|
||||
foreach ($span->getAttributes() as $k => $v) {
|
||||
if (!array_key_exists('tags', $row)) {
|
||||
$row['tags'] = [];
|
||||
}
|
||||
$row['tags'][$k] = $this->sanitiseTagValue($v->getValue());
|
||||
}
|
||||
|
||||
foreach ($span->getEvents() as $event) {
|
||||
if (!array_key_exists('annotations', $row)) {
|
||||
$row['annotations'] = [];
|
||||
}
|
||||
$row['annotations'][] = [
|
||||
'timestamp' => (int) ($event->getTimestamp() / 1e3), // RealtimeClock in microseconds
|
||||
'value' => $event->getName(),
|
||||
];
|
||||
}
|
||||
|
||||
return $row;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,217 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Contrib\OtlpGrpc;
|
||||
|
||||
use grpc;
|
||||
use InvalidArgumentException;
|
||||
use Opentelemetry\Proto\Collector\Trace\V1\ExportTraceServiceRequest;
|
||||
use Opentelemetry\Proto\Collector\Trace\V1\TraceServiceClient;
|
||||
use OpenTelemetry\Sdk\Trace;
|
||||
|
||||
class Exporter implements Trace\Exporter
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $endpointURL;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $protocol;
|
||||
|
||||
/**
|
||||
* @var bool|string
|
||||
*/
|
||||
private $insecure;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $certificateFile;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $headers;
|
||||
|
||||
/**
|
||||
* @var bool|string
|
||||
*/
|
||||
private $compression;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $timeout;
|
||||
/**
|
||||
* @var SpanConverter
|
||||
*/
|
||||
private $spanConverter;
|
||||
|
||||
private $metadata;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $running = true;
|
||||
|
||||
/**
|
||||
* @var TraceServiceClient
|
||||
*/
|
||||
|
||||
private $client;
|
||||
|
||||
/**
|
||||
* OTLP GRPC Exporter Constructor
|
||||
*/
|
||||
public function __construct(
|
||||
string $endpointURL = 'localhost:4317',
|
||||
bool $insecure = true,
|
||||
string $certificateFile = '',
|
||||
string $headers = '',
|
||||
bool $compression = false,
|
||||
int $timeout = 10,
|
||||
TraceServiceClient $client = null
|
||||
) {
|
||||
|
||||
// Set default values based on presence of env variable
|
||||
$this->endpointURL = getenv('OTEL_EXPORTER_OTLP_ENDPOINT') ?: $endpointURL;
|
||||
$this->protocol = getenv('OTEL_EXPORTER_OTLP_PROTOCOL') ?: 'grpc'; // I guess this is redundant?
|
||||
$this->insecure = getenv('OTEL_EXPORTER_OTLP_INSECURE') ? filter_var(getenv('OTEL_EXPORTER_OTLP_INSECURE'), FILTER_VALIDATE_BOOLEAN): $insecure;
|
||||
$this->certificateFile = getenv('OTEL_EXPORTER_OTLP_CERTIFICATE') ?: $certificateFile;
|
||||
$this->headers = getenv('OTEL_EXPORTER_OTLP_HEADERS') ?: $headers;
|
||||
$this->compression = getenv('OTEL_EXPORTER_OTLP_COMPRESSION') ?: $compression;
|
||||
$this->timeout =(int) getenv('OTEL_EXPORTER_OTLP_TIMEOUT') ?: $timeout;
|
||||
|
||||
$this->spanConverter = new SpanConverter();
|
||||
|
||||
$this->metadata = $this->metadataFromHeaders($this->headers);
|
||||
|
||||
$opts = $this->getClientOptions();
|
||||
|
||||
$this->client = $client ?? new TraceServiceClient($this->endpointURL, $opts);
|
||||
}
|
||||
|
||||
public function getClientOptions(): array
|
||||
{
|
||||
$opts = [
|
||||
'update_metadata' => function () {
|
||||
return $this->metadata;
|
||||
},
|
||||
'timeout' => $this->timeout,
|
||||
];
|
||||
|
||||
if (!$this->insecure && !$this->certificateFile) {
|
||||
// Assumed default
|
||||
$opts['credentials'] = Grpc\ChannelCredentials::createSsl('');
|
||||
} elseif (!$this->insecure && $this->certificateFile !== '') {
|
||||
// Should we validate more?
|
||||
$opts['credentials'] = Grpc\ChannelCredentials::createSsl(file_get_contents($this->certificateFile));
|
||||
} else {
|
||||
$opts['credentials'] = Grpc\ChannelCredentials::createInsecure();
|
||||
}
|
||||
|
||||
if ($this->compression) {
|
||||
// gzip is the only specified compression method for now
|
||||
$opts['grpc.default_compression_algorithm'] = 2;
|
||||
}
|
||||
|
||||
return $opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports the provided Span data via the OTLP protocol
|
||||
*
|
||||
* @param iterable<Trace\ReadableSpan> $spans Array of Spans
|
||||
* @return int return code, defined on the Exporter interface
|
||||
*/
|
||||
public function export(iterable $spans): int
|
||||
{
|
||||
if (!$this->running) {
|
||||
return Exporter::FAILED_NOT_RETRYABLE;
|
||||
}
|
||||
|
||||
if (empty($spans)) {
|
||||
return Trace\Exporter::SUCCESS;
|
||||
}
|
||||
|
||||
$resourcespans = [$this->spanConverter->as_otlp_resource_span($spans)];
|
||||
|
||||
$request = new ExportTraceServiceRequest([
|
||||
'resource_spans' => $resourcespans,
|
||||
]);
|
||||
|
||||
list($response, $status) = $this->client->Export($request)->wait();
|
||||
|
||||
if ($status->code == \Grpc\STATUS_OK) {
|
||||
return Trace\Exporter::SUCCESS;
|
||||
}
|
||||
|
||||
if (in_array($status->code, [
|
||||
\Grpc\STATUS_CANCELLED,
|
||||
\Grpc\STATUS_DEADLINE_EXCEEDED,
|
||||
\Grpc\STATUS_PERMISSION_DENIED,
|
||||
\Grpc\STATUS_RESOURCE_EXHAUSTED,
|
||||
\Grpc\STATUS_ABORTED,
|
||||
\Grpc\STATUS_OUT_OF_RANGE,
|
||||
\Grpc\STATUS_UNAVAILABLE,
|
||||
\Grpc\STATUS_DATA_LOSS,
|
||||
\Grpc\STATUS_UNAUTHENTICATED,
|
||||
])) {
|
||||
return Trace\Exporter::FAILED_RETRYABLE;
|
||||
}
|
||||
|
||||
return Trace\Exporter::FAILED_NOT_RETRYABLE;
|
||||
}
|
||||
|
||||
public function setHeader($key, $value)
|
||||
{
|
||||
$this->metadata[$key] = [$value];
|
||||
}
|
||||
|
||||
public function getHeaders()
|
||||
{
|
||||
return $this->metadata;
|
||||
}
|
||||
|
||||
public function metadataFromHeaders($headers): array
|
||||
{
|
||||
if (is_array($headers)) {
|
||||
throw new InvalidArgumentException('Configuring Headers Via');
|
||||
}
|
||||
|
||||
if (strlen($headers) <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$pairs = explode(',', $headers);
|
||||
|
||||
$metadata = [];
|
||||
foreach ($pairs as $pair) {
|
||||
if (!strpos($pair, '=')) {
|
||||
continue;
|
||||
}
|
||||
list($key, $value) = explode('=', $pair, 2);
|
||||
$metadata[$key] = [$value];
|
||||
}
|
||||
|
||||
return $metadata;
|
||||
}
|
||||
|
||||
public function shutdown(): void
|
||||
{
|
||||
$this->running = false;
|
||||
}
|
||||
|
||||
public static function fromConnectionString(string $endpointUrl = null, string $name = null, $args = null)
|
||||
{
|
||||
return new Exporter();
|
||||
}
|
||||
// public function getHeaders()
|
||||
// {
|
||||
// return $this->metadataFromHeaders($this->headers);
|
||||
// }
|
||||
}
|
|
@ -0,0 +1,189 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Contrib\OtlpGrpc;
|
||||
|
||||
use Opentelemetry\Proto;
|
||||
use Opentelemetry\Proto\Common\V1\AnyValue;
|
||||
use Opentelemetry\Proto\Common\V1\ArrayValue;
|
||||
use Opentelemetry\Proto\Common\V1\InstrumentationLibrary;
|
||||
use Opentelemetry\Proto\Common\V1\KeyValue;
|
||||
use Opentelemetry\Proto\Trace\V1\InstrumentationLibrarySpans;
|
||||
use Opentelemetry\Proto\Trace\V1\ResourceSpans;
|
||||
use Opentelemetry\Proto\Trace\V1\Span as CollectorSpan;
|
||||
use Opentelemetry\Proto\Trace\V1\Span\Event;
|
||||
use Opentelemetry\Proto\Trace\V1\Span\SpanKind;
|
||||
use Opentelemetry\Proto\Trace\V1\Status;
|
||||
use Opentelemetry\Proto\Trace\V1\Status\StatusCode;
|
||||
use OpenTelemetry\Sdk\Trace\ReadableSpan;
|
||||
use OpenTelemetry\Sdk\Trace\SpanStatus;
|
||||
|
||||
class SpanConverter
|
||||
{
|
||||
public function as_otlp_key_value($key, $value): KeyValue
|
||||
{
|
||||
return new KeyValue([
|
||||
'key' => $key,
|
||||
'value' => $this->as_otlp_any_value($value),
|
||||
]);
|
||||
}
|
||||
|
||||
public function as_otlp_any_value($value): AnyValue
|
||||
{
|
||||
$result = new AnyValue();
|
||||
|
||||
switch (true) {
|
||||
case is_array($value):
|
||||
|
||||
$values = [];
|
||||
foreach ($value as $element) {
|
||||
$this->as_otlp_any_value($element);
|
||||
}
|
||||
|
||||
$result->setArrayValue(new ArrayValue($values));
|
||||
|
||||
break;
|
||||
case is_int($value):
|
||||
$result->setIntValue($value);
|
||||
|
||||
break;
|
||||
case is_bool($value):
|
||||
$result->setBoolValue($value);
|
||||
|
||||
break;
|
||||
case is_double($value):
|
||||
$result->setDoubleValue($value);
|
||||
|
||||
break;
|
||||
case is_string($value):
|
||||
$result->setStringValue($value);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function as_otlp_span_kind($kind): int
|
||||
{
|
||||
switch ($kind) {
|
||||
case 0: return SpanKind::SPAN_KIND_INTERNAL;
|
||||
case 1: return SpanKind::SPAN_KIND_CLIENT;
|
||||
case 2: return SpanKind::SPAN_KIND_SERVER;
|
||||
case 3: return SpanKind::SPAN_KIND_PRODUCER;
|
||||
case 4: return SpanKind::SPAN_KIND_CONSUMER;
|
||||
}
|
||||
|
||||
return SpanKind::SPAN_KIND_UNSPECIFIED;
|
||||
}
|
||||
|
||||
public function as_otlp_span(ReadableSpan $span): CollectorSpan
|
||||
{
|
||||
$end_timestamp = ($span->getStartEpochTimestamp() + $span->getDuration());
|
||||
|
||||
$parent_span = $span->getParent();
|
||||
$parent_span_id = $parent_span ? $parent_span->getSpanId() : false;
|
||||
|
||||
$row = [
|
||||
'trace_id' => hex2bin($span->getContext()->getTraceId()),
|
||||
'span_id' => hex2bin($span->getContext()->getSpanId()),
|
||||
'parent_span_id' => $parent_span_id ? hex2bin($parent_span_id) : null,
|
||||
'name' => $span->getSpanName(),
|
||||
'start_time_unix_nano' => $span->getStartEpochTimestamp(),
|
||||
'end_time_unix_nano' => $end_timestamp,
|
||||
'kind' => $this->as_otlp_span_kind($span->getSpanKind()),
|
||||
// 'trace_state' => $span->getContext()
|
||||
// 'links' =>
|
||||
];
|
||||
|
||||
foreach ($span->getEvents() as $event) {
|
||||
if (!array_key_exists('events', $row)) {
|
||||
$row['events'] = [];
|
||||
}
|
||||
$attrs = [];
|
||||
|
||||
foreach ($event->getAttributes() as $k => $v) {
|
||||
array_push($attrs, $this->as_otlp_key_value($k, $v->getValue()));
|
||||
}
|
||||
|
||||
$row['events'][] = new Event([
|
||||
'time_unix_nano' => $event->getTimestamp(),
|
||||
'name' => $event->getName(),
|
||||
'attributes' => $attrs,
|
||||
]);
|
||||
}
|
||||
|
||||
foreach ($span->getAttributes() as $k => $v) {
|
||||
if (!array_key_exists('attributes', $row)) {
|
||||
$row['attributes'] = [];
|
||||
}
|
||||
array_push($row['attributes'], $this->as_otlp_key_value($k, $v->getValue()));
|
||||
}
|
||||
|
||||
$status = new Status();
|
||||
|
||||
switch ($span->getStatus()->getCanonicalStatusCode()) {
|
||||
case SpanStatus::OK:
|
||||
$status->setCode(StatusCode::STATUS_CODE_OK);
|
||||
|
||||
break;
|
||||
case SpanStatus::ERROR:
|
||||
$status->setCode(StatusCode::STATUS_CODE_ERROR)->setMessage($span->getStatus()->getStatusDescription());
|
||||
|
||||
break;
|
||||
default:
|
||||
$status->setCode(StatusCode::STATUS_CODE_UNSET);
|
||||
}
|
||||
|
||||
$row['status'] = $status;
|
||||
|
||||
return new CollectorSpan($row);
|
||||
}
|
||||
|
||||
// @return KeyValue[]
|
||||
public function as_otlp_resource_attributes(iterable $spans): array
|
||||
{
|
||||
$attrs = [];
|
||||
foreach ($spans as $span) {
|
||||
foreach ($span->getResource()->getAttributes() as $k => $v) {
|
||||
array_push($attrs, $this->as_otlp_key_value($k, $v->getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
return $attrs;
|
||||
}
|
||||
|
||||
public function as_otlp_resource_span(iterable $spans): ResourceSpans
|
||||
{
|
||||
// TODO: Should return an empty ResourceSpans when $spans is empty
|
||||
// At the minute it returns an semi populated ResourceSpan
|
||||
|
||||
$ils = $convertedSpans = [];
|
||||
foreach ($spans as $span) {
|
||||
/** @var \OpenTelemetry\Sdk\InstrumentationLibrary $il */
|
||||
$il = $span->getInstrumentationLibrary();
|
||||
$ilKey = sprintf('%s@%s', $il->getName(), $il->getVersion()??'');
|
||||
if (!isset($ils[$ilKey])) {
|
||||
$convertedSpans[$ilKey] = [];
|
||||
$ils[$ilKey] = new InstrumentationLibrary(['name' => $il->getName(), 'version' => $il->getVersion()??'']);
|
||||
}
|
||||
$convertedSpans[$ilKey][] = $this->as_otlp_span($span);
|
||||
}
|
||||
|
||||
$ilSpans = [];
|
||||
foreach ($ils as $ilKey => $il) {
|
||||
$ilSpans[] = new InstrumentationLibrarySpans([
|
||||
'instrumentation_library' => $il,
|
||||
'spans' => $convertedSpans[$ilKey],
|
||||
]);
|
||||
}
|
||||
|
||||
return new Proto\Trace\V1\ResourceSpans([
|
||||
'resource' => new Proto\Resource\V1\Resource([
|
||||
'attributes' => $this->as_otlp_resource_attributes($spans),
|
||||
]),
|
||||
'instrumentation_library_spans' => $ilSpans,
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,227 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Contrib\OtlpHttp;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Psr7\HttpFactory;
|
||||
use InvalidArgumentException;
|
||||
use Opentelemetry\Proto\Collector\Trace\V1\ExportTraceServiceRequest;
|
||||
use OpenTelemetry\Sdk\Trace;
|
||||
use Psr\Http\Client\ClientExceptionInterface;
|
||||
use Psr\Http\Client\ClientInterface;
|
||||
use Psr\Http\Client\NetworkExceptionInterface;
|
||||
use Psr\Http\Client\RequestExceptionInterface;
|
||||
use Psr\Http\Message\RequestFactoryInterface;
|
||||
use Psr\Http\Message\StreamFactoryInterface;
|
||||
|
||||
class Exporter implements Trace\Exporter
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $endpointUrl;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $protocol;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $insecure;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $certificateFile;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $headers;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $compression;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $timeout;
|
||||
/**
|
||||
* @var SpanConverter
|
||||
*/
|
||||
private $spanConverter;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $running = true;
|
||||
|
||||
/**
|
||||
* @var ClientInterface
|
||||
*/
|
||||
private $client;
|
||||
|
||||
/**
|
||||
* @var RequestFactoryInterface
|
||||
*/
|
||||
private $requestFactory;
|
||||
|
||||
/**
|
||||
* @var StreamFactoryInterface
|
||||
*/
|
||||
private $streamFactory;
|
||||
|
||||
/**
|
||||
* Exporter constructor.
|
||||
*/
|
||||
public function __construct(
|
||||
ClientInterface $client,
|
||||
RequestFactoryInterface $requestFactory,
|
||||
StreamFactoryInterface $streamFactory,
|
||||
SpanConverter $spanConverter = null
|
||||
) {
|
||||
|
||||
// Set default values based on presence of env variable
|
||||
$this->endpointUrl = getenv('OTEL_EXPORTER_OTLP_ENDPOINT') ?: 'https://localhost:55681/v1/traces';
|
||||
$this->protocol = getenv('OTEL_EXPORTER_OTLP_PROTOCOL') ?: 'http/protobuf';
|
||||
$this->certificateFile = getenv('OTEL_EXPORTER_OTLP_CERTIFICATE') ?: 'none';
|
||||
$this->headers = $this->processHeaders(getenv('OTEL_EXPORTER_OTLP_HEADERS'));
|
||||
$this->compression = getenv('OTEL_EXPORTER_OTLP_COMPRESSION') ?: 'none';
|
||||
$this->timeout =(int) getenv('OTEL_EXPORTER_OTLP_TIMEOUT') ?: 10;
|
||||
|
||||
$this->client = $client;
|
||||
$this->requestFactory = $requestFactory;
|
||||
$this->streamFactory = $streamFactory;
|
||||
$this->spanConverter = $spanConverter ?? new SpanConverter();
|
||||
|
||||
if ($this->protocol != 'http/protobuf') {
|
||||
throw new InvalidArgumentException('Invalid OTLP Protocol Specified');
|
||||
}
|
||||
|
||||
$parsedDsn = parse_url($this->endpointUrl);
|
||||
|
||||
if (!is_array($parsedDsn)) {
|
||||
throw new InvalidArgumentException('Unable to parse provided DSN');
|
||||
}
|
||||
|
||||
if (
|
||||
!isset($parsedDsn['scheme'])
|
||||
|| !isset($parsedDsn['host'])
|
||||
|| !isset($parsedDsn['port'])
|
||||
|| !isset($parsedDsn['path'])
|
||||
) {
|
||||
throw new InvalidArgumentException('Endpoint should have scheme, host, port and path');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports the provided Span data via the OTLP protocol
|
||||
*
|
||||
* @param iterable<Trace\ReadableSpan> $spans Array of Spans
|
||||
* @return int return code, defined on the Exporter interface
|
||||
*/
|
||||
public function export(iterable $spans): int
|
||||
{
|
||||
if (!$this->running) {
|
||||
return Exporter::FAILED_NOT_RETRYABLE;
|
||||
}
|
||||
|
||||
if (empty($spans)) {
|
||||
return Trace\Exporter::SUCCESS;
|
||||
}
|
||||
|
||||
$resourcespans = [$this->spanConverter->as_otlp_resource_span($spans)];
|
||||
|
||||
$exportrequest = new ExportTraceServiceRequest([
|
||||
'resource_spans' => $resourcespans,
|
||||
]);
|
||||
|
||||
$bytes = $exportrequest->serializeToString();
|
||||
|
||||
try {
|
||||
$request = $this->requestFactory
|
||||
->createRequest('POST', $this->endpointUrl)
|
||||
->withHeader('content-type', 'application/x-protobuf');
|
||||
|
||||
foreach ($this->headers as $header => $value) {
|
||||
$request = $request->withHeader($header, $value);
|
||||
}
|
||||
|
||||
if ($this->compression === 'gzip') {
|
||||
// TODO: Add Tests
|
||||
$body = $this->streamFactory->createStream(gzencode($bytes));
|
||||
$request = $request->withHeader('Content-Encoding', 'gzip');
|
||||
} else {
|
||||
$body = $this->streamFactory->createStream($bytes);
|
||||
}
|
||||
|
||||
$request = $request->withBody($body);
|
||||
|
||||
$response = $this->client->sendRequest($request);
|
||||
} catch (RequestExceptionInterface $e) {
|
||||
return Trace\Exporter::FAILED_NOT_RETRYABLE;
|
||||
} catch (NetworkExceptionInterface | ClientExceptionInterface $e) {
|
||||
return Trace\Exporter::FAILED_RETRYABLE;
|
||||
}
|
||||
|
||||
if ($response->getStatusCode() >= 400 && $response->getStatusCode() < 500) {
|
||||
return Trace\Exporter::FAILED_NOT_RETRYABLE;
|
||||
}
|
||||
|
||||
if ($response->getStatusCode() >= 500 && $response->getStatusCode() < 600) {
|
||||
return Trace\Exporter::FAILED_RETRYABLE;
|
||||
}
|
||||
|
||||
return Trace\Exporter::SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* processHeaders converts comma separated headers into an array
|
||||
*/
|
||||
public function processHeaders($headers): array
|
||||
{
|
||||
if (empty($headers)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$pairs = explode(',', $headers);
|
||||
|
||||
$metadata = [];
|
||||
foreach ($pairs as $pair) {
|
||||
$kv = explode('=', $pair, 2);
|
||||
|
||||
if (count($kv) !== 2) {
|
||||
throw new InvalidArgumentException('Invalid headers passed');
|
||||
}
|
||||
|
||||
list($key, $value) = $kv;
|
||||
|
||||
$metadata[$key] = $value;
|
||||
}
|
||||
|
||||
return $metadata;
|
||||
}
|
||||
|
||||
public function shutdown(): void
|
||||
{
|
||||
$this->running = false;
|
||||
}
|
||||
|
||||
public static function fromConnectionString(string $endpointUrl = null, string $name = null, $args = null)
|
||||
{
|
||||
$factory = new HttpFactory();
|
||||
$exporter = new Exporter(
|
||||
new Client(),
|
||||
$factory,
|
||||
$factory
|
||||
);
|
||||
|
||||
return $exporter;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,187 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Contrib\OtlpHttp;
|
||||
|
||||
use Opentelemetry\Proto;
|
||||
use Opentelemetry\Proto\Common\V1\AnyValue;
|
||||
use Opentelemetry\Proto\Common\V1\ArrayValue;
|
||||
use Opentelemetry\Proto\Common\V1\InstrumentationLibrary;
|
||||
use Opentelemetry\Proto\Common\V1\KeyValue;
|
||||
use Opentelemetry\Proto\Trace\V1\InstrumentationLibrarySpans;
|
||||
use Opentelemetry\Proto\Trace\V1\ResourceSpans;
|
||||
use Opentelemetry\Proto\Trace\V1\Span as CollectorSpan;
|
||||
use Opentelemetry\Proto\Trace\V1\Span\Event;
|
||||
use Opentelemetry\Proto\Trace\V1\Span\SpanKind;
|
||||
use Opentelemetry\Proto\Trace\V1\Status;
|
||||
use Opentelemetry\Proto\Trace\V1\Status\StatusCode;
|
||||
use OpenTelemetry\Sdk\Trace\ReadableSpan;
|
||||
use OpenTelemetry\Sdk\Trace\SpanStatus;
|
||||
|
||||
class SpanConverter
|
||||
{
|
||||
public function as_otlp_key_value($key, $value): KeyValue
|
||||
{
|
||||
return new KeyValue([
|
||||
'key' => $key,
|
||||
'value' => $this->as_otlp_any_value($value),
|
||||
]);
|
||||
}
|
||||
|
||||
public function as_otlp_any_value($value): AnyValue
|
||||
{
|
||||
$result = new AnyValue();
|
||||
|
||||
switch (true) {
|
||||
case is_array($value):
|
||||
$values = [];
|
||||
foreach ($value as $element) {
|
||||
array_push($values, $this->as_otlp_any_value($element));
|
||||
}
|
||||
$result->setArrayValue(new ArrayValue(['values' => $values]));
|
||||
|
||||
break;
|
||||
case is_int($value):
|
||||
$result->setIntValue($value);
|
||||
|
||||
break;
|
||||
case is_bool($value):
|
||||
$result->setBoolValue($value);
|
||||
|
||||
break;
|
||||
case is_double($value):
|
||||
$result->setDoubleValue($value);
|
||||
|
||||
break;
|
||||
case is_string($value):
|
||||
$result->setStringValue($value);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function as_otlp_span_kind($kind): int
|
||||
{
|
||||
switch ($kind) {
|
||||
case 0: return SpanKind::SPAN_KIND_INTERNAL;
|
||||
case 1: return SpanKind::SPAN_KIND_CLIENT;
|
||||
case 2: return SpanKind::SPAN_KIND_SERVER;
|
||||
case 3: return SpanKind::SPAN_KIND_PRODUCER;
|
||||
case 4: return SpanKind::SPAN_KIND_CONSUMER;
|
||||
}
|
||||
|
||||
return SpanKind::SPAN_KIND_UNSPECIFIED;
|
||||
}
|
||||
|
||||
public function as_otlp_span(ReadableSpan $span): CollectorSpan
|
||||
{
|
||||
$end_timestamp = ($span->getStartEpochTimestamp() + $span->getDuration());
|
||||
|
||||
$parent_span = $span->getParent();
|
||||
$parent_span_id = $parent_span ? $parent_span->getSpanId() : false;
|
||||
|
||||
$row = [
|
||||
'trace_id' => hex2bin($span->getContext()->getTraceId()),
|
||||
'span_id' => hex2bin($span->getContext()->getSpanId()),
|
||||
'parent_span_id' => $parent_span_id ? hex2bin($parent_span_id) : null,
|
||||
'name' => $span->getSpanName(),
|
||||
'start_time_unix_nano' => $span->getStartEpochTimestamp(),
|
||||
'end_time_unix_nano' => $end_timestamp,
|
||||
'kind' => $this->as_otlp_span_kind($span->getSpanKind()),
|
||||
// 'trace_state' => $span->getContext()
|
||||
// 'links' =>
|
||||
];
|
||||
|
||||
foreach ($span->getEvents() as $event) {
|
||||
if (!array_key_exists('events', $row)) {
|
||||
$row['events'] = [];
|
||||
}
|
||||
$attrs = [];
|
||||
|
||||
foreach ($event->getAttributes() as $k => $v) {
|
||||
array_push($attrs, $this->as_otlp_key_value($k, $v->getValue()));
|
||||
}
|
||||
|
||||
$row['events'][] = new Event([
|
||||
'time_unix_nano' => $event->getTimestamp(),
|
||||
'name' => $event->getName(),
|
||||
'attributes' => $attrs,
|
||||
]);
|
||||
}
|
||||
|
||||
foreach ($span->getAttributes() as $k => $v) {
|
||||
if (!array_key_exists('attributes', $row)) {
|
||||
$row['attributes'] = [];
|
||||
}
|
||||
array_push($row['attributes'], $this->as_otlp_key_value($k, $v->getValue()));
|
||||
}
|
||||
|
||||
$status = new Status();
|
||||
|
||||
switch ($span->getStatus()->getCanonicalStatusCode()) {
|
||||
case SpanStatus::OK:
|
||||
$status->setCode(StatusCode::STATUS_CODE_OK);
|
||||
|
||||
break;
|
||||
case SpanStatus::ERROR:
|
||||
$status->setCode(StatusCode::STATUS_CODE_ERROR)->setMessage($span->getStatus()->getStatusDescription());
|
||||
|
||||
break;
|
||||
default:
|
||||
$status->setCode(StatusCode::STATUS_CODE_UNSET);
|
||||
}
|
||||
|
||||
$row['status'] = $status;
|
||||
|
||||
return new CollectorSpan($row);
|
||||
}
|
||||
|
||||
// @return KeyValue[]
|
||||
public function as_otlp_resource_attributes(iterable $spans): array
|
||||
{
|
||||
$attrs = [];
|
||||
foreach ($spans as $span) {
|
||||
foreach ($span->getResource()->getAttributes() as $k => $v) {
|
||||
array_push($attrs, $this->as_otlp_key_value($k, $v->getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
return $attrs;
|
||||
}
|
||||
|
||||
public function as_otlp_resource_span(iterable $spans): ResourceSpans
|
||||
{
|
||||
// TODO: Should return an empty ResourceSpans when $spans is empty
|
||||
// At the minute it returns an semi populated ResourceSpan
|
||||
|
||||
$ils = $convertedSpans = [];
|
||||
foreach ($spans as $span) {
|
||||
/** @var \OpenTelemetry\Sdk\InstrumentationLibrary $il */
|
||||
$il = $span->getInstrumentationLibrary();
|
||||
$ilKey = sprintf('%s@%s', $il->getName(), $il->getVersion()??'');
|
||||
if (!isset($ils[$ilKey])) {
|
||||
$convertedSpans[$ilKey] = [];
|
||||
$ils[$ilKey] = new InstrumentationLibrary(['name' => $il->getName(), 'version' => $il->getVersion()??'']);
|
||||
}
|
||||
$convertedSpans[$ilKey][] = $this->as_otlp_span($span);
|
||||
}
|
||||
|
||||
$ilSpans = [];
|
||||
foreach ($ils as $ilKey => $il) {
|
||||
$ilSpans[] = new InstrumentationLibrarySpans([
|
||||
'instrumentation_library' => $il,
|
||||
'spans' => $convertedSpans[$ilKey],
|
||||
]);
|
||||
}
|
||||
|
||||
return new Proto\Trace\V1\ResourceSpans([
|
||||
'resource' => new Proto\Resource\V1\Resource([
|
||||
'attributes' => $this->as_otlp_resource_attributes($spans),
|
||||
]),
|
||||
'instrumentation_library_spans' => $ilSpans,
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Contrib\Zipkin;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Psr7\HttpFactory;
|
||||
use InvalidArgumentException;
|
||||
use OpenTelemetry\Sdk\Trace;
|
||||
use Psr\Http\Client\ClientExceptionInterface;
|
||||
use Psr\Http\Client\ClientInterface;
|
||||
use Psr\Http\Client\NetworkExceptionInterface;
|
||||
use Psr\Http\Client\RequestExceptionInterface;
|
||||
use Psr\Http\Message\RequestFactoryInterface;
|
||||
use Psr\Http\Message\StreamFactoryInterface;
|
||||
|
||||
/**
|
||||
* Class ZipkinExporter - implements the export interface for data transfer via Zipkin protocol
|
||||
* @package OpenTelemetry\Exporter
|
||||
*/
|
||||
class Exporter implements Trace\Exporter
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $endpointUrl;
|
||||
|
||||
/**
|
||||
* @var SpanConverter
|
||||
*/
|
||||
private $spanConverter;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $running = true;
|
||||
|
||||
/**
|
||||
* @var ClientInterface
|
||||
*/
|
||||
private $client;
|
||||
|
||||
private $requestFactory;
|
||||
|
||||
private $streamFactory;
|
||||
|
||||
public function __construct(
|
||||
$name,
|
||||
string $endpointUrl,
|
||||
ClientInterface $client,
|
||||
RequestFactoryInterface $requestFactory,
|
||||
StreamFactoryInterface $streamFactory,
|
||||
SpanConverter $spanConverter = null
|
||||
) {
|
||||
$parsedDsn = parse_url($endpointUrl);
|
||||
|
||||
if (!is_array($parsedDsn)) {
|
||||
throw new InvalidArgumentException('Unable to parse provided DSN');
|
||||
}
|
||||
|
||||
if (
|
||||
!isset($parsedDsn['scheme'])
|
||||
|| !isset($parsedDsn['host'])
|
||||
|| !isset($parsedDsn['port'])
|
||||
|| !isset($parsedDsn['path'])
|
||||
) {
|
||||
throw new InvalidArgumentException('Endpoint should have scheme, host, port and path');
|
||||
}
|
||||
|
||||
$this->endpointUrl = $endpointUrl;
|
||||
$this->client = $client;
|
||||
$this->requestFactory = $requestFactory;
|
||||
$this->streamFactory = $streamFactory;
|
||||
$this->spanConverter = $spanConverter ?? new SpanConverter($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports the provided Span data via the Zipkin protocol
|
||||
*
|
||||
* @param iterable<Trace\ReadableSpan> $spans Array of Spans
|
||||
* @return int return code, defined on the Exporter interface
|
||||
*/
|
||||
public function export(iterable $spans): int
|
||||
{
|
||||
if (!$this->running) {
|
||||
return Exporter::FAILED_NOT_RETRYABLE;
|
||||
}
|
||||
|
||||
if (empty($spans)) {
|
||||
return Trace\Exporter::SUCCESS;
|
||||
}
|
||||
|
||||
$convertedSpans = [];
|
||||
foreach ($spans as $span) {
|
||||
array_push($convertedSpans, $this->spanConverter->convert($span));
|
||||
}
|
||||
|
||||
try {
|
||||
$body = $this->streamFactory->createStream(json_encode($convertedSpans));
|
||||
$request = $this->requestFactory
|
||||
->createRequest('POST', $this->endpointUrl)
|
||||
->withBody($body)
|
||||
->withHeader('content-type', 'application/json');
|
||||
|
||||
$response = $this->client->sendRequest($request);
|
||||
} catch (RequestExceptionInterface $e) {
|
||||
return Trace\Exporter::FAILED_NOT_RETRYABLE;
|
||||
} catch (NetworkExceptionInterface | ClientExceptionInterface $e) {
|
||||
return Trace\Exporter::FAILED_RETRYABLE;
|
||||
}
|
||||
|
||||
if ($response->getStatusCode() >= 400 && $response->getStatusCode() < 500) {
|
||||
return Trace\Exporter::FAILED_NOT_RETRYABLE;
|
||||
}
|
||||
|
||||
if ($response->getStatusCode() >= 500 && $response->getStatusCode() < 600) {
|
||||
return Trace\Exporter::FAILED_RETRYABLE;
|
||||
}
|
||||
|
||||
return Trace\Exporter::SUCCESS;
|
||||
}
|
||||
|
||||
public function shutdown(): void
|
||||
{
|
||||
$this->running = false;
|
||||
}
|
||||
public static function fromConnectionString(string $endpointUrl, string $name, $args = null)
|
||||
{
|
||||
$factory = new HttpFactory();
|
||||
$exporter = new Exporter(
|
||||
$name,
|
||||
$endpointUrl,
|
||||
new Client(),
|
||||
$factory,
|
||||
$factory
|
||||
);
|
||||
|
||||
return $exporter;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Contrib\Zipkin;
|
||||
|
||||
use OpenTelemetry\Sdk\Trace\ReadableSpan;
|
||||
|
||||
class SpanConverter
|
||||
{
|
||||
const STATUS_CODE_TAG_KEY = 'op.status_code';
|
||||
const STATUS_DESCRIPTION_TAG_KEY = 'op.status_description';
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $serviceName;
|
||||
|
||||
public function __construct(string $serviceName)
|
||||
{
|
||||
$this->serviceName = $serviceName;
|
||||
}
|
||||
|
||||
private function sanitiseTagValue($value)
|
||||
{
|
||||
// Casting false to string makes an empty string
|
||||
if (is_bool($value)) {
|
||||
return $value ? 'true' : 'false';
|
||||
}
|
||||
|
||||
// Zipkin tags must be strings, but opentelemetry
|
||||
// accepts strings, booleans, numbers, and lists of each.
|
||||
if (is_array($value)) {
|
||||
return join(',', array_map([$this, 'sanitiseTagValue'], $value));
|
||||
}
|
||||
|
||||
// Floats will lose precision if their string representation
|
||||
// is >=14 or >=17 digits, depending on PHP settings.
|
||||
// Can also throw E_RECOVERABLE_ERROR if $value is an object
|
||||
// without a __toString() method.
|
||||
// This is possible because OpenTelemetry\Trace\Span does not verify
|
||||
// setAttribute() $value input.
|
||||
return (string) $value;
|
||||
}
|
||||
|
||||
public function convert(ReadableSpan $span)
|
||||
{
|
||||
$spanParent = $span->getParent();
|
||||
$row = [
|
||||
'id' => $span->getContext()->getSpanId(),
|
||||
'traceId' => $span->getContext()->getTraceId(),
|
||||
'parentId' => $spanParent ? $spanParent->getSpanId() : null,
|
||||
'localEndpoint' => [
|
||||
'serviceName' => $this->serviceName,
|
||||
],
|
||||
'name' => $span->getSpanName(),
|
||||
'timestamp' => (int) ($span->getStartEpochTimestamp() / 1e3), // RealtimeClock in microseconds
|
||||
'duration' => (int) (($span->getEnd() - $span->getStart()) / 1e3), // Diff in microseconds
|
||||
'tags' => [
|
||||
self::STATUS_CODE_TAG_KEY => $span->getStatus()->getCanonicalStatusCode(),
|
||||
self::STATUS_DESCRIPTION_TAG_KEY => $span->getStatus()->getStatusDescription(),
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($span->getAttributes() as $k => $v) {
|
||||
$row['tags'][$k] = $this->sanitiseTagValue($v->getValue());
|
||||
}
|
||||
|
||||
foreach ($span->getEvents() as $event) {
|
||||
if (!array_key_exists('annotations', $row)) {
|
||||
$row['annotations'] = [];
|
||||
}
|
||||
$row['annotations'][] = [
|
||||
'timestamp' => (int) ($event->getTimestamp() / 1e3), // RealtimeClock in microseconds
|
||||
'value' => $event->getName(),
|
||||
];
|
||||
}
|
||||
|
||||
return $row;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,171 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Contrib\ZipkinToNewrelic;
|
||||
|
||||
use Exception;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Psr7\HttpFactory;
|
||||
use InvalidArgumentException;
|
||||
use OpenTelemetry\Sdk\Trace;
|
||||
use Psr\Http\Client\ClientExceptionInterface;
|
||||
use Psr\Http\Client\ClientInterface;
|
||||
use Psr\Http\Client\NetworkExceptionInterface;
|
||||
use Psr\Http\Client\RequestExceptionInterface;
|
||||
use Psr\Http\Message\RequestFactoryInterface;
|
||||
use Psr\Http\Message\StreamFactoryInterface;
|
||||
|
||||
/**
|
||||
* Class ZipkinExporter - implements the export interface for data transfer via Zipkin protocol
|
||||
* @package OpenTelemetry\Exporter
|
||||
*
|
||||
* This is an experimental, non-supported exporter.
|
||||
* It will send PHP Otel trace data end to end across the internet to a functional backend.
|
||||
* Needs a license key to connect. For a free account/key, go to: https://newrelic.com/signup/
|
||||
*/
|
||||
class Exporter implements Trace\Exporter
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $endpointUrl;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $licenseKey;
|
||||
|
||||
/**
|
||||
* @var SpanConverter
|
||||
*/
|
||||
private $spanConverter;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $running = true;
|
||||
|
||||
/**
|
||||
* @var ClientInterface
|
||||
*/
|
||||
private $client;
|
||||
|
||||
/**
|
||||
* @var RequestFactoryInterface
|
||||
*/
|
||||
private $requestFactory;
|
||||
|
||||
/**
|
||||
* @var StreamFactoryInterface
|
||||
*/
|
||||
private $streamFactory;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $name;
|
||||
|
||||
public function __construct(
|
||||
$name,
|
||||
string $endpointUrl,
|
||||
string $licenseKey,
|
||||
ClientInterface $client,
|
||||
RequestFactoryInterface $requestFactory,
|
||||
StreamFactoryInterface $streamFactory,
|
||||
SpanConverter $spanConverter = null
|
||||
) {
|
||||
$parsedDsn = parse_url($endpointUrl);
|
||||
|
||||
if (!is_array($parsedDsn)) {
|
||||
throw new InvalidArgumentException('Unable to parse provided DSN');
|
||||
}
|
||||
if (
|
||||
!isset($parsedDsn['scheme'])
|
||||
|| !isset($parsedDsn['host'])
|
||||
|| !isset($parsedDsn['path'])
|
||||
) {
|
||||
throw new InvalidArgumentException('Endpoint should have scheme, host, port and path');
|
||||
}
|
||||
|
||||
$this->name = $name;
|
||||
$this->endpointUrl = $endpointUrl;
|
||||
$this->licenseKey = $licenseKey;
|
||||
$this->client = $client;
|
||||
$this->requestFactory = $requestFactory;
|
||||
$this->streamFactory = $streamFactory;
|
||||
$this->spanConverter = $spanConverter ?? new SpanConverter($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports the provided Span data via the Zipkin protocol
|
||||
*
|
||||
* @param iterable<Trace\ReadableSpan> $spans Array of Spans
|
||||
* @return int return code, defined on the Exporter interface
|
||||
*/
|
||||
public function export(iterable $spans): int
|
||||
{
|
||||
if (!$this->running) {
|
||||
return Exporter::FAILED_NOT_RETRYABLE;
|
||||
}
|
||||
|
||||
if (empty($spans)) {
|
||||
return Trace\Exporter::SUCCESS;
|
||||
}
|
||||
|
||||
$convertedSpans = [];
|
||||
foreach ($spans as $span) {
|
||||
array_push($convertedSpans, $this->spanConverter->convert($span));
|
||||
}
|
||||
|
||||
try {
|
||||
$body = $this->streamFactory->createStream(json_encode($convertedSpans));
|
||||
$request = $this->requestFactory
|
||||
->createRequest('POST', $this->endpointUrl)
|
||||
->withBody($body)
|
||||
->withHeader('content-type', 'application/json')
|
||||
->withAddedHeader('Api-Key', $this->licenseKey)
|
||||
->withAddedHeader('Data-Format', 'zipkin')
|
||||
->withAddedHeader('Data-Format-Version', '2');
|
||||
|
||||
$response = $this->client->sendRequest($request);
|
||||
} catch (RequestExceptionInterface $e) {
|
||||
return Trace\Exporter::FAILED_NOT_RETRYABLE;
|
||||
} catch (NetworkExceptionInterface | ClientExceptionInterface $e) {
|
||||
return Trace\Exporter::FAILED_RETRYABLE;
|
||||
}
|
||||
|
||||
if ($response->getStatusCode() >= 400 && $response->getStatusCode() < 500) {
|
||||
return Trace\Exporter::FAILED_NOT_RETRYABLE;
|
||||
}
|
||||
|
||||
if ($response->getStatusCode() >= 500 && $response->getStatusCode() < 600) {
|
||||
return Trace\Exporter::FAILED_RETRYABLE;
|
||||
}
|
||||
|
||||
return Trace\Exporter::SUCCESS;
|
||||
}
|
||||
|
||||
public function shutdown(): void
|
||||
{
|
||||
$this->running = false;
|
||||
}
|
||||
|
||||
public static function fromConnectionString(string $endpointUrl, string $name, $args)
|
||||
{
|
||||
if ($args == false) {
|
||||
throw new Exception('Invalid license key.');
|
||||
}
|
||||
$factory = new HttpFactory();
|
||||
$exporter = new Exporter(
|
||||
$name,
|
||||
$endpointUrl,
|
||||
$args,
|
||||
new Client(),
|
||||
$factory,
|
||||
$factory
|
||||
);
|
||||
|
||||
return $exporter;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Contrib\ZipkinToNewrelic;
|
||||
|
||||
use OpenTelemetry\Sdk\Trace\ReadableSpan;
|
||||
|
||||
class SpanConverter
|
||||
{
|
||||
const STATUS_CODE_TAG_KEY = 'otel.status_code';
|
||||
const STATUS_DESCRIPTION_TAG_KEY = 'otel.status_description';
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $serviceName;
|
||||
|
||||
public function __construct(string $serviceName)
|
||||
{
|
||||
$this->serviceName = $serviceName;
|
||||
}
|
||||
|
||||
private function sanitiseTagValue($value)
|
||||
{
|
||||
// Casting false to string makes an empty string
|
||||
if (is_bool($value)) {
|
||||
return $value ? 'true' : 'false';
|
||||
}
|
||||
|
||||
// Zipkin tags must be strings, but opentelemetry
|
||||
// accepts strings, booleans, numbers, and lists of each.
|
||||
if (is_array($value)) {
|
||||
return join(',', array_map([$this, 'sanitiseTagValue'], $value));
|
||||
}
|
||||
|
||||
// Floats will lose precision if their string representation
|
||||
// is >=14 or >=17 digits, depending on PHP settings.
|
||||
// Can also throw E_RECOVERABLE_ERROR if $value is an object
|
||||
// without a __toString() method.
|
||||
// This is possible because OpenTelemetry\Trace\Span does not verify
|
||||
// setAttribute() $value input.
|
||||
return (string) $value;
|
||||
}
|
||||
|
||||
public function convert(ReadableSpan $span)
|
||||
{
|
||||
$spanParent = $span->getParent();
|
||||
$row = [
|
||||
'id' => $span->getContext()->getSpanId(),
|
||||
'traceId' => $span->getContext()->getTraceId(),
|
||||
'parentId' => $spanParent ? $spanParent->getSpanId() : null,
|
||||
'localEndpoint' => [
|
||||
'serviceName' => $this->serviceName,
|
||||
],
|
||||
'name' => $span->getSpanName(),
|
||||
'timestamp' => (int) ($span->getStartEpochTimestamp() / 1e3), // RealtimeClock in microseconds
|
||||
'duration' => (int) (($span->getEnd() - $span->getStart()) / 1e3), // Diff in microseconds
|
||||
'tags' => [
|
||||
self::STATUS_CODE_TAG_KEY => $span->getStatus()->getCanonicalStatusCode(),
|
||||
self::STATUS_DESCRIPTION_TAG_KEY => $span->getStatus()->getStatusDescription(),
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($span->getAttributes() as $k => $v) {
|
||||
$row['tags'][$k] = $this->sanitiseTagValue($v->getValue());
|
||||
}
|
||||
|
||||
foreach ($span->getEvents() as $event) {
|
||||
if (!array_key_exists('annotations', $row)) {
|
||||
$row['annotations'] = [];
|
||||
}
|
||||
$row['annotations'][] = [
|
||||
'timestamp' => (int) ($event->getTimestamp() / 1e3), // RealtimeClock in microseconds
|
||||
'value' => $event->getName(),
|
||||
];
|
||||
}
|
||||
|
||||
return $row;
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
deptrac:
|
||||
skip_violations:
|
||||
/src/Extension/Propagator/B3/_register.php:
|
||||
- OpenTelemetry\SDK\Registry
|
||||
/src/Extension/Propagator/CloudTrace/_register.php:
|
||||
- OpenTelemetry\SDK\Registry
|
||||
/src/Extension/Propagator/Jaeger/_register.php:
|
||||
- OpenTelemetry\SDK\Registry
|
||||
OpenTelemetry\API\Configuration\Config\ComponentProvider:
|
||||
- Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition
|
||||
- Symfony\Component\Config\Definition\Builder\NodeBuilder
|
||||
OpenTelemetry\API\Configuration\Config\ComponentProviderRegistry:
|
||||
- Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition
|
||||
- Symfony\Component\Config\Definition\Builder\NodeDefinition
|
168
deptrac.yaml
168
deptrac.yaml
|
@ -1,168 +0,0 @@
|
|||
|
||||
imports:
|
||||
- deptrac.baseline.yaml
|
||||
deptrac:
|
||||
analyser:
|
||||
types:
|
||||
- class
|
||||
- class_superglobal
|
||||
- use
|
||||
- file
|
||||
- function
|
||||
- function_superglobal
|
||||
paths:
|
||||
- ./src
|
||||
- ./proto
|
||||
- ./tests
|
||||
- ./deptrac/polyfills
|
||||
- ./vendor/symfony/polyfill-php83/Resources/stubs
|
||||
exclude_files:
|
||||
- '#.*test.*#'
|
||||
layers:
|
||||
- name: API
|
||||
collectors:
|
||||
- type: directory
|
||||
value: src/API/.*
|
||||
- name: SDK
|
||||
collectors:
|
||||
- type: directory
|
||||
value: src/SDK/.*
|
||||
- name: ConfigSDK
|
||||
collectors:
|
||||
- type: directory
|
||||
value: src/Config/SDK/.*
|
||||
- name: Context
|
||||
collectors:
|
||||
- type: directory
|
||||
value: src/Context/.*
|
||||
- name: SemConv
|
||||
collectors:
|
||||
- type: directory
|
||||
value: src/SemConv/.*
|
||||
- name: Contrib
|
||||
collectors:
|
||||
- type: directory
|
||||
value: src/Contrib/.*
|
||||
- name: Extension
|
||||
collectors:
|
||||
- type: directory
|
||||
value: src/Extension/.*
|
||||
- name: OtelProto
|
||||
collectors:
|
||||
- type: directory
|
||||
value: proto/otel/.*
|
||||
- name: GoogleProtobuf
|
||||
collectors:
|
||||
- type: className
|
||||
regex: ^Google\\Protobuf\\*
|
||||
- name: Grpc
|
||||
collectors:
|
||||
- type: className
|
||||
regex: ^Grpc\\*
|
||||
- name: PsrLog
|
||||
collectors:
|
||||
- type: className
|
||||
regex: ^Psr\\Log\\*
|
||||
- name: PsrHttp
|
||||
collectors:
|
||||
- type: className
|
||||
regex: ^Psr\\Http\\*
|
||||
- name: HttpPlug
|
||||
collectors:
|
||||
- type: className
|
||||
regex: ^Http\\*
|
||||
- name: Prometheus
|
||||
collectors:
|
||||
- type: className
|
||||
regex: ^Prometheus\\*
|
||||
- name: FFI
|
||||
collectors:
|
||||
- type: className
|
||||
regex: ^FFI\\*
|
||||
- name: Composer
|
||||
collectors:
|
||||
- type: className
|
||||
regex: ^Composer\\*
|
||||
- name: HttpClients
|
||||
collectors:
|
||||
- type: className
|
||||
value: ^Symfony\\Component\\HttpClient\\*
|
||||
- type: className
|
||||
value: ^GuzzleHttp\\*
|
||||
- type: className
|
||||
value: ^Buzz\\*
|
||||
- name: SPI
|
||||
collectors:
|
||||
- type: className
|
||||
value: ^Nevay\\SPI\\*
|
||||
- name: SymfonyConfig
|
||||
collectors:
|
||||
- type: className
|
||||
value: ^Symfony\\Component\\Config\\*
|
||||
- type: className
|
||||
value: ^Symfony\\Component\\Yaml\\*
|
||||
- type: className
|
||||
value: ^Symfony\\Component\\VarExporter\\*
|
||||
- name: RamseyUuid
|
||||
collectors:
|
||||
- type: className
|
||||
regex: ^Ramsey\\Uuid\\*
|
||||
- name: NyholmPsr7Server
|
||||
collectors:
|
||||
- type: className
|
||||
regex: ^Nyholm\\Psr7Server\\*
|
||||
- name: Polyfills
|
||||
collectors:
|
||||
- type: directory
|
||||
value: deptrac/polyfills/.*
|
||||
- type: directory
|
||||
value: vendor/symfony/polyfill-php83
|
||||
- name: DotenvProvider
|
||||
collectors:
|
||||
- type: className
|
||||
regex: ^Symfony\\Component\\Dotenv\\*
|
||||
- type: className
|
||||
regex: ^Dotenv\\*
|
||||
ruleset:
|
||||
Context:
|
||||
- FFI
|
||||
- Polyfills
|
||||
SemConv: ~
|
||||
ConfigSDK:
|
||||
- SymfonyConfig
|
||||
- API
|
||||
- SDK
|
||||
- SPI
|
||||
- PsrLog
|
||||
- Composer
|
||||
- Context
|
||||
- Contrib
|
||||
- Extension
|
||||
- Polyfills
|
||||
- DotenvProvider
|
||||
API:
|
||||
- Context
|
||||
- PsrLog
|
||||
- SPI
|
||||
- Polyfills
|
||||
SDK:
|
||||
- +API
|
||||
- ConfigSDK
|
||||
- SemConv
|
||||
- PsrHttp
|
||||
- HttpPlug
|
||||
- Composer
|
||||
- HttpClients
|
||||
- SPI
|
||||
- RamseyUuid
|
||||
- NyholmPsr7Server
|
||||
Contrib:
|
||||
- +SDK
|
||||
- +OtelProto
|
||||
- Grpc
|
||||
- Prometheus
|
||||
Extension:
|
||||
- +API
|
||||
OtelProto:
|
||||
- GoogleProtobuf
|
||||
- Grpc
|
|
@ -1,5 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace OpenTelemetry\Config\SDK\Configuration;
|
||||
|
||||
class ComponentPlugin {}
|
|
@ -1,5 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace OpenTelemetry\Config\SDK\Configuration;
|
||||
|
||||
class ComponentProvider {}
|
|
@ -1,5 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace OpenTelemetry\Config\SDK\Configuration;
|
||||
|
||||
class ComponentProviderRegistry {}
|
|
@ -1,5 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace OpenTelemetry\Config\SDK\Configuration;
|
||||
|
||||
class Context {}
|
|
@ -1,3 +0,0 @@
|
|||
Polyfills for deptrac, which represent classes missing from some PHP versions, or classes
|
||||
that deptrac does not understand.
|
||||
We also use some symfony polyfills for deptrac, under `vendor/symfony/polyfill-*`.
|
|
@ -1,11 +1,13 @@
|
|||
version: '3.7'
|
||||
services:
|
||||
php:
|
||||
image: ghcr.io/open-telemetry/opentelemetry-php/opentelemetry-php-base:${PHP_VERSION:-8.1}
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/Dockerfile
|
||||
volumes:
|
||||
- ./:/usr/src/myapp
|
||||
depends_on:
|
||||
- collector
|
||||
- otel-collector
|
||||
zipkin:
|
||||
image: openzipkin/zipkin-slim
|
||||
ports:
|
||||
|
@ -17,7 +19,8 @@ services:
|
|||
ports:
|
||||
- 9412:9412
|
||||
- 16686:16686
|
||||
collector:
|
||||
otel-collector:
|
||||
platform: linux/amd64
|
||||
image: otel/opentelemetry-collector-contrib
|
||||
command: ["--config=/etc/otel-collector-config.yml"]
|
||||
volumes:
|
||||
|
@ -29,5 +32,5 @@ services:
|
|||
- "13133:13133" # health_check extension
|
||||
- "9411" # Zipkin receiver
|
||||
- "4317:4317" # OTLP gRPC receiver
|
||||
- "4318:4318" # OTLP/HTTP receiver
|
||||
- "55681:55681" # Legacy OTLP/HTTP Port
|
||||
- "55680:55679" # zpages extension
|
|
@ -1,26 +0,0 @@
|
|||
version: '3.8'
|
||||
services:
|
||||
web:
|
||||
image: nginx:alpine
|
||||
ports:
|
||||
- '8080:80'
|
||||
depends_on:
|
||||
- php
|
||||
volumes:
|
||||
- ./docker/examples/nginx.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
php:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/examples/Dockerfile
|
||||
args:
|
||||
- PHP_VERSION=8.1-fpm
|
||||
- EXT_ENABLE=ffi
|
||||
environment:
|
||||
- OTEL_PHP_FIBERS_ENABLED=true
|
||||
command:
|
||||
- php-fpm
|
||||
- -d
|
||||
- opcache.preload=/php/vendor/autoload.php
|
||||
volumes:
|
||||
- ./tests/Context/Fiber/test_context_switching_ffi_observer.phpt:/var/www/public/index.php
|
||||
- ./:/php
|
|
@ -1,11 +0,0 @@
|
|||
services:
|
||||
phpdoc:
|
||||
image: phpdoc/phpdoc:3
|
||||
volumes:
|
||||
- ./:/data
|
||||
preview:
|
||||
image: nginx:alpine
|
||||
ports:
|
||||
- 8080:80
|
||||
volumes:
|
||||
- ./docs/build:/usr/share/nginx/html
|
|
@ -15,19 +15,16 @@ services:
|
|||
ports:
|
||||
- 8080:80
|
||||
depends_on:
|
||||
- php
|
||||
- php-prometheus
|
||||
volumes:
|
||||
- ./docker/examples/nginx.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
php:
|
||||
- ./docker/prometheus/nginx.conf:/etc/nginx/conf.d/default.conf
|
||||
- ./docker/prometheus/index.php:/var/www/public/index.php
|
||||
php-prometheus:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/examples/Dockerfile
|
||||
args:
|
||||
- PHP_VERSION=8.0-fpm
|
||||
- EXT_ENABLE=redis
|
||||
dockerfile: docker/prometheus/Dockerfile
|
||||
volumes:
|
||||
- ./examples/prometheus/index.php:/var/www/public/index.php
|
||||
- ./:/php
|
||||
- ./:/var/www/public
|
||||
depends_on:
|
||||
- redis
|
||||
- prometheus
|
||||
- prometheus
|
|
@ -0,0 +1,7 @@
|
|||
version: '3.7'
|
||||
services:
|
||||
proto:
|
||||
image: socialpoint/protobuf-tools
|
||||
volumes:
|
||||
- ./:/mnt
|
||||
command: sh -c "/mnt/script/proto_gen.sh"
|
|
@ -1,11 +1,9 @@
|
|||
version: '3.7'
|
||||
services:
|
||||
php:
|
||||
image: ghcr.io/open-telemetry/opentelemetry-php/opentelemetry-php-base:${PHP_VERSION:-8.1}
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/Dockerfile
|
||||
volumes:
|
||||
- ./:/usr/src/myapp
|
||||
- ./:/usr/src/open-telemetry/
|
||||
user: "${PHP_USER}:root"
|
||||
environment:
|
||||
XDEBUG_MODE: ${XDEBUG_MODE:-off}
|
||||
XDEBUG_CONFIG: ${XDEBUG_CONFIG:-''}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
version: '3.7'
|
||||
services:
|
||||
php:
|
||||
image: ghcr.io/open-telemetry/opentelemetry-php/opentelemetry-php-base:${PHP_VERSION:-8.1}
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/Dockerfile
|
||||
volumes:
|
||||
- ./:/usr/src/myapp
|
||||
user: "${PHP_USER}:root"
|
||||
env_file:
|
||||
- .env
|
||||
zipkin:
|
||||
image: openzipkin/zipkin-slim
|
||||
ports:
|
||||
|
@ -17,8 +17,3 @@ services:
|
|||
ports:
|
||||
- 9412:9412
|
||||
- 16686:16686
|
||||
collector:
|
||||
image: otel/opentelemetry-collector-contrib
|
||||
command: [ "--config=/etc/otel-collector-config.yml" ]
|
||||
volumes:
|
||||
- ./files/collector/otel-collector-config.yml:/etc/otel-collector-config.yml
|
||||
|
|
|
@ -1,46 +1,17 @@
|
|||
FROM composer:2.8 AS composer
|
||||
FROM debian:bullseye
|
||||
FROM php:8-buster
|
||||
|
||||
RUN apt-get -y update && apt-get -y install git zip && \
|
||||
curl -sS https://getcomposer.org/installer | php && \
|
||||
mv composer.phar /usr/local/bin/composer && \
|
||||
chmod +x /usr/local/bin/composer && \
|
||||
pecl install ast-1.0.10 xdebug && \
|
||||
docker-php-ext-enable ast xdebug && \
|
||||
# The pcntl extension is used for speeding up `make phan`
|
||||
docker-php-ext-install pcntl
|
||||
|
||||
# install GRPC
|
||||
RUN apt install -y --no-install-recommends zlib1g-dev && \
|
||||
pecl install grpc && \
|
||||
docker-php-ext-enable grpc
|
||||
|
||||
WORKDIR /usr/src/myapp
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends git wget gnupg2 \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& groupadd --gid 1000 php \
|
||||
&& useradd --system --uid 1000 --gid php --shell /bin/bash --create-home php
|
||||
|
||||
RUN apt-get update && apt-get install -y lsb-release apt-transport-https ca-certificates \
|
||||
&& echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list \
|
||||
&& wget -qO - https://packages.sury.org/php/apt.gpg | apt-key add - \
|
||||
&& apt-get update
|
||||
|
||||
ARG PHP_VERSION=8.3
|
||||
|
||||
RUN apt-get install -y \
|
||||
php${PHP_VERSION}-ast \
|
||||
php${PHP_VERSION}-cli \
|
||||
php${PHP_VERSION}-curl \
|
||||
php${PHP_VERSION}-dev \
|
||||
php${PHP_VERSION}-grpc \
|
||||
php${PHP_VERSION}-intl \
|
||||
php${PHP_VERSION}-mbstring \
|
||||
php${PHP_VERSION}-opcache \
|
||||
php${PHP_VERSION}-opentelemetry \
|
||||
php${PHP_VERSION}-protobuf \
|
||||
php${PHP_VERSION}-simplexml \
|
||||
php${PHP_VERSION}-sockets \
|
||||
php${PHP_VERSION}-xdebug \
|
||||
php${PHP_VERSION}-zip \
|
||||
php${PHP_VERSION}-mongodb \
|
||||
php${PHP_VERSION}-amqp \
|
||||
php${PHP_VERSION}-rdkafka \
|
||||
php${PHP_VERSION}-mysqli \
|
||||
php${PHP_VERSION}-pgsql \
|
||||
unzip
|
||||
|
||||
COPY --from=composer /usr/bin/composer /usr/local/bin/composer
|
||||
|
||||
RUN echo "grpc.enable_fork_support = 1" > $(php-config --ini-dir)/40-otel-dev.ini \
|
||||
&& echo "grpc.poll_strategy = epoll1" >> $(php-config --ini-dir)/40-otel-dev.ini \
|
||||
&& echo "zend.assertions = 1" >> $(php-config --ini-dir)/40-otel-dev.ini
|
||||
|
||||
USER php
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
|
||||
ARG PHP_VERSION
|
||||
ARG PHP_ALPINE_VERSION
|
||||
|
||||
FROM php:${PHP_VERSION:+${PHP_VERSION}-}alpine${PHP_ALPINE_VERSION} AS php
|
||||
|
||||
ENV PROJECT_ROOT=/php
|
||||
|
||||
RUN set -eux; \
|
||||
apk add --no-cache \
|
||||
bash \
|
||||
; \
|
||||
adduser -u 1000 -DSh /php php php
|
||||
|
||||
ARG EXT_INSTALL
|
||||
ARG EXT_ENABLE=''
|
||||
RUN --mount=from=mlocati/php-extension-installer,dst=/build/extension-installer,src=/usr/bin/install-php-extensions \
|
||||
set -eux; \
|
||||
/build/extension-installer \
|
||||
opcache \
|
||||
${EXT_INSTALL:-${EXT_ENABLE}} \
|
||||
; \
|
||||
docker-php-ext-enable \
|
||||
opcache \
|
||||
${EXT_ENABLE} \
|
||||
;
|
||||
|
||||
RUN set eux; \
|
||||
mkdir -p /usr/local/lib/php/vendor; \
|
||||
echo "<?php file_exists('/php/vendor/autoload.php') and require '/php/vendor/autoload.php';" > /usr/local/lib/php/vendor/autoload.php; \
|
||||
echo 'auto_prepend_file=vendor/autoload.php' > "$PHP_INI_DIR/conf.d/99-autoload.ini"
|
||||
|
||||
USER php
|
||||
WORKDIR /php
|
|
@ -1,12 +0,0 @@
|
|||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
root /var/www/public;
|
||||
|
||||
location / {
|
||||
fastcgi_pass php:9000;
|
||||
fastcgi_index index.php;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
include fastcgi_params;
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
version: '3.7'
|
||||
services:
|
||||
gitsplit:
|
||||
image: jderusse/gitsplit
|
||||
volumes:
|
||||
- ../../:/srv
|
||||
- ../../var/cache/gitsplit:/cache/gitsplit
|
||||
environment:
|
||||
GH_TOKEN: ${GITSPLIT_TOKEN:-''}
|
|
@ -0,0 +1,11 @@
|
|||
FROM php:7.3-fpm-alpine
|
||||
|
||||
RUN apk update && apk add --no-cache \
|
||||
$PHPIZE_DEPS
|
||||
|
||||
RUN pecl channel-update pecl.php.net && \
|
||||
pecl install redis \
|
||||
&& docker-php-ext-enable redis
|
||||
|
||||
# Clean
|
||||
RUN rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /var/cache/*
|
|
@ -0,0 +1,25 @@
|
|||
server {
|
||||
listen 80;
|
||||
index index.php index.html;
|
||||
server_name 127.0.0.1 localhost;
|
||||
root /var/www/public/examples/prometheus;
|
||||
|
||||
access_log /dev/stdout;
|
||||
error_log /dev/stdout debug;
|
||||
|
||||
underscores_in_headers on;
|
||||
|
||||
location / {
|
||||
try_files $uri /index.php?$args;
|
||||
}
|
||||
|
||||
location ~ \.php$ {
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass php-prometheus:9000;
|
||||
fastcgi_index index.php;
|
||||
fastcgi_read_timeout 1000;
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,256 @@
|
|||
# Integrating Opentelemetry PHP into Laravel Applications
|
||||
|
||||
## Introduction
|
||||
Distributed tracing helps developers and management gain insights into how well applications perform in terms of traces, metrics, and logs. This guide shows how developers can integrate OpenTelemetry PHP into their Laravel applications for the above benefits. Our example application visualizes exceptions from a Laravel application using both Jaeger and Zipkin.
|
||||
|
||||
To follow this guide you will need:
|
||||
|
||||
* PHP Installed; this example uses PHP 7.4.
|
||||
* [Composer](https://getcomposer.org/download/ ) for dependency management.
|
||||
* [Docker](https://docs.docker.com/get-docker/) for bundling our visualization tools. We have setup instructions for docker on this project's [readme](https://github.com/open-telemetry/opentelemetry-php#development).
|
||||
|
||||
This example uses Laravel version 8.40 .
|
||||
|
||||
## Step 1 - Creating a Laravel Application
|
||||
|
||||
The Laravel framework supports creating applications using composer. To do that, run `composer create-project <project-name>` . We are naming our project `otel-php-laravel-basic-example`, so the command is as follows:
|
||||
|
||||
`composer create-project laravel/laravel otel-php-laravel-basic-example`
|
||||
|
||||
To confirm that our application works, we can move to the application directory using `cd otel-php-laravel-basic-example` , then serve the application with `php artisan serve` .
|
||||
|
||||

|
||||
|
||||
Let's navigate to `http://127.0.0.1:8000` on our browser to see the default Laravel welcome page.
|
||||
|
||||

|
||||
## Step 2 - Require OpenTelemetry PHP Package
|
||||
|
||||
Starting from version `v.0.0.2`, the open-telemetry php package allows users to use their preferred HTTP layers for exporting traces. The benefit of this is that users can reuse already existing HTTP configurations for their applications. Hence, there is need to require packages that satisfy both `psr/http-client-implementation` and `psr/http-factory-implementation` before requiring the opentelemetry-php package.
|
||||
|
||||
By default, the Laravel framework utilizes `guzzlehttp/guzzle` and this satisfies `psr/http-client-implementation`, so we need to require the `guzzlehttp/psr7` to meet the `psr/http-factory-implementation` requirement. Let's run `composer require guzzlehttp/psr7:2.0.0-rc1`.
|
||||
|
||||
Note: We are specifying `2.0.0-rc1` as that is the release for `guzzlehttp/psr7` that includes HTTP factories as at the time of writing this guide.
|
||||
|
||||
Next, let's run `composer require open-telemetry/opentelemetry` to pull in the openTelemetry-php package.
|
||||
|
||||
## Step 3 - Bundle Zipkin and Jaeger into the Application
|
||||
|
||||
To visualize traces exported from our application, we need to integrate open source tracing tools [Zipkin](https://zipkin.io/) and [Jaeger](https://www.jaegertracing.io/) into our setup using docker.
|
||||
|
||||
First, we create a `docker-compose.yaml` file in the root of our project, with content as follows:
|
||||
|
||||
```yaml
|
||||
version: '3.7'
|
||||
services:
|
||||
zipkin:
|
||||
image: openzipkin/zipkin-slim
|
||||
ports:
|
||||
- 9411:9411
|
||||
jaeger:
|
||||
image: jaegertracing/all-in-one
|
||||
environment:
|
||||
COLLECTOR_ZIPKIN_HOST_PORT: 9412
|
||||
|
||||
ports:
|
||||
- 9412:9412
|
||||
- 16686:16686
|
||||
```
|
||||
|
||||
Next, we pull in Zipkin and Jaeger by running `docker-compose up -d`. This might take some time, depending on your internet connection speed.
|
||||
|
||||

|
||||
|
||||
We can confirm that Zipkin is up by navigating to `http://localhost:9411/` on our browser. For Jaeger, navigating to `http://localhost:16686/` on our browser should display the Jaeger home page.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## Step 5 - Instrument Laravel Application
|
||||
|
||||
For this step, we will utilize our OpenTelemetry PHP Library to export traces to both Zipkin and Jaeger.
|
||||
|
||||
The default entry point for Laravel applications is the `index.php` file located in the `public` folder. If we navigate to `public\index.php` we can see that the index file autoloads classes from packages within our vendor folder, making them easily useable within our application.
|
||||
|
||||
```php
|
||||
require __DIR__.'/../vendor/autoload.php';
|
||||
```
|
||||
|
||||
The other parts of the `index.php` file enable request and response resolution using the application kernel.
|
||||
|
||||
```php
|
||||
$app = require_once __DIR__.'/../bootstrap/app.php';
|
||||
|
||||
$kernel = $app->make(Kernel::class);
|
||||
|
||||
$response = tap($kernel->handle(
|
||||
$request = Request::capture()
|
||||
))->send();
|
||||
|
||||
$kernel->terminate($request, $response);
|
||||
```
|
||||
It is worthy of note that resources(namespaces, classes, variables) created within the `index.php` file are available within the entire application.
|
||||
|
||||
To use open-telemetry specific classes within our application we have to import them at the top of our index file, using the `use` keyword. This is what our list of open-telemetry imported classes should look like:
|
||||
|
||||
```php
|
||||
use OpenTelemetry\Contrib\Jaeger\Exporter as JaegerExporter;
|
||||
use OpenTelemetry\Contrib\Zipkin\Exporter as ZipkinExporter;
|
||||
use OpenTelemetry\Sdk\Trace\Clock;
|
||||
use OpenTelemetry\Context\Context;
|
||||
use OpenTelemetry\Sdk\Trace\Sampler\AlwaysOnSampler;
|
||||
use OpenTelemetry\Sdk\Trace\SamplingResult;
|
||||
use OpenTelemetry\Sdk\Trace\SpanProcessor\BatchSpanProcessor;
|
||||
use OpenTelemetry\Sdk\Trace\TracerProvider;
|
||||
use OpenTelemetry\Trace as API;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Psr7\HttpFactory;
|
||||
```
|
||||
|
||||
Remember that these imports should go side by side with the default class imports that come with the `index.php` file.
|
||||
|
||||
Next, we create a sample recording trace using the [AlwaysOnSampler](https://github.com/open-telemetry/opentelemetry-php/blob/main/sdk/Trace/Sampler/AlwaysOnSampler.php) class, just before the app instance is created like below:
|
||||
|
||||
```php
|
||||
$sampler = new AlwaysOnSampler();
|
||||
$samplingResult = $sampler->shouldSample(
|
||||
new Context(),
|
||||
md5((string) microtime(true)),
|
||||
'io.opentelemetry.example',
|
||||
API\SpanKind::KIND_INTERNAL
|
||||
);
|
||||
```
|
||||
|
||||
Since we are looking to export traces to both Zipkin and Jaeger we have to make use of their exporters;
|
||||
|
||||
```php
|
||||
$jaegerExporter = new JaegerExporter(
|
||||
'Hello World Web Server Jaeger',
|
||||
'http://localhost:9412/api/v2/spans',
|
||||
new Client(),
|
||||
new HttpFactory(),
|
||||
new HttpFactory()
|
||||
);
|
||||
|
||||
$zipkinExporter = new ZipkinExporter(
|
||||
'Hello World Web Server Zipkin',
|
||||
'http://localhost:9411/api/v2/spans',
|
||||
new Client(),
|
||||
new HttpFactory(),
|
||||
new HttpFactory()
|
||||
);
|
||||
```
|
||||
|
||||
Next, we create a trace then add processors for each trace(One for Jaeger and another for Zipkin). Then we proceed to start and activate a span for each trace. We create a trace only if the RECORD AND SAMPLED sampling result condition passes as follows;
|
||||
|
||||
```php
|
||||
if (SamplingResult::RECORD_AND_SAMPLE === $samplingResult->getDecision()) {
|
||||
|
||||
$jaegerTracer = (new TracerProvider(null, $sampler))
|
||||
->addSpanProcessor(new BatchSpanProcessor($jaegerExporter, Clock::get()))
|
||||
->getTracer('io.opentelemetry.contrib.php');
|
||||
|
||||
$zipkinTracer = (new TracerProvider(null, $sampler))
|
||||
->addSpanProcessor(new BatchSpanProcessor($zipkinExporter, Clock::get()))
|
||||
->getTracer('io.opentelemetry.contrib.php');
|
||||
|
||||
$request = Request::createFromGlobals();
|
||||
$jaegerSpan = $jaegerTracer->startAndActivateSpan($request->getUri());
|
||||
$zipkinSpan = $zipkinTracer->startAndActivateSpan($request->getUri());
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
Finally, we end the active spans if sampling is complete, by adding the following block at the end of the `index.php` file;
|
||||
|
||||
```php
|
||||
if (SamplingResult::RECORD_AND_SAMPLE === $samplingResult->getDecision()) {
|
||||
$zipkinSpan->end();
|
||||
$jaegerSpan->end();
|
||||
}
|
||||
```
|
||||
|
||||
Let's confirm that we can see exported traces on both Zipkin and Jaeger. To do that, we need to reload `http://127.0.0.1:8000` on our browser;
|
||||
|
||||
We also need reload both Zipkin and Jaeger on our browser, using the URLs `http://localhost:9411/` and `http://localhost:16686/`. Do ensure that both your Laravel server and docker instance are running for this step.
|
||||
|
||||
For Jaeger under service, you should see a `Hello World Web Server Jaeger` service. Go ahead and click find traces to see exported traces.
|
||||
|
||||

|
||||
|
||||
|
||||
Once we click on `Find Traces`, you should be able to see traces like below:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
We can click on a trace to get more information about the trace.
|
||||
|
||||

|
||||
|
||||
|
||||
For Zipkin, we can visualize our trace by clicking on `Run Query`
|
||||
|
||||

|
||||
|
||||
|
||||
Since resources in Laravel's `public\index.php` file are available to the entire application, we can use any of the already instantiated tracers to further instrument controllers or any other parts of our application.
|
||||
|
||||
Let's create a `Hello` controller to check this out. Run the command `php artisan make:controller HelloController`
|
||||
|
||||

|
||||
|
||||
|
||||
Next we need to add a route for accessing the controller. To do this we need to utilize the `HelloController` class within our web routes file located in the `routes\web.php` as follows:
|
||||
|
||||
```php
|
||||
use App\Http\Controllers\HelloController;
|
||||
```
|
||||
Next we need to add a route and method for the controller.
|
||||
|
||||
```php
|
||||
Route::get('/hello', [HelloController::class, 'index']);
|
||||
```
|
||||
The above snippet routes every GET request from the `/hello` route on the browser to an index method within the `HelloController` class. For now, this method does not exist, so we have to add it to our controller as follows
|
||||
|
||||
```php
|
||||
public function index(){
|
||||
return "hello";
|
||||
}
|
||||
```
|
||||
Let's confirm that everything works well by visiting the `/hello` route on our browser.
|
||||
|
||||

|
||||
|
||||
|
||||
Now that we have the `index` method working, we can simulate adding an exception event to our Zipkin trace as follows:
|
||||
|
||||
```php
|
||||
global $zipkinTracer;
|
||||
if ($zipkinTracer) {
|
||||
/** @var Span $span */
|
||||
$span = $zipkinTracer->getActiveSpan();
|
||||
|
||||
$span->setAttribute('foo', 'bar');
|
||||
$span->updateName('New name');
|
||||
|
||||
$childSpan = $zipkinTracer->startAndActivateSpan('Child span');
|
||||
try {
|
||||
throw new \Exception('Exception Example');
|
||||
} catch (\Exception $exception) {
|
||||
$span->setSpanStatus($exception->getCode(), $exception->getMessage());
|
||||
}
|
||||
$childSpan->end();
|
||||
}
|
||||
```
|
||||
In the above snippet we change the span name and attributes for our Zipkin trace, we also add an exception event to the span.
|
||||
|
||||
We need to reload our `http://127.0.0.1:8000/hello` route, then navigate to Zipkin like before, to see that our span name gets updated to `new name` and our `Exception Example` is visible.
|
||||
|
||||

|
||||
|
||||
|
||||
## Summary
|
||||
With the above example we have been able to instrument a Laravel application using the OpenTelemetry PHP library. You can fork the example project [here](https://github.com/prondubuisi/otel-php-laravel-basic-example).
|
|
@ -1,291 +0,0 @@
|
|||
# Exploring Opentelemetry in Laravel Applications
|
||||
|
||||
## Introduction
|
||||
|
||||
Distributed tracing helps developers and management gain insights into how well applications perform in terms of traces,
|
||||
metrics, and logs. This guide shows how developers can integrate OpenTelemetry PHP into their Laravel applications for
|
||||
the above benefits. Our example application visualizes exceptions from a Laravel application using both Jaeger and
|
||||
Zipkin.
|
||||
|
||||
To follow this guide you will need:
|
||||
|
||||
* PHP Installed; this example uses PHP 7.4.
|
||||
* [Composer](https://getcomposer.org/download/ ) for dependency management.
|
||||
* [Docker](https://docs.docker.com/get-docker/) for bundling our visualization tools. We have setup instructions for
|
||||
docker on this project's [readme](https://github.com/open-telemetry/opentelemetry-php#development).
|
||||
|
||||
This example uses Laravel version `9.1` and OpenTelemetry PHP SDK version `0.0.11`.
|
||||
|
||||
> ⚠ This example is only intended to introduce how OpenTelemetry can be used in a Laravel application. The
|
||||
> example code is not suited for production applications, and must not be consulted for any code that goes into
|
||||
> production.
|
||||
|
||||
## Step 1 - Creating a Laravel Application
|
||||
|
||||
The Laravel framework supports creating applications using composer. To do that,
|
||||
run `composer create-project <project-name>`. We are naming our project `otel-php-laravel-basic-example`, so the
|
||||
command is as follows:
|
||||
|
||||
`composer create-project laravel/laravel otel-php-laravel-basic-example`
|
||||
|
||||
To confirm that our application works, we can move to the application directory
|
||||
using `cd otel-php-laravel-basic-example` , then serve the application with `php artisan serve` .
|
||||
|
||||

|
||||
|
||||
Let's navigate to `http://127.0.0.1:8000` on our browser to see the default Laravel welcome page.
|
||||
|
||||

|
||||
|
||||
## Step 2 - Require OpenTelemetry PHP Package
|
||||
|
||||
Starting from version `v.0.0.2`, the open-telemetry php package allows users to use their preferred HTTP layers for
|
||||
exporting traces. The benefit of this is that users can reuse already existing HTTP configurations for their
|
||||
applications. Hence, there is need to require packages that satisfy both `psr/http-client-implementation`
|
||||
and `psr/http-factory-implementation` before requiring the opentelemetry-php package.
|
||||
|
||||
By default, the Laravel framework utilizes `guzzlehttp/guzzle` and this satisfies `psr/http-client-implementation`, so
|
||||
we need to require the `guzzlehttp/psr7` to meet the `psr/http-factory-implementation` requirement. Let's
|
||||
run `composer require guzzlehttp/psr7:2.1`.
|
||||
|
||||
Note: We are specifying `2.1` as that is the release for `guzzlehttp/psr7` that includes HTTP factories as at the
|
||||
time of writing this guide.
|
||||
|
||||
Starting from version `v.0.0.4`, opentelemetry-php package also requires `php-http/async-client-implementation`. Any
|
||||
client implementation would work, but for this example, let's go ahead with `php-http/guzzle7-adapter`. Run
|
||||
`composer require php-http/guzzle7-adapter` to install `php-http/guzzle7-adapter`.
|
||||
|
||||
Next, let's run `composer require open-telemetry/opentelemetry` to pull in the openTelemetry-php package. It is worthy
|
||||
of note that this command pulls in the last stable release for the library.
|
||||
|
||||
## Step 3 - Bundle Zipkin and Jaeger into the Application
|
||||
|
||||
To visualize traces exported from our application, we need to integrate open source tracing
|
||||
tools [Zipkin](https://zipkin.io/) and [Jaeger](https://www.jaegertracing.io/) into our setup using docker.
|
||||
|
||||
First, we create a `docker-compose.yaml` file in the root of our project, with content as follows:
|
||||
|
||||
```yaml
|
||||
version: '3.7'
|
||||
services:
|
||||
zipkin:
|
||||
image: openzipkin/zipkin-slim
|
||||
ports:
|
||||
- "9411:9411"
|
||||
jaeger:
|
||||
image: jaegertracing/all-in-one
|
||||
environment:
|
||||
COLLECTOR_ZIPKIN_HOST_PORT: 9412
|
||||
|
||||
ports:
|
||||
- "9412:9412"
|
||||
- "16686:16686"
|
||||
```
|
||||
|
||||
Next, we pull in Zipkin and Jaeger by running `docker-compose up -d`. This might take some time, depending on your
|
||||
internet connection speed.
|
||||
|
||||

|
||||
|
||||
We can confirm that Zipkin is up by navigating to `http://localhost:9411/` on our browser. For Jaeger, navigating
|
||||
to `http://localhost:16686/` on our browser should display the Jaeger home page.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## Step 5 - Instrument Laravel Application
|
||||
|
||||
For this step, we will utilize our OpenTelemetry PHP Library to export traces to both Zipkin and Jaeger.
|
||||
|
||||
The default entry point for Laravel applications is the `index.php` file located in the `public` folder. If we navigate
|
||||
to `public\index.php` we can see that the index file autoloads classes from packages within our vendor folder, making
|
||||
them easily usable within our application.
|
||||
|
||||
```php
|
||||
require __DIR__.'/../vendor/autoload.php';
|
||||
```
|
||||
|
||||
The other parts of the `index.php` file enable request and response resolution using the application kernel.
|
||||
|
||||
```php
|
||||
$app = require_once __DIR__.'/../bootstrap/app.php';
|
||||
|
||||
$kernel = $app->make(Kernel::class);
|
||||
|
||||
$response = tap($kernel->handle(
|
||||
$request = Request::capture()
|
||||
))->send();
|
||||
|
||||
$kernel->terminate($request, $response);
|
||||
```
|
||||
|
||||
It is worthy of note that resources(namespaces, classes, variables) created within the `index.php` file are available
|
||||
within the entire application.
|
||||
|
||||
To use open-telemetry specific classes within our application we have to import them at the top of our index file, using
|
||||
the `use` keyword. This is what our list of open-telemetry imported classes should look like:
|
||||
|
||||
```php
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Psr7\HttpFactory;
|
||||
use OpenTelemetry\API\Trace\Span;
|
||||
use OpenTelemetry\API\Trace\StatusCode;
|
||||
use OpenTelemetry\API\Trace\TracerInterface;
|
||||
use OpenTelemetry\Contrib\Zipkin\Exporter as ZipkinExporter;
|
||||
use OpenTelemetry\SDK\Common\Export\Http\PsrTransportFactory;
|
||||
use OpenTelemetry\SDK\Trace\Sampler\AlwaysOnSampler;
|
||||
use OpenTelemetry\SDK\Trace\SpanProcessor\BatchSpanProcessor;
|
||||
use OpenTelemetry\SDK\Trace\SpanProcessor\SimpleSpanProcessor;
|
||||
use OpenTelemetry\SDK\Trace\TracerProvider;
|
||||
```
|
||||
|
||||
Remember that these imports should go side by side with the default class imports that come with the `index.php` file.
|
||||
|
||||
Next, we create a sample recording trace using
|
||||
the [AlwaysOnSampler](https://github.com/open-telemetry/opentelemetry-php/blob/main/sdk/Trace/Sampler/AlwaysOnSampler.php)
|
||||
class, which is default. Since we are looking to export traces to both Zipkin and Jaeger, we configure a tracer with exporters for both the
|
||||
services just before the app instance is created like below;
|
||||
|
||||
```php
|
||||
$httpClient = new Client();
|
||||
$httpFactory = new HttpFactory();
|
||||
|
||||
$tracer = (new TracerProvider(
|
||||
[
|
||||
new SimpleSpanProcessor(
|
||||
new OpenTelemetry\Contrib\Zipkin\Exporter(
|
||||
PsrTransportFactory::discover()->create('http://zipkin:9411/api/v2/spans', 'application/json')
|
||||
),
|
||||
),
|
||||
],
|
||||
new AlwaysOnSampler(),
|
||||
))->getTracer('Hello World Laravel Web Server');
|
||||
```
|
||||
|
||||
Next we create a span from our tracer;
|
||||
|
||||
```php
|
||||
$request = Request::capture();
|
||||
$span = $tracer->spanBuilder($request->url())->startSpan();
|
||||
$spanScope = $span->activate();
|
||||
```
|
||||
|
||||
Finally, we end the active spans once and detach the span scope by adding the following block at the end of
|
||||
the `index.php` file;
|
||||
|
||||
```php
|
||||
$span->end();
|
||||
$spanScope->detach();
|
||||
```
|
||||
|
||||
Let's confirm that we can see exported traces on both Zipkin and Jaeger. To do that, we need to
|
||||
reload `http://127.0.0.1:8000` on our browser;
|
||||
|
||||
We also need reload both Zipkin and Jaeger on our browser, using the URLs `http://localhost:9411/`
|
||||
and `http://localhost:16686/`. Do ensure that both your Laravel server and docker instance are running for this step.
|
||||
|
||||
For Jaeger under service, you should see a `Hello World Web Server Jaeger` service. Go ahead and click find traces to
|
||||
see exported traces.
|
||||
|
||||

|
||||
|
||||
Once we click on `Find Traces`, you should be able to see traces like below:
|
||||
|
||||

|
||||
|
||||
We can click on a trace to get more information about the trace.
|
||||
|
||||

|
||||
|
||||
For Zipkin, we can visualize our trace by clicking on `Run Query`
|
||||
|
||||

|
||||
|
||||
Since resources in Laravel's `public\index.php` file are available to the entire application, we can use any of the
|
||||
already instantiated tracers to further instrument controllers or any other parts of our application.
|
||||
|
||||
Let's create a `Hello` controller to check this out. Run the command `php artisan make:controller HelloController`
|
||||
|
||||

|
||||
|
||||
Next we need to add a route for accessing the controller. To do this we need to utilize the `HelloController` class
|
||||
within our web routes file located in the `routes\web.php` as follows:
|
||||
|
||||
```php
|
||||
use App\Http\Controllers\HelloController;
|
||||
```
|
||||
|
||||
Next we need to add a route and method for the controller.
|
||||
|
||||
```php
|
||||
Route::get('/hello', [HelloController::class, 'index']);
|
||||
```
|
||||
|
||||
The above snippet routes every GET request from the `/hello` route on the browser to an index method within
|
||||
the `HelloController` class. For now, this method does not exist, so we have to add it to our controller in
|
||||
`app\Http\Controllers\HelloController.php` as follows:
|
||||
|
||||
```php
|
||||
public function index(){
|
||||
return "hello";
|
||||
}
|
||||
```
|
||||
|
||||
Let's confirm that everything works well by visiting the `/hello` route on our browser.
|
||||
|
||||

|
||||
|
||||
Now that we have the `index` method working, let's make changes to the existing rootSpan and create an exception in a
|
||||
child span of the rootSpan. We can do the same as follows:
|
||||
|
||||
- Import the required functions on top of the file:
|
||||
|
||||
```php
|
||||
use OpenTelemetry\API\Trace\Span;
|
||||
use OpenTelemetry\API\Trace\StatusCode;
|
||||
use OpenTelemetry\SDK\Trace\TracerProvider;
|
||||
```
|
||||
|
||||
- Put the below snippet in `index()` function before the `return` statement:
|
||||
|
||||
```php
|
||||
/** @var TracerProvider $tracer */
|
||||
$tracer = TracerProvider::getDefaultTracer();
|
||||
if ($tracer) {
|
||||
/** @var Span $span */
|
||||
$span = Span::getCurrent();
|
||||
|
||||
$span->setAttribute('foo', 'bar');
|
||||
$span->setAttribute('Application', 'Laravel');
|
||||
$span->setAttribute('foo', 'bar1');
|
||||
$span->updateName('New name');
|
||||
|
||||
$childSpan = $tracer->spanBuilder('Child span')->startSpan();
|
||||
$childScope = $childSpan->activate();
|
||||
try {
|
||||
throw new \Exception('Exception Example');
|
||||
} catch (\Exception $exception) {
|
||||
$childSpan->recordException($exception);
|
||||
}
|
||||
$childSpan->end();
|
||||
$childScope->detach();
|
||||
}
|
||||
```
|
||||
|
||||
In the above snippet, we set two new attributes for the current span, and then change the value for one of the attribute
|
||||
and the trace reflects the latest value. We also change the name of our current span and add an exception event to the
|
||||
child span.
|
||||
|
||||
* Point to note: all the variables in `index.php` are available in this file. But we use inbuilt functions to get `$tracer`
|
||||
rather than using the global variables.
|
||||
|
||||
We need to reload our `http://127.0.0.1:8000/hello` route, then navigate to Zipkin and Jaeger like before, to see that our
|
||||
span name gets updated to `new name` and our `Exception Example` is visible.
|
||||
|
||||

|
||||
|
||||
## Summary
|
||||
|
||||
With the above example we have been able to instrument a Laravel application using the OpenTelemetry PHP library. You
|
||||
can fork the example project [here](https://github.com/prondubuisi/otel-php-laravel-basic-example).
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue