All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.camel.impl.engine.DefaultTracer Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.camel.impl.engine;

import java.util.Map;
import java.util.Objects;
import java.util.StringJoiner;

import org.apache.camel.CamelContext;
import org.apache.camel.CamelContextAware;
import org.apache.camel.Endpoint;
import org.apache.camel.Exchange;
import org.apache.camel.NamedNode;
import org.apache.camel.NamedRoute;
import org.apache.camel.spi.EndpointServiceLocation;
import org.apache.camel.spi.ExchangeFormatter;
import org.apache.camel.spi.Tracer;
import org.apache.camel.support.CamelContextHelper;
import org.apache.camel.support.PatternHelper;
import org.apache.camel.support.builder.ExpressionBuilder;
import org.apache.camel.support.processor.DefaultExchangeFormatter;
import org.apache.camel.support.service.ServiceSupport;
import org.apache.camel.util.StringHelper;
import org.apache.camel.util.TimeUtils;
import org.apache.camel.util.URISupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static org.apache.camel.support.LoggerHelper.getLineNumberLoggerName;

/**
 * Default {@link Tracer} implementation that will log traced messages to the logger named
 * org.apache.camel.Tracing.
 */
public class DefaultTracer extends ServiceSupport implements CamelContextAware, Tracer {

    private static final String TRACING_OUTPUT = "%-4.4s [%-12.12s] [%-33.33s]";

    // use a fixed logger name so easy to spot
    private static final Logger LOG = LoggerFactory.getLogger("org.apache.camel.Tracing");

    private String tracingFormat = TRACING_OUTPUT;
    private CamelContext camelContext;
    private boolean enabled = true;
    private boolean standby;
    private boolean traceRests;
    private boolean traceTemplates;
    private long traceCounter;

    private ExchangeFormatter exchangeFormatter;
    private String tracePattern;
    private transient String[] patterns;
    private boolean traceBeforeAndAfterRoute = true;

    public DefaultTracer() {
        DefaultExchangeFormatter formatter = new DefaultExchangeFormatter();
        formatter.setShowExchangeId(true);
        formatter.setShowExchangePattern(false);
        formatter.setMultiline(false);
        formatter.setShowHeaders(true);
        formatter.setStyle(DefaultExchangeFormatter.OutputStyle.Default);
        setExchangeFormatter(formatter);
    }

    @Override
    public CamelContext getCamelContext() {
        return camelContext;
    }

    @Override
    public void setCamelContext(CamelContext camelContext) {
        this.camelContext = camelContext;
    }

    @Override
    public void traceBeforeNode(NamedNode node, Exchange exchange) {
        if (shouldTrace(node)) {
            traceCounter++;
            String routeId = ExpressionBuilder.routeIdExpression().evaluate(exchange, String.class);

            // we need to avoid leak the sensible information here
            // the sanitizeUri takes a very long time for very long string and the format cuts this to
            // 33 characters, anyway. Cut this to 50 characters. This will give enough space for removing
            // characters in the sanitizeUri method and will be reasonably fast
            String label = URISupport.sanitizeUri(StringHelper.limitLength(node.getLabel(), 50));

            StringBuilder sb = new StringBuilder(512);
            sb.append(String.format(tracingFormat, "   ", routeId, label));
            sb.append(" ");
            String data = exchangeFormatter.format(exchange);
            sb.append(data);
            String out = sb.toString();
            dumpTrace(out, node);
        }
    }

    @Override
    public void traceAfterNode(NamedNode node, Exchange exchange) {
        // noop
    }

    @Override
    public void traceSentNode(NamedNode node, Exchange exchange, Endpoint endpoint, long elapsed) {
        if (!traceBeforeAndAfterRoute) {
            return;
        }

        // skip non-remote endpoints
        if (!endpoint.isRemote()) {
            return;
        }

        if (shouldTrace(node)) {
            String routeId = ExpressionBuilder.routeIdExpression().evaluate(exchange, String.class);

            StringBuilder sb = new StringBuilder(128);
            sb.append(String.format(tracingFormat, "   ", routeId, ""));
            sb.append(" ");

            StringJoiner sj = new StringJoiner(", ");
            sj.add("url=" + endpoint);
            if (endpoint instanceof EndpointServiceLocation esl && esl.getServiceUrl() != null) {
                // enrich with service location
                sj.add("service=" + esl.getServiceUrl());
                String protocol = esl.getServiceProtocol();
                if (protocol != null) {
                    sj.add("protocol=" + protocol);
                }
                Map map = esl.getServiceMetadata();
                if (map != null) {
                    map.forEach((k, v) -> sj.add(k + "=" + v));
                }
            }

            boolean failed = exchange.isFailed();
            String data = "Sent " + (failed ? "failed" : "success") + " took " + TimeUtils.printDuration(elapsed, true);
            data += " (" + sj + ")";
            sb.append(data);
            String out = sb.toString();
            dumpTrace(out, node);
        }
    }

    @Override
    public void traceBeforeRoute(NamedRoute route, Exchange exchange) {
        if (!traceBeforeAndAfterRoute) {
            return;
        }

        // we need to avoid leak the sensible information here
        // the sanitizeUri takes a very long time for very long string and the format cuts this to
        // 33 characters, anyway. Cut this to 50 characters. This will give enough space for removing
        // characters in the sanitizeUri method and will be reasonably fast
        String uri = route.getEndpointUrl();
        String label = "from[" + URISupport.sanitizeUri(StringHelper.limitLength(uri, 50) + "]");

        // the arrow has a * if its a new exchange that is starting
        boolean original = route.getRouteId().equals(exchange.getFromRouteId());
        String arrow = original ? "*-->" : "--->";

        StringBuilder sb = new StringBuilder();
        sb.append(String.format(tracingFormat, arrow, route.getRouteId(), label));
        sb.append(" ");
        String data = exchangeFormatter.format(exchange);
        String out = sb + data;
        dumpTrace(out, route);

        // enrich with endpoint service location on incoming request
        if (original) {
            Endpoint endpoint = exchange.getFromEndpoint();
            if (endpoint instanceof EndpointServiceLocation esl && esl.getServiceUrl() != null) {
                // enrich with service location
                StringJoiner sj = new StringJoiner(", ");
                sj.add("url=" + endpoint);
                sj.add("service=" + esl.getServiceUrl());
                String protocol = esl.getServiceProtocol();
                if (protocol != null) {
                    sj.add("protocol=" + protocol);
                }
                Map map = esl.getServiceMetadata();
                if (map != null) {
                    map.forEach((k, v) -> sj.add(k + "=" + v));
                }
                data = "Received (" + sj + ")";

                sb = new StringBuilder();
                sb.append(String.format(tracingFormat, "", route.getRouteId(), ""));
                sb.append(" ");

                out = sb + data;
                dumpTrace(out, route);
            }
        }
    }

    @Override
    public void traceAfterRoute(NamedRoute route, Exchange exchange) {
        if (!traceBeforeAndAfterRoute) {
            return;
        }

        // we need to avoid leak the sensible information here
        // the sanitizeUri takes a very long time for very long string and the format cuts this to
        // 33 characters, anyway. Cut this to 50 characters. This will give enough space for removing
        // characters in the sanitizeUri method and will be reasonably fast
        String uri = route.getEndpointUrl();
        String label = "from[" + URISupport.sanitizeUri(StringHelper.limitLength(uri, 50) + "]");

        // the arrow has a * if its an exchange that is done
        boolean original = route.getRouteId().equals(exchange.getFromRouteId());
        String arrow = original ? "*<--" : "<---";

        StringBuilder sb = new StringBuilder();
        sb.append(String.format(tracingFormat, arrow, route.getRouteId(), label));
        sb.append(" ");
        String data = exchangeFormatter.format(exchange);
        sb.append(data);
        String out = sb.toString();
        dumpTrace(out, route);
    }

    @Override
    public boolean shouldTrace(NamedNode definition) {
        if (!enabled) {
            return false;
        }

        boolean pattern = true;

        if (patterns != null) {
            pattern = shouldTracePattern(definition);
        }

        if (LOG.isTraceEnabled()) {
            LOG.trace("Should trace evaluated {} -> pattern: {}", definition.getId(), pattern);
        }
        return pattern;
    }

    @Override
    public long getTraceCounter() {
        return traceCounter;
    }

    @Override
    public void resetTraceCounter() {
        traceCounter = 0;
    }

    @Override
    public boolean isEnabled() {
        return enabled;
    }

    @Override
    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    @Override
    public boolean isStandby() {
        return standby;
    }

    @Override
    public void setStandby(boolean standby) {
        this.standby = standby;
    }

    @Override
    public boolean isTraceRests() {
        return traceRests;
    }

    @Override
    public void setTraceRests(boolean traceRests) {
        this.traceRests = traceRests;
    }

    @Override
    public boolean isTraceTemplates() {
        return traceTemplates;
    }

    @Override
    public void setTraceTemplates(boolean traceTemplates) {
        this.traceTemplates = traceTemplates;
    }

    @Override
    public String getTracePattern() {
        return tracePattern;
    }

    @Override
    public void setTracePattern(String tracePattern) {
        this.tracePattern = tracePattern;
        if (tracePattern != null) {
            // the pattern can have multiple nodes separated by comma
            this.patterns = tracePattern.split(",");
        } else {
            this.patterns = null;
        }
    }

    @Override
    public boolean isTraceBeforeAndAfterRoute() {
        return traceBeforeAndAfterRoute;
    }

    @Override
    public void setTraceBeforeAndAfterRoute(boolean traceBeforeAndAfterRoute) {
        this.traceBeforeAndAfterRoute = traceBeforeAndAfterRoute;
    }

    @Override
    public ExchangeFormatter getExchangeFormatter() {
        return exchangeFormatter;
    }

    @Override
    public void setExchangeFormatter(ExchangeFormatter exchangeFormatter) {
        this.exchangeFormatter = exchangeFormatter;
    }

    protected void dumpTrace(String out, Object node) {
        String name = getLineNumberLoggerName(node);
        if (name != null) {
            Logger log = LoggerFactory.getLogger(name);
            log.info(out);
        } else {
            LOG.info(out);
        }
    }

    protected boolean shouldTracePattern(NamedNode definition) {
        for (String pattern : patterns) {
            // match either route id, or node id
            String id = definition.getId();
            // use matchPattern method from endpoint helper that has a good matcher we use in Camel
            if (PatternHelper.matchPattern(id, pattern)) {
                return true;
            }
            String routeId = CamelContextHelper.getRouteId(definition);
            if (routeId != null && !Objects.equals(routeId, id)) {
                if (PatternHelper.matchPattern(routeId, pattern)) {
                    return true;
                }
            }
        }
        // not matched the pattern
        return false;
    }

    @Override
    protected void doStart() throws Exception {
        if (getCamelContext().getTracingLoggingFormat() != null) {
            tracingFormat = getCamelContext().getTracingLoggingFormat();
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy