上一次主要是给了一个解题方案,没有具体讲解,这次又做到了就来看下几种方案,链表转置一直是我比较疑惑的问题,特别是边界处理,而这个问题要把难度加大了 我先讲一下我一开始的思路和解题方法,首先就是写一个转置方法,就处理 k 个一组的内部转置,然后外部循环处理分组以及前后连接等问题,但是这里就涉及到一个问题,就是前后连接的话对于整个链表头,也就是第一个 k 元素组来说,头是个空的,就需要额外处理,一开始就是用判空做额外处理,然后在前后连接的时候也有一些困扰,看了自己的代码库发现其实之前也做过,而且发现这个思路跟以前做的还是一样的,只不过在处理 k 个元素内部的转置的时候有了点差异
一开始的思路是这样的,想了下其实还是比较有问题,从处理难度上来说,在 k 组内处理的时候一开始 A 节点会是悬空的,然后得处理到组内最后一个元素的时候再接上,逻辑会更复杂,而另一种思路就是直接把 A 先挪到 C 后面,这样每次只需要移动一个,可能思路上会更清晰一点
而最后这种则代码会简单一些,但是需要一定的理解成本,比较重要的一点是我们加了个虚拟的头结点,第一步会先获取下链表的总长度,然后在内循环里处理转置,重点就是分四步, 也就是图里的,第一步先把 cur 的下一个节点设置成 next 的 next 节点,这里就是图里 A 连接到 C,第二步是把 next 的 next 节点设置成虚拟头的 next节点,第三步是把 pre 的next 节点设置成 next,第四步是 next 往后移动
publicvoidcallAppenders(ILoggingEvent event) { intwrites=0; for (Loggerl=this; l != null; l = l.parent) { writes += l.appendLoopOnAppenders(event); if (!l.additive) { break; } } // No appenders in hierarchy if (writes == 0) { loggerContext.noAppenderDefinedWarning(this); } }
publicvoiddoAppend(E eventObject) { // WARNING: The guard check MUST be the first statement in the // doAppend() method.
// prevent re-entry. if (Boolean.TRUE.equals(guard.get())) { return; }
try { guard.set(Boolean.TRUE);
if (!this.started) { if (statusRepeatCount++ < ALLOWED_REPEATS) { addStatus(newWarnStatus("Attempted to append to non started appender [" + name + "].", this)); } return; }
if (getFilterChainDecision(eventObject) == FilterReply.DENY) { return; }
// ok, we now invoke derived class' implementation of append this.append(eventObject);
} catch (Exception e) { if (exceptionCount++ < ALLOWED_REPEATS) { addError("Appender [" + name + "] failed to append.", e); } } finally { guard.set(Boolean.FALSE); } }
@Override protectedvoidsubAppend(E event) { // The roll-over check must precede actual writing. This is the // only correct behavior for time driven triggers.
// We need to synchronize on triggeringPolicy so that only one rollover // occurs at a time synchronized (triggeringPolicy) { if (triggeringPolicy.isTriggeringEvent(currentlyActiveFile, event)) { rollover(); } }
@Override publicbooleanisTriggeringEvent(File activeFile, final E event) {
longtime= getCurrentTime();
// first check for roll-over based on time if (time >= nextCheck) { DatedateInElapsedPeriod= dateInCurrentPeriod; elapsedPeriodsFileName = tbrp.fileNamePatternWithoutCompSuffix.convertMultipleArguments(dateInElapsedPeriod, currentPeriodsCounter); currentPeriodsCounter = 0; setDateInCurrentPeriod(time); computeNextCheck(); returntrue; }
// next check for roll-over based on size if (invocationGate.isTooSoon(time)) { returnfalse; }
if (activeFile == null) { addWarn("activeFile == null"); returnfalse; } if (maxFileSize == null) { addWarn("maxFileSize = null"); returnfalse; } if (activeFile.length() >= maxFileSize.getSize()) {
static Set<URL> findPossibleStaticLoggerBinderPathSet() { // use Set instead of list in order to deal with bug #138 // LinkedHashSet appropriate here because it preserves insertion order // during iteration Set<URL> staticLoggerBinderPathSet = newLinkedHashSet<URL>(); try { ClassLoaderloggerFactoryClassLoader= LoggerFactory.class.getClassLoader(); Enumeration<URL> paths; if (loggerFactoryClassLoader == null) { paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH); } else { paths = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH); } while (paths.hasMoreElements()) { URLpath= paths.nextElement(); staticLoggerBinderPathSet.add(path); } } catch (IOException ioe) { Util.report("Error getting resources from path", ioe); } return staticLoggerBinderPathSet; }
publicvoidconfigureByResource(URL url)throws JoranException { if (url == null) { thrownewIllegalArgumentException("URL argument cannot be null"); } finalStringurlString= url.toString(); if (urlString.endsWith("groovy")) { if (EnvUtil.isGroovyAvailable()) { // avoid directly referring to GafferConfigurator so as to avoid // loading groovy.lang.GroovyObject . See also http://jira.qos.ch/browse/LBCLASSIC-214 GafferUtil.runGafferConfiguratorOn(loggerContext, this, url); } else { StatusManagersm= loggerContext.getStatusManager(); sm.add(newErrorStatus("Groovy classes are not available on the class path. ABORTING INITIALIZATION.", loggerContext)); } } elseif (urlString.endsWith("xml")) { JoranConfiguratorconfigurator=newJoranConfigurator(); configurator.setContext(loggerContext); configurator.doConfigure(url); } else { thrownewLogbackException("Unexpected filename extension of file [" + url.toString() + "]. Should be either .groovy or .xml"); } }
longthreshold= System.currentTimeMillis(); // if (!ConfigurationWatchListUtil.wasConfigurationWatchListReset(context)) { // informContextOfURLUsedForConfiguration(getContext(), null); // } SaxEventRecorderrecorder=newSaxEventRecorder(context); recorder.recordEvents(inputSource); doConfigure(recorder.saxEventList); // no exceptions a this level StatusUtilstatusUtil=newStatusUtil(context); if (statusUtil.noXMLParsingErrorsOccurred(threshold)) { addInfo("Registering current configuration as safe fallback point"); registerSafeConfiguration(recorder.saxEventList); } }
需要解析 xml 了,先是调用 buildInterpreter 构建了 Interpreter
1 2 3 4 5 6 7
publicvoiddoConfigure(final List<SaxEvent> eventList)throws JoranException { buildInterpreter(); // disallow simultaneous configurations of the same context synchronized (context.getConfigurationLock()) { interpreter.getEventPlayer().play(eventList); } }
// add jmxConfigurator only if we have JMX available. // If running under JDK 1.4 (retrotranslateed logback) then we // might not have JMX. if (PlatformInfo.hasJMXObjectName()) { rs.addRule(newElementSelector("configuration/jmxConfigurator"), newJMXConfiguratorAction()); } rs.addRule(newElementSelector("configuration/include"), newIncludeAction());
Iterator<Action> i = applicableActionList.iterator(); while (i.hasNext()) { Actionaction= (Action) i.next(); // now let us invoke the action. We catch and report any eventual // exceptions try { action.begin(interpretationContext, tagName, atts); } catch (ActionException e) { skip = elementPath.duplicate(); cai.addError("ActionException in Action for tag [" + tagName + "]", e); } catch (RuntimeException e) { skip = elementPath.duplicate(); cai.addError("RuntimeException in Action for tag [" + tagName + "]", e); } } }
publicvoidbegin(InterpretationContext ec, String name, Attributes attributes) { // Let us forget about previous errors (in this object) inError = false; logger = null;
if (OptionHelper.isEmpty(loggerName)) { inError = true; StringaroundLine= getLineColStr(ec); StringerrorMsg="No 'name' attribute in element " + name + ", around " + aroundLine; addError(errorMsg); return; }
if (name == null) { thrownewIllegalArgumentException("name argument cannot be null"); }
// if we are asking for the root logger, then let us return it without // wasting time if (Logger.ROOT_LOGGER_NAME.equalsIgnoreCase(name)) { return root; }
inti=0; Loggerlogger= root;
// check if the desired logger exists, if it does, return it // without further ado. LoggerchildLogger= (Logger) loggerCache.get(name); // if we have the child, then let us return it without wasting time if (childLogger != null) { return childLogger; }
// if the desired logger does not exist, them create all the loggers // in between as well (if they don't already exist) String childName; while (true) { inth= LoggerNameUtil.getSeparatorIndexOf(name, i); if (h == -1) { childName = name; } else { childName = name.substring(0, h); } // move i left of the last point i = h + 1; synchronized (logger) { childLogger = logger.getChildByName(childName); if (childLogger == null) { childLogger = logger.createChildByName(childName); loggerCache.put(childName, childLogger); incSize(); } } logger = childLogger; if (h == -1) { return childLogger; } } }
publicvoidstart() { SizeAndTimeBasedFNATP<E> sizeAndTimeBasedFNATP = newSizeAndTimeBasedFNATP<E>(Usage.EMBEDDED); if(maxFileSize == null) { addError("maxFileSize property is mandatory."); return; } else { addInfo("Archive files will be limited to ["+maxFileSize+"] each."); } sizeAndTimeBasedFNATP.setMaxFileSize(maxFileSize); timeBasedFileNamingAndTriggeringPolicy = sizeAndTimeBasedFNATP; if(!isUnboundedTotalSizeCap() && totalSizeCap.getSize() < maxFileSize.getSize()) { addError("totalSizeCap of ["+totalSizeCap+"] is smaller than maxFileSize ["+maxFileSize+"] which is non-sensical"); return; } // most work is done by the parent super.start(); }
if (!timeBasedFileNamingAndTriggeringPolicy.isStarted()) { addWarn("Subcomponent did not start. TimeBasedRollingPolicy will not start."); return; }
// the maxHistory property is given to TimeBasedRollingPolicy instead of to // the TimeBasedFileNamingAndTriggeringPolicy. This makes it more convenient // for the user at the cost of inconsistency here. if (maxHistory != UNBOUND_HISTORY) { archiveRemover = timeBasedFileNamingAndTriggeringPolicy.getArchiveRemover(); archiveRemover.setMaxHistory(maxHistory); archiveRemover.setTotalSizeCap(totalSizeCap.getSize()); if (cleanHistoryOnStart) { addInfo("Cleaning on start up"); Datenow=newDate(timeBasedFileNamingAndTriggeringPolicy.getCurrentTime()); cleanUpFuture = archiveRemover.cleanAsynchronously(now); } } elseif (!isUnboundedTotalSizeCap()) { addWarn("'maxHistory' is not set, ignoring 'totalSizeCap' option with value ["+totalSizeCap+"]"); }
super.start(); }
第一步是给 renameUtil 设置 context
第二步是判断 fileNamePatternStr是否已配置,没配置会报错,如果配置了就会去判断压缩格式,是否要压缩以及压缩的是 gz 还是 zip
publicvoidstart() { // we depend on certain fields having been initialized in super class super.start(); if(usage == Usage.DIRECT) { addWarn(CoreConstants.SIZE_AND_TIME_BASED_FNATP_IS_DEPRECATED); addWarn("For more information see "+MANUAL_URL_PREFIX+"appenders.html#SizeAndTimeBasedRollingPolicy"); } if (!super.isErrorFree()) return;
if (maxFileSize == null) { addError("maxFileSize property is mandatory."); withErrors(); }
if (!validateDateAndIntegerTokens()) { withErrors(); return; }
// we need to get the correct value of currentPeriodsCounter. // usually the value is 0, unless the appender or the application // is stopped and restarted within the same period Stringregex= tbrp.fileNamePattern.toRegexForFixedDate(dateInCurrentPeriod); StringstemRegex= FileFilterUtil.afterLastSlash(regex);
publicvoidstart() { DateTokenConverter<Object> dtc = tbrp.fileNamePattern.getPrimaryDateTokenConverter(); if (dtc == null) { thrownewIllegalStateException("FileNamePattern [" + tbrp.fileNamePattern.getPattern() + "] does not contain a valid DateToken"); }
if (dtc.getTimeZone() != null) { rc = newRollingCalendar(dtc.getDatePattern(), dtc.getTimeZone(), Locale.getDefault()); } else { rc = newRollingCalendar(dtc.getDatePattern()); } addInfo("The date pattern is '" + dtc.getDatePattern() + "' from file name pattern '" + tbrp.fileNamePattern.getPattern() + "'."); rc.printPeriodicity(this);
if (!rc.isCollisionFree()) { addError("The date format in FileNamePattern will result in collisions in the names of archived log files."); addError(CoreConstants.MORE_INFO_PREFIX + COLLIDING_DATE_FORMAT_URL); withErrors(); return; }
setDateInCurrentPeriod(newDate(getCurrentTime())); if (tbrp.getParentsRawFileProperty() != null) { FilecurrentFile=newFile(tbrp.getParentsRawFileProperty()); if (currentFile.exists() && currentFile.canRead()) { setDateInCurrentPeriod(newDate(currentFile.lastModified())); } } addInfo("Setting initial period to " + dateInCurrentPeriod); computeNextCheck(); }