| 1 | package dmg.cells.nucleus; |
| 2 | |
| 3 | import java.io.Serializable; |
| 4 | |
| 5 | import org.slf4j.MDC; |
| 6 | import org.dcache.commons.util.NDC; |
| 7 | |
| 8 | import dmg.util.TimebasedCounter; |
| 9 | |
| 10 | /** |
| 11 | * The Cell Diagnostic Context, a utility class for working with the |
| 12 | * Log4j NDC and MDC. |
| 13 | * |
| 14 | * Notice that the MDC is automatically inherited by child threads |
| 15 | * upon creation. Thus the domain, cell name, and session identifier |
| 16 | * is inherited. The same is not true for the NDC, which needs to be |
| 17 | * explicitly copied. Special care must be taken when using shared |
| 18 | * thread pools, as this can span several cells. For those the MDC |
| 19 | * should be initialised per task, not per thread. |
| 20 | * |
| 21 | * The class serves two purposes: |
| 22 | * |
| 23 | * - It contains a number of static methods for manipulating the MDC |
| 24 | * and NDC. |
| 25 | * |
| 26 | * - CDC instances capture the Cells related MDC values and the NDC. |
| 27 | * These can be apply to other threads. This is useful when using |
| 28 | * worker threads that should inherit the context of the task |
| 29 | * creation point. |
| 30 | * |
| 31 | */ |
| 32 | public class CDC |
| 33 | { |
| 34 | public final static String MDC_DOMAIN = "cells.domain"; |
| 35 | public final static String MDC_CELL = "cells.cell"; |
| 36 | public final static String MDC_SESSION = "cells.session"; |
| 37 | |
| 38 | private final static TimebasedCounter _sessionCounter = |
| 39 | new TimebasedCounter(); |
| 40 | |
| 41 | private final NDC _ndc; |
| 42 | private final String _session; |
| 43 | private final String _cell; |
| 44 | private final String _domain; |
| 45 | |
| 46 | /** |
| 47 | * Captures the cells diagnostic context of the calling thread. |
| 48 | */ |
| 49 | public CDC() |
| 50 | { |
| 51 | _session = MDC.get(MDC_SESSION); |
| 52 | _cell = MDC.get(MDC_CELL); |
| 53 | _domain = MDC.get(MDC_DOMAIN); |
| 54 | _ndc = NDC.cloneNdc(); |
| 55 | } |
| 56 | |
| 57 | /** |
| 58 | * Wrapper around <code>MDC.put</code> and |
| 59 | * <code>MDC.remove</code>. <code>value</code> is allowed to e |
| 60 | * null. |
| 61 | */ |
| 62 | static private void setMdc(String key, String value) |
| 63 | { |
| 64 | if (value != null) { |
| 65 | MDC.put(key, value); |
| 66 | } else { |
| 67 | MDC.remove(key); |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | /** |
| 72 | * Applies the cells diagnostic context to the calling thread. If |
| 73 | * <code>clone</code> is false, then the <code>apply</code> can |
| 74 | * only be called once for this CDC. If <code>clone</code> is |
| 75 | * true, then the CDC may be applied several times, however the |
| 76 | * operation is more expensive. |
| 77 | */ |
| 78 | public void apply() |
| 79 | { |
| 80 | setMdc(MDC_DOMAIN, _domain); |
| 81 | setMdc(MDC_CELL, _cell); |
| 82 | setMdc(MDC_SESSION, _session); |
| 83 | if (_ndc == null) { |
| 84 | NDC.clear(); |
| 85 | } else { |
| 86 | NDC.set(_ndc); |
| 87 | } |
| 88 | } |
| 89 | |
| 90 | /** |
| 91 | * Returns the session identifier stored in the MDC of the calling |
| 92 | * thread. |
| 93 | */ |
| 94 | static public String getSession() |
| 95 | { |
| 96 | return MDC.get(MDC_SESSION); |
| 97 | } |
| 98 | |
| 99 | /** |
| 100 | * Sets the session in the MDC for the calling thread. |
| 101 | * |
| 102 | * @param session Session identifier. |
| 103 | */ |
| 104 | static public void setSession(String session) |
| 105 | { |
| 106 | setMdc(MDC_SESSION, session); |
| 107 | } |
| 108 | |
| 109 | /** |
| 110 | * Creates and sets a new session identifier. The session |
| 111 | * identifier is based on static TimedbasedCounter and is thus |
| 112 | * with a high probability unique in this JVM. |
| 113 | * |
| 114 | * As long as each JVM uses its own prefix, the identifier will be |
| 115 | * unique over multible JVMs. |
| 116 | * |
| 117 | * @see dmg.util.TimedbasedCounter |
| 118 | */ |
| 119 | static public void createSession(String prefix) |
| 120 | { |
| 121 | setSession(prefix + "-" + _sessionCounter.next()); |
| 122 | } |
| 123 | |
| 124 | /** |
| 125 | * Creates and sets a new session identifier. Uses the domain name |
| 126 | * stored in he MDC of the calling thread as a prefix. This |
| 127 | * guarantees uniqueness among a cells system (but see |
| 128 | * documentation of TimebasedCounter for the limits). |
| 129 | * |
| 130 | * @throws IllegalStateException if domain name is not set in MDC |
| 131 | * @see dmg.util.TimedbasedCounter |
| 132 | */ |
| 133 | static public void createSession() |
| 134 | { |
| 135 | Object domain = MDC.get(MDC_DOMAIN); |
| 136 | if (domain == null) |
| 137 | throw new IllegalStateException("Missing domain name in MDC"); |
| 138 | |
| 139 | createSession(domain.toString()); |
| 140 | } |
| 141 | |
| 142 | /** |
| 143 | * Setup the cell diagnostic context of the calling |
| 144 | * thread. Threads created from the calling thread automatically |
| 145 | * inherit this information. |
| 146 | */ |
| 147 | static public void setCellsContext(CellNucleus cell) |
| 148 | { |
| 149 | setCellsContext(cell.getCellName(), cell.getCellDomainName()); |
| 150 | } |
| 151 | |
| 152 | /** |
| 153 | * Setup the cell diagnostic context of the calling |
| 154 | * thread. Threads created from the calling thread automatically |
| 155 | * inherit this information. |
| 156 | */ |
| 157 | static public void setCellsContext(String cellName, String domainName) |
| 158 | { |
| 159 | setMdc(MDC_CELL, cellName); |
| 160 | setMdc(MDC_DOMAIN, domainName); |
| 161 | } |
| 162 | |
| 163 | /** |
| 164 | * Returns the message description added to the NDC as part of the |
| 165 | * message related diagnostic context. |
| 166 | */ |
| 167 | static protected String getMessageContext(CellMessage envelope) |
| 168 | { |
| 169 | StringBuilder s = new StringBuilder(); |
| 170 | |
| 171 | Object session = envelope.getSession(); |
| 172 | if (session != null) { |
| 173 | s.append(session).append(' '); |
| 174 | } |
| 175 | |
| 176 | s.append(envelope.getSourceAddress().getCellName()); |
| 177 | |
| 178 | Object msg = envelope.getMessageObject(); |
| 179 | if (msg instanceof HasDiagnosticContext) { |
| 180 | String context = |
| 181 | ((HasDiagnosticContext) msg).getDiagnosticContext(); |
| 182 | s.append(' ').append(context); |
| 183 | } |
| 184 | |
| 185 | return s.toString(); |
| 186 | } |
| 187 | |
| 188 | /** |
| 189 | * Setup message related diagnostic context for the calling |
| 190 | * thread. Adds information about a message to the MDC and NDC. |
| 191 | * |
| 192 | * @see clearMessageContext |
| 193 | */ |
| 194 | static public void setMessageContext(CellMessage envelope) |
| 195 | { |
| 196 | Object session = envelope.getSession(); |
| 197 | NDC.push(getMessageContext(envelope)); |
| 198 | setMdc(MDC_SESSION, (session == null) ? null : session.toString()); |
| 199 | } |
| 200 | |
| 201 | /** |
| 202 | * Clears the diagnostic context entries added by |
| 203 | * <code>setMessageContext</code>. For this to work, the NDC has |
| 204 | * to be in the same state as when <code>setMessageContext</code> |
| 205 | * returned. |
| 206 | * |
| 207 | * @see setMessageContext |
| 208 | */ |
| 209 | static public void clearMessageContext() |
| 210 | { |
| 211 | MDC.remove(MDC_SESSION); |
| 212 | NDC.pop(); |
| 213 | } |
| 214 | |
| 215 | /** |
| 216 | * Clears all cells related MDC entries and the NDC. |
| 217 | */ |
| 218 | static public void clear() |
| 219 | { |
| 220 | MDC.remove(MDC_DOMAIN); |
| 221 | MDC.remove(MDC_CELL); |
| 222 | MDC.remove(MDC_SESSION); |
| 223 | NDC.clear(); |
| 224 | } |
| 225 | } |