File D:\maven-qcr-plugin-testsunitaires\qcr4java\jenkins-master\target\site\qc\core\src\main\java\hudson\logging\LogRecorder.java.html

1 /*
2 * The MIT License
3 *
4 * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24 package hudson.logging;
25
26 import com.thoughtworks.xstream.XStream;
27 import hudson.BulkChange;
28 import hudson.Util;
29 import hudson.XmlFile;
30 import hudson.model.AbstractModelObject;
31 import jenkins.model.Jenkins;
32 import hudson.model.Saveable;
33 import hudson.model.listeners.SaveableListener;
34 import hudson.util.CopyOnWriteList;
35 import hudson.util.RingBufferLogHandler;
36 import hudson.util.XStream2;
37 import net.sf.json.JSONObject;
38 import org.kohsuke.stapler.DataBoundConstructor;
39 import org.kohsuke.stapler.StaplerRequest;
40 import org.kohsuke.stapler.StaplerResponse;
41 import org.kohsuke.stapler.interceptor.RequirePOST;
42
43 import javax.servlet.ServletException;
44 import java.io.File;
45 import java.io.IOException;
46 import java.util.List;
47 import java.util.Arrays;
48 import java.util.Locale;
49 import java.util.logging.Level;
50 import java.util.logging.LogRecord;
51 import java.util.logging.Logger;
52
53 /**
54 * Records a selected set of logs so that the system administrator
55 * can diagnose a specific aspect of the system.
56 *
57 * TODO: still a work in progress.
58 *
59 * <h3>Access Control</h3>
60 * {@link LogRecorder} is only visible for administrators, and this access control happens at
61 * {@link jenkins.model.Jenkins#getLog()}, the sole entry point for binding {@link LogRecorder} to URL.
62 *
63 * @author Kohsuke Kawaguchi
64 * @see LogRecorderManager
65 */
66 public class LogRecorder extends AbstractModelObject implements Saveable {
67 private volatile String name;
68
69 public final CopyOnWriteList<Target> targets = new CopyOnWriteList<Target>();
70
71 private transient /*almost final*/ RingBufferLogHandler handler = new RingBufferLogHandler() {
72 @Override
73 public void publish(LogRecord record) {
74 for (Target t : targets) {
75 if(t.includes(record)) {
76 super.publish(record);
77 return;
78 }
79 }
80 }
81 };
82
83 /**
84 * Logger that this recorder monitors, and its log level.
85 * Just a pair of (logger name,level) with convenience methods.
86 */
87 public static final class Target {
88 public final String name;
89 private final int level;
90 private transient /* almost final*/ Logger logger;
91
92 public Target(String name, Level level) {
93 this(name,level.intValue());
94 }
95
96 public Target(String name, int level) {
97 this.name = name;
98 this.level = level;
99 }
100
101 @DataBoundConstructor
102 public Target(String name, String level) {
103 this(name,Level.parse(level.toUpperCase(Locale.ENGLISH)));
104 }
105
106 public Level getLevel() {
107 return Level.parse(String.valueOf(level));
108 }
109
110 public boolean includes(LogRecord r) {
111 if(r.getLevel().intValue() < level)
112 return false; // below the threshold
113 String logName = r.getLoggerName();
114 if(logName==null || !logName.startsWith(name))
115 return false; // not within this logger
116
117 String rest = r.getLoggerName().substring(name.length());
118 return rest.startsWith(".") || rest.length()==0;
119 }
120
121 public Logger getLogger() {
122 if (logger == null) {
123 logger = Logger.getLogger(name);
124 }
125 return logger;
126 }
127
128 /**
129 * Makes sure that the logger passes through messages at the correct level to us.
130 */
131 public void enable() {
132 Logger l = getLogger();
133 if(!l.isLoggable(getLevel()))
134 l.setLevel(getLevel());
135 }
136 }
137
138 public LogRecorder(String name) {
139 this.name = name;
140 // register it only once when constructed, and when this object dies
141 // WeakLogHandler will remove it
142 new WeakLogHandler(handler,Logger.getLogger(""));
143 }
144
145 public String getDisplayName() {
146 return name;
147 }
148
149 public String getSearchUrl() {
150 return name;
151 }
152
153 public String getName() {
154 return name;
155 }
156
157 public LogRecorderManager getParent() {
158 return Jenkins.getInstance().getLog();
159 }
160
161 /**
162 * Accepts submission from the configuration page.
163 */
164 @RequirePOST
165 public synchronized void doConfigSubmit( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
166 JSONObject src = req.getSubmittedForm();
167
168 String newName = src.getString("name"), redirect = ".";
169 XmlFile oldFile = null;
170 if(!name.equals(newName)) {
171 Jenkins.checkGoodName(newName);
172 oldFile = getConfigFile();
173 // rename
174 getParent().logRecorders.remove(name);
175 this.name = newName;
176 getParent().logRecorders.put(name,this);
177 redirect = "../" + Util.rawEncode(newName) + '/';
178 }
179
180 List<Target> newTargets = req.bindJSONToList(Target.class, src.get("targets"));
181 for (Target t : newTargets)
182 t.enable();
183 targets.replaceBy(newTargets);
184
185 save();
186 if (oldFile!=null) oldFile.delete();
187 rsp.sendRedirect2(redirect);
188 }
189
190 /**
191 * Loads the settings from a file.
192 */
193 public synchronized void load() throws IOException {
194 getConfigFile().unmarshal(this);
195 for (Target t : targets)
196 t.enable();
197 }
198
199 /**
200 * Save the settings to a file.
201 */
202 public synchronized void save() throws IOException {
203 if(BulkChange.contains(this)) return;
204 getConfigFile().write(this);
205 SaveableListener.fireOnChange(this, getConfigFile());
206 }
207
208 /**
209 * Deletes this recorder, then go back to the parent.
210 */
211 @RequirePOST
212 public synchronized void doDoDelete(StaplerResponse rsp) throws IOException, ServletException {
213 getConfigFile().delete();
214 getParent().logRecorders.remove(name);
215 // Disable logging for all our targets,
216 // then reenable all other loggers in case any also log the same targets
217 for (Target t : targets)
218 t.getLogger().setLevel(null);
219 for (LogRecorder log : getParent().logRecorders.values())
220 for (Target t : log.targets)
221 t.enable();
222 rsp.sendRedirect2("..");
223 }
224
225 /**
226 * RSS feed for log entries.
227 */
228 public void doRss( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
229 LogRecorderManager.doRss(req,rsp,getLogRecords());
230 }
231
232 /**
233 * The file we save our configuration.
234 */
235 private XmlFile getConfigFile() {
236 return new XmlFile(XSTREAM, new File(Jenkins.getInstance().getRootDir(),"log/"+name+".xml"));
237 }
238
239 /**
240 * Gets a view of the log records.
241 */
242 public List<LogRecord> getLogRecords() {
243 return handler.getView();
244 }
245
246 /**
247 * Thread-safe reusable {@link XStream}.
248 */
249 public static final XStream XSTREAM = new XStream2();
250
251 static {
252 XSTREAM.alias("log",LogRecorder.class);
253 XSTREAM.alias("target",Target.class);
254 }
255
256 /**
257 * Log levels that can be configured for {@link Target}.
258 */
259 public static List<Level> LEVELS =
260 Arrays.asList(Level.SEVERE, Level.WARNING, Level.INFO, Level.CONFIG,
261 Level.FINE, Level.FINER, Level.FINEST, Level.ALL);
262 }
263