class MetricsHandler extends AbstractMetricsHandler {
    static final Logger logger = LoggerFactory.getLogger(MetricsHandler.class);

    // this is the indicator to start the reporter and construct the common tags. It cannot be static as
    // the currentPort and currentAddress are not available during the handler initialization.
    private boolean firstTime = true;
    static String MASK_KEY_SERVER_PASS= "serverPass";
    private volatile HttpHandler next;

    public MetricsHandler() {
        config = MetricsConfig.load();
        if(config.getIssuerRegex() != null) {
            pattern = Pattern.compile(config.getIssuerRegex());
        }
        ModuleRegistry.registerModule(MetricsConfig.CONFIG_NAME, MetricsHandler.class.getName(), Config.getNoneDecryptedInstance().getJsonMapConfigNoCache(MetricsConfig.CONFIG_NAME), List.of(MASK_KEY_SERVER_PASS));
        if(logger.isDebugEnabled()) logger.debug("MetricsHandler is constructed!");
    }

    @Override
    public void handleRequest(final HttpServerExchange exchange) throws Exception {<FILL_FUNCTION_BODY>}

    @Override
    public void register() {
        ModuleRegistry.registerModule(MetricsConfig.CONFIG_NAME, MetricsHandler.class.getName(), Config.getNoneDecryptedInstance().getJsonMapConfigNoCache(MetricsConfig.CONFIG_NAME), List.of(MASK_KEY_SERVER_PASS));
    }

    @Override
    public void reload() {
        config.reload();
        ModuleRegistry.registerModule(MetricsConfig.CONFIG_NAME, MetricsHandler.class.getName(), Config.getNoneDecryptedInstance().getJsonMapConfigNoCache(MetricsConfig.CONFIG_NAME), List.of(MASK_KEY_SERVER_PASS));
        if(logger.isInfoEnabled()) logger.info("MetricsHandler is reloaded.");
    }

    @Override
    public HttpHandler getNext() {
        return this.next;
    }

    @Override
    public MiddlewareHandler setNext(final HttpHandler next) {
        Handlers.handlerNotNull(next);
        this.next = next;
        return this;
    }

}

        if(logger.isDebugEnabled()) logger.debug("MetricsHandler.handleRequest starts.");
        if(firstTime) {
            commonTags.put("api", ServerConfig.getInstance().getServiceId());
            commonTags.put("env", ServerConfig.getInstance().getEnvironment());
            commonTags.put("addr", Server.currentAddress);
            commonTags.put("port", "" + (ServerConfig.getInstance().isEnableHttps() ? Server.currentHttpsPort : Server.currentHttpPort));
            InetAddress inetAddress = Util.getInetAddress();
            commonTags.put("host", inetAddress == null ? "unknown" : inetAddress.getHostName()); // will be container id if in docker.
            if(logger.isDebugEnabled()) {
                logger.debug(commonTags.toString());
            }
            try {
                TimeSeriesDbSender influxDb =
                        new InfluxDbHttpSender(config.getServerProtocol(), config.getServerHost(), config.getServerPort(),
                                config.getServerName(), config.getServerUser(), config.getServerPass());
                InfluxDbReporter reporter = InfluxDbReporter
                        .forRegistry(registry)
                        .convertRatesTo(TimeUnit.SECONDS)
                        .convertDurationsTo(TimeUnit.MILLISECONDS)
                        .filter(MetricFilter.ALL)
                        .build(influxDb);
                reporter.start(config.getReportInMinutes(), TimeUnit.MINUTES);
                if (config.enableJVMMonitor) {
                    createJVMMetricsReporter(influxDb);
                }

                logger.info("metrics is enabled and reporter is started");
            } catch (Exception e) {
                // if there are any exception, chances are influxdb is not available.
                logger.error("metrics is failed to connect to the influxdb", e);
            }
            // reset the flag so that this block will only be called once.
            firstTime = false;
        }

        long startTime = Clock.defaultClock().getTick();
        exchange.addExchangeCompleteListener((exchange1, nextListener) -> {
            try {
                Map<String, Object> auditInfo = exchange1.getAttachment(AttachmentConstants.AUDIT_INFO);
                if(auditInfo != null && !auditInfo.isEmpty()) {
                    Map<String, String> tags = new HashMap<>();
                    tags.put("endpoint", (String)auditInfo.get(Constants.ENDPOINT_STRING));
                    tags.put("clientId", auditInfo.get(Constants.CLIENT_ID_STRING) != null ? (String)auditInfo.get(Constants.CLIENT_ID_STRING) : "unknown");
                    tags.put("scopeClientId", auditInfo.get(Constants.SCOPE_CLIENT_ID_STRING) != null ? (String)auditInfo.get(Constants.SCOPE_CLIENT_ID_STRING) : "unknown");
                    tags.put("callerId", auditInfo.get(Constants.CALLER_ID_STRING) != null ? (String)auditInfo.get(Constants.CALLER_ID_STRING) : "unknown");
                    long time = Clock.defaultClock().getTick() - startTime;
                    MetricName metricName = new MetricName("response_time");
                    metricName = metricName.tagged(commonTags);
                    metricName = metricName.tagged(tags);
                    registry.getOrAdd(metricName, MetricRegistry.MetricBuilder.TIMERS).update(time, TimeUnit.NANOSECONDS);
                    incCounterForStatusCode(exchange1.getStatusCode(), commonTags, tags);
                } else {
                    // when we reach here, it will be in light-gateway so no specification is loaded on the server and also the security verification is failed.
                    // we need to come up with the endpoint at last to ensure we have some meaningful metrics info populated.
                    logger.error("auditInfo is null or empty. Please move the path prefix handler to the top of the handler chain after metrics.");
                }
            } catch (Throwable e) {
                logger.error("ExchangeListener throwable",  e);
            } finally {
                nextListener.proceed();
            }
        });
        if(logger.isDebugEnabled()) logger.debug("MetricsHandler.handleRequest ends.");
        Handler.next(exchange, next);
    
<methods>public void <init>() ,public void createJVMMetricsReporter(com.networknt.metrics.TimeSeriesDbSender) ,public void incCounterForStatusCode(int, Map<java.lang.String,java.lang.String>, Map<java.lang.String,java.lang.String>) ,public void injectMetrics(HttpServerExchange, long, java.lang.String, java.lang.String) ,public boolean isEnabled() <variables>public Map<java.lang.String,java.lang.String> commonTags,public static com.networknt.metrics.MetricsConfig config,static final Logger logger,static java.util.regex.Pattern pattern,public static final io.dropwizard.metrics.MetricRegistry registry