Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Custom Tenant Id in Request Logging Filter #172

Open
amrit9326 opened this issue Feb 16, 2024 · 4 comments
Open

Add Custom Tenant Id in Request Logging Filter #172

amrit9326 opened this issue Feb 16, 2024 · 4 comments

Comments

@amrit9326
Copy link

amrit9326 commented Feb 16, 2024

Hi Team,

We're trying to add custom tenantID in the request logging filter but couldn't find anyway of doing it , it's taking the tenantID from the subdomain by default which we don't want.

We tried adding the tenantID by extending the different filter classes and overriding it's method where we're modifying the request header by adding the tenantID but it's being overrided with the subdomain.

Please check the code below

@Configuration
public class LogConfig {

  /**
   * Configures and returns a Spring Boot FilterRegistration bean. The FilterRegistration bean holds
   * a SAP CF RequestLoggingFilter {@link RequestLoggingFilter}. Required for automatic generation
   * and propagation of correlation ID.
   *
   * @return {@link FilterRegistrationBean}
   */

  @Bean
  public FilterRegistrationBean<Filter> filterRegistrationBean() {

    FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>();

    RequestLoggingFilter requestLoggingFilter = new RequestLoggingFilter();
    registrationBean.setFilter(requestLoggingFilter);

    return registrationBean;
  }
}

@Component
public class TenantIDFilter extends AbstractLoggingFilter {

  @Override
  public void doFilter(
      ServletRequest request, ServletResponse response, FilterChain chain)
      throws IOException, ServletException {

    if (request instanceof HttpServletRequest req
        && response instanceof HttpServletResponse resp) {
      AddCustomHeaderWrapper requestWrapper = new AddCustomHeaderWrapper(req);
      requestWrapper.addHeader(Fields.TENANT_ID, SpringSecurityContext.getToken().getZoneId());
      this.doFilterRequest(requestWrapper, resp,
          chain);
    } else {
      chain.doFilter(request, response);
    }

  }
}

public class AddCustomHeaderWrapper extends HttpServletRequestWrapper {

  /**
   * construct a wrapper for this request
   *
   * @param request
   */

  private Map<String, String> headerMap = new HashMap<String, String>();

  public AddCustomHeaderWrapper(HttpServletRequest request) {
    super(request);
  }

  /**
   * add a header with given name and value
   *
   * @param name
   * @param value
   */
  public void addHeader(String name, String value) {
    headerMap.put(name, value);
  }

  @Override
  public String getHeader(String name) {
    String headerValue = super.getHeader(name);
    if (headerMap.containsKey(name)) {
      headerValue = headerMap.get(name);
    }
    return headerValue;
  }

  /**
   * get the Header names
   */
  @Override
  public Enumeration<String> getHeaderNames() {
    List<String> names = Collections.list(super.getHeaderNames());
    for (String name : headerMap.keySet()) {
      names.add(name);
    }
    return Collections.enumeration(names);
  }

  @Override
  public Enumeration<String> getHeaders(String name) {
    List<String> values = Collections.list(super.getHeaders(name));
    if (headerMap.containsKey(name)) {
      values.add(headerMap.get(name));
    }
    return Collections.enumeration(values);
  }

}

Please check the image below where tenantID is dvh and we're trying to replace it with some guid.
image

@amrit9326 amrit9326 changed the title Add Custom Tenant Id in Request Header Add Custom Tenant Id in Request Logging Filter Feb 16, 2024
@KarstenSchnitter
Copy link
Contributor

HI @amrit9326,

thanks for creating the issue. I need a little while to investigate the issue. Here are some background informations to the way the tenant id is added, that might help until then:

The tenant-id is taken from the HTTP header tenantid by the AddHttpHeadersToLogContextFilter.java Have a look at this constructor on how to take the header out of the list of propagated HTTP headers. There is also a section in the documentation on (custom header propagation)[https://github.com/SAP/cf-java-logging-support/wiki/Instrumenting-Servlets#custom-http-header-propagation]. You might also want to have a look at the ContextFieldSupplier interface documented here.

Best Regards,
Karsten

@amrit9326
Copy link
Author

Hi @KarstenSchnitter ,

Right now the tenantID is being mapped with the subdomain and I want to map it with the SpringSecurityContext.getToken().getZoneId() . Could you please let us know from which part of code implementation this can be done?
Also I tried debugging where subdomain is being splitted and mapped with tenantID but didn't find anything so far.

@KarstenSchnitter
Copy link
Contributor

Besides reading the tenantId from the HTTP header, there is no instrumentation for tenant ids in this library. That means, there is some other library, that is setting the tenant id on your behalf. I suggest to have a look at SAP Cloud ALM, if you use that. There is an old issue #122, that shows an implementation of a servlet filter, that adds a field to all log messages from the Spring Security context. Note, that it is not adding the tenant-id back as an HTTP header, but writes it directly to the MDC (wrapped by the LogContext). An alternative approach is to implement a ContextFieldSupplier like this:

package my.package;

public class TenantIdSupplier implements ContextFieldSupplier {
    @Override
    public Map<String, Object> get() {
        return new HashMap<>() {{
            put(Fields.TENANT_ID, SpringSecurityContext.getToken().getZoneId());
        }};
    }
}

You than need to add this class to your logback configuration as indicated here, e.g. a snippet for logback:

<appender name="STDOUT-JSON" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="com.sap.hcp.cf.logback.encoder.JsonEncoder">
        <contextFieldSupplier>my.package.TenantIdSupplier</contextFieldSupplier>
        <!-- all other configuraton -->
    </encoder>
</appender>

The difference between both approaches is, when and how often the tenant id is extracted from the SecurityContext:

  • Using the servlet filter this happens once when the request is received, storing the data in the MDC.
  • Using the ContextFieldSupplier, the tenant id is extracted for every log event at the time the event is created. This can also override the value from other sources in the generated JSON, but not in the event itself. The latter is important, if the logs are collected by some other frameworks, e.g. OpenTelemetry.

@KarstenSchnitter
Copy link
Contributor

@amrit9326: How are you progressing? Is this issue still relevant?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants