/// <summary> /// Creates a smart detector loader object for the child process, based on the command line arguments received from the parent process. /// </summary> /// <param name="args">The command line arguments.</param> /// <param name="tracerForLoader">The tracer to use</param> /// <returns>The smart detector loader instance</returns> public ISmartDetectorLoader CreateLoaderForChildProcess(string[] args, ITracer tracerForLoader) { // Get the temp folder name from the arguments and create the loader ChildProcessArguments arguments = ChildProcessArguments.FromCommandLineArguments(args); return(new SmartDetectorLoader(arguments.TempFolder, tracerForLoader)); }
/// <summary> /// Creates a tracer object for the child process, based on the command line arguments received from the parent process. /// </summary> /// <param name="args">The command line arguments.</param> /// <returns>The tracer instance</returns> public ITracer CreateTracerForChildProcess(string[] args) { ChildProcessArguments arguments = ChildProcessArguments.FromCommandLineArguments(args); // Get sessionId from the arguments and create tracer ITracer tracerForChildProcess = TracerFactory.Create(arguments.SessionId, null, true); // Get custom dimensions from the arguments - do not override existing properties IReadOnlyDictionary <string, string> existingProperties = tracerForChildProcess.GetCustomProperties(); foreach (var kv in arguments.CustomTracerProperties) { if (!existingProperties.ContainsKey(kv.Key)) { tracerForChildProcess.AddCustomProperty(kv.Key, kv.Value); } } return(tracerForChildProcess); }
/// <summary> /// Runs the child process task. This method reads and validates the command line /// arguments, starts listening to the parent process (for cancellation/termination), /// runs the specified function, and returns the result to the parent process. /// Should be called by the child process when it starts. /// </summary> /// <typeparam name="TInput">The child process input type</typeparam> /// <typeparam name="TOutput">The child process output type</typeparam> /// <param name="args">The command line arguments</param> /// <param name="functionToRun">The function to run</param> /// <param name="exceptionToExitCodeConverter">A function used to convert an exception thrown by the process to the exit code to return to the parent.</param> /// <param name="waitAfterFlush">Whether to wait after flushing the telemetry, to allow all traces to be sent.</param> /// <exception cref="ArgumentException">The wrong number of arguments was provided</exception> /// <returns>A <see cref="Task"/>, running the specified function and listening to the parent, returning the exit code to be returned from the process</returns> public async Task <int> RunAndListenToParentAsync <TInput, TOutput>( string[] args, Func <TInput, CancellationToken, Task <TOutput> > functionToRun, Func <Exception, int> exceptionToExitCodeConverter, bool waitAfterFlush = true) where TOutput : class { ChildProcessArguments arguments = ChildProcessArguments.FromCommandLineArguments(args); string pipeParentToChildHandle = arguments.PipeParentToChildHandle; string pipeChildToParentHandle = arguments.PipeChildToParentHandle; try { using (PipeStream pipe = new AnonymousPipeClientStream(PipeDirection.In, pipeParentToChildHandle)) { CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); try { // Read the input var input = await this.ReadFromStream <TInput>(pipe, cancellationTokenSource.Token); // Start listening to parent process - run the listeners in separate tasks // We should not wait on these tasks, since: // * If any of these tasks fail, it will requests cancellation, and it is enough to wait on the main method and // let it handle cancellation gracefully // * The cancellation listener is blocking, and cannot be canceled (anonymous pipes do not support cancellation). // Waiting on it will block the current thread. #pragma warning disable 4014 this.ParentLiveListenerAsync(pipe, cancellationTokenSource); this.ParentCancellationListenerAsync(pipe, cancellationTokenSource); #pragma warning restore 4014 // Run the main function TOutput output = await functionToRun(input, cancellationTokenSource.Token); // Write the output back to the parent await this.WriteChildProcessResult(pipeChildToParentHandle, output); // Success - return zero for exit code return(0); } catch (Exception e) { // If the exception is due to cancellation, than return dedicated error codes if (cancellationTokenSource.IsCancellationRequested) { await this.WriteChildProcessResult(pipeChildToParentHandle, "Child process was canceled by the parent"); return((int)HttpStatusCode.InternalServerError); } return(await this.HandleChildProcessException(e, pipeChildToParentHandle, exceptionToExitCodeConverter)); } finally { // Cancel the token to stop the listener tasks cancellationTokenSource.Cancel(); } } } catch (Exception e) { return(await this.HandleChildProcessException(e, pipeChildToParentHandle, exceptionToExitCodeConverter)); } finally { this.tracer.Flush(); if (waitAfterFlush) { await Task.Delay(1000 * 5); } } }