# Contributing

Welcome to OpenTelemetry Java repository!

Before you start - see OpenTelemetry general
[contributing](https://github.com/open-telemetry/community/blob/main/guides/contributor/README.md)
requirements and recommendations.

If you want to add new features or change behavior, please make sure your changes follow the
[OpenTelemetry Specification](https://github.com/open-telemetry/opentelemetry-specification).
Otherwise, file an issue or submit a pull request (PR) to the specification repo first.

Make sure to review the projects [license](LICENSE) and sign the
[CNCF CLA](https://identity.linuxfoundation.org/projects/cncf). A signed CLA will be enforced by an
automatic check once you submit a PR, but you can also sign it after opening your PR.

## Requirements

Java 17 or higher is required to build the projects in this repository. The built artifacts can be
used on Java 8 or higher.

## Building opentelemetry-java

Continuous integration builds the project, runs the tests, and runs multiple types of static
analysis.

1. Note: Currently, to run the full suite of tests, you'll need to be running a docker daemon. The
   tests that require docker are disabled if docker is not present. If you wish to run them, you
   must run a local docker daemon.

2. Clone the repository

   `git clone https://github.com/open-telemetry/opentelemetry-java.git`

3. Run the following commands to build, run tests and most static analysis, and check formatting:

   `./gradlew build`

4. If you are a Windows user, use the alternate command mentioned below to run tests and check
   formatting:

   `gradlew.bat`

## Checks

Before submitting a PR, you should make sure the style checks and unit tests pass. You can run these
with the `check` task.

```bash
$ ./gradlew check
```

Note: this gradle task will potentially generate changes to files in
the `docs/apidiffs/current_vs_latest`
directory. Please make sure to include any changes to these files in your pull request (i.e.
add those files to your commits in the PR).

## PR Review

After you submit a PR, it will be reviewed by the project maintainers and approvers. Not all
maintainers need to review a particular PR, but merging to the base branch is authorized to
restricted members (administrators).

### Draft PRs

Draft PRs are welcome, especially when exploring new ideas or experimenting with a hypothesis.
However, draft PRs may not receive the same degree of attention, feedback, or scrutiny unless
requested directly. In order to help keep the PR backlog maintainable, drafts older than 6 months
will be closed by the project maintainers. This should not be interpreted as a rejection. Closed
PRs may be reopened by the author when time or interest allows.

## Project Scope

`opentelemetry-java` is one of several repositories which comprise the OpenTelemetry Java ecosystem,
and contains the core components upon which instrumentation and extensions are built. In order to
prevent sprawl and maintain a high level of quality, we limit this repository's scope to components
which implement concepts defined in
the [opentelemetry-specification](https://github.com/open-telemetry/opentelemetry-specification),
with a few exceptions / comments:

* The [API incubator](./api/incubator) and [SDK incubator](./sdk-extensions/incubator)
  contain prototypes which have been discussed in the specification
  or [oteps](https://github.com/open-telemetry/oteps) and have a reasonable chance of becoming part
  of the specification, subject to maintainers' discretion.
* Components like the [Kotlin Extension](./extensions/kotlin) are included which are required for
  the API / SDK to function in key areas of the Java ecosystem. Inclusion is subject to maintainers'
  discretion.
* As a general rule, components which implement semantic conventions belong elsewhere.

Other repositories in the OpenTelemetry Java ecosystem include:

* [opentelemetry-java-instrumentation](https://github.com/open-telemetry/opentelemetry-java-instrumentation)
  contains instrumentation.
* [opentelemetry-java-contrib](https://github.com/open-telemetry/opentelemetry-java-contrib)
  contains extensions, prototypes, and instrumentation, including vendor specific components.
* [opentelemetry-java-examples](https://github.com/open-telemetry/opentelemetry-java-examples) contains
  working code snippets demonstrating various concepts.

## Style guideline

We follow the [Google Java Style Guide](https://google.github.io/styleguide/javaguide.html). Our
build will fail if source code is not formatted according to that style. To fix any style failures
the above [checks](#checks) show, automatically apply the formatting with:

```bash
$ ./gradlew spotlessApply
```

To verify code style manually run the following command, which
uses [google-java-format](https://github.com/google/google-java-format) library:

`./gradlew spotlessCheck`

### Best practices that we follow

* This project uses [semantic versioning](https://semver.org/). Except for major versions, a user
  should be able to update their dependency version on this project and have nothing break. This
  means we do not make breaking changes to the API (e.g., remove a public method) or to the ABI (
  e.g., change return type from void to non-void).
* Avoid exposing publicly any class/method/variable that don't need to be public.
* By default, all arguments/members are treated as non-null. Every argument/member that can
  be `null` must be annotated with `@Nullable`.
* The project aims to provide a consistent experience across all the public APIs. It is important to
  ensure consistency (same look and feel) across different public packages.
* Use `final` for public classes everywhere it is possible, this ensures that these classes cannot
  be extended when the API does not intend to offer that functionality.
* In general, we use the following ordering of class members:
  * Static fields (final before non-final)
  * Instance fields (final before non-final)
  * Constructors
    * In static utility classes (where all members are static), the private constructor
      (used to prevent construction) should be ordered after methods instead of before methods.
  * Methods
    * If methods call each other, it's nice if the calling method is ordered (somewhere) above the
      method that it calls. So, for one example, a private method would be ordered (somewhere) below
      the non-private methods that use it.
  * Nested classes
* Adding `toString()` overrides on classes is encouraged, but we only use `toString()` to provide
  debugging assistance. The implementations of all `toString()` methods should be considered to be
  unstable unless explicitly documented otherwise.
* Avoid synchronizing using a class's intrinsic lock. Instead, synchronize on a dedicated lock object. E.g:
  ```java
  private final Object lock = new Object();

  public void doSomething() {
    synchronized (lock) { ... }
  }
  ```
* Don't
  use [gradle test fixtures](https://docs.gradle.org/current/userguide/java_testing.html#sec:java_test_fixtures) (
  i.e. `java-test-fixtures` plugin) to reuse code for internal testing. The test fixtures plugin has
  side effects where test dependencies are added to the `pom.xml` and publishes an
  extra `*-test-fixtures.jar` artifact which is unnecessary for internal testing. Instead, create a
  new `*:testing-internal` module and omit the `otel.java-conventions`. For example,
  see [/exporters/otlp/testing-internal](./exporters/otlp/testing-internal).

If you notice any practice being applied in the project consistently that isn't listed here, please
consider a pull request to add it.

### Pre-commit hook

To completely delegate code style formatting to the machine, you can
add [git pre-commit hook](https://git-scm.com/docs/githooks). We provide an example script
in `buildscripts/pre-commit` file. Just copy or symlink it into `.git/hooks` folder.

### Editorconfig

As additional convenience for IntelliJ Idea users, we provide `.editorconfig` file. Idea will
automatically use it to adjust its code formatting settings. It does not support all required rules,
so you still have to run `spotlessApply` from time to time.

### Javadoc

* All public classes and their public and protected methods MUST have javadoc. It MUST be complete (
  all params documented etc.) Everything else
  (package-protected classes, private) MAY have javadoc, at the code writer's whim. It does not have
  to be complete, and reviewers are not allowed to require or disallow it.
* Each API element should have a `@since` tag specifying the minor version when it was released (or
  the next minor version).
* There MUST be NO javadoc errors.

See [section 7.3.1](https://google.github.io/styleguide/javaguide.html#s7.3.1-javadoc-exception-self-explanatory)
in the guide for exceptions to the Javadoc requirement.

* Reviewers may request documentation for any element that doesn't require Javadoc, though the style
  of documentation is up to the author.
* Try to do the least amount of change when modifying existing documentation. Don't change the style
  unless you have a good reason.
* We do not use `@author` tags in our javadoc.
* Our javadoc is available via [
  javadoc.io}(https://javadoc.io/doc/io.opentelemetry/opentelemetry-api)

### SDK Configuration Documentation

All changes to the SDK configuration options or autoconfigure module should be documented on
[opentelemetry.io](https://opentelemetry.io/docs/languages/java/configuration/).

### AutoValue

* Use [AutoValue](https://github.com/google/auto/tree/master/value), when possible, for any new
  value classes. Remember to add package-private constructors to all AutoValue classes to prevent
  classes in other packages from extending them.

### Unit Tests

* Unit tests target Java 8, so language features such as lambda and streams can be used in tests.

## Specific tasks

### Updating the Snapshot build number

The overall version number for opentelemetry-java is determined from git tags, and not fixed in any
file.

This means it will not update, even if you `git pull` from the repo tip. It will still produce a set
of libraries with the old version number.

To update it, you must fetch the tags, via `git fetch --all --tags` - which should work, even if you
have forked the repo, as long as the trunk repo is set as an upstream remote.

### Composing builds

Beware that this section is only meant for developers of opentelemetry-java, or closely related
projects. The steps described here could change at any time and what you do for one version (commit)
may break with the next one already.

Gradle provides a feature
called ["composite builds"](https://docs.gradle.org/current/userguide/composite_builds.html)
that allows to replace some normally externally provided dependencies with a project that is built
(included) in the same Gradle invocation. This can be useful to quickly test a new feature or bug
fix you are developing in opentelemetry-java with the examples or the app or instrumentation library
where you need the feature or run into the bug. Unfortunately, opentelemetry-java does not work out
of the box with this feature because Gradle is unable to map the project names to the customized
artifact coordinates (see e.g. [gradle/gradle#18291](https://github.com/gradle/gradle/issues/18291)
and related issues. However, gradle supports manually declaring the mapping between ("substitution
of")
artifact coordinates and project names. To ease this tedious task, opentelemetry-java provides a
gradle task `:generateBuildSubstitutions` that generates a code snippet with these substitutions in
kts (Kotlin Script) format.

Example usage could be as follows:

1. Run `./gradlew generateBuildSubstitutions`
2. Two files named `build/substitutions.gradle.kts` are generated in the bom and bom-alpha project's
   directory, containing substitutions for the stable and alpha projects respectively.
3. Copy & paste the content of these files to a new `settings.gradle.kts` or the one where you want
   to include the opentelemetry build into, so that it contains something like the following:

   ```kotlin
   includeBuild("PATH/TO/OPENTELEMETRY-JAVA/ROOT/DIRECTORY") {
     // Copy & paste following block from the generated substitutions.gradle.kts, *not* from here!
     dependencySubstitution {
       substitute(module("io.opentelemetry:opentelemetry-api")).using(project(":api:all"))
       substitute(module("io.opentelemetry:opentelemetry-sdk")).using(project(":sdk:all"))
       // ...
     }
   }
   ```

   Please confirm whether the local opentelemetry-java version is consistent with the
   opentelemetry-java version declared in the project that relies on opentelemetry-java.
   If it is inconsistent, `dependencySubstitution` may not take effect.

   See [the Gradle documentation](https://docs.gradle.org/current/userguide/composite_builds.html#included_build_declaring_substitutions)
   for more information.
4. If you now build your project, it will use the included build to supply the opentelemetry-java
   artifacts, ignoring any version declarations. Use the prefix `:DIRECTORY:` to refer to
   tasks/projects within the included build, where DIRECTORY is the name of the directory in the
   included build (only the part after the last `/`).
5. Here are some issues and solutions ([discussions/6551](https://github.com/open-telemetry/opentelemetry-java/discussions/6551))
   you may encounter that may be helpful to you.

### Updating the OTLP protobufs

OTLP protobuf Java bindings are published via
the [opentelemetry-proto-java](https://github.com/open-telemetry/opentelemetry-proto-java)
repository. This project does not use the java bindings, but does use the `.proto` files that are
published in the binding jar by that project.

To update the OTLP protobuf version,
first [release a new version of the java bindings](https://github.com/open-telemetry/opentelemetry-proto-java/blob/main/RELEASING.md)
then simply update the dependency version that this project has on that jar.
