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 | } |