├── .editorconfig
├── .envrc_example
├── .github
├── CODEOWNERS
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ ├── feature_request.md
│ ├── question-discussion.md
│ └── security-vulnerability-report.md
├── PULL_REQUEST_TEMPLATE.md
├── release.yml
└── workflows
│ ├── add-to-project-v2.yml
│ ├── apply-labels.yml
│ ├── publish-ghcr.yml
│ ├── stale.yml
│ └── validate-pr-title.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── DEVELOPING.md
├── LICENSE
├── NOTICE
├── OSSMETADATA
├── README.md
├── SECURITY.md
├── SUPPORT.md
├── Tiltfile
├── docker-compose.dotnet.yml
├── docker-compose.elixir.yml
├── docker-compose.java.yml
├── docker-compose.node.yml
├── docker-compose.python.yml
├── docker-compose.web.yml
├── docker-compose.yml
├── dotnet
├── .editorconfig
├── .gitignore
├── README.md
├── dotnet.sln
├── frontend
│ ├── Controllers
│ │ └── GreetingController.cs
│ ├── Dockerfile
│ ├── Program.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── appsettings.Development.json
│ ├── appsettings.json
│ └── frontend.csproj
├── global.json
├── message-service
│ ├── Controllers
│ │ └── MessageController.cs
│ ├── Dockerfile
│ ├── Program.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── Startup.cs
│ ├── appsettings.Development.json
│ ├── appsettings.json
│ └── message-service.csproj
├── name-service
│ ├── Controllers
│ │ └── NameController.cs
│ ├── Dockerfile
│ ├── Program.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── Startup.cs
│ ├── appsettings.Development.json
│ ├── appsettings.json
│ └── name-service.csproj
└── year-service
│ ├── Dockerfile
│ ├── Program.cs
│ ├── Properties
│ └── launchSettings.json
│ ├── appsettings.Development.json
│ ├── appsettings.json
│ └── year-service.csproj
├── elixir
├── .editorconfig
├── .tool-versions
├── README.md
├── frontend
│ ├── .formatter.exs
│ ├── .gitignore
│ ├── Dockerfile
│ ├── config
│ │ ├── config.exs
│ │ ├── dev.exs
│ │ ├── prod.exs
│ │ ├── prod.secret.exs
│ │ └── test.exs
│ ├── lib
│ │ ├── frontend.ex
│ │ ├── frontend
│ │ │ └── application.ex
│ │ ├── frontend_web.ex
│ │ └── frontend_web
│ │ │ ├── controllers
│ │ │ └── greetings_controller.ex
│ │ │ ├── endpoint.ex
│ │ │ ├── router.ex
│ │ │ ├── telemetry.ex
│ │ │ └── views
│ │ │ ├── error_helpers.ex
│ │ │ └── error_view.ex
│ ├── mix.exs
│ └── mix.lock
├── message
│ ├── .formatter.exs
│ ├── .gitignore
│ ├── Dockerfile
│ ├── config
│ │ └── runtime.exs
│ ├── lib
│ │ ├── message.ex
│ │ └── message
│ │ │ └── application.ex
│ ├── mix.exs
│ └── mix.lock
├── name
│ ├── .formatter.exs
│ ├── .gitignore
│ ├── Dockerfile
│ ├── config
│ │ └── runtime.exs
│ ├── lib
│ │ ├── name.ex
│ │ └── name
│ │ │ └── application.ex
│ ├── mix.exs
│ └── mix.lock
└── year
│ ├── .formatter.exs
│ ├── .gitignore
│ ├── Dockerfile
│ ├── config
│ └── runtime.exs
│ ├── lib
│ ├── year.ex
│ └── year
│ │ └── application.ex
│ ├── mix.exs
│ └── mix.lock
├── go-auto-instrumented
├── .dockerignore
├── README.md
├── docker-compose.yml
├── frontend
│ ├── Dockerfile
│ └── main.go
├── go.mod
├── go.sum
├── greetings-instrumented.yaml
├── greetings
│ ├── frontend.yaml
│ ├── kustomization.yaml
│ ├── message.yaml
│ ├── name.yaml
│ ├── ns.yaml
│ └── year.yaml
├── message-service
│ ├── Dockerfile
│ └── main.go
├── name-service
│ ├── Dockerfile
│ └── main.go
├── otel-collector.yaml
└── year-service
│ ├── Dockerfile
│ └── main.go
├── go-uninstrumented
├── .dockerignore
├── README.md
├── docker-compose.yml
├── frontend-service
│ ├── Dockerfile
│ └── main.go
├── go.mod
├── go.sum
├── greetings.yaml
├── message-service
│ ├── Dockerfile
│ └── main.go
├── name-service
│ ├── Dockerfile
│ └── main.go
└── year-service
│ ├── Dockerfile
│ └── main.go
├── golang
├── .editorconfig
├── docker-compose.yml
├── frontend
│ ├── Dockerfile
│ └── main.go
├── go.mod
├── go.sum
├── message-service
│ ├── Dockerfile
│ └── main.go
├── name-service
│ ├── Dockerfile
│ └── main.go
└── year-service
│ ├── Dockerfile
│ └── main.go
├── java
├── .editorconfig
├── frontend
│ ├── .gitignore
│ ├── Dockerfile
│ ├── build.gradle
│ ├── gradle
│ │ └── wrapper
│ │ │ ├── gradle-wrapper.jar
│ │ │ └── gradle-wrapper.properties
│ ├── gradlew
│ ├── gradlew.bat
│ ├── settings.gradle
│ └── src
│ │ └── main
│ │ ├── java
│ │ └── io
│ │ │ └── honeycomb
│ │ │ └── examples
│ │ │ └── frontend_java
│ │ │ ├── FrontendApplication.java
│ │ │ ├── GreetingController.java
│ │ │ ├── MessageService.java
│ │ │ └── NameService.java
│ │ └── resources
│ │ └── application.properties
├── message-service
│ ├── .gitignore
│ ├── Dockerfile
│ ├── build.gradle
│ ├── gradle
│ │ └── wrapper
│ │ │ ├── gradle-wrapper.jar
│ │ │ └── gradle-wrapper.properties
│ ├── gradlew
│ ├── gradlew.bat
│ ├── settings.gradle
│ └── src
│ │ └── main
│ │ ├── java
│ │ └── io
│ │ │ └── honeycomb
│ │ │ └── examples
│ │ │ └── message
│ │ │ ├── MessageApplication.java
│ │ │ ├── MessageEndpoints.java
│ │ │ └── MessageService.java
│ │ └── resources
│ │ └── application.properties
├── name-service
│ ├── .gitignore
│ ├── Dockerfile
│ ├── build.gradle
│ ├── gradle
│ │ └── wrapper
│ │ │ ├── gradle-wrapper.jar
│ │ │ └── gradle-wrapper.properties
│ ├── gradlew
│ ├── gradlew.bat
│ ├── settings.gradle
│ └── src
│ │ └── main
│ │ ├── java
│ │ └── io
│ │ │ └── honeycomb
│ │ │ └── examples
│ │ │ └── name
│ │ │ ├── MessageService.java
│ │ │ ├── NameApplication.java
│ │ │ ├── NameController.java
│ │ │ ├── NameService.java
│ │ │ └── YearService.java
│ │ └── resources
│ │ └── application.properties
└── year-service
│ ├── .gitignore
│ ├── Dockerfile
│ ├── build.gradle
│ ├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
│ ├── gradlew
│ ├── gradlew.bat
│ ├── settings.gradle
│ └── src
│ ├── main
│ ├── java
│ │ └── io
│ │ │ └── honeycomb
│ │ │ └── examples
│ │ │ └── javaotlp
│ │ │ ├── YearApplication.java
│ │ │ ├── YearController.java
│ │ │ └── YearService.java
│ └── resources
│ │ └── application.properties
│ └── test
│ └── java
│ └── io
│ └── honeycomb
│ └── examples
│ └── javaotlp
│ └── YearApplicationTests.java
├── node
├── .editorconfig
├── .gitignore
├── .tool-versions
├── README.md
├── frontend-service
│ ├── .dockerignore
│ ├── .prettierrc.js
│ ├── Dockerfile
│ ├── main.js
│ ├── package-lock.json
│ ├── package.json
│ └── tracing.js
├── message-service
│ ├── .dockerignore
│ ├── .prettierrc.js
│ ├── Dockerfile
│ ├── main.js
│ ├── package-lock.json
│ └── package.json
├── name-service
│ ├── .dockerignore
│ ├── .prettierrc.js
│ ├── Dockerfile
│ ├── main.js
│ ├── package-lock.json
│ └── package.json
└── year-service
│ ├── .dockerignore
│ ├── .prettierrc.js
│ ├── Dockerfile
│ ├── main.js
│ ├── package-lock.json
│ ├── package.json
│ └── tracing.js
├── otel-collector-config.yaml
├── python
├── .editorconfig
├── .gitignore
├── .tool-versions
├── frontend
│ ├── Dockerfile
│ ├── README.rst
│ ├── frontend
│ │ ├── __init__.py
│ │ └── __main__.py
│ ├── poetry.lock
│ ├── pyproject.toml
│ └── tests
│ │ ├── __init__.py
│ │ └── test_frontend.py
├── message-service
│ ├── Dockerfile
│ ├── README.rst
│ ├── message_service
│ │ ├── __init__.py
│ │ └── __main__.py
│ ├── poetry.lock
│ ├── pyproject.toml
│ └── tests
│ │ ├── __init__.py
│ │ └── test_message_service.py
├── name-service
│ ├── .flaskenv
│ ├── Dockerfile
│ ├── README.rst
│ ├── name_service
│ │ └── __init__.py
│ ├── poetry.lock
│ ├── pyproject.toml
│ └── tests
│ │ ├── __init__.py
│ │ └── test_name_service.py
└── year-service
│ ├── Dockerfile
│ ├── README.rst
│ ├── poetry.lock
│ ├── pyproject.toml
│ ├── tests
│ ├── __init__.py
│ └── test_year_service.py
│ └── yearservice
│ ├── manage.py
│ ├── yearapp
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── migrations
│ │ └── __init__.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
│ └── yearservice
│ ├── __init__.py
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── ruby
├── .editorconfig
├── .envrc_example
├── Tiltfile
├── docker-compose.yml
├── frontend
│ ├── .ruby-version
│ ├── Dockerfile
│ ├── Gemfile
│ ├── Gemfile.lock
│ ├── frontend.ru
│ └── o11y_wrapper.rb
├── message-service
│ ├── .ruby-version
│ ├── Dockerfile
│ ├── Gemfile
│ ├── Gemfile.lock
│ └── message.ru
├── name-service
│ ├── .ruby-version
│ ├── Dockerfile
│ ├── Gemfile
│ ├── Gemfile.lock
│ └── name.rb
└── year-service
│ ├── .ruby-version
│ ├── Dockerfile
│ ├── Gemfile
│ ├── Gemfile.lock
│ ├── config.ru
│ └── o11y_wrapper.rb
└── web
├── .eslintignore
├── .eslintrc.js
├── .prettierignore
├── .prettierrc.js
├── Dockerfile
├── README.md
├── package-lock.json
├── package.json
├── src
├── index.js
└── tracing-http.js
└── webpack.config.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: https://EditorConfig.org
2 |
3 | # When opening a file, EditorConfig plugins look for a file named .editorconfig
4 | # in the directory of the opened file and in every parent directory.
5 | # A search for .editorconfig files will stop if the root filepath is reached
6 | # or an EditorConfig file with root=true is found.
7 |
8 | # EditorConfig files are read top to bottom and the most recent rules found
9 | # take precedence. Properties from matching EditorConfig sections are applied
10 | # in the order they were read, so properties in closer files take precedence.
11 |
12 | # top-most EditorConfig file
13 | root = true
14 |
15 | [*]
16 |
17 | #### Core EditorConfig Options ####
18 |
19 | # Indentation and spacing
20 | indent_size = 4
21 | indent_style = space
22 | tab_width = 4
23 |
24 | # New line preferences
25 | end_of_line = lf
26 | insert_final_newline = true
27 |
28 | [*.md]
29 | indent_size = 2
30 | tab_width = 2
31 | insert_final_newline = true
32 |
33 | [.envrc]
34 | end_of_line = lf
35 |
36 | [.tool-versions]
37 | end_of_line = lf
--------------------------------------------------------------------------------
/.envrc_example:
--------------------------------------------------------------------------------
1 | export HONEYCOMB_API_KEY=
2 | export HONEYCOMB_API=https://api.honeycomb.io
3 | export HONEYCOMB_API_ENDPOINT=${HONEYCOMB_API}
4 | export OTEL_EXPORTER_OTLP_ENDPOINT=${HONEYCOMB_API}
5 | export OTEL_EXPORTER_OTLP_HEADERS="x-honeycomb-team=${HONEYCOMB_API_KEY}"
6 |
7 | # SERVICE_NAME set in Tiltfile, set in some code directly, but may still be needed for some services
8 |
9 | ##
10 | ## classic honeycomb ##
11 | ##
12 | # export HONEYCOMB_API_KEY=
13 | # export HONEYCOMB_DATASET=greetings
14 | # export HONEYCOMB_API=https://api.honeycomb.io
15 | # export HONEYCOMB_API_ENDPOINT=${HONEYCOMB_API}
16 | # export OTEL_EXPORTER_OTLP_ENDPOINT=${HONEYCOMB_API}
17 | # export OTEL_EXPORTER_OTLP_HEADERS="x-honeycomb-team=${HONEYCOMB_API_KEY},x-honeycomb-dataset=${HONEYCOMB_DATASET}"
18 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # Code owners file.
2 | # This file controls who is tagged for review for any given pull request.
3 |
4 | # For anything not explicitly taken by someone else:
5 | * @honeycombio/pipeline-team
6 |
7 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Let us know if something is not working as expected
4 | title: ''
5 | labels: 'type: bug'
6 | assignees: ''
7 |
8 | ---
9 |
10 |
17 |
18 | **Versions**
19 |
20 | - Example:
21 | - Runtime:
22 |
23 | **Steps to reproduce**
24 |
25 | 1.
26 |
27 | **Additional context**
28 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: 'type: enhancement'
6 | assignees: ''
7 |
8 | ---
9 |
10 |
15 |
16 | **Is your feature request related to a problem? Please describe.**
17 |
18 |
19 | **Describe the solution you'd like**
20 |
21 |
22 | **Describe alternatives you've considered**
23 |
24 |
25 | **Additional context**
26 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/question-discussion.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Question/Discussion
3 | about: General question about how things work or a discussion
4 | title: ''
5 | labels: 'type: discussion'
6 | assignees: ''
7 |
8 | ---
9 |
10 |
15 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/security-vulnerability-report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Security vulnerability report
3 | about: Let us know if you discover a security vulnerability
4 | title: ''
5 | labels: 'type: security'
6 | assignees: ''
7 |
8 | ---
9 |
10 |
15 | **Versions**
16 |
17 | - Example:
18 | - Runtime:
19 |
20 | **Description**
21 |
22 | (Please include any relevant CVE advisory links)
23 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
12 |
13 | ## Which problem is this PR solving?
14 |
15 | -
16 |
17 | ## Short description of the changes
18 |
19 | -
20 |
21 |
--------------------------------------------------------------------------------
/.github/release.yml:
--------------------------------------------------------------------------------
1 | # .github/release.yml
2 |
3 | changelog:
4 | exclude:
5 | labels:
6 | - no-changelog
7 | categories:
8 | - title: 💥 Breaking Changes 💥
9 | labels:
10 | - "version: bump major"
11 | - breaking-change
12 | - title: 💡 Enhancements
13 | labels:
14 | - "type: enhancement"
15 | - title: 🐛 Fixes
16 | labels:
17 | - "type: bug"
18 | - title: 🛠 Maintenance
19 | labels:
20 | - "type: maintenance"
21 | - title: 🤷 Other Changes
22 | labels:
23 | - "*"
--------------------------------------------------------------------------------
/.github/workflows/add-to-project-v2.yml:
--------------------------------------------------------------------------------
1 | name: Add to project
2 | on:
3 | issues:
4 | types: [opened]
5 | pull_request_target:
6 | types: [opened]
7 | jobs:
8 | add-to-project:
9 | runs-on: ubuntu-latest
10 | name: Add issues and PRs to project
11 | steps:
12 | - uses: actions/add-to-project@main
13 | with:
14 | project-url: https://github.com/orgs/honeycombio/projects/27
15 | github-token: ${{ secrets.GHPROJECTS_TOKEN }}
16 |
--------------------------------------------------------------------------------
/.github/workflows/apply-labels.yml:
--------------------------------------------------------------------------------
1 | name: Apply project labels
2 | on: [issues, pull_request_target, label]
3 | jobs:
4 | apply-labels:
5 | runs-on: ubuntu-latest
6 | name: Apply common project labels
7 | steps:
8 | - uses: honeycombio/oss-management-actions/labels@v1
9 | with:
10 | github-token: ${{ secrets.GITHUB_TOKEN }}
11 |
--------------------------------------------------------------------------------
/.github/workflows/publish-ghcr.yml:
--------------------------------------------------------------------------------
1 | name: Publish to GHCR
2 |
3 | on:
4 | workflow_dispatch:
5 | push:
6 | branches:
7 | - main
8 |
9 | jobs:
10 | build-and-publish-docker-image:
11 | runs-on: ubuntu-22.04
12 | permissions:
13 | contents: read
14 | packages: write
15 | strategy:
16 | matrix:
17 | service: [frontend, message, year, name]
18 | steps:
19 | - uses: actions/checkout@v1
20 |
21 | - name: Log in to the Container registry
22 | uses: docker/login-action@v2.1.0
23 | with:
24 | registry: ghcr.io
25 | username: ${{ github.repository_owner }}
26 | password: ${{ secrets.GITHUB_TOKEN }}
27 |
28 | - name: Set up QEMU
29 | uses: docker/setup-qemu-action@v2
30 |
31 | - name: Set up Docker Buildx
32 | uses: docker/setup-buildx-action@v2.5.0
33 |
34 | - name: Extract metadata (tags, labels) for Docker
35 | id: meta
36 | uses: docker/metadata-action@v4.4.0
37 | with:
38 | images: |
39 | ghcr.io/${{ github.repository }}/egs-${{ matrix.service }}-go
40 |
41 | - name: Build and push
42 | uses: docker/build-push-action@v4.0.0
43 | with:
44 | context: "{{ defaultContext }}:go-uninstrumented"
45 | file: ${{ matrix.service }}-service/Dockerfile
46 | tags: ghcr.io/${{ github.repository }}/egs-${{ matrix.service }}-go:dev
47 | push: true
48 | labels: ${{ steps.meta.outputs.labels }}
49 | platforms: linux/amd64,linux/arm64
50 |
--------------------------------------------------------------------------------
/.github/workflows/stale.yml:
--------------------------------------------------------------------------------
1 | name: 'Close stale issues and PRs'
2 | on:
3 | schedule:
4 | - cron: '30 1 * * *'
5 |
6 | jobs:
7 | stale:
8 | name: 'Close stale issues and PRs'
9 | runs-on: ubuntu-latest
10 | permissions:
11 | issues: write
12 | pull-requests: write
13 |
14 | steps:
15 | - uses: actions/stale@v4
16 | with:
17 | start-date: '2021-09-01T00:00:00Z'
18 | stale-issue-message: 'Marking this issue as stale because it has been open 14 days with no activity. Please add a comment if this is still an ongoing issue; otherwise this issue will be automatically closed in 7 days.'
19 | stale-pr-message: 'Marking this PR as stale because it has been open 30 days with no activity. Please add a comment if this PR is still relevant; otherwise this PR will be automatically closed in 7 days.'
20 | close-issue-message: 'Closing this issue due to inactivity. Please see our [Honeycomb OSS Lifecyle and Practices](https://github.com/honeycombio/home/blob/main/honeycomb-oss-lifecycle-and-practices.md).'
21 | close-pr-message: 'Closing this PR due to inactivity. Please see our [Honeycomb OSS Lifecyle and Practices](https://github.com/honeycombio/home/blob/main/honeycomb-oss-lifecycle-and-practices.md).'
22 | days-before-issue-stale: 14
23 | days-before-pr-stale: 30
24 | days-before-issue-close: 7
25 | days-before-pr-close: 7
26 | any-of-labels: 'status: info needed,status: revision needed'
27 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Code of Conduct
2 |
3 | This project has adopted the Honeycomb User Community Code of Conduct to clarify expected behavior in our community.
4 |
5 | https://www.honeycomb.io/honeycomb-user-community-code-of-conduct/
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing Guide
2 |
3 | Please see our [general guide for OSS lifecycle and practices.](https://github.com/honeycombio/home/blob/main/honeycomb-oss-lifecycle-and-practices.md)
4 |
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2016-Present Honeycomb, Hound Technology, Inc. All Rights Reserved.
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 |
--------------------------------------------------------------------------------
/OSSMETADATA:
--------------------------------------------------------------------------------
1 | osslifecycle=active
2 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | This security policy applies to public projects under the [honeycombio organization][gh-organization] on GitHub.
4 | For security reports involving the services provided at `(ui|ui-eu|api|api-eu).honeycomb.io`, refer to the [Honeycomb Bug Bounty Program][bugbounty] for scope, expectations, and reporting procedures.
5 |
6 | ## Security/Bugfix Versions
7 |
8 | Security and bug fixes are generally provided only for the last minor version.
9 | Fixes are released either as part of the next minor version or as an on-demand patch version.
10 |
11 | Security fixes are given priority and might be enough to cause a new version to be released.
12 |
13 | ## Reporting a Vulnerability
14 |
15 | We encourage responsible disclosure of security vulnerabilities.
16 | If you find something suspicious, we encourage and appreciate your report!
17 |
18 | ### Ways to report
19 |
20 | In order for the vulnerability reports to reach maintainers as soon as possible, the preferred way is to use the "Report a vulnerability" button under the "Security" tab of the associated GitHub project.
21 | This creates a private communication channel between the reporter and the maintainers.
22 |
23 | If you are absolutely unable to or have strong reasons not to use GitHub's vulnerability reporting workflow, please reach out to the Honeycomb security team at [security@honeycomb.io](mailto:security@honeycomb.io).
24 |
25 | [gh-organization]: https://github.com/honeycombio
26 | [bugbounty]: https://www.honeycomb.io/bugbountyprogram
27 |
--------------------------------------------------------------------------------
/SUPPORT.md:
--------------------------------------------------------------------------------
1 | # How to Get Help
2 |
3 | This project uses GitHub issues to track bugs, feature requests, and questions about using the project. Please search for existing issues before filing a new one.
4 |
--------------------------------------------------------------------------------
/docker-compose.dotnet.yml:
--------------------------------------------------------------------------------
1 | version: "2.4"
2 |
3 | # noinspection ComposeUnknownKeys
4 | x-common-env: &common-env
5 | HONEYCOMB_API_KEY:
6 | HONEYCOMB_DATASET:
7 | HONEYCOMB_API:
8 | OTEL_EXPORTER_OTLP_ENDPOINT:
9 | OTEL_EXPORTER_OTLP_HEADERS:
10 | OTEL_RESOURCE_ATTRIBUTES: app.running-in=docker
11 | Honeycomb__ApiKey: ${HONEYCOMB_API_KEY}
12 | Honeycomb__Dataset: ${HONEYCOMB_DATASET}
13 | Honeycomb__Endpoint: ${HONEYCOMB_API}
14 | Otlp__ApiKey: ${HONEYCOMB_API_KEY}
15 | Otlp__Dataset: ${HONEYCOMB_DATASET}
16 | Otlp__Endpoint: ${OTEL_EXPORTER_OTLP_ENDPOINT}
17 | MESSAGE_ENDPOINT: message-service:9000
18 | NAME_ENDPOINT: name-service:8000
19 | YEAR_ENDPOINT: year-service:6001
20 | REDIS_URL: redis
21 |
22 | services:
23 | frontend-dotnet:
24 | container_name: frontend-service
25 | build: ./dotnet/frontend
26 | image: hnyexample/frontend-dotnet
27 | environment:
28 | <<: *common-env
29 | ports:
30 | - 7777:7777
31 |
32 | message-dotnet:
33 | container_name: message-service
34 | build: ./dotnet/message-service
35 | image: hnyexample/message-dotnet
36 | environment:
37 | <<: *common-env
38 | ports:
39 | - 9000:9000
40 |
41 | name-dotnet:
42 | container_name: name-service
43 | build: ./dotnet/name-service
44 | image: hnyexample/name-dotnet
45 | environment:
46 | <<: *common-env
47 | ports:
48 | - 8000:8000
49 |
50 | year-dotnet:
51 | container_name: year-service
52 | build: ./dotnet/year-service
53 | image: hnyexample/year-dotnet
54 | environment:
55 | <<: *common-env
56 | ports:
57 | - 6001:6001
58 |
--------------------------------------------------------------------------------
/docker-compose.elixir.yml:
--------------------------------------------------------------------------------
1 | version: "2.4"
2 |
3 | # noinspection ComposeUnknownKeys
4 | x-common-env: &common-env
5 | HONEYCOMB_API_KEY:
6 | HONEYCOMB_DATASET:
7 | HONEYCOMB_API:
8 | OTEL_EXPORTER_OTLP_ENDPOINT:
9 | OTEL_EXPORTER_OTLP_HEADERS:
10 | OTEL_RESOURCE_ATTRIBUTES: app.running-in=docker
11 | MESSAGE_ENDPOINT: message-service:9000
12 | NAME_ENDPOINT: name-service:8000
13 | YEAR_ENDPOINT: year-service:6001
14 | REDIS_URL: redis
15 | OTEL_COLLECTOR_HOST: collector
16 |
17 | services:
18 | frontend-elixir:
19 | container_name: frontend-service
20 | build: ./elixir/frontend
21 | image: hnyexample/frontend-elixir
22 | environment:
23 | <<: *common-env
24 | OTEL_SERVICE_NAME: frontend-elixir
25 | ports:
26 | - 7777:7777
27 |
28 | message-elixir:
29 | container_name: message-service
30 | build: ./elixir/message
31 | image: hnyexample/message-elixir
32 | environment:
33 | <<: *common-env
34 | SERVICE_NAME: message-elixir
35 | ports:
36 | - 9000:9000
37 |
38 | name-elixir:
39 | container_name: name-service
40 | build: ./elixir/name
41 | image: hnyexample/name-elixir
42 | environment:
43 | <<: *common-env
44 | SERVICE_NAME: name-elixir
45 | ports:
46 | - 8000:8000
47 |
48 | year-elixir:
49 | container_name: year-service
50 | build: ./elixir/year
51 | image: hnyexample/year-elixir
52 | environment:
53 | <<: *common-env
54 | SERVICE_NAME: year-elixir
55 | ports:
56 | - 6001:6001
57 |
--------------------------------------------------------------------------------
/docker-compose.java.yml:
--------------------------------------------------------------------------------
1 | version: "2.4"
2 |
3 | # noinspection ComposeUnknownKeys
4 | x-common-env: &common-env
5 | HONEYCOMB_API_KEY:
6 | HONEYCOMB_DATASET:
7 | HONEYCOMB_API:
8 | OTEL_EXPORTER_OTLP_ENDPOINT:
9 | OTEL_LOGS_EXPORTER: otlp
10 | OTEL_EXPORTER_OTLP_HEADERS:
11 | OTEL_RESOURCE_ATTRIBUTES: app.running-in=docker
12 | MESSAGE_ENDPOINT: message-service:9000
13 | NAME_ENDPOINT: name-service:8000
14 | YEAR_ENDPOINT: year-service:6001
15 | REDIS_URL: redis
16 |
17 | services:
18 | frontend-java:
19 | container_name: frontend-service
20 | build: ./java/frontend
21 | image: hnyexample/frontend-java
22 | environment:
23 | <<: *common-env
24 | OTEL_SERVICE_NAME: frontend-java
25 | ports:
26 | - 7777:7777
27 |
28 | message-java:
29 | container_name: message-service
30 | build: ./java/message-service
31 | image: hnyexample/message-java
32 | environment:
33 | <<: *common-env
34 | SERVICE_NAME: message-java
35 | ports:
36 | - 9000:9000
37 |
38 | name-java:
39 | container_name: name-service
40 | build: ./java/name-service
41 | image: hnyexample/name-java
42 | environment:
43 | <<: *common-env
44 | SERVICE_NAME: name-java
45 | ports:
46 | - 8000:8000
47 |
48 | year-java:
49 | container_name: year-service
50 | build: ./java/year-service
51 | image: hnyexample/year-java
52 | environment:
53 | <<: *common-env
54 | OTEL_SERVICE_NAME: year-java
55 | ports:
56 | - 6001:6001
57 |
--------------------------------------------------------------------------------
/docker-compose.node.yml:
--------------------------------------------------------------------------------
1 | version: "2.4"
2 |
3 | x-common-env: &common-env
4 | HONEYCOMB_API_KEY: ${HONEYCOMB_API_KEY}
5 | OTEL_EXPORTER_OTLP_ENDPOINT: api.honeycomb.io
6 | OTEL_EXPORTER_OTLP_HEADERS: "x-honeycomb-team=${HONEYCOMB_API_KEY}"
7 | OTEL_RESOURCE_ATTRIBUTES: app.running-in=docker
8 | MESSAGE_ENDPOINT: http://message-service:9000
9 | NAME_ENDPOINT: http://name-service:8000
10 | YEAR_ENDPOINT: http://year-service:6001
11 | REDIS_URL: redis
12 |
13 | services:
14 | frontend-node:
15 | container_name: frontend-service
16 | build: ./node/frontend-service
17 | environment:
18 | <<: *common-env
19 | SERVICE_NAME: node-frontend-service
20 | ports:
21 | - 7777:7777
22 |
23 | message-node:
24 | container_name: message-service
25 | build: ./node/message-service
26 | environment:
27 | <<: *common-env
28 | SERVICE_NAME: node-message-service
29 | ports:
30 | - 9000:9000
31 |
32 | name-node:
33 | container_name: name-service
34 | build: ./node/name-service
35 | environment:
36 | <<: *common-env
37 | SERVICE_NAME: node-name-service
38 | ports:
39 | - 8000:8000
40 |
41 | year-node:
42 | container_name: year-service
43 | build: ./node/year-service
44 | environment:
45 | <<: *common-env
46 | OTEL_SERVICE_NAME: node-year-service
47 | ports:
48 | - 6001:6001
49 |
--------------------------------------------------------------------------------
/docker-compose.python.yml:
--------------------------------------------------------------------------------
1 | version: "2.4"
2 |
3 | # noinspection ComposeUnknownKeys
4 | x-common-env: &common-env
5 | HONEYCOMB_API_KEY:
6 | HONEYCOMB_DATASET:
7 | HONEYCOMB_API:
8 | OTEL_EXPORTER_OTLP_ENDPOINT:
9 | OTEL_EXPORTER_OTLP_HEADERS:
10 | OTEL_RESOURCE_ATTRIBUTES: app.running-in=docker
11 | MESSAGE_ENDPOINT: http://message-service:9000
12 | NAME_ENDPOINT: http://name-service:8000
13 | YEAR_ENDPOINT: http://year-service:6001
14 |
15 | services:
16 | frontend-python:
17 | container_name: frontend-service
18 | build: ./python/frontend
19 | image: hnyexample/frontend-python
20 | environment:
21 | <<: *common-env
22 | OTEL_SERVICE_NAME: frontend-python
23 | ports:
24 | - 7777:7777
25 |
26 | message-python:
27 | container_name: message-service
28 | build: ./python/message-service
29 | image: hnyexample/message-python
30 | environment:
31 | <<: *common-env
32 | OTEL_SERVICE_NAME: message-python
33 | ports:
34 | - 9000:9000
35 |
36 | name-python:
37 | container_name: name-service
38 | build: ./python/name-service
39 | image: hnyexample/name-python
40 | environment:
41 | <<: *common-env
42 | OTEL_SERVICE_NAME: name-python
43 | ports:
44 | - 8000:8000
45 |
46 | year-python:
47 | container_name: year-service
48 | build: ./python/year-service
49 | image: hnyexample/year-python
50 | environment:
51 | <<: *common-env
52 | OTEL_SERVICE_NAME: year-python
53 | ports:
54 | - 6001:6001
55 |
56 |
--------------------------------------------------------------------------------
/docker-compose.web.yml:
--------------------------------------------------------------------------------
1 | version: "2.4"
2 |
3 | x-common-env: &common-env
4 | OTEL_RESOURCE_ATTRIBUTES: app.running-in=docker
5 | REDIS_URL: redis
6 |
7 | services:
8 | web:
9 | container_name: web-app
10 | build: ./web
11 | environment:
12 | <<: *common-env
13 | SERVICE_NAME: web-app
14 | ports:
15 | - 8080:8080
16 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "2.4"
2 |
3 | services:
4 | collector:
5 | image: otel/opentelemetry-collector-contrib:0.81.0
6 | command: ["--config=/etc/otel-collector-config.yaml"]
7 | environment:
8 | - HONEYCOMB_API_KEY
9 | - HONEYCOMB_DATASET
10 | volumes:
11 | - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
12 | ports:
13 | - "127.0.0.1:55680:55680"
14 | - "127.0.0.1:55681:55681"
15 | redis:
16 | image: redis:latest
17 | ports:
18 | - "127.0.0.1:6379:6379"
19 |
--------------------------------------------------------------------------------
/dotnet/.gitignore:
--------------------------------------------------------------------------------
1 | bin/
2 | obj/
3 | .vscode
4 |
--------------------------------------------------------------------------------
/dotnet/README.md:
--------------------------------------------------------------------------------
1 | ## .NET greeting service(s)
2 |
3 | This is a .NET implementation of the example greeting service; 4 microservices that do some fancy greetin'.
4 |
5 | You'll note that instrumentation is done differently depending on the project:
6 |
7 | * `frontend` is a service built with .NET 6 that uses [Honeycomb.OpenTelemetry](https://www.nuget.org/packages/Honeycomb.OpenTelemetry), our distribution of the .NET OpenTelemetry SDK. Configuration is simpler when you use this package.
8 | * `message-service` is a service built with .NET 5 that uses the Honeycomb.OpenTelemetry distribution.
9 | * `year-service` is a service built as a minimal .NET 6 API that uses the standard OpenTelemetry libraries to configure instrumentation. It's more lines of code.
10 | * `name-service` is a service built with .NET 5 that uses the standard OpenTelemetry libraries.
11 |
12 | The goal here is to demonstrate that you can configure instrumentation in several ways with .NET but still send data to Honeycomb.
--------------------------------------------------------------------------------
/dotnet/dotnet.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "frontend", "frontend\frontend.csproj", "{A4B39423-DB5F-4B6A-A05C-A372D383BC9A}"
4 | EndProject
5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "message-service", "message-service\message-service.csproj", "{BEBB59BB-089B-472F-A303-2E7230C2D0F0}"
6 | EndProject
7 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "name-service", "name-service\name-service.csproj", "{7E3ED8AF-1156-4B1E-81D9-2D4D227ABDA5}"
8 | EndProject
9 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "year-service", "year-service\year-service.csproj", "{928D8767-4DC8-4CCC-AE79-D4D3F943FF48}"
10 | EndProject
11 | Global
12 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
13 | Debug|Any CPU = Debug|Any CPU
14 | Release|Any CPU = Release|Any CPU
15 | EndGlobalSection
16 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
17 | {A4B39423-DB5F-4B6A-A05C-A372D383BC9A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
18 | {A4B39423-DB5F-4B6A-A05C-A372D383BC9A}.Debug|Any CPU.Build.0 = Debug|Any CPU
19 | {A4B39423-DB5F-4B6A-A05C-A372D383BC9A}.Release|Any CPU.ActiveCfg = Release|Any CPU
20 | {A4B39423-DB5F-4B6A-A05C-A372D383BC9A}.Release|Any CPU.Build.0 = Release|Any CPU
21 | {BEBB59BB-089B-472F-A303-2E7230C2D0F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
22 | {BEBB59BB-089B-472F-A303-2E7230C2D0F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
23 | {BEBB59BB-089B-472F-A303-2E7230C2D0F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
24 | {BEBB59BB-089B-472F-A303-2E7230C2D0F0}.Release|Any CPU.Build.0 = Release|Any CPU
25 | {7E3ED8AF-1156-4B1E-81D9-2D4D227ABDA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
26 | {7E3ED8AF-1156-4B1E-81D9-2D4D227ABDA5}.Debug|Any CPU.Build.0 = Debug|Any CPU
27 | {7E3ED8AF-1156-4B1E-81D9-2D4D227ABDA5}.Release|Any CPU.ActiveCfg = Release|Any CPU
28 | {7E3ED8AF-1156-4B1E-81D9-2D4D227ABDA5}.Release|Any CPU.Build.0 = Release|Any CPU
29 | {928D8767-4DC8-4CCC-AE79-D4D3F943FF48}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
30 | {928D8767-4DC8-4CCC-AE79-D4D3F943FF48}.Debug|Any CPU.Build.0 = Debug|Any CPU
31 | {928D8767-4DC8-4CCC-AE79-D4D3F943FF48}.Release|Any CPU.ActiveCfg = Release|Any CPU
32 | {928D8767-4DC8-4CCC-AE79-D4D3F943FF48}.Release|Any CPU.Build.0 = Release|Any CPU
33 | EndGlobalSection
34 | EndGlobal
35 |
--------------------------------------------------------------------------------
/dotnet/frontend/Dockerfile:
--------------------------------------------------------------------------------
1 | # https://hub.docker.com/_/microsoft-dotnet
2 | FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
3 | WORKDIR /source
4 |
5 | # copy csproj and restore as distinct layers
6 | COPY *.cs ./
7 | COPY *.csproj ./
8 | COPY *.json ./
9 | COPY ./Properties ./Properties
10 | COPY ./Controllers ./Controllers
11 | RUN dotnet restore
12 |
13 | # copy everything else and build app
14 | RUN dotnet publish -c release -o /app --no-restore
15 |
16 | # final stage/image
17 | FROM mcr.microsoft.com/dotnet/aspnet:6.0
18 | WORKDIR /app
19 | COPY --from=build /app ./
20 |
21 | ENV ASPNETCORE_URLS=http://+:7777
22 | ENV ASPNETCORE_ENVIRONMENT="development"
23 | EXPOSE 7777
24 | ENTRYPOINT ["dotnet", "frontend.dll"]
25 |
--------------------------------------------------------------------------------
/dotnet/frontend/Program.cs:
--------------------------------------------------------------------------------
1 | using OpenTelemetry.Trace;
2 |
3 | var builder = WebApplication.CreateBuilder(args);
4 |
5 | // Add services to the container.
6 |
7 | builder.Services.AddControllers();
8 | builder.Services.AddHttpClient();
9 | var honeycombOptions = builder.Configuration.GetHoneycombOptions();
10 | builder.Services.AddOpenTelemetry().WithTracing(otelBuilder =>
11 | {
12 | otelBuilder
13 | .AddHoneycomb(honeycombOptions)
14 | .AddCommonInstrumentations()
15 | .AddAspNetCoreInstrumentationWithBaggage();
16 | });
17 | builder.Services.AddSingleton(TracerProvider.Default.GetTracer(honeycombOptions.ServiceName));
18 |
19 | // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
20 | builder.Services.AddEndpointsApiExplorer();
21 | builder.Services.AddSwaggerGen();
22 |
23 | var app = builder.Build();
24 |
25 | // Configure the HTTP request pipeline.
26 | if (app.Environment.IsDevelopment())
27 | {
28 | app.UseSwagger();
29 | app.UseSwaggerUI();
30 | }
31 |
32 | app.UseHttpsRedirection();
33 |
34 | app.UseAuthorization();
35 |
36 | app.MapControllers();
37 |
38 | app.Run();
39 |
--------------------------------------------------------------------------------
/dotnet/frontend/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/launchsettings.json",
3 | "iisSettings": {
4 | "windowsAuthentication": false,
5 | "anonymousAuthentication": true,
6 | "iisExpress": {
7 | "applicationUrl": "http://localhost:36268",
8 | "sslPort": 44358
9 | }
10 | },
11 | "profiles": {
12 | "IIS Express": {
13 | "commandName": "IISExpress",
14 | "launchBrowser": true,
15 | "launchUrl": "swagger",
16 | "environmentVariables": {
17 | "ASPNETCORE_ENVIRONMENT": "Development"
18 | }
19 | },
20 | "frontend": {
21 | "commandName": "Project",
22 | "dotnetRunMessages": "true",
23 | "applicationUrl": "http://localhost:7777",
24 | "environmentVariables": {
25 | "ASPNETCORE_ENVIRONMENT": "Development"
26 | }
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/dotnet/frontend/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/dotnet/frontend/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "AllowedHosts": "*",
10 | "Honeycomb": {
11 | "ServiceName": "frontend-dotnet",
12 | "Endpoint": "https://api.honeycomb.io",
13 | "ApiKey": "",
14 | "Dataset": "greetings"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/dotnet/frontend/frontend.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | enable
6 | enable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/dotnet/global.json:
--------------------------------------------------------------------------------
1 | {
2 | "sdk": {
3 | "version": "6.0",
4 | "rollForward": "latestMajor",
5 | "allowPrerelease": true
6 | }
7 | }
--------------------------------------------------------------------------------
/dotnet/message-service/Controllers/MessageController.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Mvc;
2 | using OpenTelemetry.Trace;
3 | using System.Threading.Tasks;
4 |
5 | namespace message_service.Controllers
6 | {
7 | using StackExchange.Redis;
8 |
9 | [Route("[controller]")]
10 | [ApiController]
11 | public class MessageController : ControllerBase
12 | {
13 | private readonly Tracer _tracer;
14 | private readonly IConnectionMultiplexer _redisConnection;
15 |
16 | public MessageController(Tracer tracer, IConnectionMultiplexer redisConnection)
17 | {
18 | _tracer = tracer;
19 | _redisConnection = redisConnection;
20 | }
21 |
22 | [HttpGet]
23 | public async Task GetAsync()
24 | {
25 | var message = await DetermineMessage();
26 | var currentSpan = Tracer.CurrentSpan;
27 | currentSpan.SetAttribute("app.message", message);
28 | return message;
29 | }
30 |
31 | private async Task DetermineMessage()
32 | {
33 | using var span = _tracer.StartActiveSpan("📖 look up message ✨");
34 | var db = _redisConnection.GetDatabase();
35 | var message = "generic hello";
36 | var result = await db.SetRandomMemberAsync("messages");
37 | if (result.IsNull)
38 | {
39 | span.AddEvent("message was empty from redis, using default");
40 | }
41 | else
42 | {
43 | message = result.ToString();
44 | }
45 | span.SetAttribute("app.message", message);
46 | return message;
47 | }
48 | }
49 | }
--------------------------------------------------------------------------------
/dotnet/message-service/Dockerfile:
--------------------------------------------------------------------------------
1 | # https://hub.docker.com/_/microsoft-dotnet
2 | FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
3 | WORKDIR /source
4 |
5 | # copy csproj and restore as distinct layers
6 | COPY *.cs ./
7 | COPY *.csproj ./
8 | COPY *.json ./
9 | COPY ./Properties ./Properties
10 | COPY ./Controllers ./Controllers
11 | RUN dotnet restore
12 |
13 | # copy everything else and build app
14 | RUN dotnet publish -c release -o /app --no-restore
15 |
16 | # final stage/image
17 | FROM mcr.microsoft.com/dotnet/aspnet:5.0
18 | WORKDIR /app
19 | COPY --from=build /app ./
20 |
21 | ENV ASPNETCORE_URLS=http://+:9000
22 | ENV ASPNETCORE_ENVIRONMENT="development"
23 | EXPOSE 9000
24 | ENTRYPOINT ["dotnet", "message-service.dll"]
25 |
--------------------------------------------------------------------------------
/dotnet/message-service/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Hosting;
2 | using Microsoft.Extensions.Hosting;
3 |
4 | namespace message_service
5 | {
6 | public class Program
7 | {
8 | public static void Main(string[] args)
9 | {
10 | CreateHostBuilder(args).Build().Run();
11 | }
12 |
13 | public static IHostBuilder CreateHostBuilder(string[] args) =>
14 | Host.CreateDefaultBuilder(args)
15 | .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); });
16 | }
17 | }
--------------------------------------------------------------------------------
/dotnet/message-service/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/launchsettings.json",
3 | "iisSettings": {
4 | "windowsAuthentication": false,
5 | "anonymousAuthentication": true,
6 | "iisExpress": {
7 | "applicationUrl": "http://localhost:36268",
8 | "sslPort": 44358
9 | }
10 | },
11 | "profiles": {
12 | "IIS Express": {
13 | "commandName": "IISExpress",
14 | "launchBrowser": true,
15 | "launchUrl": "swagger",
16 | "environmentVariables": {
17 | "ASPNETCORE_ENVIRONMENT": "Development"
18 | }
19 | },
20 | "message_service": {
21 | "commandName": "Project",
22 | "dotnetRunMessages": "true",
23 | "applicationUrl": "http://localhost:9000",
24 | "environmentVariables": {
25 | "ASPNETCORE_ENVIRONMENT": "Development"
26 | }
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/dotnet/message-service/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/dotnet/message-service/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "AllowedHosts": "*",
10 | "Honeycomb": {
11 | "ServiceName": "message-dotnet",
12 | "Endpoint": "https://api.honeycomb.io",
13 | "ApiKey": "",
14 | "Dataset": "greetings"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/dotnet/message-service/message-service.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net5.0
5 | message_service
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/dotnet/name-service/Dockerfile:
--------------------------------------------------------------------------------
1 | # https://hub.docker.com/_/microsoft-dotnet
2 | FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
3 | WORKDIR /source
4 |
5 | # copy csproj and restore as distinct layers
6 | COPY *.cs ./
7 | COPY *.csproj ./
8 | COPY *.json ./
9 | COPY ./Properties ./Properties
10 | COPY ./Controllers ./Controllers
11 | RUN dotnet restore
12 |
13 | # copy everything else and build app
14 | RUN dotnet publish -c release -o /app --no-restore
15 |
16 | # final stage/image
17 | FROM mcr.microsoft.com/dotnet/aspnet:5.0
18 | WORKDIR /app
19 | COPY --from=build /app ./
20 |
21 | ENV ASPNETCORE_URLS=http://+:8000
22 | ENV ASPNETCORE_ENVIRONMENT="development"
23 | EXPOSE 8000
24 | ENTRYPOINT ["dotnet", "name-service.dll"]
25 |
--------------------------------------------------------------------------------
/dotnet/name-service/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Hosting;
2 | using Microsoft.Extensions.Hosting;
3 |
4 | namespace name_service
5 | {
6 | public class Program
7 | {
8 | public static void Main(string[] args)
9 | {
10 | CreateHostBuilder(args).Build().Run();
11 | }
12 |
13 | public static IHostBuilder CreateHostBuilder(string[] args) =>
14 | Host.CreateDefaultBuilder(args)
15 | .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); });
16 | }
17 | }
--------------------------------------------------------------------------------
/dotnet/name-service/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/launchsettings.json",
3 | "iisSettings": {
4 | "windowsAuthentication": false,
5 | "anonymousAuthentication": true,
6 | "iisExpress": {
7 | "applicationUrl": "http://localhost:36268",
8 | "sslPort": 44358
9 | }
10 | },
11 | "profiles": {
12 | "IIS Express": {
13 | "commandName": "IISExpress",
14 | "launchBrowser": true,
15 | "launchUrl": "swagger",
16 | "environmentVariables": {
17 | "ASPNETCORE_ENVIRONMENT": "Development"
18 | }
19 | },
20 | "name_service": {
21 | "commandName": "Project",
22 | "dotnetRunMessages": "true",
23 | "applicationUrl": "http://localhost:8000",
24 | "environmentVariables": {
25 | "ASPNETCORE_ENVIRONMENT": "Development"
26 | }
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/dotnet/name-service/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/dotnet/name-service/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "AllowedHosts": "*",
10 | "Otlp": {
11 | "ServiceName": "name-dotnet",
12 | "Endpoint": "https://api.honeycomb.io",
13 | "ApiKey": "",
14 | "Dataset": "greetings"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/dotnet/name-service/name-service.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net5.0
5 | name_service
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/dotnet/year-service/Dockerfile:
--------------------------------------------------------------------------------
1 | # https://hub.docker.com/_/microsoft-dotnet
2 | FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
3 | WORKDIR /source
4 |
5 | # copy csproj and restore as distinct layers
6 | COPY *.cs ./
7 | COPY *.csproj ./
8 | COPY *.json ./
9 | COPY ./Properties ./Properties
10 | RUN dotnet restore
11 |
12 | # copy everything else and build app
13 | RUN dotnet publish -c release -o /app --no-restore
14 |
15 | # final stage/image
16 | FROM mcr.microsoft.com/dotnet/aspnet:6.0
17 | WORKDIR /app
18 | COPY --from=build /app ./
19 |
20 | ENV ASPNETCORE_URLS=http://+:6001
21 | ENV ASPNETCORE_ENVIRONMENT="development"
22 | EXPOSE 6001
23 | ENTRYPOINT ["dotnet", "year-service.dll"]
24 |
--------------------------------------------------------------------------------
/dotnet/year-service/Program.cs:
--------------------------------------------------------------------------------
1 | using OpenTelemetry.Resources;
2 | using OpenTelemetry.Trace;
3 | using OpenTelemetry.Logs;
4 | using OpenTelemetry.Exporter;
5 |
6 | var builder = WebApplication.CreateBuilder(args);
7 |
8 | const string telemetrySourceName = "honeycomb.examples.year-service-dotnet";
9 |
10 | var resourceBuilder = ResourceBuilder.CreateDefault()
11 | .AddService(builder.Configuration.GetValue("Otlp:ServiceName"))
12 | .AddEnvironmentVariableDetector();
13 |
14 | var configureOtlpExporter = (OtlpExporterOptions options) =>
15 | {
16 | options.Endpoint = new Uri(builder.Configuration.GetValue("Otlp:Endpoint"));
17 | var apiKey = builder.Configuration.GetValue("Otlp:ApiKey");
18 | var dataset = builder.Configuration.GetValue("Otlp:Dataset");
19 | options.Headers = $"x-honeycomb-team={apiKey},x-honeycomb-dataset={dataset}";
20 | };
21 |
22 | builder.Services.AddOpenTelemetry()
23 | .WithTracing(options => options
24 | .SetResourceBuilder(resourceBuilder)
25 | .AddSource(telemetrySourceName)
26 | .AddAspNetCoreInstrumentation()
27 | .AddHttpClientInstrumentation()
28 | .AddOtlpExporter(configureOtlpExporter));
29 |
30 | builder.Logging.AddOpenTelemetry(options => options
31 | .SetResourceBuilder(resourceBuilder)
32 | .AddOtlpExporter(configureOtlpExporter)
33 | );
34 |
35 | Tracer tracer = TracerProvider.Default.GetTracer(telemetrySourceName);
36 | var app = builder.Build();
37 | int[] years = { 2015, 2016, 2017, 2018, 2019, 2020 };
38 |
39 | app.MapGet("/year", () =>
40 | {
41 | using var span = tracer.StartActiveSpan("🗓 get-a-year ✨");
42 | span.SetAttribute("banana", 1);
43 | var rng = new Random();
44 | var i = rng.Next(years.Length - 1);
45 | var year = years[i];
46 | app.Logger.LogInformation($"Selected year: ${year}");
47 | return year;
48 | });
49 |
50 | app.Run();
51 |
--------------------------------------------------------------------------------
/dotnet/year-service/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/launchsettings.json",
3 | "iisSettings": {
4 | "windowsAuthentication": false,
5 | "anonymousAuthentication": true,
6 | "iisExpress": {
7 | "applicationUrl": "http://localhost:36268",
8 | "sslPort": 44358
9 | }
10 | },
11 | "profiles": {
12 | "IIS Express": {
13 | "commandName": "IISExpress",
14 | "launchBrowser": true,
15 | "launchUrl": "swagger",
16 | "environmentVariables": {
17 | "ASPNETCORE_ENVIRONMENT": "Development"
18 | }
19 | },
20 | "year_service": {
21 | "commandName": "Project",
22 | "dotnetRunMessages": "true",
23 | "launchUrl": "api/year",
24 | "applicationUrl": "http://localhost:6001",
25 | "environmentVariables": {
26 | "ASPNETCORE_ENVIRONMENT": "Development"
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/dotnet/year-service/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/dotnet/year-service/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "AllowedHosts": "*",
10 | "Otlp": {
11 | "ServiceName": "year-dotnet",
12 | "Endpoint": "https://api.honeycomb.io",
13 | "ApiKey": "",
14 | "Dataset": "greetings"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/dotnet/year-service/year-service.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | enable
6 | enable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/elixir/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: https://EditorConfig.org
2 |
3 | # When opening a file, EditorConfig plugins look for a file named .editorconfig
4 | # in the directory of the opened file and in every parent directory.
5 | # A search for .editorconfig files will stop if the root filepath is reached
6 | # or an EditorConfig file with root=true is found.
7 |
8 | # EditorConfig files are read top to bottom and the most recent rules found
9 | # take precedence. Properties from matching EditorConfig sections are applied
10 | # in the order they were read, so properties in closer files take precedence.
11 |
12 | # https://gist.github.com/eksperimental/6a43b9a0ab8d718f5886e7025062fcbd
13 |
14 | # Elixir files
15 | [*.eex, *.exs]
16 |
17 | indent_size = 2
18 | end_of_line = lf
19 | charset = utf-8
20 | trim_trailing_whitespace = true
21 | insert_final_newline = true
22 |
23 | [Makefile]
24 | indent_style = tab
--------------------------------------------------------------------------------
/elixir/.tool-versions:
--------------------------------------------------------------------------------
1 | elixir 1.14.3
2 | erlang 25.3
3 |
--------------------------------------------------------------------------------
/elixir/README.md:
--------------------------------------------------------------------------------
1 | # Elixir Greeting Services
2 |
3 | ## Services
4 | - Frontend: Service that calls the message, name and year services to create a greeting message served at `localhost:7777/greeting`.
5 | - Message: Service that chooses a random greeting message.
6 | - Name: Service that chooses a random name.
7 | - Year: Service that returns a random year.
8 |
9 | ## How to run without Tilt/Docker
10 |
11 | See top-level README.md for instructions on how to run services with Tilt and Docker (easiest).
12 |
13 | ### Install Elixir
14 | If using option 1 or 3 below, make sure you have Elixir installed. The preferred way to install Elixir is with [asdf](https://asdf-vm.com/guide/getting-started.html#_1-install-dependencies).
15 |
16 | Once you have asdf set up, run the following to install Elixir & Erlang. Check the version needed in the `.tool-versions` file in the root of this repo.
17 |
18 | ```shell
19 | asdf plugin add erlang https://github.com/asdf-vm/asdf-erlang.git
20 | ```
21 |
22 | ```shell
23 | asdf plugin-add elixir https://github.com/asdf-vm/asdf-elixir.git
24 | ```
25 |
26 | ```shell
27 | asdf install erlang
28 | asdf install elixir
29 | ```
30 |
31 | ### Run each service
32 |
33 | Install dependencies in the service directory
34 |
35 | ```shell
36 | mix local.hex --force && mix local.rebar --force && mix deps.get && mix deps.compile
37 | ```
38 |
39 | In each service directory, run the mix command that starts the service.
40 |
41 | Frontend:
42 |
43 | ```shell
44 | mix phx.server
45 | ```
46 |
47 | Message/Name/Year:
48 |
49 | ```shell
50 | mix run --no-halt
51 | ```
52 |
53 | ## See it in action
54 |
55 | `curl localhost:7777/greeting` for greeting
56 |
57 | `curl localhost:9000/message` for message only
58 |
59 | `curl localhost:8000/name` for name only
60 |
61 | `curl localhost:6001` for year only
62 |
63 | ## Troubleshooting
64 |
65 | ### Port in use error
66 |
67 | If an error comes up about a port in use, either tilt or docker might still be running in the background.
68 |
69 | To shut down docker services run `docker-compose down`
70 |
71 | To shut down tilt services run `tilt down`
72 |
--------------------------------------------------------------------------------
/elixir/frontend/.formatter.exs:
--------------------------------------------------------------------------------
1 | [
2 | import_deps: [:phoenix],
3 | inputs: ["*.{ex,exs}", "{config,lib,test}/**/*.{ex,exs}"]
4 | ]
5 |
--------------------------------------------------------------------------------
/elixir/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | # The directory Mix will write compiled artifacts to.
2 | /_build/
3 |
4 | # If you run "mix test --cover", coverage assets end up here.
5 | /cover/
6 |
7 | # The directory Mix downloads your dependencies sources to.
8 | /deps/
9 |
10 | # Where 3rd-party dependencies like ExDoc output generated docs.
11 | /doc/
12 |
13 | # Ignore .fetch files in case you like to edit your project deps locally.
14 | /.fetch
15 |
16 | # If the VM crashes, it generates a dump, let's ignore it too.
17 | erl_crash.dump
18 |
19 | # Also ignore archive artifacts (built via "mix archive.build").
20 | *.ez
21 |
22 | # Ignore package tarball (built via "mix hex.build").
23 | frontend-*.tar
24 |
25 | # Since we are building assets from assets/,
26 | # we ignore priv/static. You may want to comment
27 | # this depending on your deployment strategy.
28 | /priv/static/
29 |
--------------------------------------------------------------------------------
/elixir/frontend/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM elixir:1.14
2 | WORKDIR /app
3 | COPY mix.* /app
4 | COPY lib /app/lib
5 | COPY config /app/config
6 | RUN mix local.hex --force
7 | RUN mix local.rebar --force
8 | RUN mix deps.get
9 | RUN mix deps.compile
10 |
11 | EXPOSE 7777
12 | CMD mix phx.server
13 |
--------------------------------------------------------------------------------
/elixir/frontend/config/config.exs:
--------------------------------------------------------------------------------
1 | # This file is responsible for configuring your application
2 | # and its dependencies with the aid of the Mix.Config module.
3 | #
4 | # This configuration file is loaded before any dependency and
5 | # is restricted to this project.
6 |
7 | # General application configuration
8 | import Config
9 |
10 | # Configures the endpoint
11 | config :frontend, FrontendWeb.Endpoint,
12 | url: [host: "localhost"],
13 | secret_key_base: "28gPO3Atbcv0ZX9ATXD6/prb3MOwl6DHGGbHWP8jEIzP4xV4/GPftCS+BtGqgO7n",
14 | render_errors: [view: FrontendWeb.ErrorView, accepts: ~w(json), layout: false],
15 | pubsub_server: Frontend.PubSub,
16 | live_view: [signing_salt: "RCSHpWzo"]
17 |
18 | # Configures Elixir's Logger
19 | config :logger, :console,
20 | format: "$time $metadata[$level] $message\n",
21 | metadata: [:request_id]
22 |
23 | # Use Jason for JSON parsing in Phoenix
24 | config :phoenix, :json_library, Jason
25 |
26 | # Configures OpenTelemetry
27 | config :opentelemetry, :resource, service: [name: "frontend-elixir"]
28 | config :opentelemetry, :propagators, :otel_propagator_http_w3c
29 |
30 | config :opentelemetry, :processors,
31 | otel_batch_processor: %{
32 | exporter: {
33 | :opentelemetry_exporter,
34 | %{
35 | endpoints: [
36 | {
37 | :http,
38 | System.get_env("OTEL_COLLECTOR_HOST", "localhost"),
39 | System.get_env("OTEL_COLLECTOR_PORT", "55681") |> String.to_integer(),
40 | []
41 | }
42 | ]
43 | }
44 | }
45 | }
46 |
47 | # Import environment specific config. This must remain at the bottom
48 | # of this file so it overrides the configuration defined above.
49 | import_config "#{Mix.env()}.exs"
50 |
--------------------------------------------------------------------------------
/elixir/frontend/config/dev.exs:
--------------------------------------------------------------------------------
1 | import Config
2 |
3 | # For development, we disable any cache and enable
4 | # debugging and code reloading.
5 | #
6 | # The watchers configuration can be used to run external
7 | # watchers to your application. For example, we use it
8 | # with webpack to recompile .js and .css sources.
9 | config :frontend, FrontendWeb.Endpoint,
10 | http: [port: 7777],
11 | debug_errors: true,
12 | code_reloader: true,
13 | check_origin: false,
14 | watchers: []
15 |
16 | # ## SSL Support
17 | #
18 | # In order to use HTTPS in development, a self-signed
19 | # certificate can be generated by running the following
20 | # Mix task:
21 | #
22 | # mix phx.gen.cert
23 | #
24 | # Note that this task requires Erlang/OTP 20 or later.
25 | # Run `mix help phx.gen.cert` for more information.
26 | #
27 | # The `http:` config above can be replaced with:
28 | #
29 | # https: [
30 | # port: 4001,
31 | # cipher_suite: :strong,
32 | # keyfile: "priv/cert/selfsigned_key.pem",
33 | # certfile: "priv/cert/selfsigned.pem"
34 | # ],
35 | #
36 | # If desired, both `http:` and `https:` keys can be
37 | # configured to run both http and https servers on
38 | # different ports.
39 |
40 | # Do not include metadata nor timestamps in development logs
41 | config :logger, :console, format: "[$level] $message\n"
42 |
43 | # Set a higher stacktrace during development. Avoid configuring such
44 | # in production as building large stacktraces may be expensive.
45 | config :phoenix, :stacktrace_depth, 20
46 |
47 | # Initialize plugs at runtime for faster development compilation
48 | config :phoenix, :plug_init_mode, :runtime
49 |
--------------------------------------------------------------------------------
/elixir/frontend/config/prod.exs:
--------------------------------------------------------------------------------
1 | import Config
2 |
3 | # For production, don't forget to configure the url host
4 | # to something meaningful, Phoenix uses this information
5 | # when generating URLs.
6 | #
7 | # Note we also include the path to a cache manifest
8 | # containing the digested version of static files. This
9 | # manifest is generated by the `mix phx.digest` task,
10 | # which you should run after static files are built and
11 | # before starting your production server.
12 | config :frontend, FrontendWeb.Endpoint,
13 | url: [host: "example.com", port: 80],
14 | cache_static_manifest: "priv/static/cache_manifest.json"
15 |
16 | # Do not print debug messages in production
17 | config :logger, level: :info
18 |
19 | # ## SSL Support
20 | #
21 | # To get SSL working, you will need to add the `https` key
22 | # to the previous section and set your `:url` port to 443:
23 | #
24 | # config :frontend, FrontendWeb.Endpoint,
25 | # ...
26 | # url: [host: "example.com", port: 443],
27 | # https: [
28 | # port: 443,
29 | # cipher_suite: :strong,
30 | # keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"),
31 | # certfile: System.get_env("SOME_APP_SSL_CERT_PATH"),
32 | # transport_options: [socket_opts: [:inet6]]
33 | # ]
34 | #
35 | # The `cipher_suite` is set to `:strong` to support only the
36 | # latest and more secure SSL ciphers. This means old browsers
37 | # and clients may not be supported. You can set it to
38 | # `:compatible` for wider support.
39 | #
40 | # `:keyfile` and `:certfile` expect an absolute path to the key
41 | # and cert in disk or a relative path inside priv, for example
42 | # "priv/ssl/server.key". For all supported SSL configuration
43 | # options, see https://hexdocs.pm/plug/Plug.SSL.html#configure/1
44 | #
45 | # We also recommend setting `force_ssl` in your endpoint, ensuring
46 | # no data is ever sent via http, always redirecting to https:
47 | #
48 | # config :frontend, FrontendWeb.Endpoint,
49 | # force_ssl: [hsts: true]
50 | #
51 | # Check `Plug.SSL` for all available options in `force_ssl`.
52 |
53 | # Finally import the config/prod.secret.exs which loads secrets
54 | # and configuration from environment variables.
55 | import_config "prod.secret.exs"
56 |
--------------------------------------------------------------------------------
/elixir/frontend/config/prod.secret.exs:
--------------------------------------------------------------------------------
1 | # In this file, we load production configuration and secrets
2 | # from environment variables. You can also hardcode secrets,
3 | # although such is generally not recommended and you have to
4 | # remember to add this file to your .gitignore.
5 | import Config
6 |
7 | secret_key_base =
8 | System.get_env("SECRET_KEY_BASE") ||
9 | raise """
10 | environment variable SECRET_KEY_BASE is missing.
11 | You can generate one by calling: mix phx.gen.secret
12 | """
13 |
14 | config :frontend, FrontendWeb.Endpoint,
15 | http: [
16 | port: String.to_integer(System.get_env("PORT") || "7777"),
17 | transport_options: [socket_opts: [:inet6]]
18 | ],
19 | secret_key_base: secret_key_base
20 |
21 | # ## Using releases (Elixir v1.9+)
22 | #
23 | # If you are doing OTP releases, you need to instruct Phoenix
24 | # to start each relevant endpoint:
25 | #
26 | # config :frontend, FrontendWeb.Endpoint, server: true
27 | #
28 | # Then you can assemble a release by calling `mix release`.
29 | # See `mix help release` for more information.
30 |
--------------------------------------------------------------------------------
/elixir/frontend/config/test.exs:
--------------------------------------------------------------------------------
1 | import Config
2 |
3 | # We don't run a server during test. If one is required,
4 | # you can enable the server option below.
5 | config :frontend, FrontendWeb.Endpoint,
6 | http: [port: 4002],
7 | server: false
8 |
9 | # Print only warnings and errors during test
10 | config :logger, level: :warn
11 |
--------------------------------------------------------------------------------
/elixir/frontend/lib/frontend.ex:
--------------------------------------------------------------------------------
1 | defmodule Frontend do
2 | require OpenTelemetry.Tracer, as: Tracer
3 |
4 | def name do
5 | Tracer.with_span "✨ call /name ✨" do
6 | endpoint = System.get_env("NAME_ENDPOINT", "http://localhost:8000")
7 | headers = :otel_propagator_text_map.inject([])
8 | HTTPoison.get!("#{endpoint}/name", headers).body
9 | end
10 | end
11 |
12 | def message do
13 | Tracer.with_span "✨ call /message ✨" do
14 | endpoint = System.get_env("MESSAGE_ENDPOINT", "http://localhost:9000")
15 | headers = :otel_propagator_text_map.inject([])
16 | HTTPoison.get!("#{endpoint}/message", headers).body
17 | end
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/elixir/frontend/lib/frontend/application.ex:
--------------------------------------------------------------------------------
1 | defmodule Frontend.Application do
2 | # See https://hexdocs.pm/elixir/Application.html
3 | # for more information on OTP Applications
4 | @moduledoc false
5 |
6 | use Application
7 |
8 | def start(_type, _args) do
9 | children = [
10 | # Start the Telemetry supervisor
11 | FrontendWeb.Telemetry,
12 | # Start the PubSub system
13 | {Phoenix.PubSub, name: Frontend.PubSub},
14 | # Start the Endpoint (http/https)
15 | FrontendWeb.Endpoint
16 | # Start a worker by calling: Frontend.Worker.start_link(arg)
17 | # {Frontend.Worker, arg}
18 | ]
19 |
20 | OpentelemetryPhoenix.setup()
21 |
22 | # See https://hexdocs.pm/elixir/Supervisor.html
23 | # for other strategies and supported options
24 | opts = [strategy: :one_for_one, name: Frontend.Supervisor]
25 | Supervisor.start_link(children, opts)
26 | end
27 |
28 | # Tell Phoenix to update the endpoint configuration
29 | # whenever the application is updated.
30 | def config_change(changed, _new, removed) do
31 | FrontendWeb.Endpoint.config_change(changed, removed)
32 | :ok
33 | end
34 | end
35 |
--------------------------------------------------------------------------------
/elixir/frontend/lib/frontend_web.ex:
--------------------------------------------------------------------------------
1 | defmodule FrontendWeb do
2 | @moduledoc """
3 | The entrypoint for defining your web interface, such
4 | as controllers, views, channels and so on.
5 |
6 | This can be used in your application as:
7 |
8 | use FrontendWeb, :controller
9 | use FrontendWeb, :view
10 |
11 | The definitions below will be executed for every view,
12 | controller, etc, so keep them short and clean, focused
13 | on imports, uses and aliases.
14 |
15 | Do NOT define functions inside the quoted expressions
16 | below. Instead, define any helper function in modules
17 | and import those modules here.
18 | """
19 |
20 | def controller do
21 | quote do
22 | use Phoenix.Controller, namespace: FrontendWeb
23 |
24 | import Plug.Conn
25 | alias FrontendWeb.Router.Helpers, as: Routes
26 | end
27 | end
28 |
29 | def view do
30 | quote do
31 | use Phoenix.View,
32 | root: "lib/frontend_web/templates",
33 | namespace: FrontendWeb
34 |
35 | # Import convenience functions from controllers
36 | import Phoenix.Controller,
37 | only: [get_flash: 1, get_flash: 2, view_module: 1, view_template: 1]
38 |
39 | # Include shared imports and aliases for views
40 | unquote(view_helpers())
41 | end
42 | end
43 |
44 | def router do
45 | quote do
46 | use Phoenix.Router
47 |
48 | import Plug.Conn
49 | import Phoenix.Controller
50 | end
51 | end
52 |
53 | def channel do
54 | quote do
55 | use Phoenix.Channel
56 | end
57 | end
58 |
59 | defp view_helpers do
60 | quote do
61 | # Import basic rendering functionality (render, render_layout, etc)
62 | import Phoenix.View
63 |
64 | import FrontendWeb.ErrorHelpers
65 | alias FrontendWeb.Router.Helpers, as: Routes
66 | end
67 | end
68 |
69 | @doc """
70 | When used, dispatch to the appropriate controller/view/etc.
71 | """
72 | defmacro __using__(which) when is_atom(which) do
73 | apply(__MODULE__, which, [])
74 | end
75 | end
76 |
--------------------------------------------------------------------------------
/elixir/frontend/lib/frontend_web/controllers/greetings_controller.ex:
--------------------------------------------------------------------------------
1 | defmodule FrontendWeb.GreetingsController do
2 | use FrontendWeb, :controller
3 |
4 | require OpenTelemetry.Tracer, as: Tracer
5 |
6 | def index(conn, _params) do
7 | name = Frontend.name()
8 | message = Frontend.message()
9 | Tracer.with_span "🎨 render message ✨" do
10 | text(conn, "Hello #{name}, #{message}")
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/elixir/frontend/lib/frontend_web/endpoint.ex:
--------------------------------------------------------------------------------
1 | defmodule FrontendWeb.Endpoint do
2 | use Phoenix.Endpoint, otp_app: :frontend
3 |
4 | plug Plug.RequestId
5 | plug Plug.Telemetry, event_prefix: [:phoenix, :endpoint]
6 | plug FrontendWeb.Router
7 | end
8 |
--------------------------------------------------------------------------------
/elixir/frontend/lib/frontend_web/router.ex:
--------------------------------------------------------------------------------
1 | defmodule FrontendWeb.Router do
2 | use FrontendWeb, :router
3 |
4 | get "/greeting", FrontendWeb.GreetingsController, :index
5 | end
6 |
--------------------------------------------------------------------------------
/elixir/frontend/lib/frontend_web/telemetry.ex:
--------------------------------------------------------------------------------
1 | defmodule FrontendWeb.Telemetry do
2 | use Supervisor
3 | import Telemetry.Metrics
4 |
5 | def start_link(arg) do
6 | Supervisor.start_link(__MODULE__, arg, name: __MODULE__)
7 | end
8 |
9 | @impl true
10 | def init(_arg) do
11 | children = [
12 | # Telemetry poller will execute the given period measurements
13 | # every 10_000ms. Learn more here: https://hexdocs.pm/telemetry_metrics
14 | {:telemetry_poller, measurements: periodic_measurements(), period: 10_000}
15 | # Add reporters as children of your supervision tree.
16 | # {Telemetry.Metrics.ConsoleReporter, metrics: metrics()}
17 | ]
18 |
19 | Supervisor.init(children, strategy: :one_for_one)
20 | end
21 |
22 | def metrics do
23 | [
24 | # Phoenix Metrics
25 | summary("phoenix.endpoint.stop.duration",
26 | unit: {:native, :millisecond}
27 | ),
28 | summary("phoenix.router_dispatch.stop.duration",
29 | tags: [:route],
30 | unit: {:native, :millisecond}
31 | ),
32 |
33 | # VM Metrics
34 | summary("vm.memory.total", unit: {:byte, :kilobyte}),
35 | summary("vm.total_run_queue_lengths.total"),
36 | summary("vm.total_run_queue_lengths.cpu"),
37 | summary("vm.total_run_queue_lengths.io")
38 | ]
39 | end
40 |
41 | defp periodic_measurements do
42 | [
43 | # A module, function and arguments to be invoked periodically.
44 | # This function must call :telemetry.execute/3 and a metric must be added above.
45 | # {FrontendWeb, :count_users, []}
46 | ]
47 | end
48 | end
49 |
--------------------------------------------------------------------------------
/elixir/frontend/lib/frontend_web/views/error_helpers.ex:
--------------------------------------------------------------------------------
1 | defmodule FrontendWeb.ErrorHelpers do
2 | @moduledoc """
3 | Conveniences for translating and building error messages.
4 | """
5 |
6 | @doc """
7 | Translates an error message.
8 | """
9 | def translate_error({msg, opts}) do
10 | # Because the error messages we show in our forms and APIs
11 | # are defined inside Ecto, we need to translate them dynamically.
12 | Enum.reduce(opts, msg, fn {key, value}, acc ->
13 | String.replace(acc, "%{#{key}}", to_string(value))
14 | end)
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/elixir/frontend/lib/frontend_web/views/error_view.ex:
--------------------------------------------------------------------------------
1 | defmodule FrontendWeb.ErrorView do
2 | use FrontendWeb, :view
3 |
4 | # If you want to customize a particular status code
5 | # for a certain format, you may uncomment below.
6 | # def render("500.json", _assigns) do
7 | # %{errors: %{detail: "Internal Server Error"}}
8 | # end
9 |
10 | # By default, Phoenix returns the status message from
11 | # the template name. For example, "404.json" becomes
12 | # "Not Found".
13 | def template_not_found(template, _assigns) do
14 | %{errors: %{detail: Phoenix.Controller.status_message_from_template(template)}}
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/elixir/frontend/mix.exs:
--------------------------------------------------------------------------------
1 | defmodule Frontend.MixProject do
2 | use Mix.Project
3 |
4 | def project do
5 | [
6 | app: :frontend,
7 | version: "0.1.0",
8 | elixir: "~> 1.7",
9 | elixirc_paths: elixirc_paths(Mix.env()),
10 | start_permanent: Mix.env() == :prod,
11 | aliases: aliases(),
12 | deps: deps()
13 | ]
14 | end
15 |
16 | # Configuration for the OTP application.
17 | #
18 | # Type `mix help compile.app` for more information.
19 | def application do
20 | [
21 | mod: {Frontend.Application, []},
22 | extra_applications: [:logger, :runtime_tools]
23 | ]
24 | end
25 |
26 | # Specifies which paths to compile per environment.
27 | defp elixirc_paths(:test), do: ["lib", "test/support"]
28 | defp elixirc_paths(_), do: ["lib"]
29 |
30 | # Specifies your project dependencies.
31 | #
32 | # Type `mix help deps` for examples and options.
33 | defp deps do
34 | [
35 | {:phoenix, "~> 1.7.2"},
36 | {:phoenix_view, "~> 2.0"},
37 | {:telemetry_metrics, "~> 0.6.1"},
38 | {:telemetry_poller, "~> 1.0.0"},
39 | {:jason, "~> 1.0"},
40 | {:plug_cowboy, "~> 2.6.1"},
41 | {:httpoison, "~> 2.1"},
42 | {:opentelemetry, "~> 1.2.1"},
43 | {:opentelemetry_api, "~> 1.2.1"},
44 | {:opentelemetry_phoenix, "~> 1.1.0"},
45 | {:opentelemetry_exporter, "~> 1.4.0"}
46 | ]
47 | end
48 |
49 | # Aliases are shortcuts or tasks specific to the current project.
50 | # For example, to install project dependencies and perform other setup tasks, run:
51 | #
52 | # $ mix setup
53 | #
54 | # See the documentation for `Mix` for more info on aliases.
55 | defp aliases do
56 | [
57 | setup: ["deps.get"]
58 | ]
59 | end
60 | end
61 |
--------------------------------------------------------------------------------
/elixir/message/.formatter.exs:
--------------------------------------------------------------------------------
1 | # Used by "mix format"
2 | [
3 | inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"],
4 | import_deps: [:plug]
5 | ]
6 |
--------------------------------------------------------------------------------
/elixir/message/.gitignore:
--------------------------------------------------------------------------------
1 | # The directory Mix will write compiled artifacts to.
2 | /_build/
3 |
4 | # If you run "mix test --cover", coverage assets end up here.
5 | /cover/
6 |
7 | # The directory Mix downloads your dependencies sources to.
8 | /deps/
9 |
10 | # Where third-party dependencies like ExDoc output generated docs.
11 | /doc/
12 |
13 | # Ignore .fetch files in case you like to edit your project deps locally.
14 | /.fetch
15 |
16 | # If the VM crashes, it generates a dump, let's ignore it too.
17 | erl_crash.dump
18 |
19 | # Also ignore archive artifacts (built via "mix archive.build").
20 | *.ez
21 |
22 | # Ignore package tarball (built via "mix hex.build").
23 | message-*.tar
24 |
25 | # Temporary files, for example, from tests.
26 | /tmp/
27 |
--------------------------------------------------------------------------------
/elixir/message/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM elixir:1.14
2 | WORKDIR /app
3 | COPY mix.* /app
4 | COPY lib /app/lib
5 | COPY config /app/config
6 | RUN mix local.hex --force
7 | RUN mix local.rebar --force
8 | RUN mix deps.get
9 | RUN mix deps.compile
10 |
11 | EXPOSE 9000
12 | CMD mix run --no-halt
13 |
--------------------------------------------------------------------------------
/elixir/message/config/runtime.exs:
--------------------------------------------------------------------------------
1 | import Config
2 |
3 | config :opentelemetry, :resource, service: [name: "message-elixir"]
4 |
5 | config :opentelemetry, :propagators, :otel_propagator_http_w3c
6 |
7 | config :opentelemetry, :processors,
8 | otel_batch_processor: %{
9 | exporter: {
10 | :opentelemetry_exporter,
11 | %{
12 | endpoints: [
13 | {
14 | :http,
15 | System.get_env("OTEL_COLLECTOR_HOST", "localhost"),
16 | System.get_env("OTEL_COLLECTOR_PORT", "55681") |> String.to_integer(),
17 | []
18 | }
19 | ]
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/elixir/message/lib/message.ex:
--------------------------------------------------------------------------------
1 | defmodule Message do
2 | use Plug.Router
3 |
4 | require OpenTelemetry.Tracer, as: Tracer
5 |
6 | plug :match
7 | plug :dispatch
8 |
9 | @messages [
10 | "how are you?",
11 | "how are you doing?",
12 | "what's good?",
13 | "what's up?",
14 | "how do you do?",
15 | "sup?",
16 | "good day to you",
17 | "how are things?",
18 | "howzit?",
19 | "woohoo"
20 | ]
21 |
22 | get "/message" do
23 | :otel_ctx.attach(:opentelemetry_process_propagator.fetch_parent_ctx())
24 |
25 | Tracer.with_span "/message" do
26 | Tracer.set_attributes(
27 | "http.method": conn.method,
28 | "http.scheme": conn.scheme,
29 | "http.host": conn.host,
30 | "http.route": conn.request_path
31 | )
32 |
33 | conn
34 | |> put_resp_content_type("text/plain")
35 | |> send_resp(200, message())
36 | end
37 | end
38 |
39 | defp message do
40 | Tracer.with_span "📖 look up message ✨" do
41 | :timer.sleep(Enum.random(0..5))
42 | Enum.random(@messages)
43 | end
44 | end
45 | end
46 |
--------------------------------------------------------------------------------
/elixir/message/lib/message/application.ex:
--------------------------------------------------------------------------------
1 | defmodule Message.Application do
2 | # See https://hexdocs.pm/elixir/Application.html
3 | # for more information on OTP Applications
4 | @moduledoc false
5 |
6 | use Application
7 |
8 | @impl true
9 | def start(_type, _args) do
10 | children = [
11 | {Plug.Cowboy, scheme: :http, plug: Message, options: [port: 9000]}
12 | ]
13 |
14 | :opentelemetry_cowboy.setup()
15 |
16 | # See https://hexdocs.pm/elixir/Supervisor.html
17 | # for other strategies and supported options
18 | opts = [strategy: :one_for_one, name: Message.Supervisor]
19 | Supervisor.start_link(children, opts)
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/elixir/message/mix.exs:
--------------------------------------------------------------------------------
1 | defmodule Message.MixProject do
2 | use Mix.Project
3 |
4 | def project do
5 | [
6 | app: :message,
7 | version: "0.1.0",
8 | elixir: "~> 1.12",
9 | start_permanent: Mix.env() == :prod,
10 | deps: deps()
11 | ]
12 | end
13 |
14 | # Run "mix help compile.app" to learn about applications.
15 | def application do
16 | [
17 | extra_applications: [:logger],
18 | mod: {Message.Application, []}
19 | ]
20 | end
21 |
22 | # Run "mix help deps" to learn about dependencies.
23 | defp deps do
24 | [
25 | {:plug_cowboy, "~> 2.6"},
26 | {:opentelemetry, "~> 1.2.0"},
27 | {:opentelemetry_api, "~> 1.2.1"},
28 | {:opentelemetry_exporter, "~> 1.4.0"},
29 | {:opentelemetry_process_propagator, "~> 0.2.2"},
30 | {:opentelemetry_cowboy, "~> 0.2"},
31 | {:hackney, "~> 1.17"},
32 | {:poison, "~> 5.0"}
33 | ]
34 | end
35 | end
36 |
--------------------------------------------------------------------------------
/elixir/name/.formatter.exs:
--------------------------------------------------------------------------------
1 | # Used by "mix format"
2 | [
3 | inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"],
4 | import_deps: [:plug]
5 | ]
6 |
--------------------------------------------------------------------------------
/elixir/name/.gitignore:
--------------------------------------------------------------------------------
1 | # The directory Mix will write compiled artifacts to.
2 | /_build/
3 |
4 | # If you run "mix test --cover", coverage assets end up here.
5 | /cover/
6 |
7 | # The directory Mix downloads your dependencies sources to.
8 | /deps/
9 |
10 | # Where third-party dependencies like ExDoc output generated docs.
11 | /doc/
12 |
13 | # Ignore .fetch files in case you like to edit your project deps locally.
14 | /.fetch
15 |
16 | # If the VM crashes, it generates a dump, let's ignore it too.
17 | erl_crash.dump
18 |
19 | # Also ignore archive artifacts (built via "mix archive.build").
20 | *.ez
21 |
22 | # Ignore package tarball (built via "mix hex.build").
23 | name-*.tar
24 |
25 | # Temporary files, for example, from tests.
26 | /tmp/
27 |
--------------------------------------------------------------------------------
/elixir/name/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM elixir:1.14
2 | WORKDIR /app
3 | COPY mix.* /app
4 | COPY lib /app/lib
5 | COPY config /app/config
6 | RUN mix local.hex --force
7 | RUN mix local.rebar --force
8 | RUN mix deps.get
9 | RUN mix deps.compile
10 |
11 | EXPOSE 8000
12 | CMD mix run --no-halt
13 |
--------------------------------------------------------------------------------
/elixir/name/config/runtime.exs:
--------------------------------------------------------------------------------
1 | import Config
2 |
3 | config :opentelemetry, :resource, service: [name: "name-elixir"]
4 |
5 | config :opentelemetry, :propagators, :otel_propagator_http_w3c
6 |
7 | config :opentelemetry, :processors,
8 | otel_batch_processor: %{
9 | exporter: {
10 | :opentelemetry_exporter,
11 | %{
12 | endpoints: [
13 | {
14 | :http,
15 | System.get_env("OTEL_COLLECTOR_HOST", "localhost"),
16 | System.get_env("OTEL_COLLECTOR_PORT", "55681") |> String.to_integer(),
17 | []
18 | }
19 | ]
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/elixir/name/lib/name.ex:
--------------------------------------------------------------------------------
1 | defmodule Name do
2 | use Plug.Router
3 |
4 | require OpenTelemetry.Tracer, as: Tracer
5 |
6 | plug :match
7 | plug :dispatch
8 |
9 | @names %{
10 | 2015 => ~w[sophia jackson emma aiden olivia liam ava lucas mia noah],
11 | 2016 => ~w[sophia jackson emma aiden olivia lucas ava liam mia noah],
12 | 2017 => ~w[sophia jackson olivia liam emma noah ava aiden isabella lucas],
13 | 2018 => ~w[sophia jackson olivia liam emma noah ava aiden isabella caden],
14 | 2019 => ~w[sophia liam olivia jackson emma noah ava aiden aira grayson],
15 | 2020 => ~w[olivia noah emma liam ava elijah isabella oliver sophia lucas]
16 | }
17 |
18 | get "/name" do
19 | conn
20 | |> put_resp_content_type("text/plain")
21 | |> send_resp(200, name_by(year()))
22 | end
23 |
24 | defp name_by(year) do
25 | :otel_ctx.attach(:opentelemetry_process_propagator.fetch_parent_ctx())
26 | Tracer.with_span "📖 look up name based on year ✨" do
27 | :timer.sleep(Enum.random(1..5))
28 | Map.get(@names, year) |> Enum.random()
29 | end
30 | end
31 |
32 | defp year do
33 | :otel_ctx.attach(:opentelemetry_process_propagator.fetch_parent_ctx())
34 | Tracer.with_span "✨ call /year ✨" do
35 | endpoint = System.get_env("YEAR_ENDPOINT", "http://localhost:6001")
36 | headers = :otel_propagator_text_map.inject([])
37 | HTTPoison.get!("#{endpoint}/year", headers).body |> String.to_integer()
38 | end
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/elixir/name/lib/name/application.ex:
--------------------------------------------------------------------------------
1 | defmodule Name.Application do
2 | # See https://hexdocs.pm/elixir/Application.html
3 | # for more information on OTP Applications
4 | @moduledoc false
5 |
6 | use Application
7 |
8 | @impl true
9 | def start(_type, _args) do
10 | children = [
11 | {Plug.Cowboy, scheme: :http, plug: Name, options: [port: 8000]}
12 | ]
13 |
14 | :opentelemetry_cowboy.setup()
15 |
16 | # See https://hexdocs.pm/elixir/Supervisor.html
17 | # for other strategies and supported options
18 | opts = [strategy: :one_for_one, name: Name.Supervisor]
19 | Supervisor.start_link(children, opts)
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/elixir/name/mix.exs:
--------------------------------------------------------------------------------
1 | defmodule Name.MixProject do
2 | use Mix.Project
3 |
4 | def project do
5 | [
6 | app: :name,
7 | version: "0.1.0",
8 | elixir: "~> 1.12",
9 | start_permanent: Mix.env() == :prod,
10 | deps: deps()
11 | ]
12 | end
13 |
14 | # Run "mix help compile.app" to learn about applications.
15 | def application do
16 | [
17 | extra_applications: [:logger],
18 | mod: {Name.Application, []}
19 | ]
20 | end
21 |
22 | # Run "mix help deps" to learn about dependencies.
23 | defp deps do
24 | [
25 | {:plug_cowboy, "~> 2.6"},
26 | {:opentelemetry, "~> 1.2.1"},
27 | {:opentelemetry_api, "~> 1.2.1"},
28 | {:opentelemetry_exporter, "~> 1.4.0"},
29 | {:opentelemetry_process_propagator, "~> 0.2.2"},
30 | {:opentelemetry_cowboy, "~> 0.2"},
31 | {:httpoison, "~> 2.1"}
32 | ]
33 | end
34 | end
35 |
--------------------------------------------------------------------------------
/elixir/year/.formatter.exs:
--------------------------------------------------------------------------------
1 | # Used by "mix format"
2 | [
3 | inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"],
4 | import_deps: [:plug]
5 | ]
6 |
--------------------------------------------------------------------------------
/elixir/year/.gitignore:
--------------------------------------------------------------------------------
1 | # The directory Mix will write compiled artifacts to.
2 | /_build/
3 |
4 | # If you run "mix test --cover", coverage assets end up here.
5 | /cover/
6 |
7 | # The directory Mix downloads your dependencies sources to.
8 | /deps/
9 |
10 | # Where third-party dependencies like ExDoc output generated docs.
11 | /doc/
12 |
13 | # Ignore .fetch files in case you like to edit your project deps locally.
14 | /.fetch
15 |
16 | # If the VM crashes, it generates a dump, let's ignore it too.
17 | erl_crash.dump
18 |
19 | # Also ignore archive artifacts (built via "mix archive.build").
20 | *.ez
21 |
22 | # Ignore package tarball (built via "mix hex.build").
23 | year-*.tar
24 |
25 | # Temporary files, for example, from tests.
26 | /tmp/
27 |
--------------------------------------------------------------------------------
/elixir/year/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM elixir:1.14
2 | WORKDIR /app
3 | COPY mix.* /app
4 | COPY lib /app/lib
5 | COPY config /app/config
6 | RUN mix local.hex --force
7 | RUN mix local.rebar --force
8 | RUN mix deps.get
9 | RUN mix deps.compile
10 |
11 | EXPOSE 6001
12 | CMD mix run --no-halt
13 |
--------------------------------------------------------------------------------
/elixir/year/config/runtime.exs:
--------------------------------------------------------------------------------
1 | import Config
2 |
3 | config :opentelemetry, :resource, service: [name: "year-elixir"]
4 |
5 | config :opentelemetry, :propagators, :otel_propagator_http_w3c
6 |
7 | config :opentelemetry, :processors,
8 | otel_batch_processor: %{
9 | exporter: {
10 | :opentelemetry_exporter,
11 | %{
12 | endpoints: [
13 | {
14 | :http,
15 | System.get_env("OTEL_COLLECTOR_HOST", "localhost"),
16 | System.get_env("OTEL_COLLECTOR_PORT", "55681") |> String.to_integer(),
17 | []
18 | }
19 | ]
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/elixir/year/lib/year.ex:
--------------------------------------------------------------------------------
1 | defmodule Year do
2 | use Plug.Router
3 |
4 | require OpenTelemetry.Tracer, as: Tracer
5 |
6 | plug :match
7 | plug :dispatch
8 |
9 | get "/year" do
10 | conn
11 | |> put_resp_content_type("text/plain")
12 | |> send_resp(200, year() |> to_string())
13 | end
14 |
15 | defp year do
16 | :otel_ctx.attach(:opentelemetry_process_propagator.fetch_parent_ctx())
17 | Tracer.with_span "🗓 get-a-year ✨" do
18 | :timer.sleep(Enum.random(0..5))
19 | Enum.random(2015..2020)
20 | end
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/elixir/year/lib/year/application.ex:
--------------------------------------------------------------------------------
1 | defmodule Year.Application do
2 | # See https://hexdocs.pm/elixir/Application.html
3 | # for more information on OTP Applications
4 | @moduledoc false
5 |
6 | use Application
7 |
8 | @impl true
9 | def start(_type, _args) do
10 | children = [
11 | {Plug.Cowboy, scheme: :http, plug: Year, options: [port: 6001]}
12 | ]
13 |
14 | :opentelemetry_cowboy.setup()
15 |
16 | # See https://hexdocs.pm/elixir/Supervisor.html
17 | # for other strategies and supported options
18 | opts = [strategy: :one_for_one, name: Year.Supervisor]
19 | Supervisor.start_link(children, opts)
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/elixir/year/mix.exs:
--------------------------------------------------------------------------------
1 | defmodule Year.MixProject do
2 | use Mix.Project
3 |
4 | def project do
5 | [
6 | app: :year,
7 | version: "0.1.0",
8 | elixir: "~> 1.12",
9 | start_permanent: Mix.env() == :prod,
10 | deps: deps()
11 | ]
12 | end
13 |
14 | # Run "mix help compile.app" to learn about applications.
15 | def application do
16 | [
17 | extra_applications: [:logger],
18 | mod: {Year.Application, []}
19 | ]
20 | end
21 |
22 | # Run "mix help deps" to learn about dependencies.
23 | defp deps do
24 | [
25 | {:plug_cowboy, "~> 2.6"},
26 | {:opentelemetry, "~> 1.2.0"},
27 | {:opentelemetry_api, "~> 1.2.1"},
28 | {:opentelemetry_exporter, "~> 1.4.0"},
29 | {:opentelemetry_process_propagator, "~> 0.2.2"},
30 | {:opentelemetry_cowboy, "~> 0.2"}
31 | ]
32 | end
33 | end
34 |
--------------------------------------------------------------------------------
/go-auto-instrumented/.dockerignore:
--------------------------------------------------------------------------------
1 | **/bin/
2 | **/obj/
3 |
--------------------------------------------------------------------------------
/go-auto-instrumented/README.md:
--------------------------------------------------------------------------------
1 | # auto-instrument go services
2 |
3 | ## setup
4 |
5 | ```sh
6 | # replace `` with actual api key
7 | export HONEYCOMB_API_KEY=
8 |
9 | # build the docker images for all the services
10 | docker-compose build
11 |
12 | # to run with Kind using locally built images, see step below and come back
13 |
14 | # deploy the greetings namespace and services in k8s
15 | kubectl apply -k greetings/
16 |
17 | # create secret with api key
18 | kubectl create secret generic honeycomb --from-literal=api-key=$HONEYCOMB_API_KEY -n greetings
19 |
20 | # deploy the services with the auto-instrumentation agent
21 | kubectl apply -f greetings-instrumented.yaml -n greetings
22 |
23 | # deploy the collector
24 | kubectl apply -f otel-collector.yaml -n greetings
25 |
26 | # if running with kind, port-forward the frontend service (not necessary otherwise bc of loadbalancer)
27 | kubectl port-forward svc/frontend 7007:7007 -n greetings
28 |
29 | # make sure everything is up and running
30 | kubectl get pods -n greetings
31 |
32 | # follow logs for collector (optional)
33 | kubectl logs deployments/otel-collector --follow -n greetings
34 | ```
35 |
36 | `curl localhost:7007/greeting`
37 |
38 | ### run with kind
39 |
40 | after buildings things locally...
41 |
42 | ```sh
43 | # create cluster with name kind
44 | kind create cluster
45 |
46 | # load locally built docker images into kind
47 | kind load docker-image frontend-go-auto:local message-go-auto:local name-go-auto:local year-go-auto:local otel-go-instrumentation:v0.1 otel/opentelemetry-collector:0.75.0
48 | ```
49 |
50 | ## cleanup
51 |
52 | ```sh
53 | # delete secret with api key
54 | kubectl delete namespace greetings
55 |
56 | # make sure everything is gone
57 | kubectl get pods -n greetings
58 |
59 | # if using kind, delete the cluster
60 | kind delete cluster
61 | ```
62 |
--------------------------------------------------------------------------------
/go-auto-instrumented/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "2.4"
2 |
3 | services:
4 | frontend:
5 | build:
6 | context: .
7 | dockerfile: ./frontend/Dockerfile
8 | image: frontend-go-auto:local
9 | ports:
10 | - 7007:7007
11 |
12 | message:
13 | build:
14 | context: .
15 | dockerfile: ./message-service/Dockerfile
16 | image: message-go-auto:local
17 | ports:
18 | - 9000:9000
19 |
20 | name:
21 | build:
22 | context: .
23 | dockerfile: ./name-service/Dockerfile
24 | image: name-go-auto:local
25 | ports:
26 | - 8000:8000
27 |
28 | year:
29 | build:
30 | context: .
31 | dockerfile: ./year-service/Dockerfile
32 | image: year-go-auto:local
33 | ports:
34 | - 6001:6001
35 |
36 |
--------------------------------------------------------------------------------
/go-auto-instrumented/frontend/Dockerfile:
--------------------------------------------------------------------------------
1 | # to be built from docker-compose
2 | FROM golang:1.20 AS build
3 | WORKDIR /src
4 | ENV CGO_ENABLED=0
5 |
6 | COPY go.mod .
7 | COPY frontend/. .
8 | RUN go get
9 | RUN go build -o /out/frontend .
10 |
11 | FROM scratch AS bin
12 | WORKDIR /app
13 | COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
14 | COPY --from=build /out/frontend /app/
15 |
16 | # local code uses localhost:9000 and localhost:8000
17 | # image will use the below endpoints
18 | ENV MESSAGE_ENDPOINT=http://message:9000
19 | ENV NAME_ENDPOINT=http://name:8000
20 | # or override at runtime in k8s or docker-compose
21 |
22 | CMD ["/app/frontend"]
23 |
--------------------------------------------------------------------------------
/go-auto-instrumented/frontend/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "net/http"
7 | "io/ioutil"
8 | "log"
9 | "os"
10 | "github.com/gorilla/mux"
11 | )
12 |
13 | func getEnv(key, fallback string) string {
14 | value := os.Getenv(key)
15 | if len(value) == 0 {
16 | return fallback
17 | }
18 | return value
19 | }
20 |
21 | var (
22 | nameServiceUrl = getEnv("NAME_ENDPOINT", "http://localhost:8000") + "/name"
23 | messageServiceUrl = getEnv("MESSAGE_ENDPOINT", "http://localhost:9000") + "/message"
24 | )
25 |
26 | func main() {
27 |
28 | r := mux.NewRouter()
29 | r.HandleFunc("/greeting", func(w http.ResponseWriter, r *http.Request) {
30 | name := getName(r.Context())
31 | message := getMessage(r.Context())
32 |
33 | _, _ = fmt.Fprintf(w, "Hello %s, %s", name, message)
34 | })
35 |
36 | log.Println("Listening on http://localhost:7007/greeting")
37 | log.Fatal(http.ListenAndServe(":7007", r))
38 | }
39 |
40 | func getName(ctx context.Context) string {
41 | return makeRequest(ctx, nameServiceUrl)
42 | }
43 |
44 | func getMessage(ctx context.Context) string {
45 | return makeRequest(ctx, messageServiceUrl)
46 | }
47 |
48 | func makeRequest(ctx context.Context, url string) string {
49 | req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
50 | client := http.Client{Transport: http.DefaultTransport}
51 | res, err := client.Do(req)
52 | if err != nil {
53 | panic(err)
54 | }
55 | body, err := ioutil.ReadAll(res.Body)
56 | res.Body.Close()
57 | if err != nil {
58 | panic(err)
59 | }
60 | return string(body)
61 | }
62 |
--------------------------------------------------------------------------------
/go-auto-instrumented/go.mod:
--------------------------------------------------------------------------------
1 | module go-auto-instrumented
2 |
3 | go 1.18
4 |
5 | require github.com/gorilla/mux v1.8.0
6 |
--------------------------------------------------------------------------------
/go-auto-instrumented/go.sum:
--------------------------------------------------------------------------------
1 | github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
2 | github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
3 |
--------------------------------------------------------------------------------
/go-auto-instrumented/greetings/frontend.yaml:
--------------------------------------------------------------------------------
1 | ##################################################################################################
2 | # frontend-go services
3 | ##################################################################################################
4 | kind: ServiceAccount
5 | apiVersion: v1
6 | metadata:
7 | name: frontend-go
8 | namespace: greetings
9 | ---
10 | apiVersion: v1
11 | kind: Service
12 | metadata:
13 | name: frontend
14 | namespace: greetings
15 | labels:
16 | app: frontend
17 | service: frontend
18 | app.kubernetes.io/name: frontend-go
19 | spec:
20 | selector:
21 | app: frontend
22 | ports:
23 | - name: http
24 | port: 7007
25 | targetPort: 7007
26 | type: LoadBalancer
27 | ---
28 | apiVersion: apps/v1
29 | kind: Deployment
30 | metadata:
31 | name: frontend-go
32 | namespace: greetings
33 | labels:
34 | app: frontend-go
35 | app.kubernetes.io/name: frontend-go
36 | spec:
37 | replicas: 1
38 | selector:
39 | matchLabels:
40 | app: frontend
41 | template:
42 | metadata:
43 | labels:
44 | app: frontend
45 | spec:
46 | serviceAccountName: frontend-go
47 | terminationGracePeriodSeconds: 0
48 | containers:
49 | - name: frontend
50 | image: frontend-go-auto:local
51 | ports:
52 | - containerPort: 7007
53 | name: http
54 | env:
55 | - name: MESSAGE_ENDPOINT
56 | value: http://message:9000
57 | - name: NAME_ENDPOINT
58 | value: http://name:8000
59 |
--------------------------------------------------------------------------------
/go-auto-instrumented/greetings/kustomization.yaml:
--------------------------------------------------------------------------------
1 | resources:
2 | - ns.yaml
3 | - frontend.yaml
4 | - message.yaml
5 | - name.yaml
6 | - year.yaml
7 |
--------------------------------------------------------------------------------
/go-auto-instrumented/greetings/message.yaml:
--------------------------------------------------------------------------------
1 | ##################################################################################################
2 | # message-go services
3 | ##################################################################################################
4 | kind: ServiceAccount
5 | apiVersion: v1
6 | metadata:
7 | name: message-go
8 | namespace: greetings
9 | ---
10 | apiVersion: v1
11 | kind: Service
12 | metadata:
13 | name: message
14 | namespace: greetings
15 | labels:
16 | app: message
17 | service: message
18 | app.kubernetes.io/name: message-go
19 | spec:
20 | selector:
21 | app: message
22 | ports:
23 | - name: http
24 | port: 9000
25 | targetPort: 9000
26 | type: LoadBalancer
27 | ---
28 | apiVersion: apps/v1
29 | kind: Deployment
30 | metadata:
31 | name: message-go
32 | namespace: greetings
33 | labels:
34 | app: message-go
35 | app.kubernetes.io/name: message-go
36 | spec:
37 | replicas: 1
38 | selector:
39 | matchLabels:
40 | app: message
41 | template:
42 | metadata:
43 | labels:
44 | app: message
45 | spec:
46 | serviceAccountName: message-go
47 | terminationGracePeriodSeconds: 0
48 | containers:
49 | - name: message
50 | image: message-go-auto:local
51 | ports:
52 | - containerPort: 9000
53 | name: http
54 |
--------------------------------------------------------------------------------
/go-auto-instrumented/greetings/name.yaml:
--------------------------------------------------------------------------------
1 | ##################################################################################################
2 | # name-go services
3 | ##################################################################################################
4 | kind: ServiceAccount
5 | apiVersion: v1
6 | metadata:
7 | name: name-go
8 | namespace: greetings
9 | ---
10 | apiVersion: v1
11 | kind: Service
12 | metadata:
13 | name: name
14 | namespace: greetings
15 | labels:
16 | app: name
17 | service: name
18 | app.kubernetes.io/name: name-go
19 | spec:
20 | selector:
21 | app: name
22 | ports:
23 | - name: http
24 | port: 8000
25 | targetPort: 8000
26 | type: LoadBalancer
27 | ---
28 | apiVersion: apps/v1
29 | kind: Deployment
30 | metadata:
31 | name: name-go
32 | namespace: greetings
33 | labels:
34 | app: name-go
35 | app.kubernetes.io/name: name-go
36 | spec:
37 | replicas: 1
38 | selector:
39 | matchLabels:
40 | app: name
41 | template:
42 | metadata:
43 | labels:
44 | app: name
45 | spec:
46 | serviceAccountName: name-go
47 | terminationGracePeriodSeconds: 0
48 | containers:
49 | - name: name
50 | image: name-go-auto:local
51 | ports:
52 | - containerPort: 8000
53 | name: http
54 | env:
55 | - name: YEAR_ENDPOINT
56 | value: http://year:6001
57 |
--------------------------------------------------------------------------------
/go-auto-instrumented/greetings/ns.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Namespace
3 | metadata:
4 | name: greetings
5 |
--------------------------------------------------------------------------------
/go-auto-instrumented/greetings/year.yaml:
--------------------------------------------------------------------------------
1 | ##################################################################################################
2 | # year-go services
3 | ##################################################################################################
4 | kind: ServiceAccount
5 | apiVersion: v1
6 | metadata:
7 | name: year-go
8 | namespace: greetings
9 | ---
10 | apiVersion: v1
11 | kind: Service
12 | metadata:
13 | name: year
14 | namespace: greetings
15 | labels:
16 | app: year
17 | service: year
18 | app.kubernetes.io/name: year-go
19 | spec:
20 | selector:
21 | app: year
22 | ports:
23 | - name: http
24 | port: 6001
25 | targetPort: 6001
26 | type: LoadBalancer
27 | ---
28 | apiVersion: apps/v1
29 | kind: Deployment
30 | metadata:
31 | name: year-go
32 | namespace: greetings
33 | labels:
34 | app: year-go
35 | app.kubernetes.io/name: year-go
36 | spec:
37 | replicas: 1
38 | selector:
39 | matchLabels:
40 | app: year
41 | template:
42 | metadata:
43 | labels:
44 | app: year
45 | spec:
46 | serviceAccountName: year-go
47 | terminationGracePeriodSeconds: 0
48 | containers:
49 | - name: year
50 | image: year-go-auto:local
51 | ports:
52 | - containerPort: 6001
53 | name: http
54 |
55 |
--------------------------------------------------------------------------------
/go-auto-instrumented/message-service/Dockerfile:
--------------------------------------------------------------------------------
1 | # to be built from docker-compose
2 | FROM golang:1.20 AS build
3 | WORKDIR /src
4 | ENV CGO_ENABLED=0
5 |
6 | COPY go.mod .
7 |
8 | COPY message-service/. .
9 | RUN go build -o /out/message-service .
10 |
11 | FROM scratch AS bin
12 | WORKDIR /app
13 | COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
14 | COPY --from=build /out/message-service /app/
15 | CMD ["/app/message-service"]
16 |
--------------------------------------------------------------------------------
/go-auto-instrumented/message-service/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "math/rand"
7 | "net/http"
8 | "time"
9 | )
10 |
11 | func main() {
12 | messages := []string{
13 | "how are you?", "how are you doing?", "what's good?", "what's up?", "how do you do?",
14 | "sup?", "good day to you", "how are things?", "howzit?", "woohoo",
15 | }
16 |
17 | mux := http.NewServeMux()
18 | mux.HandleFunc("/message", func(w http.ResponseWriter, r *http.Request) {
19 | rand.Seed(time.Now().UnixNano())
20 | time.Sleep(time.Duration(rand.Intn(5)) * time.Millisecond)
21 | _, _ = fmt.Fprintf(w, messages[rand.Intn(len(messages))])
22 | })
23 | handler := http.Handler(mux)
24 |
25 | log.Println("Listening on http://localhost:9000/message")
26 | log.Fatal(http.ListenAndServe(":9000",handler))
27 | }
28 |
--------------------------------------------------------------------------------
/go-auto-instrumented/name-service/Dockerfile:
--------------------------------------------------------------------------------
1 | # to be built from docker-compose
2 | FROM golang:1.20 AS build
3 | WORKDIR /src
4 | ENV CGO_ENABLED=0
5 |
6 | COPY go.mod .
7 |
8 | COPY name-service/. .
9 | RUN go build -o /out/name-service .
10 |
11 | FROM scratch AS bin
12 | WORKDIR /app
13 | COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
14 | COPY --from=build /out/name-service /app/
15 |
16 | # local code uses localhost:6001/year
17 | # image will use the below endpoint
18 | ENV YEAR_ENDPOINT=http://year:6001
19 | # or override at runtime in k8s or docker-compose
20 |
21 | CMD ["/app/name-service"]
22 |
--------------------------------------------------------------------------------
/go-auto-instrumented/name-service/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "io/ioutil"
7 | "log"
8 | "math/rand"
9 | "net/http"
10 | "os"
11 | "strconv"
12 | "time"
13 | )
14 | func getEnv(key, fallback string) string {
15 | value := os.Getenv(key)
16 | if len(value) == 0 {
17 | return fallback
18 | }
19 | return value
20 | }
21 |
22 | var (
23 | yearServiceUrl = getEnv("YEAR_ENDPOINT", "http://localhost:6001") + "/year"
24 | )
25 |
26 | func main() {
27 | // initialize the random number generator
28 | rand.Seed(time.Now().UnixNano())
29 |
30 | namesByYear := map[int][]string{
31 | 2016: {"sophia", "jackson", "emma", "aiden", "olivia", "lucas", "ava", "liam", "mia", "noah"},
32 | 2017: {"sophia", "jackson", "olivia", "liam", "emma", "noah", "ava", "aiden", "isabella", "lucas"},
33 | 2018: {"sophia", "jackson", "olivia", "liam", "emma", "noah", "ava", "aiden", "isabella", "caden"},
34 | 2019: {"sophia", "liam", "olivia", "jackson", "emma", "noah", "ava", "aiden", "aria", "grayson"},
35 | 2020: {"olivia", "noah", "emma", "liam", "ava", "elijah", "isabella", "oliver", "sophia", "lucas"},
36 | }
37 |
38 | mux := http.NewServeMux()
39 | mux.HandleFunc("/name", func(w http.ResponseWriter, r *http.Request) {
40 | time.Sleep(time.Duration(rand.Intn(5)) * time.Millisecond)
41 | year, _ := getYear(r.Context())
42 | names := namesByYear[year]
43 | _, _ = fmt.Fprintf(w, names[rand.Intn(len(names))])
44 | })
45 |
46 | handler := http.Handler(mux)
47 |
48 | log.Println("Listening on http://localhost:8000/name")
49 | log.Fatal(http.ListenAndServe(":8000", handler))
50 | }
51 |
52 | func getYear(ctx context.Context) (int, context.Context) {
53 | req, err := http.NewRequestWithContext(ctx, "GET", yearServiceUrl, nil)
54 | if err != nil {
55 | fmt.Printf("error creating request: %s", err)
56 | }
57 | client := http.Client{Transport: http.DefaultTransport}
58 | res, err := client.Do(req)
59 | if err != nil {
60 | panic(err)
61 | }
62 | body, err := ioutil.ReadAll(res.Body)
63 | res.Body.Close()
64 | if err != nil {
65 | panic(err)
66 | }
67 | year, err := strconv.Atoi(string(body))
68 | if err != nil {
69 | panic(err)
70 | }
71 | return year, ctx
72 | }
73 |
--------------------------------------------------------------------------------
/go-auto-instrumented/year-service/Dockerfile:
--------------------------------------------------------------------------------
1 | # to be built from docker-compose
2 | FROM golang:1.20 AS build
3 | WORKDIR /src
4 | ENV CGO_ENABLED=0
5 |
6 | COPY go.mod .
7 |
8 | COPY year-service/*.go .
9 | RUN go build -o /out/year-service .
10 |
11 | FROM scratch AS bin
12 | WORKDIR /app
13 | COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
14 | COPY --from=build /out/year-service /app/
15 | CMD ["/app/year-service"]
16 |
--------------------------------------------------------------------------------
/go-auto-instrumented/year-service/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "log"
7 | "math/rand"
8 | "net/http"
9 | "time"
10 | )
11 |
12 | func calculateYear() int {
13 | years := []int{2016, 2017, 2018, 2019, 2020}
14 | rand.Seed(time.Now().UnixNano())
15 | time.Sleep(time.Duration(rand.Intn(5)) * time.Millisecond)
16 | return years[rand.Intn(len(years))]
17 | }
18 |
19 | func yearHandler(w http.ResponseWriter, r *http.Request) {
20 | ctx := r.Context()
21 |
22 | year := func(ctx context.Context) int {
23 | return calculateYear()
24 | }(ctx)
25 |
26 | _, _ = fmt.Fprintf(w, "%d", year)
27 | }
28 |
29 | func main() {
30 | mux := http.NewServeMux()
31 | mux.HandleFunc("/year", yearHandler)
32 |
33 | handler := http.Handler(mux)
34 |
35 | log.Println("Listening on http://localhost:6001/year")
36 | log.Fatal(http.ListenAndServe(":6001", handler))
37 | }
38 |
--------------------------------------------------------------------------------
/go-uninstrumented/.dockerignore:
--------------------------------------------------------------------------------
1 | **/bin/
2 | **/obj/
3 |
--------------------------------------------------------------------------------
/go-uninstrumented/README.md:
--------------------------------------------------------------------------------
1 | # example greeting service in golang - uninstrumented
2 |
3 | Images are now available on [GitHub Packages](https://github.com/orgs/honeycombio/packages?repo_name=example-greeting-service)
4 |
5 | ## Steps to Run in Kubernetes
6 |
7 | ```sh
8 | # deploy the services in k8s
9 | kubectl apply -f greetings.yaml
10 | # port-forward the frontend service
11 | kubectl port-forward svc/frontend 7777:7777
12 | ```
13 |
14 | Then in a new terminal, `curl localhost:7777/greeting`
15 |
16 | When all done:
17 |
18 | ```sh
19 | kubectl delete -f greetings.yaml
20 | ```
21 |
22 | ## Steps to Build Locally
23 |
24 | To use local images, build using `docker-compose` and update the `greetings.yaml` with the new image names.
25 |
26 | ```sh
27 | # build docker images
28 | docker-compose build
29 | ```
30 |
31 | As an example:
32 |
33 | ```yaml
34 | spec:
35 | serviceAccountName: year-go
36 | terminationGracePeriodSeconds: 0
37 | containers:
38 | - name: year
39 | imagePullPolicy: IfNotPresent
40 | image: egs-year-go:local
41 | ports:
42 | - containerPort: 6001
43 | name: http
44 | ```
45 |
--------------------------------------------------------------------------------
/go-uninstrumented/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "2.4"
2 |
3 | services:
4 | frontend:
5 | build:
6 | context: .
7 | dockerfile: ./frontend-service/Dockerfile
8 | image: egs-frontend-go:local
9 | ports:
10 | - 7777:7777
11 |
12 | message:
13 | build:
14 | context: .
15 | dockerfile: ./message-service/Dockerfile
16 | image: egs-message-go:local
17 | ports:
18 | - 9000:9000
19 |
20 | name:
21 | build:
22 | context: .
23 | dockerfile: ./name-service/Dockerfile
24 | image: egs-name-go:local
25 | ports:
26 | - 8000:8000
27 |
28 | year:
29 | build:
30 | context: .
31 | dockerfile: ./year-service/Dockerfile
32 | image: egs-year-go:local
33 | ports:
34 | - 6001:6001
35 |
36 |
--------------------------------------------------------------------------------
/go-uninstrumented/frontend-service/Dockerfile:
--------------------------------------------------------------------------------
1 | # to be built from docker-compose or publish-ghcr workflow
2 | FROM golang:1.20 AS build
3 | WORKDIR /src
4 | ENV CGO_ENABLED=0
5 |
6 | COPY go.mod .
7 | COPY frontend-service/. .
8 | RUN go get
9 | RUN go build -o /out/frontend-service .
10 |
11 | FROM scratch AS bin
12 | WORKDIR /app
13 | COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
14 | COPY --from=build /out/frontend-service /app/
15 |
16 | # local code uses localhost:9000 and localhost:8000
17 | # image will use the below endpoints
18 | ENV MESSAGE_ENDPOINT=http://message:9000
19 | ENV NAME_ENDPOINT=http://name:8000
20 | # or override at runtime in k8s or docker-compose
21 |
22 | CMD ["/app/frontend-service"]
23 |
--------------------------------------------------------------------------------
/go-uninstrumented/frontend-service/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "net/http"
7 | "io/ioutil"
8 | "log"
9 | "os"
10 | "github.com/gorilla/mux"
11 | )
12 |
13 | func getEnv(key, fallback string) string {
14 | value := os.Getenv(key)
15 | if len(value) == 0 {
16 | return fallback
17 | }
18 | return value
19 | }
20 |
21 | var (
22 | nameServiceUrl = getEnv("NAME_ENDPOINT", "http://localhost:8000") + "/name"
23 | messageServiceUrl = getEnv("MESSAGE_ENDPOINT", "http://localhost:9000") + "/message"
24 | )
25 |
26 | func main() {
27 |
28 | r := mux.NewRouter()
29 | r.HandleFunc("/greeting", func(w http.ResponseWriter, r *http.Request) {
30 | name := getName(r.Context())
31 | message := getMessage(r.Context())
32 |
33 | _, _ = fmt.Fprintf(w, "Hello %s, %s", name, message)
34 | })
35 |
36 | log.Println("Listening on http://localhost:7777/greeting")
37 | log.Fatal(http.ListenAndServe(":7777", r))
38 | }
39 |
40 | func getName(ctx context.Context) string {
41 | return makeRequest(ctx, nameServiceUrl)
42 | }
43 |
44 | func getMessage(ctx context.Context) string {
45 | return makeRequest(ctx, messageServiceUrl)
46 | }
47 |
48 | func makeRequest(ctx context.Context, url string) string {
49 | req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
50 | client := http.Client{Transport: http.DefaultTransport}
51 | res, err := client.Do(req)
52 | if err != nil {
53 | panic(err)
54 | }
55 | body, err := ioutil.ReadAll(res.Body)
56 | res.Body.Close()
57 | if err != nil {
58 | panic(err)
59 | }
60 | return string(body)
61 | }
62 |
--------------------------------------------------------------------------------
/go-uninstrumented/go.mod:
--------------------------------------------------------------------------------
1 | module go-auto-instrumented
2 |
3 | go 1.18
4 |
5 | require github.com/gorilla/mux v1.8.0
6 |
--------------------------------------------------------------------------------
/go-uninstrumented/go.sum:
--------------------------------------------------------------------------------
1 | github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
2 | github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
3 |
--------------------------------------------------------------------------------
/go-uninstrumented/message-service/Dockerfile:
--------------------------------------------------------------------------------
1 | # to be built from docker-compose or publish-ghcr workflow
2 | FROM golang:1.20 AS build
3 | WORKDIR /src
4 | ENV CGO_ENABLED=0
5 |
6 | COPY go.mod .
7 |
8 | COPY message-service/. .
9 | RUN go build -o /out/message-service .
10 |
11 | FROM scratch AS bin
12 | WORKDIR /app
13 | COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
14 | COPY --from=build /out/message-service /app/
15 | CMD ["/app/message-service"]
16 |
--------------------------------------------------------------------------------
/go-uninstrumented/message-service/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "math/rand"
7 | "net/http"
8 | "time"
9 | )
10 |
11 | func main() {
12 | messages := []string{
13 | "how are you?", "how are you doing?", "what's good?", "what's up?", "how do you do?",
14 | "sup?", "good day to you", "how are things?", "howzit?", "woohoo",
15 | }
16 |
17 | mux := http.NewServeMux()
18 | mux.HandleFunc("/message", func(w http.ResponseWriter, r *http.Request) {
19 | rand.Seed(time.Now().UnixNano())
20 | time.Sleep(time.Duration(rand.Intn(5)) * time.Millisecond)
21 | _, _ = fmt.Fprintf(w, messages[rand.Intn(len(messages))])
22 | })
23 | handler := http.Handler(mux)
24 |
25 | log.Println("Listening on http://localhost:9000/message")
26 | log.Fatal(http.ListenAndServe(":9000",handler))
27 | }
28 |
--------------------------------------------------------------------------------
/go-uninstrumented/name-service/Dockerfile:
--------------------------------------------------------------------------------
1 | # to be built from docker-compose or publish-ghcr workflow
2 | FROM golang:1.20 AS build
3 | WORKDIR /src
4 | ENV CGO_ENABLED=0
5 |
6 | COPY go.mod .
7 |
8 | COPY name-service/. .
9 | RUN go build -o /out/name-service .
10 |
11 | FROM scratch AS bin
12 | WORKDIR /app
13 | COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
14 | COPY --from=build /out/name-service /app/
15 |
16 | # local code uses localhost:6001/year
17 | # image will use the below endpoint
18 | ENV YEAR_ENDPOINT=http://year:6001
19 | # or override at runtime in k8s or docker-compose
20 |
21 | CMD ["/app/name-service"]
22 |
--------------------------------------------------------------------------------
/go-uninstrumented/name-service/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "io/ioutil"
7 | "log"
8 | "math/rand"
9 | "net/http"
10 | "os"
11 | "strconv"
12 | "time"
13 | )
14 | func getEnv(key, fallback string) string {
15 | value := os.Getenv(key)
16 | if len(value) == 0 {
17 | return fallback
18 | }
19 | return value
20 | }
21 |
22 | var (
23 | yearServiceUrl = getEnv("YEAR_ENDPOINT", "http://localhost:6001") + "/year"
24 | )
25 |
26 | func main() {
27 | // initialize the random number generator
28 | rand.Seed(time.Now().UnixNano())
29 |
30 | namesByYear := map[int][]string{
31 | 2016: {"sophia", "jackson", "emma", "aiden", "olivia", "lucas", "ava", "liam", "mia", "noah"},
32 | 2017: {"sophia", "jackson", "olivia", "liam", "emma", "noah", "ava", "aiden", "isabella", "lucas"},
33 | 2018: {"sophia", "jackson", "olivia", "liam", "emma", "noah", "ava", "aiden", "isabella", "caden"},
34 | 2019: {"sophia", "liam", "olivia", "jackson", "emma", "noah", "ava", "aiden", "aria", "grayson"},
35 | 2020: {"olivia", "noah", "emma", "liam", "ava", "elijah", "isabella", "oliver", "sophia", "lucas"},
36 | }
37 |
38 | mux := http.NewServeMux()
39 | mux.HandleFunc("/name", func(w http.ResponseWriter, r *http.Request) {
40 | time.Sleep(time.Duration(rand.Intn(5)) * time.Millisecond)
41 | year, _ := getYear(r.Context())
42 | names := namesByYear[year]
43 | _, _ = fmt.Fprintf(w, names[rand.Intn(len(names))])
44 | })
45 |
46 | handler := http.Handler(mux)
47 |
48 | log.Println("Listening on http://localhost:8000/name")
49 | log.Fatal(http.ListenAndServe(":8000", handler))
50 | }
51 |
52 | func getYear(ctx context.Context) (int, context.Context) {
53 | req, err := http.NewRequestWithContext(ctx, "GET", yearServiceUrl, nil)
54 | if err != nil {
55 | fmt.Printf("error creating request: %s", err)
56 | }
57 | client := http.Client{Transport: http.DefaultTransport}
58 | res, err := client.Do(req)
59 | if err != nil {
60 | panic(err)
61 | }
62 | body, err := ioutil.ReadAll(res.Body)
63 | res.Body.Close()
64 | if err != nil {
65 | panic(err)
66 | }
67 | year, err := strconv.Atoi(string(body))
68 | if err != nil {
69 | panic(err)
70 | }
71 | return year, ctx
72 | }
73 |
--------------------------------------------------------------------------------
/go-uninstrumented/year-service/Dockerfile:
--------------------------------------------------------------------------------
1 | # to be built from docker-compose or publish-ghcr workflow
2 | FROM golang:1.20 AS build
3 | WORKDIR /src
4 | ENV CGO_ENABLED=0
5 |
6 | COPY go.mod .
7 |
8 | COPY year-service/*.go .
9 | RUN go build -o /out/year-service .
10 |
11 | FROM scratch AS bin
12 | WORKDIR /app
13 | COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
14 | COPY --from=build /out/year-service /app/
15 | CMD ["/app/year-service"]
16 |
--------------------------------------------------------------------------------
/go-uninstrumented/year-service/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "log"
7 | "math/rand"
8 | "net/http"
9 | "time"
10 | )
11 |
12 | func calculateYear() int {
13 | years := []int{2016, 2017, 2018, 2019, 2020}
14 | rand.Seed(time.Now().UnixNano())
15 | time.Sleep(time.Duration(rand.Intn(5)) * time.Millisecond)
16 | return years[rand.Intn(len(years))]
17 | }
18 |
19 | func yearHandler(w http.ResponseWriter, r *http.Request) {
20 | ctx := r.Context()
21 |
22 | year := func(ctx context.Context) int {
23 | return calculateYear()
24 | }(ctx)
25 |
26 | _, _ = fmt.Fprintf(w, "%d", year)
27 | }
28 |
29 | func main() {
30 | mux := http.NewServeMux()
31 | mux.HandleFunc("/year", yearHandler)
32 |
33 | handler := http.Handler(mux)
34 |
35 | log.Println("Listening on http://localhost:6001/year")
36 | log.Fatal(http.ListenAndServe(":6001", handler))
37 | }
38 |
--------------------------------------------------------------------------------
/golang/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: https://EditorConfig.org
2 |
3 | # When opening a file, EditorConfig plugins look for a file named .editorconfig
4 | # in the directory of the opened file and in every parent directory.
5 | # A search for .editorconfig files will stop if the root filepath is reached
6 | # or an EditorConfig file with root=true is found.
7 |
8 | # EditorConfig files are read top to bottom and the most recent rules found
9 | # take precedence. Properties from matching EditorConfig sections are applied
10 | # in the order they were read, so properties in closer files take precedence.
11 |
12 | # go files
13 | [*.go]
14 |
15 | indent_size = 8
16 | indent_style = tab
17 | tab_width = 8
--------------------------------------------------------------------------------
/golang/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "2.4"
2 |
3 | x-common-env: &common-env
4 | HONEYCOMB_API_KEY: ${HONEYCOMB_API_KEY}
5 | OTEL_EXPORTER_OTLP_ENDPOINT: api.honeycomb.io
6 | OTEL_RESOURCE_ATTRIBUTES: app.running-in=docker
7 | MESSAGE_ENDPOINT: http://message:9000
8 | NAME_ENDPOINT: http://name:8000
9 | YEAR_ENDPOINT: http://year:6001
10 | REDIS_URL: redis
11 |
12 | services:
13 | frontend:
14 | build:
15 | context: .
16 | dockerfile: ./frontend/Dockerfile
17 | image: hnyexample/frontend-golang
18 | environment:
19 | <<: *common-env
20 | OTEL_SERVICE_NAME: frontend-go
21 | ports:
22 | - 7777:7777
23 |
24 | message:
25 | build:
26 | context: .
27 | dockerfile: ./message-service/Dockerfile
28 | image: hnyexample/message-golang
29 | environment:
30 | <<: *common-env
31 | OTEL_SERVICE_NAME: message-go
32 | ports:
33 | - 9000:9000
34 |
35 | name:
36 | build:
37 | context: .
38 | dockerfile: ./name-service/Dockerfile
39 | image: hnyexample/name-golang
40 | environment:
41 | <<: *common-env
42 | OTEL_SERVICE_NAME: name-go
43 | ports:
44 | - 8000:8000
45 |
46 | year:
47 | build:
48 | context: .
49 | dockerfile: ./year-service/Dockerfile
50 | image: hnyexample/year-golang
51 | environment:
52 | <<: *common-env
53 | OTEL_SERVICE_NAME: year-go
54 | ports:
55 | - 6001:6001
56 |
57 | redis:
58 | image: redis:latest
59 | ports:
60 | - "127.0.0.1:6379:6379"
61 |
62 |
--------------------------------------------------------------------------------
/golang/frontend/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.20 AS build
2 | WORKDIR /src
3 | ENV CGO_ENABLED=0
4 |
5 | COPY go.mod .
6 | COPY go.sum .
7 | COPY frontend/. .
8 |
9 | RUN go get
10 | RUN go build -o /out/frontend .
11 |
12 | FROM scratch AS bin
13 | WORKDIR /app
14 | COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
15 | COPY --from=build /out/frontend /app/
16 | CMD ["/app/frontend"]
17 |
--------------------------------------------------------------------------------
/golang/message-service/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.20 AS build
2 | WORKDIR /src
3 | ENV CGO_ENABLED=0
4 |
5 | COPY go.mod .
6 | COPY go.sum .
7 | COPY message-service/. .
8 |
9 | RUN go build -o /out/message-service .
10 |
11 | FROM scratch AS bin
12 | WORKDIR /app
13 | COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
14 | COPY --from=build /out/message-service /app/
15 | CMD ["/app/message-service"]
16 |
--------------------------------------------------------------------------------
/golang/message-service/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "math/rand"
7 | "net/http"
8 | "os"
9 | "time"
10 |
11 | "github.com/honeycombio/beeline-go"
12 | "github.com/honeycombio/beeline-go/propagation"
13 | "github.com/honeycombio/beeline-go/wrappers/config"
14 | "github.com/honeycombio/beeline-go/wrappers/hnynethttp"
15 | )
16 |
17 | func getHttpEndpoint() string {
18 | apiEndpoint, exists := os.LookupEnv("HONEYCOMB_API_ENDPOINT")
19 | if !exists {
20 | apiEndpoint = "https://api.honeycomb.io"
21 | }
22 | return apiEndpoint
23 | }
24 |
25 | func traceParserHook(r *http.Request) *propagation.PropagationContext {
26 | headers := map[string]string{
27 | "traceparent": r.Header.Get("traceparent"),
28 | }
29 | ctx := r.Context()
30 | ctx, prop, err := propagation.UnmarshalW3CTraceContext(ctx, headers)
31 | if err != nil {
32 | fmt.Println("Error unmarshaling header")
33 | fmt.Println(err)
34 | }
35 | return prop
36 | }
37 |
38 | func main() {
39 | beeline.Init(beeline.Config{
40 | APIHost: getHttpEndpoint(),
41 | WriteKey: os.Getenv("HONEYCOMB_API_KEY"),
42 | Dataset: os.Getenv("HONEYCOMB_DATASET"),
43 | ServiceName: "message-go",
44 | })
45 | defer beeline.Close()
46 |
47 | messages := []string{
48 | "how are you?", "how are you doing?", "what's good?", "what's up?", "how do you do?",
49 | "sup?", "good day to you", "how are things?", "howzit?", "woohoo",
50 | }
51 |
52 | mux := http.NewServeMux()
53 | mux.HandleFunc("/message", func(w http.ResponseWriter, r *http.Request) {
54 | ctx := r.Context()
55 | ctx, span := beeline.StartSpan(ctx, "📖 look up message ✨")
56 | defer span.Send()
57 | rand.Seed(time.Now().UnixNano())
58 | time.Sleep(time.Duration(rand.Intn(5)) * time.Millisecond)
59 | _, _ = fmt.Fprintf(w, messages[rand.Intn(len(messages))])
60 | })
61 |
62 | log.Println("Listening on http://localhost:9000/message")
63 | log.Fatal(
64 | http.ListenAndServe(
65 | ":9000",
66 | hnynethttp.WrapHandlerWithConfig(
67 | mux,
68 | config.HTTPIncomingConfig{
69 | HTTPParserHook: traceParserHook})))
70 | }
71 |
--------------------------------------------------------------------------------
/golang/name-service/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.20 AS build
2 | WORKDIR /src
3 | ENV CGO_ENABLED=0
4 |
5 | COPY go.mod .
6 | COPY go.sum .
7 | COPY name-service/. .
8 |
9 | RUN go build -o /out/name-service ./main.go
10 |
11 | FROM scratch AS bin
12 | WORKDIR /app
13 | COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
14 | COPY --from=build /out/name-service /app/
15 | CMD ["/app/name-service"]
16 |
--------------------------------------------------------------------------------
/golang/year-service/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.20 AS build
2 | WORKDIR /src
3 | ENV CGO_ENABLED=0
4 |
5 | COPY go.mod .
6 | COPY go.sum .
7 | COPY year-service/. .
8 |
9 | RUN go build -o /out/year-service ./main.go
10 |
11 | FROM scratch AS bin
12 | WORKDIR /app
13 | COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
14 | COPY --from=build /out/year-service /app/
15 | CMD ["/app/year-service"]
16 |
--------------------------------------------------------------------------------
/golang/year-service/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "log"
7 | "math/rand"
8 | "net/http"
9 | "os"
10 | "time"
11 |
12 | _ "github.com/honeycombio/honeycomb-opentelemetry-go"
13 |
14 | "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
15 | "go.opentelemetry.io/otel"
16 | "go.opentelemetry.io/otel/exporters/otlp/otlptrace"
17 | "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
18 | "go.opentelemetry.io/otel/propagation"
19 | sdktrace "go.opentelemetry.io/otel/sdk/trace"
20 | "go.opentelemetry.io/otel/trace"
21 | )
22 |
23 | var (
24 | tracer trace.Tracer
25 | )
26 |
27 | func calculateYear() int {
28 | years := []int{2016, 2017, 2018, 2019, 2020}
29 | rand.Seed(time.Now().UnixNano())
30 | time.Sleep(time.Duration(rand.Intn(5)) * time.Millisecond)
31 | return years[rand.Intn(len(years))]
32 | }
33 |
34 | func yearHandler(w http.ResponseWriter, r *http.Request) {
35 | ctx := r.Context()
36 |
37 | year := func(ctx context.Context) int {
38 | _, span := tracer.Start(ctx, "calculate-year")
39 | defer span.End()
40 |
41 | return calculateYear()
42 | }(ctx)
43 |
44 | _, _ = fmt.Fprintf(w, "%d", year)
45 | }
46 |
47 | func main() {
48 | ctx := context.Background()
49 | exporter, _ := otlptrace.New(ctx, otlptracegrpc.NewClient(
50 | otlptracegrpc.WithEndpoint(os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT")),
51 | otlptracegrpc.WithHeaders(map[string]string{
52 | "x-honeycomb-team": os.Getenv("HONEYCOMB_API_KEY"),
53 | }),
54 | ))
55 | tracerProvider := sdktrace.NewTracerProvider(
56 | sdktrace.WithBatcher(exporter),
57 | )
58 | defer tracerProvider.Shutdown(ctx)
59 |
60 | otel.SetTracerProvider(tracerProvider)
61 | otel.SetTextMapPropagator(
62 | propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}),
63 | )
64 |
65 | tracer = otel.Tracer("greeting-service/year-service")
66 |
67 | mux := http.NewServeMux()
68 | mux.HandleFunc("/year", yearHandler)
69 |
70 | wrappedHandler := otelhttp.NewHandler(mux, "year")
71 |
72 | log.Println("Listening on http://localhost:6001/year")
73 | log.Fatal(http.ListenAndServe(":6001", wrappedHandler))
74 | }
75 |
--------------------------------------------------------------------------------
/java/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: https://EditorConfig.org
2 |
3 | # When opening a file, EditorConfig plugins look for a file named .editorconfig
4 | # in the directory of the opened file and in every parent directory.
5 | # A search for .editorconfig files will stop if the root filepath is reached
6 | # or an EditorConfig file with root=true is found.
7 |
8 | # EditorConfig files are read top to bottom and the most recent rules found
9 | # take precedence. Properties from matching EditorConfig sections are applied
10 | # in the order they were read, so properties in closer files take precedence.
11 |
12 | # Java files
13 | [*.java]
14 |
15 | # indent style and size match root level rules
--------------------------------------------------------------------------------
/java/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | .gradle
3 | agent/
4 | build/
5 | !gradle/wrapper/gradle-wrapper.jar
6 | !**/src/main/**/build/
7 | !**/src/test/**/build/
8 |
9 | ### STS ###
10 | .apt_generated
11 | .classpath
12 | .factorypath
13 | .project
14 | .settings
15 | .springBeans
16 | .sts4-cache
17 | bin/
18 | !**/src/main/**/bin/
19 | !**/src/test/**/bin/
20 |
21 | ### IntelliJ IDEA ###
22 | .idea
23 | *.iws
24 | *.iml
25 | *.ipr
26 | out/
27 | !**/src/main/**/out/
28 | !**/src/test/**/out/
29 |
30 | ### NetBeans ###
31 | /nbproject/private/
32 | /nbbuild/
33 | /dist/
34 | /nbdist/
35 | /.nb-gradle/
36 |
37 | ### VS Code ###
38 | .vscode/
39 |
--------------------------------------------------------------------------------
/java/frontend/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM gradle:8.1.0-jdk17 AS build
2 | WORKDIR /home/gradle/
3 | RUN mkdir ./libs
4 | COPY --chown=gradle:gradle *.gradle ./
5 | COPY --chown=gradle:gradle ./src ./src
6 | RUN gradle bootJar --no-daemon
7 |
8 | FROM eclipse-temurin:17-jdk
9 | VOLUME /tmp
10 | ENV JAVA_AGENT=otel-javaagent.jar
11 | ENV JAVA_TOOL_OPTIONS=-javaagent:${JAVA_AGENT}
12 | COPY --from=build /home/gradle/agent/${JAVA_AGENT} ./${JAVA_AGENT}
13 | COPY --from=build /home/gradle/build/libs/*.jar app.jar
14 | CMD ["java", "-jar", "app.jar"]
15 |
--------------------------------------------------------------------------------
/java/frontend/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'org.springframework.boot' version '3.2.3'
3 | id 'io.spring.dependency-management' version '1.1.4'
4 | id 'java'
5 | }
6 |
7 | group = 'io.honeycomb.examples'
8 | version = '0.0.1-SNAPSHOT'
9 | sourceCompatibility = '17'
10 |
11 | ext {
12 | otelApiVersion = '1.36.0'
13 | otelAgentVersion = '1.33.1'
14 | }
15 |
16 | configurations {
17 | agent
18 | }
19 |
20 | repositories {
21 | mavenCentral()
22 | mavenLocal()
23 | }
24 |
25 | dependencies {
26 | implementation("io.opentelemetry:opentelemetry-api:${otelApiVersion}")
27 | implementation("io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations:${otelAgentVersion}")
28 |
29 | agent "io.opentelemetry.javaagent:opentelemetry-javaagent:${otelAgentVersion}"
30 |
31 | implementation 'org.springframework.boot:spring-boot-starter-web'
32 | testImplementation('org.springframework.boot:spring-boot-starter-test') {
33 | exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
34 | }
35 | }
36 |
37 | task copyAgentJar(type: Copy) {
38 | from configurations.agent {
39 | include '**/opentelemetry-javaagent*.jar'
40 | }
41 | into "agent"
42 | rename { fileName -> "otel-javaagent.jar" }
43 | }
44 |
45 | compileJava.dependsOn copyAgentJar
46 | bootRun.dependsOn copyAgentJar
47 |
48 | bootRun.doFirst {
49 | jvmArgs("-javaagent:agent/otel-javaagent.jar")
50 | }
51 |
52 | clean.doFirst {
53 | delete "agent"
54 | }
55 |
56 | test {
57 | useJUnitPlatform()
58 | }
59 |
--------------------------------------------------------------------------------
/java/frontend/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeycombio/example-greeting-service/6705c90566e5309e583e9c1312d87fe8a220cc1d/java/frontend/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/java/frontend/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/java/frontend/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'frontend-java'
2 |
--------------------------------------------------------------------------------
/java/frontend/src/main/java/io/honeycomb/examples/frontend_java/FrontendApplication.java:
--------------------------------------------------------------------------------
1 | package io.honeycomb.examples.frontend_java;
2 |
3 | import io.opentelemetry.api.GlobalOpenTelemetry;
4 | import io.opentelemetry.api.trace.Tracer;
5 | import org.springframework.boot.SpringApplication;
6 | import org.springframework.boot.autoconfigure.SpringBootApplication;
7 | import org.springframework.context.annotation.Bean;
8 |
9 | @SpringBootApplication
10 | public class FrontendApplication {
11 |
12 | @Bean
13 | public Tracer tracer() {
14 | return GlobalOpenTelemetry.getTracer("frontend-internal");
15 | }
16 |
17 | public static void main(String[] args) {
18 | SpringApplication.run(FrontendApplication.class, args);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/java/frontend/src/main/java/io/honeycomb/examples/frontend_java/GreetingController.java:
--------------------------------------------------------------------------------
1 | package io.honeycomb.examples.frontend_java;
2 |
3 | import io.opentelemetry.api.baggage.Baggage;
4 | import io.opentelemetry.api.trace.Span;
5 | import io.opentelemetry.api.trace.Tracer;
6 | import io.opentelemetry.context.Scope;
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.web.bind.annotation.RequestMapping;
9 | import org.springframework.web.bind.annotation.RestController;
10 |
11 | import java.net.URISyntaxException;
12 |
13 | @RestController
14 | public class GreetingController {
15 | @Autowired
16 | private NameService nameService;
17 |
18 | @Autowired
19 | private MessageService messageService;
20 |
21 | @Autowired
22 | private Tracer tracer;
23 |
24 | @RequestMapping("/greeting")
25 | public String index() throws URISyntaxException {
26 | String name = nameService.getName();
27 | Span.current().setAttribute("app.username", name);
28 |
29 | try (final Scope ignored = Baggage.current()
30 | .toBuilder()
31 | .put("app.username", name)
32 | .build()
33 | .makeCurrent()
34 | ) {
35 | String message = messageService.getMessage();
36 |
37 | Span span = tracer.spanBuilder("🎨 render message ✨").startSpan();
38 | String greeting = String.format("Hello, %s, %s", name, message);
39 | span.end();
40 | return greeting;
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/java/frontend/src/main/java/io/honeycomb/examples/frontend_java/MessageService.java:
--------------------------------------------------------------------------------
1 | package io.honeycomb.examples.frontend_java;
2 |
3 | import java.io.IOException;
4 | import java.net.URI;
5 | import java.net.URISyntaxException;
6 | import java.net.http.HttpClient;
7 | import java.net.http.HttpRequest;
8 | import java.net.http.HttpResponse;
9 |
10 | import org.springframework.beans.factory.annotation.Autowired;
11 | import org.springframework.stereotype.Component;
12 |
13 | import io.opentelemetry.api.trace.Span;
14 | import io.opentelemetry.api.trace.Tracer;
15 | import io.opentelemetry.instrumentation.annotations.WithSpan;
16 |
17 | @Component
18 | public class MessageService {
19 | @Autowired
20 | private Tracer tracer;
21 |
22 | private String message_endpoint() {
23 | String messageEndpointFromEnv = "http://" + System.getenv().getOrDefault("MESSAGE_ENDPOINT", "localhost:9000");
24 | return messageEndpointFromEnv + "/message";
25 | }
26 |
27 | @WithSpan
28 | public String getMessage() throws URISyntaxException {
29 | URI message_uri = new URI(message_endpoint());
30 |
31 | HttpClient client = HttpClient.newHttpClient();
32 | HttpRequest request = HttpRequest.newBuilder()
33 | .uri(message_uri)
34 | .header("accept", "application/json")
35 | .build();
36 | HttpResponse response = null;
37 | try {
38 | Span messageServiceCallSpan = tracer.spanBuilder("✨ call /message ✨").startSpan();
39 | messageServiceCallSpan.makeCurrent();
40 | response = client.send(request, HttpResponse.BodyHandlers.ofString());
41 | messageServiceCallSpan.end();
42 | } catch (IOException | InterruptedException e) {
43 | e.printStackTrace();
44 |
45 | }
46 | return response.body();
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/java/frontend/src/main/java/io/honeycomb/examples/frontend_java/NameService.java:
--------------------------------------------------------------------------------
1 | package io.honeycomb.examples.frontend_java;
2 |
3 | import java.io.IOException;
4 | import java.net.URI;
5 | import java.net.URISyntaxException;
6 | import java.net.http.HttpClient;
7 | import java.net.http.HttpRequest;
8 | import java.net.http.HttpResponse;
9 |
10 | import org.springframework.beans.factory.annotation.Autowired;
11 | import org.springframework.stereotype.Component;
12 |
13 | import io.opentelemetry.api.trace.Span;
14 | import io.opentelemetry.api.trace.Tracer;
15 | import io.opentelemetry.instrumentation.annotations.WithSpan;
16 |
17 | @Component
18 | public class NameService {
19 | @Autowired
20 | private Tracer tracer;
21 |
22 | private String name_endpoint() {
23 | String nameEndpointFromEnv = "http://" + System.getenv().getOrDefault("NAME_ENDPOINT", "localhost:8000");
24 | return nameEndpointFromEnv + "/name";
25 | }
26 | @WithSpan
27 | public String getName() throws URISyntaxException {
28 | URI name_uri = new URI(name_endpoint());
29 |
30 | HttpClient client = HttpClient.newHttpClient();
31 | HttpRequest request = HttpRequest.newBuilder()
32 | .uri(name_uri)
33 | .header("accept", "application/json")
34 | .build();
35 | HttpResponse response = null;
36 | try {
37 | Span nameServiceCallSpan = tracer.spanBuilder("✨ call /name ✨").startSpan();
38 | nameServiceCallSpan.makeCurrent();
39 | response = client.send(request, HttpResponse.BodyHandlers.ofString());
40 | nameServiceCallSpan.end();
41 | } catch (IOException | InterruptedException e) {
42 | e.printStackTrace();
43 | }
44 | return response.body();
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/java/frontend/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | server.port=7777
2 |
--------------------------------------------------------------------------------
/java/message-service/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | .gradle
3 | agent/
4 | build/
5 | !gradle/wrapper/gradle-wrapper.jar
6 | !**/src/main/**/build/
7 | !**/src/test/**/build/
8 |
9 | ### STS ###
10 | .apt_generated
11 | .classpath
12 | .factorypath
13 | .project
14 | .settings
15 | .springBeans
16 | .sts4-cache
17 | bin/
18 | !**/src/main/**/bin/
19 | !**/src/test/**/bin/
20 |
21 | ### IntelliJ IDEA ###
22 | .idea
23 | *.iws
24 | *.iml
25 | *.ipr
26 | out/
27 | !**/src/main/**/out/
28 | !**/src/test/**/out/
29 |
30 | ### NetBeans ###
31 | /nbproject/private/
32 | /nbbuild/
33 | /dist/
34 | /nbdist/
35 | /.nb-gradle/
36 |
37 | ### VS Code ###
38 | .vscode/
39 |
--------------------------------------------------------------------------------
/java/message-service/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM gradle:8.1.0-jdk17 AS build
2 | WORKDIR /home/gradle/
3 | RUN mkdir ./libs
4 | COPY --chown=gradle:gradle *.gradle ./
5 | COPY --chown=gradle:gradle ./src ./src
6 | RUN gradle bootJar --no-daemon
7 |
8 | FROM eclipse-temurin:17-jdk
9 | VOLUME /tmp
10 | ENV HONEY_JAVA_AGENT=honey-javaagent.jar
11 | ENV JAVA_TOOL_OPTIONS=-javaagent:${HONEY_JAVA_AGENT}
12 | COPY --from=build /home/gradle/agent/${HONEY_JAVA_AGENT} ./${HONEY_JAVA_AGENT}
13 | COPY --from=build /home/gradle/build/libs/*.jar app.jar
14 | CMD ["java", "-jar", "app.jar"]
15 |
--------------------------------------------------------------------------------
/java/message-service/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'org.springframework.boot' version '3.2.3'
3 | id 'io.spring.dependency-management' version '1.1.4'
4 | id 'java'
5 | }
6 |
7 | group = 'io.honeycomb.examples'
8 | version = '0.0.1-SNAPSHOT'
9 | sourceCompatibility = '17'
10 |
11 | ext {
12 | distroVersion = '1.6.0'
13 | }
14 |
15 | configurations {
16 | agent
17 | }
18 |
19 | repositories {
20 | mavenCentral()
21 | mavenLocal()
22 | }
23 |
24 | dependencies {
25 | agent "io.honeycomb:honeycomb-opentelemetry-javaagent:${distroVersion}"
26 |
27 | implementation("io.honeycomb:honeycomb-opentelemetry-sdk:${distroVersion}")
28 | implementation 'org.springframework.boot:spring-boot-starter-web'
29 | testImplementation('org.springframework.boot:spring-boot-starter-test') {
30 | exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
31 | }
32 | }
33 |
34 | task copyAgentJar(type: Copy) {
35 | from configurations.agent {
36 | include '**/honeycomb-opentelemetry-javaagent*.jar'
37 | }
38 | into "agent"
39 | rename { fileName -> "honey-javaagent.jar" }
40 | }
41 |
42 | compileJava.dependsOn copyAgentJar
43 | bootRun.dependsOn copyAgentJar
44 |
45 | bootRun.doFirst {
46 | jvmArgs("-javaagent:agent/honey-javaagent.jar")
47 | }
48 |
49 | clean.doFirst {
50 | delete "agent"
51 | }
52 |
53 | test {
54 | useJUnitPlatform()
55 | }
56 |
--------------------------------------------------------------------------------
/java/message-service/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeycombio/example-greeting-service/6705c90566e5309e583e9c1312d87fe8a220cc1d/java/message-service/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/java/message-service/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/java/message-service/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'message-service-java'
2 |
--------------------------------------------------------------------------------
/java/message-service/src/main/java/io/honeycomb/examples/message/MessageApplication.java:
--------------------------------------------------------------------------------
1 | package io.honeycomb.examples.message;
2 |
3 | import io.opentelemetry.api.GlobalOpenTelemetry;
4 | import io.opentelemetry.api.trace.Tracer;
5 | import org.springframework.boot.SpringApplication;
6 | import org.springframework.boot.autoconfigure.SpringBootApplication;
7 | import org.springframework.context.annotation.Bean;
8 |
9 | @SpringBootApplication
10 | public class MessageApplication {
11 | @Bean
12 | public Tracer tracer() {
13 | return GlobalOpenTelemetry.getTracer("frontend-internal");
14 | }
15 |
16 | public static void main(String[] args) {
17 | SpringApplication.run(MessageApplication.class, args);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/java/message-service/src/main/java/io/honeycomb/examples/message/MessageEndpoints.java:
--------------------------------------------------------------------------------
1 | package io.honeycomb.examples.message;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.util.MimeTypeUtils;
5 | import org.springframework.web.bind.annotation.*;
6 |
7 | @RestController
8 | @RequestMapping(value = "message")
9 | public class MessageEndpoints {
10 | @Autowired
11 | private MessageService messageService;
12 |
13 | @GetMapping(produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
14 | public String message() {
15 | return messageService.getMessage();
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/java/message-service/src/main/java/io/honeycomb/examples/message/MessageService.java:
--------------------------------------------------------------------------------
1 | package io.honeycomb.examples.message;
2 |
3 | import java.util.Random;
4 |
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.stereotype.Component;
7 |
8 | import io.opentelemetry.api.trace.Span;
9 | import io.opentelemetry.api.trace.Tracer;
10 | import io.opentelemetry.instrumentation.annotations.WithSpan;
11 |
12 | @Component
13 | public class MessageService {
14 | @Autowired
15 | private Tracer tracer;
16 |
17 | private static final String[] MESSAGES = new String[] { "how are you?", "how are you doing?", "what's good?", "what's up?", "how do you do?",
18 | "sup?", "good day to you", "how are things?", "howzit?", "woohoo" };
19 | private static final Random generator = new Random();
20 |
21 | @WithSpan
22 | public String getMessage() {
23 | return pickMessage();
24 | }
25 |
26 | private String pickMessage() {
27 | Span messageLookupSpan = tracer.spanBuilder("📖 look up message ✨").startSpan();
28 | messageLookupSpan.makeCurrent();
29 | int rnd = generator.nextInt(MESSAGES.length);
30 | String message = MESSAGES[rnd];
31 | messageLookupSpan.end();
32 | return message;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/java/message-service/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | server.port=9000
2 |
--------------------------------------------------------------------------------
/java/name-service/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | .gradle
3 | agent/
4 | build/
5 | !gradle/wrapper/gradle-wrapper.jar
6 | !**/src/main/**/build/
7 | !**/src/test/**/build/
8 |
9 | ### STS ###
10 | .apt_generated
11 | .classpath
12 | .factorypath
13 | .project
14 | .settings
15 | .springBeans
16 | .sts4-cache
17 | bin/
18 | !**/src/main/**/bin/
19 | !**/src/test/**/bin/
20 |
21 | ### IntelliJ IDEA ###
22 | .idea
23 | *.iws
24 | *.iml
25 | *.ipr
26 | out/
27 | !**/src/main/**/out/
28 | !**/src/test/**/out/
29 |
30 | ### NetBeans ###
31 | /nbproject/private/
32 | /nbbuild/
33 | /dist/
34 | /nbdist/
35 | /.nb-gradle/
36 |
37 | ### VS Code ###
38 | .vscode/
39 |
--------------------------------------------------------------------------------
/java/name-service/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM gradle:8.1.0-jdk17 AS build
2 | WORKDIR /home/gradle/
3 | RUN mkdir ./libs
4 | COPY --chown=gradle:gradle *.gradle ./
5 | COPY --chown=gradle:gradle ./src ./src
6 | RUN gradle bootJar --no-daemon
7 |
8 | FROM eclipse-temurin:17-jdk
9 | VOLUME /tmp
10 | ENV HONEY_JAVA_AGENT=honey-javaagent.jar
11 | ENV JAVA_TOOL_OPTIONS=-javaagent:${HONEY_JAVA_AGENT}
12 | COPY --from=build /home/gradle/agent/${HONEY_JAVA_AGENT} ./${HONEY_JAVA_AGENT}
13 | COPY --from=build /home/gradle/build/libs/*.jar app.jar
14 | CMD ["java", "-jar", "app.jar"]
15 |
--------------------------------------------------------------------------------
/java/name-service/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'org.springframework.boot' version '3.2.3'
3 | id 'io.spring.dependency-management' version '1.1.4'
4 | id 'java'
5 | }
6 |
7 | group = 'io.honeycomb.examples'
8 | version = '0.0.1-SNAPSHOT'
9 | sourceCompatibility = '17'
10 |
11 | ext {
12 | distroVersion = '1.6.0'
13 | }
14 |
15 | configurations {
16 | agent
17 | }
18 |
19 | repositories {
20 | mavenCentral()
21 | mavenLocal()
22 | }
23 |
24 | dependencies {
25 | agent "io.honeycomb:honeycomb-opentelemetry-javaagent:${distroVersion}"
26 |
27 | implementation("io.honeycomb:honeycomb-opentelemetry-sdk:${distroVersion}")
28 | implementation 'org.springframework.boot:spring-boot-starter-web'
29 | testImplementation('org.springframework.boot:spring-boot-starter-test') {
30 | exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
31 | }
32 | }
33 |
34 | task copyAgentJar(type: Copy) {
35 | from configurations.agent {
36 | include '**/honeycomb-opentelemetry-javaagent*.jar'
37 | }
38 | into "agent"
39 | rename { fileName -> "honey-javaagent.jar" }
40 | }
41 |
42 | compileJava.dependsOn copyAgentJar
43 | bootRun.dependsOn copyAgentJar
44 |
45 | bootRun.doFirst {
46 | jvmArgs("-javaagent:agent/honey-javaagent.jar")
47 | }
48 |
49 | clean.doFirst {
50 | delete "agent"
51 | }
52 |
53 | test {
54 | useJUnitPlatform()
55 | }
56 |
--------------------------------------------------------------------------------
/java/name-service/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeycombio/example-greeting-service/6705c90566e5309e583e9c1312d87fe8a220cc1d/java/name-service/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/java/name-service/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/java/name-service/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'name-java'
2 |
--------------------------------------------------------------------------------
/java/name-service/src/main/java/io/honeycomb/examples/name/MessageService.java:
--------------------------------------------------------------------------------
1 | package io.honeycomb.examples.name;
2 |
3 | import java.io.IOException;
4 | import java.net.URI;
5 | import java.net.URISyntaxException;
6 | import java.net.http.HttpClient;
7 | import java.net.http.HttpRequest;
8 | import java.net.http.HttpResponse;
9 |
10 | import org.springframework.beans.factory.annotation.Autowired;
11 | import org.springframework.stereotype.Component;
12 |
13 | import io.opentelemetry.api.trace.Span;
14 | import io.opentelemetry.api.trace.Tracer;
15 | import io.opentelemetry.instrumentation.annotations.WithSpan;
16 |
17 | @Component
18 | public class MessageService {
19 | @Autowired
20 | private Tracer tracer;
21 |
22 | private String message_endpoint = "http://localhost:9000/message";
23 |
24 | @WithSpan
25 | public String getMessage() throws URISyntaxException {
26 | URI message_uri = new URI(message_endpoint);
27 |
28 | HttpClient client = HttpClient.newHttpClient();
29 | HttpRequest request = HttpRequest.newBuilder()
30 | .uri(message_uri)
31 | .header("accept", "application/json")
32 | .build();
33 | HttpResponse response = null;
34 | try {
35 | Span messageServiceCallSpan = tracer.spanBuilder("✨ call /name ✨").startSpan();
36 | messageServiceCallSpan.makeCurrent();
37 | response = client.send(request, HttpResponse.BodyHandlers.ofString());
38 | messageServiceCallSpan.end();
39 | } catch (IOException | InterruptedException e) {
40 | e.printStackTrace();
41 |
42 | }
43 | return response.body();
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/java/name-service/src/main/java/io/honeycomb/examples/name/NameApplication.java:
--------------------------------------------------------------------------------
1 | package io.honeycomb.examples.name;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.context.annotation.Bean;
6 |
7 | import io.opentelemetry.api.GlobalOpenTelemetry;
8 | import io.opentelemetry.api.trace.Tracer;
9 |
10 | @SpringBootApplication
11 | public class NameApplication {
12 |
13 | @Bean
14 | public Tracer tracer() {
15 | return GlobalOpenTelemetry.getTracer("frontend-internal");
16 | }
17 |
18 | public static void main(String[] args) {
19 | SpringApplication.run(NameApplication.class, args);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/java/name-service/src/main/java/io/honeycomb/examples/name/NameController.java:
--------------------------------------------------------------------------------
1 | package io.honeycomb.examples.name;
2 |
3 | import java.io.IOException;
4 | import java.net.URISyntaxException;
5 |
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.web.bind.annotation.RequestMapping;
8 | import org.springframework.web.bind.annotation.RestController;
9 |
10 | @RestController
11 | public class NameController {
12 | @Autowired
13 | private NameService nameService;
14 |
15 | @RequestMapping("/name")
16 | public String index() throws URISyntaxException, IOException, InterruptedException {
17 | return nameService.getName();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/java/name-service/src/main/java/io/honeycomb/examples/name/NameService.java:
--------------------------------------------------------------------------------
1 | package io.honeycomb.examples.name;
2 |
3 | import java.io.IOException;
4 | import java.net.URISyntaxException;
5 | import java.util.HashMap;
6 | import java.util.Map;
7 | import java.util.Random;
8 |
9 | import org.springframework.beans.factory.annotation.Autowired;
10 | import org.springframework.stereotype.Component;
11 |
12 | import io.opentelemetry.api.trace.Span;
13 | import io.opentelemetry.api.trace.Tracer;
14 | import io.opentelemetry.instrumentation.annotations.WithSpan;
15 |
16 | @Component
17 | public class NameService {
18 | @Autowired
19 | private Tracer tracer;
20 |
21 | private static final Map namesByYear = new HashMap<>() {{
22 | this.put(2015, new String[]{"sophia", "jackson", "emma", "aiden", "olivia", "liam", "ava", "lucas", "mia", "noah"});
23 | this.put(2016, new String[]{"sophia", "jackson", "emma", "aiden", "olivia", "lucas", "ava", "liam", "mia", "noah"});
24 | this.put(2017, new String[]{"sophia", "jackson", "olivia", "liam", "emma", "noah", "ava", "aiden", "isabella", "lucas"});
25 | this.put(2018, new String[]{"sophia", "jackson", "olivia", "liam", "emma", "noah", "ava", "aiden", "isabella", "caden"});
26 | this.put(2019, new String[]{"sophia", "liam", "olivia", "jackson", "emma", "noah", "ava", "aiden", "aira", "grayson"});
27 | this.put(2020, new String[]{"olivia", "noah", "emma", "liam", "ava", "elijah", "isabella", "oliver", "sophia", "lucas"});
28 | }};
29 |
30 | private static final Random generator = new Random();
31 |
32 | @Autowired
33 | YearService yearService;
34 |
35 | @WithSpan
36 | public String getName() throws NumberFormatException, URISyntaxException, IOException, InterruptedException {
37 | int year = Integer.parseInt(yearService.getYear());
38 |
39 | Span nameLookupSpan = tracer.spanBuilder("📖 look up name based on year ✨").startSpan();
40 | nameLookupSpan.makeCurrent();
41 | String[] candidateNames = namesByYear.get(year);
42 | int rnd = generator.nextInt(candidateNames.length);
43 | String name = candidateNames[rnd];
44 | nameLookupSpan.end();
45 |
46 | return name;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/java/name-service/src/main/java/io/honeycomb/examples/name/YearService.java:
--------------------------------------------------------------------------------
1 | package io.honeycomb.examples.name;
2 |
3 | import java.io.IOException;
4 | import java.net.URI;
5 | import java.net.URISyntaxException;
6 | import java.net.http.HttpClient;
7 | import java.net.http.HttpRequest;
8 | import java.net.http.HttpResponse;
9 |
10 | import org.springframework.beans.factory.annotation.Autowired;
11 | import org.springframework.stereotype.Component;
12 |
13 | import io.opentelemetry.api.trace.Span;
14 | import io.opentelemetry.api.trace.Tracer;
15 | import io.opentelemetry.instrumentation.annotations.WithSpan;
16 |
17 | @Component
18 | public class YearService {
19 | @Autowired
20 | private Tracer tracer;
21 |
22 | private String year_endpoint() {
23 | String yearEndpointFromEnv = "http://" + System.getenv().getOrDefault("YEAR_ENDPOINT", "localhost:6001");
24 | return yearEndpointFromEnv + "/year";
25 | }
26 |
27 | @WithSpan
28 | public String getYear() throws URISyntaxException {
29 | URI yearUri = new URI(year_endpoint());
30 |
31 | HttpClient client = HttpClient.newHttpClient();
32 | HttpRequest request = HttpRequest.newBuilder()
33 | .uri(yearUri)
34 | .header("accept", "application/json")
35 | .build();
36 | HttpResponse response = null;
37 | Span yearServiceCallSpan = tracer.spanBuilder("✨ call /year ✨").startSpan();
38 | yearServiceCallSpan.makeCurrent();
39 | try {
40 | response = client.send(request, HttpResponse.BodyHandlers.ofString());
41 | } catch (IOException | InterruptedException e) {
42 | e.printStackTrace();
43 | } finally {
44 | yearServiceCallSpan.end();
45 | }
46 | return response == null
47 | ? ""
48 | : response.body();
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/java/name-service/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | server.port=8000
2 |
--------------------------------------------------------------------------------
/java/year-service/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | .gradle
3 | agent/
4 | build/
5 | !gradle/wrapper/gradle-wrapper.jar
6 | !**/src/main/**/build/
7 | !**/src/test/**/build/
8 |
9 | ### STS ###
10 | .apt_generated
11 | .classpath
12 | .factorypath
13 | .project
14 | .settings
15 | .springBeans
16 | .sts4-cache
17 | bin/
18 | !**/src/main/**/bin/
19 | !**/src/test/**/bin/
20 |
21 | ### IntelliJ IDEA ###
22 | .idea
23 | *.iws
24 | *.iml
25 | *.ipr
26 | out/
27 | !**/src/main/**/out/
28 | !**/src/test/**/out/
29 |
30 | ### NetBeans ###
31 | /nbproject/private/
32 | /nbbuild/
33 | /dist/
34 | /nbdist/
35 | /.nb-gradle/
36 |
37 | ### VS Code ###
38 | .vscode/
39 |
--------------------------------------------------------------------------------
/java/year-service/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM gradle:8.1.0-jdk17 AS build
2 | WORKDIR /home/gradle/
3 | RUN mkdir ./libs
4 | COPY --chown=gradle:gradle *.gradle ./
5 | COPY --chown=gradle:gradle ./src ./src
6 | RUN gradle bootJar --no-daemon
7 |
8 | FROM eclipse-temurin:17-jdk
9 | VOLUME /tmp
10 | ENV JAVA_AGENT=otel-javaagent.jar
11 | ENV JAVA_TOOL_OPTIONS=-javaagent:${JAVA_AGENT}
12 | COPY --from=build /home/gradle/agent/${JAVA_AGENT} ./${JAVA_AGENT}
13 | COPY --from=build /home/gradle/build/libs/*.jar app.jar
14 | CMD ["java", "-jar", "app.jar"]
15 |
--------------------------------------------------------------------------------
/java/year-service/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'org.springframework.boot' version '3.2.3'
3 | id 'io.spring.dependency-management' version '1.1.4'
4 | id 'java'
5 | }
6 |
7 | group = 'io.honeycomb.examples'
8 | version = '0.0.1-SNAPSHOT'
9 | sourceCompatibility = '17'
10 |
11 | ext {
12 | otelAlphaVersion = '1.33.1-alpha'
13 | otelApiVersion = '1.36.0'
14 | otelAgentVersion = '1.33.1'
15 | }
16 |
17 | configurations {
18 | agent
19 | }
20 |
21 | repositories {
22 | mavenCentral()
23 | mavenLocal()
24 | }
25 |
26 | dependencies {
27 | implementation("org.apache.logging.log4j:log4j-api:2.17.2")
28 | implementation("org.apache.logging.log4j:log4j-core:2.17.2")
29 |
30 | implementation("io.opentelemetry:opentelemetry-api:${otelApiVersion}")
31 | implementation("io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations:${otelAgentVersion}")
32 |
33 | agent "io.opentelemetry.javaagent:opentelemetry-javaagent:${otelAgentVersion}"
34 |
35 | implementation 'org.springframework.boot:spring-boot-starter-web'
36 | testImplementation('org.springframework.boot:spring-boot-starter-test') {
37 | exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
38 | }
39 | }
40 |
41 | task copyAgentJar(type: Copy) {
42 | from configurations.agent {
43 | include '**/opentelemetry-javaagent*.jar'
44 | }
45 | into "agent"
46 | rename { fileName -> "otel-javaagent.jar" }
47 | }
48 |
49 | compileJava.dependsOn copyAgentJar
50 | bootRun.dependsOn copyAgentJar
51 |
52 | bootRun.doFirst {
53 | jvmArgs("-javaagent:agent/otel-javaagent.jar")
54 | }
55 |
56 | clean.doFirst {
57 | delete "agent"
58 | }
59 |
60 | test {
61 | useJUnitPlatform()
62 | }
63 |
--------------------------------------------------------------------------------
/java/year-service/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeycombio/example-greeting-service/6705c90566e5309e583e9c1312d87fe8a220cc1d/java/year-service/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/java/year-service/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/java/year-service/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'year-service-java'
2 |
--------------------------------------------------------------------------------
/java/year-service/src/main/java/io/honeycomb/examples/javaotlp/YearApplication.java:
--------------------------------------------------------------------------------
1 | package io.honeycomb.examples.javaotlp;
2 |
3 | import io.opentelemetry.api.OpenTelemetry;
4 | import io.opentelemetry.api.GlobalOpenTelemetry;
5 | import org.springframework.boot.SpringApplication;
6 | import org.springframework.boot.autoconfigure.SpringBootApplication;
7 | import org.springframework.context.annotation.Bean;
8 |
9 | @SpringBootApplication
10 | public class YearApplication {
11 |
12 | public static void main(String[] args) {
13 | SpringApplication.run(YearApplication.class, args);
14 | }
15 |
16 | @Bean
17 | public OpenTelemetry openTelemetry() {
18 | return GlobalOpenTelemetry.get();
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/java/year-service/src/main/java/io/honeycomb/examples/javaotlp/YearController.java:
--------------------------------------------------------------------------------
1 | package io.honeycomb.examples.javaotlp;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.web.bind.annotation.RequestMapping;
5 | import org.springframework.web.bind.annotation.RestController;
6 |
7 | @RestController
8 | public class YearController {
9 | @Autowired
10 | private YearService yearService;
11 |
12 | @RequestMapping("/year")
13 | public String index() {
14 | return yearService.getYear();
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/java/year-service/src/main/java/io/honeycomb/examples/javaotlp/YearService.java:
--------------------------------------------------------------------------------
1 | package io.honeycomb.examples.javaotlp;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 | import java.util.Random;
6 | import org.apache.logging.log4j.Logger;
7 | import org.apache.logging.log4j.LogManager;
8 | import org.apache.logging.log4j.message.ObjectMessage;
9 | import org.springframework.stereotype.Component;
10 | import io.opentelemetry.instrumentation.annotations.WithSpan;
11 | import io.opentelemetry.api.OpenTelemetry;
12 | import io.opentelemetry.api.trace.Span;
13 | import io.opentelemetry.api.trace.Tracer;
14 |
15 | @Component
16 | public class YearService {
17 | private static final String[] YEARS = new String[] { "2015", "2016", "2017", "2018", "2019", "2020" };
18 | private static final Random generator = new Random();
19 |
20 | private Tracer tracer;
21 | private static final Logger logger = LogManager.getLogger("my-logger");
22 |
23 | public YearService(OpenTelemetry openTelemetry) {
24 | tracer = openTelemetry.getTracer("year-tracer");
25 | }
26 |
27 | @WithSpan
28 | public String getYear() {
29 | Span span = tracer.spanBuilder("🗓 get-a-year ✨").startSpan();
30 | int rnd = generator.nextInt(YEARS.length);
31 | String year = YEARS[rnd];
32 | Map mapMessage = new HashMap<>();
33 | mapMessage.put("selected_year", year);
34 | logger.info(new ObjectMessage(mapMessage));
35 | span.end();
36 |
37 | return year;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/java/year-service/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | server.port=6001
2 |
--------------------------------------------------------------------------------
/java/year-service/src/test/java/io/honeycomb/examples/javaotlp/YearApplicationTests.java:
--------------------------------------------------------------------------------
1 | package io.honeycomb.examples.javaotlp;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.boot.test.context.SpringBootTest;
5 |
6 | @SpringBootTest
7 | class YearApplicationTests {
8 |
9 | @Test
10 | void contextLoads() {
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/node/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: https://EditorConfig.org
2 |
3 | # When opening a file, EditorConfig plugins look for a file named .editorconfig
4 | # in the directory of the opened file and in every parent directory.
5 | # A search for .editorconfig files will stop if the root filepath is reached
6 | # or an EditorConfig file with root=true is found.
7 |
8 | # EditorConfig files are read top to bottom and the most recent rules found
9 | # take precedence. Properties from matching EditorConfig sections are applied
10 | # in the order they were read, so properties in closer files take precedence.
11 |
12 | # JavaScript, TypeScript files
13 | [*.js, *.ts]
14 |
15 | indent_size = 2
16 | insert_final_newline = true
17 | trim_trailing_whitespace = true
--------------------------------------------------------------------------------
/node/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | tilt_config.json
--------------------------------------------------------------------------------
/node/.tool-versions:
--------------------------------------------------------------------------------
1 | nodejs 18.16.0
2 |
--------------------------------------------------------------------------------
/node/README.md:
--------------------------------------------------------------------------------
1 | # Node Greeting Services
2 |
3 | ## Notes
4 |
5 | - Frontend, message, and name services use beelines
6 | - Year service uses vanilla OTel
7 |
8 | ## How to run
9 |
10 | **Required**: set environment variables
11 |
12 | - HONEYCOMB_API_KEY
13 | - HONEYCOMB_DATASET
14 |
15 | ### Install Node
16 | If you're using option 1 or 3 listed below, you will have to install node on your machine. The preferred way to install node is with [asdf](https://asdf-vm.com/guide/getting-started.html#_1-install-dependencies).
17 |
18 | Once you have asdf set up, run the following to install node. Check the version needed in the `.tool-versions` file in the root of this repo.
19 |
20 | ```
21 | asdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs.git
22 | ```
23 |
24 | ```
25 | asdf install nodejs
26 | ```
27 |
28 | ### Option 1: One service at a time
29 |
30 | - In each individual service directory, run `npm start`
31 |
32 | ### Option 2: All Node Services via Docker
33 |
34 | - In Node directory, run `docker-compose up --build`
35 |
36 | ### Option 3: All Node Services via Tilt
37 |
38 | In top-level directory run `tilt up node`
39 |
40 | ## See it in action
41 |
42 | `curl localhost:7777/greeting` for greeting
43 |
44 | `curl localhost:9000/message` for message only
45 |
46 | `curl localhost:8000/name` for name only
47 |
48 | `curl localhost:6001` for year only
49 |
--------------------------------------------------------------------------------
/node/frontend-service/.dockerignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log
--------------------------------------------------------------------------------
/node/frontend-service/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | trailingComma: 'es5',
3 | tabWidth: 2,
4 | singleQuote: true,
5 | printWidth: 100,
6 | };
7 |
--------------------------------------------------------------------------------
/node/frontend-service/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:18
2 |
3 | # Create app directory
4 | WORKDIR /usr/src/app
5 |
6 | # Install app dependencies
7 | # A wildcard is used to ensure both package.json AND package-lock.json are copied
8 | # where available (npm@5+)
9 | COPY package*.json ./
10 |
11 | RUN npm install
12 | # If you are building your code for production
13 | # RUN npm ci --only=production
14 |
15 | # Bundle app source
16 | COPY . .
17 |
18 | EXPOSE 7777
19 | CMD [ "npm", "start" ]
20 |
--------------------------------------------------------------------------------
/node/frontend-service/main.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const opentelemetry = require('@opentelemetry/api');
3 |
4 | const express = require('express');
5 | const fetch = require('node-fetch');
6 | const cors = require('cors');
7 |
8 | // Constants
9 | const PORT = 7777;
10 | const HOST = '0.0.0.0';
11 | const MESSAGE_ENDPOINT = process.env.MESSAGE_ENDPOINT || 'localhost:9000';
12 | const NAME_ENDPOINT = process.env.NAME_ENDPOINT || 'localhost:8000';
13 |
14 | console.log(NAME_ENDPOINT)
15 | const nameUrl = `${NAME_ENDPOINT}/name`;
16 | const messageUrl = `${MESSAGE_ENDPOINT}/message`;
17 |
18 | // App
19 | const app = express();
20 |
21 | // CORS for use with web frontend
22 | const corsOptions = {
23 | origin: ['http://localhost:8080'],
24 | optionsSuccessStatus: 200
25 | };
26 |
27 | app.use(cors(corsOptions))
28 |
29 | const tracer = opentelemetry.trace.getTracer(
30 | 'default'
31 | );
32 |
33 | app.get('/greeting', async (req, res) => {
34 | const greetingSpan = tracer.startSpan('✨ preparing greeting ✨');
35 | greetingSpan.end()
36 |
37 | const nameSpan = tracer.startSpan('✨ call /name ✨');
38 | const name = await getName(nameUrl);
39 | nameSpan.setAttribute("app.user_name", name);
40 | nameSpan.end()
41 |
42 | const messageSpan = tracer.startSpan('✨ call /message ✨');
43 | const message = await getMessage(messageUrl);
44 | messageSpan.setAttribute("app.user_message", message);
45 | messageSpan.end()
46 |
47 | const responseSpan = tracer.startSpan('✨ post response ✨');
48 | res.send(`Hello ${name}, ${message}`);
49 | responseSpan.end()
50 | });
51 |
52 | const getName = (url) =>
53 | fetch(url)
54 | .then((data) => {
55 | return data.text();
56 | })
57 | .then((text) => {
58 | console.log(text);
59 | return text;
60 | })
61 | .catch((err) => console.error(`Problem getting name: ${err}`));
62 |
63 | const getMessage = (url) =>
64 | fetch(url)
65 | .then((data) => {
66 | return data.text();
67 | })
68 | .then((text) => {
69 | console.log(text);
70 | return text;
71 | })
72 | .catch((err) => console.error(`Problem getting message: ${err}`));
73 |
74 | app.listen(PORT, HOST);
75 | console.log(`Running node frontend service on http://${HOST}:${PORT}`);
76 |
--------------------------------------------------------------------------------
/node/frontend-service/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "frontend",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "main.js",
6 | "scripts": {
7 | "dev": "nodemon main.js",
8 | "start": "node -r ./tracing.js main.js",
9 | "test": "echo \"Error: no test specified\" && exit 1"
10 | },
11 | "author": "",
12 | "license": "ISC",
13 | "dependencies": {
14 | "@honeycombio/opentelemetry-node": "^0.7.0",
15 | "@opentelemetry/api": "^1.4.1",
16 | "@opentelemetry/auto-instrumentations-node": "^0.42.0",
17 | "cors": "^2.8.5",
18 | "express": "^4.18.3",
19 | "node-fetch": "^2.6.1"
20 | },
21 | "devDependencies": {
22 | "nodemon": "^3.0.1",
23 | "prettier": "^3.0.0"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/node/frontend-service/tracing.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const { HoneycombSDK } = require('@honeycombio/opentelemetry-node');
4 | const {
5 | getNodeAutoInstrumentations,
6 | } = require('@opentelemetry/auto-instrumentations-node');
7 |
8 | const sdk = new HoneycombSDK({
9 | instrumentations: [getNodeAutoInstrumentations({
10 | '@opentelemetry/instrumentation-fs': {
11 | enabled: false,
12 | },
13 | })],
14 | serviceName: "frontend-node",
15 | });
16 |
17 | sdk.start()
18 |
--------------------------------------------------------------------------------
/node/message-service/.dockerignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log
--------------------------------------------------------------------------------
/node/message-service/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | trailingComma: 'es5',
3 | tabWidth: 2,
4 | singleQuote: true,
5 | printWidth: 100,
6 | };
7 |
--------------------------------------------------------------------------------
/node/message-service/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:18
2 |
3 | # Create app directory
4 | WORKDIR /usr/src/app
5 |
6 | # Install app dependencies
7 | # A wildcard is used to ensure both package.json AND package-lock.json are copied
8 | # where available (npm@5+)
9 | COPY package*.json ./
10 |
11 | RUN npm install
12 | # If you are building your code for production
13 | # RUN npm ci --only=production
14 |
15 | # Bundle app source
16 | COPY . .
17 |
18 | EXPOSE 9000
19 | CMD [ "node", "main.js" ]
20 |
--------------------------------------------------------------------------------
/node/message-service/main.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const beeline = require('honeycomb-beeline');
3 |
4 | beeline({
5 | // Get this via https://ui.honeycomb.io/account after signing up for Honeycomb
6 | writeKey: process.env.HONEYCOMB_API_KEY,
7 | // The name of your app is a good choice to start with
8 | dataset: process.env.HONEYCOMB_DATASET,
9 | serviceName: process.env.SERVICE_NAME || 'node-message-service',
10 | apiHost: process.env.HONEYCOMB_API_ENDPOINT || 'https://api.honeycomb.io',
11 | httpTraceParserHook: beeline.w3c.httpTraceParserHook,
12 | httpTracePropagationHook: beeline.w3c.httpTracePropagationHook,
13 | });
14 |
15 | const express = require('express');
16 |
17 | // Constants
18 | const PORT = 9000;
19 | const HOST = '0.0.0.0';
20 |
21 | // App
22 | const app = express();
23 | app.get('/message', async (req, res) => {
24 | beeline.addContext({ name: 'Message' });
25 | const messageSpan = beeline.startSpan({ name: 'look up message' });
26 | const message = await determineMessage(messages);
27 | beeline.addTraceContext({ user_message: message });
28 | beeline.finishSpan(messageSpan);
29 | res.send(`${message}`);
30 | });
31 |
32 | const messages = [
33 | 'how are you?',
34 | 'how are you doing?',
35 | "what's good?",
36 | "what's up?",
37 | 'how do you do?',
38 | 'sup?',
39 | 'good day to you',
40 | 'how are things?',
41 | 'howzit?',
42 | 'woohoo',
43 | ];
44 |
45 | function determineMessage() {
46 | return messages[Math.floor(Math.random() * messages.length)];
47 | }
48 |
49 | app.listen(PORT, HOST);
50 | console.log(`Running node message service on http://${HOST}:${PORT}`);
51 |
--------------------------------------------------------------------------------
/node/message-service/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "message-service",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "main.js",
6 | "scripts": {
7 | "dev": "nodemon main.js",
8 | "start": "node main.js",
9 | "test": "echo \"Error: no test specified\" && exit 1"
10 | },
11 | "author": "",
12 | "license": "ISC",
13 | "dependencies": {
14 | "express": "^4.18.3",
15 | "honeycomb-beeline": "^4.1.0"
16 | },
17 | "devDependencies": {
18 | "nodemon": "^3.0.1",
19 | "prettier": "^3.0.0"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/node/name-service/.dockerignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log
--------------------------------------------------------------------------------
/node/name-service/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | trailingComma: 'es5',
3 | tabWidth: 2,
4 | singleQuote: true,
5 | printWidth: 100,
6 | };
7 |
--------------------------------------------------------------------------------
/node/name-service/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:18
2 |
3 | # Create app directory
4 | WORKDIR /usr/src/app
5 |
6 | # Install app dependencies
7 | # A wildcard is used to ensure both package.json AND package-lock.json are copied
8 | # where available (npm@5+)
9 | COPY package*.json ./
10 |
11 | RUN npm install
12 | # If you are building your code for production
13 | # RUN npm ci --only=production
14 |
15 | # Bundle app source
16 | COPY . .
17 |
18 | EXPOSE 8000
19 | CMD [ "node", "main.js" ]
20 |
--------------------------------------------------------------------------------
/node/name-service/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "name-service",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "main.js",
6 | "scripts": {
7 | "dev": "nodemon main.js",
8 | "start": "node main.js",
9 | "test": "echo \"Error: no test specified\" && exit 1"
10 | },
11 | "author": "",
12 | "license": "ISC",
13 | "dependencies": {
14 | "express": "^4.18.3",
15 | "honeycomb-beeline": "^4.1.0",
16 | "node-fetch": "^2.6.12"
17 | },
18 | "devDependencies": {
19 | "nodemon": "^3.0.1",
20 | "prettier": "^3.0.0"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/node/year-service/.dockerignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log
--------------------------------------------------------------------------------
/node/year-service/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | trailingComma: 'es5',
3 | tabWidth: 2,
4 | singleQuote: true,
5 | printWidth: 100,
6 | };
7 |
--------------------------------------------------------------------------------
/node/year-service/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:18
2 |
3 | # Create app directory
4 | WORKDIR /usr/src/app
5 |
6 | # Install app dependencies
7 | # A wildcard is used to ensure both package.json AND package-lock.json are copied
8 | # where available (npm@5+)
9 | COPY package*.json ./
10 |
11 | RUN npm install
12 | # If you are building your code for production
13 | # RUN npm ci --only=production
14 |
15 | # Bundle app source
16 | COPY . .
17 |
18 | EXPOSE 6001
19 | CMD [ "npm", "start" ]
20 |
--------------------------------------------------------------------------------
/node/year-service/main.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const opentelemetry = require('@opentelemetry/api');
4 | const bunyan = require('bunyan');
5 |
6 | const express = require('express');
7 | const logger = bunyan.createLogger({name: 'myapp', level: 'info'});
8 |
9 | // Constants
10 | const PORT = 6001;
11 | const HOST = '0.0.0.0';
12 |
13 | // App
14 | const app = express();
15 | app.get('/year', async (req, res) => {
16 | const span = opentelemetry.trace.getTracer('default').startSpan('Getting year');
17 | const year = await determineYear(years);
18 | logger.info({"selected_year":year});
19 | res.send(`${year}`);
20 | span.end();
21 | });
22 |
23 | const years = [2015, 2016, 2017, 2018, 2019, 2020];
24 |
25 | function determineYear() {
26 | return years[Math.floor(Math.random() * years.length)];
27 | }
28 |
29 | app.listen(PORT, HOST);
30 | console.log(`Running node year service on http://${HOST}:${PORT}`);
31 |
--------------------------------------------------------------------------------
/node/year-service/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "year-service",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "main.js",
6 | "scripts": {
7 | "dev": "nodemon main.js",
8 | "start": "node -r ./tracing.js main.js",
9 | "test": "echo \"Error: no test specified\" && exit 1"
10 | },
11 | "author": "",
12 | "license": "ISC",
13 | "dependencies": {
14 | "@opentelemetry/auto-instrumentations-node": "^0.42.0",
15 | "@opentelemetry/exporter-logs-otlp-grpc": "^0.49.1",
16 | "@opentelemetry/exporter-trace-otlp-grpc": "^0.49.1",
17 | "@opentelemetry/sdk-node": "^0.49.1",
18 | "bunyan": "^1.8.15",
19 | "express": "^4.18.3"
20 | },
21 | "devDependencies": {
22 | "nodemon": "^3.0.1",
23 | "prettier": "^3.0.0"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/node/year-service/tracing.js:
--------------------------------------------------------------------------------
1 | const process = require('process');
2 |
3 | const { NodeSDK, logs, tracing } = require('@opentelemetry/sdk-node');
4 | const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
5 | const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-grpc');
6 | const { OTLPLogExporter } = require('@opentelemetry/exporter-logs-otlp-grpc');
7 |
8 | const sdk = new NodeSDK({
9 | spanProcessor: new tracing.SimpleSpanProcessor(new OTLPTraceExporter()),
10 | logRecordProcessor: new logs.SimpleLogRecordProcessor(new OTLPLogExporter()),
11 | instrumentations: [getNodeAutoInstrumentations({
12 | '@opentelemetry/instrumentation-fs': {
13 | enabled: false,
14 | },
15 | })],
16 | });
17 |
18 | sdk.start();
19 |
20 | process.on('SIGTERM', () => {
21 | sdk
22 | .shutdown()
23 | .then(() => console.log('Tracing terminated'))
24 | .catch((error) => console.log('Error terminating tracing', error))
25 | .finally(() => process.exit(0));
26 | });
27 |
--------------------------------------------------------------------------------
/otel-collector-config.yaml:
--------------------------------------------------------------------------------
1 | receivers:
2 | otlp:
3 | protocols:
4 | grpc:
5 | endpoint: 0.0.0.0:55680
6 | http:
7 | endpoint: 0.0.0.0:55681
8 | # CORS for browser communication
9 | cors:
10 | allowed_origins: "http://localhost:8080"
11 |
12 |
13 | processors:
14 | batch:
15 |
16 | exporters:
17 | logging:
18 | loglevel: debug
19 | otlp/hny:
20 | endpoint: api.honeycomb.io:443
21 | headers:
22 | # use environment variables to set the values for these headers
23 | "x-honeycomb-team": "${HONEYCOMB_API_KEY}"
24 | # "x-honeycomb-dataset": "${HONEYCOMB_DATASET}" for classic only
25 | # file: # optionally export data to a file
26 | # path: ./data.json # optional file to store exported data
27 | service:
28 | pipelines:
29 | traces:
30 | receivers: [otlp]
31 | processors: [batch]
32 | exporters: [otlp/hny, logging]
33 | logs:
34 | receivers: [otlp]
35 | processors: [batch]
36 | exporters: [otlp/hny, logging]
37 | # exporters: [otlp/hny, logging, file]
38 |
--------------------------------------------------------------------------------
/python/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: https://EditorConfig.org
2 |
3 | # When opening a file, EditorConfig plugins look for a file named .editorconfig
4 | # in the directory of the opened file and in every parent directory.
5 | # A search for .editorconfig files will stop if the root filepath is reached
6 | # or an EditorConfig file with root=true is found.
7 |
8 | # EditorConfig files are read top to bottom and the most recent rules found
9 | # take precedence. Properties from matching EditorConfig sections are applied
10 | # in the order they were read, so properties in closer files take precedence.
11 |
12 | # Python files
13 | [*.py]
14 |
15 | insert_final_newline = true
16 | trim_trailing_whitespace = true
17 | end_of_line = lf
18 | charset = utf-8
19 |
20 | # https://www.python.org/dev/peps/pep-0008/#id19
21 | max_line_length = 79
--------------------------------------------------------------------------------
/python/.gitignore:
--------------------------------------------------------------------------------
1 | __pycache__
2 | *.egg-info
--------------------------------------------------------------------------------
/python/.tool-versions:
--------------------------------------------------------------------------------
1 | poetry 1.3.2
2 | python 3.10.10
3 |
--------------------------------------------------------------------------------
/python/frontend/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.10-slim
2 |
3 | WORKDIR /app
4 |
5 | ENV PYTHONFAULTHANDLER=1 \
6 | PYTHONUNBUFFERED=1 \
7 | PYTHONHASHSEED=random \
8 | PIP_NO_CACHE_DIR=off \
9 | PIP_DISABLE_PIP_VERSION_CHECK=on \
10 | PIP_DEFAULT_TIMEOUT=100 \
11 | POETRY_VERSION=1.3.2
12 |
13 | RUN pip install "poetry==$POETRY_VERSION"
14 |
15 | COPY poetry.lock pyproject.toml ./
16 | RUN poetry config virtualenvs.create false \
17 | && poetry install --no-root --no-interaction --no-ansi
18 |
19 | COPY ./frontend ./frontend
20 |
21 | EXPOSE 7777
22 | CMD ["poetry", "run", "python", "-m", "frontend"]
23 |
--------------------------------------------------------------------------------
/python/frontend/README.rst:
--------------------------------------------------------------------------------
1 | frontend service implemented using `werkzeug` and `urllib` middleware.
--------------------------------------------------------------------------------
/python/frontend/frontend/__init__.py:
--------------------------------------------------------------------------------
1 | __version__ = '0.1.0'
2 |
3 | import os
4 |
5 | from werkzeug.routing import Map, Rule
6 | from werkzeug.wrappers import Request, Response
7 | from http.client import HTTPException
8 |
9 | from beeline.patch import urllib
10 | import urllib
11 |
12 |
13 | class Greeting(object):
14 | NAME_ENDPOINT = os.environ.get('NAME_ENDPOINT', 'http://localhost:8000') + '/name'
15 | MESSAGE_ENDPOINT = os.environ.get('MESSAGE_ENDPOINT', 'http://localhost:9000') + '/message'
16 | def __init__(self):
17 | self.url_map = Map([
18 | Rule('/greeting', endpoint='greeting'),
19 | ])
20 |
21 | def on_greeting(self, request):
22 | name = self.get_name()
23 | message = self.get_message()
24 | return Response('Hello %s, %s' % (name, message), mimetype='text/plain')
25 |
26 | def dispatch_request(self, request):
27 | adapter = self.url_map.bind_to_environ(request.environ)
28 | try:
29 | endpoint, values = adapter.match()
30 | return getattr(self, 'on_' + endpoint)(request, **values)
31 | except HTTPException as e:
32 | return e
33 |
34 | def get_name(self):
35 | with urllib.request.urlopen(self.NAME_ENDPOINT) as f:
36 | return f.read().decode('utf-8')
37 |
38 | def get_message(self):
39 | with urllib.request.urlopen(self.MESSAGE_ENDPOINT) as f:
40 | return f.read().decode('utf-8')
41 |
42 | def wsgi_app(self, environ, start_response):
43 | request = Request(environ)
44 | response = self.dispatch_request(request)
45 | return response(environ, start_response)
46 |
47 | def __call__(self, environ, start_response):
48 | return self.wsgi_app(environ, start_response)
49 |
50 |
51 | def create_app():
52 | app = Greeting()
53 | return app
54 |
--------------------------------------------------------------------------------
/python/frontend/frontend/__main__.py:
--------------------------------------------------------------------------------
1 | import os
2 | from frontend import create_app
3 | from werkzeug.serving import run_simple
4 |
5 | import beeline
6 | from beeline.middleware.werkzeug import HoneyWSGIMiddleware
7 | import beeline.propagation.w3c as w3c
8 |
9 | beeline.init(
10 | # Get this via https://ui.honeycomb.io/account after signing up for Honeycomb
11 | writekey=os.environ.get("HONEYCOMB_API_KEY"),
12 | # The name of your app is a good choice to start with
13 | api_host=os.environ.get("HONEYCOMB_API_ENDPOINT",
14 | "https://api.honeycomb.io"),
15 | service_name='frontend-python',
16 | debug=True,
17 | http_trace_parser_hook=w3c.http_trace_parser_hook,
18 | http_trace_propagation_hook=w3c.http_trace_propagation_hook
19 | )
20 |
21 |
22 | app = HoneyWSGIMiddleware(create_app())
23 | run_simple('0.0.0.0', 7777, app, use_debugger=True, use_reloader=True)
24 |
--------------------------------------------------------------------------------
/python/frontend/pyproject.toml:
--------------------------------------------------------------------------------
1 | [tool.poetry]
2 | name = "frontend"
3 | version = "0.1.0"
4 | description = ""
5 | authors = ["Honeycomb"]
6 |
7 | [tool.poetry.dependencies]
8 | python = "^3.10"
9 | werkzeug = "^2.3.8"
10 | honeycomb-beeline = "^3.6.0"
11 | [tool.poetry.dev-dependencies]
12 | pytest = "^8.1.1"
13 |
14 | [build-system]
15 | requires = ["poetry>=0.12"]
16 | build-backend = "poetry.masonry.api"
17 |
--------------------------------------------------------------------------------
/python/frontend/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeycombio/example-greeting-service/6705c90566e5309e583e9c1312d87fe8a220cc1d/python/frontend/tests/__init__.py
--------------------------------------------------------------------------------
/python/frontend/tests/test_frontend.py:
--------------------------------------------------------------------------------
1 | from frontend import __version__
2 |
3 |
4 | def test_version():
5 | assert __version__ == '0.1.0'
6 |
--------------------------------------------------------------------------------
/python/message-service/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.10-slim
2 |
3 | WORKDIR /app
4 |
5 | ENV PYTHONFAULTHANDLER=1 \
6 | PYTHONUNBUFFERED=1 \
7 | PYTHONHASHSEED=random \
8 | PIP_NO_CACHE_DIR=off \
9 | PIP_DISABLE_PIP_VERSION_CHECK=on \
10 | PIP_DEFAULT_TIMEOUT=100 \
11 | POETRY_VERSION=1.3.2
12 |
13 | RUN pip install "poetry==$POETRY_VERSION"
14 |
15 | COPY poetry.lock pyproject.toml ./
16 | RUN poetry config virtualenvs.create false \
17 | && poetry install --no-root --no-interaction --no-ansi
18 |
19 | COPY ./message_service ./message_service
20 |
21 | EXPOSE 9000
22 | CMD ["poetry", "run", "python", "-m", "message_service"]
23 |
--------------------------------------------------------------------------------
/python/message-service/README.rst:
--------------------------------------------------------------------------------
1 | message service implemented using `bottle`.
--------------------------------------------------------------------------------
/python/message-service/message_service/__init__.py:
--------------------------------------------------------------------------------
1 | __version__ = '0.1.0'
2 |
--------------------------------------------------------------------------------
/python/message-service/message_service/__main__.py:
--------------------------------------------------------------------------------
1 | import os
2 | import random
3 |
4 | from bottle import Bottle, run
5 | from opentelemetry import trace
6 | from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import \
7 | OTLPSpanExporter
8 | from opentelemetry.instrumentation.requests import RequestsInstrumentor
9 | from opentelemetry.instrumentation.wsgi import OpenTelemetryMiddleware
10 | from opentelemetry.sdk.resources import Resource, SERVICE_NAME
11 | from opentelemetry.sdk.trace import TracerProvider
12 | from opentelemetry.sdk.trace.export import BatchSpanProcessor
13 |
14 | messages = [
15 | "how are you?", "how are you doing?", "what's good?", "what's up?", "how do you do?",
16 | "sup?", "good day to you", "how are things?", "howzit?", "woohoo",
17 | ]
18 | trace.set_tracer_provider(TracerProvider(
19 | resource=Resource.create({SERVICE_NAME: "message-python"})
20 | ))
21 | tracer = trace.get_tracer(os.getenv("OTEL_SERVICE_NAME", "message-tracer"))
22 |
23 | trace.get_tracer_provider().add_span_processor(
24 | BatchSpanProcessor(OTLPSpanExporter(
25 | headers=(("x-honeycomb-team", os.environ.get("HONEYCOMB_API_KEY")),),
26 | endpoint=os.environ.get("HONEYCOMB_API_ENDPOINT",
27 | "https://api.honeycomb.io")
28 | )))
29 |
30 | app = Bottle()
31 | app.wsgi = OpenTelemetryMiddleware(app.wsgi)
32 | RequestsInstrumentor().instrument()
33 |
34 | @app.route('/message')
35 | @tracer.start_as_current_span("🤖 choosing message ✨")
36 | def message():
37 | return random.choice(messages)
38 |
39 | run(app=app, host='0.0.0.0', port=9000)
40 |
--------------------------------------------------------------------------------
/python/message-service/pyproject.toml:
--------------------------------------------------------------------------------
1 | [tool.poetry]
2 | name = "message-service"
3 | version = "0.1.0"
4 | description = ""
5 | authors = ["Honeycomb"]
6 |
7 | [tool.poetry.dependencies]
8 | python = "^3.10"
9 | bottle = "^0.12.25"
10 | opentelemetry-api = "^1.23.0"
11 | opentelemetry-sdk = "^1.23.0"
12 | opentelemetry-exporter-otlp-proto-grpc = "^1.23.0"
13 | opentelemetry-instrumentation-requests = "^0.44b0"
14 | opentelemetry-instrumentation-wsgi = "^0.44b0"
15 | requests = "^2.31.0"
16 |
17 | [tool.poetry.dev-dependencies]
18 | pytest = "^8.1.1"
19 |
20 | [build-system]
21 | requires = ["poetry>=0.12"]
22 | build-backend = "poetry.masonry.api"
23 |
--------------------------------------------------------------------------------
/python/message-service/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeycombio/example-greeting-service/6705c90566e5309e583e9c1312d87fe8a220cc1d/python/message-service/tests/__init__.py
--------------------------------------------------------------------------------
/python/message-service/tests/test_message_service.py:
--------------------------------------------------------------------------------
1 | from message_service import __version__
2 |
3 |
4 | def test_version():
5 | assert __version__ == '0.1.0'
6 |
--------------------------------------------------------------------------------
/python/name-service/.flaskenv:
--------------------------------------------------------------------------------
1 | FLASK_APP=name_service
2 | FLASK_RUN_PORT=8000
3 |
--------------------------------------------------------------------------------
/python/name-service/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.10-slim
2 |
3 | WORKDIR /app
4 |
5 | ENV PYTHONFAULTHANDLER=1 \
6 | PYTHONUNBUFFERED=1 \
7 | PYTHONHASHSEED=random \
8 | PIP_NO_CACHE_DIR=off \
9 | PIP_DISABLE_PIP_VERSION_CHECK=on \
10 | PIP_DEFAULT_TIMEOUT=100 \
11 | POETRY_VERSION=1.3.2
12 |
13 | RUN pip install "poetry==$POETRY_VERSION"
14 |
15 | COPY poetry.lock pyproject.toml ./
16 | RUN poetry config virtualenvs.create false \
17 | && poetry install --no-root --no-interaction --no-ansi
18 |
19 | COPY ./.flaskenv ./
20 | COPY ./name_service ./name_service
21 |
22 | EXPOSE 8000
23 | CMD ["poetry", "run", "opentelemetry-instrument", "flask", "run", "--host", "0.0.0.0"]
24 |
--------------------------------------------------------------------------------
/python/name-service/README.rst:
--------------------------------------------------------------------------------
1 | name service implemented using `flask` and `requests`
--------------------------------------------------------------------------------
/python/name-service/name_service/__init__.py:
--------------------------------------------------------------------------------
1 | __version__ = '0.1.0'
2 |
3 | import logging
4 | import os
5 | import random
6 |
7 | import requests
8 | from flask import Flask
9 | from opentelemetry import trace
10 |
11 | names_by_year = {
12 | 2015: ['sophia', 'jackson', 'emma', 'aiden', 'olivia', 'liam', 'ava',
13 | 'lucas', 'mia', 'noah'],
14 | 2016: ['sophia', 'jackson', 'emma', 'aiden', 'olivia', 'lucas', 'ava',
15 | 'liam', 'mia', 'noah'],
16 | 2017: ['sophia', 'jackson', 'olivia', 'liam', 'emma', 'noah', 'ava',
17 | 'aiden', 'isabella', 'lucas'],
18 | 2018: ['sophia', 'jackson', 'olivia', 'liam', 'emma', 'noah', 'ava',
19 | 'aiden', 'isabella', 'caden'],
20 | 2019: ['sophia', 'liam', 'olivia', 'jackson', 'emma', 'noah', 'ava',
21 | 'aiden', 'aira', 'grayson'],
22 | 2020: ['olivia', 'noah', 'emma', 'liam', 'ava', 'elijah', 'isabella',
23 | 'oliver', 'sophia', 'lucas']
24 | }
25 |
26 | YEAR_ENDPOINT = os.environ.get('YEAR_ENDPOINT',
27 | 'http://localhost:6001') + '/year'
28 |
29 |
30 | def get_year():
31 | with tracer.start_as_current_span("✨ call /year ✨"):
32 | r = requests.get(YEAR_ENDPOINT)
33 | return int(r.text)
34 |
35 | tracer = trace.get_tracer(os.getenv("OTEL_SERVICE_NAME", "year-tracer"))
36 |
37 | logger = logging.getLogger("my-logger")
38 | logger.setLevel(logging.INFO)
39 |
40 | app = Flask(__name__)
41 |
42 | @app.route('/name')
43 | def get_name():
44 | year = get_year()
45 | current_span = trace.get_current_span()
46 | logger.info("Selected year: %s", year)
47 | current_span.set_attribute("app.year_selected", year)
48 |
49 | with tracer.start_as_current_span("📖 look up name based on year ✨"):
50 | span = trace.get_current_span()
51 | names = names_by_year[year]
52 | name = random.choice(names)
53 | span.set_attribute("app.name_selected", name)
54 | return name
55 |
--------------------------------------------------------------------------------
/python/name-service/pyproject.toml:
--------------------------------------------------------------------------------
1 | [tool.poetry]
2 | name = "name-service"
3 | version = "0.1.0"
4 | description = ""
5 | authors = ["Honeycomb"]
6 |
7 | [tool.poetry.dependencies]
8 | python = "^3.10"
9 | flask = "^2.3.2"
10 | python-dotenv = "^1.0.1"
11 | requests = "^2.31.0"
12 | opentelemetry-api = "^1.22.0"
13 | honeycomb-opentelemetry = "0.4.0b0"
14 | opentelemetry-instrumentation-requests = "^0.43b0"
15 | opentelemetry-instrumentation-flask = "^0.43b0"
16 |
17 | [tool.poetry.dev-dependencies]
18 | pytest = "^8.1.1"
19 | autopep8 = "^2.0.4"
20 |
--------------------------------------------------------------------------------
/python/name-service/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeycombio/example-greeting-service/6705c90566e5309e583e9c1312d87fe8a220cc1d/python/name-service/tests/__init__.py
--------------------------------------------------------------------------------
/python/name-service/tests/test_name_service.py:
--------------------------------------------------------------------------------
1 | from name_service import __version__
2 |
3 |
4 | def test_version():
5 | assert __version__ == '0.1.0'
6 |
--------------------------------------------------------------------------------
/python/year-service/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.10-slim
2 |
3 | WORKDIR /app
4 |
5 | ENV PYTHONFAULTHANDLER=1 \
6 | PYTHONUNBUFFERED=1 \
7 | PYTHONHASHSEED=random \
8 | PIP_NO_CACHE_DIR=off \
9 | PIP_DISABLE_PIP_VERSION_CHECK=on \
10 | PIP_DEFAULT_TIMEOUT=100 \
11 | POETRY_VERSION=1.3.2
12 |
13 | RUN pip install "poetry==$POETRY_VERSION"
14 |
15 | COPY poetry.lock pyproject.toml ./
16 | RUN poetry config virtualenvs.create false \
17 | && poetry install --no-root --no-interaction --no-ansi
18 |
19 | COPY ./yearservice ./yearservice
20 |
21 | EXPOSE 6001
22 | CMD ["poetry", "run", "yearservice/manage.py", "runserver", "0.0.0.0:6001", "--noreload"]
23 |
--------------------------------------------------------------------------------
/python/year-service/README.rst:
--------------------------------------------------------------------------------
1 | year service implemented using `django`.
--------------------------------------------------------------------------------
/python/year-service/pyproject.toml:
--------------------------------------------------------------------------------
1 | [tool.poetry]
2 | name = "year-service"
3 | version = "0.1.0"
4 | description = ""
5 | authors = ["Honeycomb"]
6 |
7 | [tool.poetry.dependencies]
8 | python = "^3.10"
9 | django = "^5.0.2"
10 | python-dotenv = "^1.0.1"
11 | opentelemetry-instrumentation-django = "^0.44b0"
12 | opentelemetry-sdk = "^1.23.0"
13 | opentelemetry-exporter-otlp-proto-grpc = "^1.23.0"
14 |
15 | [tool.poetry.dev-dependencies]
16 | pytest = "^8.1.1"
17 | autopep8 = "^2.0.4"
18 |
--------------------------------------------------------------------------------
/python/year-service/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeycombio/example-greeting-service/6705c90566e5309e583e9c1312d87fe8a220cc1d/python/year-service/tests/__init__.py
--------------------------------------------------------------------------------
/python/year-service/tests/test_year_service.py:
--------------------------------------------------------------------------------
1 | from year_service import __version__
2 |
3 |
4 | def test_version():
5 | assert __version__ == '0.1.0'
6 |
--------------------------------------------------------------------------------
/python/year-service/yearservice/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | """Django's command-line utility for administrative tasks."""
3 | import os
4 | import sys
5 |
6 | from opentelemetry.instrumentation.django import DjangoInstrumentor
7 |
8 |
9 | def main():
10 | """Run administrative tasks."""
11 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'yearservice.settings')
12 |
13 | # This call is what makes the Django application be instrumented
14 | DjangoInstrumentor().instrument()
15 | try:
16 | from django.core.management import execute_from_command_line
17 | except ImportError as exc:
18 | raise ImportError(
19 | "Couldn't import Django. Are you sure it's installed and "
20 | "available on your PYTHONPATH environment variable? Did you "
21 | "forget to activate a virtual environment?"
22 | ) from exc
23 | execute_from_command_line(sys.argv)
24 |
25 |
26 | if __name__ == '__main__':
27 | main()
28 |
--------------------------------------------------------------------------------
/python/year-service/yearservice/yearapp/__init__.py:
--------------------------------------------------------------------------------
1 | default_app_config = 'yearapp.apps.YearappConfig'
2 |
--------------------------------------------------------------------------------
/python/year-service/yearservice/yearapp/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | # Register your models here.
4 |
--------------------------------------------------------------------------------
/python/year-service/yearservice/yearapp/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeycombio/example-greeting-service/6705c90566e5309e583e9c1312d87fe8a220cc1d/python/year-service/yearservice/yearapp/migrations/__init__.py
--------------------------------------------------------------------------------
/python/year-service/yearservice/yearapp/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 | # Create your models here.
4 |
--------------------------------------------------------------------------------
/python/year-service/yearservice/yearapp/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/python/year-service/yearservice/yearapp/views.py:
--------------------------------------------------------------------------------
1 | from django.http import HttpResponse
2 | import random
3 | import logging
4 |
5 | logger = logging.getLogger('my-logger')
6 |
7 | years = [2015, 2016, 2017, 2018, 2019, 2020]
8 |
9 |
10 | def year(request):
11 | year = random.choice(years)
12 | logger.info({'selected_year': year})
13 | return HttpResponse(year)
14 |
--------------------------------------------------------------------------------
/python/year-service/yearservice/yearservice/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeycombio/example-greeting-service/6705c90566e5309e583e9c1312d87fe8a220cc1d/python/year-service/yearservice/yearservice/__init__.py
--------------------------------------------------------------------------------
/python/year-service/yearservice/yearservice/asgi.py:
--------------------------------------------------------------------------------
1 | """
2 | ASGI config for yearservice project.
3 |
4 | It exposes the ASGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/3.1/howto/deployment/asgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.asgi import get_asgi_application
13 |
14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'yearservice.settings')
15 |
16 | application = get_asgi_application()
17 |
--------------------------------------------------------------------------------
/python/year-service/yearservice/yearservice/urls.py:
--------------------------------------------------------------------------------
1 | """yearservice URL Configuration
2 |
3 | The `urlpatterns` list routes URLs to views. For more information please see:
4 | https://docs.djangoproject.com/en/3.1/topics/http/urls/
5 | Examples:
6 | Function views
7 | 1. Add an import: from my_app import views
8 | 2. Add a URL to urlpatterns: path('', views.home, name='home')
9 | Class-based views
10 | 1. Add an import: from other_app.views import Home
11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
12 | Including another URLconf
13 | 1. Import the include() function: from django.urls import include, path
14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
15 | """
16 | from django.contrib import admin
17 | from django.urls import path
18 | from yearapp import views
19 |
20 | urlpatterns = [
21 | path('admin/', admin.site.urls),
22 | path('year', views.year, name="year")
23 | ]
24 |
--------------------------------------------------------------------------------
/python/year-service/yearservice/yearservice/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for yearservice project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/3.1/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.wsgi import get_wsgi_application
13 |
14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'yearservice.settings')
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/ruby/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: https://EditorConfig.org
2 |
3 | # When opening a file, EditorConfig plugins look for a file named .editorconfig
4 | # in the directory of the opened file and in every parent directory.
5 | # A search for .editorconfig files will stop if the root filepath is reached
6 | # or an EditorConfig file with root=true is found.
7 |
8 | # EditorConfig files are read top to bottom and the most recent rules found
9 | # take precedence. Properties from matching EditorConfig sections are applied
10 | # in the order they were read, so properties in closer files take precedence.
11 |
12 | # Ruby files
13 | [*.{rb,ru}]
14 |
15 | indent_size = 2
16 |
17 | # https://rubystyle.guide/
18 | max_line_length = 80
19 | insert_final_newline = true
20 | trim_trailing_whitespace = true
21 | end_of_line = lf
22 |
--------------------------------------------------------------------------------
/ruby/.envrc_example:
--------------------------------------------------------------------------------
1 | source_up
2 | layout ruby
--------------------------------------------------------------------------------
/ruby/Tiltfile:
--------------------------------------------------------------------------------
1 | docker_compose("./docker-compose.yml")
2 |
--------------------------------------------------------------------------------
/ruby/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "2.4"
2 |
3 | x-common-env: &common-env
4 | HONEYCOMB_API_KEY:
5 | HONEYCOMB_DATASET:
6 | HONEYCOMB_API:
7 | OTEL_EXPORTER_OTLP_ENDPOINT:
8 | OTEL_EXPORTER_OTLP_HEADERS:
9 | OTEL_RESOURCE_ATTRIBUTES: app.running-in=docker
10 | MESSAGE_ENDPOINT: http://message:9000
11 | NAME_ENDPOINT: http://name:8000
12 | YEAR_ENDPOINT: http://year:6001
13 | REDIS_URL: redis
14 |
15 | services:
16 | frontend:
17 | build: ./frontend
18 | image: hnyexample/frontend-ruby
19 | environment:
20 | <<: *common-env
21 | ports:
22 | - 7777:7777
23 |
24 | message:
25 | build: ./message-service
26 | image: hnyexample/message-ruby
27 | environment:
28 | <<: *common-env
29 | ports:
30 | - 9000:9000
31 |
32 | name:
33 | build: ./name-service
34 | image: hnyexample/name-ruby
35 | environment:
36 | <<: *common-env
37 | ports:
38 | - 8000:8000
39 |
40 | year:
41 | build: ./year-service
42 | image: hnyexample/year-ruby
43 | environment:
44 | <<: *common-env
45 | ports:
46 | - 6001:6001
47 |
48 | redis:
49 | image: redis:latest
50 | ports:
51 | - "127.0.0.1:6379:6379"
--------------------------------------------------------------------------------
/ruby/frontend/.ruby-version:
--------------------------------------------------------------------------------
1 | 3.0.6
2 |
--------------------------------------------------------------------------------
/ruby/frontend/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ruby:3.0
2 | RUN gem install bundler
3 | WORKDIR /myapp
4 | COPY Gemfile* /myapp/
5 | RUN bundle install
6 | COPY frontend.ru /myapp
7 | COPY o11y_wrapper.rb /myapp
8 |
9 | EXPOSE 7777
10 |
11 | HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD [ "curl", "--fail", "http://localhost:7777/up" ]
12 | CMD [ "bundle", "exec", "rackup", "frontend.ru", "--server", "puma", "--host", "0.0.0.0"]
13 |
--------------------------------------------------------------------------------
/ruby/frontend/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | gem 'rails'
4 | gem 'puma'
5 | gem 'opentelemetry-sdk'
6 | gem 'opentelemetry-exporter-otlp'
7 | gem 'opentelemetry-instrumentation-all'
8 | gem 'faraday'
9 |
--------------------------------------------------------------------------------
/ruby/message-service/.ruby-version:
--------------------------------------------------------------------------------
1 | 3.0.6
2 |
--------------------------------------------------------------------------------
/ruby/message-service/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ruby:3.0
2 | RUN gem install bundler
3 | WORKDIR /myapp
4 | COPY Gemfile /myapp/Gemfile
5 | RUN bundle install
6 | COPY message.ru /myapp
7 |
8 | EXPOSE 9000
9 | CMD [ "bundle", "exec", "rackup", "message.ru", "--server", "puma", "--host", "0.0.0.0"]
10 |
--------------------------------------------------------------------------------
/ruby/message-service/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | gem 'rails'
4 | gem 'puma'
5 | gem 'honeycomb-beeline'
6 | gem 'faraday'
7 | gem 'rackup'
8 |
--------------------------------------------------------------------------------
/ruby/name-service/.ruby-version:
--------------------------------------------------------------------------------
1 | 3.0.6
2 |
--------------------------------------------------------------------------------
/ruby/name-service/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ruby:3.0
2 | RUN gem install bundler
3 | WORKDIR /myapp
4 | COPY Gemfile /myapp/Gemfile
5 | RUN bundle install
6 | COPY name.rb /myapp
7 |
8 | EXPOSE 8000
9 | CMD [ "bundle", "exec", "ruby", "name.rb"]
10 |
--------------------------------------------------------------------------------
/ruby/name-service/Gemfile:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | gem "honeycomb-beeline"
4 | gem "sinatra"
5 | gem "faraday"
6 | gem "rackup"
7 |
--------------------------------------------------------------------------------
/ruby/name-service/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://rubygems.org/
3 | specs:
4 | addressable (2.8.6)
5 | public_suffix (>= 2.0.2, < 6.0)
6 | base64 (0.2.0)
7 | domain_name (0.6.20240107)
8 | excon (0.110.0)
9 | faraday (2.9.0)
10 | faraday-net_http (>= 2.0, < 3.2)
11 | faraday-net_http (3.1.0)
12 | net-http
13 | ffi (1.16.3)
14 | ffi-compiler (1.3.2)
15 | ffi (>= 1.15.5)
16 | rake
17 | honeycomb-beeline (3.1.0)
18 | libhoney (>= 2.3.0)
19 | http (5.2.0)
20 | addressable (~> 2.8)
21 | base64 (~> 0.1)
22 | http-cookie (~> 1.0)
23 | http-form_data (~> 2.2)
24 | llhttp-ffi (~> 0.5.0)
25 | http-cookie (1.0.5)
26 | domain_name (~> 0.5)
27 | http-form_data (2.3.0)
28 | libhoney (2.3.0)
29 | addressable (~> 2.0)
30 | excon
31 | http (>= 2.0, < 6.0)
32 | llhttp-ffi (0.5.0)
33 | ffi-compiler (~> 1.0)
34 | rake (~> 13.0)
35 | mustermann (3.0.0)
36 | ruby2_keywords (~> 0.0.1)
37 | net-http (0.4.1)
38 | uri
39 | public_suffix (5.0.4)
40 | rack (3.0.9.1)
41 | rack-protection (4.0.0)
42 | base64 (>= 0.1.0)
43 | rack (>= 3.0.0, < 4)
44 | rack-session (2.0.0)
45 | rack (>= 3.0.0)
46 | rackup (2.1.0)
47 | rack (>= 3)
48 | webrick (~> 1.8)
49 | rake (13.1.0)
50 | ruby2_keywords (0.0.5)
51 | sinatra (4.0.0)
52 | mustermann (~> 3.0)
53 | rack (>= 3.0.0, < 4)
54 | rack-protection (= 4.0.0)
55 | rack-session (>= 2.0.0, < 3)
56 | tilt (~> 2.0)
57 | tilt (2.3.0)
58 | uri (0.13.0)
59 | webrick (1.8.1)
60 |
61 | PLATFORMS
62 | arm64-darwin-23
63 |
64 | DEPENDENCIES
65 | faraday
66 | honeycomb-beeline
67 | rackup
68 | sinatra
69 |
70 | BUNDLED WITH
71 | 2.2.33
72 |
--------------------------------------------------------------------------------
/ruby/name-service/name.rb:
--------------------------------------------------------------------------------
1 | require 'sinatra'
2 | require 'honeycomb-beeline'
3 | require 'honeycomb/propagation/w3c'
4 | require 'faraday'
5 |
6 | Honeycomb.configure do |config|
7 | config.write_key = ENV['HONEYCOMB_API_KEY']
8 | config.service_name = ENV['SERVICE_NAME'] || "name-ruby"
9 | config.api_host = ENV['HONEYCOMB_API_ENDPOINT']
10 | config.http_trace_parser_hook do |env|
11 | Honeycomb::W3CPropagation::UnmarshalTraceContext.parse_rack_env(env)
12 | end
13 | config.http_trace_propagation_hook do |env, context|
14 | Honeycomb::W3CPropagation::MarshalTraceContext.parse_faraday_env(env, context)
15 | end
16 | #config.client = Libhoney::LogClient.new
17 | end
18 |
19 | use Honeycomb::Sinatra::Middleware, client: Honeycomb.client
20 |
21 | set :bind, '0.0.0.0'
22 | set :port, 8000
23 |
24 | names_by_year = {
25 | 2015 => ['sophia', 'jackson', 'emma', 'aiden', 'olivia', 'liam', 'ava', 'lucas', 'mia', 'noah'],
26 | 2016 => ['sophia', 'jackson', 'emma', 'aiden', 'olivia', 'lucas', 'ava', 'liam', 'mia', 'noah'],
27 | 2017 => ['sophia', 'jackson', 'olivia', 'liam', 'emma', 'noah', 'ava', 'aiden', 'isabella', 'lucas'],
28 | 2018 => ['sophia', 'jackson', 'olivia', 'liam', 'emma', 'noah', 'ava', 'aiden', 'isabella', 'caden'],
29 | 2019 => ['sophia', 'liam', 'olivia', 'jackson', 'emma', 'noah', 'ava', 'aiden', 'aira', 'grayson'],
30 | 2020 => ['olivia', 'noah', 'emma', 'liam', 'ava', 'elijah', 'isabella', 'oliver', 'sophia', 'lucas']
31 | }
32 |
33 | get '/name' do
34 | year = get_year
35 | names = names_by_year[year]
36 | names[rand(names.length)]
37 | end
38 |
39 | def get_year
40 | year_service_connection = Faraday.new(ENV["YEAR_ENDPOINT"] || "http://localhost:6001")
41 | year_service_response = year_service_connection.get("/year") do |request|
42 | end
43 | year_service_response.body.to_i
44 | end
45 |
--------------------------------------------------------------------------------
/ruby/year-service/.ruby-version:
--------------------------------------------------------------------------------
1 | 3.0.6
2 |
--------------------------------------------------------------------------------
/ruby/year-service/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ruby:3.0
2 | RUN gem install bundler
3 | WORKDIR /app
4 | COPY Gemfile /app/Gemfile
5 | COPY Gemfile.lock /app/Gemfile.lock
6 | RUN bundle install
7 | COPY config.ru /app
8 | COPY o11y_wrapper.rb /app
9 |
10 | EXPOSE 6001
11 | CMD ["bundle", "exec", "puma", "--port", "6001"]
12 |
--------------------------------------------------------------------------------
/ruby/year-service/Gemfile:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | gem 'opentelemetry-sdk'
4 | gem 'opentelemetry-exporter-otlp'
5 | gem 'opentelemetry-instrumentation-all'
6 | gem "grape"
7 | gem "puma"
8 |
--------------------------------------------------------------------------------
/ruby/year-service/config.ru:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "bundler/setup"
4 | Bundler.require
5 |
6 | require 'opentelemetry/sdk'
7 | require 'opentelemetry/exporter/otlp'
8 | require 'opentelemetry/instrumentation/all'
9 | require_relative './o11y_wrapper.rb'
10 |
11 | begin
12 | OpenTelemetry::SDK.configure do |c|
13 | c.service_name = ENV['SERVICE_NAME'] || "year-ruby"
14 |
15 | # enable all auto-instrumentation available
16 | c.use_all()
17 |
18 | # add the Baggage and CarryOn processors to thepipeline
19 | c.add_span_processor(O11yWrapper::BaggageSpanProcessor.new)
20 | c.add_span_processor(O11yWrapper::CarryOnSpanProcessor.new)
21 |
22 | # Because we tinkered with the pipeline, we'll need to
23 | # wire up span batching and sending via OTLP ourselves.
24 | # This is usually the default.
25 | c.add_span_processor(
26 | OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor.new(
27 | OpenTelemetry::Exporter::OTLP::Exporter.new()
28 | )
29 | )
30 | end
31 | rescue OpenTelemetry::SDK::ConfigurationError => e
32 | puts "What now?"
33 | puts e.inspect
34 | end
35 |
36 | Tracer = OpenTelemetry.tracer_provider.tracer("year-internal")
37 |
38 | class App < Grape::API
39 | format :txt
40 |
41 | get :year do
42 | Tracer.in_span("🗓 get-a-year ✨") do
43 | sleep rand(0..0.005)
44 | (2015..2020).to_a.sample
45 | end
46 | end
47 | end
48 |
49 | use OpenTelemetry::Instrumentation::Rack::Middlewares::TracerMiddleware
50 | run App
51 |
--------------------------------------------------------------------------------
/web/.eslintignore:
--------------------------------------------------------------------------------
1 | eslintrc.js
--------------------------------------------------------------------------------
/web/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | browser: true,
4 | es2021: true,
5 | },
6 | extends: 'eslint:recommended',
7 | parserOptions: {
8 | ecmaVersion: 'latest',
9 | sourceType: 'module',
10 | },
11 | rules: {},
12 | };
13 |
--------------------------------------------------------------------------------
/web/.prettierignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | bld
--------------------------------------------------------------------------------
/web/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | trailingComma: 'es5',
3 | tabWidth: 2,
4 | singleQuote: true,
5 | printWidth: 100,
6 | };
7 |
--------------------------------------------------------------------------------
/web/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:18
2 |
3 | # Create app directory
4 | WORKDIR /usr/src/app
5 |
6 | # Install app dependencies
7 | # A wildcard is used to ensure both package.json AND package-lock.json are copied
8 | # where available (npm@5+)
9 | COPY package*.json ./
10 |
11 | RUN npm install
12 | # If you are building your code for production
13 | # RUN npm ci --only=production
14 |
15 | # Bundle app source
16 | COPY . .
17 | RUN npm run build
18 |
19 | EXPOSE 8080
20 | CMD [ "npm", "start" ]
21 |
--------------------------------------------------------------------------------
/web/README.md:
--------------------------------------------------------------------------------
1 | # Browser greeting service
2 |
3 | A browser app that sends OTel traces to a collector that passes it through to Honeycomb. The browser app makes a request to `http://localhost:7777/greeting` to trace HTTP requests from the frontend through to the backend.
4 |
5 | ## Running the app
6 |
7 | This will run the browser app that generates a greeting through JS code in the browser. To run the browser app so that it gets a greeting from a server run.
8 |
9 | ```shell
10 | tilt up web node
11 | ```
12 |
13 | It should be okay to run with other language backend services too, but they may not be configured with CORS to allow requests from localhost:8080, the node app definitely is so it's safest to use that one.
14 |
--------------------------------------------------------------------------------
/web/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "web",
3 | "version": "1.0.0",
4 | "description": "An example browser app that sends traces through Opentelemetry",
5 | "scripts": {
6 | "build": "webpack",
7 | "lint": "eslint src",
8 | "lint:fix": "eslint src --fix",
9 | "start": "webpack serve --open",
10 | "test": "echo \"Error: no test specified\" && exit 1"
11 | },
12 | "author": "",
13 | "license": "ISC",
14 | "dependencies": {
15 | "@honeycombio/opentelemetry-web": "^0.0.1",
16 | "@opentelemetry/api": "1.7.0",
17 | "@opentelemetry/auto-instrumentations-web": "^0.36.0"
18 | },
19 | "devDependencies": {
20 | "assert": "^2.0.0",
21 | "browserify-zlib": "^0.2.0",
22 | "buffer": "^6.0.3",
23 | "eslint": "^8.37.0",
24 | "events": "^3.3.0",
25 | "html-webpack-plugin": "^5.5.0",
26 | "os-browserify": "^0.3.0",
27 | "path-browserify": "^1.0.1",
28 | "prettier": "2.7.1",
29 | "prettier-eslint": "^15.0.1",
30 | "process": "^0.11.10",
31 | "querystring-es3": "^0.2.1",
32 | "stream-browserify": "^3.0.0",
33 | "stream-http": "^3.2.0",
34 | "url": "^0.11.0",
35 | "util": "^0.12.5",
36 | "webpack": "^5.76.0",
37 | "webpack-cli": "^5.0.2",
38 | "webpack-dev-server": "^4.9.2"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/web/src/tracing-http.js:
--------------------------------------------------------------------------------
1 | import { HoneycombWebSDK } from '@honeycombio/opentelemetry-web';
2 | import { getWebAutoInstrumentations } from '@opentelemetry/auto-instrumentations-web';
3 | const sdk = new HoneycombWebSDK({
4 | // we're sending data to a local collector here to avoid exposing the API key in the browser
5 | endpoint: 'http://localhost:55681/v1/traces',
6 | serviceName: 'egs-browser',
7 | instrumentations: [getWebAutoInstrumentations()],
8 | });
9 | sdk.start();
10 |
--------------------------------------------------------------------------------
/web/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const HtmlWebpackPlugin = require('html-webpack-plugin');
3 | const webpack = require("webpack");
4 |
5 | module.exports = {
6 | entry: {
7 | index: './src/index.js',
8 | 'tracing-http': './src/tracing-http.js',
9 | },
10 | devtool: 'inline-source-map',
11 | devServer: {
12 | static: './bld',
13 | },
14 | mode: 'development',
15 | output: {
16 | filename: '[name].bundle.js',
17 | path: path.resolve(__dirname, 'dist'),
18 | clean: true,
19 | },
20 | optimization: {
21 | runtimeChunk: 'single',
22 | },
23 | plugins: [
24 | new HtmlWebpackPlugin({
25 | title: 'Development',
26 | }),
27 | new webpack.DefinePlugin({
28 | FRONTEND_ENDPOINT: JSON.stringify(process.env.FRONTEND_ENDPOINT || 'http://localhost:7777'),
29 | }),
30 | ],
31 | resolve: {
32 | fallback: {
33 | path: require.resolve('path-browserify'),
34 | },
35 | },
36 | };
37 |
--------------------------------------------------------------------------------