- 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/
我使用一个应用程序,我们可以在其中操作 JCR 节点,通常您可以重命名、删除、删除或移动它们。 使用 JCR API,我可以访问我的存储库中的节点并按照它们在 JCR 中的相同顺序返回一组节点。 我希
我只是想知道 jcr:primaryType 和 jcr:mixinTypes 之间有什么区别,以及为什么使用 jcr:mixinTypes ?这意味着什么?如果您检查 crx 中的/content/
使用 JCR-SQL2 查询时,我注意到 CONTAINS运算符查找节点 没有与条件中的字符串完全相同的字符串。 例子 以下查询: SELECT * FROM [nt:base] AS s WHERE
我想在 CQ 5.6 (AEM) 实例中包含的 JCR 存储库中搜索包含给定字符串 xxx 的所有 .jsp 文件。作为 JCR 对 XPath(或 JCR-SQL2)的解释的新手,我在表达查询方面不
需要帮助获取节点属性的 string[] 值? 例如,我有一个节点图像,它具有 String[] 类型的属性“references”。我需要获取数组的第一个值。 谢谢 最佳答案 从节点,您可以获取re
我想使用一个或多个值作为多值属性的输入参数在内容存储库中进行搜索 类似于:找到所有主类型为“nt:unstructured”的节点,其属性“multiprop”(多值属性)包含值“one”和“two”
Modeshape 文档的第 7.1.6 节说“您的应用程序现在可以使用标准 JCR 2.0 API 创建和删除工作区。” JCR 2.0 文档说使用 Workspace.createWorkspac
我正在构建一个 jcr 查询并从存储库接收数据。这是我的代码: String queryString = "SELECT * FROM public:hours"; try { // ge
我将 Jackrabbit 与 JCR 一起使用,如何更改节点的顺序? 最佳答案 有很多方法; 1) 创建一个带有“orderable”选项的父节点类型。 [ns:NodeType] > ns:Par
如何确定 JCR 属性是否为多阀属性?我尝试对该属性调用 getType(),但如果它是字符串或字符串数组,则返回相同的值。 谢谢! 最佳答案 使用 Property 的 isMultiple()
我的 AEM 项目中有一段配置需要简化。 配置可以由两组用户更改。一个需要对一组参数进行精细控制,而另一个只关心单个参数。 我没有编写自定义 Ext JS 插件来隐藏/显示字段并添加一个额外的字段来在
我有以下代码将“rose.gif”插入到roseNode中。但是如何从存储库中检索文件? Node roseNode = session.getRootNode().getNode("wiki
我注意到,当我为下拉列表设置我的 defaultValue 时,尽管当我第一次将组件添加到页面时在下拉列表中正确选择了它,它不会将 defaultValue 写入相应的 JCR,直到我编辑组件并保存它
对于我工作的 CQ5 环境,我们有一个发布服务器场。这些服务器上的某些内容受到限制,因此只有属于特定组的用户才能看到这些内容。我想编写要保护的文件夹(节点)的权限设置脚本,这样我就不必使用内容资源管理
如何在以下站点结构中配置 jcr:language 属性,例如在节点 (/content/myapp/uk/en/jcr:content)? { "content": { "m
对于我工作的 CQ5 环境,我们有一个发布服务器场。这些服务器上的某些内容受到限制,因此只有属于特定组的用户才能看到这些内容。我想编写要保护的文件夹(节点)的权限设置脚本,这样我就不必使用内容资源管理
我注意到 javax.jcr 的 javadocs 丢失了。据我所知,所有存储此内容的页面现在都显示 404。 示例: link1 link2 link3 有相关消息吗?还有其他地方可以找到 java
我有一些测试代码,用于检查 JCR 节点上的属性值,对被测系统执行外部操作,然后检查同一 JCR 节点上相同属性的值以检查它是否正确实际上已经改变了。 我遇到的问题是第一个查询之后的所有后续查询都返回
我正在将 Magnolia 用于我的一个项目,并在其中创建了一个 REST 端点。其余端点将从我的自定义工作区读取节点并将结果返回给客户端。非常简单。 如果我在 Magnolia JCR Utils
我很难找到使用 SQL2 查询语言比较日期的方法。 我希望能够做这样的事情: SELECT p.* FROM [nt:base] AS p WHERE EXTRACT(YEAR FROM p.[som
我是一名优秀的程序员,十分优秀!