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

org.apache.camel.impl.console.RouteDevConsole Maven / Gradle / Ivy

The newest version!
/*
 * 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.console;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;

import org.apache.camel.CamelContext;
import org.apache.camel.Exchange;
import org.apache.camel.Route;
import org.apache.camel.api.management.ManagedCamelContext;
import org.apache.camel.api.management.mbean.ManagedProcessorMBean;
import org.apache.camel.api.management.mbean.ManagedRouteMBean;
import org.apache.camel.spi.annotations.DevConsole;
import org.apache.camel.support.ExceptionHelper;
import org.apache.camel.support.LoggerHelper;
import org.apache.camel.support.PatternHelper;
import org.apache.camel.support.console.AbstractDevConsole;
import org.apache.camel.util.StringHelper;
import org.apache.camel.util.TimeUtils;
import org.apache.camel.util.json.JsonArray;
import org.apache.camel.util.json.JsonObject;
import org.apache.camel.util.json.Jsoner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@DevConsole(name = "route", description = "Route information")
public class RouteDevConsole extends AbstractDevConsole {

    private static final Logger LOG = LoggerFactory.getLogger(RouteDevConsole.class);

    /**
     * Filters the routes matching by route id, route uri, and source location
     */
    public static final String FILTER = "filter";

    /**
     * Limits the number of entries displayed
     */
    public static final String LIMIT = "limit";

    /**
     * Whether to include processors
     */
    public static final String PROCESSORS = "processors";

    /**
     * Action to perform such as start,stop,suspend,resume on one or more routes
     */
    public static final String ACTION = "action";

    public RouteDevConsole() {
        super("camel", "route", "Route", "Route information");
    }

    @Override
    protected String doCallText(Map options) {
        String action = (String) options.get(ACTION);
        String filter = (String) options.get(FILTER);
        if (action != null) {
            doAction(getCamelContext(), action, filter);
            return "";
        }

        final boolean processors = "true".equals(options.getOrDefault(PROCESSORS, "false"));
        final StringBuilder sb = new StringBuilder();
        Function task = mrb -> {
            if (!sb.isEmpty()) {
                sb.append("\n");
            }
            sb.append(String.format("    Id: %s", mrb.getRouteId()));
            if (mrb.getNodePrefixId() != null) {
                sb.append(String.format("    Node Prefix Id: %s", mrb.getNodePrefixId()));
            }
            sb.append(String.format("\n    From: %s", mrb.getEndpointUri()));
            sb.append(String.format("\n    Remote: %s", mrb.isRemoteEndpoint()));
            if (mrb.getSourceLocation() != null) {
                sb.append(String.format("\n    Source: %s", mrb.getSourceLocation()));
            }
            sb.append(String.format("\n    State: %s", mrb.getState()));
            if (mrb.getLastError() != null) {
                String phase = StringHelper.capitalize(mrb.getLastError().getPhase().name().toLowerCase());
                String ago = TimeUtils.printSince(mrb.getLastError().getDate().getTime());
                sb.append(String.format("\n    Error Ago: %s", ago));
                sb.append(String.format("\n    Error Phase: %s", phase));
                Throwable cause = mrb.getLastError().getException();
                if (cause != null) {
                    sb.append(String.format("\n    Error Message: %s", cause.getMessage()));

                    final String stackTrace = ExceptionHelper.stackTraceToString(cause);
                    sb.append("\n\n");
                    sb.append(stackTrace);
                    sb.append("\n\n");
                }
            }
            sb.append(String.format("\n    Uptime: %s", mrb.getUptime()));
            String coverage = calculateRouteCoverage(mrb, true);
            if (coverage != null) {
                sb.append(String.format("\n    Coverage: %s", coverage));
            }
            String load1 = getLoad1(mrb);
            String load5 = getLoad5(mrb);
            String load15 = getLoad15(mrb);
            if (!load1.isEmpty() || !load5.isEmpty() || !load15.isEmpty()) {
                sb.append(String.format("\n    Load Average: %s %s %s\n", load1, load5, load15));
            }
            String thp = getThroughput(mrb);
            if (!thp.isEmpty()) {
                sb.append(String.format("\n    Messages/Sec: %s", thp));
            }
            sb.append(String.format("\n    Total: %s", mrb.getExchangesTotal()));
            sb.append(String.format("\n    Failed: %s", mrb.getExchangesFailed()));
            sb.append(String.format("\n    Inflight: %s", mrb.getExchangesInflight()));
            long idle = mrb.getIdleSince();
            if (idle > 0) {
                sb.append(String.format("\n    Idle Since: %s", TimeUtils.printDuration(idle)));
            } else {
                sb.append(String.format("\n    Idle Since: %s", ""));
            }
            sb.append(String.format("\n    Mean Time: %s", TimeUtils.printDuration(mrb.getMeanProcessingTime(), true)));
            sb.append(String.format("\n    Max Time: %s", TimeUtils.printDuration(mrb.getMaxProcessingTime(), true)));
            sb.append(String.format("\n    Min Time: %s", TimeUtils.printDuration(mrb.getMinProcessingTime(), true)));
            if (mrb.getExchangesTotal() > 0) {
                sb.append(String.format("\n    Last Time: %s", TimeUtils.printDuration(mrb.getLastProcessingTime(), true)));
                sb.append(String.format("\n    Delta Time: %s", TimeUtils.printDuration(mrb.getDeltaProcessingTime(), true)));
            }
            Date last = mrb.getLastExchangeCreatedTimestamp();
            if (last != null) {
                String ago = TimeUtils.printSince(last.getTime());
                sb.append(String.format("\n    Since Last Started: %s", ago));
            }
            last = mrb.getLastExchangeCompletedTimestamp();
            if (last != null) {
                String ago = TimeUtils.printSince(last.getTime());
                sb.append(String.format("\n    Since Last Completed: %s", ago));
            }
            last = mrb.getLastExchangeFailureTimestamp();
            if (last != null) {
                String ago = TimeUtils.printSince(last.getTime());
                sb.append(String.format("\n    Since Last Failed: %s", ago));
            }
            if (processors) {
                includeProcessorsText(mrb, sb);
            }
            sb.append("\n");
            return null;
        };
        doCall(options, task);
        return sb.toString();
    }

    private void includeProcessorsText(ManagedRouteMBean mrb, StringBuilder sb) {
        ManagedCamelContext mcc = getCamelContext().getCamelContextExtension().getContextPlugin(ManagedCamelContext.class);

        Collection ids;
        try {
            ids = mrb.processorIds();
        } catch (Exception e) {
            return;
        }

        // sort by index
        List mps = new ArrayList<>();
        for (String id : ids) {
            ManagedProcessorMBean mp = mcc.getManagedProcessor(id);
            if (mp != null) {
                mps.add(mp);
            }
        }
        // sort processors by index
        mps.sort(Comparator.comparingInt(ManagedProcessorMBean::getIndex));

        for (ManagedProcessorMBean mp : mps) {
            sb.append("\n");
            sb.append(String.format("\n        Id: %s", mp.getProcessorId()));
            if (mp.getNodePrefixId() != null) {
                sb.append(String.format("\n        Node Prefix Id: %s", mp.getNodePrefixId()));
            }
            sb.append(String.format("\n        Processor: %s", mp.getProcessorName()));
            sb.append(String.format("\n        Level: %d", mp.getLevel()));
            if (mp.getSourceLocation() != null) {
                String loc = mp.getSourceLocation();
                if (mp.getSourceLineNumber() != null) {
                    loc += ":" + mp.getSourceLineNumber();
                }
                sb.append(String.format("\n        Source: %s", loc));
            }
            sb.append(String.format("\n        Total: %s", mp.getExchangesTotal()));
            sb.append(String.format("\n        Failed: %s", mp.getExchangesFailed()));
            sb.append(String.format("\n        Inflight: %s", mp.getExchangesInflight()));
            long idle = mp.getIdleSince();
            if (idle > 0) {
                sb.append(String.format("\n        Idle Since: %s", TimeUtils.printDuration(idle)));
            } else {
                sb.append(String.format("\n        Idle Since: %s", ""));
            }
            sb.append(String.format("\n        Mean Time: %s", TimeUtils.printDuration(mp.getMeanProcessingTime(), true)));
            sb.append(String.format("\n        Max Time: %s", TimeUtils.printDuration(mp.getMaxProcessingTime(), true)));
            sb.append(String.format("\n        Min Time: %s", TimeUtils.printDuration(mp.getMinProcessingTime(), true)));
            if (mp.getExchangesTotal() > 0) {
                sb.append(String.format("\n        Last Time: %s", TimeUtils.printDuration(mp.getLastProcessingTime(), true)));
                sb.append(
                        String.format("\n        Delta Time: %s", TimeUtils.printDuration(mp.getDeltaProcessingTime(), true)));
            }
            Date last = mp.getLastExchangeCompletedTimestamp();
            if (last != null) {
                String ago = TimeUtils.printSince(last.getTime());
                sb.append(String.format("\n        Since Last Completed: %s", ago));
            }
            last = mp.getLastExchangeFailureTimestamp();
            if (last != null) {
                String ago = TimeUtils.printSince(last.getTime());
                sb.append(String.format("\n        Since Last Failed: %s", ago));
            }
        }
    }

    @Override
    protected JsonObject doCallJson(Map options) {
        String action = (String) options.get(ACTION);
        String filter = (String) options.get(FILTER);
        if (action != null) {
            doAction(getCamelContext(), action, filter);
            return new JsonObject();
        }

        final boolean processors = "true".equals(options.getOrDefault(PROCESSORS, "false"));
        final JsonObject root = new JsonObject();
        final List list = new ArrayList<>();
        Function task = mrb -> {
            JsonObject jo = new JsonObject();
            list.add(jo);
            jo.put("routeId", mrb.getRouteId());
            if (mrb.getNodePrefixId() != null) {
                jo.put("nodePrefixId", mrb.getNodePrefixId());
            }
            jo.put("from", mrb.getEndpointUri());
            jo.put("remote", mrb.isRemoteEndpoint());
            if (mrb.getSourceLocation() != null) {
                jo.put("source", mrb.getSourceLocation());
            }
            jo.put("state", mrb.getState());
            jo.put("uptime", mrb.getUptime());
            if (mrb.getLastError() != null) {
                String phase = StringHelper.capitalize(mrb.getLastError().getPhase().name().toLowerCase());
                JsonObject eo = new JsonObject();
                eo.put("phase", phase);
                eo.put("timestamp", mrb.getLastError().getDate().getTime());
                Throwable cause = mrb.getLastError().getException();
                if (cause != null) {
                    eo.put("message", cause.getMessage());
                    JsonArray arr2 = new JsonArray();
                    final String trace = ExceptionHelper.stackTraceToString(cause);
                    eo.put("stackTrace", arr2);
                    Collections.addAll(arr2, trace.split("\n"));
                }
                jo.put("lastError", eo);
            }
            JsonObject stats = new JsonObject();
            String coverage = calculateRouteCoverage(mrb, false);
            if (coverage != null) {
                stats.put("coverage", coverage);
            }
            String load1 = getLoad1(mrb);
            String load5 = getLoad5(mrb);
            String load15 = getLoad15(mrb);
            if (!load1.isEmpty() || !load5.isEmpty() || !load15.isEmpty()) {
                stats.put("load01", load1);
                stats.put("load05", load5);
                stats.put("load15", load15);
            }
            String thp = getThroughput(mrb);
            if (!thp.isEmpty()) {
                stats.put("exchangesThroughput", thp);
            }
            stats.put("idleSince", mrb.getIdleSince());
            stats.put("exchangesTotal", mrb.getExchangesTotal());
            stats.put("exchangesFailed", mrb.getExchangesFailed());
            stats.put("exchangesInflight", mrb.getExchangesInflight());
            stats.put("meanProcessingTime", mrb.getMeanProcessingTime());
            stats.put("maxProcessingTime", mrb.getMaxProcessingTime());
            stats.put("minProcessingTime", mrb.getMinProcessingTime());
            if (mrb.getExchangesTotal() > 0) {
                stats.put("lastProcessingTime", mrb.getLastProcessingTime());
                stats.put("deltaProcessingTime", mrb.getDeltaProcessingTime());
            }
            Date last = mrb.getLastExchangeCreatedTimestamp();
            if (last != null) {
                stats.put("lastCreatedExchangeTimestamp", last.getTime());
            }
            last = mrb.getLastExchangeCompletedTimestamp();
            if (last != null) {
                stats.put("lastCompletedExchangeTimestamp", last.getTime());
            }
            last = mrb.getLastExchangeFailureTimestamp();
            if (last != null) {
                stats.put("lastFailedExchangeTimestamp", last.getTime());
            }
            jo.put("statistics", stats);
            if (processors) {
                JsonArray arr = new JsonArray();
                jo.put("processors", arr);
                includeProcessorsJson(mrb, arr);
            }
            return null;
        };
        doCall(options, task);
        root.put("routes", list);
        return root;
    }

    private void includeProcessorsJson(ManagedRouteMBean mrb, JsonArray arr) {
        ManagedCamelContext mcc = getCamelContext().getCamelContextExtension().getContextPlugin(ManagedCamelContext.class);

        Collection ids;
        try {
            ids = mrb.processorIds();
        } catch (Exception e) {
            return;
        }

        List mps = ids.stream().map(mcc::getManagedProcessor)
                .filter(Objects::nonNull)
                // sort processors by index
                .sorted(Comparator.comparingInt(ManagedProcessorMBean::getIndex))
                .toList();

        for (ManagedProcessorMBean mp : mps) {
            JsonObject jo = new JsonObject();
            arr.add(jo);

            jo.put("id", mp.getProcessorId());
            if (mp.getNodePrefixId() != null) {
                jo.put("nodePrefixId", mp.getNodePrefixId());
            }
            if (mp.getSourceLocation() != null) {
                String loc = mp.getSourceLocation();
                if (mp.getSourceLineNumber() != null) {
                    loc += ":" + mp.getSourceLineNumber();
                }
                jo.put("source", loc);
            }
            String line = ConsoleHelper.loadSourceLine(getCamelContext(), mp.getSourceLocation(), mp.getSourceLineNumber());
            if (line != null) {
                JsonArray ca = new JsonArray();
                jo.put("code", ca);
                JsonObject c = new JsonObject();
                if (mp.getSourceLineNumber() != null) {
                    c.put("line", mp.getSourceLineNumber());
                }
                c.put("code", Jsoner.escape(line));
                c.put("match", true);
                ca.add(c);
            }
            jo.put("processor", mp.getProcessorName());
            jo.put("level", mp.getLevel());
            final JsonObject stats = getStatsObject(mp);
            jo.put("statistics", stats);
        }
    }

    private static JsonObject getStatsObject(ManagedProcessorMBean mp) {
        JsonObject stats = new JsonObject();
        stats.put("idleSince", mp.getIdleSince());
        stats.put("exchangesTotal", mp.getExchangesTotal());
        stats.put("exchangesFailed", mp.getExchangesFailed());
        stats.put("exchangesInflight", mp.getExchangesInflight());
        stats.put("meanProcessingTime", mp.getMeanProcessingTime());
        stats.put("maxProcessingTime", mp.getMaxProcessingTime());
        stats.put("minProcessingTime", mp.getMinProcessingTime());
        if (mp.getExchangesTotal() > 0) {
            stats.put("lastProcessingTime", mp.getLastProcessingTime());
            stats.put("deltaProcessingTime", mp.getDeltaProcessingTime());
        }
        Date last = mp.getLastExchangeCreatedTimestamp();
        if (last != null) {
            stats.put("lastCreatedExchangeTimestamp", last.getTime());
        }
        last = mp.getLastExchangeCompletedTimestamp();
        if (last != null) {
            stats.put("lastCompletedExchangeTimestamp", last.getTime());
        }
        last = mp.getLastExchangeFailureTimestamp();
        if (last != null) {
            stats.put("lastFailedExchangeTimestamp", last.getTime());
        }
        return stats;
    }

    protected void doCall(Map options, Function task) {
        String path = (String) options.get(Exchange.HTTP_PATH);
        String subPath = path != null ? StringHelper.after(path, "/") : null;
        String filter = (String) options.get(FILTER);
        String limit = (String) options.get(LIMIT);
        final int max = limit == null ? Integer.MAX_VALUE : Integer.parseInt(limit);

        ManagedCamelContext mcc = getCamelContext().getCamelContextExtension().getContextPlugin(ManagedCamelContext.class);
        if (mcc != null) {
            List routes = getCamelContext().getRoutes();
            routes.sort((o1, o2) -> o1.getRouteId().compareToIgnoreCase(o2.getRouteId()));
            routes.stream()
                    .map(route -> mcc.getManagedRoute(route.getRouteId()))
                    .filter(Objects::nonNull)
                    .filter(r -> accept(r, filter))
                    .filter(r -> accept(r, subPath))
                    .sorted(RouteDevConsole::sort)
                    .limit(max)
                    .forEach(task::apply);
        }
    }

    private static boolean accept(ManagedRouteMBean mrb, String filter) {
        if (filter == null || filter.isBlank()) {
            return true;
        }

        String onlyName = LoggerHelper.sourceNameOnly(mrb.getSourceLocation());
        return PatternHelper.matchPattern(mrb.getRouteId(), filter)
                || PatternHelper.matchPattern(mrb.getEndpointUri(), filter)
                || PatternHelper.matchPattern(mrb.getSourceLocationShort(), filter)
                || PatternHelper.matchPattern(onlyName, filter);
    }

    private static int sort(ManagedRouteMBean o1, ManagedRouteMBean o2) {
        return o1.getRouteId().compareToIgnoreCase(o2.getRouteId());
    }

    private String getLoad1(ManagedRouteMBean mrb) {
        String s = mrb.getLoad01();
        // lets use dot as separator
        s = s.replace(',', '.');
        return s;
    }

    private String getLoad5(ManagedRouteMBean mrb) {
        String s = mrb.getLoad05();
        // lets use dot as separator
        s = s.replace(',', '.');
        return s;
    }

    private String getLoad15(ManagedRouteMBean mrb) {
        String s = mrb.getLoad15();
        // lets use dot as separator
        s = s.replace(',', '.');
        return s;
    }

    private String getThroughput(ManagedRouteMBean mrb) {
        String s = mrb.getThroughput();
        // lets use dot as separator
        s = s.replace(',', '.');
        return s;
    }

    private String calculateRouteCoverage(ManagedRouteMBean mrb, boolean percent) {
        ManagedCamelContext mcc = getCamelContext().getCamelContextExtension().getContextPlugin(ManagedCamelContext.class);

        Collection ids;
        try {
            ids = mrb.processorIds();
        } catch (Exception e) {
            return null;
        }

        int total = ids.size();
        int covered = 0;

        for (String id : ids) {
            ManagedProcessorMBean mp = mcc.getManagedProcessor(id);
            if (mp != null) {
                if (mp.getExchangesTotal() > 0) {
                    covered++;
                }
            }
        }

        if (percent) {
            double p;
            if (total > 0) {
                p = ((double) covered / total) * 100;
            } else {
                p = 0;
            }
            String f = String.format("%.0f", p);
            return covered + "/" + total + " (" + f + "%)";
        } else {
            return covered + "/" + total;
        }
    }

    protected void doAction(CamelContext camelContext, String command, String filter) {
        if (filter == null) {
            filter = "*";
        }
        String[] patterns = filter.split(",");
        // find matching IDs
        List ids = camelContext.getRoutes()
                .stream().map(Route::getRouteId)
                .filter(routeId -> {
                    for (String p : patterns) {
                        if (PatternHelper.matchPattern(routeId, p)) {
                            return true;
                        }
                    }
                    return false;
                })
                .toList();
        for (String id : ids) {
            try {
                if ("start".equals(command)) {
                    if ("*".equals(id)) {
                        camelContext.getRouteController().startAllRoutes();
                    } else {
                        camelContext.getRouteController().startRoute(id);
                    }
                } else if ("stop".equals(command)) {
                    if ("*".equals(id)) {
                        camelContext.getRouteController().stopAllRoutes();
                    } else {
                        camelContext.getRouteController().stopRoute(id);
                    }
                } else if ("suspend".equals(command)) {
                    if ("*".equals(id)) {
                        camelContext.suspend();
                    } else {
                        camelContext.getRouteController().suspendRoute(id);
                    }
                } else if ("resume".equals(command)) {
                    if ("*".equals(id)) {
                        camelContext.resume();
                    } else {
                        camelContext.getRouteController().resumeRoute(id);
                    }
                }
            } catch (Exception e) {
                LOG.warn("Error {} route: {} due to: {}. This exception is ignored.", command, id, e.getMessage(), e);
            }
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy