| /* |
| * 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.geode.internal.util; |
| |
| import org.apache.geode.annotations.Immutable; |
| import org.apache.geode.annotations.internal.MutableForTesting; |
| import org.apache.geode.distributed.internal.DistributionConfig; |
| import org.apache.geode.internal.cache.EventID; |
| |
| /** |
| * Breadcrumbs establishes traces in thread names that are useful in figuring out what is going on |
| * in a distributed system given only stack traces. |
| * |
| * @since GemFire 20 May 2014 |
| * |
| */ |
| public class Breadcrumbs { |
| |
| private static final ThreadLocal<EventID> EventIDs = new ThreadLocal<EventID>(); |
| |
| @MutableForTesting |
| public static boolean ENABLED = |
| Boolean.getBoolean(DistributionConfig.GEMFIRE_PREFIX + "enable-breadcrumbs"); |
| |
| |
| /** delimiter for crumb numbers */ |
| static final String CrumbDelimiter = "/"; |
| |
| /** string that starts a crumb */ |
| static final String CommonBreadcrumbStart = "\n\t" + CrumbDelimiter; |
| |
| /** all known types of breadcrumbs */ |
| private enum CrumbType { |
| RECEIVE_SIDE, EVENTID, SEND_SIDE, PROBLEM |
| } |
| |
| /** crumb with the highest ordinal, for initialization of delimiter strings */ |
| @Immutable |
| private static final CrumbType Crumbiest = CrumbType.PROBLEM; |
| |
| private static final String[] crumbLabels = new String[] {"rcv", "evt", "snd", "oops"}; |
| |
| /** strings that start a particular breadcrumb */ |
| private static final String[] crumbStarts = new String[Crumbiest.ordinal() + 1]; |
| |
| /** strings the terminate a particular breadcrumb */ |
| private static final String[] crumbEnds = new String[Crumbiest.ordinal() + 1]; |
| |
| |
| static { |
| // initialize breadcrumb delimiter strings |
| for (int i = 0; i <= Crumbiest.ordinal(); i++) { |
| crumbStarts[i] = CommonBreadcrumbStart + crumbLabels[i] + CrumbDelimiter + " "; |
| crumbEnds[i] = " " + CrumbDelimiter + crumbLabels[i] + CrumbDelimiter; |
| } |
| } |
| |
| |
| //////////////////////// P U B L I C A P I |
| |
| |
| /** set the "sender" breadcrumb for a message while it's being processed */ |
| public static void setReceiveSide(Object crumb) { |
| if (ENABLED) { |
| setBreadcrumb(Thread.currentThread(), CrumbType.RECEIVE_SIDE, crumb); |
| } |
| } |
| |
| /** set the "recipients" breadcrumb for a message while waiting for it */ |
| public static void setSendSide(Object crumb) { |
| if (ENABLED) { |
| setBreadcrumb(Thread.currentThread(), CrumbType.SEND_SIDE, crumb); |
| } |
| } |
| |
| /** sets the given breadcrumb into the name of the current thread */ |
| public static void setEventId(Object crumb) { |
| if (ENABLED) { |
| if (crumb instanceof EventID) { |
| EventID other = EventIDs.get(); |
| if (other != null && other != crumb) { |
| ((EventID) crumb).incBreadcrumbCounter(); |
| setBreadcrumb(Thread.currentThread(), CrumbType.EVENTID, crumb); |
| } |
| } else { |
| setBreadcrumb(Thread.currentThread(), CrumbType.EVENTID, crumb); |
| } |
| } |
| } |
| |
| /** |
| * a problem crumb can be set using I18n message strings and arguments. Breadcrumb will localize |
| * the string with the given args |
| */ |
| public static void setProblem(String msg, Object[] args) { |
| if (ENABLED) { |
| setBreadcrumb(Thread.currentThread(), CrumbType.PROBLEM, String.format(msg, args)); |
| } |
| } |
| |
| /** sets the given breadcrumb into the name of the current thread */ |
| public static void setProblem(Object crumb) { |
| if (ENABLED) { |
| setBreadcrumb(Thread.currentThread(), CrumbType.PROBLEM, crumb); |
| } |
| } |
| |
| /** clears the breadcrumbs from the name of the current thread */ |
| public static void clearBreadcrumb() { |
| if (ENABLED) { |
| clearBreadcrumb(Thread.currentThread()); |
| EventIDs.set(null); |
| } |
| } |
| |
| |
| |
| /////////////////////// E N D O F P U B L I C A P I |
| |
| |
| /** sets the given breadcrumb into the name of the given thread */ |
| private static void setBreadcrumb(Thread t, CrumbType type, Object crumb) { |
| setCrumbInThread(t, type, crumb); |
| } |
| |
| /** clears the breadcrumb from the name of the given thread */ |
| private static void clearBreadcrumb(Thread t) { |
| String name = t.getName(); |
| int i = name.indexOf(CommonBreadcrumbStart); |
| if (i >= 0) { |
| t.setName(name.substring(0, i)); |
| } |
| } |
| |
| |
| /** |
| * This method does all of the work of setting/clearing individual breadcrumbs in thread names |
| * |
| * @param t the thread to modify |
| * @param type the type of breadcrumb |
| * @param crumb the crumb to insert, or null to clear |
| */ |
| private static void setCrumbInThread(Thread t, CrumbType type, Object crumb) { |
| |
| int typeIndex = type.ordinal(); |
| String name = t.getName(); |
| String crumbString; |
| |
| if (crumb == null) { |
| crumbString = ""; // remove existing crumb, if any |
| } else { |
| crumbString = crumbStarts[typeIndex] + crumb + crumbEnds[typeIndex]; |
| } |
| |
| int startIndex = name.indexOf(crumbStarts[typeIndex]); |
| int endIndex = -1; |
| |
| if (startIndex < 0) { |
| // there may be a higher numbered breadcrumb already in the thread name |
| for (int i = typeIndex + 1; (startIndex < 0) && (i <= Crumbiest.ordinal()); i++) { |
| startIndex = name.indexOf(crumbStarts[i]); |
| if (startIndex >= 0) { |
| endIndex = name.indexOf(crumbEnds[i], startIndex + 1); |
| } |
| } |
| } |
| |
| if (startIndex < 0) { |
| // no equal or higher numbered breadcrumbs - just tack this |
| // one onto the end of the thread's name |
| t.setName(name + crumbString); |
| } else if (endIndex > 0) { |
| // insert before the higher-numbered breadcrumb |
| t.setName( |
| name.substring(0, startIndex) + crumbString + name.substring(startIndex, name.length())); |
| } else { |
| // replace the existing breadcrumb |
| endIndex = name.indexOf(crumbEnds[typeIndex], startIndex + 1); |
| |
| // this shouldn't happen |
| assert endIndex > 0 : "odd thread name: " + name; |
| |
| t.setName(name.substring(0, startIndex) + crumbString |
| + name.substring(endIndex + crumbEnds[typeIndex].length(), name.length())); |
| } |
| } |
| |
| |
| private static void clearCrumbInThread(Thread t) { |
| String name = t.getName(); |
| int i = name.indexOf(CommonBreadcrumbStart); |
| if (i >= 0) { |
| t.setName(name.substring(0, i)); |
| } |
| } |
| |
| public static void main(String args[]) { |
| String threadName = null; |
| |
| ENABLED = true; |
| |
| setReceiveSide("processorId=17332"); |
| threadName = Thread.currentThread().getName(); |
| System.out.println("thread name with sender=" + threadName); |
| |
| setEventId("eventId"); |
| threadName = Thread.currentThread().getName(); |
| System.out.println("thread name with eventId=" + threadName); |
| |
| setEventId("eventId2"); |
| threadName = Thread.currentThread().getName(); |
| System.out.println("replaced eventId: " + threadName); |
| |
| setSendSide("recipients"); |
| threadName = Thread.currentThread().getName(); |
| System.out.println("with recipients: " + threadName); |
| |
| setEventId("eventId3"); |
| threadName = Thread.currentThread().getName(); |
| System.out.println("replaced eventId3: " + threadName); |
| |
| setSendSide("recipients2"); |
| threadName = Thread.currentThread().getName(); |
| System.out.println("with recipients2: " + threadName); |
| |
| setReceiveSide("processorId=22222"); |
| threadName = Thread.currentThread().getName(); |
| System.out.println("thread name with processor 22222=" + threadName); |
| |
| setSendSide(null); |
| threadName = Thread.currentThread().getName(); |
| System.out.println("with recipients removed: " + threadName); |
| |
| clearBreadcrumb(); |
| threadName = Thread.currentThread().getName(); |
| System.out.println("thread name cleared=" + threadName); |
| } |
| |
| } |