- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我要运行一个可能需要几分钟甚至几小时的过程。为了跟踪此类运行的历史记录,我为每次运行创建了一个自定义类型的节点,并在其中存储了相关的流程元数据。另外,我想将日志文件存储在这样的节点下。与将日志文件与进程meta分开存储在磁盘上相比,这似乎是一种更一致,更方便的方法。
现在,nt:file
节点类型本身具有一个具有jcr:content
属性的jcr:data
子节点,该属性允许我存储二进制内容。对于一次或不经常更改文件内容来说,这是很好的选择。
但是,我将不断向该文件追加新内容,此外,还要在单独的线程中轮询其内容(以跟踪进度)。
面对javax.jcr.ValueFactory
,javax.jcr.Binary
的JCR API似乎并不真正支持这种方法,我宁愿每次添加一个文件时都一遍又一遍地重写该文件(或更准确地说是二进制属性)。单行日志。我担心性能。
我在文档中搜索了一些工具,这些工具可以打开该文件的输出流,并定期将更改从该流刷新到JCR,但似乎没有可用的东西。
那么,还有什么比普通的javax.jcr.ValueFactory
和javax.jcr.Binary
更聪明的呢?
最佳答案
考虑了一段时间后,我在这里有了所有选择:
将日志保存在内存中,每当用户调用info/warn/error
时将其保存到CRX。优点:与迁移任务元数据存储在同一位置的日志,易于查找和访问。缺点:在大量日志条目的情况下,这可能是所有方法中最慢且资源效率最低的。
将日志保留在内存中,仅在迁移结束时将其保存到JCR。优点:易于重构当前解决方案,在迁移过程中对CRX的压力较小。缺点:无法跟踪实时进度,在意外错误或实例关闭期间可能丢失的日志。
为每个日志条目而不是log.txt创建一个自定义类型的节点。通过特殊的日志servlet聚合文本文件中的日志。即/var/migration/uuid/log.txt
或/var/migration/uuid/log.json
。优点:更多的JCR存储此类内容的方式。对于自定义节点类型和索引,应该足够快地考虑作为一种选择。具有多样性以支持日志的文本和json格式。缺点:与当前方法的性能比较不清楚。由于位于同一级别的大量节点而导致的潜在问题。用户应了解日志servlet的存在,否则用户将无法以方便的格式查看它们。在大量日志条目的情况下,日志servlet的性能不清楚。
在文件系统上创建日志文件(假设在crx-quickstart/logs/migration/<uuid>.log
),通过API显示其内容(如果需要),并能够将日志API响应中继到最后100-1000行。优点:当日志文件存储在文件系统上时,经典且众所周知的日志方法。 Sling提供了已配置的绑定slf4j
到LogBack
,并导出了所有必需的LogBack依赖关系,以供在自定义捆绑包中使用。缺点:日志和任务元数据分离。用户应了解磁盘上的日志文件位置。
从选项1开始,我然后意识到日志条目的数量可能会扩展到成千上万-罕见,但可能的情况。因此,最终决定选择选项4。
万一任何人都会面临类似的任务,我将在此处发布选项4的实现细节,因为它乍一看并不那么琐碎。
我正在使用AEM 6.2
(引擎盖下的Felix-Jackrabbit-Sling),并且我希望运行每个迁移任务-本质上只是一个单独的线程-用特殊名称创建它自己的日志文件-该迁移过程的唯一标识符。
现在,Sling本身允许您通过org.apache.sling.commons.log.LogManager.factory.config
OSGi配置定义多个日志配置。但是,对于这种情况,这些日志配置太简单了-您无法在LogBack中创建SiftingAppender所谓的
因此,从逻辑上考虑,您可能希望在运行时以编程方式获取Sling的LogBack配置(例如,当您上传自定义捆绑包并激活它时),并使用它为特定的记录器配置此类附加程序。不幸的是,尽管有很多关于如何通过
logback.xml
配置LogBack的文档,但是很少有文档描述如何通过LogBack的Java对象(如
ch.qos.logback.classic.LoggerContext
)以编程方式进行此操作,其中有零个文档说明了如何配置该方法。
SiftingAppender
的方式。
因此,在阅读了LogBack的源代码和测试后,我得到了这个帮助器类:
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.sift.MDCBasedDiscriminator;
import ch.qos.logback.classic.sift.SiftingAppender;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.Context;
import ch.qos.logback.core.FileAppender;
import ch.qos.logback.core.joran.spi.JoranException;
import ch.qos.logback.core.sift.AppenderFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import java.util.Objects;
/**
* This class dynamically adds configuration to AEM's LogBack logging implementation behind slf4j.
* The point is to provide loggers bound to specific task ID and therefore specific log file, so
* each migration task run will be written in it's standalone log file.
* */
public class LogUtil {
static {
LoggerContext rootContext = (LoggerContext) LoggerFactory.getILoggerFactory();
ch.qos.logback.classic.Logger logger = rootContext.getLogger("migration-logger");
//since appender lives until AEM instance restarted
//we are checking if appender had being registered previously
//to ensure we won't do it more than once
if(logger.getAppender("MIGRATION-TASK-SIFT") == null) {
MDCBasedDiscriminator mdcBasedDiscriminator = new MDCBasedDiscriminator();
mdcBasedDiscriminator.setContext(rootContext);
mdcBasedDiscriminator.setKey("taskId");
mdcBasedDiscriminator.setDefaultValue("no-task-id");
mdcBasedDiscriminator.start();
SiftingAppender siftingAppender = new SiftingAppender();
siftingAppender.setContext(rootContext);
siftingAppender.setName("MIGRATION-TASK-SIFT");
siftingAppender.setDiscriminator(mdcBasedDiscriminator);
siftingAppender.setAppenderFactory(new FileAppenderFactory());
siftingAppender.start();
logger.setAdditive(false);
logger.setLevel(ch.qos.logback.classic.Level.ALL);
logger.addAppender(siftingAppender);
}
}
public static class FileAppenderFactory implements AppenderFactory<ILoggingEvent> {
@Override
public Appender<ILoggingEvent> buildAppender(Context context, String taskId) throws JoranException {
PatternLayoutEncoder logEncoder = new PatternLayoutEncoder();
logEncoder.setContext(context);
logEncoder.setPattern("%-12date{YYYY-MM-dd HH:mm:ss.SSS} %-5level - %msg%n");
logEncoder.start();
FileAppender<ILoggingEvent> appender = new FileAppender<>();
appender.setContext(context);
appender.setName("migration-log-file");
appender.setFile("crx-quickstart/logs/migration/task-" + taskId + ".log");
appender.setEncoder(logEncoder);
appender.setAppend(true);
appender.start();
//need to add cleanup configuration for old logs ?
return appender;
}
}
private LogUtil(){
}
public static Logger getTaskLogger(String taskId) {
Objects.requireNonNull(taskId);
MDC.put("taskId", taskId);
return LoggerFactory.getLogger("migration-logger");
}
public static void releaseTaskLogger() {
MDC.remove("taskId");
}
}
SiftingAppender
要求您实现
AppenderFactory
接口,该接口将为每个线程使用的记录器生成已配置的附加程序。
LogUtil.getTaskLogger("some-task-uuid")
crq-quickstart/logs/migration/task-<taskId>.log
创建日志文件,例如
taskId
LogUtil.releaseTaskLogger()
关于aem - JCR中的附加文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43921910/
我关注了 tutorial on creating a popup for an add-on在 Firefox 中,效果很好。 我现在遇到的问题是它创建的弹出窗口不会更改大小以适应我添加到其中的内容
我有一些视频,我需要连接一个标题并添加一些覆盖,问题是我需要先做覆盖,否则时间不正确,然后才将标题连接到视频的开头 ffmpeg -i talk.mp4 -i start_pancarte.png
我正在尝试附加一个 CSV 文件。这是我正在使用的线路。不幸的是,我找不到 export-csv 的附加选项。任何想法都有助于使其发挥作用。 Get-ADGroupMember "Domain Adm
我正在努力理解 Attach API (com.sun.tools.attach.*) 的用途。它的典型用途是什么?它是为了“模拟”JVM,以便您可以在不部署/启动代码的情况下测试您的代码吗?它是一个
我不明白为什么这不起作用。 soup_main = BeautifulSoup('FooBar') soup_append = BeautifulSoup('Meh') soup_main.body.
我有以下代码来返回我想要的字符串 $sql = " SELECT `description` FROM `auctions` WHERE `description` REGEX
我正在尝试从数组中附加具有多个值的元素,但我做错了。这是我的代码: for(var i=0; i ` + pricesArray[i].start_date ` ` + pricesArray[i
我正在尝试将图像链接添加到此 javascript 附加表中。使图像位于按钮上方 这是代码 $("#1").append(""+section+""+no+""+price+""+button+""
我有一个问题,我已经解决了,但它太烦人了。 我有一个 js 代码,当使用“追加”按下按钮时,它会放下一些 html 代码,并且通过该代码,我为 x 按钮提供了一个 id,并为容器元素提供了一个 id。
我想逐行读取文件,并且每一行可能都有很多字符。 这个版本的readline效果很好 func readLine(r *bufio.Reader) ([]byte, error) { var (
我有一个网站,每次用户登录或注销时,我都会将其保存到文本文件中。 如果不存在,我的代码在附加数据或创建文本文件时不起作用。这是示例代码 $myfile = fopen("logs.txt", "wr"
我正在尝试使用 typescript 和 Formik 创建一个自定义输入字段。我可以就完成以下代码的最佳方式获得一些帮助吗?我需要添加额外的 Prop 标签和名称......我已经坚持了一段时间,希
我有一个字符串 big_html,我想将它添加到某个 div 中。我观察到以下方面的性能差异: $('#some-div').append( big_html ); // takes about 10
如何使用 FormData 创建以下结果 ------WebKitFormBoundaryOmz20xyMCkE27rN7 Content-Disposition: form-data; name="
有没有办法附加 jQuery 事件处理程序,以便在任何先前附加的事件处理程序之前触发该处理程序?我遇到了this article ,但代码不起作用,因为事件处理程序不再存储在数组中,而这正是他的代码所
我正在开发一个需要网络登录的 iPhone 应用程序。像往常一样我打电话 [[UIApplication sharedApplication] openURL:loginURL]; 这将关闭应用程序并
我想开发一个仅针对特定域激活的扩展。 我不希望它在不浏览此特定域时出现在浏览器菜单中。 有可能这样做吗? 最佳答案 可能:对于菜单,您可以添加一个弹出窗口侦听器,用于检查当前加载的URL(docs f
这段 JavaScript 代码 function writeCookie(CookieName, CookieValue, CookieDuration) { var expiration
我正在使用 Handlebars 来渲染使用ajax从本地服务器获得的信息。我的 HTML 看起来像: {{#each Tabs}}
我尝试了以下代码,但当输入框中没有数据时它不会通知。当我直接添加此内容(不附加)时,它会起作用。我在这里做错了什么 var output = "\n"+ "\n"+
我是一名优秀的程序员,十分优秀!