Search Results for

    Show / Hide Table of Contents

    Debugging

    Logging Output

    The Logging output is the main source of information when the ETL application behaves unexpectedly, both during development, and when deployed to test and production servers.

    • If there is an issue with the logging itself, check the NLog internal log file, and if needed also enable its Info or more verbose log events. Also consider temporarily removing any "nlog.config" file, to instead get the actionETL default logging configuration.
    • If the application crashes or hangs, and log events seems to be missing at the end, consider switching from asynchronous and buffered logging, to synchronous and unbuffered logging, while troubleshooting.
    • If the Info level information is insufficient, consider switching to Debug level, which is normally OK also in production.
      • The Trace level is even more verbose, but is normally not OK to enable globally in production. When using it, consider only enabling it for the specific worker or port that needs more verbose logging.
    • If the issue is directly related to your code (e.g. your custom worker, or callbacks you've added to the out of box workers), consider logging additional messages from your code.
    • Also consider ways to improve logging by changing the implementation, e.g.:
      • Add or change workers, e.g. to take action when an earlier worker fails
      • Increase logging from dataflow, e.g. by adding additional columns to rows, as well as using error output ports (which can dump row contents).

    Using the Debugger

    When using the logging output is not sufficient for resolving the issue, running a debugger on the application is often a good choice. This is most commonly done as part of development, but you can also attach the debugger to an already running process, both locally and remotely (e.g. even in production).

    When stopped in the debugger, you can traverse the worker system hierarchy and dataflow ports using the following properties:

    • WorkerParent.Children (a property on workers and the worker system)
    • WorkerBase.Parent
    • WorkerBase.Outputs and any typed output properties, often called Output
    • WorkerBase.Inputs and any typed input properties, often called Input
    • OutputPortBase.Worker
    • InputPort.Worker

    While troubleshooting, consider using KeepChildrenLevels to retain more than the top-most level of workers.

    Note

    A Debug build is the preferred and easiest to debug, since optimizations are turned off, and it by default includes your source code.

    You can also debug a Release build, although optimizations can make it trickier, with many temporary variables removed from your code etc. Dataflow Port Breakpoints work well even with Release builds. For some scenarios, consider temporarily suppressing JIT optimization.

    Breakpoints in Your Code

    You can step through your code in a debugger, and also add and run to breakpoints (F9 and F5 respectively in Visual Studio) in your code. This is particularly useful for checking the application:

    1. Before creating the worker system
    2. Before starting the worker system
    3. During worker creation
    4. During execution of callbacks you have added, including dataflow callbacks
    5. After the worker system has completed, to check the properties of completed workers
      • Sending dataflow rows to a CollectionTarget, breaking in the debugger after the worker system has finished and checking CollectionTarget.Rows is simple and useful. More advanced scenarios are covered below.

    Here's an example with multiple breakpoints added:

    Debugging Breakpoints

    Note

    To view the dataflow rows when your dataflow doesn't include a suitable worker to put a breakpoint in, either temporarily add a RowActionTransaform1 to the dataflow as per above, or use Dataflow Port Breakpoints instead.

    WorkerParent State Breakpoints

    You can't set a normal breakpoint for when a worker runs or completes, since this is handled inside the actionETL code.

    Instead use the WorkerParent.DebugCommands property to instruct actionETL to launch or break a debugger when a worker or the worker system changes status. You can view current worker and worker hierarchy, and where supported even edit their properties. This can be very useful to check the exact configuration of a worker before it runs, and after it completes.

    If the next example is run with debugging, the debugger will hit a breakpoint for:

    • All state changes of the "check trigger" worker (i.e. Created, Running, and any completed state)
    • The Running state of the "target" worker
    // using actionETL;
    
    private sealed class MyRow
    {
        public string Description;
        public int Metric;
    }
    
    var workerSystem = new WorkerSystem()
        .Root(ws =>
        {
            var exists = new FileExistsWorker(ws, "check trigger", "myfile.trg", false);
            exists.DebugCommands = DebugWorkerParentCommands.BreakOnAll;
    
            var source = new RepeatRowsSource<MyRow>(ws, "source", () => exists.IsSucceeded,
                2000, new MyRow { Description = "The answer is", Metric = 42 }
                );
    
            var target = source.Output.Link.CollectionTarget("target");
            target.DebugCommands = DebugWorkerParentCommands.BreakOnRunning;
        });
    workerSystem.Start().ThrowOnFailure();
    

    When stopped in the debugger from these breakpoints, use the Locals window and expand debugWorkerParentContext to show exactly which worker, and worker system hit the breakpoint. You can also view worker properties, the worker hierarchy etc. Where supported, you can even edit properties.

    Here we have stopped on "check trigger" getting the Running state. We have also expanded the WorkerSystem instance, and can e.g. navigate its Children worker hierarchy.

    Debugging WorkerParent Breakpoints Context

    Important
    • The "Break" command only works when the debugger is already attached to the application. Switch to a "Launch" command to have the application launch and attach the debugger (if needed) when hitting a breakpoint. This allows debugging even when just running the EXE from the command line.
    • If the debugger is not launching with "Launch*" as expected, ensure it is the default debugger for managed code. In Visual Studio e.g., enable Tools > Options > Debugging > Just In Time > Managed.
      • If the Just In Time section is not present in the options, use "Visual Studio Installer" > Modify > Individual Components > Debugging and testing > Just-In-Time debugger to install it.

    Dataflow Port Breakpoints

    You can instruct actionETL to launch or break a debugger when one or more dataflow ports change status, or when output and error output ports send dataflow rows. You can view and even edit rows before they are passed to the downstream worker. This can be very useful to discover where in the dataflow an issue originates.

    Important
    • Enabling port debugging brings a noticeable performance overhead; a warning is logged for each port where it is enabled.
    • Rows sent to unlinked error output ports cannot be viewed in the debugger.

    Port debugging is controlled by the following members:

    Member Description
    OutputPortBase.DebugCommands Launch/break on state changes and/or rows sent.
    OutputPortBase.DebugBreakAfterRows Launch/break after any, or a specific number of rows sent, and ports completed.
    OutputPortBase.SetDebugCommands(DebugPortCommands,long), OutputPortBase.DebugCommand(DebugPortCommands) Methods setting the above properties.

    This example generates 2000 rows, and does a simple transformation on them. It enables port debugging on both the source and transform output ports, breaking the debugger when either worker sends rows, as well as when each output port (and its downstream input port) completes.

    The "Break" command only works when the debugger is already attached to the application. Use a "Launch" command to have the application launch and attach the debugger (if needed) when hitting a breakpoint. This allows debugging even when just running the EXE from the command line.

    // using actionETL;
    
    private sealed class MyRow
    {
        public string Description;
        public int Metric;
    }
    
    var workerSystem = new WorkerSystem()
        .Root(ws =>
        {
            new RepeatRowsSource<MyRow>(ws, "source", 2000
                , new MyRow { Description = "The answer is", Metric = 42 }
                )
            .Output
            .SetDebugCommands(DebugPortCommands.BreakOnRowsAndStateCompleted)
    
            .Link.RowActionTransform1("transform", row => { row.Metric *= 2; } )
            .Output
            .SetDebugCommands(DebugPortCommands.BreakOnRowsAndStateCompleted)
    
            .Link.CollectionTarget("target");
        });
    workerSystem.Start().ThrowOnFailure();
    

    When stopped in the debugger, use the Locals window to view (and optionally edit) rows to be sent downstream. The port Locator and status is displayed in the top row:

    Debugging Port Breakpoints Rows

    Expand debugPortContext to show exactly which output port (and it's downstream input), worker, and worker system hit the breakpoint, and you can also view their properties. Here we have expanded the WorkerSystem instance, and can navigate its Children worker hierarchy.

    Furthermore, we have also set DebugBreakAfterRows to 1000; when we Continue, this port won't break until it gets to the 1000's row (i.e. #999). You can also modify DebugCommands to change or disable port debugging.

    Debugging Port Breakpoints Context

    See Also

    • Common Tasks
    • Getting Started
      • Dotnet Templates
      • Add actionETL Manually
      • Samples
      • Development Guidelines
    • Worker System
      • Configuration
      • Logging
      • Licensing
    • Troubleshooting
    • API documentation
    In This Article
    Back to top Copyright © 2023 Envobi Ltd