Dominik
Dominik

Reputation: 1431

Spring Boot Admin Server: App Specific Email Notification

We monitor our Spring Boot apps with the Spring Boot Admin Server and using Slack and Email notification

spring.boot.admin.notify.slack.enabled: true
...
spring.boot.admin.notify.mail.enabled: true

Is it possible to define separate recipients for the notification email per app, e.g. something like this

spring.boot.admin.notify.mail.enabled.app1: true
spring.boot.admin.notify.mail.to.app1: [email protected]
spring.boot.admin.notify.mail.enabled.app2: true
spring.boot.admin.notify.mail.to.app2: [email protected]

Upvotes: 4

Views: 3533

Answers (1)

Romil Patel
Romil Patel

Reputation: 13777

According to the SBA document, we may not implement client-specific notifications (at least not now) as required. I have gone through the code and sharing some brief how it currently works and possible ways to customize implementation using the same,


How it works?

spring.boot.admin.notify.mail

SBA implements the notifier using the AdminServerNotifierAutoConfiguration which does the basic configurations based on the properties we defined for notifications. For mail service, it has MailNotifierConfiguration along with TemplateEngine as mailNotifierTemplateEngine as follows

     @Bean
     @ConditionalOnMissingBean
     @ConfigurationProperties("spring.boot.admin.notify.mail")
     public MailNotifier mailNotifier(JavaMailSender mailSender, InstanceRepository repository) {
         return new MailNotifier(mailSender, repository, mailNotifierTemplateEngine());
     }

    @Bean
    public TemplateEngine mailNotifierTemplateEngine() {
        SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
        resolver.setApplicationContext(this.applicationContext);
        resolver.setTemplateMode(TemplateMode.HTML);
        resolver.setCharacterEncoding(StandardCharsets.UTF_8.name());

        SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        templateEngine.addTemplateResolver(resolver);
        return templateEngine;
    }

spring.boot.admin.notify.mail.to
spring.boot.admin.notify.mail.from

MailNotifier consist of the basic mail details such as from, to and additional info, etc along with default mail templet which extends the AbstractStatusChangeNotifier which is responsible for status update of applications.

I have created one enhancement request for the same here


Way 1

Step 1: Create client specific MailNotifier

You may also create subclass inside MailNotifier.

public class MailNotifierClient1 extends AbstractStatusChangeNotifier {

    private final JavaMailSender mailSender;

    private final TemplateEngine templateEngine;
    
    private String[] to = { "root@localhost" };
    private String[] cc = {};
    private String from = "Spring Boot Admin <noreply@localhost>";
    private Map<String, Object> additionalProperties = new HashMap<>();
    @Nullable private String baseUrl;
    private String template = "classpath:/META-INF/spring-boot-admin-server/mail/status-changed.html";

    public MailNotifierClient1(JavaMailSender mailSender, InstanceRepository repository, TemplateEngine templateEngine) {
        super(repository);
        this.mailSender = mailSender;
        this.templateEngine = templateEngine;
    }

    @Override
    protected Mono<Void> doNotify(InstanceEvent event, Instance instance) {
        return Mono.fromRunnable(() -> {
            Context ctx = new Context();
            ctx.setVariables(additionalProperties);
            ctx.setVariable("baseUrl", this.baseUrl);
            ctx.setVariable("event", event);
            ctx.setVariable("instance", instance);
            ctx.setVariable("lastStatus", getLastStatus(event.getInstance()));

            try {
                MimeMessage mimeMessage = mailSender.createMimeMessage();
                MimeMessageHelper message = new MimeMessageHelper(mimeMessage, StandardCharsets.UTF_8.name());
                message.setText(getBody(ctx).replaceAll("\\s+\\n", "\n"), true);
                message.setSubject(getSubject(ctx));
                message.setTo(this.to);
                message.setCc(this.cc);
                message.setFrom(this.from);
                mailSender.send(mimeMessage);
            }
            catch (MessagingException ex) {
                throw new RuntimeException("Error sending mail notification", ex);
            }
        });
    }

    protected String getBody(Context ctx) {
        return templateEngine.process(this.template, ctx);
    }

Step 2: Mapping for specific client and properties in AdminServerNotifierAutoConfiguration

        @Bean
        @ConditionalOnMissingBean
        @ConfigurationProperties("spring.boot.admin.notify.mail.client1")
        public MailNotifierClient1 mailNotifierClient1(JavaMailSender mailSender, InstanceRepository repository) {
            return new MailNotifierClient1(mailSender, repository, mailNotifierTemplateEngine());
        }

Way 2

Here you may add modify doNotify method using the InstanceEvent, Instance (using Registration which consists of basic client details) domains, and set the recipients for mail into the field. You may change the conditions according to your requirements. These way you do not have to create other class/subclass.

@Override
    protected Mono<Void> doNotify(InstanceEvent event, Instance instance) {
        return Mono.fromRunnable(() -> {
            Context ctx = new Context();
            ctx.setVariables(additionalProperties);
            ctx.setVariable("baseUrl", this.baseUrl);
            ctx.setVariable("event", event);
            ctx.setVariable("instance", instance);
            ctx.setVariable("lastStatus", getLastStatus(event.getInstance()));

            try {
                MimeMessage mimeMessage = mailSender.createMimeMessage();
                MimeMessageHelper message = new MimeMessageHelper(mimeMessage, StandardCharsets.UTF_8.name());
                message.setText(getBody(ctx).replaceAll("\\s+\\n", "\n"), true);
                message.setSubject(getSubject(ctx));
                message.setCc(this.cc);
                message.setFrom(this.from);
                if(instance.getRegistration().getName().equals("client1")) {
                    message.setTo("client1.to");
                }
                mailSender.send(mimeMessage);
            }
            catch (MessagingException ex) {
                throw new RuntimeException("Error sending mail notification", ex);
            }
        });
    }

In case if we do not want to customize SBA then alternative way to achieve the requirement is to create the individual SBA for each client

Upvotes: 4

Related Questions