On Thu, 2017-09-14 at 01:15 +0000, John Logan wrote:
> I got this working and thought I'd follow up with what I did in case
> anyone else needs this sort of thing.

Looks interesting, thanks for sharing.

Robert

> 
> 
> I used SiftingAppender pretty much as shown in any of the examples
> one can find online.  I put the following logback.xml in my
> sling.home, and pointed the OSGI configuration variable
> org.apache.sling.commons.log.configurationFile to it:
> 
> 
> <configuration>
>   <appender name="SIFT"
> class="ch.qos.logback.classic.sift.SiftingAppender">
>     <discriminator>
>       <key>jobId</key>
>       <defaultValue>/var/log/sling/error.log</defaultValue>
>     </discriminator>
>     <sift>
>       <appender name="FILE-${jobId}"
> class="ch.qos.logback.core.FileAppender">
>         <file>${logPath}</file>
>         <layout class="ch.qos.logback.classic.PatternLayout">
>           <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -
> %msg%n</pattern>
>         </layout>
>       </appender>
>     </sift>
>   </appender>
> 
>   <root level="info">
>     <appender-ref ref="SIFT" />
>   </root>
> </configuration>
> 
> 
> An abstract wrapper implementation of JobExecutor sets up the MDC to
> conform to what's expected in the logback.xml, and provides a few
> other niceties.  Replace the SlingResourceProvider/StorageNode stuff
> with whatever you use to access files and Sling nodes; it's just a
> simple abstraction layer that we happen to be using.
> 
> 
> package com.xyz.content.sling.processor;
> 
> import java.io.BufferedReader;
> import java.io.IOException;
> import java.nio.charset.StandardCharsets;
> import java.nio.file.Files;
> import java.nio.file.Path;
> import java.nio.file.Paths;
> 
> import org.apache.sling.api.resource.LoginException;
> import org.apache.sling.api.resource.ResourceResolver;
> import org.apache.sling.api.resource.ResourceResolverFactory;
> import org.apache.sling.event.jobs.Job;
> import org.apache.sling.event.jobs.consumer.JobExecutionContext;
> import org.apache.sling.event.jobs.consumer.JobExecutionResult;
> import org.apache.sling.event.jobs.consumer.JobExecutor;
> import org.slf4j.Logger;
> import org.slf4j.LoggerFactory;
> import org.slf4j.MDC;
> 
> import com.xyz.content.sling.storage.SlingResourceProvider;
> import com.xyz.storage.StorageNode;
> 
> /**
>  * Sling job executor with build job logging.
>  *
>  * This class wraps build processing with job-specific log
> management.
>  * Job submitters need to configure the following job properties
>  * prior to submitting a job:
>  * * jobId - A symbolic identifier for the job.  This value must be
>  *           unique throughout the life of the job.  Subclasses
>  *           may use this value as a job name for submitting
>  *           and monitoring SLURM jobs.
>  * * logPath - The pathname to the build log file.
>  * * damLogPath - If specified, the DAM resource to which
>  *                the log output should be copied upon build
> completion.
>  * * clearLog - An option boolean parameter which, when true,
>  *              removes the build log file if it exists prior
>  *              to commencing the build.
>  *
>  * @author john
>  *
>  */
> public abstract class BuildJobExecutor implements JobExecutor {
>     private static Logger LOG =
> LoggerFactory.getLogger(BuildJobExecutor.class);
> 
>     /**
>      * Retrieve a resource resolver factory for build processing.
>      *
>      * @return  the resource resolver factory
>      */
>     protected abstract ResourceResolverFactory getResolverFactory();
> 
>     /**
>      * Subclass-specific build processing method.
>      *
>      * @param job
>      * @param context
>      * @param resolver
>      * @return  the result of build processing
>      */
>     protected abstract JobExecutionResult build(Job job,
> JobExecutionContext context, ResourceResolver resolver);
> 
>     @Override
>     public JobExecutionResult process(Job job, JobExecutionContext
> context) {
>         //
>         //  Prepare for the log directory and file.
>         //
>         final String jobId = job.getProperty("jobId", String.class);
>         final Path logPath = Paths.get(job.getProperty("logPath",
> String.class));
>         final Path logParentPath = logPath.getParent();
>         if (logParentPath != null) {
>             try {
>                 Files.createDirectories(logParentPath);
>             }
>             catch (final IOException e) {
>                 return handleError(context, "Unable to create log
> directory " + logParentPath);
>             }
>         }
> 
>         if (Boolean.TRUE.equals(job.getProperty("resetLog"))) {
>             try {
>                 Files.deleteIfExists(logPath);
>             }
>             catch (final IOException e) {
>                 return handleError(context, "Unable to clear log file
> " + logPath);
>             }
>         }
> 
>         LOG.info("Starting build job with ID " + jobId);
>         ResourceResolver resolver;
>         try {
>             resolver =
> getResolverFactory().getServiceResourceResolver(null);
>         }
>         catch (final LoginException e) {
>             return handleError(context, "Unable get build job
> resource resolver, check cbservice user configuration.");
>         }
> 
>         //
>         //  Perform the build operation.  Logging to the job-specific 
> log file starts here.
>         //
>         MDC.put("jobId", jobId);
>         MDC.put("logPath", logPath.toString());
>         try {
>             final JobExecutionResult result = build(job, context,
> resolver);
>             if (job.getProperty("damLogPath") != null) {
>                 final Path damLogPath =
> Paths.get(job.getProperty("damLogPath", String.class));
>                 saveLog(resolver, logPath, damLogPath);
>             }
>             return result;
>         }
>         catch (final Throwable e) {
>             LOG.error("Build job failed with an exception.", e);
>             return handleError(context, "Build job failed with an
> exception: " + e.getMessage());
>         }
>         finally {
>             MDC.remove("jobId");
>             MDC.remove("logPath");
>             resolver.close();
>         }
>     }
> 
>     private JobExecutionResult handleError(JobExecutionContext
> context, String message) {
>         LOG.error(message);
>         return context.result().message(message).cancelled();
>     }
> 
>     private void saveLog(ResourceResolver resolver, Path logPath,
> Path damLogPath) {
>         try (BufferedReader reader = Files.newBufferedReader(logPath,
> StandardCharsets.UTF_8)) {
>             final SlingResourceProvider storageProvider = new
> SlingResourceProvider();
>             storageProvider.setResolver(resolver);
>             final StorageNode damLogNode =
> storageProvider.get(damLogPath);
>             damLogNode.copyFromReader(reader,
> StandardCharsets.UTF_8);
>             resolver.commit();
>         }
>         catch (final IOException e) {
>             LOG.error("Unable to move build log " + logPath + " to
> DAM resource " + damLogPath, e);
>         }
>     }
> }
> 
> 
> 
> ________________________________
> From: Robert Munteanu <romb...@apache.org>
> Sent: Friday, September 8, 2017 1:13:02 AM
> To: users@sling.apache.org
> Subject: Re: Directing Sling job logging output to separate files?
> 
> Hi John,
> 
> On Fri, 2017-09-08 at 05:28 +0000, John Logan wrote:
> > Hi,
> > 
> > 
> > I'm using the Sling job manager to handle some long running tasks,
> > and would like to direct the log output for each job to its own
> > file
> > at a job-specific path.  Is there a straightforward way to achieve
> > this?
> 
> If your jobs use separate loggers, you can achieve that either by:
> 
> - manually creating loggers and appenders via http://localhost:8080/s
> ys
> tem/console/slinglog/
> - adding specific loggers/appenders to the provisioning model
> 
> There might be a way of adding those at runtime using the logback
> APIs,
> but I haven't tried it before.
> 
> Robert

Reply via email to