gpt4 book ai didi

aem - JCR中的附加文件

转载 作者:行者123 更新时间:2023-12-04 20:35:17 26 4
gpt4 key购买 nike

我要运行一个可能需要几分钟甚至几小时的过程。为了跟踪此类运行的历史记录,我为每次运行创建了一个自定义类型的节点,并在其中存储了相关的流程元数据。另外,我想将日志文件存储在这样的节点下。与将日志文件与进程meta分开存储在磁盘上相比,这似乎是一种更一致,更方便的方法。

现在,nt:file节点类型本身具有一个具有jcr:content属性的jcr:data子节点,该属性允许我存储二进制内容。对于一次或不经常更改文件内容来说,这是很好的选择。

但是,我将不断向该文件追加新内容,此外,还要在单独的线程中轮询其内容(以跟踪进度)。

面对javax.jcr.ValueFactoryjavax.jcr.Binary的JCR API似乎并不真正支持这种方法,我宁愿每次添加一个文件时都一遍又一遍地重写该文件(或更准确地说是二进制属性)。单行日志。我担心性能。

我在文档中搜索了一些工具,这些工具可以打开该文件的输出流,并定期将更改从该流刷新到JCR,但似乎没有可用的东西。

那么,还有什么比普通的javax.jcr.ValueFactoryjavax.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提供了已配置的绑定slf4jLogBack,并导出了所有必需的LogBack依赖关系,以供在自定义捆绑包中使用。缺点:日志和任务元数据分离。用户应了解磁盘上的日志文件位置。


从选项1开始,我然后意识到日志条目的数量可能会扩展到成千上万-罕见,但可能的情况。因此,最终决定选择选项4。

万一任何人都会面临类似的任务,我将在此处发布选项4的实现细节,因为它乍一看并不那么琐碎。

我正在使用AEM 6.2(引擎盖下的Felix-Jackrabbit-Sling),并且我希望运行每个迁移任务-本质上只是一个单独的线程-用特殊名称创建它自己的日志文件-该迁移过程的唯一标识符。

现在,Sling本身允许您通过org.apache.sling.commons.log.LogManager.factory.config
OSGi配置定义多个日志配置。但是,对于这种情况,这些日志配置太简单了-您无法在LogBack中创建SiftingAppender所谓的 -日志附加程序的特殊情况,它将在每个线程中实例化特定记录器的附加程序,而不是在整个应用程序范围内实例化附加程序。您无法指示LogBack使用OSGi配置为每个线程创建文件的单词。

因此,从逻辑上考虑,您可能希望在运行时以编程方式获取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/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com