/// <summary> /// This is a static method to run a job whose args are passed in /// By default, /// a) The job will be run continuously in a while loop. Could be overridden using 'once' argument /// b) The sleep duration between each run when running continuously is 5000 milliSeconds. Could be overridden using '-Sleep' argument /// </summary> /// <param name="job">Job to run</param> /// <param name="commandLineArgs">Args contains args to the job runner like (dbg, once and so on) and for the job itself</param> /// <returns></returns> public static async Task Run(JobBase job, string[] commandLineArgs) { if (commandLineArgs.Length > 0 && string.Equals(commandLineArgs[0], "-dbg", StringComparison.OrdinalIgnoreCase)) { commandLineArgs = commandLineArgs.Skip(1).ToArray(); Debugger.Launch(); } bool consoleLogOnly = false; if (commandLineArgs.Length > 0 && string.Equals(commandLineArgs[0], "-ConsoleLogOnly", StringComparison.OrdinalIgnoreCase)) { commandLineArgs = commandLineArgs.Skip(1).ToArray(); consoleLogOnly = true; } // Set JobTraceListener. This will be done on every job run as well SetJobTraceListener(job, consoleLogOnly); try { Trace.TraceInformation("Started..."); // Get the args passed in or provided as an env variable based on jobName as a dictionary of <string argName, string argValue> var jobArgsDictionary = JobConfigurationManager.GetJobArgsDictionary(job.JobTraceListener, commandLineArgs, job.JobName); bool runContinuously = !JobConfigurationManager.TryGetBoolArgument(jobArgsDictionary, JobArgumentNames.Once); int? sleepDuration = JobConfigurationManager.TryGetIntArgument(jobArgsDictionary, JobArgumentNames.Sleep); // Setup the job for running JobSetup(job, jobArgsDictionary, ref sleepDuration); // Run the job loop await JobLoop(job, runContinuously, sleepDuration.Value, consoleLogOnly); } catch (AggregateException ex) { var innerEx = ex.InnerExceptions.Count > 0 ? ex.InnerExceptions[0] : null; if (innerEx != null) { Trace.TraceError("[FAILED]: " + innerEx); } else { Trace.TraceError("[FAILED]: " + ex); } } catch (Exception ex) { Trace.TraceError("[FAILED]: " + ex); } // Call FlushAll here. This is VERY IMPORTANT // Exception message(s) when the job faults are still in the queue. Need to be flushed // Also, when the job is only run once, FlushAll is important again job.JobTraceListener.Close(); }
private static void SetJobTraceListener(JobBase job, bool consoleLogOnly) { if(consoleLogOnly) { job.SetJobTraceListener(new JobTraceListener()); Trace.TraceWarning("You have chosen not to log messages to Azure blob storage. Note that this is NOT recommended"); } else { job.SetJobTraceListener(new AzureBlobJobTraceListener(job.JobName)); } }
private static void JobSetup(JobBase job, IDictionary<string, string> jobArgsDictionary, ref int? sleepDuration) { if (JobConfigurationManager.TryGetBoolArgument(jobArgsDictionary, "dbg")) { throw new ArgumentException("-dbg is a special argument and should be the first argument..."); } if (JobConfigurationManager.TryGetBoolArgument(jobArgsDictionary, "ConsoleLogOnly")) { throw new ArgumentException("-ConsoleLogOnly is a special argument and should be the first argument (can be the second if '-dbg' is used)..."); } if (sleepDuration == null) { Trace.TraceWarning("SleepDuration is not provided or is not a valid integer. Unit is milliSeconds. Assuming default of 5000 ms..."); sleepDuration = 5000; } // Initialize the job once with everything needed. // JobTraceListener(s) are already initialized if (!job.Init(jobArgsDictionary)) { // If the job could not be initialized successfully, STOP! Trace.TraceError("Exiting. The job could not be initialized successfully with the arguments passed"); } }
private static async Task<string> JobLoop(JobBase job, bool runContinuously, int sleepDuration, bool consoleLogOnly) { // Run the job now var stopWatch = new Stopwatch(); bool success; while (true) { Trace.WriteLine("Running " + (runContinuously ? " continuously..." : " once...")); Trace.WriteLine("SleepDuration is " + PrettyPrintTime(sleepDuration)); Trace.WriteLine("Job run started..."); // Force a flush here to create a blob corresponding to run indicating that the run has started job.JobTraceListener.Flush(); stopWatch.Restart(); success = await job.Run(); stopWatch.Stop(); Trace.WriteLine("Job run ended..."); Trace.TraceInformation("Job run took {0}", PrettyPrintTime(stopWatch.ElapsedMilliseconds)); if(success) { Trace.TraceInformation(_jobSucceeded); } else { Trace.TraceWarning(_jobFailed); } // At this point, FlushAll is not called, So, what happens when the job is run only once? // Since, FlushAll is called right at the end of the program, this is no issue if (!runContinuously) { break; } // Wait for <sleepDuration> milliSeconds and run the job again Trace.WriteLine(string.Format("Will sleep for {0} before the next Job run", PrettyPrintTime(sleepDuration))); // Flush All the logs for this run job.JobTraceListener.Close(); // Use Console.WriteLine when you don't want it to be logged in Azure blobs Console.WriteLine("Sleeping for {0} before the next job run", PrettyPrintTime(sleepDuration)); Thread.Sleep(sleepDuration); SetJobTraceListener(job, consoleLogOnly); } return success ? _jobSucceeded : _jobFailed; }
private static async Task <int> Run(JobBase job, string[] commandLineArgs, bool?runContinuously) { if (commandLineArgs.Length > 0 && string.Equals(commandLineArgs[0], "-" + JobArgumentNames.Dbg, StringComparison.OrdinalIgnoreCase)) { commandLineArgs = commandLineArgs.Skip(1).ToArray(); Debugger.Launch(); } // Configure logging before Application Insights is enabled. // This is done so, in case Application Insights fails to initialize, we still see output. var loggerFactory = ConfigureLogging(job); int exitCode; try { _logger.LogInformation("Started..."); // Get the args passed in or provided as an env variable based on jobName as a dictionary of <string argName, string argValue> var jobArgsDictionary = JobConfigurationManager.GetJobArgsDictionary( ServiceContainer, loggerFactory.CreateLogger(typeof(JobConfigurationManager)), commandLineArgs); // Determine job and instance name, for logging. var jobName = job.GetType().Assembly.GetName().Name; var instanceName = JobConfigurationManager.TryGetArgument(jobArgsDictionary, JobArgumentNames.InstanceName) ?? jobName; TelemetryConfiguration.Active.TelemetryInitializers.Add(new JobNameTelemetryInitializer(jobName, instanceName)); // Setup logging if (!ApplicationInsights.Initialized) { string instrumentationKey = JobConfigurationManager.TryGetArgument(jobArgsDictionary, JobArgumentNames.InstrumentationKey); if (!string.IsNullOrWhiteSpace(instrumentationKey)) { ApplicationInsights.Initialize(instrumentationKey); } } // Configure our logging again with Application Insights initialized. loggerFactory = ConfigureLogging(job); var hasOnceArgument = JobConfigurationManager.TryGetBoolArgument(jobArgsDictionary, JobArgumentNames.Once); if (runContinuously.HasValue && hasOnceArgument) { _logger.LogWarning( $"This job is designed to {(runContinuously.Value ? "run continuously" : "run once")} so " + $"the -{JobArgumentNames.Once} argument is {(runContinuously.Value ? "ignored" : "redundant")}."); } runContinuously = runContinuously ?? !hasOnceArgument; var reinitializeAfterSeconds = JobConfigurationManager.TryGetIntArgument(jobArgsDictionary, JobArgumentNames.ReinitializeAfterSeconds); var sleepDuration = JobConfigurationManager.TryGetIntArgument(jobArgsDictionary, JobArgumentNames.Sleep); // sleep is in milliseconds if (!sleepDuration.HasValue) { sleepDuration = JobConfigurationManager.TryGetIntArgument(jobArgsDictionary, JobArgumentNames.Interval); if (sleepDuration.HasValue) { sleepDuration = sleepDuration.Value * 1000; // interval is in seconds } } if (!sleepDuration.HasValue) { if (runContinuously.Value) { _logger.LogInformation("SleepDuration is not provided or is not a valid integer. Unit is milliSeconds. Assuming default of 5000 ms..."); } sleepDuration = 5000; } else if (!runContinuously.Value) { _logger.LogWarning( $"The job is designed to run once so the -{JobArgumentNames.Sleep} and " + $"-{JobArgumentNames.Interval} arguments are ignored."); } if (!reinitializeAfterSeconds.HasValue) { _logger.LogInformation( $"{JobArgumentNames.ReinitializeAfterSeconds} command line argument is not provided or is not a valid integer. " + "The job will reinitialize on every iteration"); } else if (!runContinuously.Value) { _logger.LogWarning( $"The job is designed to run once so the -{JobArgumentNames.ReinitializeAfterSeconds} " + $"argument is ignored."); } // Ensure that SSLv3 is disabled and that Tls v1.2 is enabled. ServicePointManager.SecurityProtocol &= ~SecurityProtocolType.Ssl3; ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12; // Run the job loop exitCode = await JobLoop(job, runContinuously.Value, sleepDuration.Value, reinitializeAfterSeconds, jobArgsDictionary); } catch (Exception ex) { exitCode = 1; _logger.LogError("Job runner threw an exception: {Exception}", ex); } Trace.Close(); TelemetryConfiguration.Active.TelemetryChannel.Flush(); return(exitCode); }
/// <summary> /// This is a static method to run a job whose args are passed in /// By default, /// a) The job will be run the job once, irrespective of the 'once' argument. /// b) The sleep duration between each run when running continuously is 5000 milliSeconds. Could be overridden using '-Sleep' argument /// </summary> /// <param name="job">Job to run</param> /// <param name="commandLineArgs">Args contains args to the job runner like (dbg, once and so on) and for the job itself</param> /// <returns>The exit code, where 0 indicates success and non-zero indicates an error.</returns> public static async Task <int> RunOnce(JobBase job, string[] commandLineArgs) { return(await Run(job, commandLineArgs, runContinuously : false)); }
private static async Task <int> JobLoop( JobBase job, bool runContinuously, int sleepDuration, int?reinitializeAfterSeconds, IDictionary <string, string> jobArgsDictionary) { // Run the job now var stopWatch = new Stopwatch(); Stopwatch timeSinceInitialization = null; int exitCode; while (true) { _logger.LogInformation("Running {RunType}", (runContinuously ? " continuously..." : " once...")); _logger.LogInformation("SleepDuration is {SleepDuration}", PrettyPrintTime(sleepDuration)); _logger.LogInformation("Job run started..."); var initialized = false; stopWatch.Restart(); try { if (ShouldInitialize(reinitializeAfterSeconds, timeSinceInitialization)) { job.Init(ServiceContainer, jobArgsDictionary); timeSinceInitialization = Stopwatch.StartNew(); } initialized = true; await job.Run(); exitCode = 0; _logger.LogInformation(JobSucceeded); } catch (Exception e) { exitCode = 1; _logger.LogError("{JobState}: {Exception}", initialized ? JobFailed : JobUninitialized, e); } finally { _logger.LogInformation("Job run ended..."); stopWatch.Stop(); _logger.LogInformation("Job run took {RunDuration}", PrettyPrintTime(stopWatch.ElapsedMilliseconds)); } if (!runContinuously) { // It is ok that we do not flush the logs here. // Logs will be flushed at the end of Run(). break; } // Wait for <sleepDuration> milliSeconds and run the job again _logger.LogInformation("Will sleep for {SleepDuration} before the next Job run", PrettyPrintTime(sleepDuration)); await Task.Delay(sleepDuration); } return(exitCode); }
/// <summary> /// This is a static method to run a job whose args are passed in /// By default, /// a) The job will be run continuously in a while loop. Could be overridden using 'once' argument /// b) The sleep duration between each run when running continuously is 5000 milliSeconds. Could be overridden using '-Sleep' argument /// </summary> /// <param name="job">Job to run</param> /// <param name="commandLineArgs">Args contains args to the job runner like (dbg, once and so on) and for the job itself</param> /// <returns></returns> public static async Task Run(JobBase job, string[] commandLineArgs) { if (commandLineArgs.Length > 0 && string.Equals(commandLineArgs[0], "-" + JobArgumentNames.Dbg, StringComparison.OrdinalIgnoreCase)) { commandLineArgs = commandLineArgs.Skip(1).ToArray(); Debugger.Launch(); } // Configure logging before Application Insights is enabled. // This is done so, in case Application Insights fails to initialize, we still see output. var loggerFactory = ConfigureLogging(job); try { _logger.LogInformation("Started..."); // Get the args passed in or provided as an env variable based on jobName as a dictionary of <string argName, string argValue> var jobArgsDictionary = JobConfigurationManager.GetJobArgsDictionary(loggerFactory.CreateLogger(typeof(JobConfigurationManager)), commandLineArgs, job.JobName, (ISecretReaderFactory)ServiceContainer.GetService(typeof(ISecretReaderFactory))); // Setup logging if (!ApplicationInsights.Initialized) { string instrumentationKey = JobConfigurationManager.TryGetArgument(jobArgsDictionary, JobArgumentNames.InstrumentationKey); if (!string.IsNullOrWhiteSpace(instrumentationKey)) { ApplicationInsights.Initialize(instrumentationKey); } } // Configure our logging again with Application Insights initialized. loggerFactory = ConfigureLogging(job); var runContinuously = !JobConfigurationManager.TryGetBoolArgument(jobArgsDictionary, JobArgumentNames.Once); var sleepDuration = JobConfigurationManager.TryGetIntArgument(jobArgsDictionary, JobArgumentNames.Sleep); // sleep is in milliseconds if (!sleepDuration.HasValue) { sleepDuration = JobConfigurationManager.TryGetIntArgument(jobArgsDictionary, JobArgumentNames.Interval); if (sleepDuration.HasValue) { sleepDuration = sleepDuration.Value * 1000; // interval is in seconds } } if (sleepDuration == null) { _logger.LogInformation("SleepDuration is not provided or is not a valid integer. Unit is milliSeconds. Assuming default of 5000 ms..."); sleepDuration = 5000; } // Ensure that SSLv3 is disabled and that Tls v1.2 is enabled. ServicePointManager.SecurityProtocol &= ~SecurityProtocolType.Ssl3; ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12; // Run the job loop await JobLoop(job, runContinuously, sleepDuration.Value, jobArgsDictionary); } catch (Exception ex) { _logger.LogError("Job runner threw an exception: {Exception}", ex); } }
/// <summary> /// This is a static method to run a job whose args are passed in /// By default, /// a) The job will be run continuously in a while loop. Could be overridden using 'once' argument /// b) The sleep duration between each run when running continuously is 5000 milliSeconds. Could be overridden using '-Sleep' argument /// </summary> /// <param name="job">Job to run</param> /// <param name="commandLineArgs">Args contains args to the job runner like (dbg, once and so on) and for the job itself</param> /// <returns></returns> public static async Task Run(JobBase job, string[] commandLineArgs) { await Run(job, commandLineArgs, runContinuously : null); }