How to add a label to a target in Prometheus using Spring boot? - spring

I'm trying to add a label to a target in Prometheus modifying files in SpringBoot.
I tried adding a Prometheus label in Springboot the way below (modifying application.yml in SpringBoot), but it didn't work.
management:
metrics:
tags:
application: ${spring.application.name}
threadLimitInPrometheus: 40 # This tag didn't work
Could you let me know any way to add a Prometheus label in SpringBoot?
I know there is a way to add a new label to a target modifying prometheus.yml the way below
- target_label: "foo"
replacement: "bar"
However, I'd like to find ways in SpringBoot, not Prometheus.
Thank you.

There are several ways to define tags.
Common tags could be defined in application.yml
management:
metrics:
tags:
key2: value2
In case it doesn't work make sure that configuration is applied. You can run your service locally and use http://localhost:<port>/actuator/prometheus to check exposed metrics.
As an alternative you can
use MeterRegistryCustomizer
#Configuration
public class MeterRegistryConfiguration {
#Bean
public MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
return (registry) -> registry.config().commonTags("key2", "value2");
}
}
If you need to define tags for specific metrics only, use MeterFilter
#Bean
public MeterFilter customMeterFilter() {
return new MeterFilter() {
#Override
public Meter.Id map(Meter.Id id) {
if (id.getName().contains("name")) {
return id.withTag(Tag.of("key3", "value3"));
}
return id;
}
};
}

Related

Spring data mongodb reactive: use spring.data.mongodb.uri or implement AbstractReactiveMongoConfiguration.getDatabaseName

I'm reading on several sites, I need to configure something like that in order to get access to mongodb.
#EnableReactiveMongoRepositories
public class MongoReactiveApplication
extends AbstractReactiveMongoConfiguration {
#Override
protected String getDatabaseName() {
return "<database>";
}
}
I'm using:
spring:
data:
mongodb:
uri: mongodb://<user>:<password>#<rs1>:<port>,<rs2>:<port>,<rs3>:<port>/<database>
As you can see, I'm using spring.data.mongodb.uri property in order to set uri. Inside this value there is the <database>.
Why should I need to implement AbstractReactiveMongoConfiguration.getDatabaseName once again programatically?

EnableBinding, Output, Input deprecated Since version of 3.1 of Spring Cloud Stream

Since version 3.1 the major API for working with queues is deprecated.
In the class comment it says:
Deprecated
as of 3.1 in favor of functional programming model
I searched a lot in the web for a solution but didn't find a solid E2E explanation on how I should migrate.
Looking for examples for:
read from queue
write to queue
If there are a few ways to do that (as I saw in web) I'd be glad for an explanation and the typical use case for each option as well.
I'm assuming you are already familiar with the main concepts, and will focus on the migration.
I'm using kotlin for the demo code, to reduce verbosity
First, some references which may help:
Here is the initial relevant doc: link
This is an explanation for the naming scheme in the new functional format: link
This is a more detailed explanation with some more advanced scenarios: link
TL;DR
Instead of working with annotation-based configuration, spring now uses detected beans of Consumer/Function/Supplier to define your streams for you.
Input/Consumer
Whereas before you had code looking like this:
interface BindableGradesChannel {
#Input
fun gradesChannel(): SubscribableChannel
companion object {
const val INPUT = "gradesChannel"
}
}
and the usage was similar to:
#Service
#EnableBinding(BindableGradesChannel::class)
class GradesListener {
private val log = LoggerFactory.getLogger(GradesListener::class.java)
#StreamListener(BindableScoresChannel.INPUT)
fun listen(grade: Grade) {
log.info("Received $grade")
// do something
}
}
now the entire definition is irrelevant, and can be done like so:
#Service
class GradesListener {
private val log = LoggerFactory.getLogger(GradesListener::class.java)
#Bean
fun gradesChannel(): Consumer<Grade> {
return Consumer { listen(grade = it) }
}
fun listen(grade: Grade) {
log.info("Received $grade")
// do something
}
}
notice how the Consumer bean replaced the #StreamListener and the #Input.
Regarding the configuration, if before in order to configure you had an application.yml looking like so:
spring:
cloud:
stream:
bindings:
gradesChannel:
destination: GradesExchange
group: grades-updates
consumer:
concurrency: 10
max-attempts: 3
now it should be like so:
spring:
cloud:
stream:
bindings:
gradesChannel-in-0:
destination: GradesExchange
group: grades-updates
consumer:
concurrency: 10
max-attempts: 3
notice how gradesChannel was replaced by gradesChannel-in-0 - to understand the full naming convention please see the naming convention link at the top.
Some details:
If you have more than one such bean in your application, you need to define the spring.cloud.function.definition property.
You have the option to give your channels custom names, so if you'd like to continue using gradesChannel you can set spring.cloud.stream.function.bindings.gradesChannel-in-0=gradesChannel and use everywhere in the configuration gradesChannel.
Output/Supplier
The concept here is similar, you replace config and code looking like this:
interface BindableStudentsChannel {
#Output
fun studentsChannel(): MessageChannel
}
and
#Service
#EnableBinding(BindableStudentsChannel::class)
class StudentsQueueWriter(private val studentsChannel: BindableStudentsChannel) {
fun publish(message: Message<Student>) {
studentsChannel.studentsChannel().send(message)
}
}
can now be replaced by:
#Service
class StudentsQueueWriter {
#Bean
fun studentsChannel(): Supplier<Student> {
return Supplier { Student("Adam") }
}
}
As you can see, we have a major difference - when is it called and by who?
Before we could trigger it manually, but now it is triggered by spring, every second (by default). This is fine for use cases such as when you need to publish a sensor data every second, but this is not good when you want to send the message on an event. Besides using Function for whatever reason, spring offers 2 alternatives:
StreamBridge - link
Using StreamBridge you can. define the target explicitly like so:
#Service
class StudentsQueueWriter(private val streamBridge: StreamBridge) {
fun publish(message: Message<Student>) {
streamBridge.send("studentsChannel-out-0", message)
}
}
This way you don't define the target channel as a bean, but you can still send the message. The downside is that you have some explicit configuration in your class.
Reactor API - link
The other way is to use some kind of reactive mechanism such as Sinks.Many, and to return it. Using this your code will look similar to:
#Service
class StudentsQueueWriter {
val students: Sinks.Many<String> = Sinks.many().multicast().onBackpressureBuffer()
#Bean
fun studentsChannel(): Supplier<Flux<Student>> {
return Supplier { students.asFlux() }
}
}
and the usage may be similar to:
class MyClass(val studentsQueueWriter: StudentsQueueWriter) {
fun newStudent() {
studentsQueueWriter.students.tryEmitNext(Student("Adam"))
}
}

GraphQL + Spring Boot: how to collect (error) metrics?

I've been working on adding monitoring metrics in our GraphQL gateway recently.
We're using graphql-spring-boot starter for the gateway.
After reading the following documentations, I manage to send the basic graphql.timer.query.* metrics to Datadog
https://www.baeldung.com/spring-boot-actuators
https://docs.spring.io/spring-boot/docs/2.0.x/actuator-api/html/#metrics
https://github.com/graphql-java-kickstart/graphql-spring-boot#tracing-and-metrics
What I've achieved so far is, when I send a GraphQL query/mutation, I'd collect the request count and time accordingly. e.g. sending the query below
query HelloWorldQuery {
greeting(
name: "Bob"
) {
message
}
}
I'll see metrics graphql.timer.query.count / graphql.timer.query.sum with tags operationName=HelloWorldQuery
It works like perfectly, until I want to test a query with errors. I realise there is no metrics/tags related to a failed query. For example, if I the above query returns null data and some GraphQL errors, I'd still collect graphql.timer.query.count (operationName=HelloWorldQuery), but there's no additional tags for me to tell there is an error for that query.
In the gateway, I have implemented a custom GraphQLErrorHandler, so I was thinking maybe I should add error counter (via MeterRegistry) in that class, but I am unable to get the operationName simply from GraphQLError type. the best I can get is error.getPath() which gives the method name (e.g. greeting) rather than the custom query name (HelloWorldQuery - to be consistent with what graphql.timer.query.* provides).
My question is, how to solve the above problem?
And generally what is the best way of collecting GraphQL query metrics (including errors)?
------------------- Update -------------------
2019-12-31
I read a bit more about GraphQL Instrumentation here and checked the MetricsInstrumentation implementation in graphql-spring-boot repo, the I have an idea of extending the MetricsInstrumentation class by adding error metrics there.
2020-01-02
I tried to ingest my CustomMetricsInstrumentation class, but with no luck. There is internal AutoConfiguration wiring, which I cannot insert my auto configuration in the middle.
You can override the default TracingInstrumentation with your own implementation. It will be picked automatically due to the #ConditionalOnMissingBean annotation in the GraphQLInstrumentationAutoConfiguration class. Here is a simple example that adds two custom metrics: graphql.counter.query.success and graphql.counter.query.error:
#Component
public class CustomMetricsInstrumentation extends TracingInstrumentation {
private static final String QUERY_STATUS_COUNTER_METRIC_NAME = "graphql.counter.query";
private static final String OPERATION_NAME_TAG = "operationName";
private static final String UNKNOWN_OPERATION_NAME = "unknown";
private MeterRegistry meterRegistry;
public CustomMetricsInstrumentation(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
#Override
public CompletableFuture<ExecutionResult> instrumentExecutionResult(ExecutionResult executionResult,
InstrumentationExecutionParameters parameters) {
String status = CollectionUtils.isEmpty(executionResult.getErrors()) ? "success" : "error";
String operation = parameters.getOperation() != null ? parameters.getOperation() : UNKNOWN_OPERATION_NAME;
Collection<Tag> tags = Arrays.asList(Tag.of(OPERATION_NAME_TAG, operation));
meterRegistry.counter(QUERY_STATUS_COUNTER_METRIC_NAME + "." + status, tags).increment();
return super.instrumentExecutionResult(executionResult, parameters);
}
}
My application.yaml, just in case:
graphql:
servlet:
tracing-enabled: true
actuator-metrics: true
management:
endpoint:
metrics:
enabled: true
endpoints:
web:
exposure:
include: health,metrics
I'm using spring-boot-starter-parent:2.2.2.RELEASE, graphql-spring-boot-starter:6.0.0
I hope it helps.

Spring How to map list of custom pojo

In my application.yml file, I want to define a list of rules.
rules:
- name: abc
value: something
- name: edf
value: something
Then I want to define a service like this
#Service
public class MyService {
public MyService(#Value("${rules}") List<Rule> rules) {
}
}
For the Rule pojo, it's like this.
public class Rule {
public String name, value;
}
Currently, I'm facing these errors.
If I leave rules empty, it throws can't convert String to List<Rule>
rules: []
If I keep the values, it throws could not resolve placeholder 'rules'
I really don't know what I'm doing wrong here.
From Spring docs, I found this.
Using the #Value("${property}") annotation to inject configuration
properties can sometimes be cumbersome, especially if you are working
with multiple properties or your data is hierarchical in nature.
Spring Boot provides an alternative method of working with properties
that lets strongly typed beans govern and validate the configuration
of your application
Link: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-typesafe-configuration-properties
At the end, I have to introduce another class.
#Configuration
#ConfigurationProperties(prefix="rules")
public class Rules {
public List<Rule> list;
}
Then I autowire it in MyService.

Spring-boot ConditionalOnProperty with map-based properties

My spring-boot yaml properties look like this:
service:
mycomponent:
foo:
url: http://foo
bar:
url: http://bar
This results in the following properties being set in the Spring environment:
service.mycomponent.foo.url: http://foo
service.mycomponent.bar.url: http://bar
I'd like to define a 'mycomponent' bean if there are any properties that match service.mycomponent.[a-z]*.url. Is this possible using #ConditionalOnExpression or some other type of #Conditional?
I realize I can work around this by either adding a property such as service.mycomponent.enabled: true that could be used with #ConditionalOnProperty but I'd rather avoid that if possible.
Here's the solution I ended up taking:
Create a custom Condition which searches for any properties with a certain prefix. The RelaxedPropertyResolver has the convenient getSubProperties() method. Alternative options I found were cumbersome to iterate through the PropertySource instances.
public class MyComponentCondition extends SpringBootCondition {
#Override
public ConditionOutcome getMatchOutcome(final ConditionContext context,
final AnnotatedTypeMetadata metadata) {
final RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(context.getEnvironment());
final Map<String, Object> properties = resolver.getSubProperties("service.mycomponent.");
return new ConditionOutcome(!properties.isEmpty(), "My Component");
}
}
Use that condition when setting up the bean:
#Conditional(MyComponentCondition.class)
#Bean
public MyComponent myComponent() {
return new MyComponent();
}
I'm still curious if the same thing could be done with #ConditionalOnExpression directly.

Resources