Skip to content

Commit

Permalink
Merge branch 'main' into feature/issue-5826-create-observability-prim…
Browse files Browse the repository at this point in the history
…er.md
  • Loading branch information
iguitton authored Jan 7, 2025
2 parents 781eeb3 + 0e25505 commit 4732c6f
Show file tree
Hide file tree
Showing 10 changed files with 411 additions and 567 deletions.
7 changes: 4 additions & 3 deletions .github/workflows/pr-actions.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
name: PR actions
# cSpell:ignore esac htmltest refcache nvmrc

on:
issue_comment:
Expand Down Expand Up @@ -29,9 +30,9 @@ jobs:
- name: Extract action name
id: extract_action_name
run: |
PR_ACTION=$(echo $COMMENT | grep -oP '/fix:\K[-_0-9a-z]+')
PR_ACTION=$(echo $COMMENT | grep -oP '/fix:\K[:-_0-9a-z]+')
echo "Action is $PR_ACTION"
ACTION_NAMES="all|dict|expired|filenames|format|htmltest-config|i18n|markdown|refcache|submodules?|text"
ACTION_NAMES="all|dict|expired|filenames|format|htmltest-config|i18n|markdown|refcache(:refresh)?|submodules?|text"
if [[ ! "$PR_ACTION" =~ ^($ACTION_NAMES)$ ]]; then
echo "Invalid action name: $PR_ACTION"
echo "Action name should be one of: $ACTION_NAMES"
Expand Down Expand Up @@ -69,7 +70,7 @@ jobs:

- run: |
case $PR_ACTION in
all|refcache|text)
all|refcache*|text)
npm install --omit=optional
;&
*)
Expand Down
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@
[submodule "content-modules/opentelemetry-java-examples"]
path = content-modules/opentelemetry-java-examples
url = https://github.com/open-telemetry/opentelemetry-java-examples.git
javaexamples-pin = 63cc9b4
javaexamples-pin = cce0f6a
4 changes: 4 additions & 0 deletions content/en/blog/2025/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,7 @@ title: 2025
weight: -2025
outputs: [HTML, RSS]
---

## Happy New Year!

Amazing posts are on their way for 2025 — check back soon.
146 changes: 132 additions & 14 deletions content/en/docs/languages/java/sdk.md
Original file line number Diff line number Diff line change
Expand Up @@ -435,8 +435,7 @@ Span exporters built-in to the SDK and maintained by the community in
| `InterceptableSpanExporter` | `io.opentelemetry.contrib:opentelemetry-processors:{{% param vers.contrib %}}-alpha` | Passes spans to a flexible interceptor before exporting. |
| `KafkaSpanExporter` | `io.opentelemetry.contrib:opentelemetry-kafka-exporter:{{% param vers.contrib %}}-alpha` | Exports spans by writing to a Kafka topic. |

**[1]**: See [OTLP exporter sender](#otlp-exporter-senders) for implementation
details.
**[1]**: See [OTLP exporters](#otlp-exporters) for implementation details.

The following code snippet demonstrates `SpanExporter` programmatic
configuration:
Expand Down Expand Up @@ -767,8 +766,7 @@ Metric exporters built-in to the SDK and maintained by the community in
| `OtlpStdoutMetricExporter` | `io.opentelemetry:opentelemetry-exporter-logging-otlp:{{% param vers.otel %}}` | Logs metrics to `System.out` in the OTLP [JSON file encoding][] (experimental). |
| `InterceptableMetricExporter` | `io.opentelemetry.contrib:opentelemetry-processors:{{% param vers.contrib %}}-alpha` | Passes metrics to a flexible interceptor before exporting. |

**[1]**: See [OTLP exporter sender](#otlp-exporter-senders) for implementation
details.
**[1]**: See [OTLP exporters](#otlp-exporters) for implementation details.

The following code snippet demonstrates `MetricExporter` programmatic
configuration:
Expand Down Expand Up @@ -1095,8 +1093,7 @@ Span exporters built-in to the SDK and maintained by the community in
| `OtlpStdoutLogRecordExporter` | `io.opentelemetry:opentelemetry-exporter-logging-otlp:{{% param vers.otel %}}` | Logs log records to `System.out` in the OTLP [JSON file encoding][] (experimental). |
| `InterceptableLogRecordExporter` | `io.opentelemetry.contrib:opentelemetry-processors:{{% param vers.contrib %}}-alpha` | Passes log records to a flexible interceptor before exporting. |

**[1]**: See [OTLP exporter sender](#otlp-exporter-senders) for implementation
details.
**[1]**: See [OTLP exporters](#otlp-exporters) for implementation details.

**[2]**: `OtlpJsonLoggingLogRecordExporter` logs to JUL, and may cause infinite
loops (i.e. JUL -> SLF4J -> Logback -> OpenTelemetry Appender -> OpenTelemetry
Expand Down Expand Up @@ -1362,20 +1359,29 @@ public class IgnoreExportErrorsFilter implements java.util.logging.Filter {
io.opentelemetry.sdk.trace.export.BatchSpanProcessor = io.opentelemetry.extension.logging.IgnoreExportErrorsFilter
```

### OTLP exporter senders
### OTLP exporters

The [span exporter](#spanexporter), [metric exporter](#metricexporter), and
[log exporter](#logrecordexporter) discuss OTLP exporters of the form:
[log exporter](#logrecordexporter) sections describe OTLP exporters of the form:

- `OtlpHttp{Signal}Exporter`s export data via OTLP `http/protobuf`.
- `OtlpGrpc{Signal}Exporter`s export data via OTLP `grpc`.
- `OtlpHttp{Signal}Exporter`, which exports data via OTLP `http/protobuf`
- `OtlpGrpc{Signal}Exporter`, which exports data via OTLP `grpc`

The exporters for all signals are available via
`io.opentelemetry:opentelemetry-exporter-otlp:{{% param vers.otel %}}`.
`io.opentelemetry:opentelemetry-exporter-otlp:{{% param vers.otel %}}`, and have
significant overlap across `grpc` and `http/protobuf` versions of the OTLP
protocol, and between signals. The following sections elaborate on these key
concepts:

Internally, these exporters depend on various client libraries to execute HTTP
and gRPC requests. There is no single HTTP / gRPC client library which satisfies
all use cases in the Java ecosystem:
- [Senders](#senders): an abstraction for a different HTTP / gRPC client
libraries.
- [Authentication](#authentication) options for OTLP exporters.

#### Senders

The OTLP exporters depend on various client libraries to execute HTTP and gRPC
requests. There is no single HTTP / gRPC client library which satisfies all use
cases in the Java ecosystem:

- Java 11+ brings the built-in `java.net.http.HttpClient`, but
`opentelemetry-java` needs to support Java 8+ users, and this can't be used to
Expand Down Expand Up @@ -1403,6 +1409,118 @@ add a dependency on the alternative.
you must also add a dependency on a
[gRPC transport implementations](https://github.com/grpc/grpc-java#transport).

#### Authentication

The OTLP exporters provide mechanisms for static and dynamic header-based
authentication, and for mTLS.

If using
[zero-code SDK autoconfigure](../configuration/#zero-code-sdk-autoconfigure)
with environment variables and system properties, see
[relevant system properties](../configuration/#properties-exporters):

- `otel.exporter.otlp.headers` for static header-based authentication.
- `otel.exporter.otlp.client.key`, `otel.exporter.otlp.client.certificate` for
mTLS authentication.

The following code snippet demonstrates programmatic configuration of static and
dynamic header-based authentication:

<!-- prettier-ignore-start -->
<?code-excerpt "src/main/java/otel/OtlpAuthenticationConfig.java"?>
```java
package otel;

import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter;
import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporter;
import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter;
import java.time.Duration;
import java.time.Instant;
import java.util.Collections;
import java.util.Map;
import java.util.function.Supplier;

public class OtlpAuthenticationConfig {
public static void staticAuthenticationHeader(String endpoint) {
// If the OTLP destination accepts a static, long-lived authentication header like an API key,
// set it as a header.
// This reads the API key from the OTLP_API_KEY env var to avoid hard coding the secret in
// source code.
String apiKeyHeaderName = "api-key";
String apiKeyHeaderValue = System.getenv("OTLP_API_KEY");

// Initialize OTLP Span, Metric, and LogRecord exporters using a similar pattern
OtlpHttpSpanExporter spanExporter =
OtlpHttpSpanExporter.builder()
.setEndpoint(endpoint)
.addHeader(apiKeyHeaderName, apiKeyHeaderValue)
.build();
OtlpHttpMetricExporter metricExporter =
OtlpHttpMetricExporter.builder()
.setEndpoint(endpoint)
.addHeader(apiKeyHeaderName, apiKeyHeaderValue)
.build();
OtlpHttpLogRecordExporter logRecordExporter =
OtlpHttpLogRecordExporter.builder()
.setEndpoint(endpoint)
.addHeader(apiKeyHeaderName, apiKeyHeaderValue)
.build();
}

public static void dynamicAuthenticationHeader(String endpoint) {
// If the OTLP destination requires a dynamic authentication header, such as a JWT which needs
// to be periodically refreshed, use a header supplier.
// Here we implement a simple supplier which adds a header of the form "Authorization: Bearer
// <token>", where <token> is fetched from refreshBearerToken every 10 minutes.
String username = System.getenv("OTLP_USERNAME");
String password = System.getenv("OTLP_PASSWORD");
Supplier<Map<String, String>> supplier =
new AuthHeaderSupplier(() -> refreshToken(username, password), Duration.ofMinutes(10));

// Initialize OTLP Span, Metric, and LogRecord exporters using a similar pattern
OtlpHttpSpanExporter spanExporter =
OtlpHttpSpanExporter.builder().setEndpoint(endpoint).setHeaders(supplier).build();
OtlpHttpMetricExporter metricExporter =
OtlpHttpMetricExporter.builder().setEndpoint(endpoint).setHeaders(supplier).build();
OtlpHttpLogRecordExporter logRecordExporter =
OtlpHttpLogRecordExporter.builder().setEndpoint(endpoint).setHeaders(supplier).build();
}

private static class AuthHeaderSupplier implements Supplier<Map<String, String>> {
private final Supplier<String> tokenRefresher;
private final Duration tokenRefreshInterval;
private Instant refreshedAt = Instant.ofEpochMilli(0);
private String currentTokenValue;

private AuthHeaderSupplier(Supplier<String> tokenRefresher, Duration tokenRefreshInterval) {
this.tokenRefresher = tokenRefresher;
this.tokenRefreshInterval = tokenRefreshInterval;
}

@Override
public Map<String, String> get() {
return Collections.singletonMap("Authorization", "Bearer " + getToken());
}

private synchronized String getToken() {
Instant now = Instant.now();
if (currentTokenValue == null || now.isAfter(refreshedAt.plus(tokenRefreshInterval))) {
currentTokenValue = tokenRefresher.get();
refreshedAt = now;
}
return currentTokenValue;
}
}

private static String refreshToken(String username, String password) {
// For a production scenario, this would be replaced with out-of-band request to exchange
// username / password for bearer token.
return "abc123";
}
}
```
<!-- prettier-ignore-end -->

### Testing

TODO: document tools available for testing the SDK
Expand Down
2 changes: 1 addition & 1 deletion gulp-src/prune.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ async function pruneTask() {
return;
} else if (n == 0) {
console.log(
`WARN: num is ${n} so no entries will be pruned by date. Specify number of entries to prune as --num <n>.`,
`WARN: num is ${n} so no entries will be pruned by date. Specify number of entries to prune as --num <n>. For more info use --info`,
);
if (numEntriesWith4xxStatus == 0) return;
}
Expand Down
35 changes: 35 additions & 0 deletions layouts/blog/content.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{{/* Docsy override (temporary) */ -}}

<div class="td-content">
<h1>{{ .Title }}</h1>
{{ with .Params.description }}<div class="lead">{{ . | markdownify }}</div>{{ end }}
<div class="td-byline mb-4">
{{ with .Params.author }}{{ T "post_byline_by" }} <b>{{ . | markdownify }}</b> |{{ end}}
<time datetime="{{ $.Date.Format "2006-01-02" }}" class="text-body-secondary">{{ $.Date.Format $.Site.Params.time_format_blog }}</time>
</div>
<header class="article-meta">
{{ partial "taxonomy_terms_article_wrapper.html" . -}}
{{ if (and (not .Params.hide_readingtime) (.Site.Params.ui.readingtime.enable)) -}}
{{ partial "reading-time.html" . -}}
{{ end -}}
</header>
{{ if .Date.Before (now.AddDate -1 0 0) -}}
<div class="pageinfo pageinfo-warning">
<p>
<i class="fa-solid fa-triangle-exclamation"></i>
Blog posts are not updated after publication. This post is more
than a year old, so its content may be outdated, and some links may be
invalid. Cross-verify any information before relying on it.
</p>
</div>
{{ end -}}
{{ .Content }}
{{ if (.Site.Config.Services.Disqus.Shortname) -}}
<br />
{{- partial "disqus-comment.html" . -}}
<br />
{{ end -}}

{{ partial "pager.html" . }}
{{ partial "page-meta-lastmod.html" . -}}
</div>
43 changes: 43 additions & 0 deletions layouts/blog/list.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{{ define "main" }}
{{ if (and .Parent .Parent.IsHome) -}}
{{ $.Scratch.Set "blog-pages" (where .Site.RegularPages "Section" .Section) -}}
{{ else -}}
{{$.Scratch.Set "blog-pages" .Pages -}}
{{ end -}}

{{/* Docsy override - temporary */ -}}
{{ .Content -}}

<div class="td-blog-posts">
{{ if .Pages -}}
{{ $pag := .Paginate (( $.Scratch.Get "blog-pages").GroupByDate "2006" ) -}}
{{ range $pag.PageGroups -}}
<div class="h2">{{ T "post_posts_in" }} {{ .Key }}</div>
<ul class="td-blog-posts-list">
{{ range .Pages -}}
<li class="td-blog-posts-list__item">
<div class="td-blog-posts-list__body">
<h5 class="mt-0 mb-1"><a href="{{ .RelPermalink }}">{{ .Title }}</a></h5>
<p class="mb-2 mb-md-3"><small class="text-body-secondary">{{ .Date.Format ($.Param "time_format_blog") }} {{ T "ui_in"}} {{ .CurrentSection.LinkTitle }}</small></p>
<header class="article-meta">
{{ partial "taxonomy_terms_article_wrapper.html" . -}}
{{ if (and (not .Params.hide_readingtime) (.Site.Params.ui.readingtime.enable)) -}}
{{ partial "reading-time.html" . -}}
{{ end -}}
</header>
{{ partial "featured-image.html" (dict "p" . "w" 250 "h" 125 "class" "float-start me-3 pt-1 d-none d-md-block") -}}
<p class="pt-0 mt-0">{{ .Plain | safeHTML | truncate 250 }}</p>
<p class="pt-0"><a href="{{ .RelPermalink }}" aria-label="{{ T "ui_read_more"}} - {{ .LinkTitle }}">{{ T "ui_read_more"}}</a></p>
</div>
</li>
{{ end -}}
</ul>
{{ end -}}
{{ end }}
</div>
<div class="td-blog-posts__pagination">
{{ if .Pages -}}
{{ template "_internal/pagination.html" . -}}
{{ end -}}
</div>
{{ end -}}
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"_prebuild": "npm run seq -- get:submodule cp:spec",
"_prepare:docsy": "cd themes/docsy && npm install",
"_prettier:any": "npx prettier --ignore-path ''",
"_refcache:prune": "npx gulp prune",
"_rename-to-kebab-case": "find assets content static -name '*_*' ! -name '[_.]*' -exec sh -c 'mv \"$1\" \"${1//_/-}\"' _ {} \\;",
"_serve:hugo": "hugo server --buildDrafts --minify",
"_serve:netlify": "netlify dev -c \"npm run _serve:hugo -- --renderToMemory\"",
Expand Down Expand Up @@ -75,6 +76,7 @@
"fix:i18n": "npm run fix:i18n:new",
"fix:markdown": "npm run check:markdown -- --fix",
"fix:refcache": "npm run check:links",
"fix:refcache:fresh": "npm run _refcache:prune -- -n ${PRUNE_N:-128}",
"fix:submodule": "npm run pin:submodule",
"fix:text": "npm run check:text -- --fix",
"fix": "npm run fix:all",
Expand All @@ -86,6 +88,7 @@
"netlify-build:preview": "npm run seq -- build:preview diff:check",
"netlify-build:production": "npm run seq -- build:production diff:check",
"pin:submodule": "npm run _pin:submodule -- $PIN_SKIP",
"postfix:refcache:fresh": "npm run fix:refcache",
"postfix:submodule": "git submodule",
"postget:submodule": "git submodule",
"prebuild:preview": "npm run _prebuild",
Expand All @@ -106,7 +109,7 @@
"test": "npm run check",
"update:pkg:hugo": "npm install --save-dev --save-exact hugo-extended@latest",
"update:pkgs": "npx npm-check-updates -u",
"update:submodule": "set -x && git submodule update --remote ${DEPTH:- --depth 999}"
"update:submodule": "set -x && git submodule update --remote ${DEPTH:- --depth 999} && git submodule foreach 'git fetch $(git remote | tail -1) --tags'"
},
"devDependencies": {
"@cspell/dict-es-es": "^3.0.3",
Expand Down
Loading

0 comments on commit 4732c6f

Please sign in to comment.