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.

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: