Skip to content

Commit e8a71d3

Browse files
committed
init commit
1 parent b4297d5 commit e8a71d3

17 files changed

Lines changed: 2440 additions & 1 deletion

.github/workflows/build.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
name: Maven Build Artifact
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- '*'
7+
8+
jobs:
9+
build:
10+
uses: valitydev/java-workflow/.github/workflows/maven-library-build.yml@v3
11+

.github/workflows/deploy.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
name: Maven Deploy Artifact
2+
3+
on:
4+
push:
5+
branches:
6+
- 'master'
7+
- 'main'
8+
- 'rc/**'
9+
10+
jobs:
11+
deploy:
12+
uses: valitydev/java-workflow/.github/workflows/maven-library-deploy.yml@v3
13+
secrets:
14+
server-username: ${{ secrets.OSSRH_USERNAME }}
15+
server-password: ${{ secrets.OSSRH_TOKEN }}
16+
deploy-secret-key: ${{ secrets.OSSRH_GPG_SECRET_KEY }}
17+
deploy-secret-key-password: ${{ secrets.OSSRH_GPG_SECRET_KEY_PASSWORD }}
18+
mm-webhook-url: ${{ secrets.MATTERMOST_WEBHOOK_URL }}
19+

.gitignore

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Compiled class file
2+
*.class
3+
4+
# Log file
5+
*.log
6+
7+
# BlueJ files
8+
*.ctxt
9+
10+
# Mobile Tools for Java (J2ME)
11+
.mtj.tmp/
12+
13+
# Package Files #
14+
*.jar
15+
*.war
16+
*.nar
17+
*.ear
18+
*.zip
19+
*.tar.gz
20+
*.rar
21+
22+
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
23+
hs_err_pid*
24+
replay_pid*
25+
26+
# Maven
27+
target/
28+
pom.xml.tag
29+
pom.xml.releaseBackup
30+
pom.xml.versionsBackup
31+
pom.xml.next
32+
release.properties
33+
dependency-reduced-pom.xml
34+
buildNumber.properties
35+
.mvn/timing.properties
36+
.mvn/wrapper/maven-wrapper.jar
37+
38+
# IDE
39+
.idea/
40+
*.iml
41+
*.ipr
42+
*.iws
43+
.project
44+
.classpath
45+
.settings/
46+
.factorypath
47+
.vscode/
48+
*.swp
49+
*~
50+
51+
# OS
52+
.DS_Store
53+
Thumbs.db

README.md

Lines changed: 225 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,225 @@
1-
# otel-baggify
1+
# otel-baggify
2+
3+
Библиотека для декларативного обогащения OpenTelemetry Baggage через Spring AOP аннотации.
4+
5+
## Назначение
6+
7+
`otel-baggify` предоставляет Spring AOP механизм для извлечения значений из аргументов метода и обогащения OpenTelemetry Baggage на время выполнения метода. Это позволяет прозрачно передавать контекстную информацию через границы сервисов без модификации бизнес-логики.
8+
9+
## Требования
10+
11+
- Java 17+
12+
- Spring Boot 3.x
13+
- OpenTelemetry API 1.35+
14+
15+
## Подключение
16+
17+
```xml
18+
<dependency>
19+
<groupId>dev.vality</groupId>
20+
<artifactId>otel-baggify</artifactId>
21+
<version>1.0.0-SNAPSHOT</version>
22+
</dependency>
23+
```
24+
25+
## Быстрый старт
26+
27+
### Базовое использование
28+
29+
```java
30+
@Service
31+
public class UserService {
32+
33+
@WithBaggage(@BaggageField(key = "user.id", path = "#userId"))
34+
public User getUser(String userId) {
35+
// Baggage содержит "user.id" во время выполнения метода
36+
return userRepository.findById(userId);
37+
}
38+
}
39+
```
40+
41+
### Несколько полей
42+
43+
```java
44+
@WithBaggage({
45+
@BaggageField(key = "user.id", path = "#request.userId"),
46+
@BaggageField(key = "order.id", path = "#request.orderId"),
47+
@BaggageField(key = "tenant", path = "#tenantId")
48+
})
49+
public void processOrder(OrderRequest request, String tenantId) {
50+
// Все значения доступны в Baggage
51+
}
52+
```
53+
54+
### Вложенные поля
55+
56+
```java
57+
@WithBaggage(@BaggageField(key = "customer.email", path = "#order.customer.email"))
58+
public void notifyCustomer(Order order) {
59+
// Извлекается order.getCustomer().getEmail()
60+
}
61+
```
62+
63+
### Запись в Span Attributes
64+
65+
По умолчанию все поля записываются и в Baggage, и в Span Attributes (`addToSpanAttributes = true`).
66+
67+
```java
68+
@WithSpan
69+
@WithBaggage(@BaggageField(key = "user.id", path = "#userId"))
70+
public void tracedOperation(String userId) {
71+
// Значение записывается и в Baggage, и в Span Attributes (по умолчанию)
72+
}
73+
```
74+
75+
Можно отключить запись в Span Attributes для отдельных полей:
76+
77+
```java
78+
@WithSpan
79+
@WithBaggage({
80+
@BaggageField(key = "user.id", path = "#userId"), // -> Baggage + Span Attributes
81+
@BaggageField(key = "internal.trace", path = "#traceId", addToSpanAttributes = false) // -> только Baggage
82+
})
83+
public void tracedOperation(String userId, String traceId) {
84+
// user.id виден в span attributes, internal.trace — только в baggage
85+
}
86+
```
87+
88+
## API
89+
90+
### @WithBaggage
91+
92+
Основная аннотация, применяется к методам.
93+
94+
| Параметр | Тип | По умолчанию | Описание |
95+
|----------|-----|--------------|----------|
96+
| `value` | `BaggageField[]` | - | Массив полей для извлечения |
97+
98+
### @BaggageField
99+
100+
Описывает одно соответствие между путём к значению и ключом в Baggage.
101+
102+
| Параметр | Тип | По умолчанию | Описание |
103+
|----------|-----|--------------|----------|
104+
| `key` | `String` | - | Имя ключа в Baggage (обязательный) |
105+
| `path` | `String` | - | Путь к значению (обязательный) |
106+
| `addToSpanAttributes` | `boolean` | `true` | Записывать ли в Span Attributes |
107+
| `converter` | `Class<? extends BaggageValueConverter<?>>` | `NoOp` | Класс кастомного конвертера |
108+
| `converterBean` | `String` | `""` | Имя Spring Bean конвертера |
109+
110+
## Синтаксис путей
111+
112+
Путь должен начинаться с `#` и имени параметра метода:
113+
114+
```
115+
#userId // Параметр целиком
116+
#request.userId // request.getUserId()
117+
#order.customer.email // order.getCustomer().getEmail()
118+
```
119+
120+
> **Примечание:** Работает из коробки со Spring Boot. Если имена параметров не распознаются, добавьте в maven-compiler-plugin опцию `<parameters>true</parameters>`.
121+
122+
## Конвертация значений
123+
124+
Порядок выбора механизма конвертации (от высшего приоритета к низшему):
125+
126+
1. **Кастомный конвертер** (через `converterBean` или `converter`)
127+
2. **Spring ConversionService** (если доступен и может конвертировать)
128+
3. **Строка как есть** (если значение уже `String`)
129+
4. **toString()** (fallback)
130+
131+
### Пример кастомного конвертера
132+
133+
```java
134+
// Класс конвертера
135+
public class UserIdConverter implements BaggageValueConverter<UserId> {
136+
@Override
137+
public String convert(UserId value) {
138+
return value != null ? value.getValue() : null;
139+
}
140+
}
141+
142+
// Использование
143+
@BaggageField(key = "user.id", path = "#userId", converter = UserIdConverter.class)
144+
```
145+
146+
### Spring Bean конвертер
147+
148+
```java
149+
@Component("maskedEmailConverter")
150+
public class MaskedEmailConverter implements BaggageValueConverter<String> {
151+
@Override
152+
public String convert(String email) {
153+
// Маскирование email для логов
154+
return email.replaceAll("(?<=.{2}).(?=.*@)", "*");
155+
}
156+
}
157+
158+
// Использование
159+
@BaggageField(key = "user.email", path = "#email", converterBean = "maskedEmailConverter")
160+
```
161+
162+
## Порядок выполнения аспектов
163+
164+
При совместном использовании с `@WithSpan`, аспект `@WithBaggage` выполняется **после** `@WithSpan`, то есть обогащение Baggage происходит внутри уже активного Span.
165+
166+
Это обеспечивается через Spring AOP ordering: `WithBaggageAspect` имеет порядок `Ordered.LOWEST_PRECEDENCE - 100`.
167+
168+
## Обработка null значений
169+
170+
- Если значение не найдено или равно `null`, запись в Baggage **не выполняется**
171+
- Исключения **не выбрасываются** при отсутствии значения
172+
- При отсутствии активного Span, запись в Span Attributes безопасно пропускается
173+
174+
## Восстановление контекста
175+
176+
После завершения метода (успешного или с исключением) исходный OTEL контекст и Baggage автоматически восстанавливаются без утечек.
177+
178+
## Конфигурация
179+
180+
Библиотека использует Spring Boot Auto Configuration. Все необходимые бины создаются автоматически при наличии OpenTelemetry на classpath.
181+
182+
Для кастомизации можно переопределить бины:
183+
184+
```java
185+
@Configuration
186+
public class CustomBaggifyConfig {
187+
188+
@Bean
189+
public PathValueExtractor customPathValueExtractor() {
190+
// Ваша реализация
191+
}
192+
193+
@Bean
194+
public BaggageValueConverterResolver customConverterResolver(
195+
BeanFactory beanFactory,
196+
ConversionService conversionService) {
197+
// Ваша реализация
198+
}
199+
}
200+
```
201+
202+
## Обработка ошибок
203+
204+
Библиотека **никогда не ломает бизнес-логику**. При любых проблемах с конфигурацией или извлечением значений:
205+
206+
- Метод выполняется как обычно
207+
- В лог пишется предупреждение (WARN)
208+
- Проблемное поле просто пропускается
209+
210+
Примеры ситуаций, которые логируются как предупреждения:
211+
212+
- `key` пустой или `null`
213+
- `path` не начинается с `#`
214+
- Параметр с указанным именем не найден
215+
- Дублирующиеся ключи в одной аннотации
216+
- Ошибка в кастомном конвертере
217+
218+
```
219+
WARN WithBaggageAspect : Method 'process': @BaggageField path 'userId' for key 'user.id' must start with '#', skipping
220+
WARN WithBaggageAspect : Method 'process': Duplicate baggage key 'user.id', only first occurrence will be used
221+
```
222+
223+
## Лицензия
224+
225+
Apache License 2.0

0 commit comments

Comments
 (0)