Add custom serchable fields in logs

I'm using Java APM client in my Spring Boot 2.2.7 application. It's a multi-tenant application, so I'd like to set (dynamically) tenant's id to the current transaction.

So far I configured the agent like this:

        #********************************************************************?
        # Configuration file for APM ElasticSearch
        # https://www.elastic.co/guide/en/apm/agent/java/current   /configuration.html#_option_reference
        #********************************************************************

       recording = true
    service_name = my-server
    application_packages = my.package

    server_urls = https://myurl.com
    secret_token = secretToken

    # PERFORMANCE VALUES
    transaction_sample_rate = 0.50
    transaction_max_spans = 500
    capture_headers = false
    metrics_interval = 60s
    enable_jaxrs_annotation_inheritance = false
    profiling_inferred_spans_enabled = false
    ignore_exceptions = javax.validation.ConstraintViolationException

and this is the Spring config class:

    @Profile({"dev", "stage", "prod"})
    @Component
    @Order(Ordered.HIGHEST_PRECEDENCE)
    @Log4j2
    public class ApmConfig {

        @Autowired
        private Environment environment;

        @PostConstruct
        public void init() {
            log.info("Starting APM Java agent...");
            Map<String, String> propertyMap = new HashMap<>();
            String activeProfile = System.getProperty("spring.profiles.active");
            try {
                final Properties props = new Properties();
                try (InputStream resourceStream = ElasticApmAttacher.class.getClassLoader().getResourceAsStream("elasticapm-" + activeProfile + ".properties")) {
                    if (resourceStream != null) {
                        props.load(resourceStream);
                        for (String propertyName : props.stringPropertyNames()) {
                            propertyMap.put(propertyName, props.getProperty(propertyName));
                        }
                    }
                } catch (IOException e) {
                    log.error("", e);
                }
                //Add environment value
                propertyMap.put("environment", activeProfile);
            } catch (Exception e) {
                log.error("", e);
            }
            ElasticApmAttacher.attach(propertyMap);
        }
    }

I didn't touch anything else in my application and I'd like to continue to do so. Am I wondering if is there a way to set additional information (like context, but searchable) and what's the best place where I can set them in the Spring flow.

I guess I should use labels (https://www.elastic.co/guide/en/apm/agent/java/current/public-api.html#api-span-add-tag) but I'd like to have some hint about it, expeciallly how to integrate them in a multi-tenant monolithic application where every request could be from a different tenant.

Thanks

This contradicts that:

You would need to modify you app code in order to add labels. If this is an option, find a place in your code that:

  1. is being executed during traced transactions
  2. your tenant ID is accessible at

Than add the agent's public API as a dependency of your app, and in your code, get the current transaction and add a label to it.

Hi, I ended up to add this code in my Filter (OncePerRequestFilter):

         private void decorateApmInformation(String tenantId, String userSid, String username) {
                //******************************************************************** 
                // ADDITIONAL INFORMATION FOR APM METRICS
                // https://www.elastic.co/guide/en/apm/agent/java/current/public-api.html
                //********************************************************************
                try {
                    Span currentSpan = ElasticApm.currentSpan();
                    if (currentSpan != null) {
                        currentSpan.addLabel("tenantId", tenantId);
                    }

                    Transaction currentTransaction = ElasticApm.currentTransaction();
                    if (currentTransaction != null) {
                        currentTransaction.addLabel("tenantId", tenantId);
                        currentTransaction.setUser(userSid, null, username);
                    }
                } catch (Exception e) {
                    logger.error("", e);
                }
            }

It works, I hope it doesn't hit against any best practice :wink:

Looks good, only make sure the currentSpan and currentTransaction are not the same if you want to avoid the possibility to adding twice to transactions.

1 Like

Thanks for your hint. How can I be sure there are not the same? Should I check their ID?

 try {
        Span currentSpan = ElasticApm.currentSpan();
        Transaction currentTransaction = ElasticApm.currentTransaction();
        if (currentTransaction != null) {
            currentTransaction.addLabel("tenantId", tenantId);
            currentTransaction.setUser(userSid, null, username);
        }

        if (currentSpan != null && !currentSpan.getId().equalsIgnoreCase(currentTransaction.getId())) {
            currentSpan.addLabel("tenantId", tenantId);
        }


    } catch (Exception e) {
        logger.error("", e);
    }

Thanks

Yes, ID equality is the way to go :+1:

1 Like