PageRenderTime 48ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/core/src/main/java/hudson/logging/LogRecorder.java

https://bitbucket.org/higginsci/higgins
Java | 264 lines | 158 code | 31 blank | 75 comment | 19 complexity | ab21e29c9133197781f16b8f744eda37 MD5 | raw file
Possible License(s): MIT, BSD-3-Clause
  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. import com.thoughtworks.xstream.XStream;
  26. import hudson.BulkChange;
  27. import hudson.Util;
  28. import hudson.XmlFile;
  29. import hudson.model.AbstractModelObject;
  30. import jenkins.model.Jenkins;
  31. import hudson.model.Saveable;
  32. import hudson.model.listeners.SaveableListener;
  33. import hudson.util.CopyOnWriteList;
  34. import hudson.util.RingBufferLogHandler;
  35. import hudson.util.XStream2;
  36. import net.sf.json.JSONObject;
  37. import org.kohsuke.stapler.DataBoundConstructor;
  38. import org.kohsuke.stapler.StaplerRequest;
  39. import org.kohsuke.stapler.StaplerResponse;
  40. import org.kohsuke.stapler.interceptor.RequirePOST;
  41. import javax.servlet.ServletException;
  42. import java.io.File;
  43. import java.io.IOException;
  44. import java.util.List;
  45. import java.util.Arrays;
  46. import java.util.Locale;
  47. import java.util.logging.Level;
  48. import java.util.logging.LogRecord;
  49. import java.util.logging.Logger;
  50. /**
  51. * Records a selected set of logs so that the system administrator
  52. * can diagnose a specific aspect of the system.
  53. *
  54. * TODO: still a work in progress.
  55. *
  56. * <h3>Access Control</h3>
  57. * {@link LogRecorder} is only visible for administrators, and this access control happens at
  58. * {@link jenkins.model.Jenkins#getLog()}, the sole entry point for binding {@link LogRecorder} to URL.
  59. *
  60. * @author Kohsuke Kawaguchi
  61. * @see LogRecorderManager
  62. */
  63. public class LogRecorder extends AbstractModelObject implements Saveable {
  64. private volatile String name;
  65. public final CopyOnWriteList<Target> targets = new CopyOnWriteList<Target>();
  66. private transient /*almost final*/ RingBufferLogHandler handler = new RingBufferLogHandler() {
  67. @Override
  68. public void publish(LogRecord record) {
  69. for (Target t : targets) {
  70. if(t.includes(record)) {
  71. super.publish(record);
  72. return;
  73. }
  74. }
  75. }
  76. };
  77. /**
  78. * Logger that this recorder monitors, and its log level.
  79. * Just a pair of (logger name,level) with convenience methods.
  80. */
  81. public static final class Target {
  82. public final String name;
  83. private final int level;
  84. private transient /* almost final*/ Logger logger;
  85. public Target(String name, Level level) {
  86. this(name,level.intValue());
  87. }
  88. public Target(String name, int level) {
  89. this.name = name;
  90. this.level = level;
  91. }
  92. @DataBoundConstructor
  93. public Target(String name, String level) {
  94. this(name,Level.parse(level.toUpperCase(Locale.ENGLISH)));
  95. }
  96. public Level getLevel() {
  97. return Level.parse(String.valueOf(level));
  98. }
  99. public boolean includes(LogRecord r) {
  100. if(r.getLevel().intValue() < level)
  101. return false; // below the threshold
  102. if (name.length() == 0) {
  103. return true; // like root logger, includes everything
  104. }
  105. String logName = r.getLoggerName();
  106. if(logName==null || !logName.startsWith(name))
  107. return false; // not within this logger
  108. String rest = logName.substring(name.length());
  109. return rest.startsWith(".") || rest.length()==0;
  110. }
  111. public Logger getLogger() {
  112. if (logger == null) {
  113. logger = Logger.getLogger(name);
  114. }
  115. return logger;
  116. }
  117. /**
  118. * Makes sure that the logger passes through messages at the correct level to us.
  119. */
  120. public void enable() {
  121. Logger l = getLogger();
  122. if(!l.isLoggable(getLevel()))
  123. l.setLevel(getLevel());
  124. }
  125. }
  126. public LogRecorder(String name) {
  127. this.name = name;
  128. // register it only once when constructed, and when this object dies
  129. // WeakLogHandler will remove it
  130. new WeakLogHandler(handler,Logger.getLogger(""));
  131. }
  132. public String getDisplayName() {
  133. return name;
  134. }
  135. public String getSearchUrl() {
  136. return name;
  137. }
  138. public String getName() {
  139. return name;
  140. }
  141. public LogRecorderManager getParent() {
  142. return Jenkins.getInstance().getLog();
  143. }
  144. /**
  145. * Accepts submission from the configuration page.
  146. */
  147. @RequirePOST
  148. public synchronized void doConfigSubmit( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
  149. JSONObject src = req.getSubmittedForm();
  150. String newName = src.getString("name"), redirect = ".";
  151. XmlFile oldFile = null;
  152. if(!name.equals(newName)) {
  153. Jenkins.checkGoodName(newName);
  154. oldFile = getConfigFile();
  155. // rename
  156. getParent().logRecorders.remove(name);
  157. this.name = newName;
  158. getParent().logRecorders.put(name,this);
  159. redirect = "../" + Util.rawEncode(newName) + '/';
  160. }
  161. List<Target> newTargets = req.bindJSONToList(Target.class, src.get("targets"));
  162. for (Target t : newTargets)
  163. t.enable();
  164. targets.replaceBy(newTargets);
  165. save();
  166. if (oldFile!=null) oldFile.delete();
  167. rsp.sendRedirect2(redirect);
  168. }
  169. /**
  170. * Loads the settings from a file.
  171. */
  172. public synchronized void load() throws IOException {
  173. getConfigFile().unmarshal(this);
  174. for (Target t : targets)
  175. t.enable();
  176. }
  177. /**
  178. * Save the settings to a file.
  179. */
  180. public synchronized void save() throws IOException {
  181. if(BulkChange.contains(this)) return;
  182. getConfigFile().write(this);
  183. SaveableListener.fireOnChange(this, getConfigFile());
  184. }
  185. /**
  186. * Deletes this recorder, then go back to the parent.
  187. */
  188. @RequirePOST
  189. public synchronized void doDoDelete(StaplerResponse rsp) throws IOException, ServletException {
  190. getConfigFile().delete();
  191. getParent().logRecorders.remove(name);
  192. // Disable logging for all our targets,
  193. // then reenable all other loggers in case any also log the same targets
  194. for (Target t : targets)
  195. t.getLogger().setLevel(null);
  196. for (LogRecorder log : getParent().logRecorders.values())
  197. for (Target t : log.targets)
  198. t.enable();
  199. rsp.sendRedirect2("..");
  200. }
  201. /**
  202. * RSS feed for log entries.
  203. */
  204. public void doRss( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
  205. LogRecorderManager.doRss(req,rsp,getLogRecords());
  206. }
  207. /**
  208. * The file we save our configuration.
  209. */
  210. private XmlFile getConfigFile() {
  211. return new XmlFile(XSTREAM, new File(Jenkins.getInstance().getRootDir(),"log/"+name+".xml"));
  212. }
  213. /**
  214. * Gets a view of the log records.
  215. */
  216. public List<LogRecord> getLogRecords() {
  217. return handler.getView();
  218. }
  219. /**
  220. * Thread-safe reusable {@link XStream}.
  221. */
  222. public static final XStream XSTREAM = new XStream2();
  223. static {
  224. XSTREAM.alias("log",LogRecorder.class);
  225. XSTREAM.alias("target",Target.class);
  226. }
  227. /**
  228. * Log levels that can be configured for {@link Target}.
  229. */
  230. public static List<Level> LEVELS =
  231. Arrays.asList(Level.SEVERE, Level.WARNING, Level.INFO, Level.CONFIG,
  232. Level.FINE, Level.FINER, Level.FINEST, Level.ALL);
  233. }