Opentelemetry
OpenTelemetry observability - use for distributed tracing, metrics, instrumentation, Sentry integration, and monitoringFrom its SKILL.md
npx -y skills add AndVl1/claude-plugin --skill opentelemetryAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 5 stars5 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
6.5 KB, as published. Nobody here has run it
OpenTelemetry Patterns
Spring Boot Configuration
// build.gradle.kts
dependencies {
implementation(platform("io.opentelemetry.instrumentation:opentelemetry-instrumentation-bom:2.15.0"))
implementation("io.opentelemetry.instrumentation:opentelemetry-spring-boot-starter")
implementation("io.micrometer:micrometer-tracing-bridge-otel")
implementation("io.opentelemetry:opentelemetry-exporter-zipkin")
// Sentry integration
implementation("io.sentry:sentry-spring-boot-starter-jakarta:8.26.0")
implementation("io.sentry:sentry-logback:8.26.0")
}
# application.yaml
spring:
application:
name: your-project
management:
tracing:
sampling:
probability: 1.0 # 100% in dev, lower in prod
otlp:
tracing:
endpoint: http://localhost:4318/v1/traces
otel:
exporter:
otlp:
endpoint: http://otel-collector:4317
service:
name: your-project
resource:
attributes:
deployment.environment: ${ENVIRONMENT:dev}
service.version: ${APP_VERSION:unknown}
sentry:
dsn: ${SENTRY_DSN:}
environment: ${ENVIRONMENT:dev}
traces-sample-rate: 1.0
Custom Span Creation
import io.opentelemetry.api.trace.Span
import io.opentelemetry.api.trace.Tracer
import io.opentelemetry.context.Context
import org.springframework.stereotype.Component
@Component
class TracingService(
private val tracer: Tracer
) {
fun <T> withSpan(
spanName: String,
attributes: Map<String, String> = emptyMap(),
block: () -> T
): T {
val span = tracer.spanBuilder(spanName)
.setParent(Context.current())
.startSpan()
attributes.forEach { (key, value) ->
span.setAttribute(key, value)
}
return try {
span.makeCurrent().use {
block()
}
} catch (e: Exception) {
span.recordException(e)
span.setStatus(io.opentelemetry.api.trace.StatusCode.ERROR, e.message ?: "Error")
throw e
} finally {
span.end()
}
}
}
// Usage for Telegram bot
@Service
class CommandService(
private val tracingService: TracingService,
private val userRepository: UserRepository
) {
suspend fun handleCommand(message: CommonMessage<*>, command: String) {
tracingService.withSpan(
"CommandService.handleCommand",
mapOf(
"telegram.command" to command,
"telegram.chat_id" to message.chat.id.chatId.toString(),
"telegram.user_id" to (message.from?.id?.chatId?.toString() ?: "unknown")
)
) {
Span.current().addEvent("Processing command: $command")
when (command) {
"start" -> handleStart(message)
"help" -> handleHelp(message)
else -> handleUnknown(message)
}
}
}
}
Annotation-Based Tracing
import io.micrometer.tracing.annotation.NewSpan
import io.micrometer.tracing.annotation.SpanTag
@Service
class MessageService {
@NewSpan("bot.sendMessage")
suspend fun sendMessage(
@SpanTag("telegram.chat_id") chatId: Long,
@SpanTag("message.type") type: String
): Message {
// Automatically traced
return bot.sendMessage(ChatId(chatId), text)
}
@NewSpan("bot.handleCallback")
suspend fun handleCallback(
@SpanTag("callback.data") data: String,
@SpanTag("telegram.user_id") userId: Long
) {
// Process callback query
}
}
Baggage Propagation
import io.opentelemetry.api.baggage.Baggage
// Set baggage (propagates across services)
fun setUserContext(userId: String, tenantId: String) {
Baggage.current()
.toBuilder()
.put("user.id", userId)
.put("tenant.id", tenantId)
.build()
.makeCurrent()
}
// Read baggage
fun getCurrentUserId(): String? {
return Baggage.current().getEntryValue("user.id")
}
Metrics
// Kotlin/Spring Boot
import io.micrometer.core.instrument.MeterRegistry
import io.micrometer.core.instrument.Timer
@Component
class BotMetricsService(
private val registry: MeterRegistry
) {
private val commandCounter = registry.counter(
"your-project.bot.commands",
"command", "unknown"
)
private val messageProcessingTimer = Timer.builder("your-project.bot.message.duration")
.description("Time to process a message")
.register(registry)
fun recordCommand(command: String) {
registry.counter("your-project.bot.commands", "command", command).increment()
}
fun recordCallback(action: String) {
registry.counter("your-project.bot.callbacks", "action", action).increment()
}
fun <T> timeMessageProcessing(block: () -> T): T {
return messageProcessingTimer.recordCallable(block)!!
}
}
Sentry Integration
// Error reporting with Sentry for Telegram bot
import io.sentry.Sentry
import io.sentry.SentryLevel
class BotErrorHandler {
fun handleBotException(e: Exception, chatId: Long?, command: String?) {
Sentry.withScope { scope ->
scope.setTag("error.type", e.javaClass.simpleName)
scope.setTag("bot.command", command ?: "unknown")
scope.setLevel(SentryLevel.ERROR)
scope.setContexts("telegram", mapOf(
"chat_id" to (chatId?.toString() ?: "unknown"),
"command" to (command ?: "none")
))
Sentry.captureException(e)
}
}
}
// Usage in bot handler
bot.buildBehaviourWithLongPolling(
defaultExceptionsHandler = { e ->
errorHandler.handleBotException(e, null, null)
logger.error("Bot error", e)
}
) {
// handlers
}
OpenTelemetry Collector Config
# otel-collector-config.yaml
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
batch:
timeout: 1s
send_batch_size: 1024
exporters:
zipkin:
endpoint: http://zipkin:9411/api/v2/spans
prometheus:
endpoint: 0.0.0.0:8889
logging:
loglevel: debug
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [zipkin, logging]
metrics:
receivers: [otlp]
processors: [batch]
exporters: [prometheus]